mirror of
https://github.com/documize/community.git
synced 2025-08-05 05:25:27 +02:00
Provide basis for document lifecycle
This commit is contained in:
parent
d9a9a828ed
commit
ba52dfa11d
20 changed files with 350 additions and 154 deletions
|
@ -13,10 +13,12 @@ package mysql
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/documize/community/core/env"
|
||||
"github.com/documize/community/domain"
|
||||
"github.com/documize/community/domain/store/mysql"
|
||||
"github.com/documize/community/model/activity"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -69,3 +71,12 @@ func (s Scope) GetDocumentActivity(ctx domain.RequestContext, id string) (a []ac
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteDocumentChangeActivity removes all entries for document changes (add, remove, update).
|
||||
func (s Scope) DeleteDocumentChangeActivity(ctx domain.RequestContext, documentID string) (rows int64, err error) {
|
||||
b := mysql.BaseQuery{}
|
||||
rows, err = b.DeleteWhere(ctx.Transaction,
|
||||
fmt.Sprintf("DELETE FROM useractivity WHERE orgid='%s' AND documentid='%s' AND (activitytype=1 OR activitytype=2 OR activitytype=3 OR activitytype=4 OR activitytype=7)", ctx.OrgID, documentID))
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -78,15 +78,18 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: document.LabelID,
|
||||
DocumentID: document.RefID,
|
||||
SourceType: activity.SourceTypeDocument,
|
||||
ActivityType: activity.TypeRead})
|
||||
// draft mode does not record document views
|
||||
if document.Lifecycle == workflow.LifecycleLive {
|
||||
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: document.LabelID,
|
||||
DocumentID: document.RefID,
|
||||
SourceType: activity.SourceTypeDocument,
|
||||
ActivityType: activity.TypeRead})
|
||||
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
h.Runtime.Log.Error(method, err)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
h.Runtime.Log.Error(method, err)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
@ -136,6 +139,9 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// get user permissions
|
||||
viewDrafts := permission.CanViewDrafts(ctx, *h.Store, spaceID)
|
||||
|
||||
// get complete list of documents
|
||||
documents, err := h.Store.Document.GetBySpace(ctx, spaceID)
|
||||
if err != nil {
|
||||
|
@ -143,10 +149,8 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
|
|||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
if len(documents) == 0 {
|
||||
documents = []doc.Document{}
|
||||
}
|
||||
|
||||
// sort by title
|
||||
sort.Sort(doc.ByTitle(documents))
|
||||
|
||||
// remove documents that cannot be seen due to lack of
|
||||
|
@ -158,6 +162,17 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
|
|||
for _, doc := range documents {
|
||||
hasCategory := false
|
||||
canSeeCategory := false
|
||||
skip := false
|
||||
|
||||
// drafts included if user can see them
|
||||
if doc.Lifecycle == workflow.LifecycleDraft && !viewDrafts {
|
||||
skip = true
|
||||
}
|
||||
|
||||
// archived never included
|
||||
if doc.Lifecycle == workflow.LifecycleArchived {
|
||||
skip = true
|
||||
}
|
||||
|
||||
OUTER:
|
||||
|
||||
|
@ -173,7 +188,7 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
if !hasCategory || canSeeCategory {
|
||||
if !skip && (!hasCategory || canSeeCategory) {
|
||||
filtered = append(filtered, doc)
|
||||
}
|
||||
}
|
||||
|
@ -248,9 +263,9 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
h.Store.Audit.Record(ctx, audit.EventTypeDocumentUpdate)
|
||||
|
||||
a, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
||||
|
||||
// Live document indexed for search
|
||||
if d.Lifecycle == workflow.LifecycleLive {
|
||||
a, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
||||
go h.Indexer.IndexDocument(ctx, d, a)
|
||||
} else {
|
||||
go h.Indexer.DeleteDocument(ctx, d.RefID)
|
||||
|
@ -332,11 +347,14 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
h.Store.Link.MarkOrphanDocumentLink(ctx, documentID)
|
||||
h.Store.Link.DeleteSourceDocumentLinks(ctx, documentID)
|
||||
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: documentID,
|
||||
SourceType: activity.SourceTypeDocument,
|
||||
ActivityType: activity.TypeDeleted})
|
||||
// Draft actions are not logged
|
||||
if doc.Lifecycle == workflow.LifecycleLive {
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: documentID,
|
||||
SourceType: activity.SourceTypeDocument,
|
||||
ActivityType: activity.TypeDeleted})
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
||||
|
@ -417,6 +435,12 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// Don't serve archived document
|
||||
if document.Lifecycle == workflow.LifecycleArchived {
|
||||
response.WriteForbiddenError(w)
|
||||
return
|
||||
}
|
||||
|
||||
// permissions
|
||||
perms, err := h.Store.Permission.GetUserSpacePermissions(ctx, document.LabelID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
|
@ -474,15 +498,17 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: document.LabelID,
|
||||
DocumentID: document.RefID,
|
||||
SourceType: activity.SourceTypeDocument,
|
||||
ActivityType: activity.TypeRead})
|
||||
if document.Lifecycle == workflow.LifecycleLive {
|
||||
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: document.LabelID,
|
||||
DocumentID: document.RefID,
|
||||
SourceType: activity.SourceTypeDocument,
|
||||
ActivityType: activity.TypeRead})
|
||||
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
h.Runtime.Log.Error(method, err)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
h.Runtime.Log.Error(method, err)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
|
|
@ -164,12 +164,15 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|||
h.Store.Block.IncrementUsage(ctx, model.Page.BlockID)
|
||||
}
|
||||
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: model.Page.DocumentID,
|
||||
PageID: model.Page.RefID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeCreated})
|
||||
// Draft actions are not logged
|
||||
if doc.Lifecycle == workflow.LifecycleLive {
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: model.Page.DocumentID,
|
||||
PageID: model.Page.RefID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeCreated})
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
||||
|
@ -433,12 +436,15 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: model.Page.DocumentID,
|
||||
PageID: model.Page.RefID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeEdited})
|
||||
// Draft edits are not logged
|
||||
if doc.Lifecycle == workflow.LifecycleLive {
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: model.Page.DocumentID,
|
||||
PageID: model.Page.RefID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeEdited})
|
||||
}
|
||||
|
||||
h.Store.Audit.Record(ctx, audit.EventTypeSectionUpdate)
|
||||
|
||||
|
@ -562,12 +568,15 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: documentID,
|
||||
PageID: pageID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeDeleted})
|
||||
// Draft actions are not logged
|
||||
if doc.Lifecycle == workflow.LifecycleLive {
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: documentID,
|
||||
PageID: pageID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeDeleted})
|
||||
}
|
||||
|
||||
go h.Indexer.DeleteContent(ctx, pageID)
|
||||
|
||||
|
@ -675,12 +684,15 @@ func (h *Handler) DeletePages(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
h.Store.Page.DeletePageRevisions(ctx, page.PageID)
|
||||
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: documentID,
|
||||
PageID: page.PageID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeDeleted})
|
||||
// Draft actions are not logged
|
||||
if doc.Lifecycle == workflow.LifecycleLive {
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: documentID,
|
||||
PageID: page.PageID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeDeleted})
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
@ -940,13 +952,15 @@ func (h *Handler) Copy(w http.ResponseWriter, r *http.Request) {
|
|||
h.Store.Block.IncrementUsage(ctx, model.Page.BlockID)
|
||||
}
|
||||
|
||||
// Log action against target document
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: targetID,
|
||||
PageID: newPageID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeCreated})
|
||||
// Log t actions are not logged
|
||||
if doc.Lifecycle == workflow.LifecycleLive {
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: targetID,
|
||||
PageID: newPageID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeCreated})
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
||||
|
@ -1189,12 +1203,15 @@ func (h *Handler) Rollback(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: p.DocumentID,
|
||||
PageID: p.RefID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeReverted})
|
||||
// Draft actions are not logged
|
||||
if doc.Lifecycle == workflow.LifecycleLive {
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: p.DocumentID,
|
||||
PageID: p.RefID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeReverted})
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
||||
|
|
|
@ -165,6 +165,25 @@ func CanViewSpace(ctx domain.RequestContext, s domain.Store, spaceID string) boo
|
|||
return false
|
||||
}
|
||||
|
||||
// CanViewDrafts returns if the user has permission to view drafts in space.
|
||||
func CanViewDrafts(ctx domain.RequestContext, s domain.Store, spaceID string) bool {
|
||||
roles, err := s.Permission.GetUserSpacePermissions(ctx, spaceID)
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, role := range roles {
|
||||
if role.RefID == spaceID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow &&
|
||||
pm.ContainsPermission(role.Action, pm.DocumentLifecycle) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasPermission returns if user can perform specified actions.
|
||||
func HasPermission(ctx domain.RequestContext, s domain.Store, spaceID string, actions ...pm.Action) bool {
|
||||
roles, err := s.Permission.GetUserSpacePermissions(ctx, spaceID)
|
||||
|
|
|
@ -217,6 +217,7 @@ type LinkStorer interface {
|
|||
type ActivityStorer interface {
|
||||
RecordUserActivity(ctx RequestContext, activity activity.UserActivity) (err error)
|
||||
GetDocumentActivity(ctx RequestContext, id string) (a []activity.DocumentActivity, err error)
|
||||
DeleteDocumentChangeActivity(ctx RequestContext, id string) (rows int64, err error)
|
||||
}
|
||||
|
||||
// SearchStorer defines required methods for persisting search queries
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue