diff --git a/.jsbeautifyrc b/.jsbeautifyrc index 0606d31e..f2c86f94 100644 --- a/.jsbeautifyrc +++ b/.jsbeautifyrc @@ -5,7 +5,17 @@ "indent_with_tabs": true, "preserve_newlines": true, "max_preserve_newlines": 2, - "newline_between_rules": true + "newline_between_rules": true, + "selector_separator_newlines": true + }, + "scss": { + "indent_size": 4, + "indent_level": 0, + "indent_with_tabs": true, + "preserve_newlines": true, + "max_preserve_newlines": 2, + "newline_between_rules": true, + "selector_separator_newlines": true }, "html": { "indent_size": 4, @@ -49,4 +59,4 @@ "indent_level": 0, "indent_with_tabs": true } -} +} \ No newline at end of file diff --git a/app/app/components/document/document-tab.js b/app/app/components/document/document-tab.js new file mode 100644 index 00000000..d8789c17 --- /dev/null +++ b/app/app/components/document/document-tab.js @@ -0,0 +1,45 @@ +// 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, { + viewMode: true, + editMode: false, + + actions: { + onEdit() { + this.set('viewMode', false); + this.set('editMode', true); + }, + + onView() { + this.set('viewMode', true); + this.set('editMode', false); + }, + + onCancel() { + this.send('onView'); + }, + + onAction(page, meta) { + this.get('onAction')(page, meta); + this.send('onView'); + }, + + onDelete() { + this.get('onDelete')(this.get('model.document'), this.get('model.page')); + return true; + } + } +}); diff --git a/app/app/components/document/document-toolbar.js b/app/app/components/document/document-toolbar.js index 8ba484ee..a9a24c86 100644 --- a/app/app/components/document/document-toolbar.js +++ b/app/app/components/document/document-toolbar.js @@ -30,6 +30,12 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { this.set('saveTemplate.description', this.get('document.excerpt')); }, + didRender() { + if (this.session.isEditor) { + this.addTooltip(document.getElementById("add-document-tab")); + } + }, + willDestroyElement() { this.destroyTooltips(); }, @@ -67,4 +73,4 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { return true; } } -}); +}); \ No newline at end of file diff --git a/app/app/components/document/document-view.js b/app/app/components/document/document-view.js index 4604a123..7c0c55be 100644 --- a/app/app/components/document/document-view.js +++ b/app/app/components/document/document-view.js @@ -18,26 +18,17 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { sectionService: Ember.inject.service('section'), appMeta: Ember.inject.service(), link: Ember.inject.service(), - /* Parameters */ document: null, - // pages: [], - attachments: [], folder: null, folders: [], isEditor: false, - /* Internal */ - drop: null, - deleteAttachment: { - id: "", - name: "", - }, noSections: Ember.computed('pages', function () { return this.get('pages.length') === 0; }), didInsertElement() { - let self = this; + // let self = this; // this.get('sectionService').refresh(this.get('document.id')).then(function (changes) { // changes.forEach(function (newPage) { @@ -57,12 +48,6 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { willDestroyElement() { this.destroyTooltips(); - - let drop = this.get('drop'); - - if (is.not.null(drop)) { - drop.destroy(); - } }, contentLinkHandler() { @@ -70,7 +55,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { let doc = this.get('document'); let self = this; - $("a[data-documize='true']").off('click').on('click', function(e) { + $("a[data-documize='true']").off('click').on('click', function (e) { let link = links.getLinkObject(self.get('meta.outboundLinks'), this); // local link? exists? @@ -99,55 +84,6 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { }, 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.showNotification(`Deleted ${attachment.name}`); - this.attrs.onAttachmentDeleted(this.get('deleteAttachment').id); - this.set('deleteAttachment', { - id: "", - name: "" - }); - - return true; - }, - onDeletePage(id, deleteChildren) { let page = this.get('pages').findBy("id", id); @@ -164,7 +100,6 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { this.attrs.onDeletePage(params); }, - // onTagChange event emitted from document/tag-editor component onTagChange(tags) { let doc = this.get('document'); doc.set('tags', tags); diff --git a/app/app/components/document/page-wizard.js b/app/app/components/document/page-wizard.js index 36d6477f..9e1728dc 100644 --- a/app/app/components/document/page-wizard.js +++ b/app/app/components/document/page-wizard.js @@ -13,31 +13,28 @@ import Ember from 'ember'; import NotifierMixin from '../../mixins/notifier'; export default Ember.Component.extend(NotifierMixin, { - sectionService: Ember.inject.service('section'), + display: 'section', // which CSS to use - didReceiveAttrs() { + didRender() { let self = this; - this.get('sectionService').getAll().then(function(sections) { - self.set('sections', sections); + + Mousetrap.bind('esc', function () { + if (self.get('isDestroyed') || self.get('isDestroying')) { + return; + } + + self.send('onCancel'); + return false; }); }, - didRender() { - let self = this; - - Mousetrap.bind('esc', function() { - self.send('onCancel'); - return false; - }); - }, - - actions: { + actions: { onCancel() { this.attrs.onCancel(); }, - addSection(section) { - this.attrs.onAction(section); - } - } + addSection(section) { + this.attrs.onAction(section); + } + } }); diff --git a/app/app/models/page.js b/app/app/models/page.js index 586991b4..05929db3 100644 --- a/app/app/models/page.js +++ b/app/app/models/page.js @@ -18,6 +18,7 @@ export default Model.extend({ documentId: attr('string'), orgId: attr('string'), contentType: attr('string'), + pageType: attr('string'), level: attr('number', { defaultValue: 1 }), sequence: attr('number', { defaultValue: 0 }), revisions: attr('number', { defaultValue: 0 }), @@ -41,4 +42,4 @@ export default Model.extend({ }), created: attr(), revised: attr() -}); +}); \ No newline at end of file diff --git a/app/app/models/section.js b/app/app/models/section.js index 8887648b..1082c772 100644 --- a/app/app/models/section.js +++ b/app/app/models/section.js @@ -16,6 +16,7 @@ import Ember from 'ember'; export default Model.extend({ contentType: attr('string'), + pageType: attr('string'), title: attr('string'), description: attr('string'), iconFont: attr('string'), @@ -24,6 +25,7 @@ export default Model.extend({ hasImage: Ember.computed('iconFont', 'iconFile', function () { return this.get('iconFile').length > 0; }), + created: attr(), revised: attr() -}); +}); \ No newline at end of file diff --git a/app/app/pods/document/controller.js b/app/app/pods/document/controller.js index 65865696..00663f8c 100644 --- a/app/app/pods/document/controller.js +++ b/app/app/pods/document/controller.js @@ -172,7 +172,8 @@ export default Ember.Controller.extend(NotifierMixin, { level: 1, sequence: 2048, body: "", - contentType: section.get('contentType') + contentType: section.get('contentType'), + pageType: section.get('pageType') }; let data = this.get('store').normalize('page', page); @@ -212,4 +213,4 @@ export default Ember.Controller.extend(NotifierMixin, { }); } } -}); +}); \ No newline at end of file diff --git a/app/app/pods/document/index/controller.js b/app/app/pods/document/index/controller.js index bb0dea21..3b6e2fd0 100644 --- a/app/app/pods/document/index/controller.js +++ b/app/app/pods/document/index/controller.js @@ -93,13 +93,6 @@ export default Ember.Controller.extend(NotifierMixin, { } 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 @@ -107,17 +100,12 @@ export default Ember.Controller.extend(NotifierMixin, { 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); } + + self.audit.record("deleted-page"); }, } }); diff --git a/app/app/pods/document/index/route.js b/app/app/pods/document/index/route.js index 724ea41f..d45abaed 100644 --- a/app/app/pods/document/index/route.js +++ b/app/app/pods/document/index/route.js @@ -62,6 +62,6 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, { 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')); + controller.set('pages', this.get('pages').filterBy('pageType', 'section')); } }); diff --git a/app/app/pods/document/route.js b/app/app/pods/document/route.js index b74bec46..ce0f552e 100644 --- a/app/app/pods/document/route.js +++ b/app/app/pods/document/route.js @@ -13,6 +13,7 @@ import Ember from 'ember'; import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; export default Ember.Route.extend(AuthenticatedRouteMixin, { + sectionService: Ember.inject.service('section'), documentService: Ember.inject.service('document'), folderService: Ember.inject.service('folder'), userService: Ember.inject.service('user'), @@ -46,7 +47,11 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, { return new Ember.RSVP.Promise(function (resolve) { self.get('documentService').getPages(documentId).then(function (pages) { self.set('pages', pages); - resolve(); + + self.get('sectionService').getAll().then(function (sections) { + self.set('sections', sections.filterBy('pageType', 'section')); + resolve(); + }); }); }); }, @@ -57,7 +62,9 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, { 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')); + controller.set('pages', this.get('pages').filterBy('pageType', 'section')); + controller.set('tabs', this.get('pages').filterBy('pageType', 'tab')); + controller.set('sections', this.get('sections')); // setup document owner let owner = this.get('users').findBy('id', model.get('userId')); diff --git a/app/app/pods/document/section/controller.js b/app/app/pods/document/section/controller.js new file mode 100644 index 00000000..e0ebd270 --- /dev/null +++ b/app/app/pods/document/section/controller.js @@ -0,0 +1,46 @@ +// 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: { + onAction(page, meta) { + let self = this; + this.showNotification("Saving"); + + let model = { + page: page.toJSON({ includeId: true }), + meta: meta.toJSON({ includeId: true }) + }; + + this.get('documentService').updatePage(page.get('documentId'), page.get('id'), model).then(function (page) { + self.audit.record("edited-page"); + let data = self.get('store').normalize('page', page); + self.get('store').push(data); + }); + }, + + onDelete(document, page) { + let self = this; + + this.get('documentService').deletePage(document.get('id'), page.get('id')).then(function () { + page.deleteRecord(); + self.audit.record("deleted-page"); + self.showNotification('Deleted'); + self.transitionToRoute('document'); + }); + } + } +}); diff --git a/app/app/pods/document/section/route.js b/app/app/pods/document/section/route.js new file mode 100644 index 00000000..070e57e6 --- /dev/null +++ b/app/app/pods/document/section/route.js @@ -0,0 +1,48 @@ +// 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'), + pageId: '', + + model(params) { + let self = this; + let document = this.modelFor('document'); + let folders = this.get('store').peekAll('folder'); + let folder = this.get('store').peekRecord('folder', this.paramsFor('document').folder_id); + + let pages = this.get('store').peekAll('page').filter((page) => { + return page.get('documentId') === document.get('id') && page.get('pageType') === 'section'; + }); + + let tabs = this.get('store').peekAll('page').filter((page) => { + return page.get('documentId') === document.get('id') && page.get('pageType') === 'tab'; + }); + + let page = tabs.findBy('id', params.page_id); + + this.audit.record("viewed-document-section-" + page.get('contentType')); + + return Ember.RSVP.hash({ + folders: folders, + folder: folder, + document: document, + pages: pages, + page: page, + meta: self.get('documentService').getPageMeta(document.get('id'), params.page_id) + }); + }, +}); diff --git a/app/app/pods/document/section/template.hbs b/app/app/pods/document/section/template.hbs new file mode 100644 index 00000000..11cc66db --- /dev/null +++ b/app/app/pods/document/section/template.hbs @@ -0,0 +1 @@ +{{document/document-tab model=model onAction=(action 'onAction') onDelete=(action 'onDelete')}} diff --git a/app/app/pods/document/template.hbs b/app/app/pods/document/template.hbs index 4e77a5b6..53fc3cce 100644 --- a/app/app/pods/document/template.hbs +++ b/app/app/pods/document/template.hbs @@ -1,15 +1,9 @@ {{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')}} + {{document/document-sidebar document=model folder=folder pages=pages page=page isEditor=isEditor sections=sections 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}} + {{document/document-toolbar document=model pages=pages tabs=tabs folder=folder isEditor=isEditor onSaveTemplate=(action 'onSaveTemplate') onDocumentDelete=(action 'onDocumentDelete')}} {{outlet}} {{/layout/zone-content}} diff --git a/app/app/pods/document/wizard/route.js b/app/app/pods/document/wizard/route.js index 9ec0fe68..357ec02f 100644 --- a/app/app/pods/document/wizard/route.js +++ b/app/app/pods/document/wizard/route.js @@ -2,7 +2,7 @@ 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'), + documentService: Ember.inject.service('document'), folderService: Ember.inject.service('folder'), sectionService: Ember.inject.service('section'), @@ -12,7 +12,9 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, { return Ember.RSVP.hash({ folder: self.get('folderService').getFolder(self.paramsFor('document').folder_id), document: self.get('documentService').getDocument(self.paramsFor('document').document_id), - sections: self.get('sectionService').getAll() + sections: this.get('sectionService').getAll().then(function (sections) { + return sections.filterBy('pageType', 'tab'); + }) }); } }); diff --git a/app/app/pods/document/wizard/template.hbs b/app/app/pods/document/wizard/template.hbs index 9692f3bf..a5bc3dfd 100644 --- a/app/app/pods/document/wizard/template.hbs +++ b/app/app/pods/document/wizard/template.hbs @@ -1 +1 @@ -{{document/page-wizard document=model.document folder=model.folder sections=model.sections onCancel=(action 'onCancel') onAction=(action 'onAddSection')}} +{{document/page-wizard display='tab' document=model.document folder=model.folder sections=model.sections onCancel=(action 'onCancel') onAction=(action 'onAddSection')}} diff --git a/app/app/router.js b/app/app/router.js index 3c843636..e40cb45a 100644 --- a/app/app/router.js +++ b/app/app/router.js @@ -41,6 +41,9 @@ export default Router.map(function () { this.route('activity', { path: 'activity' }); + this.route('section', { + path: 'section/:page_id' + }); this.route('edit', { path: 'edit/:page_id' }); diff --git a/app/app/styles/color.scss b/app/app/styles/color.scss index 37cf0a98..bef64b5c 100644 --- a/app/app/styles/color.scss +++ b/app/app/styles/color.scss @@ -20,7 +20,7 @@ $color-blue: #2667af; $color-gray: #8b9096; $color-goldy: #cc9933; -$color-header: #f3f5f8; +$color-sidebar: #f3f5f8; $color-link: #0092d3; $color-border: #f3f5f8; @@ -40,36 +40,47 @@ $color-chip-text: #1b88e3; .color-white { color: $color-white !important; } + .color-off-white { color: $color-off-white !important; } + .color-black { color: $color-black !important; } + .color-off-black { color: $color-off-black !important; } + .color-primary { color: $color-primary !important; } + .color-link { color: $color-link !important; } + .color-blue { color: $color-blue !important; } + .color-red { color: $color-red !important; } + .color-green { color: $color-green !important; } + .color-gray { color: $color-gray !important; } + .background-color-white { background-color: $color-white !important; } + .background-color-primary { background-color: $color-primary !important; } diff --git a/app/app/styles/section/github.scss b/app/app/styles/section/github.scss index f3333a40..669ba50f 100644 --- a/app/app/styles/section/github.scss +++ b/app/app/styles/section/github.scss @@ -7,7 +7,7 @@ width: 100%; padding: 10px; white-space: nowrap; - overflow: auto + overflow: auto; } .github-repo-title { @@ -21,7 +21,6 @@ } .github-list-title { - font-weight: bold; color: #4c4c4c; font-size: 14px; margin: 5px; @@ -34,17 +33,16 @@ .github-issue-label { font-size: 11px; color: white; - padding: 0px 4px; + padding: 0 4px; margin-right: 5px; border-radius: 2px; box-shadow: inset 0 -1px 0 rgba(0,0,0,0.12); display: inline-block; line-height: 22px; - } + } } .section-github-render { - a:hover { text-decoration: underline; } @@ -62,7 +60,7 @@ .heading { font-size: 1.6rem; - margin: 30px 0 0 0; + margin: 30px 0 0; } .github-table thead tr th { @@ -73,36 +71,37 @@ text-align: left; span { - color:#838d94; + color: #838d94; } } .github-table tbody tr td { border: none!important; - padding: 5px 20px 5px 20px !important; + padding: 5px 20px !important; } .github-table .right-column { text-align: right; - color:#838d94; + color: #838d94; } span.data { - color:#838d94; + color: #838d94; } .issue-label { - color:white; - font-size: 11px; - padding: 4px 6px; - border-radius: 4px; - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.12); - margin-left: 10px; + color: white; + font-size: 11px; + padding: 4px 6px; + border-radius: 4px; + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.12); + margin-left: 10px; } .progress-bar { - display: inline-block; border-radius: 3px; + display: inline-block; + border-radius: 3px; width: 40%; background-color: #f1f1f1; height: 8px; @@ -117,8 +116,8 @@ span.issue-state { float: left; - margin-right: 10px; - margin-top: 3px; + margin-right: 10px; + margin-top: 3px; } img.github-avatar { diff --git a/app/app/styles/view/document/editor.scss b/app/app/styles/view/document/editor.scss index 4ebded01..601cc3e5 100644 --- a/app/app/styles/view/document/editor.scss +++ b/app/app/styles/view/document/editor.scss @@ -1,9 +1,7 @@ .document-editor { > .toolbar { - @extend .z-depth-tiny; - background-color: $color-white; width: 100%; - padding: 20px 40px; + padding: 0; > .title { width: 50%; @@ -12,7 +10,7 @@ margin: 0; > input { - margin: 0 0 5px 0; + margin: 0 0 5px; } } } @@ -23,7 +21,7 @@ } > .canvas { - padding: 40px 40px; + padding: 40px 0; } .cancel-edits-dialog { diff --git a/app/app/styles/view/document/section.scss b/app/app/styles/view/document/section.scss new file mode 100644 index 00000000..e17965e1 --- /dev/null +++ b/app/app/styles/view/document/section.scss @@ -0,0 +1,9 @@ +.document-section { + > .toolbar { + margin-bottom: 10px; + + > .buttons { + margin: 10px 0; + } + } +} diff --git a/app/app/styles/view/document/toolbar.scss b/app/app/styles/view/document/toolbar.scss index 83b69326..1376bdfa 100644 --- a/app/app/styles/view/document/toolbar.scss +++ b/app/app/styles/view/document/toolbar.scss @@ -3,7 +3,7 @@ margin: -30px 0 40px; height: 60px; padding: 5px 30px 0; - background-color: $color-off-white; + background-color: $color-sidebar; @include border-radius-bottom-right(5px); @include border-radius-bottom-left(5px); @extend .no-select; @@ -33,6 +33,26 @@ > .active { color: $color-link; + + > .add-tab { + > i { + color: $color-link; + } + } + } + + .add-tab { + display: inline-block; + vertical-align: text-top; + + > i { + font-size: 1.5rem; + color: $color-gray; + + &:hover { + color: $color-green; + } + } } } } diff --git a/app/app/styles/view/document/wizard.scss b/app/app/styles/view/document/wizard.scss index 531bb1c7..a777c490 100644 --- a/app/app/styles/view/document/wizard.scss +++ b/app/app/styles/view/document/wizard.scss @@ -1,5 +1,5 @@ .section-wizard { - margin: 20px 0 30px 0; + margin: 20px 0 30px; > .canvas { padding: 0; @@ -27,7 +27,7 @@ color: $color-primary; } } - } + } .icon { text-align: center; @@ -65,3 +65,73 @@ } } } + +.tab-wizard { + margin: 20px 0 30px; + + > .canvas { + padding: 0; + + > .list { + margin: 0; + padding: 0; + + > .item { + list-style: none; + cursor: pointer; + padding: 20px; + float: left; + @include ease-in(); + + &:hover { + @include ease-in(); + + > .details { + > .title { + color: $color-primary; + } + + > .desc { + color: $color-primary; + } + } + } + + .icon { + text-align: center; + display: inline-block; + width: 50px; + margin-right: 10px; + float: left; + + > .img { + float: left; + text-align: center; + display: inline-block; + height: 40px; + width: 40px; + } + } + + > .details { + vertical-align: top; + display: inline-block; + float: left; + + > .title { + font-size: 1rem; + font-weight: bold; + color: $color-off-black; + letter-spacing: 0.5px; + } + + > .desc { + color: $color-gray; + font-size: 0.9rem; + margin-top: 5px; + } + } + } + } + } +} diff --git a/app/app/styles/view/layout.scss b/app/app/styles/view/layout.scss index cb48f5b4..4b3cbb45 100644 --- a/app/app/styles/view/layout.scss +++ b/app/app/styles/view/layout.scss @@ -9,8 +9,8 @@ z-index: 999; overflow-x: hidden; - > .top-zone, - > .bottom-zone { + > .bottom-zone, + > .top-zone { position: absolute; padding: 0; width: 100%; @@ -101,7 +101,7 @@ } .zone-sidebar { - background-color: $color-header; + background-color: $color-sidebar; height: 100%; min-height: 100%; padding: 30px 40px 0; @@ -116,8 +116,8 @@ font-weight: bold; margin-bottom: 10px; - > a > .material-icons, - > .material-icons { + > .material-icons, + > a > .material-icons { font-size: 1rem; vertical-align: middle; } diff --git a/app/app/styles/view/page-search.scss b/app/app/styles/view/page-search.scss index ec4011ca..d45420d7 100644 --- a/app/app/styles/view/page-search.scss +++ b/app/app/styles/view/page-search.scss @@ -1,55 +1,55 @@ .page-search { .input-control { > input { - background-color: $color-header; + background-color: $color-sidebar; } } .search-results { - .heading { - font-size: 1.2rem; - color: $color-off-black; - } + .heading { + font-size: 1.2rem; + color: $color-off-black; + } - > .list { - margin-top: 20px; + > .list { + margin-top: 20px; list-style: none; - > .item { - cursor: pointer; + > .item { + cursor: pointer; margin: 0 30px 30px 0; padding-bottom: 40px; - border-bottom: 1px solid #e1e1e1; + border-bottom: 1px solid #e1e1e1; - > .link { - text-decoration: none; - color: $color-off-black; + > .link { + text-decoration: none; + color: $color-off-black; &:hover { color: $color-link; } - > .title { - display: inline-block; + > .title { + display: inline-block; font-size: 1.2rem; - } + } > .folder { - display: inline-block; - font-size: 0.8rem; - color: $color-gray; + display: inline-block; + font-size: 0.8rem; + color: $color-gray; margin-left: 15px; - } + } - > .excerpt { + > .excerpt { margin-top: 1rem; - font-size: 0.9rem; - } + font-size: 0.9rem; + } > .chips { margin-top: 1rem; } - } + } > .references { margin-top: 1rem; @@ -69,7 +69,7 @@ } } } - } - } - } + } + } + } } diff --git a/app/app/styles/widget/widget-input.scss b/app/app/styles/widget/widget-input.scss index bba81339..903473b1 100644 --- a/app/app/styles/widget/widget-input.scss +++ b/app/app/styles/widget/widget-input.scss @@ -51,8 +51,8 @@ overflow-y: hidden; } - > div select, - > select { + > select, + > div select { background-color: $color-white; padding: 5px; border: 1px solid $color-input; @@ -167,25 +167,25 @@ padding: 30px 40px; border: 10px solid $color-border; @include border-radius(15px); +} - > .form-header { - > .title { - font-size: 1.4rem; - font-weight: normal; - font-family: $font-semibold; - pointer-events: none; - font-weight: bold; - color: $color-off-black; - } +.form-header { + > .title { + font-size: 1.4rem; + font-weight: normal; + font-family: $font-semibold; + pointer-events: none; + font-weight: bold; + color: $color-off-black; + } - > .tip { - color: $color-input; - font-size: 1.2rem; - margin: 5px 0 30px; - padding: 0; - font-family: $font-light; - text-align: left; - } + > .tip { + color: $color-input; + font-size: 1.2rem; + margin: 5px 0 30px; + padding: 0; + font-family: $font-light; + text-align: left; } } diff --git a/app/app/templates/components/document/document-sidebar.hbs b/app/app/templates/components/document/document-sidebar.hbs index c6d0ac01..d813a520 100644 --- a/app/app/templates/components/document/document-sidebar.hbs +++ b/app/app/templates/components/document/document-sidebar.hbs @@ -25,6 +25,6 @@ gotoPage=(action 'gotoPage')}} {{/if}} {{#if showSections}} - {{document/page-wizard document=document folder=folder onCancel=(action 'onCancel') onAction=(action 'onAddSection')}} + {{document/page-wizard display='section' document=document folder=folder sections=sections onCancel=(action 'onCancel') onAction=(action 'onAddSection')}} {{/if}} diff --git a/app/app/templates/components/document/document-tab.hbs b/app/app/templates/components/document/document-tab.hbs new file mode 100644 index 00000000..060ec8bd --- /dev/null +++ b/app/app/templates/components/document/document-tab.hbs @@ -0,0 +1,31 @@ +
+ + {{#if viewMode}} +
+
+
+ edit +
+
+
+ delete +
+
+
+
+ {{#dropdown-dialog target="delete-section-button" position="bottom right" button="Delete" color="flat-red" onAction=(action 'onDelete')}} +

Are you sure you want to delete this section?

+

There is no undo!

+ {{/dropdown-dialog}} + +
+

{{model.page.title}}

+ {{section/base-renderer page=model.page}} +
+ {{/if}} + + {{#if editMode}} + {{document/document-editor document=model.document folder=model.folder page=model.page meta=model.meta onCancel=(action 'onCancel') onAction=(action 'onAction')}} + {{/if}} + +
diff --git a/app/app/templates/components/document/document-toolbar.hbs b/app/app/templates/components/document/document-toolbar.hbs index 2193f529..12151c9e 100644 --- a/app/app/templates/components/document/document-toolbar.hbs +++ b/app/app/templates/components/document/document-toolbar.hbs @@ -17,6 +17,20 @@ {{#link-to 'document.activity'}}Activity{{/link-to}} {{/if}} + {{#each tabs as |tab|}} +
  • + {{#link-to 'document.section' tab.id}}{{tab.title}}{{/link-to}} +
  • + {{/each}} + {{#if isEditor}} +
  • + {{#link-to 'document.wizard'}} +
    + add +
    + {{/link-to}} +
  • + {{/if}}
      @@ -39,7 +53,7 @@

      Are you sure you want to delete this document?

      There is no undo!

      {{/dropdown-dialog}} - {{#dropdown-dialog target="save-template-button" position="bottom right" button="Save as Template" color="flat-green" onAction=(action 'saveTemplate') focusOn="new-template-name"}} + {{#dropdown-dialog target="save-template-button" position="bottom right" button="Save as Template" color="flat-green" onAction=(action 'saveTemplate') focusOn="new-template-name" }}
      @@ -57,4 +71,4 @@
      -
      +
      diff --git a/app/app/templates/components/document/document-view.hbs b/app/app/templates/components/document/document-view.hbs index 02d1f2e2..9f0f2a0e 100644 --- a/app/app/templates/components/document/document-view.hbs +++ b/app/app/templates/components/document/document-view.hbs @@ -1,5 +1,4 @@
      -

      {{document.name}}

      @@ -18,7 +17,8 @@ {{#each pages key="id" as |page index|}}
      - {{document/page-heading tagName=page.tagName document=document folder=folder page=page isEditor=isEditor onDeletePage=(action 'onDeletePage')}} {{section/base-renderer page=page}} + {{document/page-heading tagName=page.tagName document=document folder=folder page=page isEditor=isEditor onDeletePage=(action 'onDeletePage')}} + {{section/base-renderer page=page}}
      {{/each}} diff --git a/app/app/templates/components/document/page-wizard.hbs b/app/app/templates/components/document/page-wizard.hbs index 7174fb3b..1d6b3de5 100644 --- a/app/app/templates/components/document/page-wizard.hbs +++ b/app/app/templates/components/document/page-wizard.hbs @@ -1,4 +1,4 @@ -
      +
        {{#each sections as |section|}} diff --git a/app/app/templates/components/section/base-editor.hbs b/app/app/templates/components/section/base-editor.hbs index c763c112..b7176052 100644 --- a/app/app/templates/components/section/base-editor.hbs +++ b/app/app/templates/components/section/base-editor.hbs @@ -1,4 +1,4 @@ -
        +
        @@ -34,4 +34,5 @@
        {{yield}}
        +
        diff --git a/app/app/templates/components/section/github/type-editor.hbs b/app/app/templates/components/section/github/type-editor.hbs index a304615c..05bd0fe3 100644 --- a/app/app/templates/components/section/github/type-editor.hbs +++ b/app/app/templates/components/section/github/type-editor.hbs @@ -5,31 +5,26 @@ {{#if authenticated}}
        -
        -
        -
        Select Repository
        -
        Choose source of code information to be displayed
        -
        -
        - -
        Select organization or username whose repository you want to show
        - {{ui-select id="owners-dropdown" content=owners action=(action 'onOwnerChange') optionValuePath="id" optionLabelPath="name" selection=config.owner}} -
        -
        - - {{input id="branch-since" value=config.branchSince type="text" }}
        -
        -
        - -
        Select the views you want to show
        -
        - {{input id="show-milestone" checked=config.showMilestones type="checkbox"}} - - {{input id="show-issues" checked=config.showIssues type="checkbox"}} - - {{input id="show-commits" checked=config.showCommits type="checkbox" }} - -
        +
        + +
        Select organization or user whose repository you want to show
        + {{ui-select id="owners-dropdown" content=owners action=(action 'onOwnerChange') optionValuePath="id" optionLabelPath="name" selection=config.owner}} +
        +
        + +
        default is 7 days ago
        + {{input id="branch-since" value=config.branchSince type="text" }}
        +
        +
        + +
        Select the views you want to show
        +
        + {{input id="show-milestone" checked=config.showMilestones type="checkbox"}} + + {{input id="show-issues" checked=config.showIssues type="checkbox"}} + + {{input id="show-commits" checked=config.showCommits type="checkbox" }} +
        diff --git a/core/api/entity/objects.go b/core/api/entity/objects.go index f694c8fd..1d2bc4d2 100644 --- a/core/api/entity/objects.go +++ b/core/api/entity/objects.go @@ -182,6 +182,7 @@ type Page struct { DocumentID string `json:"documentId"` UserID string `json:"userId"` ContentType string `json:"contentType"` + PageType string `json:"pageType"` Level uint64 `json:"level"` Sequence float64 `json:"sequence"` Title string `json:"title"` @@ -193,6 +194,7 @@ type Page struct { func (p *Page) SetDefaults() { if len(p.ContentType) == 0 { p.ContentType = "wysiwyg" + p.PageType = "section" } p.Title = strings.TrimSpace(p.Title) diff --git a/core/api/request/page.go b/core/api/request/page.go index a00e671a..d8de13b8 100644 --- a/core/api/request/page.go +++ b/core/api/request/page.go @@ -50,7 +50,7 @@ func (p *Persister) AddPage(model models.PageModel) (err error) { model.Page.Sequence = maxSeq * 2 } - stmt, err := p.Context.Transaction.Preparex("INSERT INTO page (refid, orgid, documentid, userid, contenttype, level, title, body, revisions, sequence, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + stmt, err := p.Context.Transaction.Preparex("INSERT INTO page (refid, orgid, documentid, userid, contenttype, pagetype, level, title, body, revisions, sequence, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") defer utility.Close(stmt) if err != nil { @@ -58,7 +58,7 @@ func (p *Persister) AddPage(model models.PageModel) (err error) { return } - _, err = stmt.Exec(model.Page.RefID, model.Page.OrgID, model.Page.DocumentID, model.Page.UserID, model.Page.ContentType, model.Page.Level, model.Page.Title, model.Page.Body, model.Page.Revisions, model.Page.Sequence, model.Page.Created, model.Page.Revised) + _, err = stmt.Exec(model.Page.RefID, model.Page.OrgID, model.Page.DocumentID, model.Page.UserID, model.Page.ContentType, model.Page.PageType, model.Page.Level, model.Page.Title, model.Page.Body, model.Page.Revisions, model.Page.Sequence, model.Page.Created, model.Page.Revised) if err != nil { log.Error("Unable to execute insert for page", err) @@ -91,7 +91,7 @@ func (p *Persister) AddPage(model models.PageModel) (err error) { func (p *Persister) GetPage(pageID string) (page entity.Page, err error) { err = nil - stmt, err := Db.Preparex("SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.level, a.sequence, a.title, a.body, a.revisions, a.created, a.revised FROM page a WHERE a.orgid=? AND a.refid=?") + stmt, err := Db.Preparex("SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.pagetype, a.level, a.sequence, a.title, a.body, a.revisions, a.created, a.revised FROM page a WHERE a.orgid=? AND a.refid=?") defer utility.Close(stmt) if err != nil { @@ -113,7 +113,7 @@ func (p *Persister) GetPage(pageID string) (page entity.Page, err error) { func (p *Persister) GetPages(documentID string) (pages []entity.Page, err error) { err = nil - err = Db.Select(&pages, "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.level, a.sequence, a.title, a.body, a.revisions, a.created, a.revised FROM page a WHERE a.orgid=? AND a.documentid=? ORDER BY a.sequence", p.Context.OrgID, documentID) + err = Db.Select(&pages, "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.pagetype, a.level, a.sequence, a.title, a.body, a.revisions, a.created, a.revised FROM page a WHERE a.orgid=? AND a.documentid=? ORDER BY a.sequence", p.Context.OrgID, documentID) if err != nil { log.Error(fmt.Sprintf("Unable to execute select pages for org %s and document %s", p.Context.OrgID, documentID), err) @@ -130,7 +130,7 @@ func (p *Persister) GetPagesWhereIn(documentID, inPages string) (pages []entity. args := []interface{}{p.Context.OrgID, documentID} tempValues := strings.Split(inPages, ",") - sql := "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.level, a.sequence, a.title, a.body, a.revisions, a.created, a.revised FROM page a WHERE a.orgid=? AND a.documentid=? AND a.refid IN (?" + strings.Repeat(",?", len(tempValues)-1) + ") ORDER BY sequence" + sql := "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.pagetype, a.level, a.sequence, a.title, a.body, a.revisions, a.created, a.revised FROM page a WHERE a.orgid=? AND a.documentid=? AND a.refid IN (?" + strings.Repeat(",?", len(tempValues)-1) + ") ORDER BY sequence" inValues := make([]interface{}, len(tempValues)) @@ -180,7 +180,7 @@ func (p *Persister) GetPagesWhereIn(documentID, inPages string) (pages []entity. // GetPagesWithoutContent returns a slice containing all the page records for a given documentID, in presentation sequence, // but without the body field (which holds the HTML content). func (p *Persister) GetPagesWithoutContent(documentID string) (pages []entity.Page, err error) { - err = Db.Select(&pages, "SELECT id, refid, orgid, documentid, userid, contenttype, sequence, level, title, revisions, created, revised FROM page WHERE orgid=? AND documentid=? ORDER BY sequence", p.Context.OrgID, documentID) + err = Db.Select(&pages, "SELECT id, refid, orgid, documentid, userid, contenttype, pagetype, sequence, level, title, revisions, created, revised FROM page WHERE orgid=? AND documentid=? ORDER BY sequence", p.Context.OrgID, documentID) if err != nil { log.Error(fmt.Sprintf("Unable to execute select pages for org %s and document %s", p.Context.OrgID, documentID), err) @@ -199,7 +199,7 @@ func (p *Persister) UpdatePage(page entity.Page, refID, userID string, skipRevis // Store revision history if !skipRevision { var stmt *sqlx.Stmt - stmt, err = p.Context.Transaction.Preparex("INSERT INTO revision (refid, orgid, documentid, ownerid, pageid, userid, contenttype, title, body, rawbody, config, created, revised) SELECT ? as refid, a.orgid, a.documentid, a.userid as ownerid, a.refid as pageid, ? as userid, a.contenttype, a.title, a.body, b.rawbody, b.config, ? as created, ? as revised FROM page a, pagemeta b WHERE a.refid=? AND a.refid=b.pageid") + stmt, err = p.Context.Transaction.Preparex("INSERT INTO revision (refid, orgid, documentid, ownerid, pageid, userid, contenttype, pagetype, title, body, rawbody, config, created, revised) SELECT ? as refid, a.orgid, a.documentid, a.userid as ownerid, a.refid as pageid, ? as userid, a.contenttype, a.pagetype, a.title, a.body, b.rawbody, b.config, ? as created, ? as revised FROM page a, pagemeta b WHERE a.refid=? AND a.refid=b.pageid") defer utility.Close(stmt) diff --git a/core/database/scripts/autobuild/db_00000.sql b/core/database/scripts/autobuild/db_00000.sql index 7e82db8c..9d683a51 100644 --- a/core/database/scripts/autobuild/db_00000.sql +++ b/core/database/scripts/autobuild/db_00000.sql @@ -149,6 +149,7 @@ CREATE TABLE IF NOT EXISTS `page` ( `documentid` CHAR(16) NOT NULL COLLATE utf8_bin, `userid` CHAR(16) DEFAULT '' COLLATE utf8_bin, `contenttype` CHAR(20) NOT NULL DEFAULT 'wysiwyg', + `pagetype` CHAR(10) NOT NULL DEFAULT 'section', `level` INT UNSIGNED NOT NULL, `sequence` DOUBLE NOT NULL, `title` NVARCHAR(2000) NOT NULL, @@ -239,6 +240,7 @@ CREATE TABLE IF NOT EXISTS `revision` ( `pageid` CHAR(16) NOT NULL COLLATE utf8_bin, `userid` CHAR(16) NOT NULL COLLATE utf8_bin, `contenttype` CHAR(20) NOT NULL DEFAULT 'wysiwyg', + `pagetype` CHAR(10) NOT NULL DEFAULT 'section', `title` NVARCHAR(2000) NOT NULL, `body` LONGTEXT, `rawbody` LONGBLOB, diff --git a/core/database/scripts/autobuild/db_00005.sql b/core/database/scripts/autobuild/db_00005.sql new file mode 100644 index 00000000..ccd1e322 --- /dev/null +++ b/core/database/scripts/autobuild/db_00005.sql @@ -0,0 +1,6 @@ +/* community edition */ +ALTER TABLE page ADD COLUMN `pagetype` CHAR(10) NOT NULL DEFAULT 'section' AFTER `contenttype`; +UPDATE page SET pagetype='tab' WHERE refid IN (SELECT pageid FROM pagemeta WHERE externalsource=1); + +ALTER TABLE revision ADD COLUMN `pagetype` CHAR(10) NOT NULL DEFAULT 'section' AFTER `contenttype`; +UPDATE revision SET pagetype='tab' WHERE pageid IN (SELECT pageid FROM pagemeta WHERE externalsource=1); diff --git a/core/section/airtable/airtable.go b/core/section/airtable/airtable.go index 0a356574..767bd4f1 100644 --- a/core/section/airtable/airtable.go +++ b/core/section/airtable/airtable.go @@ -29,6 +29,7 @@ func (*Provider) Meta() provider.TypeMeta { section.Title = "Airtable" section.Description = "Databases, tables, views" section.ContentType = "airtable" + section.PageType = "tab" return section } diff --git a/core/section/code/code.go b/core/section/code/code.go index c446acf1..e5a32632 100644 --- a/core/section/code/code.go +++ b/core/section/code/code.go @@ -29,6 +29,7 @@ func (*Provider) Meta() provider.TypeMeta { section.Title = "Code" section.Description = "Formatted code snippets" section.ContentType = "code" + section.PageType = "section" section.Order = 9997 return section diff --git a/core/section/gemini/gemini.go b/core/section/gemini/gemini.go index bba1e27f..415d9b65 100644 --- a/core/section/gemini/gemini.go +++ b/core/section/gemini/gemini.go @@ -35,6 +35,7 @@ func (*Provider) Meta() provider.TypeMeta { section.Title = "Gemini" section.Description = "Work items and tickets" section.ContentType = "gemini" + section.PageType = "tab" return section } diff --git a/core/section/github/github.go b/core/section/github/github.go index 26bebe2e..8516f584 100644 --- a/core/section/github/github.go +++ b/core/section/github/github.go @@ -39,6 +39,7 @@ func init() { meta.Title = "GitHub" meta.Description = "Link code commits and issues" meta.ContentType = "github" + meta.PageType = "tab" meta.Callback = Callback } diff --git a/core/section/markdown/markdown.go b/core/section/markdown/markdown.go index 24ee7eb4..c9b4e6d1 100644 --- a/core/section/markdown/markdown.go +++ b/core/section/markdown/markdown.go @@ -30,6 +30,7 @@ func (*Provider) Meta() provider.TypeMeta { section.Title = "Markdown" section.Description = "CommonMark based content" section.ContentType = "markdown" + section.PageType = "section" section.Order = 9998 return section diff --git a/core/section/papertrail/papertrail.go b/core/section/papertrail/papertrail.go index 1023e64d..02675fe5 100644 --- a/core/section/papertrail/papertrail.go +++ b/core/section/papertrail/papertrail.go @@ -38,6 +38,7 @@ func (*Provider) Meta() provider.TypeMeta { section.Title = "Papertrail" section.Description = "Display log entries" section.ContentType = "papertrail" + section.PageType = "tab" return section } diff --git a/core/section/provider/provider.go b/core/section/provider/provider.go index 7698c82e..f45faa96 100644 --- a/core/section/provider/provider.go +++ b/core/section/provider/provider.go @@ -35,6 +35,7 @@ type TypeMeta struct { ID string `json:"id"` Order int `json:"order"` ContentType string `json:"contentType"` + PageType string `json:"pageType"` Title string `json:"title"` Description string `json:"description"` Preview bool `json:"preview"` // coming soon! diff --git a/core/section/register.go b/core/section/register.go index c6dded39..47e2bbad 100644 --- a/core/section/register.go +++ b/core/section/register.go @@ -39,5 +39,5 @@ func Register() { provider.Register("wysiwyg", &wysiwyg.Provider{}) provider.Register("airtable", &airtable.Provider{}) p := provider.List() - log.Info(fmt.Sprintf("Documize registered %d smart sections", len(p))) + log.Info(fmt.Sprintf("Documize registered %d sections and tabs", len(p))) } diff --git a/core/section/table/table.go b/core/section/table/table.go index 56c3195d..0cf0e0e1 100644 --- a/core/section/table/table.go +++ b/core/section/table/table.go @@ -29,6 +29,7 @@ func (*Provider) Meta() provider.TypeMeta { section.Title = "Tabular" section.Description = "Rows, columns for tabular data" section.ContentType = "table" + section.PageType = "section" section.Order = 9996 return section diff --git a/core/section/trello/trello.go b/core/section/trello/trello.go index d18931a1..8e30f473 100644 --- a/core/section/trello/trello.go +++ b/core/section/trello/trello.go @@ -36,6 +36,7 @@ func init() { meta.Title = "Trello" meta.Description = "Embed cards from boards and lists" meta.ContentType = "trello" + meta.PageType = "tab" } // Provider represents Trello diff --git a/core/section/wysiwyg/wysiwyg.go b/core/section/wysiwyg/wysiwyg.go index 781bd89a..b1bc60b8 100644 --- a/core/section/wysiwyg/wysiwyg.go +++ b/core/section/wysiwyg/wysiwyg.go @@ -29,6 +29,7 @@ func (*Provider) Meta() provider.TypeMeta { section.Title = "Rich Text" section.Description = "Rich text WYSIWYG" section.ContentType = "wysiwyg" + section.PageType = "section" section.Order = 9999 return section