1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 05:09:42 +02:00

New user role: view analytics

This commit is contained in:
McMatts 2018-04-05 19:59:57 +01:00
parent b65a00dd1c
commit 19a916a4b4
13 changed files with 68 additions and 29 deletions

View file

@ -11,6 +11,10 @@ ALTER TABLE rolemember ENGINE = InnoDB;
-- content analytics -- content analytics
ALTER TABLE useractivity ADD COLUMN `metadata` VARCHAR(1000) NOT NULL DEFAULT '' AFTER `activitytype`; 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 -- content likes/feedback
-- DROP TABLE IF EXISTS `vote`; -- 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 -- DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
-- ENGINE = InnoDB; -- ENGINE = InnoDB;
-- CREATE INDEX idx_vote_1 ON vaote(orgid,documentid); -- CREATE INDEX idx_vote_1 ON vote(orgid,documentid);
-- deprecations -- deprecations

View file

@ -33,8 +33,8 @@ func (s Scope) Add(ctx domain.RequestContext, account account.Account) (err erro
account.Created = time.Now().UTC() account.Created = time.Now().UTC()
account.Revised = 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 (?, ?, ?, ?, ?, ?, ?, ?, ?)", _, 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.Active, account.Created, account.Revised) account.RefID, account.OrgID, account.UserID, account.Admin, account.Editor, account.Users, account.Analytics, account.Active, account.Created, account.Revised)
if err != nil { if err != nil {
err = errors.Wrap(err, "unable to execute insert for account") 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. // 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) { func (s Scope) GetUserAccount(ctx domain.RequestContext, userID string) (account account.Account, err error) {
err = s.Runtime.Db.Get(&account, ` 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 b.company, b.title, b.message, b.domain
FROM account a, organization b FROM account a, organization b
WHERE b.refid=a.orgid AND a.orgid=? AND a.userid=?`, ctx.OrgID, userID) 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. // 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) { func (s Scope) GetUserAccounts(ctx domain.RequestContext, userID string) (t []account.Account, err error) {
err = s.Runtime.Db.Select(&t, ` 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 b.company, b.title, b.message, b.domain
FROM account a, organization b FROM account a, organization b
WHERE a.userid=? AND a.orgid=b.refid AND a.active=1 ORDER BY b.title`, userID) 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. // 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) { func (s Scope) GetAccountsByOrg(ctx domain.RequestContext) (t []account.Account, err error) {
err = s.Runtime.Db.Select(&t, 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 b.company, b.title, b.message, b.domain
FROM account a, organization b FROM account a, organization b
WHERE a.orgid=b.refid AND a.orgid=? AND a.active=1`, ctx.OrgID) 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) { func (s Scope) UpdateAccount(ctx domain.RequestContext, account account.Account) (err error) {
account.Revised = time.Now().UTC() 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 { if err != sql.ErrNoRows && err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute update for account %s", account.RefID)) err = errors.Wrap(err, fmt.Sprintf("execute update for account %s", account.RefID))

View file

@ -29,6 +29,7 @@ type RequestContext struct {
Guest bool Guest bool
Editor bool Editor bool
Global bool Global bool
Analytics bool
UserID string UserID string
OrgID string OrgID string
OrgName string OrgName string

View file

@ -162,6 +162,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
a.Editor = true a.Editor = true
a.Admin = false a.Admin = false
a.Active = true a.Active = true
a.Analytics = false
err = h.Store.Account.Add(ctx, a) err = h.Store.Account.Add(ctx, a)
if err != nil { if err != nil {
@ -481,6 +482,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
a.Admin = u.Admin a.Admin = u.Admin
a.Active = u.Active a.Active = u.Active
a.Users = u.ViewUsers a.Users = u.ViewUsers
a.Analytics = u.Analytics
err = h.Store.Account.UpdateAccount(ctx, a) err = h.Store.Account.UpdateAccount(ctx, a)
if err != nil { if err != nil {
@ -799,6 +801,7 @@ func (h *Handler) BulkImport(w http.ResponseWriter, r *http.Request) {
a.Editor = true a.Editor = true
a.Admin = false a.Admin = false
a.Active = true a.Active = true
a.Analytics = false
err = h.Store.Account.Add(ctx, a) err = h.Store.Account.Add(ctx, a)
if err != nil { if err != nil {

View file

@ -114,7 +114,7 @@ func (s Scope) GetActiveUsersForOrganization(ctx domain.RequestContext) (u []use
err = s.Runtime.Db.Select(&u, 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, `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 FROM user u, account a
WHERE u.refid=a.userid AND a.orgid=? AND a.active=1 WHERE u.refid=a.userid AND a.orgid=? AND a.active=1
ORDER BY u.firstname,u.lastname`, 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, 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, `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 FROM user u, account a
WHERE u.refid=a.userid AND a.orgid=? `+likeQuery+ WHERE u.refid=a.userid AND a.orgid=? `+likeQuery+
`ORDER BY u.firstname, u.lastname LIMIT 100`, ctx.OrgID) `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, ` 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, 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 FROM user u, account a
WHERE a.orgid=? AND u.refid = a.userid AND a.active=1 AND u.refid IN ( 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 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(` 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, 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 FROM user u, account a
WHERE a.orgid=? AND u.refid = a.userid AND a.active=1 AND u.refid IN ( 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 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, 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, `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 FROM user u, account a
WHERE a.orgid=? AND u.refid=a.userid AND a.active=1 `+likeQuery+ WHERE a.orgid=? AND u.refid=a.userid AND a.active=1 `+likeQuery+
`ORDER BY u.firstname,u.lastname LIMIT `+strconv.Itoa(maxMatches), `ORDER BY u.firstname,u.lastname LIMIT `+strconv.Itoa(maxMatches),

View file

@ -40,6 +40,7 @@ func AttachUserAccounts(ctx domain.RequestContext, s domain.Store, orgID string,
u.Admin = false u.Admin = false
u.Active = false u.Active = false
u.ViewUsers = false u.ViewUsers = false
u.Analytics = false
for _, account := range u.Accounts { for _, account := range u.Accounts {
if account.OrgID == orgID { if account.OrgID == orgID {
@ -47,6 +48,7 @@ func AttachUserAccounts(ctx domain.RequestContext, s domain.Store, orgID string,
u.Editor = account.Editor u.Editor = account.Editor
u.Active = account.Active u.Active = account.Active
u.ViewUsers = account.Users u.ViewUsers = account.Users
u.Analytics = account.Analytics
break break
} }
} }

View file

@ -27,7 +27,7 @@ export default Component.extend(AuthProvider, ModalMixin, {
init() { init() {
this._super(...arguments); this._super(...arguments);
this.password = {}; this.password = {};
this.selectedUsers = []; this.selectedUsers = [];
}, },
didReceiveAttrs() { didReceiveAttrs() {
@ -91,6 +91,13 @@ export default Component.extend(AuthProvider, ModalMixin, {
cb(user); 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) { toggleUsers(id) {
let user = this.users.findBy("id", id); let user = this.users.findBy("id", id);
user.set('viewUsers', !user.get('viewUsers')); user.set('viewUsers', !user.get('viewUsers'));
@ -208,7 +215,7 @@ export default Component.extend(AuthProvider, ModalMixin, {
this.get('groupSvc').leave(groupId, userId).then(() => { this.get('groupSvc').leave(groupId, userId).then(() => {
this.filterUsers(); this.filterUsers();
}); });
}, },
onJoinGroup(groupId) { onJoinGroup(groupId) {
@ -222,7 +229,7 @@ export default Component.extend(AuthProvider, ModalMixin, {
this.get('groupSvc').join(groupId, userId).then(() => { this.get('groupSvc').join(groupId, userId).then(() => {
this.filterUsers(); this.filterUsers();
}); });
} }
} }
}); });

View file

@ -22,6 +22,7 @@ export default Model.extend({
editor: attr('boolean', { defaultValue: false }), editor: attr('boolean', { defaultValue: false }),
admin: attr('boolean', { defaultValue: false }), admin: attr('boolean', { defaultValue: false }),
viewUsers: attr('boolean', { defaultValue: false }), viewUsers: attr('boolean', { defaultValue: false }),
analytics: attr('boolean', { defaultValue: false }),
global: attr('boolean', { defaultValue: false }), global: attr('boolean', { defaultValue: false }),
accounts: attr(), accounts: attr(),
groups: attr(), groups: attr(),

View file

@ -386,6 +386,8 @@ export default Service.extend({
let userHasChangeAwaitingReview = false; let userHasChangeAwaitingReview = false;
let userHasChangeRejected = false; let userHasChangeRejected = false;
if (is.null(source) || is.undefined(source)) source = "";
return this.get('ajax').request(`fetch/page/${documentId}?source=${source}`, { return this.get('ajax').request(`fetch/page/${documentId}?source=${source}`, {
method: 'GET' method: 'GET'
}).then((response) => { }).then((response) => {

View file

@ -1,5 +1,5 @@
<div class="view-customize my-5"> <div class="view-customize my-5">
<div class="my-2"> <div class="my-2">
<span class="font-weight-bold">Spaces</span> <span class="font-weight-bold">Spaces</span>
<span class="text-muted">&nbsp;&nbsp;&mdash;&nbsp;can add spaces, both personal and shared with others</span> <span class="text-muted">&nbsp;&nbsp;&mdash;&nbsp;can add spaces, both personal and shared with others</span>
@ -12,6 +12,10 @@
<span class="font-weight-bold">Admin</span> <span class="font-weight-bold">Admin</span>
<span class="text-muted">&nbsp;&nbsp;&mdash;&nbsp;can manage all aspects of Documize, like this screen</span> <span class="text-muted">&nbsp;&nbsp;&mdash;&nbsp;can manage all aspects of Documize, like this screen</span>
</div> </div>
<div class="my-2">
<span class="font-weight-bold">Analytics</span>
<span class="text-muted">&nbsp;&nbsp;&mdash;&nbsp;can view analytical reports</span>
</div>
<div class="mt-2 mb-4"> <div class="mt-2 mb-4">
<span class="font-weight-bold">Active</span> <span class="font-weight-bold">Active</span>
<span class="text-muted">&nbsp;&nbsp;&mdash;&nbsp;can login and use Documize</span> <span class="text-muted">&nbsp;&nbsp;&mdash;&nbsp;can login and use Documize</span>
@ -28,11 +32,12 @@
<th class="text-muted"> <th class="text-muted">
{{#if hasSelectedUsers}} {{#if hasSelectedUsers}}
<button id="bulk-delete-users" type="button" class="btn btn-danger" data-toggle="modal" data-target="#admin-user-delete-modal" data-backdrop="static">Delete selected users</button> <button id="bulk-delete-users" type="button" class="btn btn-danger" data-toggle="modal" data-target="#admin-user-delete-modal" data-backdrop="static">Delete selected users</button>
{{/if}} {{/if}}
</th> </th>
<th class="no-width">Spaces</th> <th class="no-width">Spaces</th>
<th class="no-width">Visible</th> <th class="no-width">Visible</th>
<th class="no-width">Admin</th> <th class="no-width">Admin</th>
<th class="no-width">Analytics</th>
<th class="no-width">Active</th> <th class="no-width">Active</th>
<th class="no-width"> <th class="no-width">
</th> </th>
@ -87,6 +92,13 @@
<i class="material-icons checkbox" {{action 'toggleAdmin' user.id}}>check_box_outline_blank</i> <i class="material-icons checkbox" {{action 'toggleAdmin' user.id}}>check_box_outline_blank</i>
{{/if}} {{/if}}
</td> </td>
<td class="no-width text-center">
{{#if user.analytics}}
<i class="material-icons checkbox" {{action 'toggleAnalytics' user.id}}>check_box</i>
{{else}}
<i class="material-icons checkbox" {{action 'toggleAnalytics' user.id}}>check_box_outline_blank</i>
{{/if}}
</td>
<td class="no-width text-center"> <td class="no-width text-center">
{{#if user.me}} {{#if user.me}}
<i class="material-icons color-gray">check_box</i> <i class="material-icons color-gray">check_box</i>

View file

@ -16,14 +16,15 @@ import "github.com/documize/community/model"
// Account links a User to an Organization. // Account links a User to an Organization.
type Account struct { type Account struct {
model.BaseEntity model.BaseEntity
Admin bool `json:"admin"` Admin bool `json:"admin"`
Editor bool `json:"editor"` Editor bool `json:"editor"`
Users bool `json:"viewUsers"` // either view all users or just users in your space Users bool `json:"viewUsers"` // either view all users or just users in your space
UserID string `json:"userId"` Analytics bool `json:"analytics"` // view content analytics
OrgID string `json:"orgId"` UserID string `json:"userId"`
Company string `json:"company"` OrgID string `json:"orgId"`
Title string `json:"title"` Company string `json:"company"`
Message string `json:"message"` Title string `json:"title"`
Domain string `json:"domain"` Message string `json:"message"`
Active bool `json:"active"` Domain string `json:"domain"`
Active bool `json:"active"`
} }

View file

@ -30,6 +30,7 @@ type User struct {
Editor bool `json:"editor"` Editor bool `json:"editor"`
Admin bool `json:"admin"` Admin bool `json:"admin"`
ViewUsers bool `json:"viewUsers"` ViewUsers bool `json:"viewUsers"`
Analytics bool `json:"analytics"`
Global bool `json:"global"` Global bool `json:"global"`
Password string `json:"-"` Password string `json:"-"`
Salt string `json:"-"` Salt string `json:"-"`

View file

@ -141,6 +141,7 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http
rc.Administrator = false rc.Administrator = false
rc.Editor = false rc.Editor = false
rc.Global = false rc.Global = false
rc.Analytics = false
rc.AppURL = r.Host rc.AppURL = r.Host
rc.Subdomain = organization.GetSubdomainFromHost(r) rc.Subdomain = organization.GetSubdomainFromHost(r)
rc.SSL = r.TLS != nil 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.Administrator = u.Admin
rc.Editor = u.Editor rc.Editor = u.Editor
rc.Global = u.Global rc.Global = u.Global
rc.Analytics = u.Analytics
rc.Fullname = u.Fullname() rc.Fullname = u.Fullname()
// We send back with every HTTP request/response cycle the latest // 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"` Active bool `json:"active"`
Admin bool `json:"admin"` Admin bool `json:"admin"`
Editor bool `json:"editor"` Editor bool `json:"editor"`
Analytics bool `json:"analytics"`
ViewUsers bool `json:"viewUsers"` ViewUsers bool `json:"viewUsers"`
} }
state.Active = u.Active state.Active = u.Active
state.Admin = u.Admin state.Admin = u.Admin
state.Editor = u.Editor state.Editor = u.Editor
state.Analytics = u.Analytics
state.ViewUsers = u.ViewUsers state.ViewUsers = u.ViewUsers
sb, err := json.Marshal(state) sb, err := json.Marshal(state)
@ -234,6 +238,7 @@ func (m *middleware) preAuthorizeStaticAssets(rt *env.Runtime, r *http.Request)
ctx.OrgName = org.Title ctx.OrgName = org.Title
ctx.Administrator = false ctx.Administrator = false
ctx.Editor = false ctx.Editor = false
ctx.Analytics = false
ctx.Global = false ctx.Global = false
ctx.AppURL = r.Host ctx.AppURL = r.Host
ctx.SSL = r.TLS != nil ctx.SSL = r.TLS != nil