diff --git a/README.md b/README.md index bb9b0a03..e89935f4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> We provide frequent product releases to ensure self-host customers enjoy the same product as our Cloud/SaaS platform customers. +> We're committed to providing frequent product releases to ensure self-host customers enjoy the same product as our cloud/SaaS customers. > > Harvey Kandola, CEO & Founder, Documize Inc. diff --git a/gui/app/components/customize/auth-settings.js b/gui/app/components/customize/auth-settings.js index 808df946..ef20d872 100644 --- a/gui/app/components/customize/auth-settings.js +++ b/gui/app/components/customize/auth-settings.js @@ -14,10 +14,11 @@ import { empty } from '@ember/object/computed'; import { set } from '@ember/object'; import { copy } from '@ember/object/internals'; import { inject as service } from '@ember/service'; -import Component from '@ember/component'; +import Notifier from '../../mixins/notifier'; import encoding from '../../utils/encoding'; +import Component from '@ember/component'; -export default Component.extend({ +export default Component.extend(Notifier, { appMeta: service(), isDocumizeProvider: computed('authProvider', function() { return this.get('authProvider') === this.get('constants').AuthProvider.Documize; @@ -139,6 +140,8 @@ export default Component.extend({ break; } + this.showWait(); + let data = { authProvider: provider, authConfig: JSON.stringify(config) }; this.get('onSave')(data).then(() => { @@ -158,6 +161,7 @@ export default Component.extend({ } }); } + this.showDone(); }); } } diff --git a/gui/app/components/customize/general-settings.js b/gui/app/components/customize/general-settings.js index 01df6ae4..09ff99fb 100644 --- a/gui/app/components/customize/general-settings.js +++ b/gui/app/components/customize/general-settings.js @@ -11,11 +11,12 @@ import $ from 'jquery'; import { empty, and } from '@ember/object/computed'; -import Component from '@ember/component'; import { isEmpty } from '@ember/utils'; import { set } from '@ember/object'; +import Notifier from '../../mixins/notifier'; +import Component from '@ember/component'; -export default Component.extend({ +export default Component.extend(Notifier, { titleEmpty: empty('model.general.title'), messageEmpty: empty('model.general.message'), conversionEndpointEmpty: empty('model.general.conversionEndpoint'), @@ -47,7 +48,10 @@ export default Component.extend({ this.model.general.set('allowAnonymousAccess', $("#allowAnonymousAccess").prop('checked')); + this.showWait(); + this.get('save')().then(() => { + this.showDone(); set(this, 'titleError', false); set(this, 'messageError', false); set(this, 'conversionEndpointError', false); diff --git a/gui/app/components/customize/license-key.js b/gui/app/components/customize/license-key.js index 264d7c66..748ee45f 100644 --- a/gui/app/components/customize/license-key.js +++ b/gui/app/components/customize/license-key.js @@ -12,9 +12,10 @@ import $ from 'jquery'; import { empty } from '@ember/object/computed'; import { inject as service } from '@ember/service'; +import Notifier from '../../mixins/notifier'; import Component from '@ember/component'; -export default Component.extend({ +export default Component.extend(Notifier, { appMeta: service(), LicenseError: empty('model.license'), changelog: '', @@ -36,7 +37,9 @@ export default Component.extend({ actions: { saveLicense() { + this.showWait(); this.get('saveLicense')().then(() => { + this.showDone(); window.location.reload(); }); } diff --git a/gui/app/components/customize/smtp-settings.js b/gui/app/components/customize/smtp-settings.js index 372ca1c9..77780869 100644 --- a/gui/app/components/customize/smtp-settings.js +++ b/gui/app/components/customize/smtp-settings.js @@ -11,9 +11,10 @@ import $ from 'jquery'; import { empty } from '@ember/object/computed'; +import Notifier from '../../mixins/notifier'; import Component from '@ember/component'; -export default Component.extend({ +export default Component.extend(Notifier, { SMTPHostEmptyError: empty('model.smtp.host'), SMTPPortEmptyError: empty('model.smtp.port'), SMTPSenderEmptyError: empty('model.smtp.sender'), @@ -47,9 +48,11 @@ export default Component.extend({ }, ); + this.showWait(); this.set('buttonText', 'Please wait...'); this.get('saveSMTP')().then((result) => { + this.showDone(); this.set('buttonText', 'Save & Test'); this.set('testSMTP', result); }); diff --git a/gui/app/components/document/block-editor.js b/gui/app/components/document/block-editor.js index 5847e644..3f1c9c73 100644 --- a/gui/app/components/document/block-editor.js +++ b/gui/app/components/document/block-editor.js @@ -21,7 +21,7 @@ export default Component.extend({ p.set('id', this.get('block.id')); p.set('orgId', this.get('block.orgId')); - p.set('documentId', this.get('document.id')); + p.set('documentId', 'dummy'); p.set('contentType', this.get('block.contentType')); p.set('pageType', this.get('block.pageType')); p.set('title', this.get('block.title')); @@ -31,7 +31,7 @@ export default Component.extend({ m.set('pageId', this.get('block.id')); m.set('orgId', this.get('block.orgId')); - m.set('documentId', this.get('document.id')); + m.set('documentId', 'dummy'); m.set('rawBody', this.get('block.rawBody')); m.set('config', this.get('block.config')); m.set('externalSource', this.get('block.externalSource')); diff --git a/gui/app/components/document/view-content.js b/gui/app/components/document/view-content.js index 0b6846cb..a21eecba 100644 --- a/gui/app/components/document/view-content.js +++ b/gui/app/components/document/view-content.js @@ -175,23 +175,6 @@ export default Component.extend(TooltipMixin, Notifier, { this.set('showDeleteBlockDialog', true); }, - onDeleteBlock() { - this.set('showDeleteBlockDialog', false); - - let id = this.get('deleteBlockId'); - - let cb = this.get('onDeleteBlock'); - let promise = cb(id); - - promise.then(() => { - this.set('deleteBlockId', ''); - let refresh = this.get('refresh'); - refresh(); - }); - - return true; - }, - onVote(vote) { this.get('documentService').vote(this.get('document.id'), vote); this.set('voteThanks', true); diff --git a/gui/app/components/folder/settings-blocks.js b/gui/app/components/folder/settings-blocks.js new file mode 100644 index 00000000..49ad6b01 --- /dev/null +++ b/gui/app/components/folder/settings-blocks.js @@ -0,0 +1,69 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// https://documize.com + +import { inject as service } from '@ember/service'; +import { computed } from '@ember/object'; +import AuthMixin from '../../mixins/auth'; +import Notifier from '../../mixins/notifier'; +import Component from '@ember/component'; + +export default Component.extend(AuthMixin, Notifier, { + router: service(), + spaceSvc: service('folder'), + sectionSvc: service('section'), + + showDeleteDialog: false, + deleteBlockId: '', + + isSpaceAdmin: computed('permissions', function() { + return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage'); + }), + + didReceiveAttrs() { + this._super(...arguments); + + if (!this.get('isSpaceAdmin')) return; + + this.get('sectionSvc').getSpaceBlocks(this.get('space.id')).then((blocks) => { + this.set('blocks', blocks); + }); + }, + + actions: { + onShowDeleteDialog(id) { + this.set('showDeleteDialog', true); + this.set('deleteBlockId', id); + }, + + onEdit(id) { + this.get('router').transitionTo('folder.block', this.get('space.id'), this.get('space.slug'), id); + }, + + onDeleteBlock() { + this.set('showDeleteDialog', false); + + let id = this.get('deleteBlockId'); + + this.showWait(); + + this.get('sectionSvc').deleteBlock(id).then(() => { + this.set('deleteBlockId', ''); + this.showDone(); + + this.get('sectionSvc').getSpaceBlocks(this.get('space.id')).then((blocks) => { + this.set('blocks', blocks); + }); + }); + + return true; + } + } +}); diff --git a/gui/app/components/folder/settings-general.js b/gui/app/components/folder/settings-general.js new file mode 100644 index 00000000..746f928b --- /dev/null +++ b/gui/app/components/folder/settings-general.js @@ -0,0 +1,84 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// https://documize.com + +import { A } from '@ember/array'; +import { inject as service } from '@ember/service'; +import { schedule } from '@ember/runloop'; +import { computed } from '@ember/object'; +import AuthMixin from '../../mixins/auth'; +import Notifier from '../../mixins/notifier'; +import Component from '@ember/component'; + +export default Component.extend(AuthMixin, Notifier, { + router: service(), + spaceSvc: service('folder'), + localStorage: service('localStorage'), + + isSpaceAdmin: computed('permissions', function() { + return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage'); + }), + + spaceTypeOptions: A([]), + spaceType: 0, + likes: 'Did this help you?', + allowLikes: false, + + init() { + this._super(...arguments); + }, + + didReceiveAttrs() { + this._super(...arguments); + + let constants = this.get('constants'); + let folder = this.get('space'); + + let spaceTypeOptions = A([]); + spaceTypeOptions.pushObject({id: constants.FolderType.Private, label: 'Private - viewable only by me'}); + spaceTypeOptions.pushObject({id: constants.FolderType.Protected, label: 'Protected - access is restricted to selected users'}); + spaceTypeOptions.pushObject({id: constants.FolderType.Public, label: 'Public - can be seen by everyone'}); + this.set('spaceTypeOptions', spaceTypeOptions); + this.set('spaceType', spaceTypeOptions.findBy('id', folder.get('folderType'))); + + this.set('likes', folder.get('likes')); + this.set('allowLikes', folder.get('allowLikes')); + }, + + actions: { + onSetSpaceType(t) { + this.set('spaceType', t); + }, + + onSetLikes(l) { + this.set('allowLikes', l); + + schedule('afterRender', () => { + if (l) this.$('#space-likes-prompt').focus(); + }); + }, + + onSave() { + if (!this.get('isSpaceAdmin')) return; + + let space = this.get('space'); + space.set('folderType', this.get('spaceType.id')); + + let allowLikes = this.get('allowLikes'); + space.set('likes', allowLikes ? this.get('likes') : ''); + + this.showWait(); + + this.get('spaceSvc').save(space).then(() => { + this.showDone(); + }); + } + } +}); diff --git a/gui/app/components/folder/settings-invitations.js b/gui/app/components/folder/settings-invitations.js new file mode 100644 index 00000000..195c485f --- /dev/null +++ b/gui/app/components/folder/settings-invitations.js @@ -0,0 +1,89 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// https://documize.com + +import { inject as service } from '@ember/service'; +import { computed } from '@ember/object'; +import AuthMixin from '../../mixins/auth'; +import Notifier from '../../mixins/notifier'; +import Component from '@ember/component'; + +export default Component.extend(AuthMixin, Notifier, { + spaceSvc: service('folder'), + + isSpaceAdmin: computed('permissions', function() { + return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage'); + }), + + inviteEmail: '', + inviteMessage: '', + + init() { + this._super(...arguments); + }, + + didReceiveAttrs() { + this._super(...arguments); + + if (this.get('inviteMessage').length === 0) { + this.set('inviteMessage', this.getDefaultInvitationMessage()); + } + }, + + getDefaultInvitationMessage() { + return "Hey there, I am sharing the " + this.get('space.name') + " space (in " + this.get("appMeta.title") + ") with you so we can both collaborate on documents."; + }, + + actions: { + onSpaceInvite(e) { + e.preventDefault(); + + var email = this.get('inviteEmail').trim().replace(/ /g, ''); + var message = this.get('inviteMessage').trim(); + + if (message.length === 0) { + this.set('inviteMessage', this.getDefaultInvitationMessage()); + message = this.getDefaultInvitationMessage(); + } + + if (email.length === 0) { + this.$('#space-invite-email').addClass('is-invalid').focus(); + return; + } + + this.showWait(); + + var result = { + Message: message, + Recipients: [] + }; + + // Check for multiple email addresses + if (email.indexOf(",") > -1) { + result.Recipients = email.split(','); + } + if (email.indexOf(";") > -1 && result.Recipients.length === 0) { + result.Recipients = email.split(';'); + } + + // Handle just one email address + if (result.Recipients.length === 0 && email.length > 0) { + result.Recipients.push(email); + } + + this.set('inviteEmail', ''); + + this.get('spaceSvc').share(this.get('space.id'), result).then(() => { + this.showDone(); + this.$('#space-invite-email').removeClass('is-invalid'); + }); + }, + } +}); diff --git a/gui/app/components/folder/permission-admin.js b/gui/app/components/folder/settings-permissions.js similarity index 94% rename from gui/app/components/folder/permission-admin.js rename to gui/app/components/folder/settings-permissions.js index 8fc6ff03..ea2f8604 100644 --- a/gui/app/components/folder/permission-admin.js +++ b/gui/app/components/folder/settings-permissions.js @@ -12,11 +12,12 @@ import { inject as service } from '@ember/service'; import { A } from '@ember/array'; import { debounce } from '@ember/runloop'; -import ModalMixin from '../../mixins/modal'; +import { computed } from '@ember/object'; +import Notifier from '../../mixins/notifier'; import stringUtil from '../../utils/string'; import Component from '@ember/component'; -export default Component.extend(ModalMixin, { +export default Component.extend(Notifier, { groupSvc: service('group'), spaceSvc: service('folder'), userSvc: service('user'), @@ -27,6 +28,10 @@ export default Component.extend(ModalMixin, { users: null, searchText: '', + isSpaceAdmin: computed('permissions', function() { + return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage'); + }), + didReceiveAttrs() { this._super(...arguments); @@ -128,7 +133,11 @@ export default Component.extend(ModalMixin, { }, actions: { - setPermissions() { + onSave() { + if (!this.get('isSpaceAdmin')) return; + + this.showWait(); + let message = this.getDefaultInvitationMessage(); let permissions = this.get('spacePermissions'); let folder = this.get('folder'); @@ -164,7 +173,7 @@ export default Component.extend(ModalMixin, { } this.get('spaceSvc').savePermissions(folder.get('id'), payload).then(() => { - this.modalClose('#space-permission-modal'); + this.showDone(); this.get('onRefresh')(); }); }, diff --git a/gui/app/components/folder/settings-templates.js b/gui/app/components/folder/settings-templates.js new file mode 100644 index 00000000..5c62df30 --- /dev/null +++ b/gui/app/components/folder/settings-templates.js @@ -0,0 +1,37 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// https://documize.com + +import { inject as service } from '@ember/service'; +import { computed } from '@ember/object'; +import stringUtil from '../../utils/string'; +import AuthMixin from '../../mixins/auth'; +import Notifier from '../../mixins/notifier'; +import Component from '@ember/component'; + +export default Component.extend(AuthMixin, Notifier, { + spaceSvc: service('folder'), + + isSpaceAdmin: computed('permissions', function() { + return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage'); + }), + + actions: { + onOpenTemplate(id) { + if (is.empty(id)) { + return; + } + let template = this.get('templates').findBy('id', id) + + let slug = stringUtil.makeSlug(template.get('title')); + this.get('router').transitionTo('document', this.get('space.id'), this.get('space.slug'), id, slug); + } + } +}); diff --git a/gui/app/components/folder/space-view.js b/gui/app/components/folder/space-view.js index 76c76b38..c1e001c3 100644 --- a/gui/app/components/folder/space-view.js +++ b/gui/app/components/folder/space-view.js @@ -60,10 +60,6 @@ export default Component.extend(AuthMixin, { this.send('onDocumentFilter', 'category', this.get('categoryFilter')); } else { this.send('onDocumentFilter', 'space', this.get('folder.id')); - // } else if (this.get('rootDocCount') > 0) { - // this.send('onDocumentFilter', 'space', this.get('folder.id')); - // } else if (selectedCategory !== '') { - // this.send('onDocumentFilter', 'category', selectedCategory); } }); }, diff --git a/gui/app/components/section/wysiwyg/type-editor.js b/gui/app/components/section/wysiwyg/type-editor.js index a05453a4..5c8ef916 100644 --- a/gui/app/components/section/wysiwyg/type-editor.js +++ b/gui/app/components/section/wysiwyg/type-editor.js @@ -23,10 +23,6 @@ export default Component.extend({ let page = this.get('page'); return `wysiwyg-editor-${page.id}`; }), - toolbarId: computed('page', function () { - let page = this.get('page'); - return `wysiwyg-editor-toolbar-${page.id}`; - }), didReceiveAttrs() { this._super(...arguments); @@ -45,7 +41,6 @@ export default Component.extend({ gecko_spellcheck: false, statusbar: false, inline: true, - // fixed_toolbar_container: '#' + this.get('toolbarId'), paste_data_images: true, image_advtab: true, image_caption: true, diff --git a/gui/app/components/toolbar/for-space.js b/gui/app/components/toolbar/for-space.js index 3c318c35..8e3b46b9 100644 --- a/gui/app/components/toolbar/for-space.js +++ b/gui/app/components/toolbar/for-space.js @@ -12,12 +12,10 @@ import $ from 'jquery'; import { computed } from '@ember/object'; import { schedule } from '@ember/runloop'; -import { A } from '@ember/array'; import { inject as service } from '@ember/service'; import TooltipMixin from '../../mixins/tooltip'; import ModalMixin from '../../mixins/modal'; import AuthMixin from '../../mixins/auth'; -import stringUtil from '../../utils/string'; import Component from '@ember/component'; export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, { @@ -36,8 +34,7 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, { return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage'); }), deleteSpaceName: '', - inviteEmail: '', - inviteMessage: '', + hasTemplates: computed('templates', function() { return this.get('templates.length') > 0; }), @@ -49,11 +46,6 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, { dropzone: null, - spaceTypeOptions: A([]), - spaceType: 0, - likes: '', - allowLikes: false, - init() { this._super(...arguments); this.importedDocuments = []; @@ -68,7 +60,6 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, { didReceiveAttrs() { this._super(...arguments); - let constants = this.get('constants'); let folder = this.get('space'); let targets = _.reject(this.get('spaces'), {id: folder.get('id')}); @@ -80,20 +71,6 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, { this.set('pinState.newName', folder.get('name')); this.renderTooltips(); }); - - if (this.get('inviteMessage').length === 0) { - this.set('inviteMessage', this.getDefaultInvitationMessage()); - } - - let spaceTypeOptions = A([]); - spaceTypeOptions.pushObject({id: constants.FolderType.Private, label: 'Private - viewable only by me'}); - spaceTypeOptions.pushObject({id: constants.FolderType.Protected, label: 'Protected - access is restricted to selected users'}); - spaceTypeOptions.pushObject({id: constants.FolderType.Public, label: 'Public - can be seen by everyone'}); - this.set('spaceTypeOptions', spaceTypeOptions); - this.set('spaceType', spaceTypeOptions.findBy('id', folder.get('folderType'))); - - this.set('likes', folder.get('likes')); - this.set('allowLikes', folder.get('allowLikes')); }, didInsertElement() { @@ -112,10 +89,6 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, { } }, - getDefaultInvitationMessage() { - return "Hey there, I am sharing the " + this.get('space.name') + " space (in " + this.get("appMeta.title") + ") with you so we can both collaborate on documents."; - }, - setupImport() { // already done init? if (is.not.null(this.get('dropzone'))) { @@ -194,48 +167,6 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, { return true; }, - onSpaceInvite(e) { - e.preventDefault(); - - var email = this.get('inviteEmail').trim().replace(/ /g, ''); - var message = this.get('inviteMessage').trim(); - - if (message.length === 0) { - message = this.getDefaultInvitationMessage(); - } - - if (email.length === 0) { - $('#space-invite-email').addClass('is-invalid').focus(); - return; - } - - var result = { - Message: message, - Recipients: [] - }; - - // Check for multiple email addresses - if (email.indexOf(",") > -1) { - result.Recipients = email.split(','); - } - if (email.indexOf(";") > -1 && result.Recipients.length === 0) { - result.Recipients = email.split(';'); - } - - // Handle just one email address - if (result.Recipients.length === 0 && email.length > 0) { - result.Recipients.push(email); - } - - this.set('inviteEmail', ''); - - this.get('spaceService').share(this.get('space.id'), result).then(() => { - $('#space-invite-email').removeClass('is-invalid'); - }); - - this.modalClose('#space-invite-modal'); - }, - onSpaceDelete(e) { e.preventDefault(); @@ -359,46 +290,6 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, { let cb = this.get('onRefresh'); cb(); } - }, - - onOpenTemplate(e) { - e.preventDefault(); - - let id = this.get('selectedTemplate'); - if (is.empty(id)) { - return; - } - let template = this.get('templates').findBy('id', id) - - this.modalClose("#space-template-modal"); - - let slug = stringUtil.makeSlug(template.get('title')); - this.get('router').transitionTo('document', this.get('space.id'), this.get('space.slug'), id, slug); - }, - - onSetSpaceType(t) { - this.set('spaceType', t); - }, - - onSetLikes(l) { - this.set('allowLikes', l); - schedule('afterRender', () => { - if (l) $('#space-likes-prompt').focus(); - }); - - }, - - onSpaceSettings() { - let space = this.get('space'); - space.set('folderType', this.get('spaceType.id')); - - let allowLikes = this.get('allowLikes'); - space.set('likes', allowLikes ? this.get('likes') : ''); - - this.get('spaceService').save(space).then(() => { - }); - - this.modalClose("#space-settings-modal"); } } }); diff --git a/gui/app/pods/document/index/controller.js b/gui/app/pods/document/index/controller.js index 9b32d2cc..937bb642 100644 --- a/gui/app/pods/document/index/controller.js +++ b/gui/app/pods/document/index/controller.js @@ -167,14 +167,6 @@ export default Controller.extend(Tooltips, Notifier, { }); }, - onDeleteBlock(blockId) { - return new EmberPromise((resolve) => { - this.get('sectionService').deleteBlock(blockId).then(() => { - resolve(); - }); - }); - }, - onSavePageAsBlock(block) { return new EmberPromise((resolve) => { this.get('sectionService').addBlock(block).then(() => { diff --git a/gui/app/pods/document/index/route.js b/gui/app/pods/document/index/route.js index 88e13dc7..13a417d6 100644 --- a/gui/app/pods/document/index/route.js +++ b/gui/app/pods/document/index/route.js @@ -11,8 +11,8 @@ import { Promise as EmberPromise, hash } from 'rsvp'; import { inject as service } from '@ember/service'; -import Route from '@ember/routing/route'; import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; +import Route from '@ember/routing/route'; export default Route.extend(AuthenticatedRouteMixin, { documentService: service('document'), diff --git a/gui/app/pods/document/index/template.hbs b/gui/app/pods/document/index/template.hbs index b7a9b6af..df21532e 100644 --- a/gui/app/pods/document/index/template.hbs +++ b/gui/app/pods/document/index/template.hbs @@ -69,7 +69,6 @@ onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onPageDeleted') - onDeleteBlock=(action 'onDeleteBlock') onInsertSection=(action 'onInsertSection') onSavePageAsBlock=(action 'onSavePageAsBlock') onPageLevelChange=(action 'onPageLevelChange') diff --git a/gui/app/pods/document/block/controller.js b/gui/app/pods/folder/block/controller.js similarity index 67% rename from gui/app/pods/document/block/controller.js rename to gui/app/pods/folder/block/controller.js index 170f301d..3d22b8e4 100644 --- a/gui/app/pods/document/block/controller.js +++ b/gui/app/pods/folder/block/controller.js @@ -10,18 +10,20 @@ // https://documize.com import { inject as service } from '@ember/service'; +import Notifier from '../../../mixins/notifier'; import Controller from '@ember/controller'; -export default Controller.extend({ - sectionService: service('section'), +export default Controller.extend(Notifier, { + router: service(), + sectionSvc: service('section'), actions: { onCancel( /*page*/ ) { - this.transitionToRoute('document'); + this.get('router').transitionTo('folder.settings', {queryParams: {tab: 'blocks'}}); }, onAction(page, meta) { - let self = this; + this.showWait(); let b = this.get('model.block'); b.set('title', page.get('title')); @@ -31,8 +33,9 @@ export default Controller.extend({ b.set('config', meta.get('config')); b.set('externalSource', meta.get('externalSource')); - this.get('sectionService').updateBlock(b).then(function () { - self.transitionToRoute('document'); + this.get('sectionSvc').updateBlock(b).then(() => { + this.showDone(); + this.get('router').transitionTo('folder.settings', {queryParams: {tab: 'blocks'}}); }); } } diff --git a/gui/app/pods/document/block/route.js b/gui/app/pods/folder/block/route.js similarity index 73% rename from gui/app/pods/document/block/route.js rename to gui/app/pods/folder/block/route.js index 33f12b29..3c8af87a 100644 --- a/gui/app/pods/document/block/route.js +++ b/gui/app/pods/folder/block/route.js @@ -11,21 +11,19 @@ import { hash } from 'rsvp'; import { inject as service } from '@ember/service'; -import Route from '@ember/routing/route'; import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; +import Route from '@ember/routing/route'; export default Route.extend(AuthenticatedRouteMixin, { - documentService: service('document'), - folderService: service('folder'), - sectionService: service('section'), + sectionSvc: service('section'), model(params) { let self = this; return hash({ - folder: self.modelFor('document').folder, - document: self.modelFor('document').document, - block: self.get('sectionService').getBlock(params.block_id), + space: this.modelFor('folder').folder, + permissions: this.modelFor('folder').permissions, + block: self.get('sectionSvc').getBlock(params.block_id), }); }, }); diff --git a/gui/app/pods/document/block/template.hbs b/gui/app/pods/folder/block/template.hbs similarity index 100% rename from gui/app/pods/document/block/template.hbs rename to gui/app/pods/folder/block/template.hbs diff --git a/gui/app/pods/folder/settings/controller.js b/gui/app/pods/folder/settings/controller.js new file mode 100644 index 00000000..7ac3f0bc --- /dev/null +++ b/gui/app/pods/folder/settings/controller.js @@ -0,0 +1,30 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// https://documize.com + +import { inject as service } from '@ember/service'; +import NotifierMixin from '../../../mixins/notifier'; +import Controller from '@ember/controller'; + +export default Controller.extend(NotifierMixin, { + folderService: service('folder'), + localStorage: service('localStorage'), + tab: 'general', + + actions: { + onTab(view) { + this.set('tab', view); + }, + + onRefresh() { + this.get('target._routerMicrolib').refresh(); + } + } +}); diff --git a/gui/app/pods/folder/settings/route.js b/gui/app/pods/folder/settings/route.js new file mode 100644 index 00000000..d95caf15 --- /dev/null +++ b/gui/app/pods/folder/settings/route.js @@ -0,0 +1,29 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// https://documize.com + +import { hash } from 'rsvp'; +// import { inject as service } from '@ember/service'; +import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; +import Route from '@ember/routing/route'; + +export default Route.extend(AuthenticatedRouteMixin, { + + model() { + this.get('browser').setTitle(this.modelFor('folder').folder.get('name')); + + return hash({ + folder: this.modelFor('folder').folder, + folders: this.modelFor('folder').folders, + permissions: this.modelFor('folder').permissions, + templates: this.modelFor('folder').templates, + }); + } +}); diff --git a/gui/app/pods/folder/settings/template.hbs b/gui/app/pods/folder/settings/template.hbs new file mode 100644 index 00000000..112f5c59 --- /dev/null +++ b/gui/app/pods/folder/settings/template.hbs @@ -0,0 +1,54 @@ +{{#layout/top-bar}} +
  • + {{#link-to "folder.index" model.folder.id model.folder.slug class='link'}} + {{model.folder.name}} + {{/link-to}} +
  • +
  • + {{#link-to "folder.settings" model.folder.id model.folder.slug class='link selected'}} + Settings + {{/link-to}} +
  • +{{/layout/top-bar}} + +{{#layout/middle-zone}} + {{#layout/middle-zone-content}} + {{#if (eq tab 'general')}} + {{folder/settings-general permissions=model.permissions space=model.folder}} + {{/if}} + + {{#if (eq tab 'permissions')}} + {{folder/settings-permissions permissions=model.permissions folders=model.folders folder=model.folder onRefresh=(action 'onRefresh')}} + {{/if}} + + {{#if (eq tab 'invitations')}} + {{folder/settings-invitations permissions=model.permissions space=model.folder}} + {{/if}} + + {{#if (eq tab 'templates')}} + {{folder/settings-templates permissions=model.permissions space=model.folder templates=model.templates}} + {{/if}} + + {{#if (eq tab 'blocks')}} + {{folder/settings-blocks permissions=model.permissions space=model.folder}} + {{/if}} + {{/layout/middle-zone-content}} + + {{#layout/middle-zone-sidebar}} + + {{/layout/middle-zone-sidebar}} +{{/layout/middle-zone}} + +{{#layout/bottom-bar}} +{{/layout/bottom-bar}} diff --git a/gui/app/router.js b/gui/app/router.js index 5da0fe96..d62e056e 100644 --- a/gui/app/router.js +++ b/gui/app/router.js @@ -38,6 +38,12 @@ export default Router.map(function () { this.route('category', { path: 'category' }); + this.route('settings', { + path: 'settings' + }); + this.route('block', { + path: 'block/:block_id' + }); } ); @@ -50,9 +56,6 @@ export default Router.map(function () { this.route('section', { path: 'section/:page_id' }); - this.route('block', { - path: 'block/:block_id' - }); } ); diff --git a/gui/app/styles/layout/all.scss b/gui/app/styles/layout/all.scss index 6fe5e9ce..e05b44f8 100644 --- a/gui/app/styles/layout/all.scss +++ b/gui/app/styles/layout/all.scss @@ -2,4 +2,5 @@ @import "layout-topbar.scss"; @import "layout-sidebar.scss"; @import "layout-footer.scss"; +@import "layout-content.scss"; diff --git a/gui/app/styles/layout/layout-content.scss b/gui/app/styles/layout/layout-content.scss new file mode 100644 index 00000000..c78d9e01 --- /dev/null +++ b/gui/app/styles/layout/layout-content.scss @@ -0,0 +1,32 @@ +.content-zone { + > .explainer-header { + color: $color-gray; + font-size: 1.5rem; + font-weight: 500; + } + + > .explainer-text { + margin: 3px 0; + padding: 0; + color: $color-gray; + font-size: 1.1rem; + } + + > .explainer-list { + margin: 5px 20px; + padding: 0; + color: $color-gray; + + > li { + margin: 0; + padding: 0; + font-size: 1.1rem; + list-style: square; + } + + } + + > .explainer-gap { + margin-bottom: 3rem; + } +} diff --git a/gui/app/templates/components/customize/license-key.hbs b/gui/app/templates/components/customize/license-key.hbs index 7b02c7e6..e3e1ed3e 100644 --- a/gui/app/templates/components/customize/license-key.hbs +++ b/gui/app/templates/components/customize/license-key.hbs @@ -12,7 +12,7 @@
    - +
    {{textarea value=model.license rows="10" class=(if LicenseError 'form-control is-invalid' 'form-control')}} XML format diff --git a/gui/app/templates/components/document/block-editor.hbs b/gui/app/templates/components/document/block-editor.hbs index ca58fe89..e88a3b79 100644 --- a/gui/app/templates/components/document/block-editor.hbs +++ b/gui/app/templates/components/document/block-editor.hbs @@ -2,7 +2,7 @@
    - {{component editorType document=document folder=folder page=page meta=meta blockMode=true onCancel=(action 'onCancel') onAction=(action 'onAction')}} + {{component editorType document=document space=space page=page meta=meta blockMode=true onCancel=(action 'onCancel') onAction=(action 'onAction')}}
    diff --git a/gui/app/templates/components/folder/permission-admin.hbs b/gui/app/templates/components/folder/permission-admin.hbs deleted file mode 100644 index 46fa97da..00000000 --- a/gui/app/templates/components/folder/permission-admin.hbs +++ /dev/null @@ -1,104 +0,0 @@ - \ No newline at end of file diff --git a/gui/app/templates/components/folder/settings-blocks.hbs b/gui/app/templates/components/folder/settings-blocks.hbs new file mode 100644 index 00000000..f81cd1e6 --- /dev/null +++ b/gui/app/templates/components/folder/settings-blocks.hbs @@ -0,0 +1,18 @@ +
    +
    Content blocks provide re-usable content that can be inserted into any document
    + {{#each blocks as |block|}} +
    +

    {{block.title}}

    +

    {{block.excerpt}}

    + +
    + +
    + {{/each}} +
    + +{{#if isSpaceAdmin}} +    {{#ui/ui-dialog title="Delete Content Block" confirmCaption="Delete" buttonType="btn-danger" show=showDeleteDialog onAction=(action 'onDeleteBlock')}} +        

    Are you sure you want to delete this re-usable content block?

    +    {{/ui/ui-dialog}} +{{/if}} \ No newline at end of file diff --git a/gui/app/templates/components/folder/settings-general.hbs b/gui/app/templates/components/folder/settings-general.hbs new file mode 100644 index 00000000..264f7925 --- /dev/null +++ b/gui/app/templates/components/folder/settings-general.hbs @@ -0,0 +1,25 @@ +
    +
    General options for this space
    + +
    + + {{ui-select id="spacetypes-dropdown" content=spaceTypeOptions optionValuePath="id" optionLabelPath="label" selection=spaceType action=(action 'onSetSpaceType')}} +
    + +
    + + {{#if allowLikes}} + {{input type='text' id="space-likes-prompt" class="form-control" placeholder="Did this help you?" value=likes}} + Specify the prompt, e.g. Did this help you? Was this helpful? Did you find what you needed? +
    + +
    + {{else}} +
    + +
    + {{/if}} +
    + + +
    \ No newline at end of file diff --git a/gui/app/templates/components/folder/settings-invitations.hbs b/gui/app/templates/components/folder/settings-invitations.hbs new file mode 100644 index 00000000..805e90f7 --- /dev/null +++ b/gui/app/templates/components/folder/settings-invitations.hbs @@ -0,0 +1,16 @@ +
    +
    Invite new users to this space
    +

    Email invite leads to a smooth onboarding process

    +
    +
    + + {{input id="space-invite-email" type='email' class="form-control mousetrap" placeholder="Enter email" value=inviteEmail}} + Comma separate multiple email addresses +
    +
    + + {{textarea id="space-invite-msg" value=inviteMessage class="form-control" rows="5"}} +
    +
    + +
    \ No newline at end of file diff --git a/gui/app/templates/components/folder/settings-permissions.hbs b/gui/app/templates/components/folder/settings-permissions.hbs new file mode 100644 index 00000000..b5b720d7 --- /dev/null +++ b/gui/app/templates/components/folder/settings-permissions.hbs @@ -0,0 +1,113 @@ +
    +
    Space and document permissions for both users and groups
    +

    Space level permissions:

    +
      +
    • View — see content within this space
    • +
    • Manage — manage all aspects of space except deletion
    • +
    • Owner — manage and delete space
    • +
    +

    Document level permissions:

    +
      +
    • Create — create new documents
    • +
    • Edit — edit documents
    • +
    • Delete — delete documents
    • +
    • Move — move content between documents
    • +
    • Copy — copy content between documents
    • +
    • Templates — create, edit, delete document templates and content blocks
    • +
    • Approval — approve or reject content changes
    • +
    • Lifecycle — mark documents as Draft, Live or Archived
    • +
    • Versions — create versions of documents (baselining)
    • +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + {{#each spacePermissions as |permission|}} + + + + + + + + + + + + + + + + {{/each}} + +
    SpacesDocuments
    ViewManageOwnerCreateEditDeleteMoveCopyTemplatesApprovalLifecycleVersions
    + {{#if (eq permission.who "role")}} + + people + +  {{permission.name}} + ({{permission.members}}) + + {{else}} + {{#if (eq permission.whoId constants.EveryoneUserId)}} + + language + +  {{permission.name}} + {{else}} + + person + +  {{permission.name}} + {{#if (eq permission.whoId session.user.id)}} + (you) + {{/if}} + + {{/if}} + {{/if}} + {{input type="checkbox" id=(concat 'space-role-view-' permission.whoId) checked=permission.spaceView}}{{input type="checkbox" id=(concat 'space-role-manage-' permission.whoId) checked=permission.spaceManage}}{{input type="checkbox" id=(concat 'space-role-owner-' permission.whoId) checked=permission.spaceOwner}}{{input type="checkbox" id=(concat 'doc-role-add-' permission.whoId) checked=permission.documentAdd}}{{input type="checkbox" id=(concat 'doc-role-edit-' permission.whoId) checked=permission.documentEdit}}{{input type="checkbox" id=(concat 'doc-role-delete-' permission.whoId) checked=permission.documentDelete}}{{input type="checkbox" id=(concat 'doc-role-move-' permission.whoId) checked=permission.documentMove}}{{input type="checkbox" id=(concat 'doc-role-copy-' permission.whoId) checked=permission.documentCopy}}{{input type="checkbox" id=(concat 'doc-role-template-' permission.whoId) checked=permission.documentTemplate}}{{input type="checkbox" id=(concat 'doc-role-approve-' permission.whoId) checked=permission.documentApprove}}{{input type="checkbox" id=(concat 'doc-role-lifecycle-' permission.whoId) checked=permission.documentLifecycle}}{{input type="checkbox" id=(concat 'doc-role-version-' permission.whoId) checked=permission.documentVersion}}
    +
    + + + +
    +
    +
    +
    + {{focus-input id="user-search" type="text" class="form-control mousetrap" placeholder="Search for users by firstname, lastname, email" value=searchText key-up=(action 'onSearch')}} + Find and add users to this space +
    + {{#each filteredUsers as |user|}} +
    +
    {{user.fullname}}
    +
    + +
    +
    + {{/each}} +
    +
    +
    diff --git a/gui/app/templates/components/folder/settings-templates.hbs b/gui/app/templates/components/folder/settings-templates.hbs new file mode 100644 index 00000000..90ef1e9c --- /dev/null +++ b/gui/app/templates/components/folder/settings-templates.hbs @@ -0,0 +1,10 @@ +
    +
    Content Templates provide the basis for new documentation
    + {{#each templates as |item|}} +
    +

    {{item.title}}

    +

    {{item.description}}

    + +
    + {{/each}} +
    \ No newline at end of file diff --git a/gui/app/templates/components/layout/top-bar.hbs b/gui/app/templates/components/layout/top-bar.hbs index 558fa848..3e00715c 100644 --- a/gui/app/templates/components/layout/top-bar.hbs +++ b/gui/app/templates/components/layout/top-bar.hbs @@ -4,7 +4,9 @@
      -
    • +
    • + +
    • {{#if (eq appMeta.edition 'Community')}}
    • {{#link-to "folders" class=(if (eq selectItem 'spaces') 'link selected' 'link')}}SPACES{{/link-to}} diff --git a/gui/app/templates/components/section/wysiwyg/type-editor.hbs b/gui/app/templates/components/section/wysiwyg/type-editor.hbs index 5edce364..128fb0a6 100644 --- a/gui/app/templates/components/section/wysiwyg/type-editor.hbs +++ b/gui/app/templates/components/section/wysiwyg/type-editor.hbs @@ -1,7 +1,6 @@ {{#section/base-editor-inline document=document folder=folder page=page blockMode=blockMode contentLinkerButton=true onInsertLink=(action 'onInsertLink') isDirty=(action 'isDirty') onCancel=(action 'onCancel') onAction=(action 'onAction')}} -
      {{{pageBody}}}
      diff --git a/gui/app/templates/components/toolbar/for-space.hbs b/gui/app/templates/components/toolbar/for-space.hbs index fe4c34b8..ea4c3292 100644 --- a/gui/app/templates/components/toolbar/for-space.hbs +++ b/gui/app/templates/components/toolbar/for-space.hbs @@ -94,40 +94,10 @@
    - {{#if spaceSettings}} -
    - security -
    -
    -
    - person_add -
    -
    - {{/if}} - - {{#if permissions.documentTemplate}} -
    - content_copy -
    -
    - {{/if}} - {{#if (or permissions.spaceOwner permissions.spaceManage)}} -
    - settings -
    -
    - {{/if}} - - {{#if pinState.isPinned}} -
    - star -
    -
    - {{else if session.authenticated}} -
    - star -
    + {{#link-to 'folder.settings' space.id space.slug class="button-icon-gray align-middle"}} + settings + {{/link-to}}
    {{/if}} @@ -137,45 +107,20 @@
    {{/if}} + + {{#if pinState.isPinned}} +
    + star +
    + {{else if session.authenticated}} +
    + star +
    + {{/if}}
    - - - - - - - - -{{folder/permission-admin folders=spaces folder=space onRefresh=onRefresh}}