mirror of
https://github.com/documize/community.git
synced 2025-07-20 21:59:42 +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 (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/documize/community/core/env"
|
"github.com/documize/community/core/env"
|
||||||
"github.com/documize/community/domain"
|
"github.com/documize/community/domain"
|
||||||
|
"github.com/documize/community/domain/store/mysql"
|
||||||
"github.com/documize/community/model/activity"
|
"github.com/documize/community/model/activity"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -69,3 +71,12 @@ func (s Scope) GetDocumentActivity(ctx domain.RequestContext, id string) (a []ac
|
||||||
|
|
||||||
return
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
// draft mode does not record document views
|
||||||
LabelID: document.LabelID,
|
if document.Lifecycle == workflow.LifecycleLive {
|
||||||
DocumentID: document.RefID,
|
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||||
SourceType: activity.SourceTypeDocument,
|
LabelID: document.LabelID,
|
||||||
ActivityType: activity.TypeRead})
|
DocumentID: document.RefID,
|
||||||
|
SourceType: activity.SourceTypeDocument,
|
||||||
|
ActivityType: activity.TypeRead})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Transaction.Rollback()
|
ctx.Transaction.Rollback()
|
||||||
h.Runtime.Log.Error(method, err)
|
h.Runtime.Log.Error(method, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Transaction.Commit()
|
ctx.Transaction.Commit()
|
||||||
|
@ -136,6 +139,9 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get user permissions
|
||||||
|
viewDrafts := permission.CanViewDrafts(ctx, *h.Store, spaceID)
|
||||||
|
|
||||||
// get complete list of documents
|
// get complete list of documents
|
||||||
documents, err := h.Store.Document.GetBySpace(ctx, spaceID)
|
documents, err := h.Store.Document.GetBySpace(ctx, spaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -143,10 +149,8 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
|
||||||
h.Runtime.Log.Error(method, err)
|
h.Runtime.Log.Error(method, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(documents) == 0 {
|
|
||||||
documents = []doc.Document{}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// sort by title
|
||||||
sort.Sort(doc.ByTitle(documents))
|
sort.Sort(doc.ByTitle(documents))
|
||||||
|
|
||||||
// remove documents that cannot be seen due to lack of
|
// 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 {
|
for _, doc := range documents {
|
||||||
hasCategory := false
|
hasCategory := false
|
||||||
canSeeCategory := 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:
|
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)
|
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)
|
h.Store.Audit.Record(ctx, audit.EventTypeDocumentUpdate)
|
||||||
|
|
||||||
a, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
// Live document indexed for search
|
||||||
|
|
||||||
if d.Lifecycle == workflow.LifecycleLive {
|
if d.Lifecycle == workflow.LifecycleLive {
|
||||||
|
a, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
||||||
go h.Indexer.IndexDocument(ctx, d, a)
|
go h.Indexer.IndexDocument(ctx, d, a)
|
||||||
} else {
|
} else {
|
||||||
go h.Indexer.DeleteDocument(ctx, d.RefID)
|
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.MarkOrphanDocumentLink(ctx, documentID)
|
||||||
h.Store.Link.DeleteSourceDocumentLinks(ctx, documentID)
|
h.Store.Link.DeleteSourceDocumentLinks(ctx, documentID)
|
||||||
|
|
||||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
// Draft actions are not logged
|
||||||
LabelID: doc.LabelID,
|
if doc.Lifecycle == workflow.LifecycleLive {
|
||||||
DocumentID: documentID,
|
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||||
SourceType: activity.SourceTypeDocument,
|
LabelID: doc.LabelID,
|
||||||
ActivityType: activity.TypeDeleted})
|
DocumentID: documentID,
|
||||||
|
SourceType: activity.SourceTypeDocument,
|
||||||
|
ActivityType: activity.TypeDeleted})
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Transaction.Commit()
|
ctx.Transaction.Commit()
|
||||||
|
|
||||||
|
@ -417,6 +435,12 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't serve archived document
|
||||||
|
if document.Lifecycle == workflow.LifecycleArchived {
|
||||||
|
response.WriteForbiddenError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// permissions
|
// permissions
|
||||||
perms, err := h.Store.Permission.GetUserSpacePermissions(ctx, document.LabelID)
|
perms, err := h.Store.Permission.GetUserSpacePermissions(ctx, document.LabelID)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
@ -474,15 +498,17 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
if document.Lifecycle == workflow.LifecycleLive {
|
||||||
LabelID: document.LabelID,
|
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||||
DocumentID: document.RefID,
|
LabelID: document.LabelID,
|
||||||
SourceType: activity.SourceTypeDocument,
|
DocumentID: document.RefID,
|
||||||
ActivityType: activity.TypeRead})
|
SourceType: activity.SourceTypeDocument,
|
||||||
|
ActivityType: activity.TypeRead})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Transaction.Rollback()
|
ctx.Transaction.Rollback()
|
||||||
h.Runtime.Log.Error(method, err)
|
h.Runtime.Log.Error(method, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Transaction.Commit()
|
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.Block.IncrementUsage(ctx, model.Page.BlockID)
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
// Draft actions are not logged
|
||||||
LabelID: doc.LabelID,
|
if doc.Lifecycle == workflow.LifecycleLive {
|
||||||
DocumentID: model.Page.DocumentID,
|
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||||
PageID: model.Page.RefID,
|
LabelID: doc.LabelID,
|
||||||
SourceType: activity.SourceTypePage,
|
DocumentID: model.Page.DocumentID,
|
||||||
ActivityType: activity.TypeCreated})
|
PageID: model.Page.RefID,
|
||||||
|
SourceType: activity.SourceTypePage,
|
||||||
|
ActivityType: activity.TypeCreated})
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Transaction.Commit()
|
ctx.Transaction.Commit()
|
||||||
|
|
||||||
|
@ -433,12 +436,15 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
// Draft edits are not logged
|
||||||
LabelID: doc.LabelID,
|
if doc.Lifecycle == workflow.LifecycleLive {
|
||||||
DocumentID: model.Page.DocumentID,
|
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||||
PageID: model.Page.RefID,
|
LabelID: doc.LabelID,
|
||||||
SourceType: activity.SourceTypePage,
|
DocumentID: model.Page.DocumentID,
|
||||||
ActivityType: activity.TypeEdited})
|
PageID: model.Page.RefID,
|
||||||
|
SourceType: activity.SourceTypePage,
|
||||||
|
ActivityType: activity.TypeEdited})
|
||||||
|
}
|
||||||
|
|
||||||
h.Store.Audit.Record(ctx, audit.EventTypeSectionUpdate)
|
h.Store.Audit.Record(ctx, audit.EventTypeSectionUpdate)
|
||||||
|
|
||||||
|
@ -562,12 +568,15 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
// Draft actions are not logged
|
||||||
LabelID: doc.LabelID,
|
if doc.Lifecycle == workflow.LifecycleLive {
|
||||||
DocumentID: documentID,
|
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||||
PageID: pageID,
|
LabelID: doc.LabelID,
|
||||||
SourceType: activity.SourceTypePage,
|
DocumentID: documentID,
|
||||||
ActivityType: activity.TypeDeleted})
|
PageID: pageID,
|
||||||
|
SourceType: activity.SourceTypePage,
|
||||||
|
ActivityType: activity.TypeDeleted})
|
||||||
|
}
|
||||||
|
|
||||||
go h.Indexer.DeleteContent(ctx, pageID)
|
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.Page.DeletePageRevisions(ctx, page.PageID)
|
||||||
|
|
||||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
// Draft actions are not logged
|
||||||
LabelID: doc.LabelID,
|
if doc.Lifecycle == workflow.LifecycleLive {
|
||||||
DocumentID: documentID,
|
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||||
PageID: page.PageID,
|
LabelID: doc.LabelID,
|
||||||
SourceType: activity.SourceTypePage,
|
DocumentID: documentID,
|
||||||
ActivityType: activity.TypeDeleted})
|
PageID: page.PageID,
|
||||||
|
SourceType: activity.SourceTypePage,
|
||||||
|
ActivityType: activity.TypeDeleted})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Transaction.Commit()
|
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)
|
h.Store.Block.IncrementUsage(ctx, model.Page.BlockID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log action against target document
|
// Log t actions are not logged
|
||||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
if doc.Lifecycle == workflow.LifecycleLive {
|
||||||
LabelID: doc.LabelID,
|
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||||
DocumentID: targetID,
|
LabelID: doc.LabelID,
|
||||||
PageID: newPageID,
|
DocumentID: targetID,
|
||||||
SourceType: activity.SourceTypePage,
|
PageID: newPageID,
|
||||||
ActivityType: activity.TypeCreated})
|
SourceType: activity.SourceTypePage,
|
||||||
|
ActivityType: activity.TypeCreated})
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Transaction.Commit()
|
ctx.Transaction.Commit()
|
||||||
|
|
||||||
|
@ -1189,12 +1203,15 @@ func (h *Handler) Rollback(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
// Draft actions are not logged
|
||||||
LabelID: doc.LabelID,
|
if doc.Lifecycle == workflow.LifecycleLive {
|
||||||
DocumentID: p.DocumentID,
|
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||||
PageID: p.RefID,
|
LabelID: doc.LabelID,
|
||||||
SourceType: activity.SourceTypePage,
|
DocumentID: p.DocumentID,
|
||||||
ActivityType: activity.TypeReverted})
|
PageID: p.RefID,
|
||||||
|
SourceType: activity.SourceTypePage,
|
||||||
|
ActivityType: activity.TypeReverted})
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Transaction.Commit()
|
ctx.Transaction.Commit()
|
||||||
|
|
||||||
|
|
|
@ -165,6 +165,25 @@ func CanViewSpace(ctx domain.RequestContext, s domain.Store, spaceID string) boo
|
||||||
return false
|
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.
|
// HasPermission returns if user can perform specified actions.
|
||||||
func HasPermission(ctx domain.RequestContext, s domain.Store, spaceID string, actions ...pm.Action) bool {
|
func HasPermission(ctx domain.RequestContext, s domain.Store, spaceID string, actions ...pm.Action) bool {
|
||||||
roles, err := s.Permission.GetUserSpacePermissions(ctx, spaceID)
|
roles, err := s.Permission.GetUserSpacePermissions(ctx, spaceID)
|
||||||
|
|
|
@ -217,6 +217,7 @@ type LinkStorer interface {
|
||||||
type ActivityStorer interface {
|
type ActivityStorer interface {
|
||||||
RecordUserActivity(ctx RequestContext, activity activity.UserActivity) (err error)
|
RecordUserActivity(ctx RequestContext, activity activity.UserActivity) (err error)
|
||||||
GetDocumentActivity(ctx RequestContext, id string) (a []activity.DocumentActivity, 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
|
// SearchStorer defines required methods for persisting search queries
|
||||||
|
|
|
@ -9,18 +9,16 @@
|
||||||
//
|
//
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
import Service, { inject as service } from '@ember/service';
|
import AuthProvider from '../../mixins/auth';
|
||||||
|
import ModalMixin from '../../mixins/modal';
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
export default Service.extend({
|
export default Component.extend(AuthProvider, ModalMixin, {
|
||||||
ajax: service(),
|
|
||||||
|
|
||||||
getDocumentSummary(documentId) {
|
init() {
|
||||||
return this.get('ajax').request(`activity/document/${documentId}`, {
|
this._super(...arguments);
|
||||||
method: "GET"
|
},
|
||||||
}).then((response) => {
|
|
||||||
return response;
|
actions: {
|
||||||
}).catch(() => {
|
|
||||||
return [];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -13,11 +13,12 @@ import $ from 'jquery';
|
||||||
import { computed } from '@ember/object';
|
import { computed } from '@ember/object';
|
||||||
import { notEmpty } from '@ember/object/computed';
|
import { notEmpty } from '@ember/object/computed';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import Component from '@ember/component';
|
|
||||||
import { A } from "@ember/array"
|
import { A } from "@ember/array"
|
||||||
import { schedule } from '@ember/runloop';
|
import { schedule } from '@ember/runloop';
|
||||||
|
import ModalMixin from '../../mixins/modal';
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend(ModalMixin, {
|
||||||
documentService: service('document'),
|
documentService: service('document'),
|
||||||
categoryService: service('category'),
|
categoryService: service('category'),
|
||||||
sessionService: service('session'),
|
sessionService: service('session'),
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import Component from '@ember/component';
|
|
||||||
import AuthMixin from '../../mixins/auth';
|
import AuthMixin from '../../mixins/auth';
|
||||||
import TooltipMixin from '../../mixins/tooltip';
|
import TooltipMixin from '../../mixins/tooltip';
|
||||||
import ModalMixin from '../../mixins/modal';
|
import ModalMixin from '../../mixins/modal';
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, {
|
export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, {
|
||||||
userSvc: service('user'),
|
userSvc: service('user'),
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||||
//
|
//
|
||||||
// This software (Documize Community Edition) is licensed under
|
// This software (Documize Community Edition) is licensed under
|
||||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||||
//
|
//
|
||||||
// You can operate outside the AGPL restrictions by purchasing
|
// You can operate outside the AGPL restrictions by purchasing
|
||||||
// Documize Enterprise Edition and obtaining a commercial license
|
// Documize Enterprise Edition and obtaining a commercial license
|
||||||
// by contacting <sales@documize.com>.
|
// by contacting <sales@documize.com>.
|
||||||
//
|
//
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
|
@ -15,52 +15,63 @@ import EmberObject from "@ember/object";
|
||||||
// let constants = this.get('constants');
|
// let constants = this.get('constants');
|
||||||
|
|
||||||
let constants = EmberObject.extend({
|
let constants = EmberObject.extend({
|
||||||
// Document
|
// Document
|
||||||
ProtectionType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
ProtectionType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
||||||
None: 0,
|
None: 0,
|
||||||
Lock: 1,
|
Lock: 1,
|
||||||
Review: 2,
|
Review: 2,
|
||||||
|
|
||||||
NoneLabel: 'Changes permitted without approval',
|
NoneLabel: 'Changes permitted without approval',
|
||||||
LockLabel: 'Locked, changes not permitted',
|
LockLabel: 'Locked, changes not permitted',
|
||||||
ReviewLabel: 'Changes require approval before publication'
|
ReviewLabel: 'Changes require approval before publication'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Document
|
// Document
|
||||||
ApprovalType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
ApprovalType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
||||||
None: 0,
|
None: 0,
|
||||||
Anybody: 1,
|
Anybody: 1,
|
||||||
Majority: 2,
|
Majority: 2,
|
||||||
Unanimous: 3,
|
Unanimous: 3,
|
||||||
|
|
||||||
AnybodyLabel: 'Approval required from any approver',
|
AnybodyLabel: 'Approval required from any approver',
|
||||||
MajorityLabel: 'Majority approval required from approvers',
|
MajorityLabel: 'Majority approval required from approvers',
|
||||||
UnanimousLabel: 'Unanimous approval required from all approvers'
|
UnanimousLabel: 'Unanimous approval required from all approvers'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Section
|
// Section
|
||||||
ChangeState: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
ChangeState: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
||||||
Published: 0,
|
Published: 0,
|
||||||
Pending: 1,
|
Pending: 1,
|
||||||
UnderReview: 2,
|
UnderReview: 2,
|
||||||
Rejected: 3,
|
Rejected: 3,
|
||||||
PendingNew: 4,
|
PendingNew: 4,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Section
|
// Section
|
||||||
PageType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
PageType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
||||||
Tab: 'tab',
|
Tab: 'tab',
|
||||||
Section: 'section'
|
Section: 'section'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Who a permission record relates to
|
// Who a permission record relates to
|
||||||
WhoType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
WhoType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
||||||
User: 'user',
|
User: 'user',
|
||||||
Group: 'role'
|
Group: 'role'
|
||||||
},
|
},
|
||||||
|
|
||||||
EveryoneUserId: "0",
|
EveryoneUserId: "0",
|
||||||
EveryoneUserName: "Everyone"
|
EveryoneUserName: "Everyone",
|
||||||
|
|
||||||
|
// Document
|
||||||
|
Lifecycle: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
||||||
|
Draft: 0,
|
||||||
|
Live: 1,
|
||||||
|
Archived: 2,
|
||||||
|
|
||||||
|
DraftLabel: 'Draft',
|
||||||
|
LiveLabel: 'Live',
|
||||||
|
ArchivedLabel: 'Archived',
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default { constants }
|
export default { constants }
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||||
//
|
//
|
||||||
// This software (Documize Community Edition) is licensed under
|
// This software (Documize Community Edition) is licensed under
|
||||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||||
//
|
//
|
||||||
// You can operate outside the AGPL restrictions by purchasing
|
// You can operate outside the AGPL restrictions by purchasing
|
||||||
// Documize Enterprise Edition and obtaining a commercial license
|
// Documize Enterprise Edition and obtaining a commercial license
|
||||||
// by contacting <sales@documize.com>.
|
// by contacting <sales@documize.com>.
|
||||||
//
|
//
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
export function initialize(application) {
|
export function initialize(application) {
|
||||||
application.inject('route', 'econstants', 'econstants:main');
|
application.inject('route', 'econstants', 'econstants:main');
|
||||||
application.inject('controller', 'econstants', 'econstants:main');
|
application.inject('controller', 'econstants', 'econstants:main');
|
||||||
application.inject('component', 'econstants', 'econstants:main');
|
application.inject('component', 'econstants', 'econstants:main');
|
||||||
application.inject('template', 'econstants', 'econstants:main');
|
application.inject('template', 'econstants', 'econstants:main');
|
||||||
application.inject('service', 'econstants', 'econstants:main');
|
application.inject('service', 'econstants', 'econstants:main');
|
||||||
|
application.inject('model', 'econstants', 'econstants:main');
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'econstants',
|
name: 'econstants',
|
||||||
after: "application",
|
after: "application",
|
||||||
initialize: initialize
|
initialize: initialize
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,10 +10,9 @@
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
import { computed } from '@ember/object';
|
import { computed } from '@ember/object';
|
||||||
import Model from 'ember-data/model';
|
|
||||||
import attr from 'ember-data/attr';
|
import attr from 'ember-data/attr';
|
||||||
import stringUtil from '../utils/string';
|
import stringUtil from '../utils/string';
|
||||||
// import { belongsTo, hasMany } from 'ember-data/relationships';
|
import Model from 'ember-data/model';
|
||||||
|
|
||||||
export default Model.extend({
|
export default Model.extend({
|
||||||
name: attr('string'),
|
name: attr('string'),
|
||||||
|
@ -27,6 +26,11 @@ export default Model.extend({
|
||||||
template: attr('boolean'),
|
template: attr('boolean'),
|
||||||
protection: attr('number', { defaultValue: 0 }),
|
protection: attr('number', { defaultValue: 0 }),
|
||||||
approval: attr('number', { defaultValue: 0 }),
|
approval: attr('number', { defaultValue: 0 }),
|
||||||
|
lifecycle: attr('number', { defaultValue: 1 }),
|
||||||
|
versioned: attr('boolean'),
|
||||||
|
versionID: attr('string'),
|
||||||
|
versionOrder: attr('number', { defaultValue: 0 }),
|
||||||
|
groupID: attr('string'),
|
||||||
|
|
||||||
// client-side property
|
// client-side property
|
||||||
selected: attr('boolean', { defaultValue: false }),
|
selected: attr('boolean', { defaultValue: false }),
|
||||||
|
@ -34,5 +38,24 @@ export default Model.extend({
|
||||||
return stringUtil.makeSlug(this.get('name'));
|
return stringUtil.makeSlug(this.get('name'));
|
||||||
}),
|
}),
|
||||||
created: attr(),
|
created: attr(),
|
||||||
revised: attr()
|
revised: attr(),
|
||||||
|
|
||||||
|
isDraft: computed('lifecycle', function () {
|
||||||
|
let constants = this.get('constants');
|
||||||
|
return this.get('lifecycle') == constants.Lifecycle.Draft;
|
||||||
|
}),
|
||||||
|
|
||||||
|
lifecycleLabel: computed('lifecycle', function () {
|
||||||
|
let constants = this.get('constants');
|
||||||
|
switch (this.get('lifecycle')) {
|
||||||
|
case constants.Lifecycle.Draft:
|
||||||
|
return constants.Lifecycle.DraftLabel;
|
||||||
|
case constants.Lifecycle.Live:
|
||||||
|
return constants.Lifecycle.LiveLabel;
|
||||||
|
case constants.Lifecycle.Archived:
|
||||||
|
return constants.Lifecycle.ArchivedLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,11 +10,10 @@
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
import { computed } from '@ember/object';
|
import { computed } from '@ember/object';
|
||||||
import Model from 'ember-data/model';
|
|
||||||
import attr from 'ember-data/attr';
|
import attr from 'ember-data/attr';
|
||||||
import constants from '../utils/constants';
|
import constants from '../utils/constants';
|
||||||
import stringUtil from '../utils/string';
|
import stringUtil from '../utils/string';
|
||||||
// import { belongsTo, hasMany } from 'ember-data/relationships';
|
import Model from 'ember-data/model';
|
||||||
|
|
||||||
export default Model.extend({
|
export default Model.extend({
|
||||||
name: attr('string'),
|
name: attr('string'),
|
||||||
|
|
17
gui/app/pods/customize/archive/controller.js
Normal file
17
gui/app/pods/customize/archive/controller.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// 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 Controller from '@ember/controller';
|
||||||
|
|
||||||
|
export default Controller.extend({
|
||||||
|
actions: {
|
||||||
|
}
|
||||||
|
});
|
25
gui/app/pods/customize/archive/route.js
Normal file
25
gui/app/pods/customize/archive/route.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// 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 Route from '@ember/routing/route';
|
||||||
|
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
|
||||||
|
|
||||||
|
export default Route.extend(AuthenticatedRouteMixin, {
|
||||||
|
beforeModel() {
|
||||||
|
if (!this.session.isAdmin) {
|
||||||
|
this.transitionTo('auth.login');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
activate() {
|
||||||
|
this.get('browser').setTitle('Archive');
|
||||||
|
}
|
||||||
|
});
|
1
gui/app/pods/customize/archive/template.hbs
Normal file
1
gui/app/pods/customize/archive/template.hbs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{customize/archive-admin}}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{{#toolbar/t-toolbar}}
|
{{#toolbar/t-toolbar}}
|
||||||
{{#toolbar/t-links}}
|
{{#toolbar/t-links}}
|
||||||
{{#link-to "folders" class="link" tagName="li"}}Spaces{{/link-to}}
|
{{#link-to "folders" class="link" tagName="li" }}Spaces{{/link-to}}
|
||||||
{{/toolbar/t-links}}
|
{{/toolbar/t-links}}
|
||||||
{{#toolbar/t-actions}}
|
{{#toolbar/t-actions}}
|
||||||
{{/toolbar/t-actions}}
|
{{/toolbar/t-actions}}
|
||||||
|
@ -12,19 +12,20 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col my-5">
|
<div class="col my-5">
|
||||||
<ul class="tabnav-control">
|
<ul class="tabnav-control">
|
||||||
{{#link-to 'customize.general' activeClass='selected' class="tab" tagName="li"}}General{{/link-to}}
|
{{#link-to 'customize.general' activeClass='selected' class="tab" tagName="li" }}General{{/link-to}}
|
||||||
{{#link-to 'customize.folders' activeClass='selected' class="tab" tagName="li"}}Spaces{{/link-to}}
|
{{#link-to 'customize.folders' activeClass='selected' class="tab" tagName="li" }}Spaces{{/link-to}}
|
||||||
{{#link-to 'customize.groups' activeClass='selected' class="tab" tagName="li"}}Groups{{/link-to}}
|
{{#link-to 'customize.groups' activeClass='selected' class="tab" tagName="li" }}Groups{{/link-to}}
|
||||||
{{#link-to 'customize.users' activeClass='selected' class="tab" tagName="li"}}Users{{/link-to}}
|
{{#link-to 'customize.users' activeClass='selected' class="tab" tagName="li" }}Users{{/link-to}}
|
||||||
{{#if session.isGlobalAdmin}}
|
{{#if session.isGlobalAdmin}}
|
||||||
{{#link-to 'customize.smtp' activeClass='selected' class="tab" tagName="li"}}SMTP{{/link-to}}
|
{{#link-to 'customize.smtp' activeClass='selected' class="tab" tagName="li" }}SMTP{{/link-to}}
|
||||||
{{#link-to 'customize.license' activeClass='selected' class="tab" tagName="li"}}License{{/link-to}}
|
{{#link-to 'customize.license' activeClass='selected' class="tab" tagName="li" }}License{{/link-to}}
|
||||||
{{#link-to 'customize.auth' activeClass='selected' class="tab" tagName="li"}}Authentication{{/link-to}}
|
{{#link-to 'customize.auth' activeClass='selected' class="tab" tagName="li" }}Authentication{{/link-to}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#link-to 'customize.archive' activeClass='selected' class="tab" tagName="li" }}Archive{{/link-to}}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 margin-bottom-100">
|
<div class="mt-4 margin-bottom-100">
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -16,7 +16,7 @@ var Router = EmberRouter.extend({
|
||||||
location: config.locationType
|
location: config.locationType
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Router.map(function() {
|
export default Router.map(function () {
|
||||||
this.route('folders', {
|
this.route('folders', {
|
||||||
path: '/'
|
path: '/'
|
||||||
});
|
});
|
||||||
|
@ -30,7 +30,7 @@ export default Router.map(function() {
|
||||||
{
|
{
|
||||||
path: 's/:folder_id/:folder_slug'
|
path: 's/:folder_id/:folder_slug'
|
||||||
},
|
},
|
||||||
function() {
|
function () {
|
||||||
this.route('category', {
|
this.route('category', {
|
||||||
path: 'category'
|
path: 'category'
|
||||||
});
|
});
|
||||||
|
@ -42,7 +42,7 @@ export default Router.map(function() {
|
||||||
{
|
{
|
||||||
path: 's/:folder_id/:folder_slug/d/:document_id/:document_slug'
|
path: 's/:folder_id/:folder_slug/d/:document_id/:document_slug'
|
||||||
},
|
},
|
||||||
function() {
|
function () {
|
||||||
this.route('section', {
|
this.route('section', {
|
||||||
path: 'section/:page_id'
|
path: 'section/:page_id'
|
||||||
});
|
});
|
||||||
|
@ -57,7 +57,7 @@ export default Router.map(function() {
|
||||||
{
|
{
|
||||||
path: 'settings'
|
path: 'settings'
|
||||||
},
|
},
|
||||||
function() {
|
function () {
|
||||||
this.route('general', {
|
this.route('general', {
|
||||||
path: 'general'
|
path: 'general'
|
||||||
});
|
});
|
||||||
|
@ -82,6 +82,9 @@ export default Router.map(function() {
|
||||||
this.route('audit', {
|
this.route('audit', {
|
||||||
path: 'audit'
|
path: 'audit'
|
||||||
});
|
});
|
||||||
|
this.route('archive', {
|
||||||
|
path: 'archive'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -98,7 +101,7 @@ export default Router.map(function() {
|
||||||
{
|
{
|
||||||
path: 'auth'
|
path: 'auth'
|
||||||
},
|
},
|
||||||
function() {
|
function () {
|
||||||
this.route('sso', {
|
this.route('sso', {
|
||||||
path: 'sso/:token'
|
path: 'sso/:token'
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
margin: 0 0 0 10px;
|
margin: 0 0 0 10px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
> .email {
|
> .email {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
color: $color-off-black;
|
color: $color-off-black;
|
||||||
|
@ -124,7 +124,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .smtp-failure {
|
> .smtp-failure {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -136,4 +136,16 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: $color-green;
|
color: $color-green;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .archive-admin {
|
||||||
|
> .list {
|
||||||
|
> .item {
|
||||||
|
margin: 15px 0;
|
||||||
|
padding: 15px;
|
||||||
|
@include ease-in();
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: $color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
25
gui/app/templates/components/customize/archive-admin.hbs
Normal file
25
gui/app/templates/components/customize/archive-admin.hbs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="view-customize">
|
||||||
|
<h1 class="admin-heading">Archive</h1>
|
||||||
|
<h2 class="sub-heading">Mark as live documents currently marked as archived</h2>
|
||||||
|
|
||||||
|
<div class="archive-admin my-5">
|
||||||
|
<ul class="list">
|
||||||
|
{{#each docs as |doc|}}
|
||||||
|
<li class="item row">
|
||||||
|
<div class="col-12 col-sm-10">{{doc.name}}</div>
|
||||||
|
<div class="col-12 col-sm-2 float-right">
|
||||||
|
<button class="btn btn-success" {{action 'onMarkLive' doc.id}}>Unarchive</button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{#if (eq docs.length 0)}}
|
||||||
|
<p>Nothing found</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -7,6 +7,9 @@
|
||||||
<div class="title">{{ document.name }}</div>
|
<div class="title">{{ document.name }}</div>
|
||||||
<div class="snippet">{{ document.excerpt }}</div>
|
<div class="snippet">{{ document.excerpt }}</div>
|
||||||
{{folder/document-tags documentTags=document.tags}}
|
{{folder/document-tags documentTags=document.tags}}
|
||||||
|
{{#if (not-eq document.lifecycle constants.Lifecycle.Live)}}
|
||||||
|
<button type="button" class="mt-3 btn btn-warning text-uppercase font-weight-bold">{{document.lifecycleLabel}}</button>
|
||||||
|
{{/if}}
|
||||||
{{/link-to}}
|
{{/link-to}}
|
||||||
|
|
||||||
{{#if hasDocumentActions}}
|
{{#if hasDocumentActions}}
|
||||||
|
@ -39,11 +42,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#ui/ui-dialog title="Delete Documents" confirmCaption="Delete" buttonType="btn-danger" show=showDeleteDialog onAction=(action 'onDeleteDocuments')}}
|
{{#ui/ui-dialog title="Delete Documents" confirmCaption="Delete" buttonType="btn-danger" show=showDeleteDialog onAction=(action
|
||||||
|
'onDeleteDocuments')}}
|
||||||
<p>Are you sure you want to delete {{selectedDocuments.length}} {{selectedCaption}}?</p>
|
<p>Are you sure you want to delete {{selectedDocuments.length}} {{selectedCaption}}?</p>
|
||||||
{{/ui/ui-dialog}}
|
{{/ui/ui-dialog}}
|
||||||
|
|
||||||
{{#ui/ui-dialog title="Move Documents" confirmCaption="Move" buttonType="btn-success" show=showMoveDialog onAction=(action 'onMoveDocuments')}}
|
{{#ui/ui-dialog title="Move Documents" confirmCaption="Move" buttonType="btn-success" show=showMoveDialog onAction=(action
|
||||||
|
'onMoveDocuments')}}
|
||||||
<p>Select space for {{selectedDocuments.length}} {{selectedCaption}}</p>
|
<p>Select space for {{selectedDocuments.length}} {{selectedCaption}}</p>
|
||||||
{{ui/ui-list-picker items=moveOptions nameField='name' singleSelect=true}}
|
{{ui/ui-list-picker items=moveOptions nameField='name' singleSelect=true}}
|
||||||
{{/ui/ui-dialog}}
|
{{/ui/ui-dialog}}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue