diff --git a/domain/activity/mysql/store.go b/domain/activity/mysql/store.go index bd7ebe38..1ab17ec0 100644 --- a/domain/activity/mysql/store.go +++ b/domain/activity/mysql/store.go @@ -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 +} diff --git a/domain/document/endpoint.go b/domain/document/endpoint.go index c9ed2c2f..d34269b3 100644 --- a/domain/document/endpoint.go +++ b/domain/document/endpoint.go @@ -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() diff --git a/domain/page/endpoint.go b/domain/page/endpoint.go index 2bd04b5c..5d9ede59 100644 --- a/domain/page/endpoint.go +++ b/domain/page/endpoint.go @@ -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() diff --git a/domain/permission/permission.go b/domain/permission/permission.go index a7b120fc..38dec60e 100644 --- a/domain/permission/permission.go +++ b/domain/permission/permission.go @@ -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) diff --git a/domain/storer.go b/domain/storer.go index 41087cb9..83b5727c 100644 --- a/domain/storer.go +++ b/domain/storer.go @@ -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 diff --git a/gui/app/services/activity.js b/gui/app/components/customize/archive-admin.js similarity index 56% rename from gui/app/services/activity.js rename to gui/app/components/customize/archive-admin.js index f822c0ea..33863578 100644 --- a/gui/app/services/activity.js +++ b/gui/app/components/customize/archive-admin.js @@ -9,18 +9,16 @@ // // 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({ - ajax: service(), +export default Component.extend(AuthProvider, ModalMixin, { - getDocumentSummary(documentId) { - return this.get('ajax').request(`activity/document/${documentId}`, { - method: "GET" - }).then((response) => { - return response; - }).catch(() => { - return []; - }); + init() { + this._super(...arguments); + }, + + actions: { } }); diff --git a/gui/app/components/document/document-meta.js b/gui/app/components/document/document-meta.js index 591b07ce..2586dcbe 100644 --- a/gui/app/components/document/document-meta.js +++ b/gui/app/components/document/document-meta.js @@ -13,11 +13,12 @@ import $ from 'jquery'; import { computed } from '@ember/object'; import { notEmpty } from '@ember/object/computed'; import { inject as service } from '@ember/service'; -import Component from '@ember/component'; import { A } from "@ember/array" 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'), categoryService: service('category'), sessionService: service('session'), diff --git a/gui/app/components/toolbar/for-document.js b/gui/app/components/toolbar/for-document.js index acc07d62..db507500 100644 --- a/gui/app/components/toolbar/for-document.js +++ b/gui/app/components/toolbar/for-document.js @@ -11,10 +11,10 @@ import $ from 'jquery'; import { inject as service } from '@ember/service'; -import Component from '@ember/component'; import AuthMixin from '../../mixins/auth'; import TooltipMixin from '../../mixins/tooltip'; import ModalMixin from '../../mixins/modal'; +import Component from '@ember/component'; export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, { userSvc: service('user'), diff --git a/gui/app/constants/constants.js b/gui/app/constants/constants.js index 82a5fa89..73793897 100644 --- a/gui/app/constants/constants.js +++ b/gui/app/constants/constants.js @@ -1,11 +1,11 @@ // Copyright 2016 Documize Inc. . 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 // // You can operate outside the AGPL restrictions by purchasing // Documize Enterprise Edition and obtaining a commercial license -// by contacting . +// by contacting . // // https://documize.com @@ -15,52 +15,63 @@ import EmberObject from "@ember/object"; // let constants = this.get('constants'); let constants = EmberObject.extend({ - // Document - ProtectionType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects - None: 0, - Lock: 1, - Review: 2, + // Document + ProtectionType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects + None: 0, + Lock: 1, + Review: 2, - NoneLabel: 'Changes permitted without approval', - LockLabel: 'Locked, changes not permitted', - ReviewLabel: 'Changes require approval before publication' - }, + NoneLabel: 'Changes permitted without approval', + LockLabel: 'Locked, changes not permitted', + ReviewLabel: 'Changes require approval before publication' + }, - // Document - ApprovalType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects - None: 0, - Anybody: 1, - Majority: 2, - Unanimous: 3, + // Document + ApprovalType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects + None: 0, + Anybody: 1, + Majority: 2, + Unanimous: 3, - AnybodyLabel: 'Approval required from any approver', - MajorityLabel: 'Majority approval required from approvers', - UnanimousLabel: 'Unanimous approval required from all approvers' - }, + AnybodyLabel: 'Approval required from any approver', + MajorityLabel: 'Majority approval required from approvers', + UnanimousLabel: 'Unanimous approval required from all approvers' + }, - // Section - ChangeState: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects - Published: 0, - Pending: 1, - UnderReview: 2, - Rejected: 3, - PendingNew: 4, - }, + // Section + ChangeState: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects + Published: 0, + Pending: 1, + UnderReview: 2, + Rejected: 3, + PendingNew: 4, + }, - // Section - PageType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects - Tab: 'tab', - Section: 'section' - }, + // Section + PageType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects + Tab: 'tab', + Section: 'section' + }, - // Who a permission record relates to - WhoType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects - User: 'user', - Group: 'role' - }, + // Who a permission record relates to + WhoType: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects + User: 'user', + Group: 'role' + }, - EveryoneUserId: "0", - EveryoneUserName: "Everyone" + EveryoneUserId: "0", + 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 } \ No newline at end of file +export default { constants } diff --git a/gui/app/initializers/econstants.js b/gui/app/initializers/econstants.js index dbfcf12f..90e879bd 100644 --- a/gui/app/initializers/econstants.js +++ b/gui/app/initializers/econstants.js @@ -1,24 +1,25 @@ // Copyright 2016 Documize Inc. . 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 // // You can operate outside the AGPL restrictions by purchasing // Documize Enterprise Edition and obtaining a commercial license -// by contacting . +// by contacting . // // https://documize.com export function initialize(application) { - application.inject('route', 'econstants', 'econstants:main'); - application.inject('controller', 'econstants', 'econstants:main'); - application.inject('component', 'econstants', 'econstants:main'); - application.inject('template', 'econstants', 'econstants:main'); - application.inject('service', 'econstants', 'econstants:main'); + application.inject('route', 'econstants', 'econstants:main'); + application.inject('controller', 'econstants', 'econstants:main'); + application.inject('component', 'econstants', 'econstants:main'); + application.inject('template', 'econstants', 'econstants:main'); + application.inject('service', 'econstants', 'econstants:main'); + application.inject('model', 'econstants', 'econstants:main'); } export default { - name: 'econstants', - after: "application", - initialize: initialize -}; \ No newline at end of file + name: 'econstants', + after: "application", + initialize: initialize +}; diff --git a/gui/app/models/document.js b/gui/app/models/document.js index 137e92ed..aa47c46b 100644 --- a/gui/app/models/document.js +++ b/gui/app/models/document.js @@ -10,10 +10,9 @@ // https://documize.com import { computed } from '@ember/object'; -import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import stringUtil from '../utils/string'; -// import { belongsTo, hasMany } from 'ember-data/relationships'; +import Model from 'ember-data/model'; export default Model.extend({ name: attr('string'), @@ -27,6 +26,11 @@ export default Model.extend({ template: attr('boolean'), protection: 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 selected: attr('boolean', { defaultValue: false }), @@ -34,5 +38,24 @@ export default Model.extend({ return stringUtil.makeSlug(this.get('name')); }), 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 ''; + }), }); diff --git a/gui/app/models/folder.js b/gui/app/models/folder.js index 03ae37a8..30206260 100644 --- a/gui/app/models/folder.js +++ b/gui/app/models/folder.js @@ -10,11 +10,10 @@ // https://documize.com import { computed } from '@ember/object'; -import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import constants from '../utils/constants'; import stringUtil from '../utils/string'; -// import { belongsTo, hasMany } from 'ember-data/relationships'; +import Model from 'ember-data/model'; export default Model.extend({ name: attr('string'), diff --git a/gui/app/pods/customize/archive/controller.js b/gui/app/pods/customize/archive/controller.js new file mode 100644 index 00000000..f5539165 --- /dev/null +++ b/gui/app/pods/customize/archive/controller.js @@ -0,0 +1,17 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// https://documize.com + +import Controller from '@ember/controller'; + +export default Controller.extend({ + actions: { + } +}); diff --git a/gui/app/pods/customize/archive/route.js b/gui/app/pods/customize/archive/route.js new file mode 100644 index 00000000..79424e88 --- /dev/null +++ b/gui/app/pods/customize/archive/route.js @@ -0,0 +1,25 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// 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'); + } +}); diff --git a/gui/app/pods/customize/archive/template.hbs b/gui/app/pods/customize/archive/template.hbs new file mode 100644 index 00000000..b830b27a --- /dev/null +++ b/gui/app/pods/customize/archive/template.hbs @@ -0,0 +1 @@ +{{customize/archive-admin}} \ No newline at end of file diff --git a/gui/app/pods/customize/template.hbs b/gui/app/pods/customize/template.hbs index 23241547..6ab004c0 100644 --- a/gui/app/pods/customize/template.hbs +++ b/gui/app/pods/customize/template.hbs @@ -2,7 +2,7 @@ {{#toolbar/t-toolbar}} {{#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-actions}} {{/toolbar/t-actions}} @@ -12,19 +12,20 @@
    - {{#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.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.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.groups' activeClass='selected' class="tab" tagName="li" }}Groups{{/link-to}} + {{#link-to 'customize.users' activeClass='selected' class="tab" tagName="li" }}Users{{/link-to}} {{#if session.isGlobalAdmin}} - {{#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.auth' activeClass='selected' class="tab" tagName="li"}}Authentication{{/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.auth' activeClass='selected' class="tab" tagName="li" }}Authentication{{/link-to}} {{/if}} + {{#link-to 'customize.archive' activeClass='selected' class="tab" tagName="li" }}Archive{{/link-to}}
{{outlet}}
- + \ No newline at end of file diff --git a/gui/app/router.js b/gui/app/router.js index 8e4304cf..12b2151e 100644 --- a/gui/app/router.js +++ b/gui/app/router.js @@ -16,7 +16,7 @@ var Router = EmberRouter.extend({ location: config.locationType }); -export default Router.map(function() { +export default Router.map(function () { this.route('folders', { path: '/' }); @@ -30,7 +30,7 @@ export default Router.map(function() { { path: 's/:folder_id/:folder_slug' }, - function() { + function () { this.route('category', { path: 'category' }); @@ -42,7 +42,7 @@ export default Router.map(function() { { path: 's/:folder_id/:folder_slug/d/:document_id/:document_slug' }, - function() { + function () { this.route('section', { path: 'section/:page_id' }); @@ -57,7 +57,7 @@ export default Router.map(function() { { path: 'settings' }, - function() { + function () { this.route('general', { path: 'general' }); @@ -82,6 +82,9 @@ export default Router.map(function() { this.route('audit', { path: 'audit' }); + this.route('archive', { + path: 'archive' + }); } ); @@ -98,7 +101,7 @@ export default Router.map(function() { { path: 'auth' }, - function() { + function () { this.route('sso', { path: 'sso/:token' }); diff --git a/gui/app/styles/view/customize.scss b/gui/app/styles/view/customize.scss index 193ad282..49901917 100644 --- a/gui/app/styles/view/customize.scss +++ b/gui/app/styles/view/customize.scss @@ -23,7 +23,7 @@ margin: 0 0 0 10px; display: inline-block; cursor: pointer; - + > .email { font-size: 0.9rem; color: $color-off-black; @@ -124,7 +124,7 @@ } } } - + > .smtp-failure { font-size: 1.2rem; font-weight: bold; @@ -136,4 +136,16 @@ font-weight: bold; color: $color-green; } + + > .archive-admin { + > .list { + > .item { + margin: 15px 0; + padding: 15px; + @include ease-in(); + font-size: 1.2rem; + color: $color-primary; + } + } + } } diff --git a/gui/app/templates/components/customize/archive-admin.hbs b/gui/app/templates/components/customize/archive-admin.hbs new file mode 100644 index 00000000..a26bd748 --- /dev/null +++ b/gui/app/templates/components/customize/archive-admin.hbs @@ -0,0 +1,25 @@ +
+
+
+

Archive

+

Mark as live documents currently marked as archived

+ +
+
    + {{#each docs as |doc|}} +
  • +
    {{doc.name}}
    +
    + +
    +
  • + {{/each}} +
+ {{#if (eq docs.length 0)}} +

Nothing found

+ {{/if}} +
+ +
+
+
\ No newline at end of file diff --git a/gui/app/templates/components/folder/documents-list.hbs b/gui/app/templates/components/folder/documents-list.hbs index 41f2d0b7..77cf52e5 100644 --- a/gui/app/templates/components/folder/documents-list.hbs +++ b/gui/app/templates/components/folder/documents-list.hbs @@ -7,6 +7,9 @@
{{ document.name }}
{{ document.excerpt }}
{{folder/document-tags documentTags=document.tags}} + {{#if (not-eq document.lifecycle constants.Lifecycle.Live)}} + + {{/if}} {{/link-to}} {{#if hasDocumentActions}} @@ -39,11 +42,13 @@ -{{#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')}}

Are you sure you want to delete {{selectedDocuments.length}} {{selectedCaption}}?

{{/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')}}

Select space for {{selectedDocuments.length}} {{selectedCaption}}

{{ui/ui-list-picker items=moveOptions nameField='name' singleSelect=true}} {{/ui/ui-dialog}}