1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-21 14:19:43 +02:00

WIP document versioning

This commit is contained in:
Harvey Kandola 2018-03-16 18:27:33 +00:00
parent ba52dfa11d
commit 089457f16e
6 changed files with 222 additions and 67 deletions

147
domain/document/document.go Normal file
View file

@ -0,0 +1,147 @@
// 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 document
import (
"github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain"
"github.com/documize/community/model/category"
"github.com/documize/community/model/doc"
"github.com/documize/community/model/page"
"github.com/documize/community/model/workflow"
"github.com/pkg/errors"
)
// FilterCategoryProtected removes documents that cannot be seen by user due to
// document cateogory viewing permissions.
func FilterCategoryProtected(docs []doc.Document, cats []category.Category, members []category.Member, viewDrafts bool) (filtered []doc.Document) {
filtered = []doc.Document{}
for _, doc := range docs {
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:
for _, m := range members {
if m.DocumentID == doc.RefID {
hasCategory = true
for _, cat := range cats {
if cat.RefID == m.CategoryID {
canSeeCategory = true
continue OUTER
}
}
}
}
if !skip && (!hasCategory || canSeeCategory) {
filtered = append(filtered, doc)
}
}
return
}
// CopyDocument clones an existing document
func CopyDocument(ctx domain.RequestContext, s domain.Store, documentID string) (newDocumentID string, err error) {
doc, err := s.Document.Get(ctx, documentID)
if err != nil {
err = errors.Wrap(err, "unable to fetch existing document")
return
}
newDocumentID = uniqueid.Generate()
doc.RefID = newDocumentID
doc.ID = 0
doc.Versioned = false
doc.VersionID = ""
doc.GroupID = ""
doc.Template = false
// Duplicate pages and associated meta
pages, err := s.Page.GetPages(ctx, documentID)
if err != nil {
err = errors.Wrap(err, "unable to get existing pages")
return
}
var pageModel []page.NewPage
for _, p := range pages {
p.DocumentID = newDocumentID
p.ID = 0
meta, err2 := s.Page.GetPageMeta(ctx, p.RefID)
if err2 != nil {
err = errors.Wrap(err, "unable to get existing pages meta")
return
}
pageID := uniqueid.Generate()
p.RefID = pageID
meta.PageID = pageID
meta.DocumentID = newDocumentID
m := page.NewPage{}
m.Page = p
m.Meta = meta
pageModel = append(pageModel, m)
}
// Duplicate attachments
attachments, _ := s.Attachment.GetAttachments(ctx, documentID)
for i, a := range attachments {
a.DocumentID = newDocumentID
a.RefID = uniqueid.Generate()
a.ID = 0
attachments[i] = a
}
// Now create the template: document, attachments, pages and their meta
err = s.Document.Add(ctx, doc)
if err != nil {
err = errors.Wrap(err, "unable to add copied document")
return
}
for _, a := range attachments {
err = s.Attachment.Add(ctx, a)
if err != nil {
err = errors.Wrap(err, "unable to add copied attachment")
return
}
}
for _, m := range pageModel {
err = s.Page.Add(ctx, m)
if err != nil {
err = errors.Wrap(err, "unable to add copied page")
return
}
}
return
}

View file

@ -153,45 +153,10 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
// sort by title
sort.Sort(doc.ByTitle(documents))
// remove documents that cannot be seen due to lack of
// category view/access permission
filtered := []doc.Document{}
// remove documents that cannot be seen due to lack of category view/access permission
cats, err := h.Store.Category.GetBySpace(ctx, spaceID)
members, err := h.Store.Category.GetSpaceCategoryMembership(ctx, spaceID)
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:
for _, m := range members {
if m.DocumentID == doc.RefID {
hasCategory = true
for _, cat := range cats {
if cat.RefID == m.CategoryID {
canSeeCategory = true
continue OUTER
}
}
}
}
if !skip && (!hasCategory || canSeeCategory) {
filtered = append(filtered, doc)
}
}
filtered := FilterCategoryProtected(documents, cats, members, viewDrafts)
response.WriteJSON(w, filtered)
}
@ -259,6 +224,24 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
return
}
// Record document being marked as archived
if d.Lifecycle != oldDoc.Lifecycle && d.Lifecycle == workflow.LifecycleArchived {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
LabelID: d.LabelID,
DocumentID: documentID,
SourceType: activity.SourceTypeDocument,
ActivityType: activity.TypeArchived})
}
// Record document being marked as draft
if d.Lifecycle != oldDoc.Lifecycle && d.Lifecycle == workflow.LifecycleDraft {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
LabelID: d.LabelID,
DocumentID: documentID,
SourceType: activity.SourceTypeDocument,
ActivityType: activity.TypeDraft})
}
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeDocumentUpdate)

View file

@ -175,7 +175,7 @@ func CanViewDrafts(ctx domain.RequestContext, s domain.Store, spaceID string) bo
return false
}
for _, role := range roles {
if role.RefID == spaceID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow &&
if role.OrgID == ctx.OrgID && role.RefID == spaceID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow &&
pm.ContainsPermission(role.Action, pm.DocumentLifecycle) {
return true
}
@ -184,6 +184,25 @@ func CanViewDrafts(ctx domain.RequestContext, s domain.Store, spaceID string) bo
return false
}
// CanManageVersion returns if the user has permission to manage versions in space.
func CanManageVersion(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.OrgID == ctx.OrgID && role.RefID == spaceID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow &&
pm.ContainsPermission(role.Action, pm.DocumentVersion) {
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)

View file

@ -28,9 +28,9 @@ export default Model.extend({
approval: attr('number', { defaultValue: 0 }),
lifecycle: attr('number', { defaultValue: 1 }),
versioned: attr('boolean'),
versionID: attr('string'),
versionId: attr('string'),
versionOrder: attr('number', { defaultValue: 0 }),
groupID: attr('string'),
groupId: attr('string'),
// client-side property
selected: attr('boolean', { defaultValue: false }),

View file

@ -160,7 +160,7 @@
scrollOffsetY = vy ? vy * speed : 0;
scrollOffsetX = vx ? vx * speed : 0;
if ('function' === typeof(scrollCustomFn)) {
if ('function' === typeof (scrollCustomFn)) {
return scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt);
}
@ -202,7 +202,7 @@
var originalGroup = options.group;
if (!originalGroup || typeof originalGroup != 'object') {
originalGroup = {name: originalGroup};
originalGroup = { name: originalGroup };
}
group.name = originalGroup.name;
@ -261,7 +261,7 @@
fallbackClass: 'sortable-fallback',
fallbackOnBody: false,
fallbackTolerance: 0,
fallbackOffset: {x: 0, y: 0}
fallbackOffset: { x: 0, y: 0 }
};
@ -963,7 +963,7 @@
this._nulling();
},
_nulling: function() {
_nulling: function () {
rootEl =
dragEl =
parentEl =
@ -1455,7 +1455,7 @@
};
}
}));
} catch (err) {}
} catch (err) { }
// Export utils
Sortable.utils = {
@ -1489,3 +1489,6 @@
Sortable.version = '1.6.0';
return Sortable;
});
// http://rubaxa.github.io/Sortable/

View file

@ -95,6 +95,9 @@ const (
// TypeSentSecureLink records user sending secure document link to email address(es)
TypeSentSecureLink Type = 12
// TypeDraft records user marking space/document as draft
TypeDraft Type = 13
)
// TypeName returns one-work descriptor for activity type