1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 13:19:43 +02:00

upgraded Ember and Bootstrap, merged changes

This commit is contained in:
sauls8t 2018-01-22 10:31:03 +00:00
parent b4fd42da38
commit 5dd7d9c181
114 changed files with 9814 additions and 1361 deletions

View file

@ -4,17 +4,46 @@ module.exports = {
ecmaVersion: 2017, ecmaVersion: 2017,
sourceType: 'module' sourceType: 'module'
}, },
extends: 'eslint:recommended', plugins: [
'ember'
],
extends: [
'eslint:recommended',
'plugin:ember/recommended'
],
env: { env: {
browser: true, browser: true
jquery: true,
qunit: true,
embertest: true
}, },
rules: { rules: {
}, },
overrides: [
// node files
{
files: [
'testem.js',
'ember-cli-build.js',
'config/**/*.js'
],
parserOptions: {
sourceType: 'script',
ecmaVersion: 2015
},
env: {
browser: false,
node: true
}
},
// test files
{
files: ['tests/**/*.js'],
excludedFiles: ['tests/dummy/**/*.js'],
env: {
embertest: true
}
}
],
globals: { globals: {
"$": true,
"is": true, "is": true,
"_": true, "_": true,
"tinymce": true, "tinymce": true,

View file

@ -4,18 +4,26 @@ node_js:
- "6" - "6"
sudo: false sudo: false
dist: trusty
addons:
chrome: stable
cache: cache:
directories: yarn: true
- $HOME/.npm
env:
global:
# See https://git.io/vdao3 for details.
- JOBS=1
before_install: before_install:
- npm config set spin false - curl -o- -L https://yarnpkg.com/install.sh | bash
- npm install -g phantomjs-prebuilt - export PATH=$HOME/.yarn/bin:$PATH
- phantomjs --version
install: install:
- npm install - yarn install --non-interactive
script: script:
- npm test - yarn lint:js
- yarn test

View file

@ -27,7 +27,10 @@ export default Component.extend({
KeycloakPublicKeyError: empty('keycloakConfig.publicKey'), KeycloakPublicKeyError: empty('keycloakConfig.publicKey'),
KeycloakAdminUserError: empty('keycloakConfig.adminUser'), KeycloakAdminUserError: empty('keycloakConfig.adminUser'),
KeycloakAdminPasswordError: empty('keycloakConfig.adminPassword'), KeycloakAdminPasswordError: empty('keycloakConfig.adminPassword'),
keycloakConfig: {
init() {
this._super(...arguments);
this.keycloakConfig = {
url: '', url: '',
realm: '', realm: '',
clientId: '', clientId: '',
@ -37,6 +40,7 @@ export default Component.extend({
group: '', group: '',
disableLogout: false, disableLogout: false,
defaultPermissionAddSpace: false defaultPermissionAddSpace: false
};
}, },
didReceiveAttrs() { didReceiveAttrs() {

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { empty } from '@ember/object/computed'; import { empty } from '@ember/object/computed';
import Component from '@ember/component'; import Component from '@ember/component';

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import Component from '@ember/component'; import Component from '@ember/component';
import { schedule, debounce } from '@ember/runloop'; import { schedule, debounce } from '@ember/runloop';
import AuthProvider from '../../mixins/auth'; import AuthProvider from '../../mixins/auth';
@ -17,13 +18,17 @@ import ModalMixin from '../../mixins/modal';
export default Component.extend(AuthProvider, ModalMixin, { export default Component.extend(AuthProvider, ModalMixin, {
editUser: null, editUser: null,
deleteUser: null, deleteUser: null,
password: {},
filter: '', filter: '',
filteredUsers: [],
selectedUsers: [],
hasSelectedUsers: false, hasSelectedUsers: false,
showDeleteDialog: false, showDeleteDialog: false,
init() {
this._super(...arguments);
this.password = {};
this.filteredUsers = [];
this.selectedUsers = [];
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
@ -74,25 +79,29 @@ export default Component.extend(AuthProvider, ModalMixin, {
toggleActive(id) { toggleActive(id) {
let user = this.users.findBy("id", id); let user = this.users.findBy("id", id);
user.set('active', !user.get('active')); user.set('active', !user.get('active'));
this.attrs.onSave(user); let cb = this.get('onSave');
cb(user);
}, },
toggleEditor(id) { toggleEditor(id) {
let user = this.users.findBy("id", id); let user = this.users.findBy("id", id);
user.set('editor', !user.get('editor')); user.set('editor', !user.get('editor'));
this.attrs.onSave(user); let cb = this.get('onSave');
cb(user);
}, },
toggleAdmin(id) { toggleAdmin(id) {
let user = this.users.findBy("id", id); let user = this.users.findBy("id", id);
user.set('admin', !user.get('admin')); user.set('admin', !user.get('admin'));
this.attrs.onSave(user); let cb = this.get('onSave');
cb(user);
}, },
toggleUsers(id) { toggleUsers(id) {
let user = this.users.findBy("id", id); let user = this.users.findBy("id", id);
user.set('viewUsers', !user.get('viewUsers')); user.set('viewUsers', !user.get('viewUsers'));
this.attrs.onSave(user); let cb = this.get('onSave');
cb(user);
}, },
onShowEdit(id) { onShowEdit(id) {
@ -135,11 +144,14 @@ export default Component.extend(AuthProvider, ModalMixin, {
$('#edit-user-modal').modal('hide'); $('#edit-user-modal').modal('hide');
$('#edit-user-modal').modal('dispose'); $('#edit-user-modal').modal('dispose');
this.attrs.onSave(user); let cb = this.get('onSave');
cb(user);
if (is.not.empty(password.password) && is.not.empty(password.confirmation) && if (is.not.empty(password.password) && is.not.empty(password.confirmation) &&
password.password === password.confirmation) { password.password === password.confirmation) {
this.attrs.onPassword(user, password.password);
let pw = this.get('onPassword');
pw(user, password.password);
} }
}, },
@ -153,7 +165,9 @@ export default Component.extend(AuthProvider, ModalMixin, {
this.set('selectedUsers', []); this.set('selectedUsers', []);
this.set('hasSelectedUsers', false); this.set('hasSelectedUsers', false);
this.attrs.onDelete(this.get('deleteUser.id'));
let cb = this.get('onDelete');
cb(this.get('deleteUser.id'));
return true; return true;
}, },
@ -162,7 +176,8 @@ export default Component.extend(AuthProvider, ModalMixin, {
let su = this.get('selectedUsers'); let su = this.get('selectedUsers');
su.forEach(userId => { su.forEach(userId => {
this.attrs.onDelete(userId); let cb = this.get('onDelete');
cb(userId);
}); });
this.set('selectedUsers', []); this.set('selectedUsers', []);

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { empty, and } from '@ember/object/computed'; import { empty, and } from '@ember/object/computed';
import Component from '@ember/component'; import Component from '@ember/component';
import { isEmpty } from '@ember/utils'; import { isEmpty } from '@ember/utils';
@ -16,7 +17,6 @@ import { get, set } from '@ember/object';
import AuthProvider from '../../mixins/auth'; import AuthProvider from '../../mixins/auth';
export default Component.extend(AuthProvider, { export default Component.extend(AuthProvider, {
newUser: { firstname: "", lastname: "", email: "", active: true },
firstnameEmpty: empty('newUser.firstname'), firstnameEmpty: empty('newUser.firstname'),
lastnameEmpty: empty('newUser.lastname'), lastnameEmpty: empty('newUser.lastname'),
emailEmpty: empty('newUser.email'), emailEmpty: empty('newUser.email'),
@ -24,6 +24,11 @@ export default Component.extend(AuthProvider, {
hasLastnameEmptyError: and('lastnameEmpty', 'lastnameError'), hasLastnameEmptyError: and('lastnameEmpty', 'lastnameError'),
hasEmailEmptyError: and('emailEmpty', 'emailError'), hasEmailEmptyError: and('emailEmpty', 'emailError'),
init() {
this._super(...arguments);
this.newUser = { firstname: "", lastname: "", email: "", active: true };
},
actions: { actions: {
add() { add() {
if (isEmpty(this.get('newUser.firstname'))) { if (isEmpty(this.get('newUser.firstname'))) {

View file

@ -44,11 +44,13 @@ export default Component.extend({
actions: { actions: {
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(page, meta) { onAction(page, meta) {
this.attrs.onAction(page, meta); let cb = this.get('onAction');
cb(page, meta);
} }
} }
}); });

View file

@ -20,25 +20,17 @@ export default Component.extend(ModalMixin, TooltipMixin, {
link: service(), link: service(),
linkName: '', linkName: '',
selection: null, selection: null,
tab1Selected: true, tab1Selected: true,
tab2Selected: false, tab2Selected: false,
tab3Selected: false, tab3Selected: false,
showSections: computed('tab1Selected', function() { return this.get('tab1Selected'); }), showSections: computed('tab1Selected', function() { return this.get('tab1Selected'); }),
showAttachments: computed('tab2Selected', function() { return this.get('tab2Selected'); }), showAttachments: computed('tab2Selected', function() { return this.get('tab2Selected'); }),
showSearch: computed('tab3Selected', function() { return this.get('tab3Selected'); }), showSearch: computed('tab3Selected', function() { return this.get('tab3Selected'); }),
keywords: '', keywords: '',
matches: {
documents: [],
pages: [],
attachments: []
},
hasMatches: computed('matches', function () { hasMatches: computed('matches', function () {
let m = this.get('matches'); let m = this.get('matches');
return m.documents.length || m.pages.length || m.attachments.length; return m.documents.length || m.pages.length || m.attachments.length;
}), }),
modalId: computed('page', function() { return '#content-linker-modal-' + this.get('page.id'); }), modalId: computed('page', function() { return '#content-linker-modal-' + this.get('page.id'); }),
showModal: false, showModal: false,
onToggle: function() { onToggle: function() {
@ -63,17 +55,23 @@ export default Component.extend(ModalMixin, TooltipMixin, {
this.modalOpen(modalId, {show: true}); this.modalOpen(modalId, {show: true});
}.observes('showModal'), }.observes('showModal'),
init() {
this._super(...arguments);
this.matches = {
documents: [],
pages: [],
attachments: []
};
},
didRender() { didRender() {
this._super(...arguments); this._super(...arguments);
this.renderTooltips(); this.renderTooltips();
}, },
willDestroyElement() { willDestroyElement() {
this._super(...arguments); this._super(...arguments);
this.removeTooltips(); this.removeTooltips();
this.modalClose(this.get('modalId')); this.modalClose(this.get('modalId'));
}, },

View file

@ -18,11 +18,13 @@ export default Component.extend({
actions: { actions: {
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(page, meta) { onAction(page, meta) {
this.attrs.onAction(page, meta); let cb = this.get('onAction');
cb(page, meta);
} }
} }
}); });

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { empty } from '@ember/object/computed'; import { empty } from '@ember/object/computed';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { schedule } from '@ember/runloop'; import { schedule } from '@ember/runloop';
@ -62,7 +63,8 @@ export default Component.extend({
this.set('document.excerpt', this.get('docExcerpt')); this.set('document.excerpt', this.get('docExcerpt'));
this.set('editMode', false); this.set('editMode', false);
this.attrs.onSaveDocument(this.get('document')); let cb = this.get('onSaveDocument');
cb(this.get('document'));
}, },
onCancel() { onCancel() {

View file

@ -9,7 +9,9 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { notEmpty } from '@ember/object/computed';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Component from '@ember/component'; import Component from '@ember/component';
import { A } from "@ember/array" import { A } from "@ember/array"
@ -19,12 +21,9 @@ export default Component.extend({
documentService: service('document'), documentService: service('document'),
categoryService: service('category'), categoryService: service('category'),
sessionService: service('session'), sessionService: service('session'),
maxTags: 3,
categories: A([]), categories: A([]),
newCategory: '', newCategory: '',
tagz: A([]),
tagzModal: A([]),
newTag: '',
showCategoryModal: false, showCategoryModal: false,
hasCategories: computed('categories', function() { hasCategories: computed('categories', function() {
return this.get('categories').length > 0; return this.get('categories').length > 0;
@ -36,9 +35,63 @@ export default Component.extend({
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage'); return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
}), }),
maxTags: 3,
tagz: A([]),
tagzModal: A([]),
newTag: '',
contributorMsg: '',
approverMsg: '',
userChanges: notEmpty('contributorMsg'),
isApprover: computed('permissions', function() {
return this.get('permissions.documentApprove');
}),
changeControlMsg: computed('document.protection', function() {
let p = this.get('document.protection');
let constants = this.get('constants');
let msg = '';
switch (p) {
case constants.ProtectionType.None:
msg = constants.ProtectionType.NoneLabel;
break;
case constants.ProtectionType.Lock:
msg = constants.ProtectionType.LockLabel;
break;
case constants.ProtectionType.Review:
msg = constants.ProtectionType.ReviewLabel;
break;
}
return msg;
}),
approvalMsg: computed('document.{protection,approval}', function() {
let p = this.get('document.protection');
let a = this.get('document.approval');
let constants = this.get('constants');
let msg = '';
if (p === constants.ProtectionType.Review) {
switch (a) {
case constants.ApprovalType.Anybody:
msg = constants.ApprovalType.AnybodyLabel;
break;
case constants.ApprovalType.Majority:
msg = constants.ApprovalType.MajorityLabel;
break;
case constants.ApprovalType.Unanimous:
msg = constants.ApprovalType.UnanimousLabel;
break;
}
}
return msg;
}),
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
this.load(); this.load();
this.workflowStatus();
}, },
didInsertElement() { didInsertElement() {
@ -100,6 +153,39 @@ export default Component.extend({
this.set('tagz', A(tagz)); this.set('tagz', A(tagz));
}, },
workflowStatus() {
let pages = this.get('pages');
let contributorMsg = '';
let userPendingCount = 0;
let userReviewCount = 0;
let userRejectedCount = 0;
let approverMsg = '';
let approverPendingCount = 0;
let approverReviewCount = 0;
let approverRejectedCount = 0;
pages.forEach((item) => {
if (item.get('userHasChangePending')) userPendingCount+=1;
if (item.get('userHasChangeAwaitingReview')) userReviewCount+=1;
if (item.get('userHasChangeRejected')) userRejectedCount+=1;
if (item.get('changePending')) approverPendingCount+=1;
if (item.get('changeAwaitingReview')) approverReviewCount+=1;
if (item.get('changeRejected')) approverRejectedCount+=1;
});
if (userPendingCount > 0 || userReviewCount > 0 || userRejectedCount > 0) {
let label = userPendingCount === 1 ? 'change' : 'changes';
contributorMsg = `${userPendingCount} ${label} progressing, ${userReviewCount} awaiting review, ${userRejectedCount} rejected`;
}
this.set('contributorMsg', contributorMsg);
if (approverPendingCount > 0 || approverReviewCount > 0 || approverRejectedCount > 0) {
let label = approverPendingCount === 1 ? 'change' : 'changes';
approverMsg = `${approverPendingCount} ${label} progressing, ${approverReviewCount} awaiting review, ${approverRejectedCount} rejected`;
}
this.set('approverMsg', approverMsg);
},
actions: { actions: {
onShowCategoryModal() { onShowCategoryModal() {
this.set('showCategoryModal', true); this.set('showCategoryModal', true);
@ -179,13 +265,15 @@ export default Component.extend({
let doc = this.get('document'); let doc = this.get('document');
doc.set('tags', save); doc.set('tags', save);
this.attrs.onSaveDocument(doc);
let cb = this.get('onSaveDocument');
cb(doc);
this.load(); this.load();
this.set('newTag', ''); this.set('newTag', '');
$('#document-tags-modal').modal('hide'); $('#document-tags-modal').modal('hide');
$('#document-tags-modal').modal('dispose'); $('#document-tags-modal').modal('dispose');
}, }
} }
}); });

View file

@ -17,46 +17,55 @@ export default Component.extend(TooltipMixin, {
documentService: service('document'), documentService: service('document'),
sectionService: service('section'), sectionService: service('section'),
editMode: false, editMode: false,
editPage: null,
editMeta: null,
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
if (this.get('isDestroyed') || this.get('isDestroying')) return;
if (this.get('toEdit') === this.get('page.id') && this.get('permissions.documentEdit')) this.send('onEdit');
if (this.get('isDestroyed') || this.get('isDestroying')) { if (this.get('session.authenticated')) {
return; this.workflow();
} }
},
let page = this.get('page'); workflow() {
this.set('editPage', this.get('page'));
this.get('documentService').getPageMeta(page.get('documentId'), page.get('id')).then((meta) => { this.set('editMeta', this.get('meta'));
if (this.get('isDestroyed') || this.get('isDestroying')) {
return;
}
this.set('meta', meta);
if (this.get('toEdit') === this.get('page.id') && this.get('permissions.documentEdit')) {
this.send('onEdit');
}
});
}, },
actions: { actions: {
onSavePage(page, meta) { onSavePage(page, meta) {
this.set('page', page); let constants = this.get('constants');
this.set('meta', meta);
if (this.get('document.protection') === constants.ProtectionType.Review) {
if (this.get('page.status') === constants.ChangeState.Published) {
page.set('relativeId', this.get('page.id'));
}
if (this.get('page.status') === constants.ChangeState.PendingNew) {
page.set('relativeId', '');
}
}
this.set('editMode', false); this.set('editMode', false);
this.get('onSavePage')(page, meta); let cb = this.get('onSavePage');
cb(page, meta);
}, },
onSavePageAsBlock(block) { onSavePageAsBlock(block) {
this.attrs.onSavePageAsBlock(block); let cb = this.get('onSavePageAsBlock');
cb(block);
}, },
onCopyPage(documentId) { onCopyPage(documentId) {
this.attrs.onCopyPage(this.get('page.id'), documentId); let cb = this.get('onCopyPage');
cb(this.get('page.id'), documentId);
}, },
onMovePage(documentId) { onMovePage(documentId) {
this.attrs.onMovePage(this.get('page.id'), documentId); let cb = this.get('onMovePage');
cb(this.get('page.id'), documentId);
}, },
onDeletePage(deleteChildren) { onDeletePage(deleteChildren) {
@ -72,16 +81,14 @@ export default Component.extend(TooltipMixin, {
children: deleteChildren children: deleteChildren
}; };
this.attrs.onDeletePage(params); let cb = this.get('onDeletePage');
cb(params);
}, },
// Calculate if user is editing page or a pending change as per approval process
onEdit() { onEdit() {
if (this.get('editMode')) { if (this.get('editMode')) return;
return;
}
this.get('toEdit', ''); this.get('toEdit', '');
// this.set('pageId', this.get('page.id'));
this.set('editMode', true); this.set('editMode', true);
}, },

View file

@ -9,66 +9,76 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { schedule } from '@ember/runloop'; import { schedule } from '@ember/runloop';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Component from '@ember/component'; import Component from '@ember/component';
import tocUtil from '../../utils/toc'; import tocUtil from '../../utils/toc';
import TooltipMixin from '../../mixins/tooltip';
export default Component.extend({ export default Component.extend(TooltipMixin, {
documentService: service('document'), documentService: service('document'),
document: {}, isDesktop: false,
folder: {}, emptyState: computed('pages', function () {
pages: [], return this.get('pages.length') === 0;
currentPageId: '', }),
state: { canEdit: computed('permssions', 'document', function() {
let constants = this.get('constants');
let permissions = this.get('permissions');
let authenticated = this.get('session.authenticated');
let notEmpty = this.get('pages.length') > 0;
if (notEmpty && authenticated && permissions.get('documentEdit') && this.get('document.protection') === constants.ProtectionType.None) return true;
if (notEmpty && authenticated && permissions.get('documentApprove') && this.get('document.protection') === constants.ProtectionType.Review) return true;
return false;
}),
init() {
this._super(...arguments);
this.state = {
actionablePage: false, actionablePage: false,
upDisabled: true, upDisabled: true,
downDisabled: true, downDisabled: true,
indentDisabled: true, indentDisabled: true,
outdentDisabled: true outdentDisabled: true,
pageId: ''
};
}, },
emptyState: computed('pages', function () {
return this.get('pages.length') === 0;
}),
isDesktop: false,
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
this.setState(this.get('currentPageId')); let cp = this.get('currentPageId');
this.setState(is.empty(cp) ? '' : cp);
}, },
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
this.eventBus.subscribe('documentPageAdded', this, 'onDocumentPageAdded');
this.eventBus.subscribe('resized', this, 'setSize');
this.setSize(); this.setSize();
this.eventBus.subscribe('documentPageAdded', this, 'onDocumentPageAdded'); this.renderTooltips();
this.eventBus.subscribe('resized', this, 'onResize');
this.attachResizer();
}, },
willDestroyElement() { willDestroyElement() {
this._super(...arguments); this._super(...arguments);
this.eventBus.unsubscribe('documentPageAdded'); this.eventBus.unsubscribe('documentPageAdded');
this.eventBus.unsubscribe('resized'); this.eventBus.unsubscribe('resized');
let t = '#doc-toc'; let t = '#doc-toc';
if (interact.isSet(t)) { if (interact.isSet(t)) interact(t).unset();
interact(t).unset(); this.removeTooltips();
}
}, },
onDocumentPageAdded(pageId) { onDocumentPageAdded(pageId) {
this.send('onEntryClick', pageId);
this.setSize();
},
onResize() {
this.setSize(); this.setSize();
this.send('onGotoPage', pageId);
}, },
setSize() { setSize() {
schedule('afterRender', () => {
let isDesktop = $(window).width() >= 1800; let isDesktop = $(window).width() >= 1800;
this.set('isDesktop', isDesktop); this.set('isDesktop', isDesktop);
@ -90,6 +100,8 @@ export default Component.extend({
'height': 'auto', 'height': 'auto',
'transform': '', 'transform': '',
}); });
this.attachResizer();
} }
} else { } else {
$("#doc-toc").css({ $("#doc-toc").css({
@ -100,10 +112,16 @@ export default Component.extend({
'transform': 'none', 'transform': 'none',
}); });
} }
});
}, },
attachResizer() { attachResizer() {
schedule('afterRender', () => { schedule('afterRender', () => {
let t = '#doc-toc';
if (interact.isSet(t)) {
interact(t).unset();
}
interact('#doc-toc') interact('#doc-toc')
.draggable({ .draggable({
autoScroll: true, autoScroll: true,
@ -166,15 +184,12 @@ export default Component.extend({
}, },
// Controls what user can do with the toc (left sidebar) // Controls what user can do with the toc (left sidebar)
// Identifies the target pages
setState(pageId) { setState(pageId) {
this.set('currentPageId', pageId);
let toc = this.get('pages'); let toc = this.get('pages');
let page = _.findWhere(toc, { id: pageId }); let page = _.find(toc, function(i) { return i.get('page.id') === pageId; });
let state = tocUtil.getState(toc, page); let state = tocUtil.getState(toc, is.not.undefined(page) ? page.get('page') : page);
if (!this.get('permissions.documentEdit') || is.empty(pageId)) { if (!this.get('canEdit')) {
state.actionablePage = false; state.actionablePage = false;
state.upDisabled = state.downDisabled = state.indentDisabled = state.outdentDisabled = true; state.upDisabled = state.downDisabled = state.indentDisabled = state.outdentDisabled = true;
} }
@ -185,77 +200,77 @@ export default Component.extend({
actions: { actions: {
// Page up -- above pages shunt down // Page up -- above pages shunt down
pageUp() { pageUp() {
if (this.get('state.upDisabled')) { let state = this.get('state');
if (state.upDisabled || this.get('document.protection') !== this.get('constants').ProtectionType.None) {
return; return;
} }
let state = this.get('state');
let pages = this.get('pages'); let pages = this.get('pages');
let page = _.findWhere(pages, { id: this.get('currentPageId') }); let page = _.find(pages, function(i) { return i.get('page.id') === state.pageId; });
let pendingChanges = tocUtil.moveUp(state, pages, page); if (is.not.undefined(page)) page = page.get('page');
let pendingChanges = tocUtil.moveUp(state, pages, page);
if (pendingChanges.length > 0) { if (pendingChanges.length > 0) {
this.attrs.onPageSequenceChange(pendingChanges); let cb = this.get('onPageSequenceChange');
cb(state.pageId, pendingChanges);
} }
}, },
// Move down -- pages below shift up // Move down -- pages below shift up
pageDown() { pageDown() {
if (this.get('state.downDisabled')) { if (!this.get('canEdit')) return;
return;
}
let state = this.get('state'); let state = this.get('state');
var pages = this.get('pages'); let pages = this.get('pages');
var page = _.findWhere(pages, { id: this.get('currentPageId') }); let page = _.find(pages, function(i) { return i.get('page.id') === state.pageId; });
let pendingChanges = tocUtil.moveDown(state, pages, page); if (is.not.undefined(page)) page = page.get('page');
let pendingChanges = tocUtil.moveDown(state, pages, page);
if (pendingChanges.length > 0) { if (pendingChanges.length > 0) {
this.attrs.onPageSequenceChange(pendingChanges); let cb = this.get('onPageSequenceChange');
cb(state.pageId, pendingChanges);
} }
}, },
// Indent -- changes a page from H2 to H3, etc. // Indent -- changes a page from H2 to H3, etc.
pageIndent() { pageIndent() {
if (this.get('state.indentDisabled')) { if (!this.get('canEdit')) return;
return;
}
let state = this.get('state'); let state = this.get('state');
var pages = this.get('pages'); let pages = this.get('pages');
var page = _.findWhere(pages, { id: this.get('currentPageId') }); let page = _.find(pages, function(i) { return i.get('page.id') === state.pageId; });
let pendingChanges = tocUtil.indent(state, pages, page); if (is.not.undefined(page)) page = page.get('page');
let pendingChanges = tocUtil.indent(state, pages, page);
if (pendingChanges.length > 0) { if (pendingChanges.length > 0) {
this.attrs.onPageLevelChange(pendingChanges); let cb = this.get('onPageLevelChange');
cb(state.pageId, pendingChanges);
} }
}, },
// Outdent -- changes a page from H3 to H2, etc. // Outdent -- changes a page from H3 to H2, etc.
pageOutdent() { pageOutdent() {
if (this.get('state.outdentDisabled')) { if (!this.get('canEdit')) return;
return;
}
let state = this.get('state'); let state = this.get('state');
var pages = this.get('pages'); let pages = this.get('pages');
var page = _.findWhere(pages, { id: this.get('currentPageId') }); let page = _.find(pages, function(i) { return i.get('page.id') === state.pageId; });
let pendingChanges = tocUtil.outdent(state, pages, page); if (is.not.undefined(page)) page = page.get('page');
let pendingChanges = tocUtil.outdent(state, pages, page);
if (pendingChanges.length > 0) { if (pendingChanges.length > 0) {
this.attrs.onPageLevelChange(pendingChanges); let cb = this.get('onPageLevelChange');
cb(state.pageId, pendingChanges);
} }
}, },
onEntryClick(id) { onGotoPage(id) {
if (id !== '') { if (id === '') return;
let jumpTo = "#page-" + id;
this.set('tab', 'content');
if (!$(jumpTo).inView()) {
$(jumpTo).velocity("scroll", { duration: 250, offset: -100 });
}
this.setState(id); this.setState(id);
}
let cb = this.get('onShowPage');
cb(id);
} }
} }
}); });

View file

@ -9,66 +9,72 @@
// //
// https://documize.com // https://documize.com
import Component from '@ember/component'; import $ from 'jquery';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { debounce } from '@ember/runloop';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { A } from "@ember/array"
import ModalMixin from '../../mixins/modal'; import ModalMixin from '../../mixins/modal';
import Component from '@ember/component';
export default Component.extend(ModalMixin, { export default Component.extend(ModalMixin, {
documentService: service('document'), documentService: service('document'),
searchService: service('search'),
router: service(),
deleteChildren: false, deleteChildren: false,
blockTitle: "", blockTitle: "",
blockExcerpt: "", blockExcerpt: "",
documentList: A([]), //includes the current document canEdit: false,
documentListOthers: A([]), //excludes the current document canDelete: false,
hasMenuPermissions: computed('permissions', function() { canMove: false,
let permissions = this.get('permissions'); docSearchFilter: '',
return permissions.get('documentDelete') || permissions.get('documentCopy') || onKeywordChange: function () {
permissions.get('documentMove') || permissions.get('documentTemplate'); debounce(this, this.searchDocs, 750);
}.observes('docSearchFilter'),
emptySearch: computed('docSearchResults', function() {
return this.get('docSearchResults.length') === 0;
}), }),
hasMenuPermissions: computed('permissions', 'userPendingItem', 'canEdit', 'canMove', 'canDelete', function() {
let permissions = this.get('permissions');
return permissions.get('documentCopy') || permissions.get('documentTemplate') ||
this.get('canEdit') || this.get('canMove') || this.get('canDelete');
}),
init() {
this._super(...arguments);
this.docSearchResults = [];
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
// Fetch document targets once
if (this.get('documentList').length > 0) {
return;
}
this.modalInputFocus('#publish-page-modal-' + this.get('page.id'), '#block-title-' + this.get('page.id')); this.modalInputFocus('#publish-page-modal-' + this.get('page.id'), '#block-title-' + this.get('page.id'));
this.load();
}, },
load() { searchDocs() {
let permissions = this.get('permissions'); let payload = { keywords: this.get('docSearchFilter').trim(), doc: true };
if (permissions.get('documentMove') || permissions.get('documentCopy')) { if (payload.keywords.length == 0) return;
this.get('documentService').getPageMoveCopyTargets().then((d) => {
let me = this.get('document');
d.forEach((i) => { this.get('searchService').find(payload).then((response)=> {
i.set('selected', false); this.set('docSearchResults', response);
}); });
if (this.get('isDestroyed') || this.get('isDestroying')) {
return;
}
this.set('documentList', A(d));
this.set('documentListOthers', A(d.filter((item) => item.get('id') !== me.get('id'))));
});
}
}, },
actions: { actions: {
onEdit() { onEdit() {
this.attrs.onEdit(); let page = this.get('page');
if (page.get('pageType') == this.get('constants').PageType.Tab) {
this.get('router').transitionTo('document.section', page.get('id'));
} else {
let cb = this.get('onEdit');
cb();
}
}, },
onDeletePage() { onDeletePage() {
this.attrs.onDeletePage(this.get('deleteChildren')); let cb = this.get('onDeletePage');
cb(this.get('deleteChildren'));
this.load();
this.modalClose('#delete-page-modal-' + this.get('page.id')); this.modalClose('#delete-page-modal-' + this.get('page.id'));
}, },
@ -103,57 +109,60 @@ export default Component.extend(ModalMixin, {
externalSource: pm.get('externalSource') externalSource: pm.get('externalSource')
}; };
this.attrs.onSavePageAsBlock(block); let cb = this.get('onSavePageAsBlock');
cb(block);
this.set('blockTitle', ''); this.set('blockTitle', '');
this.set('blockExcerpt', ''); this.set('blockExcerpt', '');
$(titleElem).removeClass('is-invalid'); $(titleElem).removeClass('is-invalid');
$(excerptElem).removeClass('is-invalid'); $(excerptElem).removeClass('is-invalid');
this.load();
this.modalClose('#publish-page-modal-' + this.get('page.id')); this.modalClose('#publish-page-modal-' + this.get('page.id'));
let refresh = this.get('refresh');
refresh();
}); });
}, },
onSelectSearchResult(documentId) {
let results = this.get('docSearchResults');
results.forEach((d) => {
d.set('selected', d.get('documentId') === documentId);
});
this.set('docSearchResults', results);
},
onCopyPage() { onCopyPage() {
// can't proceed if no data let item = this.get('docSearchResults').findBy('selected', true);
if (this.get('documentList.length') === 0) { let documentId = is.not.undefined(item) ? item.get('documentId') : '';
return;
}
let targetDocumentId = this.get('documentList').findBy('selected', true).get('id'); if (is.empty(documentId)) return;
// fall back to self
if (is.null(targetDocumentId)) {
targetDocumentId = this.get('document.id');
}
this.attrs.onCopyPage(targetDocumentId);
this.load();
this.modalClose('#copy-page-modal-' + this.get('page.id')); this.modalClose('#copy-page-modal-' + this.get('page.id'));
let cb = this.get('onCopyPage');
cb(documentId);
let refresh = this.get('refresh');
refresh();
}, },
onMovePage() { onMovePage() {
// can't proceed if no data let item = this.get('docSearchResults').findBy('selected', true);
if (this.get('documentListOthers.length') === 0) { let documentId = is.not.undefined(item) ? item.get('documentId') : '';
return;
}
let targetDocumentId = this.get('documentListOthers').findBy('selected', true).get('id'); if (is.empty(documentId)) return;
// fall back to first document // can't move into self
if (is.null(targetDocumentId)) { if (documentId === this.get('document.id')) return;
targetDocumentId = this.get('documentListOthers')[0].get('id');
}
this.attrs.onMovePage(targetDocumentId);
this.load();
this.modalClose('#move-page-modal-' + this.get('page.id')); this.modalClose('#move-page-modal-' + this.get('page.id'));
}
let cb = this.get('onMovePage');
cb(documentId);
let refresh = this.get('refresh');
refresh();
},
} }
}); });

View file

@ -18,24 +18,28 @@ export default Component.extend({
documentService: service('document'), documentService: service('document'),
appMeta: service(), appMeta: service(),
hasAttachments: notEmpty('files'), hasAttachments: notEmpty('files'),
deleteAttachment: {
id: "",
name: "",
},
canShow: computed('permissions', 'files', function() { canShow: computed('permissions', 'files', function() {
return this.get('files.length') > 0 || this.get('permissions.documentEdit'); return this.get('files.length') > 0 || this.get('permissions.documentEdit');
}), }),
canEdit: computed('permissions', 'document.protection', function() {
return this.get('document.protection') !== this.get('constants').ProtectionType.Lock && this.get('permissions.documentEdit');
}),
showDialog: false, showDialog: false,
init() { init() {
this._super(...arguments); this._super(...arguments);
this.getAttachments(); this.getAttachments();
this.deleteAttachment = {
id: "",
name: "",
};
}, },
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
if (!this.get('permissions.documentEdit')) { if (!this.get('permissions.documentEdit') || this.get('document.protection') === this.get('constants').ProtectionType.Lock) {
return; return;
} }
@ -78,10 +82,6 @@ export default Component.extend({
this.set('drop', dzone); this.set('drop', dzone);
}, },
willDestroyElement() {
this._super(...arguments);
},
getAttachments() { getAttachments() {
this.get('documentService').getAttachments(this.get('document.id')).then((files) => { this.get('documentService').getAttachments(this.get('document.id')).then((files) => {
this.set('files', files); this.set('files', files);

View file

@ -9,11 +9,14 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { notEmpty, empty } from '@ember/object/computed'; import { notEmpty, empty } from '@ember/object/computed';
import { schedule } from '@ember/runloop'; import { schedule } from '@ember/runloop';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import Component from '@ember/component'; import Component from '@ember/component';
import TooltipMixin from '../../mixins/tooltip'; import TooltipMixin from '../../mixins/tooltip';
import models from '../../utils/model';
export default Component.extend(TooltipMixin, { export default Component.extend(TooltipMixin, {
documentService: service('document'), documentService: service('document'),
@ -28,11 +31,15 @@ export default Component.extend(TooltipMixin, {
toEdit: '', toEdit: '',
showDeleteBlockDialog: false, showDeleteBlockDialog: false,
deleteBlockId: '', deleteBlockId: '',
canEdit: computed('permissions', 'document.protection', function() {
let canEdit = this.get('document.protection') !== this.get('constants').ProtectionType.Lock && this.get('permissions.documentEdit');
didReceiveAttrs() { if (canEdit) this.setupAddWizard();
this._super(...arguments); return canEdit;
this.loadBlocks(); }),
}, hasBlocks: computed('blocks', function() {
return this.get('blocks.length') > 0;
}),
didRender() { didRender() {
this._super(...arguments); this._super(...arguments);
@ -41,20 +48,20 @@ export default Component.extend(TooltipMixin, {
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
if (this.get('session.authenticated')) {
this.setupAddWizard(); this.setupAddWizard();
if (this.attrs.onGotoPage !== null) {
this.attrs.onGotoPage(this.get('pageId'));
}
this.renderTooltips(); this.renderTooltips();
}
}, },
willDestroyElement() { willDestroyElement() {
this._super(...arguments); this._super(...arguments);
$('.start-section:not(.start-section-empty-state)').off('.hoverIntent');
if (this.get('session.authenticated')) {
$('.start-section:not(.start-section-empty-state)').off('.hoverIntent');
this.removeTooltips(); this.removeTooltips();
}
}, },
contentLinkHandler() { contentLinkHandler() {
@ -73,7 +80,7 @@ export default Component.extend(TooltipMixin, {
link.orphan = true; link.orphan = true;
} else { } else {
if (link.linkType === "section") { if (link.linkType === "section") {
self.attrs.onGotoPage(link.targetId); this.get('browser').scrollTo(`#page-${link.targetId}`);
} }
} }
} }
@ -105,80 +112,105 @@ export default Component.extend(TooltipMixin, {
}, },
addSection(model) { addSection(model) {
// calculate sequence of page (position in document)
let sequence = 0; let sequence = 0;
let level = 1; let level = 1;
let beforePage = this.get('beforePage'); let beforePage = this.get('beforePage');
let constants = this.get('constants');
// calculate sequence of page (position in document)
if (is.not.null(beforePage)) { if (is.not.null(beforePage)) {
level = beforePage.get('level'); level = beforePage.get('level');
// get any page before the beforePage so we can insert this new section between them // get any page before the beforePage so we can insert this new section between them
let index = _.findIndex(this.get('pages'), function(p) { return p.get('id') === beforePage.get('id'); }); let index = _.findIndex(this.get('pages'), function(item) { return item.get('page.id') === beforePage.get('id'); });
if (index !== -1) { if (index !== -1) {
let beforeBeforePage = this.get('pages')[index-1]; let beforeBeforePage = this.get('pages')[index-1];
if (is.not.undefined(beforeBeforePage)) { if (is.not.undefined(beforeBeforePage)) {
sequence = (beforePage.get('sequence') + beforeBeforePage.get('sequence')) / 2; sequence = (beforePage.get('sequence') + beforeBeforePage.get('page.sequence')) / 2;
} else { } else {
sequence = beforePage.get('sequence') / 2; sequence = beforePage.get('sequence') / 2;
} }
model.page.set('sequence', sequence);
model.page.set('level', level);
} }
} }
model.page.sequence = sequence; if (this.get('document.protection') === constants.ProtectionType.Review) {
model.page.level = level; model.page.set('status', model.page.get('relativeId') === '' ? constants.ChangeState.PendingNew : constants.ChangeState.Pending);
}
this.send('onHideSectionWizard'); this.send('onHideSectionWizard');
return this.get('onInsertSection')(model); return this.get('onInsertSection')(model);
}, },
loadBlocks() {
if (is.not.undefined(this.get('folder'))) {
this.get('sectionService').getSpaceBlocks(this.get('folder.id')).then((blocks) => {
if (this.get('isDestroyed') || this.get('isDestroying')) {
return;
}
this.set('blocks', blocks);
this.set('hasBlocks', blocks.get('length') > 0);
});
}
},
actions: { actions: {
onSavePageAsBlock(block) { onSavePageAsBlock(block) {
const promise = this.attrs.onSavePageAsBlock(block); let cb = this.get('onSavePageAsBlock');
const promise = cb(block);
promise.then(() => { promise.then(() => {
this.loadBlocks(); let refresh = this.get('refresh');
refresh();
}); });
}, },
onCopyPage(pageId, documentId) { onCopyPage(pageId, documentId) {
this.attrs.onCopyPage(pageId, documentId); let cb = this.get('onCopyPage');
cb(pageId, documentId);
}, },
onMovePage(pageId, documentId) { onMovePage(pageId, documentId) {
this.attrs.onMovePage(pageId, documentId); let cb = this.get('onMovePage');
cb(pageId, documentId);
}, },
onDeletePage(params) { onDeletePage(params) {
this.attrs.onDeletePage(params); let cb = this.get('onDeletePage');
cb(params);
}, },
onSavePage(page, meta) { onSavePage(page, meta) {
let document = this.get('document');
let constants = this.get('constants');
switch (document.get('protection')) {
case constants.ProtectionType.Lock:
break;
case constants.ProtectionType.Review:
// detect edits to newly created pending page
if (page.get('relativeId') === '' && page.get('status') === constants.ChangeState.PendingNew) {
// new page, edits
this.set('toEdit', ''); this.set('toEdit', '');
this.attrs.onSavePage(page, meta); let cb = this.get('onSavePage');
cb(page, meta);
} else if (page.get('relativeId') !== '' && page.get('status') === constants.ChangeState.Published) {
// existing page, first edit
const promise = this.addSection({ page: page, meta: meta });
promise.then((/*id*/) => { this.set('toEdit', ''); });
} else if (page.get('relativeId') !== '' && page.get('status') === constants.ChangeState.Pending) {
// existing page, subsequent edits
this.set('toEdit', '');
let cb = this.get('onSavePage');
cb(page, meta);
}
break;
case constants.ProtectionType.None:
// for un-protected documents, edits welcome!
this.set('toEdit', '');
// let cb2 = this.get('onSavePage');
// cb2(page, meta);
this.attrs.onSavePage(page, meta); // eslint-disable-line ember/no-attrs-in-components
break;
}
}, },
onShowSectionWizard(page) { onShowSectionWizard(page) {
if (is.undefined(page)) { if (is.undefined(page)) page = { id: '0' };
page = { id: '0' };
}
this.set('pageId', '');
let beforePage = this.get('beforePage'); let beforePage = this.get('beforePage');
if (is.not.null(beforePage) && $("#new-section-wizard").is(':visible') && beforePage.get('id') === page.id) { if (is.not.null(beforePage) && $("#new-section-wizard").is(':visible') && beforePage.get('id') === page.id) {
@ -217,15 +249,11 @@ export default Component.extend(TooltipMixin, {
return; return;
} }
let page = { let page = models.PageModel.create();
documentId: this.get('document.id'), page.set('documentId', this.get('document.id'));
title: sectionName, page.set('title', sectionName);
level: 1, page.set('contentType', section.get('contentType'));
sequence: 0, // calculated elsewhere page.set('pageType', section.get('pageType'));
body: "",
contentType: section.get('contentType'),
pageType: section.get('pageType')
};
let meta = { let meta = {
documentId: this.get('document.id'), documentId: this.get('document.id'),
@ -240,14 +268,7 @@ export default Component.extend(TooltipMixin, {
const promise = this.addSection(model); const promise = this.addSection(model);
promise.then((id) => { promise.then((id) => {
this.set('pageId', id); this.set('toEdit', model.page.pageType === 'section' ? id: '');
if (model.page.pageType === 'section') {
this.set('toEdit', id);
} else {
this.set('toEdit', '');
}
this.setupAddWizard(); this.setupAddWizard();
}); });
}, },
@ -263,7 +284,7 @@ export default Component.extend(TooltipMixin, {
documentId: this.get('document.id'), documentId: this.get('document.id'),
title: `${block.get('title')}`, title: `${block.get('title')}`,
level: 1, level: 1,
sequence: 0, // calculated elsewhere sequence: 1024,
body: block.get('body'), body: block.get('body'),
contentType: block.get('contentType'), contentType: block.get('contentType'),
pageType: block.get('pageType'), pageType: block.get('pageType'),
@ -283,9 +304,7 @@ export default Component.extend(TooltipMixin, {
}; };
const promise = this.addSection(model); const promise = this.addSection(model);
promise.then((id) => { promise.then((/*id*/) => {
this.set('pageId', id);
this.setupAddWizard(); this.setupAddWizard();
}); });
}, },
@ -299,11 +318,14 @@ export default Component.extend(TooltipMixin, {
this.set('showDeleteBlockDialog', false); this.set('showDeleteBlockDialog', false);
let id = this.get('deleteBlockId'); let id = this.get('deleteBlockId');
const promise = this.attrs.onDeleteBlock(id);
let cb = this.get('onDeleteBlock');
let promise = cb(id);
promise.then(() => { promise.then(() => {
this.set('deleteBlockId', ''); this.set('deleteBlockId', '');
this.loadBlocks(); let refresh = this.get('refresh');
refresh();
}); });
return true; return true;

View file

@ -16,7 +16,6 @@ import ModalMixin from '../../mixins/modal';
export default Component.extend(ModalMixin, { export default Component.extend(ModalMixin, {
documentService: service('document'), documentService: service('document'),
revisions: [],
revision: null, revision: null,
diff: '', diff: '',
hasRevisions: computed('revisions', function() { hasRevisions: computed('revisions', function() {
@ -26,6 +25,11 @@ export default Component.extend(ModalMixin, {
return this.get('diff').length > 0; return this.get('diff').length > 0;
}), }),
init() {
this._super(...arguments);
this.revisions = [];
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
this.fetchRevisions(); this.fetchRevisions();
@ -65,7 +69,8 @@ export default Component.extend(ModalMixin, {
onRollback() { onRollback() {
let revision = this.get('revision'); let revision = this.get('revision');
this.attrs.onRollback(revision.pageId, revision.id); let cb = this.get('onRollback');
cb(revision.pageId, revision.id);
this.modalClose('#document-rollback-modal'); this.modalClose('#document-rollback-modal');
} }

View file

@ -12,7 +12,8 @@
import TextField from '@ember/component/text-field'; import TextField from '@ember/component/text-field';
export default TextField.extend({ export default TextField.extend({
becomeFocused: function() { didInsertElement() {
this._super(...arguments);
this.$().focus(); this.$().focus();
}.on('didInsertElement') }
}); });

View file

@ -12,7 +12,8 @@
import TextArea from '@ember/component/text-area'; import TextArea from '@ember/component/text-area';
export default TextArea.extend({ export default TextArea.extend({
becomeFocused: function() { didInsertElement() {
this._super(...arguments);
this.$().focus(); this.$().focus();
}.on('didInsertElement') }
}); });

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import Component from '@ember/component'; import Component from '@ember/component';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import TooltipMixin from '../../mixins/tooltip'; import TooltipMixin from '../../mixins/tooltip';
@ -22,7 +23,11 @@ export default Component.extend(ModalMixin, TooltipMixin, {
newCategory: '', newCategory: '',
deleteId: '', deleteId: '',
dropdown: null, dropdown: null,
users: [],
init() {
this._super(...arguments);
this.users = [];
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);

View file

@ -12,9 +12,6 @@
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
documentTags: [],
tagz: [],
init() { init() {
this._super(...arguments); this._super(...arguments);
let tagz = []; let tagz = [];

View file

@ -48,7 +48,8 @@ export default Component.extend({
this.set('selectedDocuments', A([])); this.set('selectedDocuments', A([]));
this.set('showDeleteDialog', false); this.set('showDeleteDialog', false);
this.attrs.onDeleteDocument(list); let cb = this.get('onDeleteDocument');
cb(list);
return true; return true;
}, },
@ -72,7 +73,9 @@ export default Component.extend({
this.set('showMoveDialog', false); this.set('showMoveDialog', false);
this.set('selectedDocuments', A([])); this.set('selectedDocuments', A([]));
this.attrs.onMoveDocument(list, moveSpaceId);
let cb = this.get('onMoveDocument');
cb(list, moveSpaceId);
return true; return true;
}, },

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { empty } from '@ember/object/computed'; import { empty } from '@ember/object/computed';
import { schedule } from '@ember/runloop'; import { schedule } from '@ember/runloop';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';

View file

@ -23,12 +23,16 @@ export default Component.extend(AuthMixin, {
folderService: service('folder'), folderService: service('folder'),
localStorage: service('localStorage'), localStorage: service('localStorage'),
hasCategories: gt('categories.length', 0), hasCategories: gt('categories.length', 0),
filteredDocs: [],
categoryLinkName: 'Manage', categoryLinkName: 'Manage',
spaceSettings: computed('permissions', function() { spaceSettings: computed('permissions', function() {
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage'); return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
}), }),
init() {
this._super(...arguments);
this.filteredDocs = [];
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
this.setup(); this.setup();
@ -108,7 +112,8 @@ export default Component.extend(AuthMixin, {
}); });
this.set('documents', documents); this.set('documents', documents);
this.attrs.onRefresh(); let cb = this.get('onRefresh');
cb.onRefresh();
}); });
}, },

View file

@ -9,8 +9,8 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { empty, and } from '@ember/object/computed'; import { empty, and } from '@ember/object/computed';
import { set } from '@ember/object'; import { set } from '@ember/object';
import Component from '@ember/component'; import Component from '@ember/component';
import { isEmpty } from '@ember/utils'; import { isEmpty } from '@ember/utils';

View file

@ -9,8 +9,8 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend({ export default Component.extend({

View file

@ -9,8 +9,8 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { empty, and } from '@ember/object/computed'; import { empty, and } from '@ember/object/computed';
import Component from '@ember/component'; import Component from '@ember/component';
import { isEqual, isEmpty } from '@ember/utils'; import { isEqual, isEmpty } from '@ember/utils';
import { set } from '@ember/object'; import { set } from '@ember/object';

View file

@ -12,9 +12,13 @@
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
results: [],
resultPhrase: "", resultPhrase: "",
init() {
this._super(...arguments);
this.results = [];
},
didReceiveAttrs() { didReceiveAttrs() {
let docs = this.get('results'); let docs = this.get('results');
let duped = []; let duped = [];

View file

@ -24,7 +24,8 @@ export default Component.extend({
}, },
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(title) { onAction(title) {
@ -33,7 +34,8 @@ export default Component.extend({
page.set('title', title); page.set('title', title);
meta.set('rawBody', this.get("data")); meta.set('rawBody', this.get("data"));
this.attrs.onAction(page, meta); let cb = this.get('onAction');
cb(page, meta);
} }
} }
}); });

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { empty } from '@ember/object/computed'; import { empty } from '@ember/object/computed';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import TooltipMixin from '../../mixins/tooltip'; import TooltipMixin from '../../mixins/tooltip';
@ -75,21 +76,25 @@ export default Component.extend(TooltipMixin, ModalMixin, {
return; return;
} }
this.attrs.onAction(this.get('page.title')); let cb = this.get('onAction');
cb(this.get('page.title'));
}, },
onCancel() { onCancel() {
if (this.attrs.isDirty() !== null && this.attrs.isDirty()) { let isDirty = this.get('isDirty');
if (isDirty() !== null && isDirty()) {
this.modalOpen('#discard-modal-' + this.get('page.id'), {show: true}); this.modalOpen('#discard-modal-' + this.get('page.id'), {show: true});
return; return;
} }
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onDiscard() { onDiscard() {
this.modalClose('#discard-modal-' + this.get('page.id')); this.modalClose('#discard-modal-' + this.get('page.id'));
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onPreview() { onPreview() {

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { empty } from '@ember/object/computed'; import { empty } from '@ember/object/computed';
import Component from '@ember/component'; import Component from '@ember/component';
import ModalMixin from '../../mixins/modal'; import ModalMixin from '../../mixins/modal';
@ -44,17 +45,21 @@ export default Component.extend(ModalMixin, {
actions: { actions: {
onCancel() { onCancel() {
if (this.attrs.isDirty() !== null && this.attrs.isDirty()) { let isDirty = this.get('isDirty');
if (isDirty() !== null && isDirty()) {
this.modalOpen('#discard-modal', {show: true}); this.modalOpen('#discard-modal', {show: true});
return; return;
} }
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onDiscard() { onDiscard() {
this.modalClose('#discard-modal'); this.modalClose('#discard-modal');
this.attrs.onCancel();
let cb = this.get('onCancel');
cb();
}, },
onAction() { onAction() {
@ -72,7 +77,8 @@ export default Component.extend(ModalMixin, {
return; return;
} }
this.attrs.onAction(this.get('page.title')); let cb = this.get('onAction');
cb(this.get('page.title'));
} }
} }
}); });

View file

@ -16,7 +16,7 @@ import TooltipMixin from '../../../mixins/tooltip';
export default Component.extend(TooltipMixin, { export default Component.extend(TooltipMixin, {
isDirty: false, isDirty: false,
pageBody: "", pageBody: "",
syntaxOptions: [],
codeSyntax: null, codeSyntax: null,
codeEditor: null, codeEditor: null,
editorId: computed('page', function () { editorId: computed('page', function () {
@ -30,6 +30,7 @@ export default Component.extend(TooltipMixin, {
init() { init() {
this._super(...arguments); this._super(...arguments);
this.syntaxOptions = [];
let self = this; let self = this;
let rawBody = this.get('meta.rawBody'); let rawBody = this.get('meta.rawBody');
@ -128,7 +129,8 @@ export default Component.extend(TooltipMixin, {
}, },
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(title) { onAction(title) {
@ -138,7 +140,8 @@ export default Component.extend(TooltipMixin, {
page.set('title', title); page.set('title', title);
page.set('body', meta.get('rawBody')); page.set('body', meta.get('rawBody'));
this.attrs.onAction(page, meta); let cb = this.get('onAction');
cb(page, meta);
} }
} }
}); });

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { set } from '@ember/object'; import { set } from '@ember/object';
import { schedule } from '@ember/runloop'; import { schedule } from '@ember/runloop';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
@ -21,9 +22,13 @@ export default Component.extend(SectionMixin, TooltipMixin, {
isDirty: false, isDirty: false,
waiting: false, waiting: false,
authenticated: false, authenticated: false,
user: {},
workspaces: [], init() {
config: {}, this._super(...arguments);
this.user = {};
this.workspaces = [];
this.config = {};
},
didReceiveAttrs() { didReceiveAttrs() {
let config = {}; let config = {};
@ -195,7 +200,8 @@ export default Component.extend(SectionMixin, TooltipMixin, {
}, },
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(title) { onAction(title) {
@ -206,7 +212,8 @@ export default Component.extend(SectionMixin, TooltipMixin, {
meta.set('config', JSON.stringify(this.get('config'))); meta.set('config', JSON.stringify(this.get('config')));
meta.set('externalSource', true); meta.set('externalSource', true);
this.attrs.onAction(page, meta); let cb = this.get('onAction');
cb(page, meta);
} }
} }
}); });

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import EmberObject from '@ember/object'; import EmberObject from '@ember/object';
import { A } from '@ember/array'; import { A } from '@ember/array';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
@ -21,9 +22,13 @@ export default Component.extend(SectionMixin, NotifierMixin, {
isDirty: false, isDirty: false,
busy: false, busy: false,
authenticated: false, authenticated: false,
config: {},
owners: null, owners: null,
init() {
this._super(...arguments);
this.config = {};
},
didReceiveAttrs() { didReceiveAttrs() {
let self = this; let self = this;
let page = this.get('page'); let page = this.get('page');
@ -227,7 +232,8 @@ export default Component.extend(SectionMixin, NotifierMixin, {
}, },
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(title) { onAction(title) {

View file

@ -111,7 +111,8 @@ export default Component.extend({
}, },
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(title) { onAction(title) {
@ -120,7 +121,8 @@ export default Component.extend({
page.set('title', title); page.set('title', title);
meta.set('rawBody', this.getBody()); meta.set('rawBody', this.getBody());
this.attrs.onAction(page, meta); let cb = this.get('onCancel');
cb(page, meta);
} }
} }
}); });

View file

@ -21,8 +21,12 @@ export default Component.extend(SectionMixin, NotifierMixin, {
isDirty: false, isDirty: false,
waiting: false, waiting: false,
authenticated: false, authenticated: false,
config: {},
items: {}, init() {
this._super(...arguments);
this.config = {};
this.items = {};
},
didReceiveAttrs() { didReceiveAttrs() {
let config = {}; let config = {};
@ -140,7 +144,8 @@ export default Component.extend(SectionMixin, NotifierMixin, {
}, },
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(title) { onAction(title) {

View file

@ -9,8 +9,9 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { schedule } from 'ember/runloop';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
@ -38,8 +39,10 @@ export default Component.extend({
tableResizerOffset: 10 tableResizerOffset: 10
}); });
schedule('afterRender', function() {
$(id).on('froalaEditor.contentChanged', () => { $(id).on('froalaEditor.contentChanged', () => {
this.set('isDirty', true); this.set('isDirty', true); // eslint-disable-line ember/jquery-ember-run
});
}); });
}, },
@ -53,7 +56,8 @@ export default Component.extend({
}, },
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(title) { onAction(title) {
@ -69,7 +73,8 @@ export default Component.extend({
meta.set('rawBody', body); meta.set('rawBody', body);
this.attrs.onAction(page, meta); let cb = this.get('onAction');
cb(page, meta);
} }
} }
}); });

View file

@ -11,7 +11,6 @@
/*global Trello*/ /*global Trello*/
import $ from 'jquery'; import $ from 'jquery';
import { htmlSafe } from '@ember/string'; import { htmlSafe } from '@ember/string';
import { computed, set } from '@ember/object'; import { computed, set } from '@ember/object';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
@ -25,11 +24,9 @@ export default Component.extend(SectionMixin, NotifierMixin, TooltipMixin, {
isDirty: false, isDirty: false,
busy: false, busy: false,
authenticated: false, authenticated: false,
config: {},
boards: null, boards: null,
noBoards: false, noBoards: false,
appKey: "", appKey: "",
boardStyle: computed('config.board', function () { boardStyle: computed('config.board', function () {
let board = this.get('config.board'); let board = this.get('config.board');
@ -41,6 +38,11 @@ export default Component.extend(SectionMixin, NotifierMixin, TooltipMixin, {
return htmlSafe("background-color: " + color); return htmlSafe("background-color: " + color);
}), }),
init() {
this._super(...arguments);
this.config = {};
},
didReceiveAttrs() { didReceiveAttrs() {
let page = this.get('page'); let page = this.get('page');
let config = {}; let config = {};
@ -219,7 +221,8 @@ export default Component.extend(SectionMixin, NotifierMixin, TooltipMixin, {
}, },
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(title) { onAction(title) {

View file

@ -9,8 +9,8 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { computed, set } from '@ember/object'; import { computed, set } from '@ember/object';
import Component from '@ember/component'; import Component from '@ember/component';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
@ -116,7 +116,8 @@ export default Component.extend({
}, },
onCancel() { onCancel() {
this.attrs.onCancel(); let cb = this.get('onCancel');
cb();
}, },
onAction(title) { onAction(title) {
@ -127,7 +128,8 @@ export default Component.extend({
page.set('title', title); page.set('title', title);
meta.set('rawBody', editor.getContent()); meta.set('rawBody', editor.getContent());
this.attrs.onAction(page, meta); let cb = this.get('onAction');
cb(page, meta);
} }
} }
}); });

View file

@ -16,13 +16,17 @@ import NotifierMixin from '../../mixins/notifier';
import AuthMixin from '../../mixins/auth'; import AuthMixin from '../../mixins/auth';
export default Component.extend(TooltipMixin, NotifierMixin, AuthMixin, { export default Component.extend(TooltipMixin, NotifierMixin, AuthMixin, {
publicFolders: [],
protectedFolders: [],
privateFolders: [],
hasPublicFolders: false, hasPublicFolders: false,
hasProtectedFolders: false, hasProtectedFolders: false,
hasPrivateFolders: false, hasPrivateFolders: false,
init() {
this._super(...arguments);
this.publicFolders = [];
this.protectedFolders = [];
this.privateFolders = [];
},
didReceiveAttrs() { didReceiveAttrs() {
let folders = this.get('spaces'); let folders = this.get('spaces');
let publicFolders = []; let publicFolders = [];

View file

@ -9,28 +9,35 @@
// //
// https://documize.com // https://documize.com
import Component from '@ember/component'; import $ from 'jquery';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Component from '@ember/component';
import AuthMixin from '../../mixins/auth'; import AuthMixin from '../../mixins/auth';
import TooltipMixin from '../../mixins/tooltip'; import TooltipMixin from '../../mixins/tooltip';
import ModalMixin from '../../mixins/modal'; import ModalMixin from '../../mixins/modal';
export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, { export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, {
spaceService: service('folder'), userSvc: service('user'),
store: service(),
spaceSvc: service('folder'),
session: service(), session: service(),
appMeta: service(), appMeta: service(),
pinned: service(), pinned: service(),
pinState : { showTools: true, // show document related tools? favourite, delete, make template...
showDocumentLink: false, // show link to document in breadcrumbs
init() {
this._super(...arguments);
this.pinState = {
isPinned: false, isPinned: false,
pinId: '', pinId: '',
newName: '' newName: ''
}, };
saveTemplate: { this.saveTemplate = {
name: '', name: '',
description: '' description: ''
};
}, },
showTools: true, // show document related tools? favourite, delete, make template...
showDocumentLink: false, // show link to document in breadcrumbs
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
@ -63,7 +70,8 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, {
onDocumentDelete() { onDocumentDelete() {
this.modalClose('#document-delete-modal'); this.modalClose('#document-delete-modal');
this.attrs.onDocumentDelete(); let cb = this.get('onDocumentDelete');
cb();
}, },
onPrintDocument() { onPrintDocument() {
@ -118,7 +126,8 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, {
this.set('saveTemplate.name', ''); this.set('saveTemplate.name', '');
this.set('saveTemplate.description', ''); this.set('saveTemplate.description', '');
this.attrs.onSaveTemplate(name, excerpt); let cb = this.get('onSaveTemplate');
cb(name, excerpt);
this.modalClose('#document-template-modal'); this.modalClose('#document-template-modal');

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import Component from '@ember/component'; import Component from '@ember/component';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { schedule } from '@ember/runloop'; import { schedule } from '@ember/runloop';
@ -29,12 +30,7 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, {
copyTemplate: true, copyTemplate: true,
copyPermission: true, copyPermission: true,
copyDocument: false, copyDocument: false,
clonedSpace: { id: '' },
pinState : {
isPinned: false,
pinId: '',
newName: ''
},
spaceSettings: computed('permissions', function() { spaceSettings: computed('permissions', function() {
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage'); return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
}), }),
@ -49,10 +45,21 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, {
templateDocName: '', templateDocName: '',
templateDocNameError: false, templateDocNameError: false,
selectedTemplate: '', selectedTemplate: '',
importedDocuments: [],
importStatus: [],
dropzone: null, dropzone: null,
init() {
this._super(...arguments);
this.importedDocuments = [];
this.importStatus = [];
this.clonedSpace = { id: '' };
this.pinState = {
isPinned: false,
pinId: '',
newName: ''
};
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
@ -226,8 +233,8 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, {
this.set('deleteSpaceName', ''); this.set('deleteSpaceName', '');
$("#delete-space-name").removeClass("is-invalid"); $("#delete-space-name").removeClass("is-invalid");
this.attrs.onDeleteSpace(this.get('space.id')); let cb = this.get('onDeleteSpace');
cb(this.get('space.id'));
this.modalClose('#space-delete-modal'); this.modalClose('#space-delete-modal');
}, },
@ -331,7 +338,8 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, {
if (documents.length === 0) { if (documents.length === 0) {
this.modalClose("#import-doc-modal"); this.modalClose("#import-doc-modal");
this.attrs.onRefresh(); let cb = this.get('onRefresh');
cb();
} }
}, },

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import Component from '@ember/component'; import Component from '@ember/component';
import { schedule } from '@ember/runloop'; import { schedule } from '@ember/runloop';
import { notEmpty } from '@ember/object/computed'; import { notEmpty } from '@ember/object/computed';
@ -20,9 +21,13 @@ export default Component.extend(NotifierMixin, AuthMixin, {
copyTemplate: true, copyTemplate: true,
copyPermission: true, copyPermission: true,
copyDocument: false, copyDocument: false,
clonedSpace: { id: '' },
hasClone: notEmpty('clonedSpace.id'), hasClone: notEmpty('clonedSpace.id'),
init() {
this._super(...arguments);
this.clonedSpace = { id: '' };
},
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
@ -64,7 +69,8 @@ export default Component.extend(NotifierMixin, AuthMixin, {
$('#add-space-modal').modal('hide'); $('#add-space-modal').modal('hide');
$('#add-space-modal').modal('dispose'); $('#add-space-modal').modal('dispose');
this.attrs.onAddSpace(payload); let cb = this.get('onAddSpace');
cb(payload);
} }
} }
}); });

View file

@ -21,13 +21,13 @@ export default Component.extend({
store: service(), store: service(),
pinned: service(), pinned: service(),
enableLogout: true, enableLogout: true,
pins: [],
hasPins: notEmpty('pins'), hasPins: notEmpty('pins'),
hasSpacePins: notEmpty('spacePins'), hasSpacePins: notEmpty('spacePins'),
hasDocumentPins: notEmpty('documentPins'), hasDocumentPins: notEmpty('documentPins'),
init() { init() {
this._super(...arguments); this._super(...arguments);
this.pins = [];
if (this.get('appMeta.authProvider') === constants.AuthProvider.Keycloak) { if (this.get('appMeta.authProvider') === constants.AuthProvider.Keycloak) {
let config = this.get('appMeta.authConfig'); let config = this.get('appMeta.authConfig');

View file

@ -15,7 +15,6 @@ import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
cssClass: "", cssClass: "",
content: [],
prompt: null, prompt: null,
optionValuePath: 'id', optionValuePath: 'id',
optionLabelPath: 'name', optionLabelPath: 'name',
@ -26,6 +25,11 @@ export default Component.extend({
// leaking changes to it via a 2-way binding // leaking changes to it via a 2-way binding
_selection: reads('selection'), _selection: reads('selection'),
init() {
this._super(...arguments);
this.content = [];
},
actions: { actions: {
change() { change() {
const selectEl = this.$('select')[0]; const selectEl = this.$('select')[0];

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import Component from '@ember/component'; import Component from '@ember/component';
import stringUtil from '../../utils/string'; import stringUtil from '../../utils/string';
@ -56,7 +57,8 @@ export default Component.extend({
return; return;
} }
let result = this.attrs.onAction(); let cb = this.get('onAction');
let result = cb();
if (result) { if (result) {
this.set('show', false); this.set('show', false);
} }

View file

@ -16,7 +16,6 @@ import { computed } from '@ember/object';
export default Component.extend({ export default Component.extend({
nameField: 'category', nameField: 'category',
singleSelect: false, singleSelect: false,
items: [],
maxHeight: 0, maxHeight: 0,
onSelect: null, onSelect: null,
styleCss: computed('maxHeight', function () { styleCss: computed('maxHeight', function () {
@ -29,13 +28,19 @@ export default Component.extend({
} }
}), }),
init() {
this._super(...arguments);
this.items = [];
},
actions: { actions: {
onToggle(item) { onToggle(item) {
// callback takes precedence // callback takes precedence
// caller sets item to 'selected' // caller sets item to 'selected'
let cb = this.get('onSelect'); let cb = this.get('onSelect');
if (cb !== null) { if (cb !== null) {
this.attrs.onSelect(item); let cb = this.get('onSelect');
cb(item);
return; return;
} }

View file

@ -19,7 +19,8 @@ export default Component.extend({
actions: { actions: {
onCheck() { onCheck() {
if (this.get('onClick') !== null) { if (this.get('onClick') !== null) {
this.attrs.onClick(this.get('value')); let cb = this.get('onClick');
cb(this.get('value'));
} else { } else {
this.set('selected', !this.get('selected')); this.set('selected', !this.get('selected'));
} }

View file

@ -14,7 +14,10 @@ import Component from '@ember/component';
import miscUtil from '../utils/misc'; import miscUtil from '../utils/misc';
export default Component.extend({ export default Component.extend({
notifications: [], init() {
this._super(...arguments);
this.notifications = [];
},
didInsertElement() { didInsertElement() {
this.eventBus.subscribe('notifyUser', this, 'showNotification'); this.eventBus.subscribe('notifyUser', this, 'showNotification');

View file

@ -9,15 +9,14 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { empty } from '@ember/object/computed'; import { empty } from '@ember/object/computed';
import Component from '@ember/component'; import Component from '@ember/component';
import { computed, set } from '@ember/object'; import { computed, set } from '@ember/object';
import { isPresent, isEqual, isEmpty } from '@ember/utils'; import { isPresent, isEqual, isEmpty } from '@ember/utils';
import AuthProvider from '../mixins/auth'; import AuthProvider from '../mixins/auth';
export default Component.extend(AuthProvider, { export default Component.extend(AuthProvider, {
password: { password: "", confirmation: "" },
hasFirstnameError: empty('model.firstname'), hasFirstnameError: empty('model.firstname'),
hasLastnameError: empty('model.lastname'), hasLastnameError: empty('model.lastname'),
hasEmailError: computed('model.email', function() { hasEmailError: computed('model.email', function() {
@ -45,6 +44,11 @@ export default Component.extend(AuthProvider, {
} }
}), }),
init() {
this._super(...arguments);
this.password = { password: "", confirmation: "" };
},
actions: { actions: {
save() { save() {
let password = this.get('password.password'); let password = this.get('password.password');

View file

@ -12,21 +12,42 @@
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
let constants = EmberObject.extend({ let constants = EmberObject.extend({
ProtectionType: { // Document
ProtectionType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
None: 0, None: 0,
Lock: 1, Lock: 1,
Review: 2, Review: 2,
NoneLabel: 'Changes permitted without approval', NoneLabel: 'Changes permitted without approval',
LockLabel: 'Locked, changes not permitted', LockLabel: 'Locked, changes not permitted',
ReviewLabel: 'Changes require approval before publication', ReviewLabel: 'Changes require approval before publication'
}, },
ApprovalType: { // Document
ApprovalType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
None: 0, None: 0,
Anybody: 1, Anybody: 1,
Majority: 2, Majority: 2,
Unanimous: 3 Unanimous: 3,
AnybodyLabel: 'Approval required from any approver',
MajorityLabel: 'Majority approval required from approvers',
UnanimousLabel: 'Unanimous approval required from all approvers'
},
// Section
ChangeState: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
Published: 0,
Pending: 1,
UnderReview: 2,
Rejected: 3,
PendingNew: 4,
},
// Section
PageType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
Tab: 'tab',
Section: 'section'
} }
}); });

View file

@ -0,0 +1,26 @@
// 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 EmberObject from "@ember/object";
let econstants = EmberObject.extend({
// Document
ActionType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
Read: 1,
Feedback: 2,
Contribute: 3,
ApprovalRequest: 4,
Approved: 5,
Rejected: 6,
},
});
export default { econstants }

View file

@ -9,7 +9,9 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import constants from '../constants/constants'; import constants from '../constants/constants';
import econstants from '../constants/econstants';
export function initialize(application) { export function initialize(application) {
// address insecure jquery defaults (kudos: @nathanhammond) // address insecure jquery defaults (kudos: @nathanhammond)
@ -22,7 +24,9 @@ export function initialize(application) {
}); });
let cs = constants.constants; let cs = constants.constants;
let ec = econstants.econstants;
application.register('constants:main', cs); application.register('constants:main', cs);
application.register('econstants:main', ec);
Dropzone.autoDiscover = false; Dropzone.autoDiscover = false;
CodeMirror.modeURL = "/codemirror/mode/%N/%N.js"; CodeMirror.modeURL = "/codemirror/mode/%N/%N.js";

View file

@ -14,6 +14,8 @@ export function initialize(application) {
application.inject('controller', 'constants', 'constants:main'); application.inject('controller', 'constants', 'constants:main');
application.inject('component', 'constants', 'constants:main'); application.inject('component', 'constants', 'constants:main');
application.inject('template', 'constants', 'constants:main'); application.inject('template', 'constants', 'constants:main');
application.inject('service', 'constants', 'constants:main');
application.inject('model', 'constants', 'constants:main');
} }
export default { export default {

View file

@ -0,0 +1,24 @@
// 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('route', 'econstants', 'econstants:main');
application.inject('controller', 'econstants', 'econstants:main');
application.inject('component', 'econstants', 'econstants:main');
application.inject('template', 'econstants', 'econstants:main');
application.inject('service', 'econstants', 'econstants:main');
}
export default {
name: 'econstants',
after: "application",
initialize: initialize
};

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import Mixin from '@ember/object/mixin'; import Mixin from '@ember/object/mixin';
// ID values expected format: // ID values expected format:

View file

@ -12,15 +12,15 @@
import Mixin from '@ember/object/mixin'; import Mixin from '@ember/object/mixin';
export default Mixin.create({ export default Mixin.create({
isReadonly: function () { // isReadonly() {
if (this.get('page.userId') === this.get('session.session.authenticated.user.id')) { // if (this.get('page.userId') === this.get('session.session.authenticated.user.id')) {
return undefined; // return undefined;
} else { // } else {
return "readonly"; // return "readonly";
} // }
}.property('page'), // }.property('page'),
isMine: function () { // isMine() {
return this.get('page.userId') === this.get('session.session.authenticated.user.id'); // return this.get('page.userId') === this.get('session.session.authenticated.user.id');
}.property('page') // }.property('page')
}); });

View file

@ -9,12 +9,11 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import Mixin from '@ember/object/mixin'; import Mixin from '@ember/object/mixin';
import { schedule } from '@ember/runloop'; import { schedule } from '@ember/runloop';
export default Mixin.create({ export default Mixin.create({
tooltips: [],
renderTooltips() { renderTooltips() {
schedule('afterRender', () => { schedule('afterRender', () => {
$('[data-toggle="tooltip"]').tooltip('dispose'); $('[data-toggle="tooltip"]').tooltip('dispose');
@ -24,5 +23,17 @@ export default Mixin.create({
removeTooltips() { removeTooltips() {
$('[data-toggle="tooltip"]').tooltip('dispose'); $('[data-toggle="tooltip"]').tooltip('dispose');
},
renderPopovers() {
schedule('afterRender', () => {
$('[data-toggle="popover"]').popover('dispose');
$('body').popover({selector: '[data-toggle="popover"]', delay: 250});
});
},
removePopovers() {
$('[data-toggle="tooltip"]').popover('dispose');
} }
}); });

View file

@ -9,17 +9,20 @@
// //
// https://documize.com // https://documize.com
import { inject as service } from '@ember/service'; import Model from 'ember-data/model';
import Component from '@ember/component'; import attr from 'ember-data/attr';
export default Component.extend({ export default Model.extend({
documentService: service('document'), orgId: attr(),
document: attr(),
didReceiveAttrs() { documentId: attr(),
this._super(...arguments); documentSlug: attr(),
tags: attr(),
this.get('documentService').getActivity(this.get('document.id'), 7).then((activity) => { excerpt: attr(),
this.set('activity', activity); itemId: attr(),
}); itemType: attr(),
} space: attr(),
spaceId: attr(),
spaceSlug: attr(),
selected: attr()
}); });

View file

@ -19,6 +19,7 @@ export default Model.extend({
folderId: attr('string'), folderId: attr('string'),
documentId: attr('string'), documentId: attr('string'),
pageId: attr('string'), pageId: attr('string'),
pageTitle: attr('string'),
userId: attr('string'), userId: attr('string'),
firstname: attr('string'), firstname: attr('string'),
lastname: attr('string'), lastname: attr('string'),
@ -56,6 +57,9 @@ export default Model.extend({
case constants.UserActivityType.PublishedBlock: case constants.UserActivityType.PublishedBlock:
label = 'Published Block'; label = 'Published Block';
break; break;
case constants.UserActivityType.Rejected:
label = 'Rejected';
break;
default: default:
break; break;
} }
@ -94,11 +98,13 @@ export default Model.extend({
case constants.UserActivityType.PublishedBlock: case constants.UserActivityType.PublishedBlock:
color = 'color-blue'; color = 'color-blue';
break; break;
case constants.UserActivityType.Rejected:
color = 'color-red';
break;
default: default:
break; break;
} }
return color; return color;
}) })
}); });

View file

@ -0,0 +1,30 @@
// 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 Model from 'ember-data/model';
import attr from 'ember-data/attr';
// import { belongsTo, hasMany } from 'ember-data/relationships';
export default Model.extend({
// page: belongsTo('page', { inverse: null }),
// meta: belongsTo('page-meta', { inverse: null }),
// pending: hasMany('page-pending', { inverse: null }),
page: attr(),
meta: attr(),
pending: attr(),
changePending: attr('boolean'),
changeAwaitingReview: attr('boolean'),
changeRejected: attr('boolean'),
userHasChangePending: attr('boolean'),
userHasChangeAwaitingReview: attr('boolean'),
userHasChangeRejected: attr('boolean'),
userHasNewPagePending: attr('boolean')
});

View file

@ -0,0 +1,28 @@
// 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 Model from 'ember-data/model';
import attr from 'ember-data/attr';
// import { belongsTo } from 'ember-data/relationships';
export default Model.extend({
// page: belongsTo('page', { inverse: null }),
// meta: belongsTo('page-meta', { inverse: null }),
page: attr(),
meta: attr(),
owner: attr('string'),
changePending: attr('boolean'),
changeAwaitingReview: attr('boolean'),
changeRejected: attr('boolean'),
userHasChangePending: attr('boolean'),
userHasChangeAwaitingReview: attr('boolean'),
userHasChangeRejected: attr('boolean')
});

View file

@ -19,7 +19,7 @@ export default Model.extend({
contentType: attr('string'), contentType: attr('string'),
pageType: attr('string'), pageType: attr('string'),
level: attr('number', { defaultValue: 1 }), level: attr('number', { defaultValue: 1 }),
sequence: attr('number', { defaultValue: 0 }), sequence: attr('number', { defaultValue: 1024 }),
numbering: attr('string'), numbering: attr('string'),
revisions: attr('number', { defaultValue: 0 }), revisions: attr('number', { defaultValue: 0 }),
blockId: attr('string'), blockId: attr('string'),
@ -27,8 +27,9 @@ export default Model.extend({
body: attr('string'), body: attr('string'),
rawBody: attr('string'), rawBody: attr('string'),
meta: attr(), meta: attr(),
approval: attr('number', { defaultValue: 0 }), status: attr('number', { defaultValue: 0 }),
relativeId: attr('string'), relativeId: attr('string'),
userId: attr('string'),
tagName: computed('level', function () { tagName: computed('level', function () {
return "h2"; return "h2";
@ -48,5 +49,16 @@ export default Model.extend({
}), }),
created: attr(), created: attr(),
revised: attr() revised: attr(),
// is this a new page that is pending and belongs to the user?
isNewPageUserPending(userId) {
return this.get('relativeId') === '' && this.get('userId') === userId && (
this.get('status') === this.get('constants').ChangeState.PendingNew || this.get('status') === this.get('constants').ChangeState.UnderReview);
},
// is this new page ready for review?
isNewPageReviewReady() {
return this.get('relativeId') === '' && this.get('status') === this.get('constants').ChangeState.UnderReview;
}
}); });

View file

@ -15,10 +15,13 @@ import attr from 'ember-data/attr';
export default Model.extend({ export default Model.extend({
documentName: attr('string'), documentName: attr('string'),
documentId: attr('string'),
folderId: attr('string'), folderId: attr('string'),
contributed: attr('string'), contributed: attr('string'),
viewed: attr('string'), viewed: attr('string'),
created: attr('string'), created: attr('string'),
approved: attr('string'),
rejected: attr('string'),
hasContributed: computed('contributed', function () { hasContributed: computed('contributed', function () {
return this.get('contributed').length > 0; return this.get('contributed').length > 0;
@ -28,5 +31,11 @@ export default Model.extend({
}), }),
hasCreated: computed('created', function () { hasCreated: computed('created', function () {
return this.get('created').length > 0; return this.get('created').length > 0;
}),
hasApproved: computed('approved', function () {
return this.get('approved').length > 0;
}),
hasRejected: computed('rejected', function () {
return this.get('rejected').length > 0;
}) })
}); });

View file

@ -9,8 +9,8 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
import constants from '../../../utils/constants'; import constants from '../../../utils/constants';

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { Promise as EmberPromise } from 'rsvp'; import { Promise as EmberPromise } from 'rsvp';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
export default Route.extend({ export default Route.extend({

View file

@ -9,8 +9,8 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

View file

@ -9,27 +9,33 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { computed } from '@ember/computed';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import TooltipMixin from '../../../mixins/tooltip'; import TooltipMixin from '../../../mixins/tooltip';
export default Controller.extend(TooltipMixin, { export default Controller.extend(TooltipMixin, {
folderService: service('folder'), folderService: service('folder'),
folders: [],
dropdown: null, dropdown: null,
deleteSpace: {
init() {
this._super(...arguments);
this.folders = [];
this.deleteSpace = {
id: '', id: '',
name: '' name: ''
};
}, },
label: function () { label: computed('folders', function() {
switch (this.get('folders').length) { switch (this.get('folders').length) {
case 1: case 1:
return "space"; return "space";
default: default:
return "spaces"; return "spaces";
} }
}.property('folders'), }),
actions: { actions: {
onShow(id) { onShow(id) {

View file

@ -15,7 +15,11 @@ import Controller from '@ember/controller';
export default Controller.extend({ export default Controller.extend({
userService: service('user'), userService: service('user'),
newUser: { firstname: "", lastname: "", email: "", active: true },
init() {
this._super(...arguments);
this.newUser = { firstname: "", lastname: "", email: "", active: true };
},
actions: { actions: {
add(user) { add(user) {

View file

@ -10,7 +10,6 @@
// https://documize.com // https://documize.com
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import NotifierMixin from '../../mixins/notifier';
export default Controller.extend(NotifierMixin, { export default Controller.extend({
}); });

View file

@ -19,8 +19,7 @@ export default Controller.extend(TooltipMixin, {
templateService: service('template'), templateService: service('template'),
sectionService: service('section'), sectionService: service('section'),
linkService: service('link'), linkService: service('link'),
queryParams: ['pageId', 'tab'], currentPageId: '',
pageId: '',
tab: 'content', tab: 'content',
actions: { actions: {
@ -28,9 +27,13 @@ export default Controller.extend(TooltipMixin, {
this.set('tab', tab); this.set('tab', tab);
}, },
onShowPage(pageId) {
this.set('tab', 'content');
this.get('browser').scrollTo(`#page-${pageId}`);
},
onSaveDocument(doc) { onSaveDocument(doc) {
this.get('documentService').save(doc); this.get('documentService').save(doc);
this.get('browser').setTitle(doc.get('name')); this.get('browser').setTitle(doc.get('name'));
this.get('browser').setMetaDescription(doc.get('excerpt')); this.get('browser').setMetaDescription(doc.get('excerpt'));
}, },
@ -60,19 +63,26 @@ export default Controller.extend(TooltipMixin, {
}, },
onSavePage(page, meta) { onSavePage(page, meta) {
let documentId = this.get('document.id'); let document = this.get('document');
let documentId = document.get('id');
let constants = this.get('constants');
// if document approval mode is locked return
if (document.get('protection') == constants.ProtectionType.Lock) {
// should not really happen
return;
}
// Go ahead and save edits as normal
let model = { let model = {
page: page.toJSON({ includeId: true }), page: page.toJSON({ includeId: true }),
meta: meta.toJSON({ includeId: true }) meta: meta.toJSON({ includeId: true })
}; };
this.get('documentService').updatePage(documentId, page.get('id'), model).then((up) => { this.get('documentService').updatePage(documentId, page.get('id'), model).then((/*up*/) => {
this.set('pageId', up.get('id')); this.get('documentService').fetchPages(documentId, this.get('session.user.id')).then((pages) => {
this.get('documentService').getPages(this.get('document.id')).then((pages) => {
this.set('pages', pages); this.set('pages', pages);
this.get('linkService').getDocumentLinks(documentId).then((links) => {
this.get('linkService').getDocumentLinks(this.get('document.id')).then((links) => {
this.set('links', links); this.set('links', links);
}); });
}); });
@ -81,55 +91,38 @@ export default Controller.extend(TooltipMixin, {
onPageDeleted(deletePage) { onPageDeleted(deletePage) {
let documentId = this.get('document.id'); let documentId = this.get('document.id');
let pages = this.get('pages');
let deleteId = deletePage.id; let deleteId = deletePage.id;
let deleteChildren = deletePage.children; let deleteChildren = deletePage.children;
let page = _.findWhere(pages, {
id: deleteId
});
let pageIndex = _.indexOf(pages, page, false);
let pendingChanges = []; let pendingChanges = [];
let pages = this.get('pages');
let pageIndex = _.findIndex(pages, function(i) { return i.get('page.id') === deleteId; });
let item = pages[pageIndex];
// select affected pages // select affected pages
for (var i = pageIndex + 1; i < pages.get('length'); i++) { for (var i = pageIndex + 1; i < pages.get('length'); i++) {
if (pages[i].get('level') <= page.get('level')) { if (i === pageIndex + 1 && pages[i].get('page.level') === item.get('page.level')) break;
break; if (pages[i].get('page.level') <= item.get('page.level')) break;
}
pendingChanges.push({ pendingChanges.push({ pageId: pages[i].get('page.id'), level: pages[i].get('page.level') - 1 });
pageId: pages[i].get('id'),
level: pages[i].get('level') - 1
});
} }
this.set('pageId', '');
if (deleteChildren) { if (deleteChildren) {
// nuke of page tree pendingChanges.push({ pageId: deleteId });
pendingChanges.push({
pageId: deleteId
});
this.get('documentService').deletePages(documentId, deleteId, pendingChanges).then(() => { this.get('documentService').deletePages(documentId, deleteId, pendingChanges).then(() => {
// update our models so we don't have to reload from db // this.send('onPageLevelChange', '', pendingChanges);
for (var i = 0; i < pendingChanges.length; i++) { this.get('documentService').fetchPages(this.get('document.id'), this.get('session.user.id')).then((pages) => {
let pageId = pendingChanges[i].pageId; this.set('pages', pages);
this.set('pages', _.reject(pages, function (p) { //jshint ignore: line });
return p.get('id') === pageId;
}));
}
this.set('pages', _.sortBy(pages, "sequence"));
this.get('target._routerMicrolib').refresh();
}); });
} else { } else {
// page delete followed by re-leveling child pages // page delete followed by re-leveling child pages
this.get('documentService').deletePage(documentId, deleteId).then(() => { this.get('documentService').deletePage(documentId, deleteId).then(() => {
this.set('pages', _.reject(pages, function (p) { // this.send('onPageLevelChange', '', pendingChanges);
return p.get('id') === deleteId; this.get('documentService').fetchPages(this.get('document.id'), this.get('session.user.id')).then((pages) => {
})); this.set('pages', pages);
});
this.send('onPageLevelChange', pendingChanges);
}); });
} }
}, },
@ -139,10 +132,10 @@ export default Controller.extend(TooltipMixin, {
this.get('documentService').addPage(this.get('document.id'), data).then((newPage) => { this.get('documentService').addPage(this.get('document.id'), data).then((newPage) => {
let data = this.get('store').normalize('page', newPage); let data = this.get('store').normalize('page', newPage);
this.get('store').push(data); this.get('store').push(data);
this.set('pageId', newPage.id);
this.get('documentService').getPages(this.get('document.id')).then((pages) => { this.get('documentService').fetchPages(this.get('document.id'), this.get('session.user.id')).then((pages) => {
this.set('pages', pages); this.set('pages', pages);
this.eventBus.publish('documentPageAdded', newPage.id);
if (newPage.pageType === 'tab') { if (newPage.pageType === 'tab') {
this.transitionToRoute('document.section', this.transitionToRoute('document.section',
@ -185,34 +178,24 @@ export default Controller.extend(TooltipMixin, {
this.get('templateService').saveAsTemplate(this.get('document.id'), name, desc).then(function () {}); this.get('templateService').saveAsTemplate(this.get('document.id'), name, desc).then(function () {});
}, },
onPageSequenceChange(changes) { onPageSequenceChange(currentPageId, changes) {
this.set('currentPageId', currentPageId);
this.get('documentService').changePageSequence(this.get('document.id'), changes).then(() => { this.get('documentService').changePageSequence(this.get('document.id'), changes).then(() => {
this.get('documentService').getPages(this.get('document.id')).then( (pages) => { this.get('documentService').fetchPages(this.get('document.id'), this.get('session.user.id')).then( (pages) => {
this.set('pages', pages); this.set('pages', pages);
}); });
}); });
}, },
onPageLevelChange(changes) { onPageLevelChange(currentPageId, changes) {
this.set('currentPageId', currentPageId);
this.get('documentService').changePageLevel(this.get('document.id'), changes).then(() => { this.get('documentService').changePageLevel(this.get('document.id'), changes).then(() => {
this.get('documentService').getPages(this.get('document.id')).then( (pages) => { this.get('documentService').fetchPages(this.get('document.id'), this.get('session.user.id')).then( (pages) => {
this.set('pages', pages); this.set('pages', pages);
}); });
}); });
}, },
onGotoPage(id) {
if (id !== '') {
this.set('pageId', id);
this.set('tab', 'content');
let jumpTo = "#page-" + id;
if (!$(jumpTo).inView()) {
$(jumpTo).velocity("scroll", { duration: 250, offset: -100 });
}
}
},
onTagChange(tags) { onTagChange(tags) {
let doc = this.get('document'); let doc = this.get('document');
doc.set('tags', tags); doc.set('tags', tags);
@ -224,6 +207,16 @@ export default Controller.extend(TooltipMixin, {
this.set('tab', 'content'); this.set('tab', 'content');
this.get('target._routerMicrolib').refresh(); this.get('target._routerMicrolib').refresh();
}); });
},
refresh() {
this.get('documentService').fetchPages(this.get('document.id'), this.get('session.user.id')).then((data) => {
this.set('pages', data);
});
this.get('sectionService').getSpaceBlocks(this.get('folder.id')).then((data) => {
this.set('blocks', data);
});
} }
} }
}); });

View file

@ -9,7 +9,7 @@
// //
// https://documize.com // https://documize.com
import { hash } from 'rsvp'; import { Promise as EmberPromise, hash } from 'rsvp';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
@ -20,6 +20,15 @@ export default Route.extend(AuthenticatedRouteMixin, {
folderService: service('folder'), folderService: service('folder'),
userService: service('user'), userService: service('user'),
beforeModel() {
return new EmberPromise((resolve) => {
this.get('documentService').fetchPages(this.paramsFor('document').document_id, this.get('session.user.id')).then((data) => {
this.set('pages', data);
resolve();
});
});
},
model() { model() {
let document = this.modelFor('document').document; let document = this.modelFor('document').document;
this.browser.setTitle(document.get('name')); this.browser.setTitle(document.get('name'));
@ -29,11 +38,12 @@ export default Route.extend(AuthenticatedRouteMixin, {
folders: this.modelFor('document').folders, folders: this.modelFor('document').folders,
folder: this.modelFor('document').folder, folder: this.modelFor('document').folder,
document: this.modelFor('document').document, document: this.modelFor('document').document,
pages: this.get('documentService').getPages(this.modelFor('document').document.get('id')), pages: this.get('pages'),
links: this.modelFor('document').links, links: this.modelFor('document').links,
sections: this.modelFor('document').sections, sections: this.modelFor('document').sections,
permissions: this.modelFor('document').permissions, permissions: this.modelFor('document').permissions,
roles: this.modelFor('document').roles roles: this.modelFor('document').roles,
blocks: this.modelFor('document').blocks
}); });
}, },
@ -46,5 +56,6 @@ export default Route.extend(AuthenticatedRouteMixin, {
controller.set('sections', model.sections); controller.set('sections', model.sections);
controller.set('permissions', model.permissions); controller.set('permissions', model.permissions);
controller.set('roles', model.roles); controller.set('roles', model.roles);
controller.set('blocks', model.blocks);
} }
}); });

View file

@ -1,9 +1,10 @@
{{toolbar/nav-bar}} {{toolbar/nav-bar}}
{{toolbar/for-document document=document spaces=folders space=folder permissions=permissions roles=roles {{toolbar/for-document document=document spaces=folders space=folder permissions=permissions roles=roles tab=tab
onDocumentDelete=(action 'onDocumentDelete') onDocumentDelete=(action 'onDocumentDelete')
onSaveTemplate=(action 'onSaveTemplate') onSaveTemplate=(action 'onSaveTemplate')
onSaveDocument=(action 'onSaveDocument')}} onSaveDocument=(action 'onSaveDocument')
refresh=(action 'refresh')}}
<div id="doc-view" class="container"> <div id="doc-view" class="container">
<div class="row"> <div class="row">
@ -11,26 +12,25 @@
{{document/document-heading document=document permissions=permissions {{document/document-heading document=document permissions=permissions
onSaveDocument=(action 'onSaveDocument')}} onSaveDocument=(action 'onSaveDocument')}}
{{document/document-meta document=document folder=folder folders=folders permissions=permissions {{document/document-meta document=document folder=folder folders=folders permissions=permissions pages=pages
onSaveDocument=(action 'onSaveDocument')}} onSaveDocument=(action 'onSaveDocument')}}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
{{document/document-toc document=document folder=folder pages=pages page=page {{document/document-toc document=document folder=folder pages=pages page=page
permissions=permissions roles=roles currentPageId=pageId tab=tab permissions=permissions roles=roles tab=tab currentPageId=currentPageId onShowPage=(action 'onShowPage')
onPageSequenceChange=(action 'onPageSequenceChange') onPageLevelChange=(action 'onPageLevelChange') onGotoPage=(action 'onGotoPage')}} onPageSequenceChange=(action 'onPageSequenceChange') onPageLevelChange=(action 'onPageLevelChange')}}
</div> </div>
</div> </div>
<div class="row no-gutters"> <div class="row no-gutters">
<div class="col-12"> <div class="col-12">
<div class="text-center mt-5 mb-5 non-printable"> <div class="text-center non-printable document-tabnav">
<ul class="tabnav-control"> <ul class="tabnav-control">
<li class="tab {{if (eq tab 'content') 'selected'}}" {{action 'onTabChange' 'content'}}>Content</li> <li class="tab {{if (eq tab 'content') 'selected'}}" {{action 'onTabChange' 'content'}}>Content</li>
<li class="tab {{if (eq tab 'attachment') 'selected'}}" {{action 'onTabChange' 'attachment'}}>Attachments</li> <li class="tab {{if (eq tab 'attachment') 'selected'}}" {{action 'onTabChange' 'attachment'}}>Attachments</li>
{{#if session.authenticated}} {{#if session.authenticated}}
<li class="tab {{if (eq tab 'activity') 'selected'}}" {{action 'onTabChange' 'activity'}}>Activity</li>
<li class="tab {{if (eq tab 'revision') 'selected'}}" {{action 'onTabChange' 'revision'}}>Revisions</li> <li class="tab {{if (eq tab 'revision') 'selected'}}" {{action 'onTabChange' 'revision'}}>Revisions</li>
{{/if}} {{/if}}
</ul> </ul>
@ -38,20 +38,17 @@
{{#if (eq tab 'content')}} {{#if (eq tab 'content')}}
{{document/view-content {{document/view-content
document=document links=links pages=pages document=document links=links pages=pages blocks=blocks
folder=folder folders=folders sections=sections permissions=permissions pageId=pageId folder=folder folders=folders sections=sections permissions=permissions roles=roles
onSavePage=(action 'onSavePage') onInsertSection=(action 'onInsertSection') onSavePage=(action 'onSavePage') onInsertSection=(action 'onInsertSection')
onSavePageAsBlock=(action 'onSavePageAsBlock') onDeleteBlock=(action 'onDeleteBlock') onGotoPage=(action 'onGotoPage') onSavePageAsBlock=(action 'onSavePageAsBlock') onDeleteBlock=(action 'onDeleteBlock')
onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onPageDeleted')}} onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onPageDeleted') refresh=(action 'refresh')}}
{{/if}} {{/if}}
{{#if (eq tab 'attachment')}} {{#if (eq tab 'attachment')}}
{{document/view-attachment document=document permissions=permissions}} {{document/view-attachment document=document permissions=permissions}}
{{/if}} {{/if}}
{{#if (eq tab 'activity')}}
{{document/view-activity document=document pages=pages permissions=permissions}}
{{/if}}
{{#if (eq tab 'revision')}} {{#if (eq tab 'revision')}}
{{document/view-revision document=document folder=folder pages=pages onRollback=(action 'onRollback')}} {{document/view-revision document=document folder=folder pages=pages onRollback=(action 'onRollback')}}

View file

@ -20,8 +20,7 @@ export default Route.extend(AuthenticatedRouteMixin, {
folderService: service('folder'), folderService: service('folder'),
linkService: service('link'), linkService: service('link'),
beforeModel(transition) { beforeModel() {
this.set('pageId', is.not.undefined(transition.queryParams.page) ? transition.queryParams.page : "");
this.set('folderId', this.paramsFor('document').folder_id); this.set('folderId', this.paramsFor('document').folder_id);
this.set('documentId', this.paramsFor('document').document_id); this.set('documentId', this.paramsFor('document').document_id);
@ -43,11 +42,11 @@ export default Route.extend(AuthenticatedRouteMixin, {
folders: this.get('folders'), folders: this.get('folders'),
folder: this.get('folder'), folder: this.get('folder'),
document: this.get('document'), document: this.get('document'),
page: this.get('pageId'),
permissions: this.get('permissions'), permissions: this.get('permissions'),
roles: this.get('roles'), roles: this.get('roles'),
links: this.get('links'), links: this.get('links'),
sections: this.get('sectionService').getAll() sections: this.get('sectionService').getAll(),
blocks: this.get('sectionService').getSpaceBlocks(this.get('folder.id'))
}); });
}, },

View file

@ -10,7 +10,6 @@
// https://documize.com // https://documize.com
import { Promise as EmberPromise, hash } from 'rsvp'; import { Promise as EmberPromise, hash } from 'rsvp';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
@ -20,7 +19,6 @@ export default Route.extend(AuthenticatedRouteMixin, {
folderService: service('folder'), folderService: service('folder'),
templateService: service('template'), templateService: service('template'),
session: service(''), session: service(''),
folder: {},
beforeModel() { beforeModel() {
this.set('folderId', this.paramsFor('folder').folder_id) this.set('folderId', this.paramsFor('folder').folder_id)

View file

@ -17,32 +17,35 @@ export default Controller.extend({
searchService: service('search'), searchService: service('search'),
queryParams: ['filter', 'matchDoc', 'matchContent', 'matchTag', 'matchFile'], queryParams: ['filter', 'matchDoc', 'matchContent', 'matchTag', 'matchFile'],
filter: '', filter: '',
results: [],
matchDoc: true,
matchContent: true,
matchFile: false,
matchTag: false,
onKeywordChange: function () { onKeywordChange: function () {
debounce(this, this.fetch, 750); debounce(this, this.fetch, 750);
}.observes('filter'), }.observes('filter'),
matchDoc: true,
onMatchDoc: function () { onMatchDoc: function () {
debounce(this, this.fetch, 750); debounce(this, this.fetch, 750);
}.observes('matchDoc'), }.observes('matchDoc'),
matchContent: true,
onMatchContent: function () { onMatchContent: function () {
debounce(this, this.fetch, 750); debounce(this, this.fetch, 750);
}.observes('matchContent'), }.observes('matchContent'),
matchTag: false,
onMatchTag: function () { onMatchTag: function () {
debounce(this, this.fetch, 750); debounce(this, this.fetch, 750);
}.observes('matchTag'), }.observes('matchTag'),
matchFile: false,
onMatchFile: function () { onMatchFile: function () {
debounce(this, this.fetch, 750); debounce(this, this.fetch, 750);
}.observes('matchFile'), }.observes('matchFile'),
init() {
this._super(...arguments);
this.results = [];
},
fetch() { fetch() {
let self = this; let self = this;
let payload = { let payload = {

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
export default Route.extend({ export default Route.extend({

View file

@ -21,6 +21,10 @@ export default Router.map(function () {
path: '/' path: '/'
}); });
this.route('dashboard', {
path: 'dashboard'
});
this.route('folder', { this.route('folder', {
path: 's/:folder_id/:folder_slug' path: 's/:folder_id/:folder_slug'
}, function() { }, function() {
@ -61,6 +65,9 @@ export default Router.map(function () {
this.route('auth', { this.route('auth', {
path: 'auth' path: 'auth'
}); });
this.route('audit', {
path: 'audit'
});
}); });
this.route('setup', { this.route('setup', {

View file

@ -0,0 +1,13 @@
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
normalize(modelClass, resourceHash) {
return {
data: {
id: resourceHash.id ? resourceHash.id : 0,
type: modelClass.modelName,
attributes: resourceHash
}
};
}
});

View file

@ -0,0 +1,13 @@
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
normalize(modelClass, resourceHash) {
return {
data: {
id: resourceHash.id ? resourceHash.id : 0,
type: modelClass.modelName,
attributes: resourceHash
}
};
}
});

View file

@ -12,33 +12,15 @@
import Service, { inject as service } from '@ember/service'; import Service, { inject as service } from '@ember/service';
export default Service.extend({ export default Service.extend({
sessionService: service('session'),
ajax: service(), ajax: service(),
store: service(),
// document number of views, edits, approvals, etc. getDocumentSummary(documentId) {
getDocumentSummary(documentId, days) { return this.get('ajax').request(`activity/document/${documentId}`, {
return this.get('ajax').request(`activity/document/${documentId}?days=${days}`, {
method: "GET" method: "GET"
}).then((response) => { }).then((response) => {
let data = { return response;
viewers: [],
changers: []
};
data.viewers = response.viewers.map((obj) => {
let data = this.get('store').normalize('documentActivity', obj);
return this.get('store').push(data);
});
data.changers = response.changers.map((obj) => {
let data = this.get('store').normalize('documentActivity', obj);
return this.get('store').push(data);
});
return data;
}).catch(() => { }).catch(() => {
return []; return [];
}); });
}, }
}); });

View file

@ -9,7 +9,9 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import Service, { inject as service } from '@ember/service'; import Service, { inject as service } from '@ember/service';
import { schedule } from '@ember/runloop';
export default Service.extend({ export default Service.extend({
sessionService: service('session'), sessionService: service('session'),
@ -42,5 +44,17 @@ export default Service.extend({
} }
$('head').append('<meta name="description" content="' + description + '">'); $('head').append('<meta name="description" content="' + description + '">');
},
scrollTo(id) {
schedule('afterRender', () => {
let elem = $(id).offset();
if (is.undefined(elem)) return;
$('html, body').animate({
scrollTop: elem.top
}, 250);
});
} }
}); });

View file

@ -52,6 +52,8 @@ export default Service.extend({
}); });
return documents; return documents;
}).catch((error) => {
return error;
}); });
}, },
@ -100,6 +102,8 @@ export default Service.extend({
}).then((response) => { }).then((response) => {
let data = this.get('store').normalize('page', response); let data = this.get('store').normalize('page', response);
return this.get('store').push(data); return this.get('store').push(data);
}).catch((error) => {
return error;
}); });
}, },
@ -121,7 +125,6 @@ export default Service.extend({
// Returns document page with content // Returns document page with content
getPage(documentId, pageId) { getPage(documentId, pageId) {
return this.get('ajax').request(`documents/${documentId}/pages/${pageId}`, { return this.get('ajax').request(`documents/${documentId}/pages/${pageId}`, {
method: 'GET' method: 'GET'
}).then((response) => { }).then((response) => {
@ -132,7 +135,6 @@ export default Service.extend({
// Returns document page meta object // Returns document page meta object
getPageMeta(documentId, pageId) { getPageMeta(documentId, pageId) {
return this.get('ajax').request(`documents/${documentId}/pages/${pageId}/meta`, { return this.get('ajax').request(`documents/${documentId}/pages/${pageId}/meta`, {
method: 'GET' method: 'GET'
}).then((response) => { }).then((response) => {
@ -203,34 +205,12 @@ export default Service.extend({
}); });
}, },
//**************************************************
// Activity
//**************************************************
// document meta referes to number of views, edits, approvals, etc.
getActivity(documentId, days) {
return this.get('ajax').request(`documents/${documentId}/activity?days=${days}`, {
method: "GET"
}).then((response) => {
let data = [];
data = response.map((obj) => {
let data = this.get('store').normalize('documentActivity', obj);
return this.get('store').push(data);
});
return data;
}).catch(() => {
return [];
});
},
//************************************************** //**************************************************
// Table of contents // Table of contents
//************************************************** //**************************************************
// Returns all pages without the content // Returns all pages without the content
getTableOfContents(documentId) { getTableOfContents(documentId) {
return this.get('ajax').request(`documents/${documentId}/pages?content=0`, { return this.get('ajax').request(`documents/${documentId}/pages?content=0`, {
method: 'GET' method: 'GET'
}).then((response) => { }).then((response) => {
@ -268,7 +248,6 @@ export default Service.extend({
// document attachments without the actual content // document attachments without the actual content
getAttachments(documentId) { getAttachments(documentId) {
return this.get('ajax').request(`documents/${documentId}/attachments`, { return this.get('ajax').request(`documents/${documentId}/attachments`, {
method: 'GET' method: 'GET'
}).then((response) => { }).then((response) => {
@ -380,8 +359,108 @@ export default Service.extend({
data.links = response.links; data.links = response.links;
return data; return data;
}).catch((error) => {
return error;
});
},
// fetchPages returns all pages, page meta and pending changes for document.
// This method bulk fetches data to reduce network chatter.
// We produce a bunch of calculated boolean's for UI display purposes
// that can tell us quickly about pending changes for UI display.
fetchPages(documentId, currentUserId) {
let constants = this.get('constants');
let changePending = false;
let changeAwaitingReview = false;
let changeRejected = false;
let userHasChangePending = false;
let userHasChangeAwaitingReview = false;
let userHasChangeRejected = false;
return this.get('ajax').request(`fetch/page/${documentId}`, {
method: 'GET'
}).then((response) => {
let data = A([]);
response.forEach((page) => {
changePending = false;
changeAwaitingReview = false;
changeRejected = false;
userHasChangePending = false;
userHasChangeAwaitingReview = false;
userHasChangeRejected = false;
let p = this.get('store').normalize('page', page.page);
p = this.get('store').push(p);
let m = this.get('store').normalize('page-meta', page.meta);
m = this.get('store').push(m);
let pending = A([]);
page.pending.forEach((i) => {
let p = this.get('store').normalize('page', i.page);
p = this.get('store').push(p);
let m = this.get('store').normalize('page-meta', i.meta);
m = this.get('store').push(m);
let belongsToMe = p.get('userId') === currentUserId;
let pageStatus = p.get('status');
let pi = {
id: p.get('id'),
page: p,
meta: m,
owner: i.owner,
changePending: pageStatus === constants.ChangeState.Pending || pageStatus === constants.ChangeState.PendingNew,
changeAwaitingReview: pageStatus === constants.ChangeState.UnderReview,
changeRejected: pageStatus === constants.ChangeState.Rejected,
userHasChangePending: belongsToMe && (pageStatus === constants.ChangeState.Pending || pageStatus === constants.ChangeState.PendingNew),
userHasChangeAwaitingReview: belongsToMe && pageStatus === constants.ChangeState.UnderReview,
userHasChangeRejected: belongsToMe && pageStatus === constants.ChangeState.Rejected
};
let pim = this.get('store').normalize('page-pending', pi);
pim = this.get('store').push(pim);
pending.pushObject(pim);
if (p.get('status') === constants.ChangeState.Pending || p.get('status') === constants.ChangeState.PendingNew) {
changePending = true;
userHasChangePending = belongsToMe;
}
if (p.get('status') === constants.ChangeState.UnderReview) {
changeAwaitingReview = true;
userHasChangeAwaitingReview = belongsToMe;
}
if (p.get('status') === constants.ChangeState.Rejected) {
changeRejected = p.get('status') === constants.ChangeState.Rejected;
userHasChangeRejected = changeRejected && belongsToMe;
}
}); });
let pi = {
id: p.get('id'),
page: p,
meta: m,
pending: pending,
changePending: changePending,
changeAwaitingReview: changeAwaitingReview,
changeRejected: changeRejected,
userHasChangePending: userHasChangePending,
userHasChangeAwaitingReview: userHasChangeAwaitingReview,
userHasChangeRejected: userHasChangeRejected,
userHasNewPagePending: p.isNewPageUserPending(this.get('sessionService.user.id'))
};
let pim = this.get('store').normalize('page-container', pi);
pim = this.get('store').push(pim);
data.pushObject(pim);
});
return data;
}).catch((error) => {
return error;
});
} }
}); });

View file

@ -19,10 +19,12 @@ export default BaseService.extend({
ajax: service(), ajax: service(),
localStorage: service(), localStorage: service(),
store: service(), store: service(),
// selected folder
currentFolder: null, currentFolder: null,
permissions: {},
init() {
this._super(...arguments);
this.permissions = {};
},
// Add a new folder. // Add a new folder.
add(payload) { add(payload) {

View file

@ -10,7 +10,6 @@
// https://documize.com // https://documize.com
import { Promise as EmberPromise } from 'rsvp'; import { Promise as EmberPromise } from 'rsvp';
import Service, { inject as service } from '@ember/service'; import Service, { inject as service } from '@ember/service';
import netUtil from '../utils/net'; import netUtil from '../utils/net';
@ -19,7 +18,11 @@ export default Service.extend({
ajax: service(), ajax: service(),
appMeta: service(), appMeta: service(),
keycloak: null, keycloak: null,
config: {},
init() {
this._super(...arguments);
this.config = {};
},
boot() { boot() {
return new EmberPromise((resolve, reject) => { return new EmberPromise((resolve, reject) => {

View file

@ -19,9 +19,13 @@ export default Service.extend({
ajax: service(), ajax: service(),
appMeta: service(), appMeta: service(),
store: service(), store: service(),
pins: [],
initialized: false, initialized: false,
init() {
this._super(...arguments);
this.pins = [];
},
getUserPins() { getUserPins() {
let userId = this.get('session.user.id'); let userId = this.get('session.user.id');

View file

@ -10,17 +10,33 @@
// https://documize.com // https://documize.com
import Service, { inject as service } from '@ember/service'; import Service, { inject as service } from '@ember/service';
import { A } from '@ember/array';
import ArrayProxy from '@ember/array/proxy';
export default Service.extend({ export default Service.extend({
sessionService: service('session'), sessionService: service('session'),
ajax: service(), ajax: service(),
store: service(),
// find all matching documents. // find all matching documents
find(payload) { find(payload) {
return this.get('ajax').request("search", { return this.get('ajax').request("search", {
method: "POST", method: "POST",
data: JSON.stringify(payload), data: JSON.stringify(payload),
contentType: 'json' contentType: 'json'
}).then((response) => {
let results = ArrayProxy.create({
content: A([])
});
results = response.map((doc) => {
let data = this.get('store').normalize('doc-search-result', doc);
return this.get('store').push(data);
});
return results;
}).catch((error) => {
return error;
}); });
}, },
}); });

View file

@ -40,6 +40,7 @@ export default SimpleAuthSession.extend({
}), }),
authenticated: computed('session.content.authenticated.user', function () { authenticated: computed('session.content.authenticated.user', function () {
if (is.null(this.get('session.authenticator'))) return false;
return this.get('session.authenticator') !== 'authenticator:anonymous' && this.get('session.content.authenticated.user.id') !== '0'; return this.get('session.authenticator') !== 'authenticator:anonymous' && this.get('session.content.authenticated.user.id') !== '0';
}), }),

View file

@ -0,0 +1,55 @@
.document-copy-move {
> .documents-list {
margin: 0;
padding: 0;
width: 100%;
> .document {
@include ease-in();
margin: 0 0 5px 0;
padding: 10px 15px;
color: $color-gray;
background-color: $color-off-white;
cursor: pointer;
position: relative;
list-style-type: none;
&:hover {
color: $color-black;
}
> .title {
color : $color-off-black;
font-size: 1rem;
}
> .space {
color : $color-gray;
font-size: 0.8rem;
font-style: italic;
font-weight: bold;
margin-bottom: 10px;
}
> .snippet {
color : $color-gray;
font-size: 0.9rem;
}
> .material-icons {
position: absolute;
top: 10px;
right: 10px;
color: $color-white;
}
}
> .selected {
background-color: $color-link !important;
> .title, .space, .snippet {
color: $color-white !important;
}
}
}
}

View file

@ -1,3 +1,7 @@
.document-tabnav {
margin: 50px 0 100px 0;
}
.document-structure { .document-structure {
margin: 0 0 0 0; margin: 0 0 0 0;
@ -15,6 +19,14 @@
display: inline-block; display: inline-block;
} }
> .page-state-pending {
color: $color-red;
}
> .page-state-review {
color: $color-green;
}
> .page-title { > .page-title {
display: inline-block; display: inline-block;
font-size: 1.8rem; font-size: 1.8rem;

View file

@ -1,4 +1,5 @@
@import "add-section.scss"; @import "add-section.scss";
@import "copy-move.scss";
@import "doc-meta.scss"; @import "doc-meta.scss";
@import "doc-structure.scss"; @import "doc-structure.scss";
@import "doc-toc.scss"; @import "doc-toc.scss";
@ -7,4 +8,3 @@
@import "view-activity.scss"; @import "view-activity.scss";
@import "view-revision.scss"; @import "view-revision.scss";
@import "wysiwyg.scss"; @import "wysiwyg.scss";

View file

@ -1,66 +1,64 @@
.view-activity { .view-activity {
> .items { margin: 50px;
list-style-type: none;
margin: 0;
padding: 0;
white-space: nowrap;
> .item { .title {
margin: 0; font-size: 1.8rem;
padding: 15px 0; font-weight: bold;
margin: 0 0 30px 0;
color: $color-gray;
}
> .list {
margin: 0 0 50px 0;
padding: 0;
width: 100%; width: 100%;
> .avatar-box { > .item {
display: inline-block; @include ease-in();
cursor: default; list-style: none;
padding: 10px 0;
margin: 5px 0;
white-space: nowrap;
position: relative; position: relative;
overflow: hidden;
width: 35px;
height: 35px;
line-height: 34px;
padding: 0;
border-radius: 50%;
text-align: center;
font-weight: bold;
background-color: $color-gray;
color: $color-white;
vertical-align: middle;
margin: 0 20px 0 0; > .dash {
position: absolute;
top: 19px;
left: -20px;
background-color: $color-border;
height: 3px;
width: 10px;
} }
> .activity { > .spacer {
display: inline-block;
padding-left: 10px;
}
> .details {
@include ease-in();
vertical-align: top;
display: inline-block; display: inline-block;
> .name { > .doc {
display: inline-block;
font-size: 1.2rem; font-size: 1.2rem;
font-weight: normal;
color: $color-off-black; color: $color-off-black;
font-weight: bold; letter-spacing: 0.5px;
} }
> .detail { > .note {
display: inline-block;
font-size: 1rem;
color: $color-gray; color: $color-gray;
font-size: 1rem;
.viewed { margin-top: 2px;
color: $color-goldy; }
}
}
} }
.added { > .list-timeline {
color: $color-green; border-left: 5px solid $color-border;
} padding-left: 20px;
margin-left: 30px;
.changed {
color: $color-blue;
}
.deleted {
color: $color-red;
}
}
}
}
} }
} }

View file

@ -1,6 +1,6 @@
{{#unless editMode}} {{#unless editMode}}
<div class="view-document"> <div class="view-document">
<div class="document-heading {{if permissions.documentEdit 'cursor-pointer'}}" onclick={{if permissions.documentEdit (action 'toggleEdit')}}> <div class="document-heading {{if canEdit 'cursor-pointer'}}" onclick={{if canEdit (action 'toggleEdit')}}>
<h1 class="doc-title"> <h1 class="doc-title">
{{#if document.template}} {{#if document.template}}
<span class="bg-warning p-1 pr-2 pl-2">Template</span>&nbsp;&nbsp; <span class="bg-warning p-1 pr-2 pl-2">Template</span>&nbsp;&nbsp;

View file

@ -42,6 +42,39 @@
</div> </div>
</div> </div>
<div class="row">
<div class="col-12 col-sm-3 heading">Change Control</div>
<div class="col-12 col-sm-9 value-static">
<span>{{changeControlMsg}}</span>
</div>
</div>
{{#if (eq document.protection constants.ProtectionType.Review)}}
<div class="row">
<div class="col-12 col-sm-3 heading">Approval Process</div>
<div class="col-12 col-sm-9 value-static">
<span>{{approvalMsg}}</span>
</div>
</div>
{{#if userChanges}}
<div class="row">
<div class="col-12 col-sm-3 heading">Your Contributions</div>
<div class="col-12 col-sm-9 value-static">
<span>{{contributorMsg}}</span>
</div>
</div>
{{/if}}
{{#if isApprover}}
<div class="row">
<div class="col-12 col-sm-3 heading">Approver Status</div>
<div class="col-12 col-sm-9 value-static">
<span>{{approverMsg}}</span>
</div>
</div>
{{/if}}
{{/if}}
</div> </div>
</div> </div>

View file

@ -1,12 +1,12 @@
<div id="page-{{page.id}}" class="is-a-page" data-id="{{ page.id }}" data-type="{{page.contentType}}"> <div id="page-{{page.id}}" data-id="{{page.id}}" data-type="{{page.contentType}}">
{{#if editMode}} {{#if editMode}}
<div class="wysiwyg"> <div class="wysiwyg">
{{document/document-editor document=document folder=folder page=page meta=meta onCancel=(action 'onCancelEdit') onAction=(action 'onSavePage')}} {{document/document-editor document=document folder=folder page=editPage meta=editMeta onCancel=(action 'onCancelEdit') onAction=(action 'onSavePage')}}
</div> </div>
{{else}} {{else}}
{{document/page-heading document=document folder=folder page=page permissions=permissions tabMode=tabMode {{document/page-heading document=document folder=folder page=page meta=meta pending=pending permissions=permissions tabMode=tabMode roles=roles blocks=blocks
onEdit=(action 'onEdit') onSavePageAsBlock=(action 'onSavePageAsBlock') onEdit=(action 'onEdit') onSavePageAsBlock=(action 'onSavePageAsBlock')
onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}} onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage') refresh=(action refresh)}}
<div class="wysiwyg"> <div class="wysiwyg">
{{section/base-renderer page=page}} {{section/base-renderer page=page}}
</div> </div>

View file

@ -1,10 +1,9 @@
<div id="doc-toc" class="document-toc {{if isDesktop 'document-toc-desktop'}}"> {{#unless emptyState}}
<div id="doc-toc" class="document-toc {{if isDesktop 'document-toc-desktop'}}">
<div class="header"> <div class="header">
<div class="title">Table of contents</div> <div class="title">Table of contents</div>
{{#if session.authenticated}} {{#if canEdit}}
{{#if permissions.documentEdit}} <div id="tocToolbar" class="toc-controls {{if state.actionablePage 'current-page'}}">
{{#unless emptyState}}
<div id="tocToolbar" class="toc-controls {{if state.actionablePage 'current-page' ''}}">
<div id="toc-up-button" class="button-icon-green button-icon-small {{if state.upDisabled 'disabled'}}" {{action 'pageUp'}}> <div id="toc-up-button" class="button-icon-green button-icon-small {{if state.upDisabled 'disabled'}}" {{action 'pageUp'}}>
<i class="material-icons">arrow_upward</i> <i class="material-icons">arrow_upward</i>
</div> </div>
@ -21,18 +20,27 @@
<i class="material-icons">arrow_forward</i> <i class="material-icons">arrow_forward</i>
</div> </div>
</div> </div>
{{/unless}}
{{/if}}
{{/if}} {{/if}}
</div> </div>
<ul class="index-list"> <ul class="index-list">
{{#each pages key="id" as |page index|}} {{#each pages key="id" as |item index|}}
<li class="item"> <li class="item">
<a id="index-{{page.id}}" {{action 'onEntryClick' page.id}} class="link toc-index-item {{page.tocIndentCss}} {{if (eq page.id currentPageId) 'selected'}}"> <a id="index-{{item.page.id}}" {{action 'onGotoPage' item.page.id}}
<span class="numbering">{{page.numbering}}</span>{{page.title}} class="link toc-index-item {{item.page.tocIndentCss}} {{if (eq item.page.id state.pageId) 'selected'}}">
<span class="numbering">{{item.page.numbering}}</span>
{{#if (or item.userHasChangePending userHasNewPagePending)}}
<span class="color-red" title="Pending approval" data-toggle="tooltip" data-placement="top">[*]&nbsp;</span>
{{/if}}
{{#if (or permissions.documentApprove roles.documentApprove)}}
{{#if item.changeAwaitingReview}}
<span class="color-green" title="Awaiting approval" data-toggle="tooltip" data-placement="top">[*]&nbsp;</span>
{{/if}}
{{/if}}
{{item.page.title}}
</a> </a>
</li> </li>
{{/each}} {{/each}}
</ul> </ul>
</div> </div>
{{/unless}}

View file

@ -1,5 +1,4 @@
<div class="row no-gutters align-items-center"> <div class="row no-gutters align-items-center">
<div class="col-10"> <div class="col-10">
<div class="document-structure"> <div class="document-structure">
<div class="page-header"> <div class="page-header">
@ -8,20 +7,13 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-2"> <div class="col-2">
{{#unless (eq document.protection constants.ProtectionType.Lock)}}
<div class="document-structure"> <div class="document-structure">
<div class="page-toolbar" id="page-toolbar-{{ page.id }}"> <div class="page-toolbar" id="page-toolbar-{{ page.id }}">
{{#if permissions.documentEdit}}
<div class="float-right"> <div class="float-right">
{{#if (is-equal page.pageType 'tab')}} {{#if canEdit}}
{{#link-to 'document.section' page.id}} <div id="page-edit-button-{{page.id}}" class="button-icon-gray align-middle d-none d-md-inline-block" {{action 'onEdit'}}>
<div id="page-edit-button-{{page.id}}" class="button-icon-gray align-middle">
<i class="material-icons">mode_edit</i>
</div>
{{/link-to}}
{{else}}
<div id="page-edit-button-{{page.id}}" class="button-icon-gray align-middle" {{action 'onEdit'}}>
<i class="material-icons">mode_edit</i> <i class="material-icons">mode_edit</i>
</div> </div>
{{/if}} {{/if}}
@ -31,27 +23,31 @@
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</div> </div>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="page-menu-button-{{page.id}}"> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="page-menu-button-{{page.id}}">
{{#if canEdit}}
<a class="dropdown-item" href="#" id={{concat 'edit-page-button-' page.id}} {{action 'onEdit'}}>Edit</a>
{{/if}}
{{#if permissions.documentCopy}} {{#if permissions.documentCopy}}
<a class="dropdown-item" href="#" id={{concat 'copy-page-button-' page.id}} data-toggle="modal" data-target={{concat '#copy-page-modal-' page.id}} data-backdrop="static">Copy</a> <a class="dropdown-item" href="#" id={{concat 'copy-page-button-' page.id}} data-toggle="modal" data-target={{concat '#copy-page-modal-' page.id}} data-backdrop="static">Copy</a>
{{/if}} {{/if}}
{{#if permissions.documentMove}} {{#if canMove}}
<a class="dropdown-item" href="#" id={{concat 'move-page-button-' page.id}} data-toggle="modal" data-target={{concat '#move-page-modal-' page.id}} data-backdrop="static">Move</a> <a class="dropdown-item" href="#" id={{concat 'move-page-button-' page.id}} data-toggle="modal" data-target={{concat '#move-page-modal-' page.id}} data-backdrop="static">Move</a>
{{/if}} {{/if}}
{{#if permissions.documentTemplate}} {{#if permissions.documentTemplate}}
<a class="dropdown-item" href="#" id={{concat 'publish-page-button-' page.id}} data-toggle="modal" data-target={{concat '#publish-page-modal-' page.id}} data-backdrop="static">Publish</a> <a class="dropdown-item" href="#" id={{concat 'publish-page-button-' page.id}} data-toggle="modal" data-target={{concat '#publish-page-modal-' page.id}} data-backdrop="static">Publish</a>
{{/if}} {{/if}}
{{#if permissions.documentDelete}} {{#if canDelete}}
<a class="dropdown-item text-danger" href="#" id={{concat 'delete-page-button-' page.id}} data-toggle="modal" data-target={{concat '#delete-page-modal-' page.id}} data-backdrop="static">Delete</a> <a class="dropdown-item text-danger" href="#" id={{concat 'delete-page-button-' page.id}} data-toggle="modal" data-target={{concat '#delete-page-modal-' page.id}} data-backdrop="static">Delete</a>
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}
</div> </div>
{{/if}}
</div> </div>
</div> </div>
{{/unless}}
</div> </div>
</div>
{{#if permissions.documentCopy}} {{#if permissions.documentCopy}}
<div id={{concat 'copy-page-modal-' page.id}} class="modal" tabindex="-1" role="dialog"> <div id={{concat 'copy-page-modal-' page.id}} class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
@ -59,7 +55,24 @@
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
<label>Destination Document</label> <label>Destination Document</label>
{{ui/ui-list-picker items=documentList nameField='name' singleSelect=true maxHeight=300}} {{focus-input type="text" value=docSearchFilter class="form-control mb-4" placeholder='a OR b, x AND y, "phrase mat*"'}}
</div>
<div class="my-5 document-copy-move">
{{#if emptySearch}}
<p>No matching documents found.</p>
{{/if}}
<ul class="documents-list">
{{#each docSearchResults key="id" as |result index|}}
<li class="document {{if result.selected 'selected'}}" {{action 'onSelectSearchResult' result.documentId}}>
<div class="title">{{result.document}}</div>
<div class="space">{{result.space}}</div>
<div class="snippet">{{result.excerpt}}</div>
{{#if result.selected}}
<i class="material-icons">check</i>
{{/if}}
</li>
{{/each}}
</ul>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@ -69,9 +82,9 @@
</div> </div>
</div> </div>
</div> </div>
{{/if}} {{/if}}
{{#if permissions.documentMove}} {{#if permissions.documentMove}}
<div id={{concat 'move-page-modal-' page.id}} class="modal" tabindex="-1" role="dialog"> <div id={{concat 'move-page-modal-' page.id}} class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
@ -79,7 +92,24 @@
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
<label>Destination Document</label> <label>Destination Document</label>
{{ui/ui-list-picker items=documentListOthers nameField='name' singleSelect=true maxHeight=300}} {{focus-input type="text" value=docSearchFilter class="form-control mb-4" placeholder='a OR b, x AND y, "phrase mat*"'}}
</div>
<div class="my-5 document-copy-move">
{{#if emptySearch}}
<p>No matching documents found.</p>
{{/if}}
<ul class="documents-list">
{{#each docSearchResults key="id" as |result index|}}
<li class="document {{if result.selected 'selected'}}" {{action 'onSelectSearchResult' result.documentId}}>
<div class="title">{{result.document}}</div>
<div class="space">{{result.space}}</div>
<div class="snippet">{{result.excerpt}}</div>
{{#if result.selected}}
<i class="material-icons">check</i>
{{/if}}
</li>
{{/each}}
</ul>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@ -89,17 +119,17 @@
</div> </div>
</div> </div>
</div> </div>
{{/if}} {{/if}}
{{#if permissions.documentDelete}} {{#if permissions.documentDelete}}
<div id={{concat 'delete-page-modal-' page.id}} class="modal" tabindex="-1" role="dialog"> <div id={{concat 'delete-page-modal-' page.id}} class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header">Delete Section</div> <div class="modal-header">Delete Section</div>
<div class="modal-body"> <div class="modal-body">
<p>Are you shire you want to delete this section?</p> <p>Are you sure you want to delete this section?</p>
<label class="form-check-label"> <label class="form-check-label">
<input type="checkbox" class="form-check-input" checked={{deleteChildren}} /> {{input type="checkbox" class="form-check-input" checked=deleteChildren}}
Also delete child sections Also delete child sections
</label> </label>
</div> </div>
@ -110,9 +140,9 @@
</div> </div>
</div> </div>
</div> </div>
{{/if}} {{/if}}
{{#if permissions.documentTemplate}} {{#if permissions.documentTemplate}}
<div id={{concat 'publish-page-modal-' page.id}} class="modal" tabindex="-1" role="dialog"> <div id={{concat 'publish-page-modal-' page.id}} class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
@ -136,6 +166,4 @@
</div> </div>
</div> </div>
</div> </div>
{{/if}} {{/if}}
</div>

View file

@ -1,13 +0,0 @@
<div class="view-activity mt-5">
<ul class="items">
{{#each activity as |a|}}
<li class="item">
<div class="avatar-box">{{user-initials a.firstname a.lastname}}</div>
<div class="activity">
<div class="name">{{a.firstname}} {{a.lastname}}&nbsp;&nbsp;</div>
<div class="detail {{a.activityColor}}">{{a.activityLabel}} on {{formatted-date a.created}}</div>
</div>
</li>
{{/each}}
</ul>
</div>

View file

@ -8,7 +8,7 @@
<a href="{{appMeta.endpoint}}/public/attachments/{{appMeta.orgId}}/{{a.id}}"> <a href="{{appMeta.endpoint}}/public/attachments/{{appMeta.orgId}}/{{a.id}}">
<span class="file">{{ a.filename }}</span> <span class="file">{{ a.filename }}</span>
</a> </a>
{{#if permissions.documentEdit}} {{#if canEdit}}
<div class="button-icon-danger align-middle action" {{action 'onShowDialog' a.id a.filename}}> <div class="button-icon-danger align-middle action" {{action 'onShowDialog' a.id a.filename}}>
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
</div> </div>
@ -17,7 +17,7 @@
{{/each}} {{/each}}
</ul> </ul>
{{/if}} {{/if}}
{{#if permissions.documentEdit}} {{#if canEdit}}
<div class="upload-document-files"> <div class="upload-document-files">
<div id="upload-document-files" class="btn btn-secondary">Upload</div> <div id="upload-document-files" class="btn btn-secondary">Upload</div>
</div> </div>
@ -27,5 +27,4 @@
{{#ui/ui-dialog title="Delete Attachment" confirmCaption="Delete" buttonType="btn-danger" show=showDialog onAction=(action 'onDelete')}} {{#ui/ui-dialog title="Delete Attachment" confirmCaption="Delete" buttonType="btn-danger" show=showDialog onAction=(action 'onDelete')}}
<p>Are you sure you want to delete {{deleteAttachment.name}}?</p> <p>Are you sure you want to delete {{deleteAttachment.name}}?</p>
{{/ui/ui-dialog}} {{/ui/ui-dialog}}
{{/if}} {{/if}}

Some files were not shown because too many files have changed in this diff Show more