diff --git a/domain/category/endpoint.go b/domain/category/endpoint.go index c4c18828..ae8a1d58 100644 --- a/domain/category/endpoint.go +++ b/domain/category/endpoint.go @@ -415,6 +415,32 @@ func (h *Handler) GetDocumentCategoryMembership(w http.ResponseWriter, r *http.R response.WriteJSON(w, cat) } -/* - - filter space documents by category -- URL param? nested route? -*/ +// GetSpaceCategoryMembers returns category/document associations within space. +func (h *Handler) GetSpaceCategoryMembers(w http.ResponseWriter, r *http.Request) { + method := "category.GetSpaceCategoryMembers" + ctx := domain.GetRequestContext(r) + + spaceID := request.Param(r, "spaceID") + if len(spaceID) == 0 { + response.WriteMissingDataError(w, method, "spaceID") + return + } + + if !permission.HasPermission(ctx, *h.Store, spaceID, pm.SpaceView) { + response.WriteForbiddenError(w) + return + } + + cat, err := h.Store.Category.GetSpaceCategoryMembership(ctx, spaceID) + if err != nil && err != sql.ErrNoRows { + h.Runtime.Log.Error("get document category membership for space", err) + response.WriteServerError(w, method, err) + return + } + + if len(cat) == 0 { + cat = []category.Member{} + } + + response.WriteJSON(w, cat) +} diff --git a/domain/category/mysql/store.go b/domain/category/mysql/store.go index 35f849fc..aef7762a 100644 --- a/domain/category/mysql/store.go +++ b/domain/category/mysql/store.go @@ -45,7 +45,7 @@ func (s Scope) Add(ctx domain.RequestContext, c category.Category) (err error) { return } -// GetBySpace returns space categories for a user. +// GetBySpace returns space categories accessible by user. // Context is used to for user ID. func (s Scope) GetBySpace(ctx domain.RequestContext, spaceID string) (c []category.Category, err error) { err = s.Runtime.Db.Select(&c, ` @@ -185,7 +185,7 @@ func (s Scope) GetSpaceCategorySummary(ctx domain.RequestContext, spaceID string err = nil } if err != nil { - err = errors.Wrap(err, fmt.Sprintf("unable to execute select category summary for space %s", spaceID)) + err = errors.Wrap(err, fmt.Sprintf("select category summary for space %s", spaceID)) } return @@ -206,3 +206,25 @@ func (s Scope) GetDocumentCategoryMembership(ctx domain.RequestContext, document return } + +// GetSpaceCategoryMembership returns category/document associations within space. +func (s Scope) GetSpaceCategoryMembership(ctx domain.RequestContext, spaceID string) (c []category.Member, err error) { + err = s.Runtime.Db.Select(&c, ` + SELECT id, refid, orgid, labelid, categoryid, documentid, created, revised FROM categorymember + WHERE orgid=? AND labelid=? + AND labelid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN ( + SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space' UNION ALL + SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' + AND p.action='view' AND r.userid=? + )) + ORDER BY documentid`, ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID) + + if err == sql.ErrNoRows { + err = nil + } + if err != nil { + err = errors.Wrap(err, fmt.Sprintf("select all category/document membership for space %s", spaceID)) + } + + return +} diff --git a/domain/document/endpoint.go b/domain/document/endpoint.go index aabd5877..a03da564 100644 --- a/domain/document/endpoint.go +++ b/domain/document/endpoint.go @@ -137,11 +137,10 @@ func (h *Handler) DocumentLinks(w http.ResponseWriter, r *http.Request) { // BySpace is an endpoint that returns the documents for given space. func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) { - method := "document.space" + method := "document.BySpace" ctx := domain.GetRequestContext(r) spaceID := request.Query(r, "space") - if len(spaceID) == 0 { response.WriteMissingDataError(w, method, "space") return @@ -152,45 +151,47 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) { return } + // get complete list of documents documents, err := h.Store.Document.GetBySpace(ctx, spaceID) - - if len(documents) == 0 { - documents = []doc.Document{} - } - if err != nil && err != sql.ErrNoRows { response.WriteServerError(w, method, err) h.Runtime.Log.Error(method, err) return } - - response.WriteJSON(w, documents) -} - -// ByTag is an endpoint that returns the documents with a given tag. -func (h *Handler) ByTag(w http.ResponseWriter, r *http.Request) { - method := "document.space" - ctx := domain.GetRequestContext(r) - - tag := request.Query(r, "tag") - if len(tag) == 0 { - response.WriteMissingDataError(w, method, "tag") - return - } - - documents, err := h.Store.Document.GetByTag(ctx, tag) - if len(documents) == 0 { documents = []doc.Document{} } - if err != nil && err != sql.ErrNoRows { - response.WriteServerError(w, method, err) - h.Runtime.Log.Error(method, err) - return + // remove documents that cannot be seen due to lack of + // category view/access permission + filtered := []doc.Document{} + cats, err := h.Store.Category.GetBySpace(ctx, spaceID) + members, err := h.Store.Category.GetSpaceCategoryMembership(ctx, spaceID) + + for _, doc := range documents { + hasCategory := false + canSeeCategory := false + + OUTER: + + for _, m := range members { + if m.DocumentID == doc.RefID { + hasCategory = true + for _, cat := range cats { + if cat.RefID == m.CategoryID { + canSeeCategory = true + continue OUTER + } + } + } + } + + if !hasCategory || canSeeCategory { + filtered = append(filtered, doc) + } } - response.WriteJSON(w, documents) + response.WriteJSON(w, filtered) } // Update updates an existing document using the diff --git a/domain/document/mysql/store.go b/domain/document/mysql/store.go index 5387e94e..5741492f 100644 --- a/domain/document/mysql/store.go +++ b/domain/document/mysql/store.go @@ -101,9 +101,22 @@ func (s Scope) GetAll() (ctx domain.RequestContext, documents []doc.Document, er return } -// GetBySpace returns a slice containing the documents for a given space, most recient first. -func (s Scope) GetBySpace(ctx domain.RequestContext, folderID string) (documents []doc.Document, err error) { - err = s.Runtime.Db.Select(&documents, "SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? AND template=0 AND labelid=? ORDER BY revised DESC", ctx.OrgID, folderID) +// GetBySpace returns a slice containing the documents for a given space. +// No attempt is made to hide documents that are protected +// by category permissions -- caller must filter as required. +func (s Scope) GetBySpace(ctx domain.RequestContext, spaceID string) (documents []doc.Document, err error) { + err = s.Runtime.Db.Select(&documents, ` + SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised + FROM document + WHERE orgid=? AND template=0 AND labelid IN ( + SELECT refid FROM label WHERE orgid=? AND refid IN + (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid=? AND refid IN ( + SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space' UNION ALL + SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? + AND p.who='role' AND p.location='space' AND p.refid=? AND p.action='view' AND r.userid=? + )) + ) + ORDER BY title`, ctx.OrgID, ctx.OrgID, ctx.OrgID, spaceID, ctx.OrgID, ctx.UserID, ctx.OrgID, spaceID, ctx.UserID) if err != nil { err = errors.Wrap(err, "select documents by space") @@ -112,31 +125,6 @@ func (s Scope) GetBySpace(ctx domain.RequestContext, folderID string) (documents return } -// GetByTag returns a slice containing the documents with the specified tag, in title order. -func (s Scope) GetByTag(ctx domain.RequestContext, tag string) (documents []doc.Document, err error) { - tagQuery := "tags LIKE '%#" + tag + "#%'" - - err = s.Runtime.Db.Select(&documents, ` - SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? AND template=0 AND `+tagQuery+` - AND labelid IN - ( - SELECT refid FROM label WHERE orgid=? - AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN ( - SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space' - UNION ALL - SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=? - )) - ) - ORDER BY title - `, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID) - - if err != nil { - err = errors.Wrap(err, "select documents by tag") - } - - return -} - // Templates returns a slice containing the documents available as templates to the client's organisation, in title order. func (s Scope) Templates(ctx domain.RequestContext) (documents []doc.Document, err error) { err = s.Runtime.Db.Select(&documents, diff --git a/domain/space/endpoint.go b/domain/space/endpoint.go index c58d3a9e..fe6f1072 100644 --- a/domain/space/endpoint.go +++ b/domain/space/endpoint.go @@ -811,4 +811,4 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) { ctx.Transaction.Commit() response.WriteEmpty(w) -} +} \ No newline at end of file diff --git a/domain/storer.go b/domain/storer.go index 35cecb75..b617ebde 100644 --- a/domain/storer.go +++ b/domain/storer.go @@ -75,6 +75,7 @@ type CategoryStorer interface { RemoveCategoryMembership(ctx RequestContext, categoryID string) (rows int64, err error) DeleteBySpace(ctx RequestContext, spaceID string) (rows int64, err error) GetDocumentCategoryMembership(ctx RequestContext, documentID string) (c []category.Category, err error) + GetSpaceCategoryMembership(ctx RequestContext, spaceID string) (c []category.Member, err error) } // PermissionStorer defines required methods for space/document permission management @@ -157,8 +158,7 @@ type DocumentStorer interface { Add(ctx RequestContext, document doc.Document) (err error) Get(ctx RequestContext, id string) (document doc.Document, err error) GetAll() (ctx RequestContext, documents []doc.Document, err error) - GetBySpace(ctx RequestContext, folderID string) (documents []doc.Document, err error) - GetByTag(ctx RequestContext, tag string) (documents []doc.Document, err error) + GetBySpace(ctx RequestContext, spaceID string) (documents []doc.Document, err error) DocumentList(ctx RequestContext) (documents []doc.Document, err error) Templates(ctx RequestContext) (documents []doc.Document, err error) TemplatesBySpace(ctx RequestContext, spaceID string) (documents []doc.Document, err error) diff --git a/gui/app/components/folder/documents-list.js b/gui/app/components/folder/documents-list.js index c8194be7..afc2337c 100644 --- a/gui/app/components/folder/documents-list.js +++ b/gui/app/components/folder/documents-list.js @@ -12,15 +12,6 @@ import Ember from 'ember'; export default Ember.Component.extend({ - folderService: Ember.inject.service('folder'), - moveTarget: null, - - didReceiveAttrs() { - this._super(...arguments); - - this.set('deleteTargets', this.get('folders').rejectBy('id', this.get('folder.id'))); - }, - actions: { selectDocument(documentId) { let doc = this.get('documents').findBy('id', documentId); diff --git a/gui/app/components/folder/space-view.js b/gui/app/components/folder/space-view.js index a3a828a8..cf3b1c34 100644 --- a/gui/app/components/folder/space-view.js +++ b/gui/app/components/folder/space-view.js @@ -26,6 +26,54 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, AuthMixin, { selectedDocuments: [], hasSelectedDocuments: Ember.computed.gt('selectedDocuments.length', 0), showStartDocument: false, + filteredDocs: [], + selectedCategory: '', + + didReceiveAttrs() { + this._super(...arguments); + + let categories = this.get('categories'); + let categorySummary = this.get('categorySummary'); + let selectedCategory = ''; + + categories.forEach((cat)=> { + let summary = _.findWhere(categorySummary, {type: "documents", categoryId: cat.get('id')}); + let docCount = is.not.undefined(summary) ? summary.count : 0; + cat.set('docCount', docCount); + if (docCount > 0 && selectedCategory === '') selectedCategory = cat.get('id'); + }); + + this.set('categories', categories); + this.set('selectedCategory', selectedCategory); + + // Default document view logic: + // + // 1. show space root documents if we have any + // 2. show category documents for first category that has documents + if (this.get('rootDocCount') > 0) { + this.send('onDocumentFilter', 'space', this.get('folder.id')); + } else { + if (selectedCategory !== '') { + this.send('onDocumentFilter', 'category', selectedCategory); + } + } + }, + + didRender() { + this._super(...arguments); + + if (this.get('categories.length') > 0) { + this.addTooltip(document.getElementById("uncategorized-button")); + } + }, + + willDestroyElement() { + this._super(...arguments); + + if (this.get('isDestroyed') || this.get('isDestroying')) return; + + this.destroyTooltips(); + }, actions: { onMoveDocument(folder) { @@ -86,6 +134,36 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, AuthMixin, { onHideStartDocument() { this.set('showStartDocument', false); + }, + + onDocumentFilter(filter, id) { + let docs = this.get('documents'); + let categoryMembers = this.get('categoryMembers'); + let filtered = []; + + // filter doc list by category + if (filter === 'category') { + let allowed = _.pluck(_.where(categoryMembers, {'categoryId': id}), 'documentId'); + docs.forEach((d) => { + if (_.contains(allowed, d.get('id'))) { + filtered.pushObject(d); + } + }); + } + + // filter doc list by space (i.e. have no category) + if (filter === 'space') { + this.set('selectedCategory', id); + let allowed = _.pluck(categoryMembers, 'documentId'); + docs.forEach((d) => { + if (!_.contains(allowed, d.get('id'))) { + filtered.pushObject(d); + } + }); + } + + this.set('filteredDocs', filtered); + this.set('selectedCategory', id); } } }); diff --git a/gui/app/pods/folder/index/route.js b/gui/app/pods/folder/index/route.js index 9f16fed7..ec983db3 100644 --- a/gui/app/pods/folder/index/route.js +++ b/gui/app/pods/folder/index/route.js @@ -13,6 +13,8 @@ import Ember from 'ember'; import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; export default Ember.Route.extend(AuthenticatedRouteMixin, { + categoryService: Ember.inject.service('category'), + model() { this.get('browser').setTitle(this.modelFor('folder').folder.get('name')); @@ -23,9 +25,29 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, { documents: this.modelFor('folder').documents, templates: this.modelFor('folder').templates, showStartDocument: false, + categories: this.get('categoryService').getUserVisible(this.modelFor('folder').folder.get('id')), + categorySummary: this.get('categoryService').getSummary(this.modelFor('folder').folder.get('id')), + categoryMembers: this.get('categoryService').getSpaceCategoryMembership(this.modelFor('folder').folder.get('id')), + rootDocCount: 0 }); }, + afterModel(model, transition) { // eslint-disable-line no-unused-vars + let docs = model.documents; + let categoryMembers = model.categoryMembers; + let rootDocCount = 0; + + // get documentId's from category members + let withCat = _.pluck(categoryMembers, 'documentId'); + + // calculate documents without category; + docs.forEach((d) => { + if (!withCat.includes(d.get('id'))) rootDocCount+=1; + }); + + model.rootDocCount = rootDocCount; + }, + activate() { this.set('model.showStartDocument', false); } diff --git a/gui/app/pods/folder/index/template.hbs b/gui/app/pods/folder/index/template.hbs index 932b3f8e..88b71434 100644 --- a/gui/app/pods/folder/index/template.hbs +++ b/gui/app/pods/folder/index/template.hbs @@ -1,20 +1,26 @@ {{#layout/zone-container}} {{#layout/zone-sidebar}} - {{folder/sidebar-zone folders=model.folders folder=model.folder - permissions=model.permissions tab=tab onAddSpace=(action 'onAddSpace')}} + {{folder/sidebar-zone + folders=model.folders + folder=model.folder + permissions=model.permissions + tab=tab + onAddSpace=(action 'onAddSpace')}} {{/layout/zone-sidebar}} {{#layout/zone-content}} - - {{folder/space-view - folders=model.folders - folder=model.folder - templates=model.templates - permissions=model.permissions - documents=model.documents - onRefresh=(action 'onRefresh')}} - + {{folder/space-view + folders=model.folders + folder=model.folder + templates=model.templates + permissions=model.permissions + documents=model.documents + categories=model.categories + categorySummary=model.categorySummary + categoryMembers=model.categoryMembers + rootDocCount=model.rootDocCount + onRefresh=(action 'onRefresh')}} {{/layout/zone-content}} {{/layout/zone-container}} \ No newline at end of file diff --git a/gui/app/services/category.js b/gui/app/services/category.js index ae5d7c42..5062830d 100644 --- a/gui/app/services/category.js +++ b/gui/app/services/category.js @@ -143,5 +143,13 @@ export default BaseService.extend({ }).then((response) => { return response; }); + }, + + getSpaceCategoryMembership(spaceId) { + return this.get('ajax').request(`category/member/space/${spaceId}`, { + method: 'GET' + }).then((response) => { + return response; + }); } }); diff --git a/gui/app/services/document.js b/gui/app/services/document.js index b8755000..e14cc6c5 100644 --- a/gui/app/services/document.js +++ b/gui/app/services/document.js @@ -51,24 +51,6 @@ export default Ember.Service.extend({ }); }, - // getDocumentsByTag returns all documents for specified tag (not folder!). - getAllByTag(tag) { - return this.get('ajax').request(`documents?filter=tag&tag=${tag}`, { - method: "GET" - }).then((response) => { - let documents = Ember.ArrayProxy.create({ - content: Ember.A([]) - }); - - documents = response.map((doc) => { - let data = this.get('store').normalize('document', doc); - return this.get('store').push(data); - }); - - return documents; - }); - }, - // saveDocument updates an existing document record. save(doc) { let id = doc.get('id'); @@ -79,7 +61,7 @@ export default Ember.Service.extend({ }); }, - changePageSequence: function (documentId, payload) { + changePageSequence(documentId, payload) { let url = `documents/${documentId}/pages/sequence`; return this.get('ajax').post(url, { @@ -97,7 +79,7 @@ export default Ember.Service.extend({ }); }, - deleteDocument: function (documentId) { + deleteDocument(documentId) { let url = `documents/${documentId}`; return this.get('ajax').request(url, { @@ -122,7 +104,7 @@ export default Ember.Service.extend({ }, // addPage inserts new page to an existing document. - addPage: function (documentId, payload) { + addPage(documentId, payload) { let url = `documents/${documentId}/pages`; return this.get('ajax').post(url, { @@ -132,7 +114,7 @@ export default Ember.Service.extend({ }, // Nukes multiple pages from the document. - deletePages: function (documentId, pageId, payload) { + deletePages(documentId, pageId, payload) { let url = `documents/${documentId}/pages`; return this.get('ajax').request(url, { @@ -143,7 +125,7 @@ export default Ember.Service.extend({ }, // Nukes a single page from the document. - deletePage: function (documentId, pageId) { + deletePage(documentId, pageId) { let url = `documents/${documentId}/pages/${pageId}`; return this.get('ajax').request(url, { diff --git a/gui/app/styles/color.scss b/gui/app/styles/color.scss index 0b0796b9..16839c89 100644 --- a/gui/app/styles/color.scss +++ b/gui/app/styles/color.scss @@ -38,14 +38,25 @@ $color-border: #f3f5f8; $color-checkbox: #0092d3; +// lightblue sidebar $color-sidebar: #f2faff; $color-sidebar-border: #dff0f9; -$color-sidebar-text: $color-off-black; +$color-sidebar-text: $color-black; $color-sidebar-link: $color-link; -$color-selected-item: $color-sidebar; $color-nav-button: $color-sidebar; $color-nav-button-text: #2667af; $color-nav-button-border: #dff0f9; +$color-selected-item: $color-sidebar; + +// black sidebar +// $color-sidebar: $color-off-black; +// $color-sidebar-border: $color-black; +// $color-sidebar-text: $color-white; +// $color-sidebar-link: orange; +// $color-nav-button: orange; +// $color-nav-button-text: $color-off-black; +// $color-nav-button-border: $color-black; +// $color-selected-item: orange; // Color utility classes for direct usage in HTML .color-white { diff --git a/gui/app/styles/view/document/space-category-tag.scss b/gui/app/styles/view/document/space-category-tag.scss index 667805b5..a7b2249e 100644 --- a/gui/app/styles/view/document/space-category-tag.scss +++ b/gui/app/styles/view/document/space-category-tag.scss @@ -5,7 +5,7 @@ text-transform: uppercase; color: $color-gray; font-weight: bold; - font-size: 1.2rem; + font-size: 1.0rem; margin: 0 0 10px 0; } } @@ -22,7 +22,7 @@ text-transform: uppercase; color: $color-gray; font-weight: bold; - font-size: 1.2rem; + font-size: 1.0rem; margin: 0 0 10px 0; } } @@ -34,7 +34,7 @@ text-transform: uppercase; color: $color-gray; font-weight: bold; - font-size: 1.2rem; + font-size: 1.0rem; margin: 0 0 10px 0; } diff --git a/gui/app/styles/view/folder/document.scss b/gui/app/styles/view/folder/document.scss index 70fae834..17d91189 100644 --- a/gui/app/styles/view/folder/document.scss +++ b/gui/app/styles/view/folder/document.scss @@ -1,21 +1,28 @@ -.folder-heading { +.space-heading { margin: 0 0 55px 0; - .folder-title { + .space-name { font-size: 2rem; margin: 0 0 10px 0; font-weight: normal; + // color: $color-primary; + } + + .space-summary { + font-size: 1.2rem; + color: $color-gray; + margin: 0 0 45px; } } -.edit-folder-heading { +.edit-space-heading { margin: 0 0 10px 0; - .edit-folder-title { + .edit-space-name { > input { font-size: 2rem; font-weight: normal; - margin: 0 0 10px; + margin: 0 0 40px 0; color: $color-wysiwyg; } } @@ -23,6 +30,7 @@ .documents-list { @include content-container(); + margin-bottom: 50px; .document-item { margin: 0; @@ -68,7 +76,15 @@ &:hover { text-decoration: none; - color: $color-off-black; + color: $color-link; + + > .title { + color: $color-link; + } + + > .snippet { + color: $color-link; + } } > .title { @@ -116,3 +132,11 @@ color: $color-link; } } + +.category-filter { + margin-bottom: 30px; + + > .selected { + @extend .button-nav; + } +} diff --git a/gui/app/styles/view/folder/folder.scss b/gui/app/styles/view/folder/folder.scss index 1cad0848..f14eb625 100644 --- a/gui/app/styles/view/folder/folder.scss +++ b/gui/app/styles/view/folder/folder.scss @@ -21,11 +21,11 @@ > .link { font-size: 1rem; - color: $color-off-black; + color: $color-sidebar-text; text-decoration: none; &:hover { - color: $color-link; + color: $color-sidebar-link; } > .item { @@ -37,7 +37,7 @@ } > .selected { - color: $color-link; + color: $color-sidebar-link; font-weight: bold; } } diff --git a/gui/app/templates/components/document/space-category.hbs b/gui/app/templates/components/document/space-category.hbs index a0b84b94..2e64bd5b 100644 --- a/gui/app/templates/components/document/space-category.hbs +++ b/gui/app/templates/components/document/space-category.hbs @@ -15,7 +15,9 @@
{{cat.category}}
{{else}} {{#if canAddCategory}} - {{#link-to 'folder.settings.category' folder.id folder.slug}}Manage{{/link-to}} + {{#unless canSelectCategory}} + {{#link-to 'folder.settings.category' folder.id folder.slug}}Manage{{/link-to}} + {{/unless}} {{else}}

 

{{/if}} diff --git a/gui/app/templates/components/folder/documents-list.hbs b/gui/app/templates/components/folder/documents-list.hbs index a2ee0aaa..5e40f273 100644 --- a/gui/app/templates/components/folder/documents-list.hbs +++ b/gui/app/templates/components/folder/documents-list.hbs @@ -1,22 +1,24 @@ -
- {{#each documents key="id" as |document|}} -
-
- {{#link-to 'document.index' folder.id folder.slug document.id document.slug class="link"}} -
{{ document.name }}
-
{{ document.excerpt }}
-
{{folder/document-tags documentTags=document.tags}}
- {{/link-to}} - {{#if session.authenticated}} -
- {{#if document.selected}} - check_box - {{else}} - check_box_outline_blank - {{/if}} -
- {{/if}} +{{#if (gt documents.length 0)}} +
+ {{#each documents key="id" as |document|}} +
+
+ {{#link-to 'document.index' folder.id folder.slug document.id document.slug class="link"}} +
{{ document.name }}
+
{{ document.excerpt }}
+
{{folder/document-tags documentTags=document.tags}}
+ {{/link-to}} + {{#if session.authenticated}} +
+ {{#if document.selected}} + check_box + {{else}} + check_box_outline_blank + {{/if}} +
+ {{/if}} +
-
- {{/each}} -
+ {{/each}} +
+{{/if}} \ No newline at end of file diff --git a/gui/app/templates/components/folder/space-heading.hbs b/gui/app/templates/components/folder/space-heading.hbs index fc0bbcfa..7b431a2c 100644 --- a/gui/app/templates/components/folder/space-heading.hbs +++ b/gui/app/templates/components/folder/space-heading.hbs @@ -1,12 +1,15 @@
{{#unless editMode}} -
-

{{folder.name}}

+
+

{{folder.name}}

+
+ This space contains {{documents.length}} {{if (eq rootDocCount.length 1) 'document' 'documents'}} across {{categories.length}} {{if (eq categories.length 1) 'category' 'categories'}} +
{{else}}
-
-
+
+
{{focus-input id="folder-name" type="text" value=folderName class=(if hasNameError 'error-inline') placeholder="Name" autocomplete="off"}}
diff --git a/gui/app/templates/components/folder/space-view.hbs b/gui/app/templates/components/folder/space-view.hbs index c6cb3cf8..74401269 100644 --- a/gui/app/templates/components/folder/space-view.hbs +++ b/gui/app/templates/components/folder/space-view.hbs @@ -1,17 +1,36 @@ -{{folder/space-heading folder=folder permissions=permissions}} +
+ {{folder/space-heading folder=folder permissions=permissions documents=documents categories=categories}} -{{folder/space-toolbar folders=folders folder=folder - permissions=permissions hasSelectedDocuments=hasSelectedDocuments - onDeleteDocument=(action 'onDeleteDocument') onMoveDocument=(action 'onMoveDocument') - onDeleteSpace=(action 'onDeleteSpace') onStartDocument=(action 'onStartDocument')}} - -
+ {{folder/space-toolbar folders=folders folder=folder + permissions=permissions hasSelectedDocuments=hasSelectedDocuments + onDeleteDocument=(action 'onDeleteDocument') onMoveDocument=(action 'onMoveDocument') + onDeleteSpace=(action 'onDeleteSpace') onStartDocument=(action 'onStartDocument')}} +
+
{{#if showStartDocument}} {{folder/start-document folder=folder templates=templates permissions=permissions onImport=(action 'onImport') onHideStartDocument=(action 'onHideStartDocument')}} {{else}} - {{folder/documents-list documents=documents folders=folders folder=folder + + {{#if (gt categories.length 0)}} +
+ {{#if (gt rootDocCount 0)}} +
+ {{folder.name}} ({{rootDocCount}}) +
+ {{/if}} + {{#each categories as |cat index|}} +
+
+ {{cat.category}} ({{cat.docCount}}) +
+ {{/each}} +
+ {{/if}} + + {{folder/documents-list documents=filteredDocs folders=folders folder=folder templates=templates permissions=permissions selectedDocuments=(mut selectedDocuments) onImport=(action 'onImport')}} + {{/if}} \ No newline at end of file diff --git a/server/routing/routes.go b/server/routing/routes.go index efd08924..b058cec4 100644 --- a/server/routing/routes.go +++ b/server/routing/routes.go @@ -85,7 +85,6 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) { Add(rt, RoutePrefixPrivate, "import/folder/{folderID}", []string{"POST", "OPTIONS"}, nil, conversion.UploadConvert) - Add(rt, RoutePrefixPrivate, "documents", []string{"GET", "OPTIONS"}, []string{"filter", "tag"}, document.ByTag) Add(rt, RoutePrefixPrivate, "documents", []string{"GET", "OPTIONS"}, nil, document.BySpace) Add(rt, RoutePrefixPrivate, "documents/{documentID}", []string{"GET", "OPTIONS"}, nil, document.Get) Add(rt, RoutePrefixPrivate, "documents/{documentID}", []string{"PUT", "OPTIONS"}, nil, document.Update) @@ -136,6 +135,7 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) { Add(rt, RoutePrefixPrivate, "category/{categoryID}/permission", []string{"PUT", "OPTIONS"}, nil, permission.SetCategoryPermissions) Add(rt, RoutePrefixPrivate, "category/{categoryID}/permission", []string{"GET", "OPTIONS"}, nil, permission.GetCategoryPermissions) Add(rt, RoutePrefixPrivate, "category/{categoryID}/user", []string{"GET", "OPTIONS"}, nil, permission.GetCategoryViewers) + Add(rt, RoutePrefixPrivate, "category/member/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.GetSpaceCategoryMembers) Add(rt, RoutePrefixPrivate, "category/member", []string{"POST", "OPTIONS"}, nil, category.SetDocumentCategoryMembership) Add(rt, RoutePrefixPrivate, "users/{userID}/password", []string{"POST", "OPTIONS"}, nil, user.ChangePassword)