From 3f31d6d15ef81689bc2ee6ea464445d664266373 Mon Sep 17 00:00:00 2001 From: Harvey Kandola Date: Thu, 21 Sep 2017 15:48:00 +0100 Subject: [PATCH] category permission admin, re-vamped view layout --- domain/category/endpoint.go | 8 +- domain/permission/endpoint.go | 120 ++++++++++++++++-- domain/permission/mysql/store.go | 48 +++++++ domain/storer.go | 2 + ...attachments.js => document-attachments.js} | 4 +- ...idebar-view-index.js => document-index.js} | 0 .../{sidebar-zone.js => document-toolbar.js} | 44 +++---- ...bar-view-index-entry.js => index-entry.js} | 0 gui/app/components/document/page-heading.js | 2 +- gui/app/components/document/space-category.js | 34 +++++ gui/app/components/folder/category-admin.js | 103 ++++++++++++++- gui/app/components/folder/folder-toolbar.js | 7 +- gui/app/components/folder/permission-admin.js | 2 +- gui/app/components/ui/ui-list-picker.js | 23 ++++ gui/app/mixins/tooltip.js | 5 +- gui/app/pods/document/index/controller.js | 6 + gui/app/pods/document/index/template.hbs | 39 ++++-- gui/app/pods/folder/settings/route.js | 2 +- gui/app/pods/folder/settings/template.hbs | 2 +- gui/app/services/category.js | 27 +++- gui/app/services/user.js | 1 - gui/app/styles/print.scss | 3 +- gui/app/styles/view/common.scss | 3 +- ...debar-view-activity.scss => activity.scss} | 0 gui/app/styles/view/document/all.scss | 6 +- ...view-attachments.scss => attachments.scss} | 27 +--- .../{sidebar-view-index.scss => toc.scss} | 0 gui/app/styles/view/document/view.scss | 24 +++- gui/app/styles/view/folder/settings.scss | 24 +++- gui/app/styles/view/layout-sidebar.scss | 58 +-------- gui/app/styles/widget/widget-list-picker.scss | 41 ++++++ gui/app/styles/widget/widget.scss | 11 +- .../document/document-attachments.hbs | 41 ++++++ .../components/document/document-index.hbs | 35 +++++ .../components/document/document-toolbar.hbs | 80 ++++++++++++ .../components/document/document-view.hbs | 2 + ...r-view-index-entry.hbs => index-entry.hbs} | 0 .../document/sidebar-view-attachments.hbs | 47 ------- .../document/sidebar-view-index.hbs | 34 ----- .../components/document/sidebar-zone.hbs | 118 ----------------- .../components/document/space-category.hbs | 14 ++ .../components/document/tag-editor.hbs | 2 +- .../components/folder/category-admin.hbs | 47 +++++-- .../components/folder/folder-toolbar.hbs | 2 +- .../components/ui/ui-list-picker.hbs | 12 ++ model/audit/audit.go | 1 + model/permission/permission.go | 13 +- server/routing/routes.go | 2 + 48 files changed, 753 insertions(+), 373 deletions(-) rename gui/app/components/document/{sidebar-view-attachments.js => document-attachments.js} (97%) rename gui/app/components/document/{sidebar-view-index.js => document-index.js} (100%) rename gui/app/components/document/{sidebar-zone.js => document-toolbar.js} (87%) rename gui/app/components/document/{sidebar-view-index-entry.js => index-entry.js} (100%) create mode 100644 gui/app/components/document/space-category.js create mode 100644 gui/app/components/ui/ui-list-picker.js rename gui/app/styles/view/document/{sidebar-view-activity.scss => activity.scss} (100%) rename gui/app/styles/view/document/{sidebar-view-attachments.scss => attachments.scss} (69%) rename gui/app/styles/view/document/{sidebar-view-index.scss => toc.scss} (100%) create mode 100644 gui/app/styles/widget/widget-list-picker.scss create mode 100644 gui/app/templates/components/document/document-attachments.hbs create mode 100644 gui/app/templates/components/document/document-index.hbs create mode 100644 gui/app/templates/components/document/document-toolbar.hbs rename gui/app/templates/components/document/{sidebar-view-index-entry.hbs => index-entry.hbs} (100%) delete mode 100644 gui/app/templates/components/document/sidebar-view-attachments.hbs delete mode 100644 gui/app/templates/components/document/sidebar-view-index.hbs delete mode 100644 gui/app/templates/components/document/sidebar-zone.hbs create mode 100644 gui/app/templates/components/document/space-category.hbs create mode 100644 gui/app/templates/components/ui/ui-list-picker.hbs diff --git a/domain/category/endpoint.go b/domain/category/endpoint.go index ef54ab40..3da7fbc4 100644 --- a/domain/category/endpoint.go +++ b/domain/category/endpoint.go @@ -279,7 +279,9 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { } /* - 6. add category view permission !!! - 7. link/unlink document to category - 8. filter space documents by category -- URL param? nested route? + - category view permission handling + - filter users using new permission + - link/unlink document to category + - check print/pdf + - filter space documents by category -- URL param? nested route? */ diff --git a/domain/permission/endpoint.go b/domain/permission/endpoint.go index f711b733..154c7f10 100644 --- a/domain/permission/endpoint.go +++ b/domain/permission/endpoint.go @@ -30,6 +30,7 @@ import ( "github.com/documize/community/model/audit" "github.com/documize/community/model/permission" "github.com/documize/community/model/space" + "github.com/documize/community/model/user" ) // Handler contains the runtime information such as logging and database. @@ -43,25 +44,20 @@ func (h *Handler) SetSpacePermissions(w http.ResponseWriter, r *http.Request) { method := "space.SetPermissions" ctx := domain.GetRequestContext(r) - if !ctx.Editor { - response.WriteForbiddenError(w) - return - } - id := request.Param(r, "spaceID") if len(id) == 0 { response.WriteMissingDataError(w, method, "spaceID") return } - sp, err := h.Store.Space.Get(ctx, id) - if err != nil { - response.WriteNotFoundError(w, method, "space not found") + if !HasPermission(ctx, *h.Store, id, permission.SpaceManage, permission.SpaceOwner) { + response.WriteForbiddenError(w) return } - if sp.UserID != ctx.UserID { - response.WriteForbiddenError(w) + sp, err := h.Store.Space.Get(ctx, id) + if err != nil { + response.WriteNotFoundError(w, method, "space not found") return } @@ -220,7 +216,7 @@ func (h *Handler) SetSpacePermissions(w http.ResponseWriter, r *http.Request) { response.WriteEmpty(w) } -// GetSpacePermissions returns permissions for alll users for given space. +// GetSpacePermissions returns permissions for all users for given space. func (h *Handler) GetSpacePermissions(w http.ResponseWriter, r *http.Request) { method := "space.GetPermissions" ctx := domain.GetRequestContext(r) @@ -276,3 +272,105 @@ func (h *Handler) GetUserSpacePermissions(w http.ResponseWriter, r *http.Request record := permission.DecodeUserPermissions(perms) response.WriteJSON(w, record) } + +// GetCategoryPermissions returns user permissions for given category. +func (h *Handler) GetCategoryPermissions(w http.ResponseWriter, r *http.Request) { + method := "space.GetCategoryPermissions" + ctx := domain.GetRequestContext(r) + + categoryID := request.Param(r, "categoryID") + if len(categoryID) == 0 { + response.WriteMissingDataError(w, method, "categoryID") + return + } + + u, err := h.Store.Permission.GetCategoryUsers(ctx, categoryID) + if err != nil && err != sql.ErrNoRows { + response.WriteServerError(w, method, err) + return + } + if len(u) == 0 { + u = []user.User{} + } + + response.WriteJSON(w, u) +} + +// SetCategoryPermissions persists specified category permissions +func (h *Handler) SetCategoryPermissions(w http.ResponseWriter, r *http.Request) { + method := "permission.SetCategoryPermissions" + ctx := domain.GetRequestContext(r) + + id := request.Param(r, "categoryID") + if len(id) == 0 { + response.WriteMissingDataError(w, method, "categoryID") + return + } + + defer streamutil.Close(r.Body) + body, err := ioutil.ReadAll(r.Body) + if err != nil { + response.WriteBadRequestError(w, method, err.Error()) + h.Runtime.Log.Error(method, err) + return + } + + var model = []permission.CategoryViewRequestModel{} + err = json.Unmarshal(body, &model) + if err != nil { + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + if len(model) == 0 { + response.WriteEmpty(w) + return + } + + spaceID := model[0].SpaceID + if !HasPermission(ctx, *h.Store, spaceID, permission.SpaceManage, permission.SpaceOwner) { + response.WriteForbiddenError(w) + return + } + + ctx.Transaction, err = h.Runtime.Db.Beginx() + if err != nil { + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + // Nuke all previous permissions for this category + _, err = h.Store.Permission.DeleteCategoryPermissions(ctx, id) + if err != nil { + ctx.Transaction.Rollback() + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + for _, m := range model { + perm := permission.Permission{} + perm.OrgID = ctx.OrgID + perm.Who = "user" + perm.WhoID = m.UserID + perm.Scope = "object" + perm.Location = "category" + perm.RefID = m.CategoryID + perm.Action = permission.CategoryView + + err = h.Store.Permission.AddPermission(ctx, perm) + if err != nil { + ctx.Transaction.Rollback() + response.WriteServerError(w, method, err) + return + } + } + + h.Store.Audit.Record(ctx, audit.EventTypeCategoryPermission) + + ctx.Transaction.Commit() + + response.WriteEmpty(w) +} diff --git a/domain/permission/mysql/store.go b/domain/permission/mysql/store.go index 2dc585f4..e1da8f4d 100644 --- a/domain/permission/mysql/store.go +++ b/domain/permission/mysql/store.go @@ -22,6 +22,7 @@ import ( "github.com/documize/community/domain" "github.com/documize/community/domain/store/mysql" "github.com/documize/community/model/permission" + "github.com/documize/community/model/user" "github.com/pkg/errors" ) @@ -155,3 +156,50 @@ func (s Scope) DeleteSpaceCategoryPermissions(ctx domain.RequestContext, spaceID return b.DeleteWhere(ctx.Transaction, sql) } + +// GetCategoryPermissions returns category permissions for all users. +func (s Scope) GetCategoryPermissions(ctx domain.RequestContext, catID string) (r []permission.Permission, err error) { + err = s.Runtime.Db.Select(&r, ` + SELECT id, orgid, who, whoid, action, scope, location, refid + FROM permission WHERE orgid=? AND location='category' AND refid=? AND who='user' + UNION ALL + SELECT p.id, p.orgid, p.who, p.whoid, p.action, p.scope, p.location, p.refid + FROM permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.location='space' AND p.refid=? + AND p.who='role'`, + ctx.OrgID, catID, ctx.OrgID, catID) + + if err == sql.ErrNoRows { + err = nil + } + if err != nil { + err = errors.Wrap(err, fmt.Sprintf("unable to execute select category permissions %s", catID)) + return + } + + return +} + +// GetCategoryUsers returns space permissions for all users. +func (s Scope) GetCategoryUsers(ctx domain.RequestContext, catID string) (u []user.User, err error) { + err = s.Runtime.Db.Select(&u, ` + SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised + FROM user u, account a + WHERE a.orgid=? AND u.refid = a.userid AND a.active=1 AND u.refid IN ( + SELECT whoid from permission WHERE orgid=? AND who='user' AND location='category' AND refid=? UNION ALL + SELECT r.userid from rolemember r LEFT JOIN permission p ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' + AND p.location='category' AND p.refid=? + ) + GROUP by u.id + ORDER BY firstname, lastname`, + ctx.OrgID, ctx.OrgID, catID, ctx.OrgID, catID) + + if err == sql.ErrNoRows { + err = nil + } + if err != nil { + err = errors.Wrap(err, fmt.Sprintf("unable to execute select category user %s", catID)) + return + } + + return +} diff --git a/domain/storer.go b/domain/storer.go index 51959f5f..6e3c05bc 100644 --- a/domain/storer.go +++ b/domain/storer.go @@ -86,6 +86,8 @@ type PermissionStorer interface { DeleteUserPermissions(ctx RequestContext, userID string) (rows int64, err error) DeleteCategoryPermissions(ctx RequestContext, categoryID string) (rows int64, err error) DeleteSpaceCategoryPermissions(ctx RequestContext, spaceID string) (rows int64, err error) + GetCategoryPermissions(ctx RequestContext, catID string) (r []permission.Permission, err error) + GetCategoryUsers(ctx RequestContext, catID string) (u []user.User, err error) } // UserStorer defines required methods for user management diff --git a/gui/app/components/document/sidebar-view-attachments.js b/gui/app/components/document/document-attachments.js similarity index 97% rename from gui/app/components/document/sidebar-view-attachments.js rename to gui/app/components/document/document-attachments.js index aa425eec..97f220db 100644 --- a/gui/app/components/document/sidebar-view-attachments.js +++ b/gui/app/components/document/document-attachments.js @@ -17,7 +17,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { documentService: Ember.inject.service('document'), appMeta: Ember.inject.service(), drop: null, - emptyState: Ember.computed.empty('files'), + hasAttachments: Ember.computed.notEmpty('files'), deleteAttachment: { id: "", name: "", @@ -104,7 +104,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, { target: $(".delete-attachment-" + id)[0], content: $(".delete-attachment-dialog")[0], classes: 'drop-theme-basic', - position: "bottom left", + position: "bottom right", openOn: "always", tetherOptions: { offset: "5px 0", diff --git a/gui/app/components/document/sidebar-view-index.js b/gui/app/components/document/document-index.js similarity index 100% rename from gui/app/components/document/sidebar-view-index.js rename to gui/app/components/document/document-index.js diff --git a/gui/app/components/document/sidebar-zone.js b/gui/app/components/document/document-toolbar.js similarity index 87% rename from gui/app/components/document/sidebar-zone.js rename to gui/app/components/document/document-toolbar.js index a422456b..c593722d 100644 --- a/gui/app/components/document/sidebar-zone.js +++ b/gui/app/components/document/document-toolbar.js @@ -31,38 +31,31 @@ export default Ember.Component.extend(TooltipMixin, NotifierMixin, { name: "", description: "" }, - tab: '', init() { this._super(...arguments); - - if (is.empty(this.get('tab')) || is.undefined(this.get('tab'))) { - this.set('tab', 'index'); - } }, didReceiveAttrs() { this._super(...arguments); - + this.set('saveTemplate.name', this.get('document.name')); this.set('saveTemplate.description', this.get('document.excerpt')); - + this.set('pinState.pinId', this.get('pinned').isDocumentPinned(this.get('document.id'))); this.set('pinState.isPinned', this.get('pinState.pinId') !== ''); - this.set('pinState.newName', this.get('document.name').substring(0,3).toUpperCase()); + this.set('pinState.newName', this.get('document.name')); + }, + + didRender() { + this.destroyTooltips(); + + if (this.get('permissions.documentEdit')) { + this.addTooltip(document.getElementById("document-activity-button")); + } }, actions: { - onChangeTab(tab) { - this.set('tab', tab); - }, - - onTagChange(tags) { - let doc = this.get('document'); - doc.set('tags', tags); - this.get('documentService').save(doc); - }, - onMenuOpen() { this.set('menuOpen', !this.get('menuOpen')); }, @@ -78,15 +71,15 @@ export default Ember.Component.extend(TooltipMixin, NotifierMixin, { onPageSequenceChange(changes) { this.get('onPageSequenceChange')(changes); - }, + }, onPageLevelChange(changes) { this.get('onPageLevelChange')(changes); - }, + }, onGotoPage(id) { this.get('onGotoPage')(id); - }, + }, onUnpin() { this.get('pinned').unpinItem(this.get('pinState.pinId')).then(() => { @@ -136,12 +129,15 @@ export default Ember.Component.extend(TooltipMixin, NotifierMixin, { return true; }, - + onLayoutChange(layout) { let doc = this.get('document'); doc.set('layout', layout); - this.get('documentService').save(doc); - + + if (this.get('permissions.documentEdit')) { + this.get('documentService').save(doc); + } + return true; } } diff --git a/gui/app/components/document/sidebar-view-index-entry.js b/gui/app/components/document/index-entry.js similarity index 100% rename from gui/app/components/document/sidebar-view-index-entry.js rename to gui/app/components/document/index-entry.js diff --git a/gui/app/components/document/page-heading.js b/gui/app/components/document/page-heading.js index c88f4f03..2839d0e4 100644 --- a/gui/app/components/document/page-heading.js +++ b/gui/app/components/document/page-heading.js @@ -76,7 +76,7 @@ export default Ember.Component.extend(TooltipMixin, { let permissions = this.get('permissions'); return permissions.get('documentDelete') || permissions.get('documentCopy') || - permissions.get('documentMove') || permissions.get('documentTemplate');; + permissions.get('documentMove') || permissions.get('documentTemplate'); }), didRender() { diff --git a/gui/app/components/document/space-category.js b/gui/app/components/document/space-category.js new file mode 100644 index 00000000..b0fad8f9 --- /dev/null +++ b/gui/app/components/document/space-category.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 TooltipMixin from '../../mixins/tooltip'; +import NotifierMixin from '../../mixins/notifier'; + +export default Ember.Component.extend(TooltipMixin, NotifierMixin, { + documentService: Ember.inject.service('document'), + sectionService: Ember.inject.service('section'), + sessionService: Ember.inject.service('session'), + appMeta: Ember.inject.service(), + userService: Ember.inject.service('user'), + localStorage: Ember.inject.service(), + + init() { + this._super(...arguments); + }, + + didReceiveAttrs() { + this._super(...arguments); + }, + + actions: { + } +}); diff --git a/gui/app/components/folder/category-admin.js b/gui/app/components/folder/category-admin.js index 23d94eab..6d3d5c61 100644 --- a/gui/app/components/folder/category-admin.js +++ b/gui/app/components/folder/category-admin.js @@ -11,25 +11,59 @@ import Ember from 'ember'; import NotifierMixin from '../../mixins/notifier'; +import TooltipMixin from '../../mixins/tooltip'; const { inject: { service } } = Ember; -export default Ember.Component.extend(NotifierMixin, { - folderService: service('folder'), +export default Ember.Component.extend(NotifierMixin, TooltipMixin, { + userService: service('user'), categoryService: service('category'), appMeta: service(), store: service(), newCategory: '', + drop: null, + users: [], didReceiveAttrs() { this.load(); }, + didRender() { + // this.addTooltip(this.$(".action")); + }, + + willDestroyElement() { + let drop = this.get('drop'); + + if (is.not.null(drop)) { + drop.destroy(); + } + }, + load() { + // get categories this.get('categoryService').getAll(this.get('folder.id')).then((c) => { this.set('category', c); + + // get users that this space admin user can see + this.get('userService').getAll().then((users) => { + // set up Everyone user + let u = { + orgId: this.get('folder.orgId'), + folderId: this.get('folder.id'), + userId: '', + firstname: 'Everyone', + lastname: '', + }; + + let data = this.get('store').normalize('user', u) + users.pushObject(this.get('store').push(data)); + + users = users.sortBy('firstname', 'lastname'); + this.set('users', users); + }); }); }, @@ -76,7 +110,7 @@ export default Ember.Component.extend(NotifierMixin, { this.setEdit(id, true); }, - onCancel(id) { + onEditCancel(id) { this.setEdit(id, false); this.load(); }, @@ -94,6 +128,69 @@ export default Ember.Component.extend(NotifierMixin, { this.get('categoryService').save(cat).then(() => { this.load(); }); + }, + + onShowAccessPicker(catId) { + let users = this.get('users'); + let category = this.get('category').findBy('id', catId); + + this.get('categoryService').getViewers(category.get('id')).then((viewers) => { + // mark those users as selected that have already been given permission + // to see the current category; + console.log(viewers); + + users.forEach((user) => { + let selected = viewers.isAny('id', user.get('id')); + user.set('selected', selected); + }); + + this.set('categoryUsers', users); + this.set('currentCategory', category); + + $(".category-access-dialog").css("display", "block"); + + let drop = new Drop({ + target: $("#category-access-button-" + catId)[0], + content: $(".category-access-dialog")[0], + classes: 'drop-theme-basic', + position: "bottom right", + openOn: "always", + tetherOptions: { + offset: "5px 0", + targetOffset: "10px 0" + }, + remove: false + }); + + this.set('drop', drop); + }); + }, + + onGrantCancel() { + let drop = this.get('drop'); + drop.close(); + }, + + onGrantAccess() { + let category = this.get('currentCategory'); + let users = this.get('categoryUsers').filterBy('selected', true); + let viewers = []; + + users.forEach((user) => { + let v = { + orgId: this.get('folder.orgId'), + folderId: this.get('folder.id'), + categoryId: category.get('id'), + userId: user.get('id') + }; + + viewers.push(v); + }); + + this.get('categoryService').setViewers(category.get('id'), viewers).then( () => {}); + + let drop = this.get('drop'); + drop.close(); } } }); diff --git a/gui/app/components/folder/folder-toolbar.js b/gui/app/components/folder/folder-toolbar.js index fcfe713d..6b04e581 100644 --- a/gui/app/components/folder/folder-toolbar.js +++ b/gui/app/components/folder/folder-toolbar.js @@ -60,6 +60,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, AuthMixin, { if (this.get('permissions.documentMove')) { this.addTooltip(document.getElementById("move-documents-button")); } + if (this.get('permissions.documentDelete')) { this.addTooltip(document.getElementById("delete-documents-button")); } @@ -67,11 +68,13 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, AuthMixin, { if (this.get('permissions.spaceOwner')) { this.addTooltip(document.getElementById("space-delete-button")); } + if (this.get('permissions.spaceManage')) { this.addTooltip(document.getElementById("space-settings-button")); } - if (this.get('session.authenticated')) { - this.addTooltip(document.getElementById("space-unpin-button")); + + if (this.get('pinState.isPinned')) { + this.addTooltip(document.getElementById("space-unpin-button")); } else { this.addTooltip(document.getElementById("space-pin-button")); } diff --git a/gui/app/components/folder/permission-admin.js b/gui/app/components/folder/permission-admin.js index 847a1b13..74b0d5df 100644 --- a/gui/app/components/folder/permission-admin.js +++ b/gui/app/components/folder/permission-admin.js @@ -69,7 +69,7 @@ export default Ember.Component.extend(NotifierMixin, { documentMove: false, documentCopy: false, documentTemplate: false - }; + }; let data = this.get('store').normalize('space-permission', u) folderPermissions.pushObject(this.get('store').push(data)); diff --git a/gui/app/components/ui/ui-list-picker.js b/gui/app/components/ui/ui-list-picker.js new file mode 100644 index 00000000..9a083fee --- /dev/null +++ b/gui/app/components/ui/ui-list-picker.js @@ -0,0 +1,23 @@ +// 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'; + +export default Ember.Component.extend({ + nameField: 'category', + items: [], + + actions: { + onToggle(item) { + Ember.set(item, 'selected', !item.get('selected')); + } + } +}); diff --git a/gui/app/mixins/tooltip.js b/gui/app/mixins/tooltip.js index 21afd12a..ab7aca1b 100644 --- a/gui/app/mixins/tooltip.js +++ b/gui/app/mixins/tooltip.js @@ -15,10 +15,9 @@ export default Ember.Mixin.create({ tooltips: [], addTooltip(elem) { - - if(elem == null) { + if (elem == null) { return; - } + } let t = new Tooltip({ target: elem diff --git a/gui/app/pods/document/index/controller.js b/gui/app/pods/document/index/controller.js index d75e1bc6..9a2e8eab 100644 --- a/gui/app/pods/document/index/controller.js +++ b/gui/app/pods/document/index/controller.js @@ -226,6 +226,12 @@ export default Ember.Controller.extend(NotifierMixin, { if (this.get('pageId') !== id && id !== '') { this.set('pageId', id); } + }, + + onTagChange(tags) { + let doc = this.get('model.document'); + doc.set('tags', tags); + this.get('documentService').save(doc); } } }); diff --git a/gui/app/pods/document/index/template.hbs b/gui/app/pods/document/index/template.hbs index d75dcb78..9283d7db 100644 --- a/gui/app/pods/document/index/template.hbs +++ b/gui/app/pods/document/index/template.hbs @@ -1,27 +1,44 @@ {{#layout/zone-container}} + {{#layout/zone-sidebar}} - {{document/sidebar-zone folders=model.folders folder=model.folder document=model.document - pages=model.pages sections=model.section links=model.links permissions=model.permissions tab=tab - onDocumentDelete=(action 'onDocumentDelete') onSaveTemplate=(action 'onSaveTemplate') + {{document/document-index + document=model.document folder=model.folder pages=model.pages page=model.page permissions=model.permissions onPageSequenceChange=(action 'onPageSequenceChange') onPageLevelChange=(action 'onPageLevelChange') onGotoPage=(action 'onGotoPage')}} {{/layout/zone-sidebar}} + {{#layout/zone-content}}
-
- {{#link-to 'folder' model.folder.id model.folder.slug}} -
- arrow_back -
{{model.folder.name}}
-
- {{/link-to}} + +
+ {{document/space-category document=model.document folder=model.folder folders=model.folders permissions=model.permissions}}
+ +
+ {{document/document-toolbar + document=model.document folder=model.folder folders=model.folders permissions=model.permissions + onDocumentDelete=(action 'onDocumentDelete') onSaveTemplate=(action 'onSaveTemplate') + onPageSequenceChange=(action 'onPageSequenceChange') onPageLevelChange=(action 'onPageLevelChange') + onGotoPage=(action 'onGotoPage')}} +
+
+ + {{#if model.document.template}} +
Template
+ {{/if}} + {{document/document-heading document=model.document permissions=model.permissions onSaveDocument=(action 'onSaveDocument')}} - {{document/document-view document=model.document links=model.links pages=model.pages + + {{document/tag-editor documentTags=model.document.tags permissions=model.permissions onChange=(action 'onTagChange')}} + + {{document/document-view + document=model.document links=model.links pages=model.pages folder=model.folder folders=model.folders sections=model.sections permissions=model.permissions pageId=pageId onSavePage=(action 'onSavePage') onInsertSection=(action 'onInsertSection') onSavePageAsBlock=(action 'onSavePageAsBlock') onDeleteBlock=(action 'onDeleteBlock') onGotoPage=(action 'onGotoPage') onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onPageDeleted')}} +
{{/layout/zone-content}} + {{/layout/zone-container}} diff --git a/gui/app/pods/folder/settings/route.js b/gui/app/pods/folder/settings/route.js index b065f731..e7595d5e 100644 --- a/gui/app/pods/folder/settings/route.js +++ b/gui/app/pods/folder/settings/route.js @@ -15,7 +15,7 @@ import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-rout export default Ember.Route.extend(AuthenticatedRouteMixin, { beforeModel: function (transition) { if (is.equal(transition.targetName, 'folder.settings.index')) { - this.transitionTo('folder.settings.security'); + this.transitionTo('folder.settings.invitation'); } }, diff --git a/gui/app/pods/folder/settings/template.hbs b/gui/app/pods/folder/settings/template.hbs index a46ba99b..30f84ce7 100644 --- a/gui/app/pods/folder/settings/template.hbs +++ b/gui/app/pods/folder/settings/template.hbs @@ -10,7 +10,7 @@ {{#link-to 'folder.settings.invitation' activeClass='selected' class="option" tagName="li"}}Invite{{/link-to}} {{/if}} {{#link-to 'folder.settings.security' activeClass='selected' class="option" tagName="li"}}Secure{{/link-to}} - {{#link-to 'folder.settings.category' activeClass='selected' class="option" tagName="li"}}Organize{{/link-to}} + {{#link-to 'folder.settings.category' activeClass='selected' class="option" tagName="li"}}Categorize{{/link-to}}
diff --git a/gui/app/services/category.js b/gui/app/services/category.js index 7fa700e2..a3eea11c 100644 --- a/gui/app/services/category.js +++ b/gui/app/services/category.js @@ -83,5 +83,30 @@ export default BaseService.extend({ return this.get('ajax').request(`category/${categoryId}`, { method: 'DELETE' }); - } + }, + + // Get list of users who can see given category + getViewers(categoryId) { + return this.get('ajax').request(`category/${categoryId}/permission`, { + method: 'GET' + }).then((response) => { + let data = []; + + data = response.map((obj) => { + let data = this.get('store').normalize('user', obj); + return this.get('store').push(data); + }); + + return data; + }); + }, + + // Save list of users who can see given category + setViewers(categoryId, viewers) { + return this.get('ajax').request(`category/${categoryId}/permission`, { + method: 'PUT', + contentType: 'json', + data: JSON.stringify(viewers) + }); + }, }); diff --git a/gui/app/services/user.js b/gui/app/services/user.js index 30a6cb3a..1379e6c7 100644 --- a/gui/app/services/user.js +++ b/gui/app/services/user.js @@ -66,7 +66,6 @@ export default Ember.Service.extend({ }); }, - // Returns all users that can see folder. getFolderUsers(folderId) { let url = `users/folder/${folderId}`; diff --git a/gui/app/styles/print.scss b/gui/app/styles/print.scss index 2275f41c..ff1518b1 100644 --- a/gui/app/styles/print.scss +++ b/gui/app/styles/print.scss @@ -48,13 +48,12 @@ #sidebar-wrapper, .sidebar-wrapper, .sidebar-common, - .sidebar-toolbar, .sidebar-panel, .edit-document-heading, .back-to-space, .start-section, .start-button, - .is-a-tab, + .is-a-tab, .new-section-wizard { float: none !important; display: none !important; diff --git a/gui/app/styles/view/common.scss b/gui/app/styles/view/common.scss index 3d0908fb..019328ed 100644 --- a/gui/app/styles/view/common.scss +++ b/gui/app/styles/view/common.scss @@ -15,7 +15,8 @@ } .back-to-space { - margin: 10px 0; + margin: 0 0 10px 0; + display: inline-block; > a { > .regular-button { diff --git a/gui/app/styles/view/document/sidebar-view-activity.scss b/gui/app/styles/view/document/activity.scss similarity index 100% rename from gui/app/styles/view/document/sidebar-view-activity.scss rename to gui/app/styles/view/document/activity.scss diff --git a/gui/app/styles/view/document/all.scss b/gui/app/styles/view/document/all.scss index 07c3c3a3..81367013 100644 --- a/gui/app/styles/view/document/all.scss +++ b/gui/app/styles/view/document/all.scss @@ -2,8 +2,8 @@ @import "history.scss"; @import "inline-editor.scss"; @import "section-editor.scss"; -@import "sidebar-view-activity.scss"; -@import "sidebar-view-attachments.scss"; -@import "sidebar-view-index.scss"; +@import "activity.scss"; +@import "attachments.scss"; +@import "toc.scss"; @import "view.scss"; @import "wysiwyg.scss"; diff --git a/gui/app/styles/view/document/sidebar-view-attachments.scss b/gui/app/styles/view/document/attachments.scss similarity index 69% rename from gui/app/styles/view/document/sidebar-view-attachments.scss rename to gui/app/styles/view/document/attachments.scss index cc5b23cc..616b45c9 100644 --- a/gui/app/styles/view/document/sidebar-view-attachments.scss +++ b/gui/app/styles/view/document/attachments.scss @@ -1,31 +1,17 @@ -.document-sidebar-view-attachments { +.document-attachments { margin: 0; > .upload-document-files { - width: 100%; - padding: 20px; - margin-bottom: 20px; - text-align: center; - color: $color-gray; - border: 1px solid $color-stroke; - cursor: pointer; - font-size: 0.9rem; - line-height: 1.7rem; + margin: 10px 0 0 0; @include ease-in(); - &:hover { - border-color: $color-link; - color: $color-link; - } - - > .dz-preview, - .dz-processing { + > .dz-preview, .dz-processing { display: none !important; } } > .list { - margin: 0 0 50px; + margin: 20px 0 0 0; padding: 7px 0; > .item { @@ -45,7 +31,7 @@ > a { @extend .truncate; - width: 200px; + width: 80%; color: $color-gray; &:hover { @@ -56,7 +42,8 @@ @extend .truncate; display: inline-block; font-size: 0.9rem; - width: 200px; + width: 80%; + vertical-align: text-top; } } diff --git a/gui/app/styles/view/document/sidebar-view-index.scss b/gui/app/styles/view/document/toc.scss similarity index 100% rename from gui/app/styles/view/document/sidebar-view-index.scss rename to gui/app/styles/view/document/toc.scss diff --git a/gui/app/styles/view/document/view.scss b/gui/app/styles/view/document/view.scss index f47571c3..5cef4a18 100644 --- a/gui/app/styles/view/document/view.scss +++ b/gui/app/styles/view/document/view.scss @@ -8,7 +8,7 @@ .doc-excerpt { font-size: 1rem; color: $color-gray; - margin: 0 0 45px; + margin: 0 0 60px; } .edit-document-heading { @@ -341,3 +341,25 @@ .dropdown-page-toolbar { width: 300px; } + +.document-toolbar { + > .round-button-mono { + background-color: $color-white; + border: 1px solid $color-gray; + + > .material-icons { + @include ease-in(); + color: $color-gray; + } + } +} + +.document-template-header { + color: $color-goldy; + font-size: 1.5em; + margin-bottom: 20px; +} + +.document-tags { + margin-top: 15px; +} diff --git a/gui/app/styles/view/folder/settings.scss b/gui/app/styles/view/folder/settings.scss index 355b3fdb..63229611 100644 --- a/gui/app/styles/view/folder/settings.scss +++ b/gui/app/styles/view/folder/settings.scss @@ -43,15 +43,26 @@ > .row { margin: 15px 0; - padding: 8px 10px; + padding: 15px; background-color: $color-off-white; @include border-radius(2px); > .category { - font-size: 1.2rem; - vertical-align: bottom; display: inline-block; - margin-top: 8px; + + > .name { + font-size: 1.2rem; + } + + > .info { + font-size: 0.9rem; + margin-top: 8px; + color: $color-gray; + } + } + + > .buttons { + margin-top: 5px; } > .action { @@ -63,7 +74,7 @@ display: inline-block; > input { - margin: 0; + margin: 0 0 8px 0; padding: 0; font-size: 1.2rem; } @@ -72,3 +83,6 @@ } } +.category-access-dialog { + display: none; +} diff --git a/gui/app/styles/view/layout-sidebar.scss b/gui/app/styles/view/layout-sidebar.scss index 9b2c79da..9c58966d 100644 --- a/gui/app/styles/view/layout-sidebar.scss +++ b/gui/app/styles/view/layout-sidebar.scss @@ -58,48 +58,6 @@ $sidebar-width: 400px; #sidebar-wrapper { width: $sidebar-width; } - - // #page-content-wrapper { - // padding: 30px; - // position: relative; - // } -} - -.sidebar-toolbar { - display: inline-block; - width: 60px; - background-color: $color-primary; - text-align: center; - position: fixed; - left: 0; - top: 0; - height: 100%; - padding: 40px 0 0 0; - - > .selected { - background-color: $color-link !important; - border: 1px solid $color-link !important; - - > .material-icons { - color: $color-white !important; - } - } - - > .round-button-mono { - background-color: $color-off-white; - border: 1px solid $color-off-white; - - > .material-icons { - @include ease-in(); - color: $color-gray; - } - - &:hover { - > .material-icons { - color: $color-link; - } - } - } } .sidebar-common { @@ -108,20 +66,6 @@ $sidebar-width: 400px; padding: 40px 20px 0px 20px; margin-left: 20px; - > .pinner { - cursor: pointer; - - > .material-icons { - color: $color-primary; - } - } - - > .template-header { - color: $color-goldy; - font-size: 1.5em; - margin-bottom: 20px; - } - .zone-sidebar-page-title { color: $color-primary; font-size: 1.3rem; @@ -150,7 +94,7 @@ $sidebar-width: 400px; margin-bottom: 30px; } - .folder-sidebar-form-wrapper, .document-sidebar-form-wrapper { + .document-sidebar-form-wrapper { padding: 20px; border: 1px solid $color-stroke; @include border-radius(3px); diff --git a/gui/app/styles/widget/widget-list-picker.scss b/gui/app/styles/widget/widget-list-picker.scss new file mode 100644 index 00000000..6ba8bc25 --- /dev/null +++ b/gui/app/styles/widget/widget-list-picker.scss @@ -0,0 +1,41 @@ +.widget-list-picker { + margin: 10px 0; + + > .options { + width: 300px; + max-height: 400px; + overflow: auto; + + > .option { + margin: 0 0 5px 0; + padding: 10px 15px; + color: $color-gray; + background-color: $color-off-white; + cursor: pointer; + position: relative; + + &:hover { + color: $color-white; + background-color: $color-gray; + } + + > .text { + width: 220px; + overflow: hidden; + font-weight: bold; + } + + > .material-icons { + position: absolute; + top: 10px; + right: 10px; + color: $color-white; + } + } + + > .selected { + color: $color-white !important; + background-color: $color-link !important; + } + } +} diff --git a/gui/app/styles/widget/widget.scss b/gui/app/styles/widget/widget.scss index b41956ba..ae8287ca 100644 --- a/gui/app/styles/widget/widget.scss +++ b/gui/app/styles/widget/widget.scss @@ -64,15 +64,16 @@ @import "widget-avatar"; @import "widget-button"; @import "widget-card"; +@import "widget-checkbox"; @import "widget-chip"; @import "widget-dropdown"; @import "widget-input"; +@import "widget-list-picker"; @import "widget-notification"; +@import "widget-radio"; +@import "widget-selection"; @import "widget-sidebar-menu"; +@import "widget-symbol"; +@import "widget-tab"; @import "widget-table"; @import "widget-tooltip"; -@import "widget-checkbox"; -@import "widget-radio"; -@import "widget-tab"; -@import "widget-selection"; -@import "widget-symbol"; diff --git a/gui/app/templates/components/document/document-attachments.hbs b/gui/app/templates/components/document/document-attachments.hbs new file mode 100644 index 00000000..aa145e63 --- /dev/null +++ b/gui/app/templates/components/document/document-attachments.hbs @@ -0,0 +1,41 @@ +
+ {{#if hasAttachments}} +
    + {{#each files key="id" as |a index|}} +
  • + + + {{ a.filename }} + + {{#if permissions.documentEdit}} +
    + delete +
    + {{/if}} +
  • + {{/each}} +
+ {{/if}} + {{#if permissions.documentEdit}} +
+
+ + attachment +
+
+ {{/if}} +
+ + diff --git a/gui/app/templates/components/document/document-index.hbs b/gui/app/templates/components/document/document-index.hbs new file mode 100644 index 00000000..3c65420f --- /dev/null +++ b/gui/app/templates/components/document/document-index.hbs @@ -0,0 +1,35 @@ +