diff --git a/domain/category/mysql/store.go b/domain/category/mysql/store.go index aef7762a..fb3e9ed3 100644 --- a/domain/category/mysql/store.go +++ b/domain/category/mysql/store.go @@ -155,6 +155,26 @@ func (s Scope) RemoveCategoryMembership(ctx domain.RequestContext, categoryID st return b.DeleteWhere(ctx.Transaction, sql) } +// RemoveSpaceCategoryMemberships removes all category associations from the store for the space. +func (s Scope) RemoveSpaceCategoryMemberships(ctx domain.RequestContext, spaceID string) (rows int64, err error) { + b := mysql.BaseQuery{} + + sql := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND labelid='%s'", + ctx.OrgID, spaceID) + + return b.DeleteWhere(ctx.Transaction, sql) +} + +// RemoveDocumentCategories removes all document category associations from the store. +func (s Scope) RemoveDocumentCategories(ctx domain.RequestContext, documentID string) (rows int64, err error) { + b := mysql.BaseQuery{} + + sql := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND documentid='%s'", + ctx.OrgID, documentID) + + return b.DeleteWhere(ctx.Transaction, sql) +} + // DeleteBySpace removes all category and category associations for given space. func (s Scope) DeleteBySpace(ctx domain.RequestContext, spaceID string) (rows int64, err error) { b := mysql.BaseQuery{} diff --git a/domain/document/endpoint.go b/domain/document/endpoint.go index a03da564..5b8fccc2 100644 --- a/domain/document/endpoint.go +++ b/domain/document/endpoint.go @@ -206,11 +206,6 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { return } - // if !ctx.Editor { - // response.WriteForbiddenError(w) - // return - // } - if !permission.CanChangeDocument(ctx, *h.Store, documentID) { response.WriteForbiddenError(w) return @@ -241,6 +236,18 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { return } + // if space changed for document, remove document categories + oldDoc, err := h.Store.Document.Get(ctx, documentID) + if err != nil { + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + if oldDoc.LabelID != d.LabelID { + h.Store.Category.RemoveDocumentCategories(ctx, d.RefID) + } + err = h.Store.Document.Update(ctx, d) if err != nil { ctx.Transaction.Rollback() diff --git a/domain/space/endpoint.go b/domain/space/endpoint.go index fe6f1072..6cb2e8da 100644 --- a/domain/space/endpoint.go +++ b/domain/space/endpoint.go @@ -301,11 +301,11 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { } // GetAll returns spaces the user can see. -func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) { - method := "space.getAll" +func (h *Handler) GetViewable(w http.ResponseWriter, r *http.Request) { + method := "space.GetViewable" ctx := domain.GetRequestContext(r) - sp, err := h.Store.Space.GetAll(ctx) + sp, err := h.Store.Space.GetViewable(ctx) if err != nil && err != sql.ErrNoRows { response.WriteServerError(w, method, err) @@ -320,23 +320,31 @@ func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) { response.WriteJSON(w, sp) } -// GetSpaceViewers returns the users that can see the shared spaces. -func (h *Handler) GetSpaceViewers(w http.ResponseWriter, r *http.Request) { - method := "space.viewers" + +// GetAll returns every space for documize admin users to manage +func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) { + method := "space.getAll" ctx := domain.GetRequestContext(r) - v, err := h.Store.Space.Viewers(ctx) + if !ctx.Administrator { + response.WriteForbiddenError(w) + h.Runtime.Log.Info("rejected non-admin user request for all spaces") + return + } + + sp, err := h.Store.Space.GetAll(ctx) + if err != nil && err != sql.ErrNoRows { response.WriteServerError(w, method, err) h.Runtime.Log.Error(method, err) return } - if len(v) == 0 { - v = []space.Viewer{} + if len(sp) == 0 { + sp = []space.Space{} } - response.WriteJSON(w, v) + response.WriteJSON(w, sp) } // Update processes request to save space object to the database @@ -444,6 +452,14 @@ func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) { return } + _, err = h.Store.Category.RemoveSpaceCategoryMemberships(ctx, id) + if err != nil { + ctx.Transaction.Rollback() + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + _, err = h.Store.Space.Delete(ctx, id) if err != nil { ctx.Transaction.Rollback() @@ -811,4 +827,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/space/mysql/store.go b/domain/space/mysql/store.go index 946a64a1..859af755 100644 --- a/domain/space/mysql/store.go +++ b/domain/space/mysql/store.go @@ -69,9 +69,9 @@ func (s Scope) PublicSpaces(ctx domain.RequestContext, orgID string) (sp []space return } -// GetAll returns spaces that the user can see. +// GetViewable returns spaces that the user can see. // Also handles which spaces can be seen by anonymous users. -func (s Scope) GetAll(ctx domain.RequestContext) (sp []space.Space, err error) { +func (s Scope) GetViewable(ctx domain.RequestContext) (sp []space.Space, err error) { sql := ` SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label WHERE orgid=? @@ -90,6 +90,22 @@ func (s Scope) GetAll(ctx domain.RequestContext) (sp []space.Space, err error) { ctx.OrgID, ctx.UserID) + if err != nil { + err = errors.Wrap(err, fmt.Sprintf("failed space.GetViewable org %s", ctx.OrgID)) + } + + return +} + +// GetAll for admin users! +func (s Scope) GetAll(ctx domain.RequestContext) (sp []space.Space, err error) { + sql := ` + SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label + WHERE orgid=? + ORDER BY name` + + err = s.Runtime.Db.Select(&sp, sql, ctx.OrgID) + if err != nil { err = errors.Wrap(err, fmt.Sprintf("failed space.GetAll org %s", ctx.OrgID)) } @@ -110,28 +126,6 @@ func (s Scope) Update(ctx domain.RequestContext, sp space.Space) (err error) { return } -// Viewers returns the list of people who can see shared spaces. -func (s Scope) Viewers(ctx domain.RequestContext) (v []space.Viewer, err error) { - sql := ` - SELECT a.userid, - COALESCE(u.firstname, '') as firstname, - COALESCE(u.lastname, '') as lastname, - COALESCE(u.email, '') as email, - a.labelid, - b.label as name, - b.type - FROM labelrole a - LEFT JOIN label b ON b.refid=a.labelid - LEFT JOIN user u ON u.refid=a.userid - WHERE a.orgid=? AND b.type != 2 - GROUP BY a.labelid,a.userid - ORDER BY u.firstname,u.lastname` - - err = s.Runtime.Db.Select(&v, sql, ctx.OrgID) - - return -} - // Delete removes space from the store. func (s Scope) Delete(ctx domain.RequestContext, id string) (rows int64, err error) { b := mysql.BaseQuery{} diff --git a/domain/space/space_test.go b/domain/space/space_test.go index 5f5a2cb2..9e5c8f89 100644 --- a/domain/space/space_test.go +++ b/domain/space/space_test.go @@ -163,14 +163,6 @@ func TestSpace(t *testing.T) { } }) - t.Run("Viewers", func(t *testing.T) { - viewers, err := s.Space.Viewers(ctx) - if err != nil || viewers == nil { - t.Error("failed to get viewers") - return - } - }) - t.Run("Add Role", func(t *testing.T) { ctx.Transaction, err = rt.Db.Beginx() diff --git a/domain/storer.go b/domain/storer.go index b617ebde..b59d8591 100644 --- a/domain/storer.go +++ b/domain/storer.go @@ -55,9 +55,9 @@ type SpaceStorer interface { Add(ctx RequestContext, sp space.Space) (err error) Get(ctx RequestContext, id string) (sp space.Space, err error) PublicSpaces(ctx RequestContext, orgID string) (sp []space.Space, err error) + GetViewable(ctx RequestContext) (sp []space.Space, err error) GetAll(ctx RequestContext) (sp []space.Space, err error) Update(ctx RequestContext, sp space.Space) (err error) - Viewers(ctx RequestContext) (v []space.Viewer, err error) Delete(ctx RequestContext, id string) (rows int64, err error) } @@ -76,6 +76,8 @@ type CategoryStorer interface { 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) + RemoveDocumentCategories(ctx RequestContext, documentID string) (rows int64, err error) + RemoveSpaceCategoryMemberships(ctx RequestContext, spaceID string) (rows int64, err error) } // PermissionStorer defines required methods for space/document permission management diff --git a/gui/app/pods/customize/folders/route.js b/gui/app/pods/customize/folders/route.js index 261e6d71..605f089b 100644 --- a/gui/app/pods/customize/folders/route.js +++ b/gui/app/pods/customize/folders/route.js @@ -22,7 +22,7 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, { }, model() { - return this.get('folderService').getAll(); + return this.get('folderService').adminList(); }, setupController(controller, model) { @@ -30,33 +30,12 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, { if (is.empty(nonPrivateFolders) || is.null(model) || is.undefined(model)) { nonPrivateFolders = []; } + controller.set('folders', nonPrivateFolders); - this.get('folderService').getProtectedFolderInfo().then((people) => { - people.forEach((person) => { - person.set('isEveryone', person.get('userId') === ''); - person.set('isOwner', false); - }); - - nonPrivateFolders.forEach(function (folder) { - let shared = people.filterBy('folderId', folder.get('id')); - let person = shared.findBy('userId', folder.get('userId')); - if (is.not.undefined(person)) { - person.set('isOwner', true); - } - - folder.set('sharedWith', shared); - }); - }); }, activate() { document.title = "Spaces | Documize"; - }, - - actions: { - onChangeOwner() { - this.refresh(); - } } }); diff --git a/gui/app/pods/customize/folders/template.hbs b/gui/app/pods/customize/folders/template.hbs index 2667827f..8a72de7d 100644 --- a/gui/app/pods/customize/folders/template.hbs +++ b/gui/app/pods/customize/folders/template.hbs @@ -4,40 +4,16 @@
{{folders.length}} shared {{label}}
View and change shared space ownership
-
- - - - - - - - - {{#each folders as |folder|}} - - - - - {{/each}} - -
SpaceParticipants
- {{#link-to 'folder' folder.id folder.slug class="alt"}}{{folder.name}}{{/link-to}} - - {{#each folder.sharedWith as |person|}} - {{#if person.isEveryone}} - Everyone - {{else}} - - {{#if person.isOwner}} - {{person.firstname}} {{person.lastname}} (owner) - {{else}} - {{person.firstname}} {{person.lastname}} - make owner - {{/if}} - {{/if}} -
- {{/each}} -
+
+ {{#each folders as |folder|}} +
+ {{#link-to 'folder' folder.id folder.slug class="alt"}}{{folder.name}}{{/link-to}} +
+
+   +
+
+ {{/each}}
{{else}} diff --git a/gui/app/services/folder.js b/gui/app/services/folder.js index 24331f5e..3705baad 100644 --- a/gui/app/services/folder.js +++ b/gui/app/services/folder.js @@ -96,22 +96,6 @@ export default BaseService.extend({ }); }, - // getProtectedFolderInfo returns non-private folders and who has access to them. - getProtectedFolderInfo() { - return this.get('ajax').request(`space?filter=viewers`, { - method: "GET" - }).then((response) => { - let data = []; - - data = response.map((obj) => { - let data = this.get('store').normalize('protected-folder-participant', obj); - return this.get('store').push(data); - }); - - return data; - }); - }, - // reloads and caches folders. reload() { return this.get('ajax').request(`space`, { @@ -185,4 +169,20 @@ export default BaseService.extend({ return data2; }); }, + + // returns all spaces -- for use by documize admin user + adminList() { + return this.get('ajax').request(`space/manage`, { + method: "GET" + }).then((response) => { + let data = []; + + data = response.map((obj) => { + let data = this.get('store').normalize('folder', obj); + return this.get('store').push(data); + }); + + return data; + }); + } }); diff --git a/gui/app/styles/view/page-customize.scss b/gui/app/styles/view/page-customize.scss index 1964fd94..508f43eb 100644 --- a/gui/app/styles/view/page-customize.scss +++ b/gui/app/styles/view/page-customize.scss @@ -89,4 +89,10 @@ .edit-user-dialog, .delete-user-dialog { display: none; } + + .manage-space-list { + > .space { + margin: 10px 0; + } + } } diff --git a/server/routing/routes.go b/server/routing/routes.go index b058cec4..51e8e4f6 100644 --- a/server/routing/routes.go +++ b/server/routing/routes.go @@ -119,11 +119,11 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) { Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions/user", []string{"GET", "OPTIONS"}, nil, permission.GetUserSpacePermissions) Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions", []string{"GET", "OPTIONS"}, nil, permission.GetSpacePermissions) Add(rt, RoutePrefixPrivate, "space/{spaceID}/invitation", []string{"POST", "OPTIONS"}, nil, space.Invite) - Add(rt, RoutePrefixPrivate, "space", []string{"GET", "OPTIONS"}, []string{"filter", "viewers"}, space.GetSpaceViewers) - Add(rt, RoutePrefixPrivate, "space", []string{"POST", "OPTIONS"}, nil, space.Add) - Add(rt, RoutePrefixPrivate, "space", []string{"GET", "OPTIONS"}, nil, space.GetAll) + Add(rt, RoutePrefixPrivate, "space/manage", []string{"GET", "OPTIONS"}, nil, space.GetAll) Add(rt, RoutePrefixPrivate, "space/{spaceID}", []string{"GET", "OPTIONS"}, nil, space.Get) + Add(rt, RoutePrefixPrivate, "space", []string{"GET", "OPTIONS"}, nil, space.GetViewable) Add(rt, RoutePrefixPrivate, "space/{spaceID}", []string{"PUT", "OPTIONS"}, nil, space.Update) + Add(rt, RoutePrefixPrivate, "space", []string{"POST", "OPTIONS"}, nil, space.Add) Add(rt, RoutePrefixPrivate, "category/document/{documentID}", []string{"GET", "OPTIONS"}, nil, category.GetDocumentCategoryMembership) Add(rt, RoutePrefixPrivate, "category/space/{spaceID}", []string{"GET", "OPTIONS"}, []string{"filter", "all"}, category.GetAll)