Skip to content

Commit 4cfbd57

Browse files
committed
Allow content to contain links to network locations
1 parent 19736aa commit 4cfbd57

File tree

13 files changed

+129
-30
lines changed

13 files changed

+129
-30
lines changed

core/database/scripts/autobuild/db_00024.sql

+4
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@
33
-- max tags per document setting
44
ALTER TABLE organization ADD COLUMN `maxtags` INT NOT NULL DEFAULT 3 AFTER `authconfig`;
55

6+
-- support for network location link types
7+
ALTER TABLE link ADD COLUMN `externalid` NVARCHAR(1000) NOT NULL DEFAULT '' AFTER `targetid`;
8+
69
-- deprecations
10+
ALTER TABLE organization DROP COLUMN `url`;

domain/link/link.go

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ func getLink(t html.Token) (ok bool, link link.Link) {
6767
link.TargetID = strings.TrimSpace(a.Val)
6868
case "data-link-type":
6969
link.LinkType = strings.TrimSpace(a.Val)
70+
case "data-external-id":
71+
link.ExternalID = strings.TrimSpace(a.Val)
7072
}
7173
}
7274

domain/link/mysql/store.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ func (s Scope) Add(ctx domain.RequestContext, l link.Link) (err error) {
3636
l.Created = time.Now().UTC()
3737
l.Revised = time.Now().UTC()
3838

39-
_, err = ctx.Transaction.Exec("INSERT INTO link (refid, orgid, folderid, userid, sourcedocumentid, sourcepageid, targetdocumentid, targetid, linktype, orphan, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
40-
l.RefID, l.OrgID, l.FolderID, l.UserID, l.SourceDocumentID, l.SourcePageID, l.TargetDocumentID, l.TargetID, l.LinkType, l.Orphan, l.Created, l.Revised)
39+
_, err = ctx.Transaction.Exec("INSERT INTO link (refid, orgid, folderid, userid, sourcedocumentid, sourcepageid, targetdocumentid, targetid, externalid, linktype, orphan, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
40+
l.RefID, l.OrgID, l.FolderID, l.UserID, l.SourceDocumentID, l.SourcePageID, l.TargetDocumentID, l.TargetID, l.ExternalID, l.LinkType, l.Orphan, l.Created, l.Revised)
4141

4242
if err != nil {
4343
err = errors.Wrap(err, "execute link insert")
@@ -49,7 +49,7 @@ func (s Scope) Add(ctx domain.RequestContext, l link.Link) (err error) {
4949
// GetDocumentOutboundLinks returns outbound links for specified document.
5050
func (s Scope) GetDocumentOutboundLinks(ctx domain.RequestContext, documentID string) (links []link.Link, err error) {
5151
err = s.Runtime.Db.Select(&links,
52-
`select l.refid, l.orgid, l.folderid, l.userid, l.sourcedocumentid, l.sourcepageid, l.targetdocumentid, l.targetid, l.linktype, l.orphan, l.created, l.revised
52+
`select l.refid, l.orgid, l.folderid, l.userid, l.sourcedocumentid, l.sourcepageid, l.targetdocumentid, l.targetid, l.externalid, l.linktype, l.orphan, l.created, l.revised
5353
FROM link l
5454
WHERE l.orgid=? AND l.sourcedocumentid=?`,
5555
ctx.OrgID,
@@ -70,7 +70,7 @@ func (s Scope) GetDocumentOutboundLinks(ctx domain.RequestContext, documentID st
7070
// GetPageLinks returns outbound links for specified page in document.
7171
func (s Scope) GetPageLinks(ctx domain.RequestContext, documentID, pageID string) (links []link.Link, err error) {
7272
err = s.Runtime.Db.Select(&links,
73-
`select l.refid, l.orgid, l.folderid, l.userid, l.sourcedocumentid, l.sourcepageid, l.targetdocumentid, l.targetid, l.linktype, l.orphan, l.created, l.revised
73+
`select l.refid, l.orgid, l.folderid, l.userid, l.sourcedocumentid, l.sourcepageid, l.targetdocumentid, l.targetid, l.externalid, l.linktype, l.orphan, l.created, l.revised
7474
FROM link l
7575
WHERE l.orgid=? AND l.sourcedocumentid=? AND l.sourcepageid=?`,
7676
ctx.OrgID,
@@ -159,12 +159,12 @@ func (s Scope) SearchCandidates(ctx domain.RequestContext, keywords string) (doc
159159

160160
err = s.Runtime.Db.Select(&temp, `
161161
SELECT d.refid as documentid, d. labelid as folderid, d.title, l.label as context
162-
FROM document d LEFT JOIN label l ON d.labelid=l.refid WHERE l.orgid=? AND `+likeQuery+`
162+
FROM document d LEFT JOIN label l ON d.labelid=l.refid WHERE l.orgid=? AND `+likeQuery+`
163163
AND d.labelid IN
164164
(
165165
SELECT refid FROM label WHERE orgid=?
166166
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
167-
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
167+
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
168168
UNION ALL
169169
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role'
170170
AND p.location='space' AND p.action='view' AND (r.userid=? OR r.userid='0')
@@ -197,12 +197,12 @@ func (s Scope) SearchCandidates(ctx domain.RequestContext, keywords string) (doc
197197

198198
err = s.Runtime.Db.Select(&temp,
199199
`SELECT p.refid as targetid, p.documentid as documentid, p.title as title, p.pagetype as linktype, d.title as context, d.labelid as folderid
200-
FROM page p LEFT JOIN document d ON d.refid=p.documentid WHERE p.orgid=? AND `+likeQuery+`
200+
FROM page p LEFT JOIN document d ON d.refid=p.documentid WHERE p.orgid=? AND `+likeQuery+`
201201
AND d.labelid IN
202202
(
203203
SELECT refid FROM label WHERE orgid=?
204204
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
205-
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
205+
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
206206
UNION ALL
207207
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role'
208208
AND p.location='space' AND p.action='view' AND (r.userid=? OR r.userid='0')
@@ -235,12 +235,12 @@ func (s Scope) SearchCandidates(ctx domain.RequestContext, keywords string) (doc
235235

236236
err = s.Runtime.Db.Select(&temp,
237237
`SELECT a.refid as targetid, a.documentid as documentid, a.filename as title, a.extension as context, d.labelid as folderid
238-
FROM attachment a LEFT JOIN document d ON d.refid=a.documentid WHERE a.orgid=? AND `+likeQuery+`
238+
FROM attachment a LEFT JOIN document d ON d.refid=a.documentid WHERE a.orgid=? AND `+likeQuery+`
239239
AND d.labelid IN
240240
(
241241
SELECT refid FROM label WHERE orgid=?
242242
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
243-
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
243+
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
244244
UNION ALL
245245
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role'
246246
AND p.location='space' AND p.action='view' AND (r.userid=? OR r.userid='0')

domain/organization/mysql/store.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ func (s Scope) AddOrganization(ctx domain.RequestContext, org org.Organization)
3636
org.Revised = time.Now().UTC()
3737

3838
_, err = ctx.Transaction.Exec(
39-
"INSERT INTO organization (refid, company, title, message, url, domain, email, allowanonymousaccess, serial, maxtags, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
40-
org.RefID, org.Company, org.Title, org.Message, strings.ToLower(org.URL), strings.ToLower(org.Domain),
39+
"INSERT INTO organization (refid, company, title, message, domain, email, allowanonymousaccess, serial, maxtags, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
40+
org.RefID, org.Company, org.Title, org.Message, strings.ToLower(org.Domain),
4141
strings.ToLower(org.Email), org.AllowAnonymousAccess, org.Serial, org.MaxTags, org.Created, org.Revised)
4242

4343
if err != nil {
@@ -49,7 +49,7 @@ func (s Scope) AddOrganization(ctx domain.RequestContext, org org.Organization)
4949

5050
// GetOrganization returns the Organization reocrod from the organization database table with the given id.
5151
func (s Scope) GetOrganization(ctx domain.RequestContext, id string) (org org.Organization, err error) {
52-
stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, maxtags, created, revised FROM organization WHERE refid=?")
52+
stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, company, title, message, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, maxtags, created, revised FROM organization WHERE refid=?")
5353
defer streamutil.Close(stmt)
5454

5555
if err != nil {
@@ -80,14 +80,14 @@ func (s Scope) GetOrganizationByDomain(subdomain string) (o org.Organization, er
8080
}
8181

8282
// match on given domain name
83-
err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, maxtags, created, revised FROM organization WHERE domain=? AND active=1", subdomain)
83+
err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, maxtags, created, revised FROM organization WHERE domain=? AND active=1", subdomain)
8484
if err == nil {
8585
return
8686
}
8787
err = nil
8888

8989
// match on empty domain as last resort
90-
err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, maxtags, created, revised FROM organization WHERE domain='' AND active=1")
90+
err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, maxtags, created, revised FROM organization WHERE domain='' AND active=1")
9191
if err != nil && err != sql.ErrNoRows {
9292
err = errors.Wrap(err, "unable to execute select for empty subdomain")
9393
}

domain/page/endpoint.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -464,14 +464,21 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
464464
link.SourceDocumentID = model.Page.DocumentID
465465
link.SourcePageID = model.Page.RefID
466466

467-
if link.LinkType == "document" {
467+
if link.LinkType == "document" || link.LinkType == "network" {
468468
link.TargetID = ""
469469
}
470+
if link.LinkType != "network" {
471+
link.ExternalID = ""
472+
}
470473

471474
// We check if there was a previously saved version of this link.
472475
// If we find one, we carry forward the orphan flag.
473476
for _, p := range previousLinks {
474-
if link.TargetID == p.TargetID && link.LinkType == p.LinkType {
477+
if link.LinkType == p.LinkType && link.TargetID == p.TargetID && link.LinkType != "network" {
478+
link.Orphan = p.Orphan
479+
break
480+
}
481+
if link.LinkType == p.LinkType && link.ExternalID == p.ExternalID && link.LinkType == "network" {
475482
link.Orphan = p.Orphan
476483
break
477484
}

gui/app/components/document/content-linker.js

+25-1
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
import { debounce } from '@ember/runloop';
1313
import { computed, set } from '@ember/object';
1414
import { inject as service } from '@ember/service';
15-
import Component from '@ember/component';
15+
import stringUtil from '../../utils/string';
1616
import TooltipMixin from '../../mixins/tooltip';
1717
import ModalMixin from '../../mixins/modal';
18+
import Component from '@ember/component';
1819

1920
export default Component.extend(ModalMixin, TooltipMixin, {
2021
link: service(),
@@ -26,6 +27,8 @@ export default Component.extend(ModalMixin, TooltipMixin, {
2627
showSections: computed('tab1Selected', function() { return this.get('tab1Selected'); }),
2728
showAttachments: computed('tab2Selected', function() { return this.get('tab2Selected'); }),
2829
showSearch: computed('tab3Selected', function() { return this.get('tab3Selected'); }),
30+
showNetwork: computed('tab4Selected', function() { return this.get('tab4Selected'); }),
31+
networkLocation: '',
2932
keywords: '',
3033
hasMatches: computed('matches', function () {
3134
let m = this.get('matches');
@@ -66,6 +69,8 @@ export default Component.extend(ModalMixin, TooltipMixin, {
6669

6770
didRender() {
6871
this._super(...arguments);
72+
73+
this.$('#content-linker-networklocation').removeClass('is-invalid');
6974
this.renderTooltips();
7075
},
7176

@@ -114,7 +119,25 @@ export default Component.extend(ModalMixin, TooltipMixin, {
114119
onInsertLink() {
115120
let selection = this.get('selection');
116121

122+
if (this.get('tab4Selected')) {
123+
let loc = this.get('networkLocation').trim();
124+
let folderId = this.get('folder.id');
125+
let documentId = this.get('document.id');
126+
127+
selection = {
128+
context: '',
129+
documentId: documentId,
130+
folderId: folderId,
131+
id: stringUtil.makeId(16),
132+
linkType: 'network',
133+
targetId: '',
134+
externalId: loc,
135+
title: loc
136+
}
137+
}
138+
117139
if (is.null(selection)) {
140+
if (this.get('tab4Selected')) this.$('#content-linker-networklocation').addClass('is-invalid').focus();
118141
return;
119142
}
120143

@@ -125,6 +148,7 @@ export default Component.extend(ModalMixin, TooltipMixin, {
125148
this.set('tab1Selected', id === 1);
126149
this.set('tab2Selected', id === 2);
127150
this.set('tab3Selected', id === 3);
151+
this.set('tab4Selected', id === 4);
128152
}
129153
}
130154
});

gui/app/components/layout/bottom-bar.js

+16
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ export default Component.extend({
1717
classNames: ['layout-footer', 'non-printable'],
1818
tagName: 'footer',
1919
appMeta: service(),
20+
showWait: false,
21+
showDone: false,
22+
showMessage: false,
23+
message: '',
2024

2125
init() {
2226
this._super(...arguments);
@@ -40,5 +44,17 @@ export default Component.extend({
4044
$('.progress-done').removeClass('zoomIn').addClass('zoomOut');
4145
}, 3000);
4246
}
47+
48+
if (msg !== 'done' && msg !== 'wait') {
49+
$('.progress-notification').removeClass('zoomOut').addClass('zoomIn');
50+
this.set('showWait', false);
51+
this.set('showDone', false);
52+
this.set('showMessage', true);
53+
this.set('message', msg);
54+
55+
setTimeout(function() {
56+
$('.progress-notification').removeClass('zoomIn').addClass('zoomOut');
57+
}, 3000);
58+
}
4359
}
4460
});

gui/app/services/link.js

+27-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
// https://documize.com
1111

1212
import Service, { inject as service } from '@ember/service';
13+
import Notifier from '../mixins/notifier';
1314

14-
export default Service.extend({
15+
export default Service.extend(Notifier, {
1516
sessionService: service('session'),
1617
ajax: service(),
1718
appMeta: service(),
1819
store: service(),
20+
eventBus: service(),
1921

2022
// Returns links within specified document
2123
getDocumentLinks(documentId) {
@@ -67,6 +69,10 @@ export default Service.extend({
6769
href = `${endpoint}/public/attachments/${orgId}/${link.targetId}`;
6870
result = `<a data-documize='true' data-link-space-id='${link.folderId}' data-link-id='${link.id}' data-link-target-document-id='${link.documentId}' data-link-target-id='${link.targetId}' data-link-type='${link.linkType}' href='${href}'>${link.title}</a>`;
6971
}
72+
if (link.linkType === "network") {
73+
href = `fileto://${link.externalId}`;
74+
result = `<a data-documize='true' data-link-space-id='${link.folderId}' data-link-id='${link.id}' data-link-target-document-id='${link.documentId}' data-link-target-id='${link.targetId}' data-link-external-id='${link.externalId}' data-link-type='${link.linkType}' href='${href}'>${link.title}</a>`;
75+
}
7076

7177
return result;
7278
},
@@ -78,11 +84,12 @@ export default Service.extend({
7884
documentId: a.attributes["data-link-target-document-id"].value,
7985
folderId: a.attributes["data-link-space-id"].value,
8086
targetId: a.attributes["data-link-target-id"].value,
87+
externalId: a.attributes["data-link-external-id"].value,
8188
url: a.attributes["href"].value,
8289
orphan: false
8390
};
8491

85-
link.orphan = _.isEmpty(link.linkId) || _.isEmpty(link.documentId) || _.isEmpty(link.folderId) || _.isEmpty(link.targetId);
92+
link.orphan = _.isEmpty(link.linkId) || _.isEmpty(link.documentId) || _.isEmpty(link.folderId) || (_.isEmpty(link.targetId) && _.isEmpty(link.externalId));
8693

8794
// we check latest state of link using database data
8895
let existing = outboundLinks.findBy('id', link.linkId);
@@ -126,5 +133,23 @@ export default Service.extend({
126133
window.location.href = link.url;
127134
return;
128135
}
136+
137+
// handle network share/drive links
138+
if (link.linkType === "network") {
139+
// window.location.href = link.externalId;
140+
const el = document.createElement('textarea');
141+
el.value = link.externalId;
142+
el.setAttribute('readonly', '');
143+
el.style.position = 'absolute';
144+
el.style.left = '-9999px';
145+
document.body.appendChild(el);
146+
el.select();
147+
document.execCommand('copy');
148+
document.body.removeChild(el);
149+
150+
this.showNotification('Copied location to clipboard');
151+
152+
return;
153+
}
129154
}
130155
});

gui/app/styles/layout/layout-footer.scss

+8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ footer {
2222

2323
> .progress {
2424
display: inline-block;
25+
text-align: right;
2526

2627
> img {
2728
padding: 0;
@@ -40,4 +41,11 @@ footer {
4041
width: 20px;
4142
@include border-radius(20px);
4243
}
44+
45+
> .progress-notification {
46+
display: inline-block;
47+
font-size: 1rem;
48+
color: $color-green;
49+
font-weight: 500;
50+
}
4351
}

gui/app/templates/components/document/content-linker.hbs

+13-3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
<div class="modal-content">
44
<div class="modal-header">Insert Link</div>
55
<div class="modal-body">
6-
76
<div class="container">
8-
97
<div class="row">
108
<div class="col mt-3 mb-5">
119
<ul class="tabnav-control text-center">
1210
<li class="tab {{if tab1Selected 'selected'}}" {{action 'onTabSelect' 1}}>Section</li>
1311
<li class="tab {{if tab2Selected 'selected'}}" {{action 'onTabSelect' 2}}>Attachment</li>
1412
<li class="tab {{if tab3Selected 'selected'}}" {{action 'onTabSelect' 3}}>Search</li>
13+
<li class="tab {{if tab4Selected 'selected'}}" {{action 'onTabSelect' 4}}>Network</li>
1514
</ul>
1615
</div>
1716
</div>
@@ -98,8 +97,19 @@
9897
</div>
9998
{{/if}}
10099

101-
</div>
100+
{{#if showNetwork}}
101+
<div class="row">
102+
<div class="col content-linker-modal-container">
103+
<p>Specify network drive/share/folder location</p>
104+
<div class="form-group">
105+
{{focus-input id="content-linker-networklocation" type="input" class="form-control" value=networkLocation placeholder="e.g. //share/folder" autocomplete="off"}}
106+
<small class="form-text text-muted"></small>
107+
</div>
108+
</div>
109+
</div>
110+
{{/if}}
102111

112+
</div>
103113
</div>
104114
<div class="modal-footer">
105115
<button type="button" class="btn btn-outline-secondary" {{action 'onCancel'}}>Cancel</button>
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<div class="row no-gutters d-flex align-items-center">
22
<div class="col d-flex justify-content-start">
3+
<div class="footer">
4+
<a href="https://documize.com?ref=af">Documize {{appMeta.version}}</a>
5+
</div>
6+
{{yield}}
7+
</div>
8+
<div class="col d-flex justify-content-end">
39
<div class="footer">
410
{{#if showWait}}
511
<div class="progress progress-wait animated fadeIn">
@@ -9,12 +15,9 @@
915
{{#if showDone}}
1016
<div class="progress progress-done animated zoomIn">&check;</div>
1117
{{/if}}
12-
</div>
13-
{{yield}}
14-
</div>
15-
<div class="col d-flex justify-content-end">
16-
<div class="footer">
17-
<a href="https://documize.com?ref=af">Documize {{appMeta.version}}</a>
18+
{{#if showMessage}}
19+
<div class="progress-notification animated zoomIn">{{message}}</div>
20+
{{/if}}
1821
</div>
1922
</div>
2023
</div>

0 commit comments

Comments
 (0)