diff --git a/app/app/components/document/document-files.js b/app/app/components/document/document-files.js new file mode 100644 index 00000000..fe5a95eb --- /dev/null +++ b/app/app/components/document/document-files.js @@ -0,0 +1,131 @@ +// 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 Ember from 'ember'; +import NotifierMixin from '../../mixins/notifier'; +import TooltipMixin from '../../mixins/tooltip'; + +export default Ember.Component.extend(NotifierMixin, TooltipMixin, { + appMeta: Ember.inject.service(), + drop: null, + deleteAttachment: { + id: "", + name: "", + }, + emptyState: Ember.computed('files', function () { + return this.get('files.length') === 0; + }), + + didInsertElement() { + if (!this.get('isEditor')) { + return; + } + + let self = this; + let documentId = this.get('document.id'); + let url = this.get('appMeta.endpoint'); + let uploadUrl = `${url}/documents/${documentId}/attachments`; + + let dzone = new Dropzone("#upload-document-files", { + headers: { + 'Authorization': 'Bearer ' + self.get('session.session.content.authenticated.token') + }, + url: uploadUrl, + method: "post", + paramName: 'attachment', + clickable: true, + maxFilesize: 10, + parallelUploads: 3, + uploadMultiple: false, + addRemoveLinks: false, + autoProcessQueue: true, + + init: function () { + this.on("success", function (file /*, response*/ ) { + self.showNotification(`Attached ${file.name}`); + }); + + this.on("queuecomplete", function () { + self.attrs.onUpload(); + }); + + this.on("addedfile", function ( /*file*/ ) { + self.audit.record('attached-file'); + }); + } + }); + + dzone.on("complete", function (file) { + dzone.removeFile(file); + }); + + this.set('drop', dzone); + }, + + willDestroyElement() { + let drop = this.get('drop'); + + if (is.not.null(drop)) { + drop.destroy(); + } + }, + + actions: { + confirmDeleteAttachment(id, name) { + this.set('deleteAttachment', { + id: id, + name: name + }); + + $(".delete-attachment-dialog").css("display", "block"); + + let drop = new Drop({ + target: $(".delete-attachment-" + id)[0], + content: $(".delete-attachment-dialog")[0], + classes: 'drop-theme-basic', + position: "bottom right", + openOn: "always", + tetherOptions: { + offset: "5px 0", + targetOffset: "10px 0" + }, + remove: false + }); + + this.set('drop', drop); + }, + + cancel() { + let drop = this.get('drop'); + drop.close(); + + this.set('deleteAttachment', { + id: "", + name: "" + }); + }, + + deleteAttachment() { + let attachment = this.get('deleteAttachment'); + let drop = this.get('drop'); + drop.close(); + + this.attrs.onDelete(this.get('deleteAttachment').id, attachment.name); + + this.set('deleteAttachment', { + id: "", + name: "" + }); + + return true; + } + } +}); diff --git a/app/app/components/document/document-meta.js b/app/app/components/document/document-meta.js new file mode 100644 index 00000000..bf0990f1 --- /dev/null +++ b/app/app/components/document/document-meta.js @@ -0,0 +1,35 @@ +// 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 Ember from 'ember'; +import NotifierMixin from '../../mixins/notifier'; +import TooltipMixin from '../../mixins/tooltip'; + +export default Ember.Component.extend(NotifierMixin, TooltipMixin, { + appMeta: Ember.inject.service(), + + actions: { + onSave() { + let doc = this.get('document'); + + if (is.empty(doc.get('excerpt'))) { + $("meta-excerpt").addClass("error").focus(); + return false; + } + + doc.set('excerpt', doc.get('excerpt').substring(0, 250)); + doc.set('userId', this.get('owner.id')); + + this.attrs.onSave(doc); + return true; + }, + } +}); diff --git a/app/app/components/document/document-sidebar-toc.js b/app/app/components/document/document-sidebar-toc.js index a8380c7b..4535e8cb 100644 --- a/app/app/components/document/document-sidebar-toc.js +++ b/app/app/components/document/document-sidebar-toc.js @@ -15,70 +15,73 @@ import TooltipMixin from '../../mixins/tooltip'; import tocUtil from '../../utils/toc'; export default Ember.Component.extend(NotifierMixin, TooltipMixin, { - document: {}, - folder: {}, - pages: [], - page: "", + document: {}, + folder: {}, + pages: [], + page: "", state: { actionablePage: false, - upDisabled: true, - downDisabled: true, - indentDisabled: true, - outdentDisabled: true + upDisabled: true, + downDisabled: true, + indentDisabled: true, + outdentDisabled: true + }, + emptyState: Ember.computed('pages', function () { + return this.get('pages.length') === 0; + }), + + didReceiveAttrs: function () { + this.set('showToc', is.not.undefined(this.get('pages')) && this.get('pages').get('length') > 2); + if (is.not.null(this.get('page'))) { + this.send('onEntryClick', this.get('page')); + } }, - didReceiveAttrs: function() { - this.set('showToc', is.not.undefined(this.get('pages')) && this.get('pages').get('length') > 2); - if (is.not.null(this.get('page'))) { - this.send('onEntryClick', this.get('page')); - } - }, - - didRender: function() { - if (this.session.authenticated) { - this.addTooltip(document.getElementById("toc-up-button")); - this.addTooltip(document.getElementById("toc-down-button")); - this.addTooltip(document.getElementById("toc-outdent-button")); - this.addTooltip(document.getElementById("toc-indent-button")); - } - }, - - didInsertElement() { - this.eventBus.subscribe('documentPageAdded', this, 'onDocumentPageAdded'); + didRender: function () { + if (this.session.authenticated) { + this.addTooltip(document.getElementById("toc-up-button")); + this.addTooltip(document.getElementById("toc-down-button")); + this.addTooltip(document.getElementById("toc-outdent-button")); + this.addTooltip(document.getElementById("toc-indent-button")); + } }, - willDestroyElement() { - this.eventBus.unsubscribe('documentPageAdded'); + didInsertElement() { + this.eventBus.subscribe('documentPageAdded', this, 'onDocumentPageAdded'); + }, + + willDestroyElement() { + this.eventBus.unsubscribe('documentPageAdded'); this.destroyTooltips(); - }, + }, - onDocumentPageAdded(pageId) { - this.send('onEntryClick', pageId); - }, + onDocumentPageAdded(pageId) { + this.send('onEntryClick', pageId); + }, - // Controls what user can do with the toc (left sidebar). - // Identifies the target pages. - setState(pageId) { + // Controls what user can do with the toc (left sidebar). + // Identifies the target pages. + setState(pageId) { this.set('page', pageId); - let toc = this.get('pages'); - let page = _.findWhere(toc, { id: pageId }); + let toc = this.get('pages'); + let page = _.findWhere(toc, { id: pageId }); let state = tocUtil.getState(toc, page); - if (!this.get('isEditor') || is.empty(pageId)) { + if (!this.get('isEditor') || is.empty(pageId)) { state.actionablePage = state.upDisabled = state.downDisabled = state.indentDisabled = state.outdentDisabled = false; - } + } this.set('state', state); - }, + }, - actions: { - // Page up - above pages shunt down. - pageUp() { + actions: { + // Page up - above pages shunt down. + pageUp() { if (this.get('state.upDisabled')) { - return; - } + return; + } let state = this.get('state'); let pages = this.get('pages'); @@ -88,75 +91,75 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { if (pendingChanges.length > 0) { this.attrs.changePageSequence(pendingChanges); - this.send('onEntryClick', this.get('page')); - this.audit.record("moved-page-up"); - this.showNotification("Moved up"); + this.send('onEntryClick', this.get('page')); + this.audit.record("moved-page-up"); + this.showNotification("Moved up"); } - }, + }, - // Move down -- pages below shift up. - pageDown() { + // Move down -- pages below shift up. + pageDown() { if (this.get('state.downDisabled')) { - return; - } + return; + } let state = this.get('state'); - var pages = this.get('pages'); - var page = _.findWhere(pages, { id: this.get('page') }); + var pages = this.get('pages'); + var page = _.findWhere(pages, { id: this.get('page') }); let pendingChanges = tocUtil.moveDown(state, pages, page); if (pendingChanges.length > 0) { - this.attrs.changePageSequence(pendingChanges); + this.attrs.changePageSequence(pendingChanges); - this.send('onEntryClick', this.get('page')); - this.audit.record("moved-page-down"); - this.showNotification("Moved down"); + this.send('onEntryClick', this.get('page')); + this.audit.record("moved-page-down"); + this.showNotification("Moved down"); } - }, + }, - // Indent - changes a page from H2 to H3, etc. - pageIndent() { + // Indent - changes a page from H2 to H3, etc. + pageIndent() { if (this.get('state.indentDisabled')) { - return; - } + return; + } let state = this.get('state'); - var pages = this.get('pages'); - var page = _.findWhere(pages, { id: this.get('page') }); + var pages = this.get('pages'); + var page = _.findWhere(pages, { id: this.get('page') }); let pendingChanges = tocUtil.indent(state, pages, page); if (pendingChanges.length > 0) { - this.attrs.changePageLevel(pendingChanges); + this.attrs.changePageLevel(pendingChanges); - this.showNotification("Indent"); - this.audit.record("changed-page-sequence"); - this.send('onEntryClick', this.get('page')); + this.showNotification("Indent"); + this.audit.record("changed-page-sequence"); + this.send('onEntryClick', this.get('page')); } - }, + }, - // Outdent - changes a page from H3 to H2, etc. - pageOutdent() { - if (this.get('state.outdentDisabled')) { - return; - } + // Outdent - changes a page from H3 to H2, etc. + pageOutdent() { + if (this.get('state.outdentDisabled')) { + return; + } let state = this.get('state'); - var pages = this.get('pages'); - var page = _.findWhere(pages, { id: this.get('page') }); + var pages = this.get('pages'); + var page = _.findWhere(pages, { id: this.get('page') }); let pendingChanges = tocUtil.outdent(state, pages, page); if (pendingChanges.length > 0) { - this.attrs.changePageLevel(pendingChanges); + this.attrs.changePageLevel(pendingChanges); - this.showNotification("Outdent"); - this.audit.record("changed-page-sequence"); - this.send('onEntryClick', this.get('page')); + this.showNotification("Outdent"); + this.audit.record("changed-page-sequence"); + this.send('onEntryClick', this.get('page')); } - }, + }, - onEntryClick(id) { - this.setState(id); - this.attrs.gotoPage(id); - }, - }, + onEntryClick(id) { + this.setState(id); + this.attrs.gotoPage(id); + }, + }, }); diff --git a/app/app/components/document/document-sidebar.js b/app/app/components/document/document-sidebar.js index 5edf0be8..2208843d 100644 --- a/app/app/components/document/document-sidebar.js +++ b/app/app/components/document/document-sidebar.js @@ -18,15 +18,12 @@ export default Ember.Component.extend(TooltipMixin, NotifierMixin, { document: {}, folder: {}, showToc: true, - showViews: false, - showContributions: false, showSections: false, showScrollTool: false, showingSections: false, didRender() { if (this.session.authenticated) { - this.addTooltip(document.getElementById("owner-avatar")); this.addTooltip(document.getElementById("section-tool")); } }, @@ -75,32 +72,13 @@ export default Ember.Component.extend(TooltipMixin, NotifierMixin, { showToc() { this.set('showToc', true); - this.set('showViews', false); - this.set('showContributions', false); this.set('showSections', false); this.set('showingSections', false); }, - showViews() { - this.set('showToc', false); - this.set('showViews', true); - this.set('showContributions', false); - this.set('showSections', false); - this.set('showingSections', false); - }, - - showContributions() { - this.set('showToc', false); - this.set('showViews', false); - this.set('showContributions', true); - this.set('showSections', false); - this.set('showingSections', false); - }, showSections() { this.set('showToc', false); - this.set('showViews', false); - this.set('showContributions', false); this.set('showSections', true); this.set('showingSections', true); }, diff --git a/app/app/components/document/document-toolbar.js b/app/app/components/document/document-toolbar.js index 4f19a868..8ba484ee 100644 --- a/app/app/components/document/document-toolbar.js +++ b/app/app/components/document/document-toolbar.js @@ -19,6 +19,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { localStorage: Ember.inject.service(), drop: null, users: [], + menuOpen: false, saveTemplate: { name: "", description: "" @@ -29,71 +30,15 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { this.set('saveTemplate.description', this.get('document.excerpt')); }, - didRender() { - if (this.get('isEditor')) { - this.addTooltip(document.getElementById("attachment-button")); - this.addTooltip(document.getElementById("save-template-button")); - this.addTooltip(document.getElementById("set-meta-button")); - this.addTooltip(document.getElementById("delete-document-button")); - } - - this.addTooltip(document.getElementById("print-document-button")); - }, - - didInsertElement() { - if (this.get('isEditor')) { - let self = this; - let documentId = this.get('document.id'); - let url = this.get('appMeta.endpoint'); - let uploadUrl = `${url}/documents/${documentId}/attachments`; - - let dzone = new Dropzone("#attachment-button > i", { - headers: { - 'Authorization': 'Bearer ' + self.get('session.session.content.authenticated.token') - }, - url: uploadUrl, - method: "post", - paramName: 'attachment', - clickable: true, - maxFilesize: 10, - parallelUploads: 3, - uploadMultiple: false, - addRemoveLinks: false, - autoProcessQueue: true, - - init: function () { - this.on("success", function (file /*, response*/ ) { - self.showNotification(`Attached ${file.name}`); - }); - - this.on("queuecomplete", function () { - self.attrs.onAttachmentUpload(); - }); - - this.on("addedfile", function ( /*file*/ ) { - self.audit.record('attached-file'); - }); - } - }); - - dzone.on("complete", function (file) { - dzone.removeFile(file); - }); - - this.set('drop', dzone); - } - }, - willDestroyElement() { - if (is.not.null(this.get('drop'))) { - this.get('drop').destroy(); - this.set('drop', null); - } - this.destroyTooltips(); }, actions: { + onMenuOpen() { + this.set('menuOpen', !this.get('menuOpen')); + }, + deleteDocument() { this.attrs.onDocumentDelete(); }, @@ -120,22 +65,6 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { this.attrs.onSaveTemplate(name, excerpt); return true; - }, - - saveMeta() { - let doc = this.get('document'); - - if (is.empty(doc.get('excerpt'))) { - $("meta-excerpt").addClass("error").focus(); - return false; - } - - doc.set('excerpt', doc.get('excerpt').substring(0, 250)); - doc.set('userId', this.get('owner.id')); - this.showNotification("Saved"); - - this.attrs.onDocumentChange(doc); - return true; - }, + } } }); diff --git a/app/app/components/document/document-view.js b/app/app/components/document/document-view.js index d18f5079..4604a123 100644 --- a/app/app/components/document/document-view.js +++ b/app/app/components/document/document-view.js @@ -39,16 +39,16 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { didInsertElement() { let self = this; - this.get('sectionService').refresh(this.get('document.id')).then(function (changes) { - changes.forEach(function (newPage) { - let oldPage = self.get('pages').findBy('id', newPage.get('id')); - if (is.not.undefined(oldPage)) { - oldPage.set('body', newPage.get('body')); - oldPage.set('revised', newPage.get('revised')); - self.showNotification(`Refreshed ${oldPage.get('title')}`); - } - }); - }); + // this.get('sectionService').refresh(this.get('document.id')).then(function (changes) { + // changes.forEach(function (newPage) { + // let oldPage = self.get('pages').findBy('id', newPage.get('id')); + // if (is.not.undefined(oldPage)) { + // oldPage.set('body', newPage.get('body')); + // oldPage.set('revised', newPage.get('revised')); + // self.showNotification(`Refreshed ${oldPage.get('title')}`); + // } + // }); + // }); }, didRender() { diff --git a/app/app/components/document/index-entry.js b/app/app/components/document/index-entry.js index 3c139660..682cc55e 100644 --- a/app/app/components/document/index-entry.js +++ b/app/app/components/document/index-entry.js @@ -16,16 +16,6 @@ export default Ember.Component.extend({ tagName: "li", classNames: ["item"], - // indentLevel: Ember.computed('page', function() { - // let nodeLevel = this.get('page.level'); - // let indent = (nodeLevel - 1) * 20; - // return indent; - // }), - - didReceiveAttrs() { - // this.set('classNames', ["item", "margin-left-" + this.get("page.tocIndent")]); - }, - actions: { onClick(id) { this.get('onClick')(id); diff --git a/app/app/components/document/tag-editor.js b/app/app/components/document/tag-editor.js index dbc7c17c..d6a5bcfa 100644 --- a/app/app/components/document/tag-editor.js +++ b/app/app/components/document/tag-editor.js @@ -23,7 +23,7 @@ export default Ember.Component.extend({ this._super(...arguments); let tagz = []; - if (this.get('documentTags').length > 1) { + if (!_.isUndefined(this.get('documentTags')) && this.get('documentTags').length > 1) { let tags = this.get('documentTags').split('#'); _.each(tags, function(tag) { if (tag.length > 0) { diff --git a/app/app/components/dropdown-dialog.js b/app/app/components/dropdown-dialog.js index 12ccd8c0..d4e0190f 100644 --- a/app/app/components/dropdown-dialog.js +++ b/app/app/components/dropdown-dialog.js @@ -85,9 +85,9 @@ export default Ember.Component.extend({ self.attrs.onOpenCallback(drop); } }); + self.set('drop', drop); } - }, willDestroyElement() { diff --git a/app/app/components/dropdown-menu.js b/app/app/components/dropdown-menu.js index 6f82ca24..ad05604f 100644 --- a/app/app/components/dropdown-menu.js +++ b/app/app/components/dropdown-menu.js @@ -18,6 +18,8 @@ export default Ember.Component.extend({ position: 'bottom right', contentId: "", drop: null, + onOpenCallback: null, // callback when opened + onCloseCallback: null, // callback when closed tether: Ember.inject.service(), didReceiveAttrs() { @@ -44,7 +46,20 @@ export default Ember.Component.extend({ } }); - self.set('drop', drop); + if (drop) { + drop.on('open', function () { + if (is.not.null(self.get("onOpenCallback"))) { + self.attrs.onOpenCallback(drop); + } + }); + drop.on('close', function () { + if (is.not.null(self.get("onCloseCallback"))) { + self.attrs.onCloseCallback(); + } + }); + + self.set('drop', drop); + } }, willDestroyElement() { diff --git a/app/app/pods/document/activity/controller.js b/app/app/pods/document/activity/controller.js new file mode 100644 index 00000000..27719168 --- /dev/null +++ b/app/app/pods/document/activity/controller.js @@ -0,0 +1,16 @@ +// 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 Ember from 'ember'; +import NotifierMixin from '../../../mixins/notifier'; + +export default Ember.Controller.extend(NotifierMixin, { +}); diff --git a/app/app/pods/document/activity/route.js b/app/app/pods/document/activity/route.js new file mode 100644 index 00000000..bfbe4ae6 --- /dev/null +++ b/app/app/pods/document/activity/route.js @@ -0,0 +1,32 @@ +// 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 Ember from 'ember'; +import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; + +export default Ember.Route.extend(AuthenticatedRouteMixin, { + documentService: Ember.inject.service('document'), + folderService: Ember.inject.service('folder'), + userService: Ember.inject.service('user'), + + beforeModel(transition) { + }, + + model() { + }, + + afterModel(model) { + }, + + setupController(controller, model) { + controller.set('model', model); + } +}); diff --git a/app/app/pods/document/activity/template.hbs b/app/app/pods/document/activity/template.hbs new file mode 100644 index 00000000..f8e916b9 --- /dev/null +++ b/app/app/pods/document/activity/template.hbs @@ -0,0 +1 @@ +activity diff --git a/app/app/pods/document/controller.js b/app/app/pods/document/controller.js index 17a79e72..65865696 100644 --- a/app/app/pods/document/controller.js +++ b/app/app/pods/document/controller.js @@ -1,14 +1,215 @@ // Copyright 2016 Documize Inc. . All rights reserved. // -// This software (Documize Community Edition) is licensed under +// This software (Documize Community Edition) is licensed under // GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html // // You can operate outside the AGPL restrictions by purchasing // Documize Enterprise Edition and obtaining a commercial license -// by contacting . +// by contacting . // // https://documize.com import Ember from 'ember'; +import NotifierMixin from '../../mixins/notifier'; -export default Ember.Controller.extend({}); \ No newline at end of file +export default Ember.Controller.extend(NotifierMixin, { + documentService: Ember.inject.service('document'), + templateService: Ember.inject.service('template'), + + page: null, + folder: {}, + pages: [], + + // Jump to the right part of the document. + scrollToPage(pageId) { + Ember.run.schedule('afterRender', function () { + let dest; + let target = "#page-title-" + pageId; + let targetOffset = $(target).offset(); + + if (is.undefined(targetOffset)) { + return; + } + + dest = targetOffset.top > $(document).height() - $(window).height() ? $(document).height() - $(window).height() : targetOffset.top; + // small correction to ensure we also show page title + dest = dest > 50 ? dest - 74 : dest; + + $("html,body").animate({ + scrollTop: dest + }, 500, "linear"); + $(".toc-index-item").removeClass("selected"); + $("#index-" + pageId).addClass("selected"); + }); + }, + + actions: { + gotoPage(pageId) { + if (is.null(pageId)) { + return; + } + + this.scrollToPage(pageId); + }, + + onPageSequenceChange(changes) { + let self = this; + + this.get('documentService').changePageSequence(this.model.get('id'), changes).then(function () { + _.each(changes, function (change) { + let pageContent = _.findWhere(self.get('pages'), { + id: change.pageId + }); + + if (is.not.undefined(pageContent)) { + pageContent.set('sequence', change.sequence); + } + }); + + self.set('pages', self.get('pages').sortBy('sequence')); + }); + }, + + onPageLevelChange(changes) { + let self = this; + + this.get('documentService').changePageLevel(this.model.get('id'), changes).then(function () { + _.each(changes, function (change) { + let pageContent = _.findWhere(self.get('pages'), { + id: change.pageId + }); + + if (is.not.undefined(pageContent)) { + pageContent.set('level', change.level); + } + }); + + let pages = self.get('pages'); + pages = pages.sortBy('sequence'); + self.set('pages', []); + self.set('pages', pages); + }); + }, + + onPageDeleted(deletePage) { + let self = this; + let documentId = this.get('model.id'); + let pages = this.get('pages'); + let deleteId = deletePage.id; + let deleteChildren = deletePage.children; + let page = _.findWhere(pages, { + id: deleteId + }); + let pageIndex = _.indexOf(pages, page, false); + let pendingChanges = []; + + // select affected pages + for (var i = pageIndex + 1; i < pages.get('length'); i++) { + if (pages[i].get('level') <= page.get('level')) { + break; + } + + pendingChanges.push({ + pageId: pages[i].get('id'), + level: pages[i].get('level') - 1 + }); + } + + if (deleteChildren) { + // nuke of page tree + pendingChanges.push({ + pageId: deleteId + }); + + this.get('documentService').deletePages(documentId, deleteId, pendingChanges).then(function () { + // update our models so we don't have to reload from db + for (var i = 0; i < pendingChanges.length; i++) { + let pageId = pendingChanges[i].pageId; + self.set('pages', _.reject(self.get('pages'), function (p) { //jshint ignore: line + return p.id === pageId; + })); + } + + self.set('pages', _.sortBy(self.get('pages'), "sequence")); + + self.audit.record("deleted-page"); + + // fetch document meta + self.get('documentService').getMeta(self.model.get('id')).then(function (meta) { + self.set('meta', meta); + }); + }); + } else { + // page delete followed by re-leveling child pages + this.get('documentService').deletePage(documentId, deleteId).then(function () { + self.set('pages', _.reject(self.get('pages'), function (p) { + return p.get('id') === deleteId; + })); + + self.audit.record("deleted-page"); + + // fetch document meta + self.get('documentService').getMeta(self.model.get('id')).then(function (meta) { + self.set('meta', meta); + }); + }); + + self.send('onPageLevelChange', pendingChanges); + } + }, + + onSaveTemplate(name, desc) { + this.get('templateService').saveAsTemplate(this.model.get('id'), name, desc).then(function () {}); + }, + + onAddSection(section) { + this.audit.record("added-section"); + this.audit.record("added-section-" + section.get('contentType')); + + let page = { + documentId: this.get('model.id'), + title: `${section.get('title')} Section`, + level: 1, + sequence: 2048, + body: "", + contentType: section.get('contentType') + }; + + let data = this.get('store').normalize('page', page); + let pageData = this.get('store').push(data); + + let meta = { + documentId: this.get('model.id'), + rawBody: "", + config: "" + }; + + let pageMeta = this.get('store').normalize('page-meta', meta); + let pageMetaData = this.get('store').push(pageMeta); + + let model = { + page: pageData, + meta: pageMetaData + }; + + this.get('documentService').addPage(this.get('model.id'), model).then((newPage) => { + this.transitionToRoute('document.edit', + this.get('folder.id'), + this.get('folder.slug'), + this.get('model.id'), + this.get('model.slug'), + newPage.id); + }); + }, + + onDocumentDelete() { + let self = this; + + this.get('documentService').deleteDocument(this.get('model.id')).then(function () { + self.audit.record("deleted-page"); + self.send("showNotification", "Deleted"); + self.transitionToRoute('folder', self.get('folder.id'), self.get('folder.slug')); + }); + } + } +}); diff --git a/app/app/pods/document/files/controller.js b/app/app/pods/document/files/controller.js new file mode 100644 index 00000000..45ef40b8 --- /dev/null +++ b/app/app/pods/document/files/controller.js @@ -0,0 +1,40 @@ +// 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 Ember from 'ember'; +import NotifierMixin from '../../../mixins/notifier'; + +export default Ember.Controller.extend(NotifierMixin, { + documentService: Ember.inject.service('document'), + + getAttachments() { + let self = this; + this.get('documentService').getAttachments(this.get('model.document.id')).then(function (files) { + self.set('model.files', files); + }); + }, + + actions: { + onUpload() { + this.getAttachments(); + }, + + onDelete(id, name) { + let self = this; + + this.showNotification(`Deleted ${name}`); + + this.get('documentService').deleteAttachment(this.get('model.document.id'), id).then(function () { + self.getAttachments(); + }); + }, + } +}); diff --git a/app/app/pods/document/files/route.js b/app/app/pods/document/files/route.js new file mode 100644 index 00000000..1057ea74 --- /dev/null +++ b/app/app/pods/document/files/route.js @@ -0,0 +1,34 @@ +// 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 Ember from 'ember'; +import RSVP from 'rsvp'; +import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; + +export default Ember.Route.extend(AuthenticatedRouteMixin, { + documentService: Ember.inject.service('document'), + folderService: Ember.inject.service('folder'), + userService: Ember.inject.service('user'), + + model() { + this.audit.record("viewed-document-attachments"); + + return RSVP.hash({ + document: this.modelFor('document'), + files: this.get('documentService').getAttachments(this.modelFor('document').get('id')) + }); + }, + + setupController(controller, model) { + controller.set('model', model); + controller.set('isEditor', this.get('folderService').get('canEditCurrentFolder')); + } +}); diff --git a/app/app/pods/document/files/template.hbs b/app/app/pods/document/files/template.hbs new file mode 100644 index 00000000..99a3403d --- /dev/null +++ b/app/app/pods/document/files/template.hbs @@ -0,0 +1 @@ +{{document/document-files document=model.document files=model.files isEditor=isEditor onUpload=(action 'onUpload') onDelete=(action 'onDelete')}} diff --git a/app/app/pods/document/index/controller.js b/app/app/pods/document/index/controller.js index f4fd8b1a..bb0dea21 100644 --- a/app/app/pods/document/index/controller.js +++ b/app/app/pods/document/index/controller.js @@ -20,18 +20,6 @@ export default Ember.Controller.extend(NotifierMixin, { page: null, folder: {}, pages: [], - attachments: null, - - getAttachments() { - let self = this; - this.get('documentService').getAttachments(this.model.get('id')).then(function (attachments) { - if (is.array(attachments)) { - self.set('attachments', attachments); - } else { - self.set('attachments', []); - } - }); - }, // Jump to the right part of the document. scrollToPage(pageId) { @@ -65,56 +53,6 @@ export default Ember.Controller.extend(NotifierMixin, { this.scrollToPage(pageId); }, - onPageSequenceChange(changes) { - let self = this; - - this.get('documentService').changePageSequence(this.model.get('id'), changes).then(function () { - _.each(changes, function (change) { - let pageContent = _.findWhere(self.get('pages'), { - id: change.pageId - }); - - if (is.not.undefined(pageContent)) { - pageContent.set('sequence', change.sequence); - } - }); - - self.set('pages', self.get('pages').sortBy('sequence')); - }); - }, - - onPageLevelChange(changes) { - let self = this; - - this.get('documentService').changePageLevel(this.model.get('id'), changes).then(function () { - _.each(changes, function (change) { - let pageContent = _.findWhere(self.get('pages'), { - id: change.pageId - }); - - if (is.not.undefined(pageContent)) { - pageContent.set('level', change.level); - } - }); - - let pages = self.get('pages'); - pages = pages.sortBy('sequence'); - self.set('pages', []); - self.set('pages', pages); - }); - }, - - onAttachmentUpload() { - this.getAttachments(); - }, - - onAttachmentDeleted(id) { - let self = this; - this.get('documentService').deleteAttachment(this.model.get('id'), id).then(function () { - self.getAttachments(); - }); - }, - onPageDeleted(deletePage) { let self = this; let documentId = this.get('model.id'); @@ -181,66 +119,5 @@ export default Ember.Controller.extend(NotifierMixin, { self.send('onPageLevelChange', pendingChanges); } }, - - onSaveTemplate(name, desc) { - this.get('templateService').saveAsTemplate(this.model.get('id'), name, desc).then(function () {}); - }, - - onDocumentChange(doc) { - let self = this; - this.get('documentService').save(doc).then(function () { - self.set('model', doc); - }); - }, - - onAddSection(section) { - this.audit.record("added-section"); - this.audit.record("added-section-" + section.get('contentType')); - - let page = { - documentId: this.get('model.id'), - title: `${section.get('title')} Section`, - level: 1, - sequence: 2048, - body: "", - contentType: section.get('contentType') - }; - - let data = this.get('store').normalize('page', page); - let pageData = this.get('store').push(data); - - let meta = { - documentId: this.get('model.id'), - rawBody: "", - config: "" - }; - - let pageMeta = this.get('store').normalize('page-meta', meta); - let pageMetaData = this.get('store').push(pageMeta); - - let model = { - page: pageData, - meta: pageMetaData - }; - - this.get('documentService').addPage(this.get('model.id'), model).then((newPage) => { - this.transitionToRoute('document.edit', - this.get('folder.id'), - this.get('folder.slug'), - this.get('model.id'), - this.get('model.slug'), - newPage.id); - }); - }, - - onDocumentDelete() { - let self = this; - - this.get('documentService').deleteDocument(this.get('model.id')).then(function () { - self.audit.record("deleted-page"); - self.send("showNotification", "Deleted"); - self.transitionToRoute('folder', self.get('folder.id'), self.get('folder.slug')); - }); - } } }); diff --git a/app/app/pods/document/index/route.js b/app/app/pods/document/index/route.js index b0135671..724ea41f 100644 --- a/app/app/pods/document/index/route.js +++ b/app/app/pods/document/index/route.js @@ -21,50 +21,37 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, { users: [], meta: [], folder: null, - - beforeModel: function (transition) { - this.pageId = is.not.undefined(transition.queryParams.page) ? transition.queryParams.page : ""; - var self = this; - - this.get('folderService').getAll().then(function (folders) { - self.set('folders', folders); - self.set('folder', folders.findBy("id", self.paramsFor('document').folder_id)); - self.get('folderService').setCurrentFolder(self.get('folder')); - }); + queryParams: { + page: { + refreshModel: false + } }, - model: function () { + beforeModel(transition) { + this.pageId = is.not.undefined(transition.queryParams.page) ? transition.queryParams.page : ""; + + let folders = this.get('store').peekAll('folder'); + let folder = this.get('store').peekRecord('folder', this.paramsFor('document').folder_id); + this.set('folders', folders); + this.set('folder', folder); + this.get('folderService').setCurrentFolder(folder); + }, + + model() { this.audit.record("viewed-document"); return this.modelFor('document'); }, - afterModel: function (model) { + afterModel(model) { var self = this; var documentId = model.get('id'); this.browser.setTitle(model.get('name')); - // We resolve the promise when all data is ready. return new Ember.RSVP.Promise(function (resolve) { self.get('documentService').getPages(documentId).then(function (pages) { self.set('pages', pages); - - self.get('documentService').getAttachments(documentId).then(function (attachments) { - self.set('attachments', is.array(attachments) ? attachments : []); - - if (self.session.authenticated) { - self.get('documentService').getMeta(documentId).then(function (meta) { - self.set('meta', meta); - - self.get('userService').getFolderUsers(self.get('folder.id')).then(function (users) { - self.set('users', users); - resolve(); - }); - }); - } else { - resolve(); - } - }); + resolve(); }); }); }, @@ -76,35 +63,5 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, { controller.set('currentPage', this.pageId); controller.set('isEditor', this.get('folderService').get('canEditCurrentFolder')); controller.set('pages', this.get('pages')); - controller.set('attachments', this.get('attachments')); - controller.set('users', this.get('users')); - - // setup document owner - let owner = this.get('users').findBy('id', model.get('userId')); - - // no document owner? You are the owner! - if (is.undefined(owner)) { - owner = this.session.user; - model.set('userId', this.get('session.session.authenticated.user.id')); - this.get('documentService').save(model); - } - - controller.set('owner', owner); - - // check for no meta - let meta = this.get('meta'); - - if (is.not.null(meta)) { - if (is.null(meta.editors)) { - meta.editors = []; - } - if (is.null(meta.viewers)) { - meta.viewers = []; - } - } - - controller.set('meta', meta); - - this.browser.setMetaDescription(model.get('excerpt')); } }); diff --git a/app/app/pods/document/index/template.hbs b/app/app/pods/document/index/template.hbs index 2b55d35a..e07d6646 100644 --- a/app/app/pods/document/index/template.hbs +++ b/app/app/pods/document/index/template.hbs @@ -1,17 +1,2 @@ -{{layout/zone-navigation}} - -{{#layout/zone-sidebar}} - {{document/document-sidebar document=model meta=meta folder=folder pages=pages page=page owner=owner isEditor=isEditor onAddSection=(action - 'onAddSection') changePageSequence=(action 'onPageSequenceChange') changePageLevel=(action 'onPageLevelChange') gotoPage=(action - 'gotoPage')}} -{{/layout/zone-sidebar}} - -{{#layout/zone-content}} - {{document/document-toolbar document=model pages=pages folder=folder owner=owner isEditor=isEditor users=users onSaveTemplate=(action - 'onSaveTemplate') onDocumentChange=(action 'onDocumentChange') onAttachmentUpload=(action 'onAttachmentUpload') onDocumentDelete=(action - 'onDocumentDelete')}} - - {{document/document-view document=model meta=meta pages=pages attachments=attachments folder=folder - folders=folders isEditor=isEditor gotoPage=(action 'gotoPage') onAttachmentDeleted=(action 'onAttachmentDeleted') onDeletePage=(action - 'onPageDeleted')}} -{{/layout/zone-content}} +{{document/document-view document=model meta=meta pages=pages attachments=attachments folder=folder +folders=folders isEditor=isEditor gotoPage=(action 'gotoPage') onDeletePage=(action 'onPageDeleted')}} diff --git a/app/app/pods/document/meta/controller.js b/app/app/pods/document/meta/controller.js new file mode 100644 index 00000000..1f0f1f4d --- /dev/null +++ b/app/app/pods/document/meta/controller.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 Ember from 'ember'; +import NotifierMixin from '../../../mixins/notifier'; + +export default Ember.Controller.extend(NotifierMixin, { + documentService: Ember.inject.service('document'), + + actions: { + onSave(doc) { + let self = this; + + this.get('documentService').save(doc).then(function () { + self.showNotification('Saved'); + self.set('model', doc); + self.transitionToRoute('document.index'); + }); + } + } +}); diff --git a/app/app/pods/document/meta/route.js b/app/app/pods/document/meta/route.js new file mode 100644 index 00000000..335b02e2 --- /dev/null +++ b/app/app/pods/document/meta/route.js @@ -0,0 +1,61 @@ +// 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 Ember from 'ember'; +import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; + +export default Ember.Route.extend(AuthenticatedRouteMixin, { + documentService: Ember.inject.service('document'), + folderService: Ember.inject.service('folder'), + userService: Ember.inject.service('user'), + + model() { + this.audit.record("viewed-document-meta"); + + let folders = this.get('store').peekAll('folder'); + let folder = this.get('store').peekRecord('folder', this.paramsFor('document').folder_id); + this.set('folders', folders); + this.set('folder', folder); + + return this.modelFor('document'); + }, + + afterModel() { + let self = this; + + return new Ember.RSVP.Promise(function (resolve) { + self.get('userService').getFolderUsers(self.get('folder.id')).then(function (users) { + self.set('users', users); + resolve(); + }); + }); + }, + + setupController(controller, model) { + controller.set('model', model); + controller.set('folder', this.get('folder')); + controller.set('folders', this.get('folders').rejectBy('id', 0)); + controller.set('isEditor', this.get('folderService').get('canEditCurrentFolder')); + controller.set('users', this.get('users')); + + // setup document owner + let owner = this.get('users').findBy('id', model.get('userId')); + + // no document owner? You are the owner! + if (is.undefined(owner)) { + owner = this.session.user; + model.set('userId', this.get('session.session.authenticated.user.id')); + this.get('documentService').save(model); + } + + controller.set('owner', owner); + } +}); diff --git a/app/app/pods/document/meta/template.hbs b/app/app/pods/document/meta/template.hbs new file mode 100644 index 00000000..d8e528ad --- /dev/null +++ b/app/app/pods/document/meta/template.hbs @@ -0,0 +1 @@ +{{document/document-meta document=model folders=folders folder=folder users=users owner=owner isEditor=isEditor onSave=(action 'onSave')}} diff --git a/app/app/pods/document/route.js b/app/app/pods/document/route.js index 52d03ef4..b74bec46 100644 --- a/app/app/pods/document/route.js +++ b/app/app/pods/document/route.js @@ -14,12 +14,66 @@ import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-rout export default Ember.Route.extend(AuthenticatedRouteMixin, { documentService: Ember.inject.service('document'), + folderService: Ember.inject.service('folder'), + userService: Ember.inject.service('user'), + pages: [], + users: [], + meta: [], + folder: null, + + beforeModel(transition) { + this.pageId = is.not.undefined(transition.queryParams.page) ? transition.queryParams.page : ""; + var self = this; + + this.get('folderService').getAll().then(function (folders) { + self.set('folders', folders); + self.set('folder', folders.findBy("id", self.paramsFor('document').folder_id)); + self.get('folderService').setCurrentFolder(self.get('folder')); + }); + }, model: function (params) { - this.audit.record("viewed-document"); + // this.audit.record("viewed-document"); return this.get('documentService').getDocument(params.document_id); }, + afterModel(model) { + var self = this; + var documentId = model.get('id'); + + this.browser.setTitle(model.get('name')); + + return new Ember.RSVP.Promise(function (resolve) { + self.get('documentService').getPages(documentId).then(function (pages) { + self.set('pages', pages); + resolve(); + }); + }); + }, + + setupController(controller, model) { + controller.set('model', model); + controller.set('folder', this.folder); + controller.set('folders', this.get('folders').rejectBy('id', 0)); + controller.set('currentPage', this.pageId); + controller.set('isEditor', this.get('folderService').get('canEditCurrentFolder')); + controller.set('pages', this.get('pages')); + + // setup document owner + let owner = this.get('users').findBy('id', model.get('userId')); + + // no document owner? You are the owner! + if (is.undefined(owner)) { + owner = this.session.user; + model.set('userId', this.get('session.session.authenticated.user.id')); + this.get('documentService').save(model); + } + + controller.set('owner', owner); + + this.browser.setMetaDescription(model.get('excerpt')); + }, + actions: { error(error /*, transition*/ ) { if (error) { diff --git a/app/app/pods/document/template.hbs b/app/app/pods/document/template.hbs index c24cd689..4e77a5b6 100644 --- a/app/app/pods/document/template.hbs +++ b/app/app/pods/document/template.hbs @@ -1 +1,15 @@ -{{outlet}} +{{layout/zone-navigation}} + +{{#layout/zone-sidebar}} + {{document/document-sidebar document=model folder=folder pages=pages page=page isEditor=isEditor onAddSection=(action + 'onAddSection') changePageSequence=(action 'onPageSequenceChange') changePageLevel=(action 'onPageLevelChange') gotoPage=(action + 'gotoPage')}} +{{/layout/zone-sidebar}} + +{{#layout/zone-content}} + {{document/document-toolbar document=model pages=pages folder=folder isEditor=isEditor onSaveTemplate=(action + 'onSaveTemplate') onDocumentDelete=(action + 'onDocumentDelete')}} + + {{outlet}} +{{/layout/zone-content}} diff --git a/app/app/router.js b/app/app/router.js index 60cefd37..3c843636 100644 --- a/app/app/router.js +++ b/app/app/router.js @@ -20,7 +20,7 @@ export default Router.map(function () { this.route('folders', { path: '/' }); - + this.route('folder', { path: 's/:folder_id/:folder_slug' }); @@ -32,6 +32,15 @@ export default Router.map(function () { this.route('document', { path: 's/:folder_id/:folder_slug/d/:document_id/:document_slug' }, function () { + this.route('files', { + path: 'files' + }); + this.route('meta', { + path: 'meta' + }); + this.route('activity', { + path: 'activity' + }); this.route('edit', { path: 'edit/:page_id' }); diff --git a/app/app/styles/app.scss b/app/app/styles/app.scss index c3eb7d18..00fda878 100644 --- a/app/app/styles/app.scss +++ b/app/app/styles/app.scss @@ -20,12 +20,7 @@ @import "view/page-auth.scss"; @import "view/page-onboard.scss"; @import "view/page-exceptions.scss"; -@import "view/document/sidebar.scss"; -@import "view/document/content.scss"; -@import "view/document/wysiwyg.scss"; -@import "view/document/editor.scss"; -@import "view/document/wizard.scss"; -@import "view/document/edit-tools.scss"; +@import "view/document/all.scss"; @import "view/common.scss"; @import "vendor.scss"; @import "responsive.scss"; diff --git a/app/app/styles/base.scss b/app/app/styles/base.scss index 49668131..ea1fbd85 100644 --- a/app/app/styles/base.scss +++ b/app/app/styles/base.scss @@ -106,7 +106,7 @@ video.responsive-video { html { overflow-y: scroll; - font-family: $base-font; + font-family: $font-regular; background-color: $color-white; font-size: 14px; height: 100%; diff --git a/app/app/styles/font.scss b/app/app/styles/font.scss index 7c6cce5b..6fe7be35 100644 --- a/app/app/styles/font.scss +++ b/app/app/styles/font.scss @@ -1,61 +1,42 @@ -.fixed-width-font { - font-family: 'courier new', courier; +.font-fixed-width { + font-family: 'courier new', courier; } - @font-face { - font-family: 'open_sansbold'; - src: url('font/opensans/opensans-bold-webfont.eot'); - src: url('font/opensans/opensans-bold-webfont.eot?#iefix') format('embedded-opentype'), - url('font/opensans/opensans-bold-webfont.woff2') format('woff2'), - url('font/opensans/opensans-bold-webfont.woff') format('woff'), - url('font/opensans/opensans-bold-webfont.ttf') format('truetype'), - url('font/opensans/opensans-bold-webfont.svg#open_sansbold') format('svg'); - font-weight: normal; - font-style: normal; + font-family: 'open_sansbold'; + src: url('font/opensans/opensans-bold-webfont.eot'); + src: url('font/opensans/opensans-bold-webfont.eot?#iefix') format('embedded-opentype'), url('font/opensans/opensans-bold-webfont.woff2') format('woff2'), url('font/opensans/opensans-bold-webfont.woff') format('woff'), url('font/opensans/opensans-bold-webfont.ttf') format('truetype'), url('font/opensans/opensans-bold-webfont.svg#open_sansbold') format('svg'); + font-weight: normal; + font-style: normal; } - @font-face { - font-family: 'open_sanslight'; - src: url('font/opensans/opensans-light-webfont.eot'); - src: url('font/opensans/opensans-light-webfont.eot?#iefix') format('embedded-opentype'), - url('font/opensans/opensans-light-webfont.woff2') format('woff2'), - url('font/opensans/opensans-light-webfont.woff') format('woff'), - url('font/opensans/opensans-light-webfont.ttf') format('truetype'), - url('font/opensans/opensans-light-webfont.svg#open_sanslight') format('svg'); - font-weight: normal; - font-style: normal; + font-family: 'open_sanslight'; + src: url('font/opensans/opensans-light-webfont.eot'); + src: url('font/opensans/opensans-light-webfont.eot?#iefix') format('embedded-opentype'), url('font/opensans/opensans-light-webfont.woff2') format('woff2'), url('font/opensans/opensans-light-webfont.woff') format('woff'), url('font/opensans/opensans-light-webfont.ttf') format('truetype'), url('font/opensans/opensans-light-webfont.svg#open_sanslight') format('svg'); + font-weight: normal; + font-style: normal; } - @font-face { - font-family: 'open_sansregular'; - src: url('font/opensans/opensans-regular-webfont.eot'); - src: url('font/opensans/opensans-regular-webfont.eot?#iefix') format('embedded-opentype'), - url('font/opensans/opensans-regular-webfont.woff2') format('woff2'), - url('font/opensans/opensans-regular-webfont.woff') format('woff'), - url('font/opensans/opensans-regular-webfont.ttf') format('truetype'), - url('font/opensans/opensans-regular-webfont.svg#open_sansregular') format('svg'); - font-weight: normal; - font-style: normal; + font-family: 'open_sansregular'; + src: url('font/opensans/opensans-regular-webfont.eot'); + src: url('font/opensans/opensans-regular-webfont.eot?#iefix') format('embedded-opentype'), url('font/opensans/opensans-regular-webfont.woff2') format('woff2'), url('font/opensans/opensans-regular-webfont.woff') format('woff'), url('font/opensans/opensans-regular-webfont.ttf') format('truetype'), url('font/opensans/opensans-regular-webfont.svg#open_sansregular') format('svg'); + font-weight: normal; + font-style: normal; } - @font-face { - font-family: 'open_sanssemibold'; - src: url('font/opensans/opensans-semibold-webfont.eot'); - src: url('font/opensans/opensans-semibold-webfont.eot?#iefix') format('embedded-opentype'), - url('font/opensans/opensans-semibold-webfont.woff2') format('woff2'), - url('font/opensans/opensans-semibold-webfont.woff') format('woff'), - url('font/opensans/opensans-semibold-webfont.ttf') format('truetype'), - url('font/opensans/opensans-semibold-webfont.svg#open_sanssemibold') format('svg'); - font-weight: normal; - font-style: normal; + font-family: 'open_sanssemibold'; + src: url('font/opensans/opensans-semibold-webfont.eot'); + src: url('font/opensans/opensans-semibold-webfont.eot?#iefix') format('embedded-opentype'), url('font/opensans/opensans-semibold-webfont.woff2') format('woff2'), url('font/opensans/opensans-semibold-webfont.woff') format('woff'), url('font/opensans/opensans-semibold-webfont.ttf') format('truetype'), url('font/opensans/opensans-semibold-webfont.svg#open_sanssemibold') format('svg'); + font-weight: normal; + font-style: normal; } -$base-font: 'open_sansregular'; - +$font-regular: 'open_sansregular'; +$font-semibold: 'open_sanssemibold'; +$font-light: 'open_sanslight'; @font-face { - font-family: "Material Icons"; - font-style : normal; - font-weight: 400; - src : url("font/icons/MaterialIcons-Regular.eot"); - src : local('Material Icons'), local('MaterialIcons-Regular'), url("font/icons/MaterialIcons-Regular.woff2") format('woff2'), url("font/icons/MaterialIcons-Regular.woff") format('woff'), url("font/icons/MaterialIcons-Regular.ttf") format('truetype'); + font-family: "Material Icons"; + font-style: normal; + font-weight: 400; + src: url("font/icons/MaterialIcons-Regular.eot"); + src: local('Material Icons'), local('MaterialIcons-Regular'), url("font/icons/MaterialIcons-Regular.woff2") format('woff2'), url("font/icons/MaterialIcons-Regular.woff") format('woff'), url("font/icons/MaterialIcons-Regular.ttf") format('truetype'); } diff --git a/app/app/styles/functions.scss b/app/app/styles/functions.scss index 1f06bdc3..809a1400 100644 --- a/app/app/styles/functions.scss +++ b/app/app/styles/functions.scss @@ -26,6 +26,20 @@ border-top-left-radius: $radius; } +@mixin border-radius-bottom-right($radius) +{ + -webkit-border-bottom-right-radius: $radius; + -moz-border-radius-bottomright: $radius; + border-bottom-right-radius: $radius; +} + +@mixin border-radius-bottom-left($radius) +{ + -webkit-border-bottom-left-radius: $radius; + -moz-border-radius-bottomleft: $radius; + border-bottom-left-radius: $radius; +} + @mixin border-radius-none() { -webkit-border-radius: none; diff --git a/app/app/styles/view/common.scss b/app/app/styles/view/common.scss index bc352e5b..c87e3c84 100644 --- a/app/app/styles/view/common.scss +++ b/app/app/styles/view/common.scss @@ -2,7 +2,7 @@ text-align: center; > .empty-state { - margin: 30px 0 0; + margin: 30px 0; font-size: 2rem; color: $color-gray; font-family: 'open_sanslight'; diff --git a/app/app/styles/view/document/all.scss b/app/app/styles/view/document/all.scss new file mode 100644 index 00000000..ee438da5 --- /dev/null +++ b/app/app/styles/view/document/all.scss @@ -0,0 +1,8 @@ +@import "content.scss"; +@import "edit-tools.scss"; +@import "editor.scss"; +@import "files.scss"; +@import "sidebar.scss"; +@import "toolbar.scss"; +@import "wizard.scss"; +@import "wysiwyg.scss"; diff --git a/app/app/styles/view/document/content.scss b/app/app/styles/view/document/content.scss index f76e0354..58bbb880 100644 --- a/app/app/styles/view/document/content.scss +++ b/app/app/styles/view/document/content.scss @@ -15,66 +15,6 @@ color: $color-gray; } - .attachment-zone { - margin: 20px 0 30px 0; - - > .list { - margin: 0; - padding: 7px 0; - - > .item { - color: $color-off-black; - margin-top: 10px; - padding: 0; - list-style-type: none; - border-bottom: 1px dotted $color-border; - padding-bottom: 10px; - - > .icon { - margin-right: 10px; - } - - > a { - color: $color-gray; - - &:hover { - color: $color-link; - } - - > .file { - font-size: 1rem; - } - } - - > .action { - float: right; - margin-top: -2px; - margin-right: 5px; - @extend .cursor-pointer; - opacity: 0.5; - @extend .transition-all; - display: none; - color: $color-stroke; - } - - &:hover { - .action { - display: inline-block; - - &:hover { - opacity: 1; - } - } - } - } - } - } - - .delete-attachment-dialog, - .delete-page-dialog { - display: none; - } - .is-template { color: $color-gray; font-weight: bold; diff --git a/app/app/styles/view/document/files.scss b/app/app/styles/view/document/files.scss new file mode 100644 index 00000000..08c60820 --- /dev/null +++ b/app/app/styles/view/document/files.scss @@ -0,0 +1,78 @@ +.document-files { + margin: 0; + + > .upload-document-files { + width: 50%; + padding: 20px; + margin: 0 auto; + text-align: center; + color: $color-gray; + border: 2px dotted $color-gray; + cursor: pointer; + font-size: 1rem; + line-height: 1.7rem; + @include border-radius(10px); + @include ease-in(); + + &:hover { + border-color: $color-link; + color: $color-link; + } + + .dz-processing, + > .dz-preview { + display: none !important; + } + } + + > .list { + margin: 0 0 50px; + padding: 7px 0; + + > .item { + color: $color-off-black; + margin-top: 10px; + padding: 0; + list-style-type: none; + border-bottom: 1px solid $color-border; + padding-bottom: 10px; + + > .icon { + margin-right: 10px; + } + + > a { + color: $color-gray; + + &:hover { + color: $color-link; + } + + > .file { + font-size: 1rem; + } + } + + > .action { + float: right; + margin-top: -2px; + margin-right: 5px; + @extend .cursor-pointer; + @extend .transition-all; + display: none; + color: $color-gray; + } + + &:hover { + .action { + display: inline-block; + } + } + } + } +} + +.delete-attachment-dialog, +.delete-page-dialog { + display: none; +} diff --git a/app/app/styles/view/document/sidebar.scss b/app/app/styles/view/document/sidebar.scss index 4edea7da..98efcd6e 100644 --- a/app/app/styles/view/document/sidebar.scss +++ b/app/app/styles/view/document/sidebar.scss @@ -2,57 +2,6 @@ @extend .no-select; width: 100%; - > .summary-line { - color: $color-gray; - margin: 30px 0; - - > .items { - padding: 0; - margin: 0; - text-align: center; - - > .divider { - margin-right: 20px; - display: inline-block; - } - - > .item { - list-style-type: none; - display: inline-block; - margin: 0; - padding: 0; - vertical-align: middle; - - > .metric { - @extend .cursor-pointer; - text-align: center; - color: $color-gray; - - .number { - font-size: 1.2rem; - font-weight: bold; - } - - .label { - margin: 0; - padding: 0; - font-size: 0.8rem; - } - - &:hover { - color: $color-link; - } - } - } - - > .active { - > .metric { - color: $color-link; - } - } - } - } - .stuck-toc { position: fixed; top: 20px; @@ -109,7 +58,6 @@ .document-structure { > .toc-controls { - text-align: center; margin: 0; color: $color-gray; @@ -140,6 +88,7 @@ overflow-x: hidden; list-style-type: none; margin: 20px 0 0; + font-family: $font-semibold; .item { padding: 4px 0; diff --git a/app/app/styles/view/document/toolbar.scss b/app/app/styles/view/document/toolbar.scss new file mode 100644 index 00000000..83b69326 --- /dev/null +++ b/app/app/styles/view/document/toolbar.scss @@ -0,0 +1,61 @@ +.document-toolbar { + position: relative; + margin: -30px 0 40px; + height: 60px; + padding: 5px 30px 0; + background-color: $color-off-white; + @include border-radius-bottom-right(5px); + @include border-radius-bottom-left(5px); + @extend .no-select; + + > .tabs { + width: 80%; + height: 50px; + margin-top: 15px; + display: inline-block; + + > .tab { + list-style-type: none; + display: inline-block; + margin: 0 20px 0 0; + padding: 0; + color: $color-gray; + font-family: $font-semibold; + cursor: pointer; + + &:hover { + color: $color-link; + } + + > a { + color: $color-gray; + } + + > .active { + color: $color-link; + } + } + } + + > .options { + width: 20%; + height: 50px; + margin-top: 15px; + display: inline-block; + text-align: right; + float: right; + + > .option { + list-style-type: none; + display: inline-block; + margin: 0 0 0 20px; + padding: 0; + color: $color-gray; + cursor: pointer; + + &:hover { + color: $color-link; + } + } + } +} diff --git a/app/app/styles/view/document/wysiwyg.scss b/app/app/styles/view/document/wysiwyg.scss index a50ad015..9d6ff419 100644 --- a/app/app/styles/view/document/wysiwyg.scss +++ b/app/app/styles/view/document/wysiwyg.scss @@ -52,7 +52,7 @@ font-size: 1.6em; font-family: open_sanslight; color:$color-off-black; - margin: 30px 0 20px 0; + margin: 0 0 20px 0; } h2 diff --git a/app/app/styles/view/layout.scss b/app/app/styles/view/layout.scss index 16cb688f..cb48f5b4 100644 --- a/app/app/styles/view/layout.scss +++ b/app/app/styles/view/layout.scss @@ -133,6 +133,6 @@ .zone-content { min-height: 500px; //ensure dropdowns render in viewport - padding: 30px 40px 30px 100px; + padding: 30px 40px 30px 110px; z-index: 777; } diff --git a/app/app/styles/widget/widget-input.scss b/app/app/styles/widget/widget-input.scss index 120b8b2c..e6eb288e 100644 --- a/app/app/styles/widget/widget-input.scss +++ b/app/app/styles/widget/widget-input.scss @@ -1,77 +1,81 @@ -* { box-sizing: border-box; } +* { + box-sizing: border-box; +} .input-control { - position: relative; - margin-bottom: 25px; + position: relative; + margin-bottom: 25px; - > label { - color: $color-input; - font-size: 1.1em; - font-weight: normal; - pointer-events: none; - } + > label { + color: $color-input; + font-size: 1.1em; + font-weight: normal; + font-family: $font-semibold; + pointer-events: none; + } - > .tip { - color: $color-input; - font-size: 0.8em; - margin: 5px 0 10px 0; - padding: 0; - text-align: left; - } + > .tip { + color: $color-input; + font-size: 1em; + margin: 5px 0 10px; + padding: 0; + text-align: left; + font-family: $font-light; + } - > input, - textarea { - font-size: 1em; - padding: 8px 0 10px 0; - margin: 0 0 15px 0; - display: block; - width: 100%; - border: none; - border-bottom: 1px solid $color-input; - height: 2.3rem; - outline: none; + > input, + textarea { + font-size: 1em; + padding: 8px 0 10px; + margin: 0 0 15px; + display: block; + width: 100%; + border: none; + border-bottom: 1px solid $color-input; + height: 2.3rem; + outline: none; - &:focus { - outline: none; - border-bottom: 1px solid $color-input !important; - box-shadow: 0 1px 0 0 $color-input !important; - transition: 0.2s ease all; - -moz-transition: 0.2s ease all; - -webkit-transition: 0.2s ease all; - } - } + &:focus { + outline: none; + border-bottom: 1px solid $color-primary !important; + box-shadow: 0 1px 0 0 $color-primary !important; + transition: 0.2s ease all; + -moz-transition: 0.2s ease all; + -webkit-transition: 0.2s ease all; + } + } - > textarea { - resize: none; - height: auto !important; + > textarea { + resize: none; + height: auto !important; overflow-y: hidden; - } + } - > select, + > select, > div select { background-color: $color-white; - padding: 5px; - border: 1px solid $color-input; - border-radius: 2px; - height: 2.3rem; + padding: 5px; + border: 1px solid $color-input; + border-radius: 2px; + height: 2.3rem; font-size: 1rem; display: inline-block; &:focus { - outline: none; - border: 1px solid $color-input !important; - box-shadow: none !important; - transition: 0.2s ease all; - -moz-transition: 0.2s ease all; - -webkit-transition: 0.2s ease all; - } + outline: none; + border: 1px solid $color-input !important; + box-shadow: none !important; + transition: 0.2s ease all; + -moz-transition: 0.2s ease all; + -webkit-transition: 0.2s ease all; + } } > .checkbox { width: 100%; font-size: 1em; - padding: 8px 0 10px 0; - margin: 0 0 15px 0; + padding: 8px 0 10px; + margin: 0 0 15px; display: inline-block; border: none; border-bottom: 1px solid $color-input; @@ -89,7 +93,7 @@ > input[type='checkbox'] { font-size: 1em; - padding: 8px 0 10px 0; + padding: 8px 0 10px; margin: 0 0 15px 5px; display: inline-block; border: none; @@ -108,66 +112,67 @@ } > label { - color: $color-off-black; - font-size: 1em; - font-weight: normal; - pointer-events: none; - display: inline-block; - margin-left: 5px; - } + color: $color-off-black; + font-size: 1em; + font-weight: normal; + pointer-events: none; + display: inline-block; + margin-left: 5px; + } } .error { border-color: $color-red; &:focus { - border-bottom: 1px solid $color-red !important; - box-shadow: 0 1px 0 0 $color-red !important; - } + border-bottom: 1px solid $color-red !important; + box-shadow: 0 1px 0 0 $color-red !important; + } } select.error { border-color: $color-red; &:focus { - border: 1px solid $color-red !important; - box-shadow: none !important; - } + border: 1px solid $color-red !important; + box-shadow: none !important; + } } } .input-inline { - display: inline-block; - cursor: default; - width: 97%; + display: inline-block; + cursor: default; + width: 97%; - > input { - font-size: 1em; - padding: 0; - margin: 0; - width: 100%; - border: none; - height: auto; - outline: none; - display: inline-block; + > input { + font-size: 1em; + padding: 0; + margin: 0; + width: 100%; + border: none; + height: auto; + outline: none; + display: inline-block; - &:focus { - outline: none; - border-bottom: none !important; - box-shadow: none !important; - } - } + &:focus { + outline: none; + border-bottom: none !important; + box-shadow: none !important; + } + } } .input-form { - border: 1px solid $color-border; - display: block; - border-radius : 3px; - background-color: $color-white; + border: 1px solid $color-border; + display: block; + border-radius: 3px; + background-color: $color-white; padding: 30px 50px; - > .heading, > form .heading { - >.title { + > .heading, + > form .heading { + > .title { font-size: 1.1rem; font-weight: bold; color: $color-off-black; @@ -176,15 +181,17 @@ > .tip { color: $color-input; font-size: 0.9em; - margin: 5px 0 30px 0; + margin: 5px 0 30px; padding: 0; text-align: left; } } } -.form-divider { - margin-top: 30px; +.form-bordered { + padding: 30px 40px; + border: 10px solid $color-border; + @include border-radius(15px); } .form-borderless { @@ -192,6 +199,10 @@ border: none !important; } +.form-divider { + margin-top: 30px; +} + .widget-checkbox { color: $color-link; cursor: pointer; diff --git a/app/app/templates/components/document/document-files.hbs b/app/app/templates/components/document/document-files.hbs new file mode 100644 index 00000000..2a274815 --- /dev/null +++ b/app/app/templates/components/document/document-files.hbs @@ -0,0 +1,44 @@ +
+
    + {{#each files key="id" as |a index|}} +
  • + + + {{ a.filename }} + + {{#if isEditor}} +
    + delete +
    + {{/if}} +
  • + {{/each}} +
+ + {{#if emptyState}} +
+
+ There are no files +
+
+ {{/if}} + +
+ Drag-drop files or click to select files +
+
+ + diff --git a/app/app/templates/components/document/document-meta.hbs b/app/app/templates/components/document/document-meta.hbs new file mode 100644 index 00000000..a40157cf --- /dev/null +++ b/app/app/templates/components/document/document-meta.hbs @@ -0,0 +1,23 @@ +
+
+ +
Set the document owner
+ {{ui-select id="document-owner" + content=users + action=(action (mut owner)) + optionValuePath="id" + optionLabelPath="fullname" + selection=owner}} +
+
+ +
Short title for this document
+ {{focus-input type='text' id="document-name" value=document.name}} +
+
+ +
Provide short summary of the document (max. 250)
+ {{textarea value=document.excerpt rows="5" id="meta-excerpt"}} +
+
Save
+
diff --git a/app/app/templates/components/document/document-sidebar-toc.hbs b/app/app/templates/components/document/document-sidebar-toc.hbs index d09d8bce..97aa36cd 100644 --- a/app/app/templates/components/document/document-sidebar-toc.hbs +++ b/app/app/templates/components/document/document-sidebar-toc.hbs @@ -1,22 +1,24 @@
{{#if this.session.authenticated}} -