mirror of
https://github.com/documize/community.git
synced 2025-07-20 13:49:42 +02:00
click handlers for content links
This commit is contained in:
parent
c27de6bcab
commit
899b4f978c
27 changed files with 265 additions and 83 deletions
|
@ -17,6 +17,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
documentService: Ember.inject.service('document'),
|
documentService: Ember.inject.service('document'),
|
||||||
sectionService: Ember.inject.service('section'),
|
sectionService: Ember.inject.service('section'),
|
||||||
appMeta: Ember.inject.service(),
|
appMeta: Ember.inject.service(),
|
||||||
|
link: Ember.inject.service(),
|
||||||
/* Parameters */
|
/* Parameters */
|
||||||
document: null,
|
document: null,
|
||||||
// pages: [],
|
// pages: [],
|
||||||
|
@ -50,6 +51,10 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
didRender() {
|
||||||
|
this.contentLinkHandler();
|
||||||
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
this.destroyTooltips();
|
this.destroyTooltips();
|
||||||
|
|
||||||
|
@ -60,6 +65,32 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
contentLinkHandler() {
|
||||||
|
let links = this.get('link');
|
||||||
|
let doc = this.get('document');
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
$("a[data-documize='true']").off('click').on('click', function() {
|
||||||
|
let link = links.getLinkObject(this);
|
||||||
|
|
||||||
|
// local link? exists?
|
||||||
|
if (link.linkType === "section" && link.documentId === doc.get('id')) {
|
||||||
|
let exists = self.get('pages').findBy('id', link.targetId);
|
||||||
|
|
||||||
|
if (_.isUndefined(exists) || link.orphan) {
|
||||||
|
self.showNotification('Broken link!');
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
self.attrs.gotoPage(link.targetId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
links.linkClick(doc, link);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
confirmDeleteAttachment(id, name) {
|
confirmDeleteAttachment(id, name) {
|
||||||
this.set('deleteAttachment', {
|
this.set('deleteAttachment', {
|
||||||
|
|
|
@ -18,20 +18,34 @@ const {
|
||||||
|
|
||||||
export default Ember.Component.extend(TooltipMixin, {
|
export default Ember.Component.extend(TooltipMixin, {
|
||||||
link: service(),
|
link: service(),
|
||||||
hasSections: false,
|
|
||||||
hasAttachments: false,
|
|
||||||
linkName: '',
|
linkName: '',
|
||||||
keywords: '',
|
keywords: '',
|
||||||
selection: null,
|
selection: null,
|
||||||
|
tabs: [
|
||||||
|
{ label: 'Section', selected: true },
|
||||||
|
{ label: 'Attachment', selected: false },
|
||||||
|
{ label: 'Search', selected: false }
|
||||||
|
],
|
||||||
|
|
||||||
|
showSections: Ember.computed('tabs.@each.selected', function() {
|
||||||
|
return this.get('tabs').findBy('label', 'Section').selected;
|
||||||
|
}),
|
||||||
|
showAttachments: Ember.computed('tabs.@each.selected', function() {
|
||||||
|
return this.get('tabs').findBy('label', 'Attachment').selected;
|
||||||
|
}),
|
||||||
|
showSearch: Ember.computed('tabs.@each.selected', function() {
|
||||||
|
return this.get('tabs').findBy('label', 'Search').selected;
|
||||||
|
}),
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
|
let folderId = this.get('folder.id');
|
||||||
let documentId = this.get('document.id');
|
let documentId = this.get('document.id');
|
||||||
let pageId = this.get('page.id');
|
let pageId = this.get('page.id');
|
||||||
|
|
||||||
this.get('link').getCandidates(documentId, pageId).then(function (candidates) {
|
this.get('link').getCandidates(folderId, documentId, pageId).then(function (candidates) {
|
||||||
self.set('candidates', candidates);
|
self.set('candidates', candidates);
|
||||||
self.set('hasSections', is.not.null(candidates.pages) && candidates.pages.length);
|
self.set('hasSections', is.not.null(candidates.pages) && candidates.pages.length);
|
||||||
self.set('hasAttachments', is.not.null(candidates.attachments) && candidates.attachments.length);
|
self.set('hasAttachments', is.not.null(candidates.attachments) && candidates.attachments.length);
|
||||||
|
@ -69,6 +83,10 @@ export default Ember.Component.extend(TooltipMixin, {
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('onInsertLink')(selection);
|
return this.get('onInsertLink')(selection);
|
||||||
|
},
|
||||||
|
|
||||||
|
onTabSelect(tabs) {
|
||||||
|
this.set('tabs', tabs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
32
app/app/components/ui/ui-tab.js
Normal file
32
app/app/components/ui/ui-tab.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// This software (Documize Community Edition) is licensed under
|
||||||
|
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||||
|
//
|
||||||
|
// You can operate outside the AGPL restrictions by purchasing
|
||||||
|
// Documize Enterprise Edition and obtaining a commercial license
|
||||||
|
// by contacting <sales@documize.com>.
|
||||||
|
//
|
||||||
|
// https://documize.com
|
||||||
|
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
myWidth: Ember.computed('tabs', function() {
|
||||||
|
let count = this.get('tabs.length');
|
||||||
|
let width = 95 / count;
|
||||||
|
return Ember.String.htmlSafe("width: " + `${width}%;`);
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
onTabSelect(tab) {
|
||||||
|
this.get('tabs').forEach(t => {
|
||||||
|
Ember.set(t, 'selected', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ember.set(tab, 'selected', true);
|
||||||
|
|
||||||
|
this.attrs.onTabSelect(this.get('tabs'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -18,6 +18,6 @@ export function initialize(application) {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'eventBus',
|
name: 'eventBus',
|
||||||
after: 'session',
|
after: 'application',
|
||||||
initialize: initialize
|
initialize: initialize
|
||||||
};
|
};
|
20
app/app/initializers/route-injector.js
Normal file
20
app/app/initializers/route-injector.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||||
|
//
|
||||||
|
// This software (Documize Community Edition) is licensed under
|
||||||
|
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||||
|
//
|
||||||
|
// You can operate outside the AGPL restrictions by purchasing
|
||||||
|
// Documize Enterprise Edition and obtaining a commercial license
|
||||||
|
// by contacting <sales@documize.com>.
|
||||||
|
//
|
||||||
|
// https://documize.com
|
||||||
|
|
||||||
|
export function initialize(application) {
|
||||||
|
application.inject('component', 'router', 'router:main');
|
||||||
|
application.inject('service', 'router', 'router:main');
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'route-injector',
|
||||||
|
initialize: initialize
|
||||||
|
};
|
|
@ -16,7 +16,6 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
||||||
documentService: Ember.inject.service('document'),
|
documentService: Ember.inject.service('document'),
|
||||||
folderService: Ember.inject.service('folder'),
|
folderService: Ember.inject.service('folder'),
|
||||||
userService: Ember.inject.service('user'),
|
userService: Ember.inject.service('user'),
|
||||||
|
|
||||||
pages: [],
|
pages: [],
|
||||||
attachments: [],
|
attachments: [],
|
||||||
users: [],
|
users: [],
|
||||||
|
@ -107,14 +106,5 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
||||||
controller.set('meta', meta);
|
controller.set('meta', meta);
|
||||||
|
|
||||||
this.browser.setMetaDescription(model.get('excerpt'));
|
this.browser.setMetaDescription(model.get('excerpt'));
|
||||||
},
|
|
||||||
|
|
||||||
// Document view needs all white background!
|
|
||||||
activate() {
|
|
||||||
Ember.$('html').addClass('background-color-white');
|
|
||||||
},
|
|
||||||
|
|
||||||
deactivate() {
|
|
||||||
Ember.$('html').removeClass('background-color-white');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
{{document/document-view document=model pages=pages attachments=attachments folder=folder folders=folders
|
{{document/document-view document=model pages=pages attachments=attachments folder=folder folders=folders
|
||||||
isEditor=isEditor
|
isEditor=isEditor
|
||||||
|
gotoPage=(action 'gotoPage')
|
||||||
onAttachmentDeleted=(action 'onAttachmentDeleted')
|
onAttachmentDeleted=(action 'onAttachmentDeleted')
|
||||||
onDeletePage=(action 'onPageDeleted')}}
|
onDeletePage=(action 'onPageDeleted')}}
|
||||||
{{/layout/zone-content}}
|
{{/layout/zone-content}}
|
||||||
|
|
|
@ -25,6 +25,7 @@ export default Ember.Service.extend({
|
||||||
appId: config.APP.intercomKey,
|
appId: config.APP.intercomKey,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
this.start();
|
this.start();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -28,5 +28,9 @@ export default Ember.Service.extend({
|
||||||
}(wait, times);
|
}(wait, times);
|
||||||
|
|
||||||
setTimeout(interv, wait);
|
setTimeout(interv, wait);
|
||||||
|
},
|
||||||
|
|
||||||
|
showNotification(msg) {
|
||||||
|
this.get('eventBus').publish('notifyUser', msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -13,6 +13,7 @@ import Ember from 'ember';
|
||||||
|
|
||||||
export default Ember.Service.extend(Ember.Evented, {
|
export default Ember.Service.extend(Ember.Evented, {
|
||||||
init() {
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
let _this = this;
|
let _this = this;
|
||||||
|
|
||||||
window.addEventListener("scroll", _.throttle(function() {
|
window.addEventListener("scroll", _.throttle(function() {
|
||||||
|
|
|
@ -19,10 +19,11 @@ export default Ember.Service.extend({
|
||||||
sessionService: service('session'),
|
sessionService: service('session'),
|
||||||
ajax: service(),
|
ajax: service(),
|
||||||
appMeta: service(),
|
appMeta: service(),
|
||||||
|
store: service(),
|
||||||
|
|
||||||
// Returns candidate links using provided parameters
|
// Returns candidate links using provided parameters
|
||||||
getCandidates(documentId, pageId /*, keywords*/ ) {
|
getCandidates(folderId, documentId, pageId /*, keywords*/ ) {
|
||||||
return this.get('ajax').request(`links/${documentId}/${pageId}`, {
|
return this.get('ajax').request(`links/${folderId}/${documentId}/${pageId}`, {
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
return response;
|
return response;
|
||||||
|
@ -35,45 +36,85 @@ export default Ember.Service.extend({
|
||||||
let endpoint = this.get('appMeta').get('endpoint');
|
let endpoint = this.get('appMeta').get('endpoint');
|
||||||
let orgId = this.get('appMeta').get('orgId');
|
let orgId = this.get('appMeta').get('orgId');
|
||||||
|
|
||||||
if (link.linkType === "section") {
|
if (link.linkType === "section" || link.linkType === "document") {
|
||||||
href = `/link/${link.linkType}/${link.id}`;
|
href = `/link/${link.linkType}/${link.id}`;
|
||||||
|
result = `<a data-documize='true' data-link-space-id='${link.folderId}' data-link-id='${link.id}' data-link-document-id='${link.documentId}' data-link-target-id='${link.targetId}' data-link-type='${link.linkType}' href='${href}'>${link.title}</a>`;
|
||||||
}
|
}
|
||||||
if (link.linkType === "file") {
|
if (link.linkType === "file") {
|
||||||
href = `${endpoint}/public/attachments/${orgId}/${link.targetId}`;
|
href = `${endpoint}/public/attachments/${orgId}/${link.targetId}`;
|
||||||
}
|
result = `<a data-documize='true' data-link-space-id='${link.folderId}' data-link-id='${link.id}' data-link-document-id='${link.documentId}' data-link-target-id='${link.targetId}' data-link-type='${link.linkType}' href='${href}'>${link.title}</a>`;
|
||||||
if (link.linkType === "document") {
|
|
||||||
href = `/link/${link.linkType}/${link.id}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = `<a data-documize='true' data-link-id='${link.id}' data-link-document-id='${link.documentId}' data-link-target-id='${link.targetId}' data-link-type='${link.linkType}' href='${href}'>${link.title}</a>`;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
getLinkObject(a) {
|
||||||
|
let link = {
|
||||||
|
linkId: a.attributes["data-link-id"].value,
|
||||||
|
linkType: a.attributes["data-link-type"].value,
|
||||||
|
documentId: a.attributes["data-link-document-id"].value,
|
||||||
|
folderId: a.attributes["data-link-space-id"].value,
|
||||||
|
targetId: a.attributes["data-link-target-id"].value,
|
||||||
|
url: a.attributes["href"].value,
|
||||||
|
orphan: false
|
||||||
|
};
|
||||||
|
|
||||||
|
link.orphan = _.isEmpty(link.linkId) || _.isEmpty(link.documentId) || _.isEmpty(link.folderId) || _.isEmpty(link.targetId);
|
||||||
|
|
||||||
|
return link;
|
||||||
|
},
|
||||||
|
|
||||||
|
linkClick(doc, link) {
|
||||||
|
if (link.orphan) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let router = this.get('router');
|
||||||
|
let targetFolder = this.get('store').peekRecord('folder', link.folderId);
|
||||||
|
let targetDocument = this.get('store').peekRecord('document', link.documentId);
|
||||||
|
let folderSlug = is.null(targetFolder) ? "s" : targetFolder.get('slug');
|
||||||
|
let documentSlug = is.null(targetDocument) ? "d" : targetDocument.get('slug');
|
||||||
|
|
||||||
|
// handle section link
|
||||||
|
if (link.linkType === "section") {
|
||||||
|
let options = {};
|
||||||
|
options['page'] = link.targetId;
|
||||||
|
router.transitionTo('document', link.folderId, folderSlug, link.documentId, documentSlug, { queryParams: options });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle document link
|
||||||
|
if (link.inkType === "document") {
|
||||||
|
router.transitionTo('document', link.folderId, folderSlug, link.documentId, documentSlug);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle attachment links
|
||||||
|
if (link.linkType === "file") {
|
||||||
|
window.location.href = link.url;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
link handler
|
|
||||||
- implement link redirect handler --
|
|
||||||
- for documents: client-side detect
|
|
||||||
- for sections:
|
|
||||||
- for attachments: direct link
|
|
||||||
-
|
|
||||||
|
|
||||||
onDelete document/section/file:
|
|
||||||
- mark link table row as ORPHAN
|
|
||||||
- doc view: meta data fetch to load orphaned content
|
|
||||||
|
|
||||||
Keyword search results - docs, section, files
|
Keyword search results - docs, section, files
|
||||||
|
|
||||||
we should not redirect to a link that is in the same document!
|
The link id's get ZERO'd in Page.Body whenever:
|
||||||
what happens if we delete attachment?
|
- doc is moved to different space
|
||||||
UpdatePage(): find and persist links from saved content
|
- doc is deleted (set to ZERO and marked as orphan)
|
||||||
|
- page is deleted (set to ZERO and marked as orphan)
|
||||||
|
- page is moved to different doc (update data-document-id attribute value)
|
||||||
|
- attachment is deleted (remove HREF)
|
||||||
|
|
||||||
1. We need to deal with links server-side
|
link/section/{documentId}/{sectionId}:
|
||||||
2. We need to click on links in the browser and 'navigate' to linked content
|
- if ZERO id show notification
|
||||||
|
- store previous positions -- localStorage, dropdown menu?
|
||||||
|
|
||||||
editor.insertContent(' <b>It\'s my button!</b> ');
|
Markdown editor support
|
||||||
Selects the first paragraph found
|
|
||||||
tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);
|
permission checks:
|
||||||
|
can view space
|
||||||
|
can view document
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -41,6 +41,8 @@ export default SimpleAuthSession.extend({
|
||||||
}),
|
}),
|
||||||
|
|
||||||
init: function () {
|
init: function () {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
this.set('isMac', is.mac());
|
this.set('isMac', is.mac());
|
||||||
this.set('isMobile', is.mobile());
|
this.set('isMobile', is.mobile());
|
||||||
},
|
},
|
||||||
|
|
|
@ -196,7 +196,7 @@ $i: 100;
|
||||||
.width-#{$i} {
|
.width-#{$i} {
|
||||||
width: #{$i}#{"%"};
|
width: #{$i}#{"%"};
|
||||||
}
|
}
|
||||||
$i: $i - 2;
|
$i: $i - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-outline {
|
.no-outline {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
.edit-tools {
|
.edit-tools {
|
||||||
margin: 15px 0 0 20px;
|
margin: 0 0 0 20px;
|
||||||
min-height: 500px;
|
min-height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-linker-dialog {
|
.content-linker-dialog {
|
||||||
width: 300px;
|
width: 350px;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
|
27
app/app/styles/widget/widget-tab.scss
Normal file
27
app/app/styles/widget/widget-tab.scss
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
.widget-tab {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 5px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid $color-border;
|
||||||
|
|
||||||
|
> .tab {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 5px 10px;
|
||||||
|
color: $color-off-black;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
@include ease-in();
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color-off-white;
|
||||||
|
color: $color-link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .selected {
|
||||||
|
background-color: $color-off-white;
|
||||||
|
color: $color-link;
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,3 +71,4 @@
|
||||||
@import "widget-table";
|
@import "widget-table";
|
||||||
@import "widget-tooltip";
|
@import "widget-tooltip";
|
||||||
@import "widget-checkbox";
|
@import "widget-checkbox";
|
||||||
|
@import "widget-tab";
|
||||||
|
|
|
@ -4,15 +4,14 @@
|
||||||
<i class="material-icons color-white">link</i>
|
<i class="material-icons color-white">link</i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#dropdown-dialog target="content-linker-button" focusOn="content-linker-search" position="bottom right" button="Insert" color="flat-blue" onAction=(action 'onInsertLink')}}
|
{{#dropdown-dialog target="content-linker-button" position="bottom right" button="Insert" color="flat-blue" onAction=(action 'onInsertLink')}}
|
||||||
<div class="content-linker-dialog">
|
<div class="content-linker-dialog">
|
||||||
<form>
|
<form>
|
||||||
<div class="input-control">
|
{{ui/ui-tab tabs=tabs onTabSelect=(action 'onTabSelect')}}
|
||||||
<label>Insert Link</label>
|
|
||||||
<div class="tip">Choose content below or search</div>
|
<div class="margin-top-40" />
|
||||||
{{focus-input id="content-linker-search" type="input" value=keywords placeholder="keyword search"}}
|
|
||||||
</div>
|
{{#if showSections}}
|
||||||
{{#if hasSections}}
|
|
||||||
<ul class="link-list">
|
<ul class="link-list">
|
||||||
{{#each candidates.pages as |p|}}
|
{{#each candidates.pages as |p|}}
|
||||||
<li class="link-item" {{ action 'setSelection' p }}>
|
<li class="link-item" {{ action 'setSelection' p }}>
|
||||||
|
@ -23,7 +22,8 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if hasAttachments}}
|
|
||||||
|
{{#if showAttachments}}
|
||||||
<ul class="link-list">
|
<ul class="link-list">
|
||||||
{{#each candidates.attachments as |a|}}
|
{{#each candidates.attachments as |a|}}
|
||||||
<li class="link-item" {{ action 'setSelection' a }}>
|
<li class="link-item" {{ action 'setSelection' a }}>
|
||||||
|
@ -35,6 +35,15 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if showSearch}}
|
||||||
|
<div class="input-control">
|
||||||
|
<label>Search</label>
|
||||||
|
<div class="tip">keywords</div>
|
||||||
|
{{focus-input id="content-linker-search" type="input" value=keywords placeholder="keyword search"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class="hide regular-button button-blue pull-right" {{ action 'onInsertLink' }}>Insert</div>
|
<div class="hide regular-button button-blue pull-right" {{ action 'onInsertLink' }}>Insert</div>
|
||||||
<div class="hide clearfix" />
|
<div class="hide clearfix" />
|
||||||
</form>
|
</form>
|
||||||
|
|
5
app/app/templates/components/ui/ui-tab.hbs
Normal file
5
app/app/templates/components/ui/ui-tab.hbs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<ul class="widget-tab">
|
||||||
|
{{#each tabs as |tab|}}
|
||||||
|
<li style={{myWidth}} class="tab {{if tab.selected 'selected'}}" {{action 'onTabSelect' tab}}>{{tab.label}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
|
@ -29,6 +29,7 @@ func GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
|
||||||
p := request.GetPersister(r)
|
p := request.GetPersister(r)
|
||||||
|
|
||||||
params := mux.Vars(r)
|
params := mux.Vars(r)
|
||||||
|
folderID := params["folderID"]
|
||||||
documentID := params["documentID"]
|
documentID := params["documentID"]
|
||||||
pageID := params["pageID"]
|
pageID := params["pageID"]
|
||||||
|
|
||||||
|
@ -68,6 +69,7 @@ func GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
|
||||||
if p.RefID != pageID {
|
if p.RefID != pageID {
|
||||||
c := entity.LinkCandidate{
|
c := entity.LinkCandidate{
|
||||||
RefID: util.UniqueID(),
|
RefID: util.UniqueID(),
|
||||||
|
FolderID: folderID,
|
||||||
DocumentID: documentID,
|
DocumentID: documentID,
|
||||||
TargetID: p.RefID,
|
TargetID: p.RefID,
|
||||||
LinkType: "section",
|
LinkType: "section",
|
||||||
|
@ -95,6 +97,7 @@ func GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
c := entity.LinkCandidate{
|
c := entity.LinkCandidate{
|
||||||
RefID: util.UniqueID(),
|
RefID: util.UniqueID(),
|
||||||
|
FolderID: folderID,
|
||||||
DocumentID: documentID,
|
DocumentID: documentID,
|
||||||
TargetID: f.RefID,
|
TargetID: f.RefID,
|
||||||
LinkType: "file",
|
LinkType: "file",
|
||||||
|
@ -124,13 +127,3 @@ func GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
util.WriteSuccessBytes(w, json)
|
util.WriteSuccessBytes(w, json)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
DocumentID string `json:"documentId"`
|
|
||||||
PageID string `json:"pageId"`
|
|
||||||
FileID string `json:"fileId"`
|
|
||||||
LinkType string `json:"linkType"`
|
|
||||||
Title string `json:"caption"` // what we label the link
|
|
||||||
Context string `json:"context"` // additional context (e.g. excerpt, parent)
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
|
@ -213,7 +213,7 @@ func init() {
|
||||||
log.IfErr(Add(RoutePrefixPrivate, "sections/refresh", []string{"GET", "OPTIONS"}, nil, RefreshSections))
|
log.IfErr(Add(RoutePrefixPrivate, "sections/refresh", []string{"GET", "OPTIONS"}, nil, RefreshSections))
|
||||||
|
|
||||||
// Links
|
// Links
|
||||||
log.IfErr(Add(RoutePrefixPrivate, "links/{documentID}/{pageID}", []string{"GET", "OPTIONS"}, nil, GetLinkCandidates))
|
log.IfErr(Add(RoutePrefixPrivate, "links/{folderID}/{documentID}/{pageID}", []string{"GET", "OPTIONS"}, nil, GetLinkCandidates))
|
||||||
|
|
||||||
// Global installation-wide config
|
// Global installation-wide config
|
||||||
log.IfErr(Add(RoutePrefixPrivate, "global", []string{"GET", "OPTIONS"}, nil, GetGlobalConfig))
|
log.IfErr(Add(RoutePrefixPrivate, "global", []string{"GET", "OPTIONS"}, nil, GetGlobalConfig))
|
||||||
|
|
|
@ -347,6 +347,7 @@ type SitemapDocument struct {
|
||||||
type Link struct {
|
type Link struct {
|
||||||
BaseEntity
|
BaseEntity
|
||||||
OrgID string `json:"orgId"`
|
OrgID string `json:"orgId"`
|
||||||
|
FolderID string `json:"folderId"`
|
||||||
UserID string `json:"userId"`
|
UserID string `json:"userId"`
|
||||||
LinkType string `json:"linkType"`
|
LinkType string `json:"linkType"`
|
||||||
SourceID string `json:"sourceId"`
|
SourceID string `json:"sourceId"`
|
||||||
|
@ -359,6 +360,7 @@ type Link struct {
|
||||||
type LinkCandidate struct {
|
type LinkCandidate struct {
|
||||||
RefID string `json:"id"`
|
RefID string `json:"id"`
|
||||||
LinkType string `json:"linkType"`
|
LinkType string `json:"linkType"`
|
||||||
|
FolderID string `json:"folderId"`
|
||||||
DocumentID string `json:"documentId"`
|
DocumentID string `json:"documentId"`
|
||||||
TargetID string `json:"targetId"`
|
TargetID string `json:"targetId"`
|
||||||
Title string `json:"title"` // what we label the link
|
Title string `json:"title"` // what we label the link
|
||||||
|
|
|
@ -27,7 +27,7 @@ func (p *Persister) AddContentLink(l entity.Link) (err error) {
|
||||||
l.Created = time.Now().UTC()
|
l.Created = time.Now().UTC()
|
||||||
l.Revised = time.Now().UTC()
|
l.Revised = time.Now().UTC()
|
||||||
|
|
||||||
stmt, err := p.Context.Transaction.Preparex("INSERT INTO link (refid, orgid, userid, sourceid, documentid, targetid, linktype, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
stmt, err := p.Context.Transaction.Preparex("INSERT INTO link (refid, orgid, folderid, userid, sourceid, documentid, targetid, linktype, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||||
defer utility.Close(stmt)
|
defer utility.Close(stmt)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -35,7 +35,7 @@ func (p *Persister) AddContentLink(l entity.Link) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = stmt.Exec(l.RefID, l.OrgID, l.UserID, l.SourceID, l.DocumentID, l.TargetID, l.LinkType, l.Created, l.Revised)
|
_, err = stmt.Exec(l.RefID, l.OrgID, l.FolderID, l.UserID, l.SourceID, l.DocumentID, l.TargetID, l.LinkType, l.Created, l.Revised)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to execute insert for link", err)
|
log.Error("Unable to execute insert for link", err)
|
||||||
|
@ -49,7 +49,7 @@ func (p *Persister) AddContentLink(l entity.Link) (err error) {
|
||||||
func (p *Persister) GetReferencedLinks(sectionID string) (links []entity.Link, err error) {
|
func (p *Persister) GetReferencedLinks(sectionID string) (links []entity.Link, err error) {
|
||||||
err = nil
|
err = nil
|
||||||
|
|
||||||
sql := "SELECT id,refid,orgid,userid,sourceid,documentid,targetid,linktype,orphan,created,revised from link WHERE orgid=? AND sourceid=?"
|
sql := "SELECT id,refid,orgid,folderid,userid,sourceid,documentid,targetid,linktype,orphan,created,revised from link WHERE orgid=? AND sourceid=?"
|
||||||
|
|
||||||
err = Db.Select(&links, sql, p.Context.OrgID, sectionID)
|
err = Db.Select(&links, sql, p.Context.OrgID, sectionID)
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ func (p *Persister) GetReferencedLinks(sectionID string) (links []entity.Link, e
|
||||||
func (p *Persister) GetContentLinksForSection(sectionID string) (links []entity.Link, err error) {
|
func (p *Persister) GetContentLinksForSection(sectionID string) (links []entity.Link, err error) {
|
||||||
err = nil
|
err = nil
|
||||||
|
|
||||||
sql := "SELECT id,refid,orgid,userid,sourceid,documentid,targetid,linktype,orphan,created,revised from link WHERE orgid=? AND sectionid=?"
|
sql := "SELECT id,refid,orgid,folderid,userid,sourceid,documentid,targetid,linktype,orphan,created,revised from link WHERE orgid=? AND sectionid=?"
|
||||||
|
|
||||||
err = Db.Select(&links, sql, p.Context.OrgID, sectionID)
|
err = Db.Select(&links, sql, p.Context.OrgID, sectionID)
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ func (p *Persister) GetContentLinksForSection(sectionID string) (links []entity.
|
||||||
func (p *Persister) GetContentLinksForDocument(documentID string) (links []entity.Link, err error) {
|
func (p *Persister) GetContentLinksForDocument(documentID string) (links []entity.Link, err error) {
|
||||||
err = nil
|
err = nil
|
||||||
|
|
||||||
sql := "SELECT id,refid,orgid,userid,sourceid,documentid,targetid,linktype,orphan,created,revised from link WHERE orgid=? AND documentid=?"
|
sql := "SELECT id,refid,orgid,folderid,userid,sourceid,documentid,targetid,linktype,orphan,created,revised from link WHERE orgid=? AND documentid=?"
|
||||||
|
|
||||||
err = Db.Select(&links, sql, p.Context.OrgID, documentID)
|
err = Db.Select(&links, sql, p.Context.OrgID, documentID)
|
||||||
|
|
||||||
|
|
|
@ -287,7 +287,7 @@ func (p *Persister) UpdatePage(page entity.Page, refID, userID string, skipRevis
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// fimnd any content links
|
// find any content links in the HTML
|
||||||
links := util.GetContentLinks(page.Body)
|
links := util.GetContentLinks(page.Body)
|
||||||
|
|
||||||
// delete previous content links for this page
|
// delete previous content links for this page
|
||||||
|
|
|
@ -14,11 +14,11 @@ package util
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestHTMLEncoding(t *testing.T) {
|
func TestHTMLEncoding(t *testing.T) {
|
||||||
html(t, "<script>alert('test')</script>", "<script>alert('test')</script>")
|
testHTML(t, "<script>alert('test')</script>", "<script>alert('test')</script>")
|
||||||
text(t, "<script>alert('test')</script>", "<script>alert('test')</script>")
|
text(t, "<script>alert('test')</script>", "<script>alert('test')</script>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func html(t *testing.T, in, out string) {
|
func testHTML(t *testing.T, in, out string) {
|
||||||
got := EncodeHTMLString(in)
|
got := EncodeHTMLString(in)
|
||||||
if got != out {
|
if got != out {
|
||||||
t.Errorf("EncodeHTMLString `%s` got `%s` expected `%s`\n", in, got, out)
|
t.Errorf("EncodeHTMLString `%s` got `%s` expected `%s`\n", in, got, out)
|
||||||
|
|
|
@ -60,6 +60,8 @@ func getLink(t html.Token) (ok bool, link entity.Link) {
|
||||||
ok = true
|
ok = true
|
||||||
case "data-link-id":
|
case "data-link-id":
|
||||||
link.RefID = strings.TrimSpace(a.Val)
|
link.RefID = strings.TrimSpace(a.Val)
|
||||||
|
case "data-link-space-id":
|
||||||
|
link.FolderID = strings.TrimSpace(a.Val)
|
||||||
case "data-link-document-id":
|
case "data-link-document-id":
|
||||||
link.DocumentID = strings.TrimSpace(a.Val)
|
link.DocumentID = strings.TrimSpace(a.Val)
|
||||||
case "data-link-target-id":
|
case "data-link-target-id":
|
||||||
|
|
|
@ -319,7 +319,8 @@ CREATE TABLE IF NOT EXISTS `link` (
|
||||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`userid` CHAR(16) DEFAULT '' COLLATE utf8_bin,
|
`folderid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`userid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`sourceid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`sourceid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`linktype` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`linktype` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
|
|
@ -5,7 +5,8 @@ CREATE TABLE IF NOT EXISTS `link` (
|
||||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`userid` CHAR(16) DEFAULT '' COLLATE utf8_bin,
|
`folderid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`userid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`sourceid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`sourceid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`linktype` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`linktype` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue