From 19a916a4b4707dd269757b7afdb7c647fa831806 Mon Sep 17 00:00:00 2001 From: McMatts Date: Thu, 5 Apr 2018 19:59:57 +0100 Subject: [PATCH] New user role: view analytics --- core/database/scripts/autobuild/db_00020.sql | 6 +++++- domain/account/mysql/store.go | 14 ++++++------- domain/context.go | 1 + domain/user/endpoint.go | 3 +++ domain/user/mysql/store.go | 10 ++++----- domain/user/user.go | 2 ++ gui/app/components/customize/user-list.js | 15 +++++++++---- gui/app/models/user.js | 1 + gui/app/services/document.js | 2 ++ .../components/customize/user-list.hbs | 16 ++++++++++++-- model/account/account.go | 21 ++++++++++--------- model/user/user.go | 1 + server/middleware.go | 5 +++++ 13 files changed, 68 insertions(+), 29 deletions(-) diff --git a/core/database/scripts/autobuild/db_00020.sql b/core/database/scripts/autobuild/db_00020.sql index 18a5ba8e..0e6697e9 100644 --- a/core/database/scripts/autobuild/db_00020.sql +++ b/core/database/scripts/autobuild/db_00020.sql @@ -11,6 +11,10 @@ ALTER TABLE rolemember ENGINE = InnoDB; -- content analytics ALTER TABLE useractivity ADD COLUMN `metadata` VARCHAR(1000) NOT NULL DEFAULT '' AFTER `activitytype`; +-- new role for viewing content analytics +ALTER TABLE account ADD COLUMN `analytics` BOOL NOT NULL DEFAULT 0 AFTER `users`; +UPDATE account SET analytics=1 WHERE admin=1; + -- content likes/feedback -- DROP TABLE IF EXISTS `vote`; @@ -31,6 +35,6 @@ ALTER TABLE useractivity ADD COLUMN `metadata` VARCHAR(1000) NOT NULL DEFAULT '' -- DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci -- ENGINE = InnoDB; --- CREATE INDEX idx_vote_1 ON vaote(orgid,documentid); +-- CREATE INDEX idx_vote_1 ON vote(orgid,documentid); -- deprecations diff --git a/domain/account/mysql/store.go b/domain/account/mysql/store.go index ead299c8..469540ab 100644 --- a/domain/account/mysql/store.go +++ b/domain/account/mysql/store.go @@ -33,8 +33,8 @@ func (s Scope) Add(ctx domain.RequestContext, account account.Account) (err erro account.Created = time.Now().UTC() account.Revised = time.Now().UTC() - _, err = ctx.Transaction.Exec("INSERT INTO account (refid, orgid, userid, admin, editor, users, active, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", - account.RefID, account.OrgID, account.UserID, account.Admin, account.Editor, account.Users, account.Active, account.Created, account.Revised) + _, err = ctx.Transaction.Exec("INSERT INTO account (refid, orgid, userid, admin, editor, users, analytics, active, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + account.RefID, account.OrgID, account.UserID, account.Admin, account.Editor, account.Users, account.Analytics, account.Active, account.Created, account.Revised) if err != nil { err = errors.Wrap(err, "unable to execute insert for account") @@ -46,7 +46,7 @@ func (s Scope) Add(ctx domain.RequestContext, account account.Account) (err erro // GetUserAccount returns the database account record corresponding to the given userID, using the client's current organizaion. func (s Scope) GetUserAccount(ctx domain.RequestContext, userID string) (account account.Account, err error) { err = s.Runtime.Db.Get(&account, ` - SELECT a.id, a.refid, a.orgid, a.userid, a.editor, a.admin, a.users, a.active, a.created, a.revised, + SELECT a.id, a.refid, a.orgid, a.userid, a.editor, a.admin, a.users, a.analytics, a.active, a.created, a.revised, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE b.refid=a.orgid AND a.orgid=? AND a.userid=?`, ctx.OrgID, userID) @@ -61,8 +61,8 @@ func (s Scope) GetUserAccount(ctx domain.RequestContext, userID string) (account // GetUserAccounts returns a slice of database account records, for all organizations that the userID is a member of, in organization title order. func (s Scope) GetUserAccounts(ctx domain.RequestContext, userID string) (t []account.Account, err error) { err = s.Runtime.Db.Select(&t, ` - SELECT a.id, a.refid, a.orgid, a.userid, a.editor, a.admin, a.users, a.active, a.created, a.revised, - b.company, b.title, b.message, b.domain + SELECT a.id, a.refid, a.orgid, a.userid, a.editor, a.admin, a.users, a.analytics, a.active, a.created, a.revised, + b.company, b.title, b.message, b.domain FROM account a, organization b WHERE a.userid=? AND a.orgid=b.refid AND a.active=1 ORDER BY b.title`, userID) @@ -76,7 +76,7 @@ func (s Scope) GetUserAccounts(ctx domain.RequestContext, userID string) (t []ac // GetAccountsByOrg returns a slice of database account records, for all users in the client's organization. func (s Scope) GetAccountsByOrg(ctx domain.RequestContext) (t []account.Account, err error) { err = s.Runtime.Db.Select(&t, - `SELECT a.id, a.refid, a.orgid, a.userid, a.editor, a.admin, a.users, a.active, a.created, a.revised, + `SELECT a.id, a.refid, a.orgid, a.userid, a.editor, a.admin, a.users, a.analytics, a.active, a.created, a.revised, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE a.orgid=b.refid AND a.orgid=? AND a.active=1`, ctx.OrgID) @@ -109,7 +109,7 @@ func (s Scope) CountOrgAccounts(ctx domain.RequestContext) (c int) { func (s Scope) UpdateAccount(ctx domain.RequestContext, account account.Account) (err error) { account.Revised = time.Now().UTC() - _, err = ctx.Transaction.NamedExec("UPDATE account SET userid=:userid, admin=:admin, editor=:editor, users=:users, active=:active, revised=:revised WHERE orgid=:orgid AND refid=:refid", &account) + _, err = ctx.Transaction.NamedExec("UPDATE account SET userid=:userid, admin=:admin, editor=:editor, users=:users, analytics=:analytics, active=:active, revised=:revised WHERE orgid=:orgid AND refid=:refid", &account) if err != sql.ErrNoRows && err != nil { err = errors.Wrap(err, fmt.Sprintf("execute update for account %s", account.RefID)) diff --git a/domain/context.go b/domain/context.go index 04473457..967a7a2b 100644 --- a/domain/context.go +++ b/domain/context.go @@ -29,6 +29,7 @@ type RequestContext struct { Guest bool Editor bool Global bool + Analytics bool UserID string OrgID string OrgName string diff --git a/domain/user/endpoint.go b/domain/user/endpoint.go index 465a540a..daaf087c 100644 --- a/domain/user/endpoint.go +++ b/domain/user/endpoint.go @@ -162,6 +162,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) { a.Editor = true a.Admin = false a.Active = true + a.Analytics = false err = h.Store.Account.Add(ctx, a) if err != nil { @@ -481,6 +482,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { a.Admin = u.Admin a.Active = u.Active a.Users = u.ViewUsers + a.Analytics = u.Analytics err = h.Store.Account.UpdateAccount(ctx, a) if err != nil { @@ -799,6 +801,7 @@ func (h *Handler) BulkImport(w http.ResponseWriter, r *http.Request) { a.Editor = true a.Admin = false a.Active = true + a.Analytics = false err = h.Store.Account.Add(ctx, a) if err != nil { diff --git a/domain/user/mysql/store.go b/domain/user/mysql/store.go index 41f6df07..691cb6a9 100644 --- a/domain/user/mysql/store.go +++ b/domain/user/mysql/store.go @@ -114,7 +114,7 @@ func (s Scope) GetActiveUsersForOrganization(ctx domain.RequestContext) (u []use 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.lastversion, u.created, u.revised, - u.global, a.active, a.editor, a.admin, a.users as viewusers + u.global, a.active, a.editor, a.admin, a.users as viewusers, a.analytics FROM user u, account a WHERE u.refid=a.userid AND a.orgid=? AND a.active=1 ORDER BY u.firstname,u.lastname`, @@ -143,7 +143,7 @@ func (s Scope) GetUsersForOrganization(ctx domain.RequestContext, filter string) 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.lastversion, u.created, u.revised, - u.global, a.active, a.editor, a.admin, a.users as viewusers + u.global, a.active, a.editor, a.admin, a.users as viewusers, a.analytics FROM user u, account a WHERE u.refid=a.userid AND a.orgid=? `+likeQuery+ `ORDER BY u.firstname, u.lastname LIMIT 100`, ctx.OrgID) @@ -165,7 +165,7 @@ func (s Scope) GetSpaceUsers(ctx domain.RequestContext, spaceID string) (u []use 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.lastversion, u.revised, u.global, - a.active, a.users AS viewusers, a.editor, a.admin + a.active, a.users AS viewusers, a.editor, a.admin, a.analytics 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 scope='object' AND location='space' AND refid=? UNION ALL @@ -194,7 +194,7 @@ func (s Scope) GetUsersForSpaces(ctx domain.RequestContext, spaces []string) (u query, args, err := sqlx.In(` SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.lastversion, u.created, u.revised, u.global, - a.active, a.users AS viewusers, a.editor, a.admin + a.active, a.users AS viewusers, a.editor, a.admin, a.analytics 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 scope='object' AND location='space' AND refid IN(?) UNION ALL @@ -295,7 +295,7 @@ func (s Scope) MatchUsers(ctx domain.RequestContext, text string, maxMatches int 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.lastversion, u.created, u.revised, - u.global, a.active, a.editor, a.admin, a.users as viewusers + u.global, a.active, a.editor, a.admin, a.users as viewusers, a.analytics FROM user u, account a WHERE a.orgid=? AND u.refid=a.userid AND a.active=1 `+likeQuery+ `ORDER BY u.firstname,u.lastname LIMIT `+strconv.Itoa(maxMatches), diff --git a/domain/user/user.go b/domain/user/user.go index 9bc938a9..aaebe442 100644 --- a/domain/user/user.go +++ b/domain/user/user.go @@ -40,6 +40,7 @@ func AttachUserAccounts(ctx domain.RequestContext, s domain.Store, orgID string, u.Admin = false u.Active = false u.ViewUsers = false + u.Analytics = false for _, account := range u.Accounts { if account.OrgID == orgID { @@ -47,6 +48,7 @@ func AttachUserAccounts(ctx domain.RequestContext, s domain.Store, orgID string, u.Editor = account.Editor u.Active = account.Active u.ViewUsers = account.Users + u.Analytics = account.Analytics break } } diff --git a/gui/app/components/customize/user-list.js b/gui/app/components/customize/user-list.js index b1a53f12..94b336cd 100644 --- a/gui/app/components/customize/user-list.js +++ b/gui/app/components/customize/user-list.js @@ -27,7 +27,7 @@ export default Component.extend(AuthProvider, ModalMixin, { init() { this._super(...arguments); this.password = {}; - this.selectedUsers = []; + this.selectedUsers = []; }, didReceiveAttrs() { @@ -91,6 +91,13 @@ export default Component.extend(AuthProvider, ModalMixin, { cb(user); }, + toggleAnalytics(id) { + let user = this.users.findBy("id", id); + user.set('analytics', !user.get('analytics')); + let cb = this.get('onSave'); + cb(user); + }, + toggleUsers(id) { let user = this.users.findBy("id", id); user.set('viewUsers', !user.get('viewUsers')); @@ -208,7 +215,7 @@ export default Component.extend(AuthProvider, ModalMixin, { this.get('groupSvc').leave(groupId, userId).then(() => { this.filterUsers(); - }); + }); }, onJoinGroup(groupId) { @@ -222,7 +229,7 @@ export default Component.extend(AuthProvider, ModalMixin, { this.get('groupSvc').join(groupId, userId).then(() => { this.filterUsers(); - }); - } + }); + } } }); diff --git a/gui/app/models/user.js b/gui/app/models/user.js index e4b4d8ac..ff56d7e9 100644 --- a/gui/app/models/user.js +++ b/gui/app/models/user.js @@ -22,6 +22,7 @@ export default Model.extend({ editor: attr('boolean', { defaultValue: false }), admin: attr('boolean', { defaultValue: false }), viewUsers: attr('boolean', { defaultValue: false }), + analytics: attr('boolean', { defaultValue: false }), global: attr('boolean', { defaultValue: false }), accounts: attr(), groups: attr(), diff --git a/gui/app/services/document.js b/gui/app/services/document.js index fc7bc826..cddcc2f2 100644 --- a/gui/app/services/document.js +++ b/gui/app/services/document.js @@ -386,6 +386,8 @@ export default Service.extend({ let userHasChangeAwaitingReview = false; let userHasChangeRejected = false; + if (is.null(source) || is.undefined(source)) source = ""; + return this.get('ajax').request(`fetch/page/${documentId}?source=${source}`, { method: 'GET' }).then((response) => { diff --git a/gui/app/templates/components/customize/user-list.hbs b/gui/app/templates/components/customize/user-list.hbs index cc796be2..9d9b65d6 100644 --- a/gui/app/templates/components/customize/user-list.hbs +++ b/gui/app/templates/components/customize/user-list.hbs @@ -1,5 +1,5 @@
- +
Spaces   — can add spaces, both personal and shared with others @@ -12,6 +12,10 @@ Admin   — can manage all aspects of Documize, like this screen
+
+ Analytics +   — can view analytical reports +
Active   — can login and use Documize @@ -28,11 +32,12 @@ {{#if hasSelectedUsers}} - {{/if}} + {{/if}} Spaces Visible Admin + Analytics Active @@ -87,6 +92,13 @@ check_box_outline_blank {{/if}} + + {{#if user.analytics}} + check_box + {{else}} + check_box_outline_blank + {{/if}} + {{#if user.me}} check_box diff --git a/model/account/account.go b/model/account/account.go index 381bf7b0..5a6f855f 100644 --- a/model/account/account.go +++ b/model/account/account.go @@ -16,14 +16,15 @@ import "github.com/documize/community/model" // Account links a User to an Organization. type Account struct { model.BaseEntity - Admin bool `json:"admin"` - Editor bool `json:"editor"` - Users bool `json:"viewUsers"` // either view all users or just users in your space - UserID string `json:"userId"` - OrgID string `json:"orgId"` - Company string `json:"company"` - Title string `json:"title"` - Message string `json:"message"` - Domain string `json:"domain"` - Active bool `json:"active"` + Admin bool `json:"admin"` + Editor bool `json:"editor"` + Users bool `json:"viewUsers"` // either view all users or just users in your space + Analytics bool `json:"analytics"` // view content analytics + UserID string `json:"userId"` + OrgID string `json:"orgId"` + Company string `json:"company"` + Title string `json:"title"` + Message string `json:"message"` + Domain string `json:"domain"` + Active bool `json:"active"` } diff --git a/model/user/user.go b/model/user/user.go index 20879666..952d0a33 100644 --- a/model/user/user.go +++ b/model/user/user.go @@ -30,6 +30,7 @@ type User struct { Editor bool `json:"editor"` Admin bool `json:"admin"` ViewUsers bool `json:"viewUsers"` + Analytics bool `json:"analytics"` Global bool `json:"global"` Password string `json:"-"` Salt string `json:"-"` diff --git a/server/middleware.go b/server/middleware.go index 4b65c9a9..818cab74 100644 --- a/server/middleware.go +++ b/server/middleware.go @@ -141,6 +141,7 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http rc.Administrator = false rc.Editor = false rc.Global = false + rc.Analytics = false rc.AppURL = r.Host rc.Subdomain = organization.GetSubdomainFromHost(r) rc.SSL = r.TLS != nil @@ -170,6 +171,7 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http rc.Administrator = u.Admin rc.Editor = u.Editor rc.Global = u.Global + rc.Analytics = u.Analytics rc.Fullname = u.Fullname() // We send back with every HTTP request/response cycle the latest @@ -179,12 +181,14 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http Active bool `json:"active"` Admin bool `json:"admin"` Editor bool `json:"editor"` + Analytics bool `json:"analytics"` ViewUsers bool `json:"viewUsers"` } state.Active = u.Active state.Admin = u.Admin state.Editor = u.Editor + state.Analytics = u.Analytics state.ViewUsers = u.ViewUsers sb, err := json.Marshal(state) @@ -234,6 +238,7 @@ func (m *middleware) preAuthorizeStaticAssets(rt *env.Runtime, r *http.Request) ctx.OrgName = org.Title ctx.Administrator = false ctx.Editor = false + ctx.Analytics = false ctx.Global = false ctx.AppURL = r.Host ctx.SSL = r.TLS != nil