mirror of
https://github.com/documize/community.git
synced 2025-07-24 15:49:44 +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'),
|
||||
sectionService: Ember.inject.service('section'),
|
||||
appMeta: Ember.inject.service(),
|
||||
link: Ember.inject.service(),
|
||||
/* Parameters */
|
||||
document: null,
|
||||
// pages: [],
|
||||
|
@ -50,6 +51,10 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
|||
});
|
||||
},
|
||||
|
||||
didRender() {
|
||||
this.contentLinkHandler();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
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: {
|
||||
confirmDeleteAttachment(id, name) {
|
||||
this.set('deleteAttachment', {
|
||||
|
|
|
@ -18,20 +18,34 @@ const {
|
|||
|
||||
export default Ember.Component.extend(TooltipMixin, {
|
||||
link: service(),
|
||||
hasSections: false,
|
||||
hasAttachments: false,
|
||||
linkName: '',
|
||||
keywords: '',
|
||||
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() {
|
||||
this._super(...arguments);
|
||||
let self = this;
|
||||
|
||||
let folderId = this.get('folder.id');
|
||||
let documentId = this.get('document.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('hasSections', is.not.null(candidates.pages) && candidates.pages.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);
|
||||
},
|
||||
|
||||
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'));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// 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>.
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
|
@ -18,6 +18,6 @@ export function initialize(application) {
|
|||
|
||||
export default {
|
||||
name: 'eventBus',
|
||||
after: 'session',
|
||||
after: 'application',
|
||||
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
|
||||
};
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// 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>.
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
|
@ -16,7 +16,6 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
|||
documentService: Ember.inject.service('document'),
|
||||
folderService: Ember.inject.service('folder'),
|
||||
userService: Ember.inject.service('user'),
|
||||
|
||||
pages: [],
|
||||
attachments: [],
|
||||
users: [],
|
||||
|
@ -107,14 +106,5 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
|||
controller.set('meta', meta);
|
||||
|
||||
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
|
||||
isEditor=isEditor
|
||||
gotoPage=(action 'gotoPage')
|
||||
onAttachmentDeleted=(action 'onAttachmentDeleted')
|
||||
onDeletePage=(action 'onPageDeleted')}}
|
||||
{{/layout/zone-content}}
|
||||
|
|
|
@ -25,6 +25,7 @@ export default Ember.Service.extend({
|
|||
appId: config.APP.intercomKey,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.start();
|
||||
},
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// 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>.
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
|
@ -28,5 +28,9 @@ export default Ember.Service.extend({
|
|||
}(wait, times);
|
||||
|
||||
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, {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
let _this = this;
|
||||
|
||||
window.addEventListener("scroll", _.throttle(function() {
|
||||
|
|
|
@ -19,10 +19,11 @@ export default Ember.Service.extend({
|
|||
sessionService: service('session'),
|
||||
ajax: service(),
|
||||
appMeta: service(),
|
||||
store: service(),
|
||||
|
||||
// Returns candidate links using provided parameters
|
||||
getCandidates(documentId, pageId /*, keywords*/ ) {
|
||||
return this.get('ajax').request(`links/${documentId}/${pageId}`, {
|
||||
getCandidates(folderId, documentId, pageId /*, keywords*/ ) {
|
||||
return this.get('ajax').request(`links/${folderId}/${documentId}/${pageId}`, {
|
||||
method: 'GET'
|
||||
}).then((response) => {
|
||||
return response;
|
||||
|
@ -35,45 +36,85 @@ export default Ember.Service.extend({
|
|||
let endpoint = this.get('appMeta').get('endpoint');
|
||||
let orgId = this.get('appMeta').get('orgId');
|
||||
|
||||
if (link.linkType === "section") {
|
||||
if (link.linkType === "section" || link.linkType === "document") {
|
||||
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") {
|
||||
href = `${endpoint}/public/attachments/${orgId}/${link.targetId}`;
|
||||
}
|
||||
if (link.linkType === "document") {
|
||||
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>`;
|
||||
}
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Keyword search results - docs, section, files
|
||||
|
||||
link handler
|
||||
- implement link redirect handler --
|
||||
- for documents: client-side detect
|
||||
- for sections:
|
||||
- for attachments: direct link
|
||||
-
|
||||
The link id's get ZERO'd in Page.Body whenever:
|
||||
- doc is moved to different space
|
||||
- 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)
|
||||
|
||||
onDelete document/section/file:
|
||||
- mark link table row as ORPHAN
|
||||
- doc view: meta data fetch to load orphaned content
|
||||
link/section/{documentId}/{sectionId}:
|
||||
- if ZERO id show notification
|
||||
- store previous positions -- localStorage, dropdown menu?
|
||||
|
||||
Keyword search results - docs, section, files
|
||||
Markdown editor support
|
||||
|
||||
we should not redirect to a link that is in the same document!
|
||||
what happens if we delete attachment?
|
||||
UpdatePage(): find and persist links from saved content
|
||||
|
||||
1. We need to deal with links server-side
|
||||
2. We need to click on links in the browser and 'navigate' to linked content
|
||||
|
||||
editor.insertContent(' <b>It\'s my button!</b> ');
|
||||
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 () {
|
||||
this._super(...arguments);
|
||||
|
||||
this.set('isMac', is.mac());
|
||||
this.set('isMobile', is.mobile());
|
||||
},
|
||||
|
|
|
@ -196,7 +196,7 @@ $i: 100;
|
|||
.width-#{$i} {
|
||||
width: #{$i}#{"%"};
|
||||
}
|
||||
$i: $i - 2;
|
||||
$i: $i - 1;
|
||||
}
|
||||
|
||||
.no-outline {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
.edit-tools {
|
||||
margin: 15px 0 0 20px;
|
||||
margin: 0 0 0 20px;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.content-linker-dialog {
|
||||
width: 300px;
|
||||
width: 350px;
|
||||
height: 400px;
|
||||
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-tooltip";
|
||||
@import "widget-checkbox";
|
||||
@import "widget-tab";
|
||||
|
|
|
@ -4,15 +4,14 @@
|
|||
<i class="material-icons color-white">link</i>
|
||||
</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">
|
||||
<form>
|
||||
<div class="input-control">
|
||||
<label>Insert Link</label>
|
||||
<div class="tip">Choose content below or search</div>
|
||||
{{focus-input id="content-linker-search" type="input" value=keywords placeholder="keyword search"}}
|
||||
</div>
|
||||
{{#if hasSections}}
|
||||
{{ui/ui-tab tabs=tabs onTabSelect=(action 'onTabSelect')}}
|
||||
|
||||
<div class="margin-top-40" />
|
||||
|
||||
{{#if showSections}}
|
||||
<ul class="link-list">
|
||||
{{#each candidates.pages as |p|}}
|
||||
<li class="link-item" {{ action 'setSelection' p }}>
|
||||
|
@ -23,7 +22,8 @@
|
|||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
{{#if hasAttachments}}
|
||||
|
||||
{{#if showAttachments}}
|
||||
<ul class="link-list">
|
||||
{{#each candidates.attachments as |a|}}
|
||||
<li class="link-item" {{ action 'setSelection' a }}>
|
||||
|
@ -35,6 +35,15 @@
|
|||
{{/each}}
|
||||
</ul>
|
||||
{{/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 clearfix" />
|
||||
</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>
|
Loading…
Add table
Add a link
Reference in a new issue