1
0
Fork 0
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:
Harvey Kandola 2018-03-15 17:11:53 +00:00
parent d9a9a828ed
commit ba52dfa11d
20 changed files with 350 additions and 154 deletions

View file

@ -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
}

View file

@ -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()

View file

@ -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()

View file

@ -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)

View file

@ -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

View file

@ -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 [];
});
} }
}); });

View file

@ -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'),

View file

@ -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'),

View file

@ -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 }

View file

@ -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
}; };

View file

@ -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 '';
}),
}); });

View file

@ -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'),

View 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: {
}
});

View 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');
}
});

View file

@ -0,0 +1 @@
{{customize/archive-admin}}

View file

@ -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>

View file

@ -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'
}); });

View file

@ -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;
}
}
}
} }

View 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>

View file

@ -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}}