1
0
Fork 0
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:
Harvey Kandola 2016-10-26 17:31:05 -07:00
parent c27de6bcab
commit 899b4f978c
27 changed files with 265 additions and 83 deletions

View file

@ -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', {

View file

@ -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);
}
}
});

View 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'));
}
}
});

View file

@ -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
};
};

View 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
};

View file

@ -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');
}
});
});

View file

@ -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}}

View file

@ -25,6 +25,7 @@ export default Ember.Service.extend({
appId: config.APP.intercomKey,
init() {
this._super(...arguments);
this.start();
},

View file

@ -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);
}
});

View file

@ -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() {

View file

@ -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('&nbsp;<b>It\'s my button!</b>&nbsp;');
Selects the first paragraph found
tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);
permission checks:
can view space
can view document
*/

View file

@ -41,6 +41,8 @@ export default SimpleAuthSession.extend({
}),
init: function () {
this._super(...arguments);
this.set('isMac', is.mac());
this.set('isMobile', is.mobile());
},

View file

@ -196,7 +196,7 @@ $i: 100;
.width-#{$i} {
width: #{$i}#{"%"};
}
$i: $i - 2;
$i: $i - 1;
}
.no-outline {

View file

@ -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;

View 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;
}
}

View file

@ -71,3 +71,4 @@
@import "widget-table";
@import "widget-tooltip";
@import "widget-checkbox";
@import "widget-tab";

View file

@ -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>

View 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>