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

Merge pull request #81 from documize/user-activity-logging

User activity logging
This commit is contained in:
Harvey Kandola 2017-02-21 13:03:26 -08:00 committed by GitHub
commit 22a63f7deb
30 changed files with 5603 additions and 4332 deletions

View file

@ -8,7 +8,7 @@ The mission is to bring software dev inspired features (refactoring, testing, li
## Latest version ## Latest version
v0.41.0 v0.42.0
## OS Support ## OS Support
@ -19,7 +19,7 @@ v0.41.0
## Tech stack ## Tech stack
- EmberJS (v2.10.0) - EmberJS (v2.10.0)
- Go (v1.7.3) - Go (v1.8)
- MySQL (v5.7.10+) or Percona (v5.7.16-10+) - MySQL (v5.7.10+) or Percona (v5.7.16-10+)
## Documentation ## Documentation

View file

@ -0,0 +1,32 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. 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 <sales@documize.com>.
//
// https://documize.com
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import Ember from 'ember';
export default Model.extend({
documentName: attr('string'),
folderId: attr('string'),
contributed: attr('string'),
viewed: attr('string'),
created: attr('string'),
hasContributed: Ember.computed('contributed', function () {
return this.get('contributed').length > 0;
}),
hasViewed: Ember.computed('viewed', function () {
return this.get('viewed').length > 0;
}),
hasCreated: Ember.computed('created', function () {
return this.get('created').length > 0;
})
});

View file

@ -32,7 +32,6 @@ $color-toast: #4c4c4c;
$color-checkbox: #2667af; $color-checkbox: #2667af;
$color-card-active: #f7fcff; $color-card-active: #f7fcff;
$color-chip: #98A2AB;
$color-chip: #dff0f9; $color-chip: #dff0f9;
$color-chip-border: #daeaf3; $color-chip-border: #daeaf3;
$color-chip-text: #1b88e3; $color-chip-text: #1b88e3;
@ -40,43 +39,36 @@ $color-chip-text: #1b88e3;
$color-symbol-box: #dce5e8; $color-symbol-box: #dce5e8;
$color-symbol-icon: #a4b8be; $color-symbol-icon: #a4b8be;
$color-table-border: #e1e1e1;
$color-table-header: #f5f5f5;
.color-white { .color-white {
color: $color-white !important; color: $color-white !important;
} }
.color-off-white { .color-off-white {
color: $color-off-white !important; color: $color-off-white !important;
} }
.color-black { .color-black {
color: $color-black !important; color: $color-black !important;
} }
.color-off-black { .color-off-black {
color: $color-off-black !important; color: $color-off-black !important;
} }
.color-primary { .color-primary {
color: $color-primary !important; color: $color-primary !important;
} }
.color-link { .color-link {
color: $color-link !important; color: $color-link !important;
} }
.color-blue { .color-blue {
color: $color-blue !important; color: $color-blue !important;
} }
.color-red { .color-red {
color: $color-red !important; color: $color-red !important;
} }
.color-green { .color-green {
color: $color-green !important; color: $color-green !important;
} }
.color-gray { .color-gray {
color: $color-gray !important; color: $color-gray !important;
} }
@ -84,7 +76,9 @@ $color-symbol-icon: #a4b8be;
.background-color-white { .background-color-white {
background-color: $color-white !important; background-color: $color-white !important;
} }
.background-color-primary { .background-color-primary {
background-color: $color-primary !important; background-color: $color-primary !important;
} }
.background-color-green {
background-color: $color-green !important;
}

View file

@ -97,6 +97,10 @@
} }
} }
> .right {
text-align: right;
}
> li.danger { > li.danger {
color: $color-red; color: $color-red;

View file

@ -1,6 +1,6 @@
{ {
"name": "documize", "name": "documize",
"version": "0.41.0", "version": "0.42.0",
"description": "The Document IDE", "description": "The Document IDE",
"private": true, "private": true,
"repository": "", "repository": "",

6778
app/vendor/moment.js vendored

File diff suppressed because it is too large Load diff

View file

@ -62,19 +62,15 @@ func uploadDocument(w http.ResponseWriter, r *http.Request) (string, string, str
} }
// generate job id // generate job id
var job = "some-uuid"
newUUID, err := uuid.NewV4() newUUID, err := uuid.NewV4()
if err != nil { if err != nil {
writeServerError(w, method, err) writeServerError(w, method, err)
return "", "", "" return "", "", ""
} }
job = newUUID.String() job := newUUID.String()
err = storageProvider.Upload(job, filename.Filename, b.Bytes()) err = storageProvider.Upload(job, filename.Filename, b.Bytes())
if err != nil { if err != nil {
writeServerError(w, method, err) writeServerError(w, method, err)
return "", "", "" return "", "", ""
@ -229,10 +225,7 @@ func processDocument(p request.Persister, filename, job, folderID string, fileRe
return return
} }
// New code from normal conversion code
tx, err = request.Db.Beginx() tx, err = request.Db.Beginx()
if err != nil { if err != nil {
log.Error("Cannot begin a transatcion", err) log.Error("Cannot begin a transatcion", err)
return return
@ -248,9 +241,13 @@ func processDocument(p request.Persister, filename, job, folderID string, fileRe
return return
} }
log.IfErr(tx.Commit()) p.RecordUserActivity(entity.UserActivity{
LabelID: newDocument.LabelID,
SourceID: newDocument.RefID,
SourceType: entity.ActivitySourceTypeDocument,
ActivityType: entity.ActivityTypeCreated})
// End new code log.IfErr(tx.Commit())
return return
} }

View file

@ -98,12 +98,31 @@ func GetDocument(w http.ResponseWriter, r *http.Request) {
} }
json, err := json.Marshal(document) json, err := json.Marshal(document)
if err != nil { if err != nil {
writeJSONMarshalError(w, method, "document", err) writeJSONMarshalError(w, method, "document", err)
return return
} }
p.Context.Transaction, err = request.Db.Beginx()
if err != nil {
writeTransactionError(w, method, err)
return
}
err = p.RecordUserActivity(entity.UserActivity{
LabelID: document.LabelID,
SourceID: document.RefID,
SourceType: entity.ActivitySourceTypeDocument,
ActivityType: entity.ActivityTypeRead})
if err != nil {
log.IfErr(p.Context.Transaction.Rollback())
log.Error("Cannot record user activity", err)
return
}
log.IfErr(p.Context.Transaction.Commit())
writeSuccessBytes(w, json) writeSuccessBytes(w, json)
} }
@ -259,8 +278,13 @@ func DeleteDocument(w http.ResponseWriter, r *http.Request) {
return return
} }
tx, err := request.Db.Beginx() doc, err := p.GetDocument(documentID)
if err != nil {
writeGeneralSQLError(w, method, err)
return
}
tx, err := request.Db.Beginx()
if err != nil { if err != nil {
writeTransactionError(w, method, err) writeTransactionError(w, method, err)
return return
@ -284,6 +308,12 @@ func DeleteDocument(w http.ResponseWriter, r *http.Request) {
return return
} }
_ = p.RecordUserActivity(entity.UserActivity{
LabelID: doc.LabelID,
SourceID: documentID,
SourceType: entity.ActivitySourceTypeDocument,
ActivityType: entity.ActivityTypeDeleted})
log.IfErr(tx.Commit()) log.IfErr(tx.Commit())
writeSuccessEmptyJSON(w) writeSuccessEmptyJSON(w)
@ -404,7 +434,6 @@ func UpdateDocument(w http.ResponseWriter, r *http.Request) {
d.RefID = documentID d.RefID = documentID
tx, err := request.Db.Beginx() tx, err := request.Db.Beginx()
if err != nil { if err != nil {
writeTransactionError(w, method, err) writeTransactionError(w, method, err)
return return
@ -413,7 +442,6 @@ func UpdateDocument(w http.ResponseWriter, r *http.Request) {
p.Context.Transaction = tx p.Context.Transaction = tx
err = p.UpdateDocument(d) err = p.UpdateDocument(d)
if err != nil { if err != nil {
log.IfErr(tx.Rollback()) log.IfErr(tx.Rollback())
writeGeneralSQLError(w, method, err) writeGeneralSQLError(w, method, err)

View file

@ -68,8 +68,8 @@ func AddFolder(w http.ResponseWriter, r *http.Request) {
id := util.UniqueID() id := util.UniqueID()
folder.RefID = id folder.RefID = id
folder.OrgID = p.Context.OrgID folder.OrgID = p.Context.OrgID
err = addFolder(p, &folder)
err = addFolder(p, &folder)
if err != nil { if err != nil {
log.IfErr(tx.Rollback()) log.IfErr(tx.Rollback())
writeGeneralSQLError(w, method, err) writeGeneralSQLError(w, method, err)
@ -78,10 +78,9 @@ func AddFolder(w http.ResponseWriter, r *http.Request) {
log.IfErr(tx.Commit()) log.IfErr(tx.Commit())
folder, err = p.GetLabel(id) folder, _ = p.GetLabel(id)
json, err := json.Marshal(folder) json, err := json.Marshal(folder)
if err != nil { if err != nil {
writeJSONMarshalError(w, method, "folder", err) writeJSONMarshalError(w, method, "folder", err)
return return

View file

@ -84,6 +84,12 @@ func AddDocumentPage(w http.ResponseWriter, r *http.Request) {
model.Meta.SetDefaults() model.Meta.SetDefaults()
// page.Title = template.HTMLEscapeString(page.Title) // page.Title = template.HTMLEscapeString(page.Title)
doc, err := p.GetDocument(documentID)
if err != nil {
writeGeneralSQLError(w, method, err)
return
}
tx, err := request.Db.Beginx() tx, err := request.Db.Beginx()
if err != nil { if err != nil {
writeTransactionError(w, method, err) writeTransactionError(w, method, err)
@ -110,12 +116,17 @@ func AddDocumentPage(w http.ResponseWriter, r *http.Request) {
p.IncrementBlockUsage(model.Page.BlockID) p.IncrementBlockUsage(model.Page.BlockID)
} }
_ = p.RecordUserActivity(entity.UserActivity{
LabelID: doc.LabelID,
SourceID: model.Page.DocumentID,
SourceType: entity.ActivitySourceTypeDocument,
ActivityType: entity.ActivityTypeCreated})
log.IfErr(tx.Commit()) log.IfErr(tx.Commit())
newPage, _ := p.GetPage(pageID) newPage, _ := p.GetPage(pageID)
json, err := json.Marshal(newPage) json, err := json.Marshal(newPage)
if err != nil { if err != nil {
writeJSONMarshalError(w, method, "page", err) writeJSONMarshalError(w, method, "page", err)
return return
@ -265,7 +276,6 @@ func GetDocumentPagesBatch(w http.ResponseWriter, r *http.Request) {
} }
json, err := json.Marshal(pages) json, err := json.Marshal(pages)
if err != nil { if err != nil {
writeJSONMarshalError(w, method, "document", err) writeJSONMarshalError(w, method, "document", err)
return return
@ -299,13 +309,17 @@ func DeleteDocumentPage(w http.ResponseWriter, r *http.Request) {
return return
} }
tx, err := request.Db.Beginx() doc, err := p.GetDocument(documentID)
if err != nil {
writeGeneralSQLError(w, method, err)
return
}
tx, err := request.Db.Beginx()
if err != nil { if err != nil {
writeTransactionError(w, method, err) writeTransactionError(w, method, err)
return return
} }
p.Context.Transaction = tx p.Context.Transaction = tx
page, err := p.GetPage(pageID) page, err := p.GetPage(pageID)
@ -327,6 +341,12 @@ func DeleteDocumentPage(w http.ResponseWriter, r *http.Request) {
return return
} }
_ = p.RecordUserActivity(entity.UserActivity{
LabelID: doc.LabelID,
SourceID: documentID,
SourceType: entity.ActivitySourceTypeDocument,
ActivityType: entity.ActivityTypeDeleted})
log.IfErr(tx.Commit()) log.IfErr(tx.Commit())
writeSuccessEmptyJSON(w) writeSuccessEmptyJSON(w)
@ -352,7 +372,6 @@ func DeleteDocumentPages(w http.ResponseWriter, r *http.Request) {
defer utility.Close(r.Body) defer utility.Close(r.Body)
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
writeBadRequestError(w, method, "Bad body") writeBadRequestError(w, method, "Bad body")
return return
@ -366,13 +385,17 @@ func DeleteDocumentPages(w http.ResponseWriter, r *http.Request) {
return return
} }
tx, err := request.Db.Beginx() doc, err := p.GetDocument(documentID)
if err != nil {
writeGeneralSQLError(w, method, err)
return
}
tx, err := request.Db.Beginx()
if err != nil { if err != nil {
writeTransactionError(w, method, err) writeTransactionError(w, method, err)
return return
} }
p.Context.Transaction = tx p.Context.Transaction = tx
for _, page := range *model { for _, page := range *model {
@ -395,6 +418,12 @@ func DeleteDocumentPages(w http.ResponseWriter, r *http.Request) {
} }
} }
_ = p.RecordUserActivity(entity.UserActivity{
LabelID: doc.LabelID,
SourceID: documentID,
SourceType: entity.ActivitySourceTypeDocument,
ActivityType: entity.ActivityTypeDeleted})
log.IfErr(tx.Commit()) log.IfErr(tx.Commit())
writeSuccessEmptyJSON(w) writeSuccessEmptyJSON(w)
@ -406,22 +435,20 @@ func DeleteDocumentPages(w http.ResponseWriter, r *http.Request) {
func UpdateDocumentPage(w http.ResponseWriter, r *http.Request) { func UpdateDocumentPage(w http.ResponseWriter, r *http.Request) {
method := "UpdateDocumentPage" method := "UpdateDocumentPage"
p := request.GetPersister(r) p := request.GetPersister(r)
params := mux.Vars(r)
if !p.Context.Editor { if !p.Context.Editor {
writeForbiddenError(w) writeForbiddenError(w)
return return
} }
params := mux.Vars(r)
documentID := params["documentID"] documentID := params["documentID"]
if len(documentID) == 0 { if len(documentID) == 0 {
writeMissingDataError(w, method, "documentID") writeMissingDataError(w, method, "documentID")
return return
} }
pageID := params["pageID"] pageID := params["pageID"]
if len(pageID) == 0 { if len(pageID) == 0 {
writeMissingDataError(w, method, "pageID") writeMissingDataError(w, method, "pageID")
return return
@ -429,7 +456,6 @@ func UpdateDocumentPage(w http.ResponseWriter, r *http.Request) {
defer utility.Close(r.Body) defer utility.Close(r.Body)
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
writeBadRequestError(w, method, "Bad request body") writeBadRequestError(w, method, "Bad request body")
return return
@ -437,7 +463,6 @@ func UpdateDocumentPage(w http.ResponseWriter, r *http.Request) {
model := new(models.PageModel) model := new(models.PageModel)
err = json.Unmarshal(body, &model) err = json.Unmarshal(body, &model)
if err != nil { if err != nil {
writePayloadError(w, method, err) writePayloadError(w, method, err)
return return
@ -448,8 +473,13 @@ func UpdateDocumentPage(w http.ResponseWriter, r *http.Request) {
return return
} }
tx, err := request.Db.Beginx() doc, err := p.GetDocument(documentID)
if err != nil {
writeGeneralSQLError(w, method, err)
return
}
tx, err := request.Db.Beginx()
if err != nil { if err != nil {
writeTransactionError(w, method, err) writeTransactionError(w, method, err)
return return
@ -488,6 +518,12 @@ func UpdateDocumentPage(w http.ResponseWriter, r *http.Request) {
err = p.UpdatePageMeta(model.Meta, true) // change the UserID to the current one err = p.UpdatePageMeta(model.Meta, true) // change the UserID to the current one
_ = p.RecordUserActivity(entity.UserActivity{
LabelID: doc.LabelID,
SourceID: model.Page.DocumentID,
SourceType: entity.ActivitySourceTypeDocument,
ActivityType: entity.ActivityTypeEdited})
log.IfErr(tx.Commit()) log.IfErr(tx.Commit())
updatedPage, err := p.GetPage(pageID) updatedPage, err := p.GetPage(pageID)
@ -894,6 +930,13 @@ func RollbackDocumentPage(w http.ResponseWriter, r *http.Request) {
return return
} }
// fetch doc
doc, err := p.GetDocument(documentID)
if err != nil {
writeGeneralSQLError(w, method, err)
return
}
// roll back page // roll back page
page.Body = revision.Body page.Body = revision.Body
refID := util.UniqueID() refID := util.UniqueID()
@ -916,6 +959,12 @@ func RollbackDocumentPage(w http.ResponseWriter, r *http.Request) {
return return
} }
_ = p.RecordUserActivity(entity.UserActivity{
LabelID: doc.LabelID,
SourceID: page.DocumentID,
SourceType: entity.ActivitySourceTypeDocument,
ActivityType: entity.ActivityTypeReverted})
log.IfErr(tx.Commit()) log.IfErr(tx.Commit())
payload, err := json.Marshal(page) payload, err := json.Marshal(page)
@ -962,6 +1011,12 @@ func CopyPage(w http.ResponseWriter, r *http.Request) {
} }
// fetch data // fetch data
doc, err := p.GetDocument(documentID)
if err != nil {
writeGeneralSQLError(w, method, err)
return
}
page, err := p.GetPage(pageID) page, err := p.GetPage(pageID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
writeNotFoundError(w, method, documentID) writeNotFoundError(w, method, documentID)
@ -1013,6 +1068,13 @@ func CopyPage(w http.ResponseWriter, r *http.Request) {
p.IncrementBlockUsage(model.Page.BlockID) p.IncrementBlockUsage(model.Page.BlockID)
} }
// Log action against target document
_ = p.RecordUserActivity(entity.UserActivity{
LabelID: doc.LabelID,
SourceID: targetID,
SourceType: entity.ActivitySourceTypeDocument,
ActivityType: entity.ActivityTypeEdited})
log.IfErr(tx.Commit()) log.IfErr(tx.Commit())
newPage, _ := p.GetPage(pageID) newPage, _ := p.GetPage(pageID)

View file

@ -126,8 +126,7 @@ func SaveAsTemplate(w http.ResponseWriter, r *http.Request) {
} }
// Duplicate attachments // Duplicate attachments
attachments, err := p.GetAttachments(model.DocumentID) attachments, _ := p.GetAttachments(model.DocumentID)
for i, a := range attachments { for i, a := range attachments {
a.DocumentID = docID a.DocumentID = docID
a.RefID = util.UniqueID() a.RefID = util.UniqueID()
@ -358,8 +357,8 @@ func StartDocumentFromSavedTemplate(w http.ResponseWriter, r *http.Request) {
return return
} }
pages, err = p.GetPages(templateID) pages, _ = p.GetPages(templateID)
attachments, err = p.GetAttachmentsWithData(templateID) attachments, _ = p.GetAttachmentsWithData(templateID)
} }
// create new document // create new document

View file

@ -189,7 +189,6 @@ func AddUser(w http.ResponseWriter, r *http.Request) {
userModel, err = getSecuredUser(p, p.Context.OrgID, userID) userModel, err = getSecuredUser(p, p.Context.OrgID, userID)
json, err := json.Marshal(userModel) json, err := json.Marshal(userModel)
if err != nil { if err != nil {
writeJSONMarshalError(w, method, "user", err) writeJSONMarshalError(w, method, "user", err)
return return
@ -442,6 +441,7 @@ func UpdateUser(w http.ResponseWriter, r *http.Request) {
account.Editor = user.Editor account.Editor = user.Editor
account.Admin = user.Admin account.Admin = user.Admin
account.Active = user.Active
err = p.UpdateAccount(account) err = p.UpdateAccount(account)
if err != nil { if err != nil {
@ -691,11 +691,13 @@ func attachUserAccounts(p request.Persister, orgID string, user *entity.User) {
user.Accounts = a user.Accounts = a
user.Editor = false user.Editor = false
user.Admin = false user.Admin = false
user.Active = false
for _, account := range user.Accounts { for _, account := range user.Accounts {
if account.OrgID == orgID { if account.OrgID == orgID {
user.Admin = account.Admin user.Admin = account.Admin
user.Editor = account.Editor user.Editor = account.Editor
user.Active = account.Active
break break
} }
} }

View file

@ -0,0 +1,42 @@
// Source: https://github.com/lib/pq/blob/b269bd035a727d6c1081f76e7a239a1b00674c40/encode.go#L521
//
// Copyright (c) 2011-2013, 'pq' Contributors Portions Copyright (C) 2011 Blake Mizerany
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Package entity provides types that mirror database tables.
package entity
import (
"database/sql/driver"
"time"
)
// NullTime represents a time.Time that may be null. NullTime implements the
// sql.Scanner interface so it can be used as a scan destination, similar to
// sql.NullString.
type NullTime struct {
Time time.Time
Valid bool // Valid is true if Time is not NULL
}
// Scan implements the Scanner interface.
func (nt *NullTime) Scan(value interface{}) error {
nt.Time, nt.Valid = value.(time.Time)
return nil
}
// Value implements the driver Valuer interface.
func (nt NullTime) Value() (driver.Value, error) {
if !nt.Valid {
return nil, nil
}
return nt.Time, nil
}

View file

@ -89,6 +89,7 @@ type Account struct {
Title string `json:"title"` Title string `json:"title"`
Message string `json:"message"` Message string `json:"message"`
Domain string `json:"domain"` Domain string `json:"domain"`
Active bool `json:"active"`
} }
// Label defines a container for documents. // Label defines a container for documents.
@ -437,3 +438,62 @@ type Pin struct {
Pin string `json:"pin"` Pin string `json:"pin"`
Sequence int `json:"sequence"` Sequence int `json:"sequence"`
} }
// UserActivity represents an activity undertaken by a user.
type UserActivity struct {
ID uint64 `json:"-"`
OrgID string `json:"orgId"`
UserID string `json:"userId"`
LabelID string `json:"folderId"`
SourceID string `json:"sourceId"`
SourceName string `json:"sourceName"` // e.g. Document or Space name
SourceType ActivitySourceType `json:"sourceType"`
ActivityType ActivityType `json:"activityType"`
Created time.Time `json:"created"`
}
// ActivitySourceType details where the activity occured.
type ActivitySourceType int
// ActivityType determines type of user activity
type ActivityType int
const (
// ActivitySourceTypeSpace indicates activity against a space.
ActivitySourceTypeSpace ActivitySourceType = 1
// ActivitySourceTypeDocument indicates activity against a document.
ActivitySourceTypeDocument ActivitySourceType = 2
)
const (
// ActivityTypeCreated records user document creation
ActivityTypeCreated ActivityType = 1
// ActivityTypeRead states user has read document
ActivityTypeRead ActivityType = 2
// ActivityTypeEdited states user has editing document
ActivityTypeEdited ActivityType = 3
// ActivityTypeDeleted records user deleting space/document
ActivityTypeDeleted ActivityType = 4
// ActivityTypeArchived records user archiving space/document
ActivityTypeArchived ActivityType = 5
// ActivityTypeApproved records user approval of document
ActivityTypeApproved ActivityType = 6
// ActivityTypeReverted records user content roll-back to previous version
ActivityTypeReverted ActivityType = 7
// ActivityTypePublishedTemplate records user creating new document template
ActivityTypePublishedTemplate ActivityType = 8
// ActivityTypePublishedBlock records user creating reusable content block
ActivityTypePublishedBlock ActivityType = 9
// ActivityTypeFeedback records user providing document feedback
ActivityTypeFeedback ActivityType = 10
)

View file

@ -14,10 +14,11 @@ package request
import ( import (
"database/sql" "database/sql"
"fmt" "fmt"
"time"
"github.com/documize/community/core/api/entity" "github.com/documize/community/core/api/entity"
"github.com/documize/community/core/log" "github.com/documize/community/core/log"
"github.com/documize/community/core/utility" "github.com/documize/community/core/utility"
"time"
) )
// AddAccount inserts the given record into the datbase account table. // AddAccount inserts the given record into the datbase account table.
@ -25,7 +26,7 @@ func (p *Persister) AddAccount(account entity.Account) (err error) {
account.Created = time.Now().UTC() account.Created = time.Now().UTC()
account.Revised = time.Now().UTC() account.Revised = time.Now().UTC()
stmt, err := p.Context.Transaction.Preparex("INSERT INTO account (refid, orgid, userid, admin, editor, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?)") stmt, err := p.Context.Transaction.Preparex("INSERT INTO account (refid, orgid, userid, admin, editor, active, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -33,7 +34,7 @@ func (p *Persister) AddAccount(account entity.Account) (err error) {
return return
} }
_, err = stmt.Exec(account.RefID, account.OrgID, account.UserID, account.Admin, account.Editor, account.Created, account.Revised) _, err = stmt.Exec(account.RefID, account.OrgID, account.UserID, account.Admin, account.Editor, account.Active, account.Created, account.Revised)
if err != nil { if err != nil {
log.Error("Unable to execute insert for account", err) log.Error("Unable to execute insert for account", err)
@ -43,11 +44,9 @@ func (p *Persister) AddAccount(account entity.Account) (err error) {
return return
} }
// GetUserAccount returns the databse 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 (p *Persister) GetUserAccount(userID string) (account entity.Account, err error) { func (p *Persister) GetUserAccount(userID string) (account entity.Account, err error) {
err = nil stmt, err := Db.Preparex("SELECT a.*, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE b.refid=a.orgid and a.orgid=? and a.userid=?")
stmt, err := Db.Preparex("SELECT a.*, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE b.refid=a.orgid and a.orgid=? and a.userid=? AND b.active=1")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -67,10 +66,7 @@ func (p *Persister) GetUserAccount(userID string) (account entity.Account, err e
// 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 (p *Persister) GetUserAccounts(userID string) (t []entity.Account, err error) { func (p *Persister) GetUserAccounts(userID string) (t []entity.Account, err error) {
err = Db.Select(&t, "SELECT a.*, 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)
err = nil
err = Db.Select(&t, "SELECT a.*, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE a.userid=? AND a.orgid=b.refid AND b.active=1 ORDER BY b.title", userID)
if err != sql.ErrNoRows && err != nil { if err != sql.ErrNoRows && err != nil {
log.Error(fmt.Sprintf("Unable to execute select account for user %s", userID), err) log.Error(fmt.Sprintf("Unable to execute select account for user %s", userID), err)
@ -81,10 +77,7 @@ func (p *Persister) GetUserAccounts(userID string) (t []entity.Account, err erro
// 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 (p *Persister) GetAccountsByOrg() (t []entity.Account, err error) { func (p *Persister) GetAccountsByOrg() (t []entity.Account, err error) {
err = Db.Select(&t, "SELECT a.*, 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", p.Context.OrgID)
err = nil
err = Db.Select(&t, "SELECT a.*, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE a.orgid=b.refid AND a.orgid=? AND b.active=1", p.Context.OrgID)
if err != sql.ErrNoRows && err != nil { if err != sql.ErrNoRows && err != nil {
log.Error(fmt.Sprintf("Unable to execute select account for org %s", p.Context.OrgID), err) log.Error(fmt.Sprintf("Unable to execute select account for org %s", p.Context.OrgID), err)
@ -95,12 +88,9 @@ func (p *Persister) GetAccountsByOrg() (t []entity.Account, err error) {
// UpdateAccount updates the database record for the given account to the given values. // UpdateAccount updates the database record for the given account to the given values.
func (p *Persister) UpdateAccount(account entity.Account) (err error) { func (p *Persister) UpdateAccount(account entity.Account) (err error) {
err = nil
account.Revised = time.Now().UTC() account.Revised = time.Now().UTC()
stmt, err := p.Context.Transaction.PrepareNamed("UPDATE account SET userid=:userid, admin=:admin, editor=:editor, revised=:revised WHERE orgid=:orgid AND refid=:refid") stmt, err := p.Context.Transaction.PrepareNamed("UPDATE account SET userid=:userid, admin=:admin, editor=:editor, active=:active, revised=:revised WHERE orgid=:orgid AND refid=:refid")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -120,7 +110,6 @@ func (p *Persister) UpdateAccount(account entity.Account) (err error) {
// HasOrgAccount returns if the given orgID has valid userID. // HasOrgAccount returns if the given orgID has valid userID.
func (p *Persister) HasOrgAccount(orgID, userID string) bool { func (p *Persister) HasOrgAccount(orgID, userID string) bool {
row := Db.QueryRow("SELECT count(*) FROM account WHERE orgid=? and userid=?", orgID, userID) row := Db.QueryRow("SELECT count(*) FROM account WHERE orgid=? and userid=?", orgID, userID)
var count int var count int

View file

@ -0,0 +1,44 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. 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 <sales@documize.com>.
//
// https://documize.com
package request
import (
"time"
"github.com/documize/community/core/api/entity"
"github.com/documize/community/core/log"
"github.com/documize/community/core/utility"
)
// RecordUserActivity logs user initiated data changes.
func (p *Persister) RecordUserActivity(activity entity.UserActivity) (err error) {
activity.OrgID = p.Context.OrgID
activity.UserID = p.Context.UserID
activity.Created = time.Now().UTC()
stmt, err := p.Context.Transaction.Preparex("INSERT INTO useractivity (orgid, userid, labelid, sourceid, sourcetype, activitytype, created) VALUES (?, ?, ?, ?, ?, ?, ?)")
defer utility.Close(stmt)
if err != nil {
log.Error("Unable to prepare insert RecordUserActivity", err)
return
}
_, err = stmt.Exec(activity.OrgID, activity.UserID, activity.LabelID, activity.SourceID, activity.SourceType, activity.ActivityType, activity.Created)
if err != nil {
log.Error("Unable to execute insert RecordUserActivity", err)
return
}
return
}

View file

@ -49,9 +49,6 @@ func (p *Persister) AddAttachment(a entity.Attachment) (err error) {
// GetAttachment returns the database attachment record specified by the parameters. // GetAttachment returns the database attachment record specified by the parameters.
func (p *Persister) GetAttachment(orgID, attachmentID string) (attachment entity.Attachment, err error) { func (p *Persister) GetAttachment(orgID, attachmentID string) (attachment entity.Attachment, err error) {
err = nil
stmt, err := Db.Preparex("SELECT id, refid, orgid, documentid, job, fileid, filename, data, extension, created, revised FROM attachment WHERE orgid=? and refid=?") stmt, err := Db.Preparex("SELECT id, refid, orgid, documentid, job, fileid, filename, data, extension, created, revised FROM attachment WHERE orgid=? and refid=?")
defer utility.Close(stmt) defer utility.Close(stmt)
@ -72,9 +69,6 @@ func (p *Persister) GetAttachment(orgID, attachmentID string) (attachment entity
// GetAttachments returns a slice containing the attachement records (excluding their data) for document docID, ordered by filename. // GetAttachments returns a slice containing the attachement records (excluding their data) for document docID, ordered by filename.
func (p *Persister) GetAttachments(docID string) (attachments []entity.Attachment, err error) { func (p *Persister) GetAttachments(docID string) (attachments []entity.Attachment, err error) {
err = nil
err = Db.Select(&attachments, "SELECT id, refid, orgid, documentid, job, fileid, filename, extension, created, revised FROM attachment WHERE orgid=? and documentid=? order by filename", p.Context.OrgID, docID) err = Db.Select(&attachments, "SELECT id, refid, orgid, documentid, job, fileid, filename, extension, created, revised FROM attachment WHERE orgid=? and documentid=? order by filename", p.Context.OrgID, docID)
if err != nil { if err != nil {
@ -87,9 +81,6 @@ func (p *Persister) GetAttachments(docID string) (attachments []entity.Attachmen
// GetAttachmentsWithData returns a slice containing the attachement records (including their data) for document docID, ordered by filename. // GetAttachmentsWithData returns a slice containing the attachement records (including their data) for document docID, ordered by filename.
func (p *Persister) GetAttachmentsWithData(docID string) (attachments []entity.Attachment, err error) { func (p *Persister) GetAttachmentsWithData(docID string) (attachments []entity.Attachment, err error) {
err = nil
err = Db.Select(&attachments, "SELECT id, refid, orgid, documentid, job, fileid, filename, extension, data, created, revised FROM attachment WHERE orgid=? and documentid=? order by filename", p.Context.OrgID, docID) err = Db.Select(&attachments, "SELECT id, refid, orgid, documentid, job, fileid, filename, extension, data, created, revised FROM attachment WHERE orgid=? and documentid=? order by filename", p.Context.OrgID, docID)
if err != nil { if err != nil {
@ -102,7 +93,7 @@ func (p *Persister) GetAttachmentsWithData(docID string) (attachments []entity.A
// DeleteAttachment deletes the id record from the database attachment table. // DeleteAttachment deletes the id record from the database attachment table.
func (p *Persister) DeleteAttachment(id string) (rows int64, err error) { func (p *Persister) DeleteAttachment(id string) (rows int64, err error) {
rows, err = p.Base.DeleteConstrained(p.Context.Transaction, "attachment", p.Context.OrgID, id) rows, _ = p.Base.DeleteConstrained(p.Context.Transaction, "attachment", p.Context.OrgID, id)
// Mark references to this document as orphaned // Mark references to this document as orphaned
err = p.MarkOrphanAttachmentLink(id) err = p.MarkOrphanAttachmentLink(id)

View file

@ -47,15 +47,11 @@ func (p *Persister) AddDocument(document entity.Document) (err error) {
return return
} }
p.Base.Audit(p.Context, "add-document", document.RefID, "")
return return
} }
// GetDocument fetches the document record with the given id fromt the document table and audits that it has been got. // GetDocument fetches the document record with the given id fromt the document table and audits that it has been got.
func (p *Persister) GetDocument(id string) (document entity.Document, err error) { func (p *Persister) GetDocument(id string) (document entity.Document, err error) {
err = nil
stmt, err := Db.Preparex("SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? and refid=?") stmt, err := Db.Preparex("SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? and refid=?")
defer utility.Close(stmt) defer utility.Close(stmt)
@ -71,15 +67,11 @@ func (p *Persister) GetDocument(id string) (document entity.Document, err error)
return return
} }
p.Base.Audit(p.Context, AuditGetDocument, id, "")
return return
} }
// GetDocumentMeta returns the metadata for a specified document. // GetDocumentMeta returns the metadata for a specified document.
func (p *Persister) GetDocumentMeta(id string) (meta entity.DocumentMeta, err error) { func (p *Persister) GetDocumentMeta(id string) (meta entity.DocumentMeta, err error) {
err = nil
// sqlViewers := `SELECT CONVERT_TZ(MAX(a.created), @@session.time_zone, '+00:00') as created, // sqlViewers := `SELECT CONVERT_TZ(MAX(a.created), @@session.time_zone, '+00:00') as created,
sqlViewers := `SELECT MAX(a.created) as created, sqlViewers := `SELECT MAX(a.created) as created,
@ -131,7 +123,6 @@ func (p *Persister) GetDocuments() (documents []entity.Document, err error) {
// GetDocumentsByFolder returns a slice containing the documents for a given folder, most recient first. // GetDocumentsByFolder returns a slice containing the documents for a given folder, most recient first.
func (p *Persister) GetDocumentsByFolder(folderID string) (documents []entity.Document, err error) { func (p *Persister) GetDocumentsByFolder(folderID string) (documents []entity.Document, err error) {
err = nil
err = 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", p.Context.OrgID, folderID) err = 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", p.Context.OrgID, folderID)
if err != nil { if err != nil {
@ -306,8 +297,6 @@ func (p *Persister) SearchDocument(keywords string) (results []entity.DocumentSe
return return
} }
p.Base.Audit(p.Context, "search", "", "")
return return
} }
@ -345,8 +334,6 @@ func (p *Persister) UpdateDocument(document entity.Document) (err error) {
return return
} }
p.Base.Audit(p.Context, "update-document", document.RefID, "")
return return
} }
@ -377,8 +364,6 @@ func (p *Persister) ChangeDocumentLabel(document, label string) (err error) {
return re return re
} }
p.Base.Audit(p.Context, "update-document-label", document, "")
return return
} }
@ -400,8 +385,6 @@ func (p *Persister) MoveDocumentLabel(id, move string) (err error) {
return return
} }
p.Base.Audit(p.Context, "move-document-label", "", "")
return return
} }
@ -444,7 +427,5 @@ func (p *Persister) DeleteDocument(documentID string) (rows int64, err error) {
return return
} }
p.Base.Audit(p.Context, "delete-document", documentID, "")
return p.Base.DeleteConstrained(p.Context.Transaction, "document", p.Context.OrgID, documentID) return p.Base.DeleteConstrained(p.Context.Transaction, "document", p.Context.OrgID, documentID)
} }

View file

@ -128,9 +128,6 @@ type baseManager struct {
} }
func (m *baseManager) Delete(tx *sqlx.Tx, table string, id string) (rows int64, err error) { func (m *baseManager) Delete(tx *sqlx.Tx, table string, id string) (rows int64, err error) {
err = nil
stmt, err := tx.Preparex("DELETE FROM " + table + " WHERE refid=?") stmt, err := tx.Preparex("DELETE FROM " + table + " WHERE refid=?")
if err != nil { if err != nil {
log.Error(fmt.Sprintf("Unable to prepare delete of row in table %s", table), err) log.Error(fmt.Sprintf("Unable to prepare delete of row in table %s", table), err)
@ -203,16 +200,6 @@ func (m *baseManager) DeleteWhere(tx *sqlx.Tx, statement string) (rows int64, er
return return
} }
// Audit inserts a record into the audit table.
func (m *baseManager) Audit(c Context, action, document, page string) {
_, err := Db.Exec("INSERT INTO audit (orgid, userid, documentid, pageid, action, created) VALUES (?, ?, ?, ?, ?, ?)", c.OrgID, c.UserID, document, page, action, time.Now().UTC())
if err != nil {
log.Error(fmt.Sprintf("Unable record audit for action %s, user %s, customer %s", action, c.UserID, c.OrgID), err)
}
}
// SQLPrepareError returns a string detailing the location of the error. // SQLPrepareError returns a string detailing the location of the error.
func (m *baseManager) SQLPrepareError(method string, id string) string { func (m *baseManager) SQLPrepareError(method string, id string) string {
return fmt.Sprintf("Unable to prepare SQL for %s, ID %s", method, id) return fmt.Sprintf("Unable to prepare SQL for %s, ID %s", method, id)
@ -222,8 +209,3 @@ func (m *baseManager) SQLPrepareError(method string, id string) string {
func (m *baseManager) SQLSelectError(method string, id string) string { func (m *baseManager) SQLSelectError(method string, id string) string {
return fmt.Sprintf("Unable to execute SQL for %s, ID %s", method, id) return fmt.Sprintf("Unable to execute SQL for %s, ID %s", method, id)
} }
const (
// AuditGetDocument means someone viewed a document
AuditGetDocument string = "get-document"
)

View file

@ -46,9 +46,6 @@ func (p *Persister) AddLabel(l entity.Label) (err error) {
// GetLabel returns a folder from the store. // GetLabel returns a folder from the store.
func (p *Persister) GetLabel(id string) (label entity.Label, err error) { func (p *Persister) GetLabel(id string) (label entity.Label, err error) {
err = nil
stmt, err := Db.Preparex("SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label WHERE orgid=? and refid=?") stmt, err := Db.Preparex("SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label WHERE orgid=? and refid=?")
defer utility.Close(stmt) defer utility.Close(stmt)
@ -69,8 +66,6 @@ func (p *Persister) GetLabel(id string) (label entity.Label, err error) {
// GetPublicFolders returns folders that anyone can see. // GetPublicFolders returns folders that anyone can see.
func (p *Persister) GetPublicFolders(orgID string) (labels []entity.Label, err error) { func (p *Persister) GetPublicFolders(orgID string) (labels []entity.Label, err error) {
err = nil
sql := "SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label a where orgid=? AND type=1" sql := "SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label a where orgid=? AND type=1"
err = Db.Select(&labels, sql, orgID) err = Db.Select(&labels, sql, orgID)
@ -86,8 +81,6 @@ func (p *Persister) GetPublicFolders(orgID string) (labels []entity.Label, err e
// GetLabels returns folders that the user can see. // GetLabels returns folders that the user can see.
// Also handles which folders can be seen by anonymous users. // Also handles which folders can be seen by anonymous users.
func (p *Persister) GetLabels() (labels []entity.Label, err error) { func (p *Persister) GetLabels() (labels []entity.Label, err error) {
err = nil
sql := ` sql := `
(SELECT id,refid,label as name,orgid,userid,type,created,revised from label WHERE orgid=? AND type=2 AND userid=?) (SELECT id,refid,label as name,orgid,userid,type,created,revised from label WHERE orgid=? AND type=2 AND userid=?)
UNION ALL UNION ALL
@ -117,7 +110,6 @@ ORDER BY name`
// UpdateLabel saves folder changes. // UpdateLabel saves folder changes.
func (p *Persister) UpdateLabel(label entity.Label) (err error) { func (p *Persister) UpdateLabel(label entity.Label) (err error) {
err = nil
label.Revised = time.Now().UTC() label.Revised = time.Now().UTC()
stmt, err := p.Context.Transaction.PrepareNamed("UPDATE label SET label=:name, type=:type, userid=:userid, revised=:revised WHERE orgid=:orgid AND refid=:refid") stmt, err := p.Context.Transaction.PrepareNamed("UPDATE label SET label=:name, type=:type, userid=:userid, revised=:revised WHERE orgid=:orgid AND refid=:refid")
@ -140,8 +132,6 @@ func (p *Persister) UpdateLabel(label entity.Label) (err error) {
// ChangeLabelOwner transfer folder ownership. // ChangeLabelOwner transfer folder ownership.
func (p *Persister) ChangeLabelOwner(currentOwner, newOwner string) (err error) { func (p *Persister) ChangeLabelOwner(currentOwner, newOwner string) (err error) {
err = nil
stmt, err := p.Context.Transaction.Preparex("UPDATE label SET userid=? WHERE userid=? AND orgid=?") stmt, err := p.Context.Transaction.Preparex("UPDATE label SET userid=? WHERE userid=? AND orgid=?")
defer utility.Close(stmt) defer utility.Close(stmt)
@ -162,8 +152,6 @@ func (p *Persister) ChangeLabelOwner(currentOwner, newOwner string) (err error)
// GetFolderVisibility returns the list of people who can see shared folders. // GetFolderVisibility returns the list of people who can see shared folders.
func (p *Persister) GetFolderVisibility() (visibleTo []entity.FolderVisibility, err error) { func (p *Persister) GetFolderVisibility() (visibleTo []entity.FolderVisibility, err error) {
err = nil
sql := ` sql := `
SELECT a.userid, SELECT a.userid,
COALESCE(u.firstname, '') as firstname, COALESCE(u.firstname, '') as firstname,

View file

@ -46,9 +46,6 @@ func (p *Persister) AddLabelRole(l entity.LabelRole) (err error) {
// GetLabelRoles returns a slice of labelrole records, for the given labelID in the client's organization, grouped by user. // GetLabelRoles returns a slice of labelrole records, for the given labelID in the client's organization, grouped by user.
func (p *Persister) GetLabelRoles(labelID string) (roles []entity.LabelRole, err error) { func (p *Persister) GetLabelRoles(labelID string) (roles []entity.LabelRole, err error) {
err = nil
query := `SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? AND labelid=?` // was + "GROUP BY userid" query := `SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? AND labelid=?` // was + "GROUP BY userid"
err = Db.Select(&roles, query, p.Context.OrgID, labelID) err = Db.Select(&roles, query, p.Context.OrgID, labelID)

View file

@ -48,9 +48,6 @@ func (p *Persister) AddContentLink(l entity.Link) (err error) {
// SearchLinkCandidates returns matching documents, sections and attachments using keywords. // SearchLinkCandidates returns matching documents, sections and attachments using keywords.
func (p *Persister) SearchLinkCandidates(keywords string) (docs []entity.LinkCandidate, func (p *Persister) SearchLinkCandidates(keywords string) (docs []entity.LinkCandidate,
pages []entity.LinkCandidate, attachments []entity.LinkCandidate, err error) { pages []entity.LinkCandidate, attachments []entity.LinkCandidate, err error) {
err = nil
// find matching documents // find matching documents
temp := []entity.LinkCandidate{} temp := []entity.LinkCandidate{}
likeQuery := "title LIKE '%" + keywords + "%'" likeQuery := "title LIKE '%" + keywords + "%'"
@ -182,8 +179,6 @@ func (p *Persister) SearchLinkCandidates(keywords string) (docs []entity.LinkCan
// GetDocumentOutboundLinks returns outbound links for specified document. // GetDocumentOutboundLinks returns outbound links for specified document.
func (p *Persister) GetDocumentOutboundLinks(documentID string) (links []entity.Link, err error) { func (p *Persister) GetDocumentOutboundLinks(documentID string) (links []entity.Link, err error) {
err = nil
err = Db.Select(&links, err = Db.Select(&links,
`select l.refid, l.orgid, l.folderid, l.userid, l.sourcedocumentid, l.sourcepageid, l.targetdocumentid, l.targetid, l.linktype, l.orphan, l.created, l.revised `select l.refid, l.orgid, l.folderid, l.userid, l.sourcedocumentid, l.sourcepageid, l.targetdocumentid, l.targetid, l.linktype, l.orphan, l.created, l.revised
FROM link l FROM link l
@ -204,8 +199,6 @@ func (p *Persister) GetDocumentOutboundLinks(documentID string) (links []entity.
// GetPageLinks returns outbound links for specified page in document. // GetPageLinks returns outbound links for specified page in document.
func (p *Persister) GetPageLinks(documentID, pageID string) (links []entity.Link, err error) { func (p *Persister) GetPageLinks(documentID, pageID string) (links []entity.Link, err error) {
err = nil
err = Db.Select(&links, err = Db.Select(&links,
`select l.refid, l.orgid, l.folderid, l.userid, l.sourcedocumentid, l.sourcepageid, l.targetdocumentid, l.targetid, l.linktype, l.orphan, l.created, l.revised `select l.refid, l.orgid, l.folderid, l.userid, l.sourcedocumentid, l.sourcepageid, l.targetdocumentid, l.targetid, l.linktype, l.orphan, l.created, l.revised
FROM link l FROM link l

View file

@ -59,8 +59,6 @@ func (p *Persister) AddOrganization(org entity.Organization) error {
// GetOrganization returns the Organization reocrod from the organization database table with the given id. // GetOrganization returns the Organization reocrod from the organization database table with the given id.
func (p *Persister) GetOrganization(id string) (org entity.Organization, err error) { func (p *Persister) GetOrganization(id string) (org entity.Organization, err error) {
err = nil
stmt, err := Db.Preparex("SELECT id, refid, company, title, message, url, domain, email, serial, active, allowanonymousaccess, created, revised FROM organization WHERE refid=?") stmt, err := Db.Preparex("SELECT id, refid, company, title, message, url, domain, email, serial, active, allowanonymousaccess, created, revised FROM organization WHERE refid=?")
defer utility.Close(stmt) defer utility.Close(stmt)
@ -128,7 +126,6 @@ func (p *Persister) GetOrganizationByDomain(subdomain string) (org entity.Organi
// UpdateOrganization updates the given organization record in the database to the values supplied. // UpdateOrganization updates the given organization record in the database to the values supplied.
func (p *Persister) UpdateOrganization(org entity.Organization) (err error) { func (p *Persister) UpdateOrganization(org entity.Organization) (err error) {
err = nil
org.Revised = time.Now().UTC() org.Revised = time.Now().UTC()
stmt, err := p.Context.Transaction.PrepareNamed("UPDATE organization SET title=:title, message=:message, email=:email, allowanonymousaccess=:allowanonymousaccess, revised=:revised WHERE refid=:refid") stmt, err := p.Context.Transaction.PrepareNamed("UPDATE organization SET title=:title, message=:message, email=:email, allowanonymousaccess=:allowanonymousaccess, revised=:revised WHERE refid=:refid")
@ -164,8 +161,6 @@ func (p *Persister) DeleteOrganization(orgID string) (rows int64, err error) {
// RemoveOrganization sets the orgID organization to be inactive, thus executing a "soft delete" operation. // RemoveOrganization sets the orgID organization to be inactive, thus executing a "soft delete" operation.
func (p *Persister) RemoveOrganization(orgID string) (err error) { func (p *Persister) RemoveOrganization(orgID string) (err error) {
err = nil
stmt, err := p.Context.Transaction.Preparex("UPDATE organization SET active=0 WHERE refid=?") stmt, err := p.Context.Transaction.Preparex("UPDATE organization SET active=0 WHERE refid=?")
defer utility.Close(stmt) defer utility.Close(stmt)

View file

@ -82,8 +82,6 @@ func (p *Persister) AddPage(model models.PageModel) (err error) {
return return
} }
p.Base.Audit(p.Context, "add-page", model.Page.DocumentID, model.Page.RefID)
return return
} }
@ -289,8 +287,6 @@ func (p *Persister) UpdatePage(page entity.Page, refID, userID string, skipRevis
} }
} }
p.Base.Audit(p.Context, "update-page", page.DocumentID, page.RefID)
return return
} }
@ -341,8 +337,6 @@ func (p *Persister) UpdatePageSequence(documentID, pageID string, sequence float
err = searches.UpdateSequence(&databaseRequest{OrgID: p.Context.OrgID}, documentID, pageID, sequence) err = searches.UpdateSequence(&databaseRequest{OrgID: p.Context.OrgID}, documentID, pageID, sequence)
p.Base.Audit(p.Context, "re-sequence-page", "", pageID)
return return
} }
@ -366,8 +360,6 @@ func (p *Persister) UpdatePageLevel(documentID, pageID string, level int) (err e
err = searches.UpdateLevel(&databaseRequest{OrgID: p.Context.OrgID}, documentID, pageID, level) err = searches.UpdateLevel(&databaseRequest{OrgID: p.Context.OrgID}, documentID, pageID, level)
p.Base.Audit(p.Context, "re-level-page", "", pageID)
return return
} }
@ -388,8 +380,6 @@ func (p *Persister) DeletePage(documentID, pageID string) (rows int64, err error
// nuke revisions // nuke revisions
_, _ = p.DeletePageRevisions(pageID) _, _ = p.DeletePageRevisions(pageID)
p.Base.Audit(p.Context, "remove-page", documentID, pageID)
} }
return return
@ -470,8 +460,6 @@ func (p *Persister) GetDocumentRevisions(documentID string) (revisions []entity.
revisions = []entity.Revision{} revisions = []entity.Revision{}
} }
p.Base.Audit(p.Context, "get-document-revisions", documentID, "")
return return
} }
@ -485,8 +473,6 @@ func (p *Persister) GetPageRevisions(pageID string) (revisions []entity.Revision
return return
} }
p.Base.Audit(p.Context, "get-page-revisions", "", pageID)
return return
} }

View file

@ -23,7 +23,6 @@ import (
// AddPin saves pinned item. // AddPin saves pinned item.
func (p *Persister) AddPin(pin entity.Pin) (err error) { func (p *Persister) AddPin(pin entity.Pin) (err error) {
row := Db.QueryRow("SELECT max(sequence) FROM pin WHERE orgid=? AND userid=?", p.Context.OrgID, p.Context.UserID) row := Db.QueryRow("SELECT max(sequence) FROM pin WHERE orgid=? AND userid=?", p.Context.OrgID, p.Context.UserID)
var maxSeq int var maxSeq int
err = row.Scan(&maxSeq) err = row.Scan(&maxSeq)

View file

@ -28,7 +28,7 @@ func (p *Persister) AddUser(user entity.User) (err error) {
user.Created = time.Now().UTC() user.Created = time.Now().UTC()
user.Revised = time.Now().UTC() user.Revised = time.Now().UTC()
stmt, err := p.Context.Transaction.Preparex("INSERT INTO user (refid, firstname, lastname, email, initials, password, salt, reset, active, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") stmt, err := p.Context.Transaction.Preparex("INSERT INTO user (refid, firstname, lastname, email, initials, password, salt, reset, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -36,7 +36,7 @@ func (p *Persister) AddUser(user entity.User) (err error) {
return return
} }
res, err := stmt.Exec(user.RefID, user.Firstname, user.Lastname, strings.ToLower(user.Email), user.Initials, user.Password, user.Salt, "", true, user.Created, user.Revised) res, err := stmt.Exec(user.RefID, user.Firstname, user.Lastname, strings.ToLower(user.Email), user.Initials, user.Password, user.Salt, "", user.Created, user.Revised)
if err != nil { if err != nil {
log.Error("Unable insert for user", err) log.Error("Unable insert for user", err)
return return
@ -48,14 +48,12 @@ func (p *Persister) AddUser(user entity.User) (err error) {
return er return er
} }
p.Base.Audit(p.Context, "add-user", "", "")
return return
} }
// GetUser returns the user record for the given id. // GetUser returns the user record for the given id.
func (p *Persister) GetUser(id string) (user entity.User, err error) { func (p *Persister) GetUser(id string) (user entity.User, err error) {
stmt, err := Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, active, created, revised FROM user WHERE refid=?") stmt, err := Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE refid=?")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -77,7 +75,7 @@ func (p *Persister) GetUser(id string) (user entity.User, err error) {
func (p *Persister) GetUserByEmail(email string) (user entity.User, err error) { func (p *Persister) GetUserByEmail(email string) (user entity.User, err error) {
email = strings.TrimSpace(strings.ToLower(email)) email = strings.TrimSpace(strings.ToLower(email))
stmt, err := Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, active, created, revised FROM user WHERE TRIM(LOWER(email))=?") stmt, err := Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE TRIM(LOWER(email))=?")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -99,7 +97,7 @@ func (p *Persister) GetUserByEmail(email string) (user entity.User, err error) {
func (p *Persister) GetUserByDomain(domain, email string) (user entity.User, err error) { func (p *Persister) GetUserByDomain(domain, email string) (user entity.User, err error) {
email = strings.TrimSpace(strings.ToLower(email)) email = strings.TrimSpace(strings.ToLower(email))
stmt, err := Db.Preparex("SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.global, u.password, u.salt, u.reset, u.active, u.created, u.revised FROM user u, account a, organization o WHERE TRIM(LOWER(u.email))=? AND u.refid=a.userid AND a.orgid=o.refid AND TRIM(LOWER(o.domain))=?") stmt, err := Db.Preparex("SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.global, u.password, u.salt, u.reset, u.created, u.revised FROM user u, account a, organization o WHERE TRIM(LOWER(u.email))=? AND u.refid=a.userid AND a.orgid=o.refid AND TRIM(LOWER(o.domain))=?")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -119,7 +117,7 @@ func (p *Persister) GetUserByDomain(domain, email string) (user entity.User, err
// GetUserByToken returns a user record given a reset token value. // GetUserByToken returns a user record given a reset token value.
func (p *Persister) GetUserByToken(token string) (user entity.User, err error) { func (p *Persister) GetUserByToken(token string) (user entity.User, err error) {
stmt, err := Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, active, created, revised FROM user WHERE reset=?") stmt, err := Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE reset=?")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -141,7 +139,7 @@ func (p *Persister) GetUserByToken(token string) (user entity.User, err error) {
// This occurs when we you share a folder with a new user and they have to complete // This occurs when we you share a folder with a new user and they have to complete
// the onboarding process. // the onboarding process.
func (p *Persister) GetUserBySerial(serial string) (user entity.User, err error) { func (p *Persister) GetUserBySerial(serial string) (user entity.User, err error) {
stmt, err := Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, active, created, revised FROM user WHERE salt=?") stmt, err := Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE salt=?")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -161,7 +159,7 @@ func (p *Persister) GetUserBySerial(serial string) (user entity.User, err error)
// identified in the Persister. // identified in the Persister.
func (p *Persister) GetUsersForOrganization() (users []entity.User, err error) { func (p *Persister) GetUsersForOrganization() (users []entity.User, err error) {
err = Db.Select(&users, err = Db.Select(&users,
"SELECT id, refid, firstname, lastname, email, initials, password, salt, reset, active, created, revised FROM user WHERE refid IN (SELECT userid FROM account where orgid = ?) ORDER BY firstname,lastname", p.Context.OrgID) "SELECT id, refid, firstname, lastname, email, initials, password, salt, reset, created, revised FROM user WHERE refid IN (SELECT userid FROM account where orgid = ?) ORDER BY firstname,lastname", p.Context.OrgID)
if err != nil { if err != nil {
log.Error(fmt.Sprintf("Unable to get all users for org %s", p.Context.OrgID), err) log.Error(fmt.Sprintf("Unable to get all users for org %s", p.Context.OrgID), err)
@ -174,7 +172,7 @@ func (p *Persister) GetUsersForOrganization() (users []entity.User, err error) {
// GetFolderUsers returns a slice containing all user records for given folder. // GetFolderUsers returns a slice containing all user records for given folder.
func (p *Persister) GetFolderUsers(folderID string) (users []entity.User, err error) { func (p *Persister) GetFolderUsers(folderID string) (users []entity.User, err error) {
err = Db.Select(&users, err = Db.Select(&users,
"SELECT id, refid, firstname, lastname, email, initials, password, salt, reset, active, created, revised FROM user WHERE refid IN (SELECT userid from labelrole WHERE orgid=? AND labelid=?) ORDER BY firstname,lastname", p.Context.OrgID, folderID) "SELECT id, refid, firstname, lastname, email, initials, password, salt, reset, created, revised FROM user WHERE refid IN (SELECT userid from labelrole WHERE orgid=? AND labelid=?) ORDER BY firstname,lastname", p.Context.OrgID, folderID)
if err != nil { if err != nil {
log.Error(fmt.Sprintf("Unable to get all users for org %s", p.Context.OrgID), err) log.Error(fmt.Sprintf("Unable to get all users for org %s", p.Context.OrgID), err)
@ -190,7 +188,7 @@ func (p *Persister) UpdateUser(user entity.User) (err error) {
user.Email = strings.ToLower(user.Email) user.Email = strings.ToLower(user.Email)
stmt, err := p.Context.Transaction.PrepareNamed( stmt, err := p.Context.Transaction.PrepareNamed(
"UPDATE user SET firstname=:firstname, lastname=:lastname, email=:email, revised=:revised, active=:active, initials=:initials WHERE refid=:refid") "UPDATE user SET firstname=:firstname, lastname=:lastname, email=:email, revised=:revised, initials=:initials WHERE refid=:refid")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -205,8 +203,6 @@ func (p *Persister) UpdateUser(user entity.User) (err error) {
return return
} }
p.Base.Audit(p.Context, "update-user", "", "")
return return
} }
@ -233,8 +229,6 @@ func (p *Persister) UpdateUserPassword(userID, salt, password string) (err error
return er return er
} }
p.Base.Audit(p.Context, "change-password", "", "")
return return
} }
@ -255,8 +249,6 @@ func (p *Persister) DeactiveUser(userID string) (err error) {
return return
} }
p.Base.Audit(p.Context, "deactivate-user", "", "")
return return
} }
@ -285,7 +277,5 @@ func (p *Persister) ForgotUserPassword(email, token string) (err error) {
return return
} }
p.Base.Audit(p.Context, "forgot-password", "", "")
return return
} }

View file

@ -0,0 +1,88 @@
/* community edition */
DROP TABLE IF EXISTS `useractivity`;
CREATE TABLE IF NOT EXISTS `useractivity` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
`userid` CHAR(16) NOT NULL COLLATE utf8_bin,
`labelid` CHAR(16) NOT NULL COLLATE utf8_bin,
`sourceid` CHAR(16) NOT NULL COLLATE utf8_bin,
`sourcetype` INT NOT NULL DEFAULT 0,
`activitytype` INT NOT NULL DEFAULT 0,
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT pk_id PRIMARY KEY (id),
INDEX `idx_activity_orgid` (`orgid` ASC),
INDEX `idx_activity_userid` (`userid` ASC),
INDEX `idx_activity_sourceid` (`sourceid` ASC),
INDEX `idx_activity_activitytype` (`activitytype` ASC))
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
ENGINE = InnoDB;
/* Note:
* - this table replaces the soon-to-be-deprecated audit log table
* - we migrate existing data where there is a migration path */
INSERT INTO useractivity (orgid, userid, labelid, sourceid, sourcetype, activitytype, created)
SELECT a.orgid, a.userid, labelid, a.documentid as sourceid, 2 as sourcetype, 1 as activitytype, a.created
FROM audit a, document d
WHERE action='add-document' AND d.refid=a.documentid;
INSERT INTO useractivity (orgid, userid, labelid, sourceid, sourcetype, activitytype, created)
SELECT a.orgid, a.userid, labelid, a.documentid as sourceid, 2 as sourcetype, 1 as activitytype, a.created
FROM audit a, document d
WHERE action='add-page' AND d.refid=a.documentid;
INSERT INTO useractivity (orgid, userid, labelid, sourceid, sourcetype, activitytype, created)
SELECT a.orgid, a.userid, labelid, a.documentid as sourceid, 2 as sourcetype, 2 as activitytype, a.created
FROM audit a, document d
WHERE action='get-document' AND d.refid=a.documentid;
INSERT INTO useractivity (orgid, userid, labelid, sourceid, sourcetype, activitytype, created)
SELECT a.orgid, a.userid, labelid, a.documentid as sourceid, 2 as sourcetype, 3 as activitytype, a.created
FROM audit a, document d
WHERE action='update-page' AND d.refid=a.documentid;
INSERT INTO useractivity (orgid, userid, labelid, sourceid, sourcetype, activitytype, created)
SELECT a.orgid, a.userid, labelid, a.documentid as sourceid, 2 as sourcetype, 3 as activitytype, a.created
FROM audit a, document d
WHERE action='re-sequence-page' AND d.refid=a.documentid;
INSERT INTO useractivity (orgid, userid, labelid, sourceid, sourcetype, activitytype, created)
SELECT a.orgid, a.userid, labelid, a.documentid as sourceid, 2 as sourcetype, 3 as activitytype, a.created
FROM audit a, document d
WHERE action='re-level-page' AND d.refid=a.documentid;
INSERT INTO useractivity (orgid, userid, labelid, sourceid, sourcetype, activitytype, created)
SELECT a.orgid, a.userid, labelid, a.documentid as sourceid, 2 as sourcetype, 4 as activitytype, a.created
FROM audit a, document d
WHERE action='delete-document' AND d.refid=a.documentid;
INSERT INTO useractivity (orgid, userid, labelid, sourceid, sourcetype, activitytype, created)
SELECT a.orgid, a.userid, labelid, a.documentid as sourceid, 2 as sourcetype, 4 as activitytype, a.created
FROM audit a, document d
WHERE action='remove-page' AND d.refid=a.documentid;
/* enterprise edition */
DROP TABLE IF EXISTS `useraction`;
CREATE TABLE IF NOT EXISTS `useraction` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
`userid` CHAR(16) NOT NULL COLLATE utf8_bin,
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
`requestorid` CHAR(16) NOT NULL COLLATE utf8_bin,
`actiontype` INT NOT NULL DEFAULT 0,
`note` NVARCHAR(2000) NOT NULL DEFAULT '',
`requested` TIMESTAMP NULL,
`due` TIMESTAMP NULL,
`completed` TIMESTAMP NULL,
`iscomplete` BOOL NOT NULL DEFAULT 0,
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT pk_id PRIMARY KEY (id),
INDEX `idx_useraction_refid` (`refid` ASC),
INDEX `idx_useraction_userid` (`userid` ASC),
INDEX `idx_useraction_documentid` (`documentid` ASC),
INDEX `idx_useraction_requestorid` (`requestorid` ASC))
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
ENGINE = InnoDB;

View file

@ -26,7 +26,7 @@ type ProdInfo struct {
// Product returns product edition details // Product returns product edition details
func Product() (p ProdInfo) { func Product() (p ProdInfo) {
p.Major = "0" p.Major = "0"
p.Minor = "41" p.Minor = "42"
p.Patch = "0" p.Patch = "0"
p.Version = fmt.Sprintf("%s.%s.%s", p.Major, p.Minor, p.Patch) p.Version = fmt.Sprintf("%s.%s.%s", p.Major, p.Minor, p.Patch)
p.Edition = "Community" p.Edition = "Community"

38
core/utility/strings.go Normal file
View file

@ -0,0 +1,38 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. 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 <sales@documize.com>.
//
// https://documize.com
package utility
import (
"strings"
)
// Conjoin returns "Suzzane, Fatima and Brian" from string of items.
func Conjoin(conj string, items []string) string {
if len(items) == 0 {
return ""
}
if len(items) == 1 {
return items[0]
}
if len(items) == 2 { // "a and b" not "a, and b"
return items[0] + " " + conj + " " + items[1]
}
sep := ", "
pieces := []string{items[0]}
for _, item := range items[1 : len(items)-1] {
pieces = append(pieces, sep, item)
}
pieces = append(pieces, sep, conj, " ", items[len(items)-1])
return strings.Replace(strings.Join(pieces, ""), ", and ", " and ", 1)
}

File diff suppressed because one or more lines are too long