mirror of
https://github.com/documize/community.git
synced 2025-07-25 08:09:43 +02:00
Merge pull request #137 from documize/version-drafts-history-archiving
Document lifecycle and versioning
This commit is contained in:
commit
7eb99c52f2
58 changed files with 7393 additions and 1278 deletions
|
@ -25,7 +25,7 @@ Anyone who wants a single place for any kind of document.
|
|||
Anyone who wants to loop in external participants complete security.
|
||||
|
||||
Anyone who wishes documentation and knowledge capture worked like agile software development.
|
||||
|
||||
|
||||
## What's different about Documize?
|
||||
|
||||
Sane organization through personal, team and public spaces.
|
||||
|
@ -52,7 +52,7 @@ Space view.
|
|||
|
||||
## Latest version
|
||||
|
||||
Community edition: v1.58.0
|
||||
Community edition: v1.59.0
|
||||
|
||||
## OS support
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ func setupAccount(rt *env.Runtime, completion onboardRequest, serial string) (er
|
|||
}
|
||||
|
||||
// assign permissions to space
|
||||
perms := []string{"view", "manage", "own", "doc-add", "doc-edit", "doc-delete", "doc-move", "doc-copy", "doc-template", "doc-approve"}
|
||||
perms := []string{"view", "manage", "own", "doc-add", "doc-edit", "doc-delete", "doc-move", "doc-copy", "doc-template", "doc-approve", "doc-version", "doc-lifecycle"}
|
||||
for _, p := range perms {
|
||||
sql = fmt.Sprintf("insert into permission (orgid, who, whoid, action, scope, location, refid) values (\"%s\", 'user', \"%s\", \"%s\", 'object', 'space', \"%s\")", orgID, userID, p, labelID)
|
||||
_, err = runSQL(rt, sql)
|
||||
|
|
27
core/database/scripts/autobuild/db_00019.sql
Normal file
27
core/database/scripts/autobuild/db_00019.sql
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* enterprise edition */
|
||||
|
||||
-- document lifecycle and versioning
|
||||
ALTER TABLE document ADD COLUMN `lifecycle` INT NOT NULL DEFAULT 1 AFTER `approval`;
|
||||
ALTER TABLE document ADD COLUMN `versioned` INT NOT NULL DEFAULT 0 AFTER `lifecycle`;
|
||||
ALTER TABLE document ADD COLUMN `versionid` VARCHAR(100) DEFAULT '' NOT NULL AFTER `versioned`;
|
||||
ALTER TABLE document ADD COLUMN `versionorder` INT NOT NULL DEFAULT 0 AFTER `versionid`;
|
||||
ALTER TABLE document ADD COLUMN `groupid` CHAR(16) NOT NULL COLLATE utf8_bin AFTER `versionorder`;
|
||||
|
||||
-- grant doc-lifecycle permission
|
||||
INSERT INTO permission(orgid, who, whoid, action, scope, location, refid, created)
|
||||
SELECT orgid, who, whoid, 'doc-lifecycle' AS action, scope, location, refid, created
|
||||
FROM permission
|
||||
WHERE action = 'doc-edit' OR action = 'doc-approve';
|
||||
|
||||
-- grant doc-versions permission
|
||||
INSERT INTO permission(orgid, who, whoid, action, scope, location, refid, created)
|
||||
SELECT orgid, who, whoid, 'doc-version' AS action, scope, location, refid, created
|
||||
FROM permission
|
||||
WHERE action = 'doc-edit' OR action = 'doc-approve';
|
||||
|
||||
-- implement document section name search indexing
|
||||
INSERT INTO search (orgid, documentid, itemid, itemtype, content)
|
||||
SELECT orgid, documentid, refid as itemid, "page" as itemtype, title as content
|
||||
FROM page WHERE status=0
|
||||
|
||||
-- deprecations
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
indexer "github.com/documize/community/domain/search"
|
||||
"github.com/documize/community/model/attachment"
|
||||
"github.com/documize/community/model/audit"
|
||||
"github.com/documize/community/model/workflow"
|
||||
uuid "github.com/nu7hatch/gouuid"
|
||||
)
|
||||
|
||||
|
@ -161,7 +162,12 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
a, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
||||
d, _ := h.Store.Document.Get(ctx, documentID)
|
||||
go h.Indexer.IndexDocument(ctx, d, a)
|
||||
|
||||
if d.Lifecycle == workflow.LifecycleLive {
|
||||
go h.Indexer.IndexDocument(ctx, d, a)
|
||||
} else {
|
||||
go h.Indexer.DeleteDocument(ctx, d.RefID)
|
||||
}
|
||||
|
||||
response.WriteEmpty(w)
|
||||
}
|
||||
|
@ -236,7 +242,12 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
all, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
||||
d, _ := h.Store.Document.Get(ctx, documentID)
|
||||
go h.Indexer.IndexDocument(ctx, d, all)
|
||||
|
||||
if d.Lifecycle == workflow.LifecycleLive {
|
||||
go h.Indexer.IndexDocument(ctx, d, all)
|
||||
} else {
|
||||
go h.Indexer.DeleteDocument(ctx, d.RefID)
|
||||
}
|
||||
|
||||
response.WriteEmpty(w)
|
||||
}
|
||||
|
|
175
domain/document/document.go
Normal file
175
domain/document/document.go
Normal file
|
@ -0,0 +1,175 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
package document
|
||||
|
||||
import (
|
||||
"github.com/documize/community/core/uniqueid"
|
||||
"github.com/documize/community/domain"
|
||||
"github.com/documize/community/model/category"
|
||||
"github.com/documize/community/model/doc"
|
||||
"github.com/documize/community/model/page"
|
||||
"github.com/documize/community/model/workflow"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// FilterCategoryProtected removes documents that cannot be seen by user due to
|
||||
// document cateogory viewing permissions.
|
||||
func FilterCategoryProtected(docs []doc.Document, cats []category.Category, members []category.Member, viewDrafts bool) (filtered []doc.Document) {
|
||||
filtered = []doc.Document{}
|
||||
|
||||
for _, doc := range docs {
|
||||
hasCategory := false
|
||||
canSeeCategory := false
|
||||
skip := false
|
||||
|
||||
// drafts included if user can see them
|
||||
if doc.Lifecycle == workflow.LifecycleDraft && !viewDrafts {
|
||||
skip = true
|
||||
}
|
||||
|
||||
// archived never included
|
||||
if doc.Lifecycle == workflow.LifecycleArchived {
|
||||
skip = true
|
||||
}
|
||||
|
||||
OUTER:
|
||||
|
||||
for _, m := range members {
|
||||
if m.DocumentID == doc.RefID {
|
||||
hasCategory = true
|
||||
for _, cat := range cats {
|
||||
if cat.RefID == m.CategoryID {
|
||||
canSeeCategory = true
|
||||
continue OUTER
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !skip && (!hasCategory || canSeeCategory) {
|
||||
filtered = append(filtered, doc)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CopyDocument clones an existing document
|
||||
func CopyDocument(ctx domain.RequestContext, s domain.Store, documentID string) (newDocumentID string, err error) {
|
||||
doc, err := s.Document.Get(ctx, documentID)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unable to fetch existing document")
|
||||
return
|
||||
}
|
||||
|
||||
newDocumentID = uniqueid.Generate()
|
||||
doc.RefID = newDocumentID
|
||||
doc.ID = 0
|
||||
doc.Versioned = false
|
||||
doc.VersionID = ""
|
||||
doc.GroupID = ""
|
||||
doc.Template = false
|
||||
|
||||
// Duplicate pages and associated meta
|
||||
pages, err := s.Page.GetPages(ctx, documentID)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unable to get existing pages")
|
||||
return
|
||||
}
|
||||
|
||||
var pageModel []page.NewPage
|
||||
|
||||
for _, p := range pages {
|
||||
p.DocumentID = newDocumentID
|
||||
p.ID = 0
|
||||
|
||||
meta, err2 := s.Page.GetPageMeta(ctx, p.RefID)
|
||||
if err2 != nil {
|
||||
err = errors.Wrap(err, "unable to get existing pages meta")
|
||||
return
|
||||
}
|
||||
|
||||
pageID := uniqueid.Generate()
|
||||
p.RefID = pageID
|
||||
meta.PageID = pageID
|
||||
meta.DocumentID = newDocumentID
|
||||
|
||||
m := page.NewPage{}
|
||||
m.Page = p
|
||||
m.Meta = meta
|
||||
|
||||
pageModel = append(pageModel, m)
|
||||
}
|
||||
|
||||
// Duplicate attachments
|
||||
attachments, _ := s.Attachment.GetAttachments(ctx, documentID)
|
||||
for i, a := range attachments {
|
||||
a.DocumentID = newDocumentID
|
||||
a.RefID = uniqueid.Generate()
|
||||
a.ID = 0
|
||||
attachments[i] = a
|
||||
}
|
||||
|
||||
// Now create the template: document, attachments, pages and their meta
|
||||
err = s.Document.Add(ctx, doc)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unable to add copied document")
|
||||
return
|
||||
}
|
||||
|
||||
for _, a := range attachments {
|
||||
err = s.Attachment.Add(ctx, a)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unable to add copied attachment")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range pageModel {
|
||||
err = s.Page.Add(ctx, m)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unable to add copied page")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FilterLastVersion returns the latest version of each document
|
||||
// by removing all previous versions.
|
||||
// If a document is not versioned, it is returned as-is.
|
||||
func FilterLastVersion(docs []doc.Document) (filtered []doc.Document) {
|
||||
filtered = []doc.Document{}
|
||||
prev := make(map[string]bool)
|
||||
|
||||
for _, doc := range docs {
|
||||
add := false
|
||||
|
||||
if doc.GroupID == "" {
|
||||
add = true
|
||||
} else {
|
||||
if _, isExisting := prev[doc.GroupID]; !isExisting {
|
||||
add = true
|
||||
prev[doc.GroupID] = true
|
||||
} else {
|
||||
add = false
|
||||
}
|
||||
}
|
||||
|
||||
if add {
|
||||
filtered = append(filtered, doc)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -44,9 +44,10 @@ type Handler struct {
|
|||
Indexer indexer.Indexer
|
||||
}
|
||||
|
||||
// Get is an endpoint that returns the document-level information for a given documentID.
|
||||
// Get is an endpoint that returns the document-level information for a
|
||||
// given documentID.
|
||||
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
method := "document.get"
|
||||
method := "document.Get"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
id := request.Param(r, "documentID")
|
||||
|
@ -78,15 +79,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()
|
||||
|
@ -98,7 +102,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// DocumentLinks is an endpoint returning the links for a document.
|
||||
func (h *Handler) DocumentLinks(w http.ResponseWriter, r *http.Request) {
|
||||
method := "document.links"
|
||||
method := "document.DocumentLinks"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
id := request.Param(r, "documentID")
|
||||
|
@ -136,55 +140,37 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// get complete list of documents
|
||||
// get user permissions
|
||||
viewDrafts := permission.CanViewDrafts(ctx, *h.Store, spaceID)
|
||||
|
||||
// Get complete list of documents regardless of category permission
|
||||
// and versioning.
|
||||
documents, err := h.Store.Document.GetBySpace(ctx, spaceID)
|
||||
if err != nil {
|
||||
response.WriteServerError(w, method, err)
|
||||
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
|
||||
// category view/access permission
|
||||
filtered := []doc.Document{}
|
||||
// Remove documents that cannot be seen due to lack of
|
||||
// category view/access permission.
|
||||
cats, err := h.Store.Category.GetBySpace(ctx, spaceID)
|
||||
members, err := h.Store.Category.GetSpaceCategoryMembership(ctx, spaceID)
|
||||
filtered := FilterCategoryProtected(documents, cats, members, viewDrafts)
|
||||
|
||||
for _, doc := range documents {
|
||||
hasCategory := false
|
||||
canSeeCategory := false
|
||||
|
||||
OUTER:
|
||||
|
||||
for _, m := range members {
|
||||
if m.DocumentID == doc.RefID {
|
||||
hasCategory = true
|
||||
for _, cat := range cats {
|
||||
if cat.RefID == m.CategoryID {
|
||||
canSeeCategory = true
|
||||
continue OUTER
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hasCategory || canSeeCategory {
|
||||
filtered = append(filtered, doc)
|
||||
}
|
||||
}
|
||||
// Keep the latest version when faced with multiple versions.
|
||||
filtered = FilterLastVersion(filtered)
|
||||
|
||||
response.WriteJSON(w, filtered)
|
||||
}
|
||||
|
||||
// Update updates an existing document using the
|
||||
// format described in NewDocumentModel() encoded as JSON in the request.
|
||||
// Update updates an existing document using the format described
|
||||
// in NewDocumentModel() encoded as JSON in the request.
|
||||
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
method := "document.space"
|
||||
method := "document.Update"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
documentID := request.Param(r, "documentID")
|
||||
|
@ -223,7 +209,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// if space changed for document, remove document categories
|
||||
// If space changed for document, remove document categories.
|
||||
oldDoc, err := h.Store.Document.Get(ctx, documentID)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
|
@ -244,19 +230,60 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// If document part of versioned document group
|
||||
// then document name must be applied to all documents
|
||||
// in the group.
|
||||
if len(d.GroupID) > 0 {
|
||||
err = h.Store.Document.UpdateGroup(ctx, d)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Record document being marked as archived.
|
||||
if d.Lifecycle != oldDoc.Lifecycle && d.Lifecycle == workflow.LifecycleArchived {
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: d.LabelID,
|
||||
DocumentID: documentID,
|
||||
SourceType: activity.SourceTypeDocument,
|
||||
ActivityType: activity.TypeArchived})
|
||||
}
|
||||
|
||||
// Record document being marked as draft.
|
||||
if d.Lifecycle != oldDoc.Lifecycle && d.Lifecycle == workflow.LifecycleDraft {
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: d.LabelID,
|
||||
DocumentID: documentID,
|
||||
SourceType: activity.SourceTypeDocument,
|
||||
ActivityType: activity.TypeDraft})
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
||||
h.Store.Audit.Record(ctx, audit.EventTypeDocumentUpdate)
|
||||
|
||||
a, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
||||
go h.Indexer.IndexDocument(ctx, d, a)
|
||||
// Live document indexed for search.
|
||||
if d.Lifecycle == workflow.LifecycleLive {
|
||||
a, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
||||
go h.Indexer.IndexDocument(ctx, d, a)
|
||||
|
||||
pages, _ := h.Store.Page.GetPages(ctx, d.RefID)
|
||||
for i := range pages {
|
||||
go h.Indexer.IndexContent(ctx, pages[i])
|
||||
}
|
||||
} else {
|
||||
go h.Indexer.DeleteDocument(ctx, d.RefID)
|
||||
}
|
||||
|
||||
response.WriteEmpty(w)
|
||||
}
|
||||
|
||||
// Delete is an endpoint that deletes a document specified by documentID.
|
||||
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
method := "document.delete"
|
||||
method := "document.Delete"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
documentID := request.Param(r, "documentID")
|
||||
|
@ -327,11 +354,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()
|
||||
|
||||
|
@ -342,9 +372,10 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
response.WriteEmpty(w)
|
||||
}
|
||||
|
||||
// SearchDocuments endpoint takes a list of keywords and returns a list of document references matching those keywords.
|
||||
// SearchDocuments endpoint takes a list of keywords and returns a list of
|
||||
// document references matching those keywords.
|
||||
func (h *Handler) SearchDocuments(w http.ResponseWriter, r *http.Request) {
|
||||
method := "document.search"
|
||||
method := "document.SearchDocuments"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
defer streamutil.Close(r.Body)
|
||||
|
@ -412,6 +443,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 {
|
||||
|
@ -455,12 +492,22 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
|
|||
sp = []space.Space{}
|
||||
}
|
||||
|
||||
// Get version information for this document.
|
||||
v, err := h.Store.Document.GetVersions(ctx, document.GroupID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare response.
|
||||
data := BulkDocumentData{}
|
||||
data.Document = document
|
||||
data.Permissions = record
|
||||
data.Roles = rolesRecord
|
||||
data.Links = l
|
||||
data.Spaces = sp
|
||||
data.Versions = v
|
||||
|
||||
ctx.Transaction, err = h.Runtime.Db.Beginx()
|
||||
if err != nil {
|
||||
|
@ -469,15 +516,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()
|
||||
|
@ -495,4 +544,5 @@ type BulkDocumentData struct {
|
|||
Roles pm.DocumentRecord `json:"roles"`
|
||||
Spaces []space.Space `json:"folders"`
|
||||
Links []link.Link `json:"links"`
|
||||
Versions []doc.Version `json:"versions"`
|
||||
}
|
||||
|
|
|
@ -29,13 +29,16 @@ type Scope struct {
|
|||
}
|
||||
|
||||
// Add inserts the given document record into the document table and audits that it has been done.
|
||||
func (s Scope) Add(ctx domain.RequestContext, document doc.Document) (err error) {
|
||||
document.OrgID = ctx.OrgID
|
||||
document.Created = time.Now().UTC()
|
||||
document.Revised = document.Created // put same time in both fields
|
||||
func (s Scope) Add(ctx domain.RequestContext, d doc.Document) (err error) {
|
||||
d.OrgID = ctx.OrgID
|
||||
d.Created = time.Now().UTC()
|
||||
d.Revised = d.Created // put same time in both fields
|
||||
|
||||
_, err = ctx.Transaction.Exec("INSERT INTO document (refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, protection, approval, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
document.RefID, document.OrgID, document.LabelID, document.UserID, document.Job, document.Location, document.Title, document.Excerpt, document.Slug, document.Tags, document.Template, document.Protection, document.Approval, document.Created, document.Revised)
|
||||
_, err = ctx.Transaction.Exec(`
|
||||
INSERT INTO document (refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, protection, approval, lifecycle, versioned, versionid, versionorder, groupid, created, revised)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
d.RefID, d.OrgID, d.LabelID, d.UserID, d.Job, d.Location, d.Title, d.Excerpt, d.Slug, d.Tags,
|
||||
d.Template, d.Protection, d.Approval, d.Lifecycle, d.Versioned, d.VersionID, d.VersionOrder, d.GroupID, d.Created, d.Revised)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execuet insert document")
|
||||
|
@ -46,7 +49,11 @@ func (s Scope) Add(ctx domain.RequestContext, document doc.Document) (err error)
|
|||
|
||||
// Get fetches the document record with the given id fromt the document table and audits that it has been got.
|
||||
func (s Scope) Get(ctx domain.RequestContext, id string) (document doc.Document, err error) {
|
||||
err = s.Runtime.Db.Get(&document, "SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, protection, approval, created, revised FROM document WHERE orgid=? and refid=?",
|
||||
err = s.Runtime.Db.Get(&document, `
|
||||
SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template,
|
||||
protection, approval, lifecycle, versioned, versionid, versionorder, groupid, created, revised
|
||||
FROM document
|
||||
WHERE orgid=? and refid=?`,
|
||||
ctx.OrgID, id)
|
||||
|
||||
if err != nil {
|
||||
|
@ -90,37 +97,32 @@ func (s Scope) DocumentMeta(ctx domain.RequestContext, id string) (meta doc.Docu
|
|||
return
|
||||
}
|
||||
|
||||
// GetAll returns a slice containg all of the the documents for the client's organisation.
|
||||
func (s Scope) GetAll() (ctx domain.RequestContext, documents []doc.Document, err error) {
|
||||
err = s.Runtime.Db.Select(&documents, "SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, protection, approval, created, revised FROM document WHERE orgid=? AND template=0 ORDER BY title", ctx.OrgID)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "select documents")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetBySpace returns a slice containing the documents for a given space.
|
||||
// No attempt is made to hide documents that are protected
|
||||
// by category permissions -- caller must filter as required.
|
||||
//
|
||||
// No attempt is made to hide documents that are protected by category
|
||||
// permissions hence caller must filter as required.
|
||||
//
|
||||
// All versions of a document are returned, hence caller must
|
||||
// decide what to do with them.
|
||||
func (s Scope) GetBySpace(ctx domain.RequestContext, spaceID string) (documents []doc.Document, err error) {
|
||||
err = s.Runtime.Db.Select(&documents, `
|
||||
SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, protection, approval, created, revised
|
||||
SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template,
|
||||
protection, approval, lifecycle, versioned, versionid, versionorder, groupid, created, revised
|
||||
FROM document
|
||||
WHERE orgid=? AND template=0 AND labelid IN (
|
||||
SELECT refid FROM label WHERE orgid=? AND refid IN
|
||||
SELECT refid FROM label WHERE orgid=? AND refid IN
|
||||
(SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid=? AND refid IN (
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
UNION ALL
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=?
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=?
|
||||
AND p.who='role' AND p.location='space' AND p.refid=? AND p.action='view' AND (r.userid=? OR r.userid='0')
|
||||
))
|
||||
)
|
||||
ORDER BY title`, ctx.OrgID, ctx.OrgID, ctx.OrgID, spaceID, ctx.OrgID, ctx.UserID, ctx.OrgID, spaceID, ctx.UserID)
|
||||
ORDER BY title, versionorder`, ctx.OrgID, ctx.OrgID, ctx.OrgID, spaceID, ctx.OrgID, ctx.UserID, ctx.OrgID, spaceID, ctx.UserID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
if err == sql.ErrNoRows || len(documents) == 0 {
|
||||
err = nil
|
||||
documents = []doc.Document{}
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "select documents by space")
|
||||
|
@ -129,37 +131,18 @@ func (s Scope) GetBySpace(ctx domain.RequestContext, spaceID string) (documents
|
|||
return
|
||||
}
|
||||
|
||||
// Templates returns a slice containing the documents available as templates to the client's organisation, in title order.
|
||||
func (s Scope) Templates(ctx domain.RequestContext) (documents []doc.Document, err error) {
|
||||
err = s.Runtime.Db.Select(&documents,
|
||||
`SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, protection, approval, created, revised FROM document WHERE orgid=? AND template=1
|
||||
AND labelid IN
|
||||
(
|
||||
SELECT refid FROM label WHERE orgid=?
|
||||
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
UNION ALL
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND (r.userid=? OR r.userid='0')
|
||||
))
|
||||
)
|
||||
ORDER BY title`, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "select document templates")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TemplatesBySpace returns a slice containing the documents available as templates for given space.
|
||||
func (s Scope) TemplatesBySpace(ctx domain.RequestContext, spaceID string) (documents []doc.Document, err error) {
|
||||
err = s.Runtime.Db.Select(&documents,
|
||||
`SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, protection, approval, created, revised FROM document WHERE orgid=? AND labelid=? AND template=1
|
||||
`SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template,
|
||||
protection, approval, lifecycle, versioned, versionid, versionorder, groupid, created, revised
|
||||
FROM document
|
||||
WHERE orgid=? AND labelid=? AND template=1 ANd lifecycle=1
|
||||
AND labelid IN
|
||||
(
|
||||
SELECT refid FROM label WHERE orgid=?
|
||||
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
UNION ALL
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND (r.userid=? OR r.userid='0')
|
||||
))
|
||||
|
@ -170,7 +153,6 @@ func (s Scope) TemplatesBySpace(ctx domain.RequestContext, spaceID string) (docu
|
|||
err = nil
|
||||
documents = []doc.Document{}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "select space document templates")
|
||||
}
|
||||
|
@ -178,44 +160,24 @@ func (s Scope) TemplatesBySpace(ctx domain.RequestContext, spaceID string) (docu
|
|||
return
|
||||
}
|
||||
|
||||
// PublicDocuments returns a slice of SitemapDocument records, holding documents in folders of type 1 (entity.TemplateTypePublic).
|
||||
// PublicDocuments returns a slice of SitemapDocument records
|
||||
// linking to documents in public spaces.
|
||||
// These documents can then be seen by search crawlers.
|
||||
func (s Scope) PublicDocuments(ctx domain.RequestContext, orgID string) (documents []doc.SitemapDocument, err error) {
|
||||
err = s.Runtime.Db.Select(&documents,
|
||||
`SELECT d.refid as documentid, d.title as document, d.revised as revised, l.refid as folderid, l.label as folder
|
||||
FROM document d LEFT JOIN label l ON l.refid=d.labelid
|
||||
WHERE d.orgid=?
|
||||
AND l.type=1
|
||||
AND d.template=0`, orgID)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, fmt.Sprintf("execute GetPublicDocuments for org %s%s", orgID))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DocumentList returns a slice containing the documents available as templates to the client's organisation, in title order.
|
||||
func (s Scope) DocumentList(ctx domain.RequestContext) (documents []doc.Document, err error) {
|
||||
err = s.Runtime.Db.Select(&documents,
|
||||
`SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, protection, approval, created, revised FROM document WHERE orgid=? AND template=0
|
||||
AND labelid IN
|
||||
(
|
||||
SELECT refid FROM label WHERE orgid=?
|
||||
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
UNION ALL
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND (r.userid=? OR r.userid='0')
|
||||
))
|
||||
)
|
||||
ORDER BY title`, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
|
||||
AND l.type=1
|
||||
AND d.lifecycle=1
|
||||
AND d.template=0`, orgID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
documents = []doc.Document{}
|
||||
documents = []doc.SitemapDocument{}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "select documents list")
|
||||
err = errors.Wrap(err, fmt.Sprintf("execute GetPublicDocuments for org %s%s", orgID))
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -225,11 +187,32 @@ func (s Scope) DocumentList(ctx domain.RequestContext) (documents []doc.Document
|
|||
func (s Scope) Update(ctx domain.RequestContext, document doc.Document) (err error) {
|
||||
document.Revised = time.Now().UTC()
|
||||
|
||||
_, err = ctx.Transaction.NamedExec("UPDATE document SET labelid=:labelid, userid=:userid, job=:job, location=:location, title=:title, excerpt=:excerpt, slug=:slug, tags=:tags, template=:template, protection=:protection, approval=:approval, revised=:revised WHERE orgid=:orgid AND refid=:refid",
|
||||
_, err = ctx.Transaction.NamedExec(`
|
||||
UPDATE document
|
||||
SET
|
||||
labelid=:labelid, userid=:userid, job=:job, location=:location, title=:title, excerpt=:excerpt, slug=:slug, tags=:tags, template=:template,
|
||||
protection=:protection, approval=:approval, lifecycle=:lifecycle, versioned=:versioned, versionid=:versionid, versionorder=:versionorder, groupid=:groupid, revised=:revised
|
||||
WHERE orgid=:orgid AND refid=:refid`,
|
||||
&document)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute update document")
|
||||
err = errors.Wrap(err, "document.store.Update")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateGroup applies same values to all documents
|
||||
// with the same group ID.
|
||||
func (s Scope) UpdateGroup(ctx domain.RequestContext, d doc.Document) (err error) {
|
||||
_, err = ctx.Transaction.Exec(`UPDATE document SET title=?, excerpt=? WHERE orgid=? AND groupid=?`,
|
||||
d.Title, d.Excerpt, ctx.OrgID, d.GroupID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "document.store.UpdateTitle")
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -311,3 +294,28 @@ func (s Scope) DeleteBySpace(ctx domain.RequestContext, spaceID string) (rows in
|
|||
|
||||
return b.DeleteConstrained(ctx.Transaction, "document", ctx.OrgID, spaceID)
|
||||
}
|
||||
|
||||
// GetVersions returns a slice containing the documents for a given space.
|
||||
//
|
||||
// No attempt is made to hide documents that are protected by category
|
||||
// permissions hence caller must filter as required.
|
||||
//
|
||||
// All versions of a document are returned, hence caller must
|
||||
// decide what to do with them.
|
||||
func (s Scope) GetVersions(ctx domain.RequestContext, groupID string) (v []doc.Version, err error) {
|
||||
err = s.Runtime.Db.Select(&v, `
|
||||
SELECT versionid, refid as documentid
|
||||
FROM document
|
||||
WHERE orgid=? AND groupid=?
|
||||
ORDER BY versionorder`, ctx.OrgID, groupID)
|
||||
|
||||
if err == sql.ErrNoRows || len(v) == 0 {
|
||||
err = nil
|
||||
v = []doc.Version{}
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "document.store.GetVersions")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -164,19 +164,27 @@ 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()
|
||||
|
||||
h.Store.Audit.Record(ctx, audit.EventTypeSectionAdd)
|
||||
|
||||
np, _ := h.Store.Page.Get(ctx, pageID)
|
||||
go h.Indexer.IndexContent(ctx, np)
|
||||
|
||||
if doc.Lifecycle == workflow.LifecycleLive {
|
||||
go h.Indexer.IndexContent(ctx, np)
|
||||
} else {
|
||||
go h.Indexer.DeleteDocument(ctx, doc.RefID)
|
||||
}
|
||||
|
||||
response.WriteJSON(w, np)
|
||||
}
|
||||
|
@ -309,6 +317,7 @@ func (h *Handler) GetMeta(w http.ResponseWriter, r *http.Request) {
|
|||
// Update will persist changed page and note the fact
|
||||
// that this is a new revision. If the page is the first in a document
|
||||
// then the corresponding document title will also be changed.
|
||||
// Draft documents do not get revision entry.
|
||||
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
method := "page.update"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
@ -406,6 +415,11 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
|||
skipRevision = true
|
||||
}
|
||||
|
||||
// We only track revisions for live documents
|
||||
if doc.Lifecycle != workflow.LifecycleLive {
|
||||
skipRevision = true
|
||||
}
|
||||
|
||||
err = h.Store.Page.Update(ctx, model.Page, refID, ctx.UserID, skipRevision)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
|
@ -422,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)
|
||||
|
||||
|
@ -470,7 +487,11 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
ctx.Transaction.Commit()
|
||||
|
||||
go h.Indexer.IndexContent(ctx, model.Page)
|
||||
if doc.Lifecycle == workflow.LifecycleLive {
|
||||
go h.Indexer.IndexContent(ctx, model.Page)
|
||||
} else {
|
||||
go h.Indexer.DeleteDocument(ctx, doc.RefID)
|
||||
}
|
||||
|
||||
updatedPage, err := h.Store.Page.Get(ctx, pageID)
|
||||
|
||||
|
@ -547,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)
|
||||
|
||||
|
@ -660,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()
|
||||
|
@ -925,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()
|
||||
|
||||
|
@ -1174,12 +1203,15 @@ func (h *Handler) Rollback(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: p.DocumentID,
|
||||
PageID: p.RefID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeReverted})
|
||||
// Draft actions are not logged
|
||||
if doc.Lifecycle == workflow.LifecycleLive {
|
||||
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: doc.LabelID,
|
||||
DocumentID: p.DocumentID,
|
||||
PageID: p.RefID,
|
||||
SourceType: activity.SourceTypePage,
|
||||
ActivityType: activity.TypeReverted})
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
||||
|
|
|
@ -165,6 +165,44 @@ 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.OrgID == ctx.OrgID && role.RefID == spaceID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow &&
|
||||
pm.ContainsPermission(role.Action, pm.DocumentLifecycle) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// CanManageVersion returns if the user has permission to manage versions in space.
|
||||
func CanManageVersion(ctx domain.RequestContext, s domain.Store, spaceID string) bool {
|
||||
roles, err := s.Permission.GetUserSpacePermissions(ctx, spaceID)
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, role := range roles {
|
||||
if role.OrgID == ctx.OrgID && role.RefID == spaceID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow &&
|
||||
pm.ContainsPermission(role.Action, pm.DocumentVersion) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasPermission returns if user can perform specified actions.
|
||||
func HasPermission(ctx domain.RequestContext, s domain.Store, spaceID string, actions ...pm.Action) bool {
|
||||
roles, err := s.Permission.GetUserSpacePermissions(ctx, spaceID)
|
||||
|
|
|
@ -121,6 +121,12 @@ func (s Scope) IndexContent(ctx domain.RequestContext, p page.Page) (err error)
|
|||
err = errors.Wrap(err, "execute insert document content entry")
|
||||
}
|
||||
|
||||
_, err = ctx.Transaction.Exec("INSERT INTO search (orgid, documentid, itemid, itemtype, content) VALUES (?, ?, ?, ?, ?)",
|
||||
ctx.OrgID, p.DocumentID, p.RefID, "page", p.Title)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute insert document page title entry")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -148,7 +154,6 @@ func (s Scope) DeleteContent(ctx domain.RequestContext, pageID string) (err erro
|
|||
// Visible documents include both those in the client's own organisation and those that are public, or whose visibility includes the client.
|
||||
func (s Scope) Documents(ctx domain.RequestContext, q search.QueryOptions) (results []search.QueryResult, err error) {
|
||||
q.Keywords = strings.TrimSpace(q.Keywords)
|
||||
|
||||
if len(q.Keywords) == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -204,29 +209,29 @@ func (s Scope) Documents(ctx domain.RequestContext, q search.QueryOptions) (resu
|
|||
|
||||
func (s Scope) matchFullText(ctx domain.RequestContext, keywords, itemType string) (r []search.QueryResult, err error) {
|
||||
sql1 := `
|
||||
SELECT
|
||||
s.id, s.orgid, s.documentid, s.itemid, s.itemtype,
|
||||
d.labelid as spaceid, COALESCE(d.title,'Unknown') AS document, d.tags, d.excerpt,
|
||||
SELECT
|
||||
s.id, s.orgid, s.documentid, s.itemid, s.itemtype,
|
||||
d.labelid as spaceid, COALESCE(d.title,'Unknown') AS document, d.tags,
|
||||
d.excerpt, d.template, d.versionid,
|
||||
COALESCE(l.label,'Unknown') AS space
|
||||
FROM
|
||||
search s,
|
||||
document d
|
||||
LEFT JOIN
|
||||
LEFT JOIN
|
||||
label l ON l.orgid=d.orgid AND l.refid = d.labelid
|
||||
WHERE
|
||||
s.orgid = ?
|
||||
AND s.itemtype = ?
|
||||
AND s.documentid = d.refid
|
||||
-- AND d.template = 0
|
||||
AND d.labelid IN
|
||||
AND s.documentid = d.refid
|
||||
AND d.labelid IN
|
||||
(
|
||||
SELECT refid FROM label WHERE orgid=?
|
||||
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
SELECT refid FROM label WHERE orgid=? AND refid IN
|
||||
(
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
UNION ALL
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=?
|
||||
))
|
||||
)
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND r.userid=?
|
||||
)
|
||||
)
|
||||
AND MATCH(s.content) AGAINST(? IN BOOLEAN MODE)`
|
||||
|
||||
err = s.Runtime.Db.Select(&r,
|
||||
|
@ -235,7 +240,6 @@ func (s Scope) matchFullText(ctx domain.RequestContext, keywords, itemType strin
|
|||
itemType,
|
||||
ctx.OrgID,
|
||||
ctx.OrgID,
|
||||
ctx.OrgID,
|
||||
ctx.UserID,
|
||||
ctx.OrgID,
|
||||
ctx.UserID,
|
||||
|
@ -245,7 +249,6 @@ func (s Scope) matchFullText(ctx domain.RequestContext, keywords, itemType strin
|
|||
err = nil
|
||||
r = []search.QueryResult{}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "search document "+itemType)
|
||||
}
|
||||
|
@ -261,25 +264,25 @@ func (s Scope) matchLike(ctx domain.RequestContext, keywords, itemType string) (
|
|||
keywords = fmt.Sprintf("%%%s%%", keywords)
|
||||
|
||||
sql1 := `
|
||||
SELECT
|
||||
s.id, s.orgid, s.documentid, s.itemid, s.itemtype,
|
||||
d.labelid as spaceid, COALESCE(d.title,'Unknown') AS document, d.tags, d.excerpt,
|
||||
SELECT
|
||||
s.id, s.orgid, s.documentid, s.itemid, s.itemtype,
|
||||
d.labelid as spaceid, COALESCE(d.title,'Unknown') AS document, d.tags, d.excerpt,
|
||||
COALESCE(l.label,'Unknown') AS space
|
||||
FROM
|
||||
search s,
|
||||
document d
|
||||
LEFT JOIN
|
||||
LEFT JOIN
|
||||
label l ON l.orgid=d.orgid AND l.refid = d.labelid
|
||||
WHERE
|
||||
s.orgid = ?
|
||||
AND s.itemtype = ?
|
||||
AND s.documentid = d.refid
|
||||
AND s.documentid = d.refid
|
||||
-- AND d.template = 0
|
||||
AND d.labelid IN
|
||||
AND d.labelid IN
|
||||
(
|
||||
SELECT refid FROM label WHERE orgid=?
|
||||
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
UNION ALL
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role'
|
||||
AND p.location='space' AND p.action='view' AND (r.userid=? OR r.userid='0')
|
||||
|
|
|
@ -168,18 +168,17 @@ type AuditStorer interface {
|
|||
type DocumentStorer interface {
|
||||
Add(ctx RequestContext, document doc.Document) (err error)
|
||||
Get(ctx RequestContext, id string) (document doc.Document, err error)
|
||||
GetAll() (ctx RequestContext, documents []doc.Document, err error)
|
||||
GetBySpace(ctx RequestContext, spaceID string) (documents []doc.Document, err error)
|
||||
DocumentList(ctx RequestContext) (documents []doc.Document, err error)
|
||||
Templates(ctx RequestContext) (documents []doc.Document, err error)
|
||||
TemplatesBySpace(ctx RequestContext, spaceID string) (documents []doc.Document, err error)
|
||||
DocumentMeta(ctx RequestContext, id string) (meta doc.DocumentMeta, err error)
|
||||
PublicDocuments(ctx RequestContext, orgID string) (documents []doc.SitemapDocument, err error)
|
||||
Update(ctx RequestContext, document doc.Document) (err error)
|
||||
UpdateGroup(ctx RequestContext, document doc.Document) (err error)
|
||||
ChangeDocumentSpace(ctx RequestContext, document, space string) (err error)
|
||||
MoveDocumentSpace(ctx RequestContext, id, move string) (err error)
|
||||
Delete(ctx RequestContext, documentID string) (rows int64, err error)
|
||||
DeleteBySpace(ctx RequestContext, spaceID string) (rows int64, err error)
|
||||
GetVersions(ctx RequestContext, groupID string) (v []doc.Version, err error)
|
||||
}
|
||||
|
||||
// SettingStorer defines required methods for persisting global and user level settings
|
||||
|
@ -217,6 +216,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
|
||||
|
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/documize/community/model/page"
|
||||
pm "github.com/documize/community/model/permission"
|
||||
"github.com/documize/community/model/template"
|
||||
"github.com/documize/community/model/workflow"
|
||||
uuid "github.com/nu7hatch/gouuid"
|
||||
)
|
||||
|
||||
|
@ -377,7 +378,12 @@ func (h *Handler) Use(w http.ResponseWriter, r *http.Request) {
|
|||
event.Handler().Publish(string(event.TypeAddDocument), nd.Title)
|
||||
|
||||
a, _ := h.Store.Attachment.GetAttachments(ctx, documentID)
|
||||
go h.Indexer.IndexDocument(ctx, nd, a)
|
||||
|
||||
if nd.Lifecycle == workflow.LifecycleLive {
|
||||
go h.Indexer.IndexDocument(ctx, nd, a)
|
||||
} else {
|
||||
go h.Indexer.DeleteDocument(ctx, d.RefID)
|
||||
}
|
||||
|
||||
response.WriteJSON(w, nd)
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func main() {
|
|||
// product details
|
||||
rt.Product = env.ProdInfo{}
|
||||
rt.Product.Major = "1"
|
||||
rt.Product.Minor = "58"
|
||||
rt.Product.Minor = "59"
|
||||
rt.Product.Patch = "0"
|
||||
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
|
||||
rt.Product.Edition = "Community"
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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: {
|
||||
}
|
||||
});
|
|
@ -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'),
|
||||
|
|
|
@ -19,12 +19,12 @@ export default Component.extend(ModalMixin, {
|
|||
groupSvc: service('group'),
|
||||
spaceSvc: service('folder'),
|
||||
userSvc: service('user'),
|
||||
appMeta: service(),
|
||||
appMeta: service(),
|
||||
store: service(),
|
||||
spacePermissions: null,
|
||||
users: null,
|
||||
searchText: '',
|
||||
|
||||
|
||||
didReceiveAttrs() {
|
||||
let spacePermissions = A([]);
|
||||
let constants = this.get('constants');
|
||||
|
@ -65,7 +65,7 @@ export default Component.extend(ModalMixin, {
|
|||
// always show everyone
|
||||
if (!hasEveryoneId) {
|
||||
let pr = this.permissionRecord(constants.WhoType.User, constants.EveryoneUserId, ' ' + constants.EveryoneUserName);
|
||||
spacePermissions.pushObject(pr);
|
||||
spacePermissions.pushObject(pr);
|
||||
}
|
||||
|
||||
this.set('spacePermissions', spacePermissions.sortBy('who', 'name'));
|
||||
|
@ -93,6 +93,8 @@ export default Component.extend(ModalMixin, {
|
|||
documentCopy: false,
|
||||
documentTemplate: false,
|
||||
documentApprove: false,
|
||||
documentLifecycle: false,
|
||||
documentVersion: false,
|
||||
};
|
||||
|
||||
let rec = this.get('store').normalize('space-permission', raw);
|
||||
|
@ -132,7 +134,8 @@ export default Component.extend(ModalMixin, {
|
|||
let hasEveryone = _.find(permissions, (permission) => {
|
||||
return permission.get('whoId') === constants.EveryoneUserId &&
|
||||
(permission.get('spaceView') || permission.get('documentAdd') || permission.get('documentEdit') || permission.get('documentDelete') ||
|
||||
permission.get('documentMove') || permission.get('documentCopy') || permission.get('documentTemplate') || permission.get('documentApprove'));
|
||||
permission.get('documentMove') || permission.get('documentCopy') || permission.get('documentTemplate') ||
|
||||
permission.get('documentApprove') || permission.get('documentLifecycle') || permission.get('documentVersion'));
|
||||
});
|
||||
|
||||
// see if more than oen user is granted access to space (excluding everyone)
|
||||
|
@ -140,8 +143,9 @@ export default Component.extend(ModalMixin, {
|
|||
permissions.forEach((permission) => {
|
||||
if (permission.get('whoId') !== constants.EveryoneUserId &&
|
||||
(permission.get('spaceView') || permission.get('documentAdd') || permission.get('documentEdit') || permission.get('documentDelete') ||
|
||||
permission.get('documentMove') || permission.get('documentCopy') || permission.get('documentTemplate') || permission.get('documentApprove'))) {
|
||||
roleCount += 1;
|
||||
permission.get('documentMove') || permission.get('documentCopy') || permission.get('documentTemplate') ||
|
||||
permission.get('documentApprove') || permission.get('documentLifecycle') || permission.get('documentVersion'))) {
|
||||
roleCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -161,7 +165,7 @@ export default Component.extend(ModalMixin, {
|
|||
},
|
||||
|
||||
onSearch() {
|
||||
debounce(this, function() {
|
||||
debounce(this, function () {
|
||||
let searchText = this.get('searchText').trim();
|
||||
|
||||
if (searchText.length === 0) {
|
||||
|
@ -181,7 +185,6 @@ export default Component.extend(ModalMixin, {
|
|||
if (is.undefined(exists)) {
|
||||
spacePermissions.pushObject(this.permissionRecord(constants.WhoType.User, user.get('id'), user.get('fullname')));
|
||||
this.set('spacePermissions', spacePermissions);
|
||||
// this.set('spacePermissions', spacePermissions.sortBy('who', 'name'));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -12,21 +12,18 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
resultPhrase: "",
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.results = [];
|
||||
},
|
||||
resultPhrase: '',
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
let docs = this.get('results');
|
||||
let duped = [];
|
||||
let phrase = 'Nothing found';
|
||||
|
||||
if (docs.length > 0) {
|
||||
duped = _.uniq(docs, function (item) {
|
||||
return item.documentId;
|
||||
return item.get('documentId');
|
||||
});
|
||||
|
||||
let references = docs.length === 1 ? "reference" : "references";
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// 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
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
|
@ -15,52 +15,70 @@ 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',
|
||||
},
|
||||
|
||||
// Document Version -- document.groupId links different versions of documents together
|
||||
VersionCreateMode: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
|
||||
Unversioned: 1, // turn unversioned into versioned document
|
||||
Cloned: 2, // create versioned document by cloning existing versioned document
|
||||
Linked: 3 // link existing unversion document into this version group
|
||||
}
|
||||
});
|
||||
|
||||
export default { constants }
|
||||
export default { constants }
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
// 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
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// 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
|
||||
};
|
||||
name: 'econstants',
|
||||
after: "application",
|
||||
initialize: initialize
|
||||
};
|
||||
|
|
|
@ -24,5 +24,7 @@ export default Model.extend({
|
|||
space: attr(),
|
||||
spaceId: attr(),
|
||||
spaceSlug: attr(),
|
||||
template: attr(),
|
||||
versionId: attr(),
|
||||
selected: attr()
|
||||
});
|
||||
|
|
|
@ -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 '';
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -27,6 +27,8 @@ export default Model.extend({
|
|||
documentCopy: attr('boolean'),
|
||||
documentTemplate: attr('boolean'),
|
||||
documentApprove: attr('boolean'),
|
||||
documentLifecycle: attr('boolean'),
|
||||
documentVersion: attr('boolean'),
|
||||
name: attr('string'), // read-only
|
||||
members: attr('number') // read-only
|
||||
});
|
||||
|
|
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-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 @@
|
|||
<div class="row">
|
||||
<div class="col my-5">
|
||||
<ul class="tabnav-control">
|
||||
{{#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}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 margin-bottom-100">
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -43,7 +43,8 @@ export default Route.extend(AuthenticatedRouteMixin, {
|
|||
sections: this.modelFor('document').sections,
|
||||
permissions: this.modelFor('document').permissions,
|
||||
roles: this.modelFor('document').roles,
|
||||
blocks: this.modelFor('document').blocks
|
||||
blocks: this.modelFor('document').blocks,
|
||||
versions: this.modelFor('document').versions
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -57,10 +58,11 @@ export default Route.extend(AuthenticatedRouteMixin, {
|
|||
controller.set('permissions', model.permissions);
|
||||
controller.set('roles', model.roles);
|
||||
controller.set('blocks', model.blocks);
|
||||
controller.set('versions', model.versions);
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
activate: function () {
|
||||
this._super(...arguments);
|
||||
window.scrollTo(0,0);
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{{toolbar/nav-bar}}
|
||||
|
||||
{{toolbar/for-document document=document spaces=folders space=folder permissions=permissions roles=roles tab=tab
|
||||
{{toolbar/nav-bar}} {{toolbar/for-document document=document spaces=folders space=folder
|
||||
permissions=permissions roles=roles tab=tab versions=versions
|
||||
onDocumentDelete=(action 'onDocumentDelete')
|
||||
onSaveTemplate=(action 'onSaveTemplate')
|
||||
onSaveDocument=(action 'onSaveDocument')
|
||||
|
@ -10,9 +9,10 @@
|
|||
<div class="row">
|
||||
<div class="col-12">
|
||||
{{document/document-heading document=document permissions=permissions
|
||||
versions=versions
|
||||
onSaveDocument=(action 'onSaveDocument')}}
|
||||
|
||||
{{document/document-meta document=document folder=folder folders=folders permissions=permissions pages=pages
|
||||
{{document/document-meta document=document folder=folder folders=folders
|
||||
permissions=permissions pages=pages versions=versions
|
||||
onSaveDocument=(action 'onSaveDocument')}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -38,8 +38,8 @@
|
|||
|
||||
{{#if (eq tab 'content')}}
|
||||
{{document/view-content
|
||||
document=document links=links pages=pages blocks=blocks currentPageId=currentPageId
|
||||
folder=folder folders=folders sections=sections permissions=permissions roles=roles
|
||||
document=document links=links pages=pages blocks=blocks currentPageId=currentPageId
|
||||
folder=folder folders=folders sections=sections permissions=permissions roles=roles
|
||||
onSavePage=(action 'onSavePage') onInsertSection=(action 'onInsertSection')
|
||||
onSavePageAsBlock=(action 'onSavePageAsBlock') onDeleteBlock=(action 'onDeleteBlock')
|
||||
onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onPageDeleted')
|
||||
|
@ -56,4 +56,4 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -32,6 +32,7 @@ export default Route.extend(AuthenticatedRouteMixin, {
|
|||
this.set('permissions', data.permissions);
|
||||
this.set('roles', data.roles);
|
||||
this.set('links', data.links);
|
||||
this.set('versions', data.versions);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
@ -45,13 +46,14 @@ export default Route.extend(AuthenticatedRouteMixin, {
|
|||
permissions: this.get('permissions'),
|
||||
roles: this.get('roles'),
|
||||
links: this.get('links'),
|
||||
versions: this.get('versions'),
|
||||
sections: this.get('sectionService').getAll(),
|
||||
blocks: this.get('sectionService').getSpaceBlocks(this.get('folder.id'))
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
error(error /*, transition*/ ) {
|
||||
error(error /*, transition*/) {
|
||||
if (error) {
|
||||
this.transitionTo('/not-found');
|
||||
return false;
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
|
|
|
@ -334,6 +334,7 @@ export default Service.extend({
|
|||
folders: [],
|
||||
folder: {},
|
||||
links: [],
|
||||
versions: [],
|
||||
};
|
||||
|
||||
let doc = this.get('store').normalize('document', response.document);
|
||||
|
@ -357,6 +358,7 @@ export default Service.extend({
|
|||
data.folders = folders;
|
||||
data.folder = folders.findBy('id', doc.get('folderId'));
|
||||
data.links = response.links;
|
||||
data.versions = response.versions;
|
||||
|
||||
return data;
|
||||
}).catch((error) => {
|
||||
|
@ -366,7 +368,7 @@ export default Service.extend({
|
|||
|
||||
// fetchPages returns all pages, page meta and pending changes for document.
|
||||
// This method bulk fetches data to reduce network chatter.
|
||||
// We produce a bunch of calculated boolean's for UI display purposes
|
||||
// We produce a bunch of calculated boolean's for UI display purposes
|
||||
// that can tell us quickly about pending changes for UI display.
|
||||
fetchPages(documentId, currentUserId) {
|
||||
let constants = this.get('constants');
|
||||
|
@ -400,7 +402,7 @@ export default Service.extend({
|
|||
page.pending.forEach((i) => {
|
||||
let p = this.get('store').normalize('page', i.page);
|
||||
p = this.get('store').push(p);
|
||||
|
||||
|
||||
let m = this.get('store').normalize('page-meta', i.meta);
|
||||
m = this.get('store').push(m);
|
||||
|
||||
|
@ -451,7 +453,7 @@ export default Service.extend({
|
|||
userHasChangeRejected: userHasChangeRejected,
|
||||
userHasNewPagePending: p.isNewPageUserPending(this.get('sessionService.user.id'))
|
||||
};
|
||||
|
||||
|
||||
let pim = this.get('store').normalize('page-container', pi);
|
||||
pim = this.get('store').push(pim);
|
||||
data.pushObject(pim);
|
||||
|
|
|
@ -39,4 +39,4 @@ export default Service.extend({
|
|||
return error;
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
1
gui/app/styles/bootstrap.scss
vendored
1
gui/app/styles/bootstrap.scss
vendored
|
@ -100,6 +100,7 @@ $link-hover-decoration: none;
|
|||
@import "node_modules/bootstrap/scss/popover";
|
||||
@import "node_modules/bootstrap/scss/tooltip";
|
||||
@import "node_modules/bootstrap/scss/tables";
|
||||
@import "node_modules/bootstrap/scss/badge";
|
||||
// @import "node_modules/bootstrap/scss/navbar";
|
||||
// @import "node_modules/bootstrap/scss/images";
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
position: relative;
|
||||
margin: 0 0 30px 0;
|
||||
width: 100%;
|
||||
// height: 150px;
|
||||
|
||||
&:hover {
|
||||
> .checkbox {
|
||||
|
@ -33,26 +32,33 @@
|
|||
|
||||
> .title {
|
||||
color: $color-black;
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 5px;
|
||||
|
||||
> .version {
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
color: $color-gray;
|
||||
}
|
||||
}
|
||||
|
||||
> .space {
|
||||
color: $color-off-black;
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
> .snippet {
|
||||
color: $color-gray;
|
||||
font-size: 1rem;
|
||||
line-height: 24px;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $color-gray;
|
||||
|
||||
> .title {
|
||||
color: $color-link;
|
||||
}
|
||||
|
||||
> .snippet {
|
||||
color: $color-link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +67,7 @@
|
|||
display: inline-block;
|
||||
margin: 5px 10px 0 5px;
|
||||
color: $color-gray;
|
||||
font-size: 0.875rem;
|
||||
font-size: 1rem;
|
||||
font-style: italic;
|
||||
|
||||
&:hover {
|
||||
|
|
|
@ -61,6 +61,27 @@
|
|||
box-shadow: 0 27px 24px 0 rgba(0, 0, 0, 0.2), 0 40px 77px 0 rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
font-size: 1.5rem;
|
||||
color: $color-gray-light;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.drag-indicator-dropzone {
|
||||
opacity: 1 !important;
|
||||
border: 2px dotted $color-border;
|
||||
}
|
||||
|
||||
.drag-indicator-chosen {
|
||||
opacity: 1 !important;
|
||||
background: $color-off-white;
|
||||
}
|
||||
|
||||
.drag-indicator-dragged {
|
||||
opacity: 1 !important;
|
||||
@include card();
|
||||
}
|
||||
|
||||
@import "widget-avatar";
|
||||
@import "widget-button";
|
||||
@import "widget-checkbox";
|
||||
|
|
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="snippet">{{ document.excerpt }}</div>
|
||||
{{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}}
|
||||
|
||||
{{#if hasDocumentActions}}
|
||||
|
@ -39,11 +42,13 @@
|
|||
</ul>
|
||||
</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>
|
||||
{{/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>
|
||||
{{ui/ui-list-picker items=moveOptions nameField='name' singleSelect=true}}
|
||||
{{/ui/ui-dialog}}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<div class="modal-header">Space Permissions</div>
|
||||
<div class="modal-body" style="overflow-x: auto;">
|
||||
<div class="space-admin table-responsive">
|
||||
|
||||
|
||||
<table class="table table-hover permission-table mb-3">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -24,6 +24,8 @@
|
|||
<th class="text-info">Copy</th>
|
||||
<th class="text-info">Templates</th>
|
||||
<th class="text-info">Approval</th>
|
||||
<th class="text-info">Lifecycle</th>
|
||||
<th class="text-info">Versions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -38,53 +40,35 @@
|
|||
<small class="form-text text-muted d-inline-block">({{permission.members}})</small>
|
||||
</span>
|
||||
{{else}}
|
||||
{{#if (eq permission.whoId constants.EveryoneUserId)}}
|
||||
<span class="button-icon-green button-icon-small align-middle">
|
||||
<i class="material-icons">language</i>
|
||||
</span>
|
||||
<span class="color-green"> {{permission.name}}</span>
|
||||
{{else}}
|
||||
<span class="button-icon-gray button-icon-small align-middle">
|
||||
<i class="material-icons">person</i>
|
||||
</span>
|
||||
<span class=""> {{permission.name}}
|
||||
{{#if (eq permission.whoId session.user.id)}}
|
||||
<small class="form-text text-muted d-inline-block">(you)</small>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
{{#if (eq permission.whoId constants.EveryoneUserId)}}
|
||||
<span class="button-icon-green button-icon-small align-middle">
|
||||
<i class="material-icons">language</i>
|
||||
</span>
|
||||
<span class="color-green"> {{permission.name}}</span>
|
||||
{{else}}
|
||||
<span class="button-icon-gray button-icon-small align-middle">
|
||||
<i class="material-icons">person</i>
|
||||
</span>
|
||||
<span class=""> {{permission.name}}
|
||||
{{#if (eq permission.whoId session.user.id)}}
|
||||
<small class="form-text text-muted d-inline-block">(you)</small>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
{{input type="checkbox" id=(concat 'space-role-view-' permission.whoId) checked=permission.spaceView}}
|
||||
</td>
|
||||
<td>
|
||||
{{input type="checkbox" id=(concat 'space-role-manage-' permission.whoId) checked=permission.spaceManage}}
|
||||
</td>
|
||||
<td>
|
||||
{{input type="checkbox" id=(concat 'space-role-owner-' permission.whoId) checked=permission.spaceOwner}}
|
||||
</td>
|
||||
<td>
|
||||
{{input type="checkbox" id=(concat 'doc-role-add-' permission.whoId) checked=permission.documentAdd}}
|
||||
</td>
|
||||
<td>
|
||||
{{input type="checkbox" id=(concat 'doc-role-edit-' permission.whoId) checked=permission.documentEdit}}
|
||||
</td>
|
||||
<td>
|
||||
{{input type="checkbox" id=(concat 'doc-role-delete-' permission.whoId) checked=permission.documentDelete}}
|
||||
</td>
|
||||
<td>
|
||||
{{input type="checkbox" id=(concat 'doc-role-move-' permission.whoId) checked=permission.documentMove}}
|
||||
</td>
|
||||
<td>
|
||||
{{input type="checkbox" id=(concat 'doc-role-copy-' permission.whoId) checked=permission.documentCopy}}
|
||||
</td>
|
||||
<td>
|
||||
{{input type="checkbox" id=(concat 'doc-role-template-' permission.whoId) checked=permission.documentTemplate}}
|
||||
</td>
|
||||
<td>
|
||||
{{input type="checkbox" id=(concat 'doc-role-approve-' permission.whoId) checked=permission.documentApprove}}
|
||||
</td>
|
||||
<td>{{input type="checkbox" id=(concat 'space-role-view-' permission.whoId) checked=permission.spaceView}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'space-role-manage-' permission.whoId) checked=permission.spaceManage}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'space-role-owner-' permission.whoId) checked=permission.spaceOwner}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'doc-role-add-' permission.whoId) checked=permission.documentAdd}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'doc-role-edit-' permission.whoId) checked=permission.documentEdit}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'doc-role-delete-' permission.whoId) checked=permission.documentDelete}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'doc-role-move-' permission.whoId) checked=permission.documentMove}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'doc-role-copy-' permission.whoId) checked=permission.documentCopy}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'doc-role-template-' permission.whoId) checked=permission.documentTemplate}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'doc-role-approve-' permission.whoId) checked=permission.documentApprove}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'doc-role-lifecycle-' permission.whoId) checked=permission.documentLifecycle}}</td>
|
||||
<td>{{input type="checkbox" id=(concat 'doc-role-version-' permission.whoId) checked=permission.documentVersion}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
|
@ -108,13 +92,13 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-success" onclick={{action 'setPermissions'}}>Save</button>
|
||||
<button type="button" class="btn btn-success" onclick= {{action 'setPermissions'}}>Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,15 +1,23 @@
|
|||
<div class="view-search my-5">
|
||||
<div class="heading">{{resultPhrase}}</div>
|
||||
<ul class="documents">
|
||||
{{#each documents key="id" as |result index|}}
|
||||
<div class="heading">{{resultPhrase}}</div>
|
||||
<ul class="documents">
|
||||
{{#each documents key="id" as |result index|}}
|
||||
<li class="document">
|
||||
<a class="link" href="s/{{result.spaceId}}/{{result.spaceSlug}}/d/{{ result.documentId }}/{{result.documentSlug}}?page={{ result.itemId }}">
|
||||
<div class="title">{{result.document}}</div>
|
||||
<a class="link" href="s/{{result.spaceId}}/{{result.spaceSlug}}/d/{{ result.documentId }}/{{result.documentSlug}}?page={{ result.itemId }}">
|
||||
<div class="title">
|
||||
{{result.document}}
|
||||
{{#if (gt result.versionId.length 0)}}
|
||||
<span class="version"> {{result.versionId}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="space">{{result.space}}</div>
|
||||
<div class="snippet">{{result.excerpt}}</div>
|
||||
<div class="snippet">({{result.space}})</div>
|
||||
{{folder/document-tags documentTags=result.tags}}
|
||||
</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{#if result.template}}
|
||||
<button type="button" class="mt-3 btn btn-warning text-uppercase font-weight-bold">TEMPLATE</button>
|
||||
{{/if}}
|
||||
</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
3933
gui/package-lock.json
generated
3933
gui/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "documize",
|
||||
"version": "1.58.0",
|
||||
"version": "1.59.0",
|
||||
"description": "The Document IDE",
|
||||
"private": true,
|
||||
"repository": "",
|
||||
|
@ -51,6 +51,7 @@
|
|||
"bootstrap": "^4.0.0",
|
||||
"mermaid": "^7.1.2",
|
||||
"node-sass": "^4.7.2",
|
||||
"npm": "^5.7.1",
|
||||
"popper.js": "^1.12.9"
|
||||
}
|
||||
}
|
||||
|
|
116
gui/vendor/sortable.js
vendored
116
gui/vendor/sortable.js
vendored
|
@ -1,3 +1,65 @@
|
|||
/**
|
||||
* jQuery plugin for Sortable
|
||||
* @author RubaXa <trash@rubaxa.org>
|
||||
* @license MIT
|
||||
*/
|
||||
(function (factory) {
|
||||
"use strict";
|
||||
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define(["jquery"], factory);
|
||||
}
|
||||
else {
|
||||
/* jshint sub:true */
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function ($) {
|
||||
"use strict";
|
||||
|
||||
|
||||
/* CODE */
|
||||
|
||||
|
||||
/**
|
||||
* jQuery plugin for Sortable
|
||||
* @param {Object|String} options
|
||||
* @param {..*} [args]
|
||||
* @returns {jQuery|*}
|
||||
*/
|
||||
$.fn.sortable = function (options) {
|
||||
var retVal,
|
||||
args = arguments;
|
||||
|
||||
this.each(function () {
|
||||
var $el = $(this),
|
||||
sortable = $el.data('sortable');
|
||||
|
||||
if (!sortable && (options instanceof Object || !options)) {
|
||||
sortable = new Sortable(this, options);
|
||||
$el.data('sortable', sortable);
|
||||
}
|
||||
|
||||
if (sortable) {
|
||||
if (options === 'widget') {
|
||||
retVal = sortable;
|
||||
}
|
||||
else if (options === 'destroy') {
|
||||
sortable.destroy();
|
||||
$el.removeData('sortable');
|
||||
}
|
||||
else if (typeof sortable[options] === 'function') {
|
||||
retVal = sortable[options].apply(sortable, [].slice.call(args, 1));
|
||||
}
|
||||
else if (options in sortable.options) {
|
||||
retVal = sortable.option.apply(sortable, args);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (retVal === void 0) ? this : retVal;
|
||||
};
|
||||
});
|
||||
|
||||
/**!
|
||||
* Sortable
|
||||
* @author RubaXa <trash@rubaxa.org>
|
||||
|
@ -109,7 +171,7 @@
|
|||
|
||||
scrollOffsetX,
|
||||
scrollOffsetY
|
||||
;
|
||||
;
|
||||
|
||||
// Delect scrollEl
|
||||
if (scrollParentEl !== rootEl) {
|
||||
|
@ -160,7 +222,7 @@
|
|||
scrollOffsetY = vy ? vy * speed : 0;
|
||||
scrollOffsetX = vx ? vx * speed : 0;
|
||||
|
||||
if ('function' === typeof(scrollCustomFn)) {
|
||||
if ('function' === typeof (scrollCustomFn)) {
|
||||
return scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt);
|
||||
}
|
||||
|
||||
|
@ -202,7 +264,7 @@
|
|||
var originalGroup = options.group;
|
||||
|
||||
if (!originalGroup || typeof originalGroup != 'object') {
|
||||
originalGroup = {name: originalGroup};
|
||||
originalGroup = { name: originalGroup };
|
||||
}
|
||||
|
||||
group.name = originalGroup.name;
|
||||
|
@ -212,7 +274,7 @@
|
|||
|
||||
options.group = group;
|
||||
}
|
||||
;
|
||||
;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -261,7 +323,7 @@
|
|||
fallbackClass: 'sortable-fallback',
|
||||
fallbackOnBody: false,
|
||||
fallbackTolerance: 0,
|
||||
fallbackOffset: {x: 0, y: 0}
|
||||
fallbackOffset: { x: 0, y: 0 }
|
||||
};
|
||||
|
||||
|
||||
|
@ -555,7 +617,7 @@
|
|||
|
||||
_onTouchMove: function (/**TouchEvent*/evt) {
|
||||
if (tapEvt) {
|
||||
var options = this.options,
|
||||
var options = this.options,
|
||||
fallbackTolerance = options.fallbackTolerance,
|
||||
fallbackOffset = options.fallbackOffset,
|
||||
touch = evt.touches ? evt.touches[0] : evt,
|
||||
|
@ -781,7 +843,7 @@
|
|||
halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5,
|
||||
nextSibling = target.nextElementSibling,
|
||||
after = false
|
||||
;
|
||||
;
|
||||
|
||||
if (floating) {
|
||||
var elTop = dragEl.offsetTop,
|
||||
|
@ -795,7 +857,7 @@
|
|||
} else {
|
||||
after = tgTop > elTop;
|
||||
}
|
||||
} else if (!isMovingBetweenSortable) {
|
||||
} else if (!isMovingBetweenSortable) {
|
||||
after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
|
||||
}
|
||||
|
||||
|
@ -963,30 +1025,30 @@
|
|||
this._nulling();
|
||||
},
|
||||
|
||||
_nulling: function() {
|
||||
_nulling: function () {
|
||||
rootEl =
|
||||
dragEl =
|
||||
parentEl =
|
||||
ghostEl =
|
||||
nextEl =
|
||||
cloneEl =
|
||||
lastDownEl =
|
||||
dragEl =
|
||||
parentEl =
|
||||
ghostEl =
|
||||
nextEl =
|
||||
cloneEl =
|
||||
lastDownEl =
|
||||
|
||||
scrollEl =
|
||||
scrollParentEl =
|
||||
scrollEl =
|
||||
scrollParentEl =
|
||||
|
||||
tapEvt =
|
||||
touchEvt =
|
||||
tapEvt =
|
||||
touchEvt =
|
||||
|
||||
moved =
|
||||
newIndex =
|
||||
moved =
|
||||
newIndex =
|
||||
|
||||
lastEl =
|
||||
lastCSS =
|
||||
lastEl =
|
||||
lastCSS =
|
||||
|
||||
putSortable =
|
||||
activeGroup =
|
||||
Sortable.active = null;
|
||||
putSortable =
|
||||
activeGroup =
|
||||
Sortable.active = null;
|
||||
|
||||
savedInputChecked.forEach(function (el) {
|
||||
el.checked = true;
|
||||
|
@ -1455,7 +1517,7 @@
|
|||
};
|
||||
}
|
||||
}));
|
||||
} catch (err) {}
|
||||
} catch (err) { }
|
||||
|
||||
// Export utils
|
||||
Sortable.utils = {
|
||||
|
|
1602
gui/yarn.lock
1602
gui/yarn.lock
File diff suppressed because it is too large
Load diff
16
meta.json
16
meta.json
|
@ -1,16 +1,14 @@
|
|||
{
|
||||
"community":
|
||||
{
|
||||
"version": "1.58.0",
|
||||
"community": {
|
||||
"version": "1.59.0",
|
||||
"major": 1,
|
||||
"minor": 58,
|
||||
"minor": 59,
|
||||
"patch": 0
|
||||
},
|
||||
"enterprise":
|
||||
{
|
||||
"version": "1.60.0",
|
||||
"enterprise": {
|
||||
"version": "1.61.0",
|
||||
"major": 1,
|
||||
"minor": 60,
|
||||
"minor": 61,
|
||||
"patch": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,6 +95,12 @@ const (
|
|||
|
||||
// TypeSentSecureLink records user sending secure document link to email address(es)
|
||||
TypeSentSecureLink Type = 12
|
||||
|
||||
// TypeDraft records user marking space/document as draft
|
||||
TypeDraft Type = 13
|
||||
|
||||
// TypeVersioned records user creating new document version
|
||||
TypeVersioned Type = 14
|
||||
)
|
||||
|
||||
// TypeName returns one-work descriptor for activity type
|
||||
|
|
|
@ -22,18 +22,23 @@ import (
|
|||
// Document represents the purpose of Documize.
|
||||
type Document struct {
|
||||
model.BaseEntity
|
||||
OrgID string `json:"orgId"`
|
||||
LabelID string `json:"folderId"`
|
||||
UserID string `json:"userId"`
|
||||
Job string `json:"job"`
|
||||
Location string `json:"location"`
|
||||
Title string `json:"name"`
|
||||
Excerpt string `json:"excerpt"`
|
||||
Slug string `json:"-"`
|
||||
Tags string `json:"tags"`
|
||||
Template bool `json:"template"`
|
||||
Protection workflow.Protection `json:"protection"`
|
||||
Approval workflow.Approval `json:"approval"`
|
||||
OrgID string `json:"orgId"`
|
||||
LabelID string `json:"folderId"`
|
||||
UserID string `json:"userId"`
|
||||
Job string `json:"job"`
|
||||
Location string `json:"location"`
|
||||
Title string `json:"name"`
|
||||
Excerpt string `json:"excerpt"`
|
||||
Slug string `json:"-"`
|
||||
Tags string `json:"tags"`
|
||||
Template bool `json:"template"`
|
||||
Protection workflow.Protection `json:"protection"`
|
||||
Approval workflow.Approval `json:"approval"`
|
||||
Lifecycle workflow.Lifecycle `json:"lifecycle"`
|
||||
Versioned bool `json:"versioned"`
|
||||
VersionID string `json:"versionId"`
|
||||
VersionOrder int `json:"versionOrder"`
|
||||
GroupID string `json:"groupId"`
|
||||
}
|
||||
|
||||
// SetDefaults ensures on blanks and cleans.
|
||||
|
@ -89,3 +94,9 @@ type SitemapDocument struct {
|
|||
Folder string
|
||||
Revised time.Time
|
||||
}
|
||||
|
||||
// Version points to a version of a document.
|
||||
type Version struct {
|
||||
VersionID string `json:"versionId"`
|
||||
DocumentID string `json:"documentId"`
|
||||
}
|
||||
|
|
|
@ -93,6 +93,12 @@ const (
|
|||
// DocumentApprove means you can approve a change to a document
|
||||
DocumentApprove Action = "doc-approve"
|
||||
|
||||
// DocumentLifecycle means you can move a document between DRAFT/LIVE/ARCHIVE states
|
||||
DocumentLifecycle Action = "doc-lifecycle"
|
||||
|
||||
// DocumentVersion means you can manage document versions
|
||||
DocumentVersion Action = "doc-version"
|
||||
|
||||
// CategoryView action means you can view a category and documents therein
|
||||
CategoryView Action = "view"
|
||||
)
|
||||
|
|
|
@ -15,21 +15,23 @@ package permission
|
|||
// This data structure is made from database permission records for the space,
|
||||
// and it is designed to be sent to HTTP clients (web, mobile).
|
||||
type Record struct {
|
||||
OrgID string `json:"orgId"`
|
||||
SpaceID string `json:"folderId"`
|
||||
WhoID string `json:"whoId"`
|
||||
Who WhoType `json:"who"`
|
||||
SpaceView bool `json:"spaceView"`
|
||||
SpaceManage bool `json:"spaceManage"`
|
||||
SpaceOwner bool `json:"spaceOwner"`
|
||||
DocumentAdd bool `json:"documentAdd"`
|
||||
DocumentEdit bool `json:"documentEdit"`
|
||||
DocumentDelete bool `json:"documentDelete"`
|
||||
DocumentMove bool `json:"documentMove"`
|
||||
DocumentCopy bool `json:"documentCopy"`
|
||||
DocumentTemplate bool `json:"documentTemplate"`
|
||||
DocumentApprove bool `json:"documentApprove"`
|
||||
Name string `json:"name"` // read-only, user or group name
|
||||
OrgID string `json:"orgId"`
|
||||
SpaceID string `json:"folderId"`
|
||||
WhoID string `json:"whoId"`
|
||||
Who WhoType `json:"who"`
|
||||
SpaceView bool `json:"spaceView"`
|
||||
SpaceManage bool `json:"spaceManage"`
|
||||
SpaceOwner bool `json:"spaceOwner"`
|
||||
DocumentAdd bool `json:"documentAdd"`
|
||||
DocumentEdit bool `json:"documentEdit"`
|
||||
DocumentDelete bool `json:"documentDelete"`
|
||||
DocumentMove bool `json:"documentMove"`
|
||||
DocumentCopy bool `json:"documentCopy"`
|
||||
DocumentTemplate bool `json:"documentTemplate"`
|
||||
DocumentApprove bool `json:"documentApprove"`
|
||||
DocumentLifecycle bool `json:"documentLifecycle"`
|
||||
DocumentVersion bool `json:"documentVersion"`
|
||||
Name string `json:"name"` // read-only, user or group name
|
||||
}
|
||||
|
||||
// DecodeUserPermissions returns a flat, usable permission summary record
|
||||
|
@ -67,6 +69,10 @@ func DecodeUserPermissions(perm []Permission) (r Record) {
|
|||
r.DocumentTemplate = true
|
||||
case DocumentApprove:
|
||||
r.DocumentApprove = true
|
||||
case DocumentLifecycle:
|
||||
r.DocumentLifecycle = true
|
||||
case DocumentVersion:
|
||||
r.DocumentVersion = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,6 +113,12 @@ func EncodeUserPermissions(r Record) (perm []Permission) {
|
|||
if r.DocumentApprove {
|
||||
perm = append(perm, EncodeRecord(r, DocumentApprove))
|
||||
}
|
||||
if r.DocumentVersion {
|
||||
perm = append(perm, EncodeRecord(r, DocumentVersion))
|
||||
}
|
||||
if r.DocumentLifecycle {
|
||||
perm = append(perm, EncodeRecord(r, DocumentLifecycle))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -114,7 +126,8 @@ func EncodeUserPermissions(r Record) (perm []Permission) {
|
|||
// HasAnyPermission returns true if user has at least one permission.
|
||||
func HasAnyPermission(p Record) bool {
|
||||
return p.SpaceView || p.SpaceManage || p.SpaceOwner || p.DocumentAdd || p.DocumentEdit ||
|
||||
p.DocumentDelete || p.DocumentMove || p.DocumentCopy || p.DocumentTemplate || p.DocumentApprove
|
||||
p.DocumentDelete || p.DocumentMove || p.DocumentCopy || p.DocumentTemplate || p.DocumentApprove ||
|
||||
p.DocumentLifecycle || p.DocumentVersion
|
||||
}
|
||||
|
||||
// EncodeRecord creates standard permission record representing user permissions for a space.
|
||||
|
@ -138,7 +151,6 @@ type CategoryViewRequestModel struct {
|
|||
CategoryID string `json:"categoryID"`
|
||||
WhoID string `json:"whoId"`
|
||||
Who WhoType `json:"who"`
|
||||
// UserID string `json:"userId"`
|
||||
}
|
||||
|
||||
// SpaceRequestModel details which users have what permissions on a given space.
|
||||
|
|
|
@ -34,4 +34,6 @@ type QueryResult struct {
|
|||
SpaceID string `json:"spaceId"`
|
||||
Space string `json:"space"`
|
||||
SpaceSlug string `json:"spaceSlug"`
|
||||
Template bool `json:"template"`
|
||||
VersionID string `json:"versionId"`
|
||||
}
|
||||
|
|
|
@ -63,3 +63,17 @@ const (
|
|||
// ChangePendingNew means a new section to a document is pending review
|
||||
ChangePendingNew ChangeStatus = 4
|
||||
)
|
||||
|
||||
// Lifecycle tells us if document is in Draft, Live, Archived
|
||||
type Lifecycle int
|
||||
|
||||
const (
|
||||
// LifecycleDraft means document is in draft mode with restricted viewing
|
||||
LifecycleDraft Lifecycle = 0
|
||||
|
||||
// LifecycleLive means document can be seen by all
|
||||
LifecycleLive Lifecycle = 1
|
||||
|
||||
// LifecycleArchived means document has been archived
|
||||
LifecycleArchived Lifecycle = 2
|
||||
)
|
||||
|
|
|
@ -13,8 +13,10 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
|
@ -77,9 +79,10 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http
|
|||
|
||||
var org = org.Organization{}
|
||||
var err = errors.New("")
|
||||
var dom string
|
||||
|
||||
if len(rc.OrgID) == 0 {
|
||||
dom := organization.GetRequestSubdomain(r)
|
||||
dom = organization.GetRequestSubdomain(r)
|
||||
dom = m.Store.Organization.CheckDomain(rc, dom)
|
||||
org, err = m.Store.Organization.GetOrganizationByDomain(dom)
|
||||
} else {
|
||||
|
@ -88,6 +91,12 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http
|
|||
|
||||
// Inability to find org record spells the end of this request.
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
response.WriteForbiddenError(w)
|
||||
m.Runtime.Log.Info(fmt.Sprintf("unable to find org (domain: %s, orgID: %s)", dom, rc.OrgID))
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteForbiddenError(w)
|
||||
m.Runtime.Log.Error(method, err)
|
||||
return
|
||||
|
|
|
@ -88,6 +88,16 @@ func Add(rt *env.Runtime, prefix, path string, methods, queries []string, endPtF
|
|||
return nil
|
||||
}
|
||||
|
||||
// AddPrivate endpoint
|
||||
func AddPrivate(rt *env.Runtime, path string, methods, queries []string, endPtFn RouteFunc) error {
|
||||
return Add(rt, RoutePrefixPrivate, path, methods, queries, endPtFn)
|
||||
}
|
||||
|
||||
// AddPublic endpoint
|
||||
func AddPublic(rt *env.Runtime, path string, methods, queries []string, endPtFn RouteFunc) error {
|
||||
return Add(rt, RoutePrefixPublic, path, methods, queries, endPtFn)
|
||||
}
|
||||
|
||||
// Remove an endpoint.
|
||||
func Remove(rt *env.Runtime, prefix, path string, methods, queries []string) error {
|
||||
k, e := routesKey(rt, prefix, path, methods, queries)
|
||||
|
|
|
@ -70,8 +70,8 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) {
|
|||
// Non-secure public info routes
|
||||
//**************************************************
|
||||
|
||||
Add(rt, RoutePrefixPublic, "meta", []string{"GET", "OPTIONS"}, nil, meta.Meta)
|
||||
Add(rt, RoutePrefixPublic, "version", []string{"GET", "OPTIONS"}, nil, func(w http.ResponseWriter, r *http.Request) {
|
||||
AddPublic(rt, "meta", []string{"GET", "OPTIONS"}, nil, meta.Meta)
|
||||
AddPublic(rt, "version", []string{"GET", "OPTIONS"}, nil, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(rt.Product.Version))
|
||||
})
|
||||
|
||||
|
@ -79,129 +79,129 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) {
|
|||
// Non-secure public service routes
|
||||
//**************************************************
|
||||
|
||||
Add(rt, RoutePrefixPublic, "authenticate/keycloak", []string{"POST", "OPTIONS"}, nil, keycloak.Authenticate)
|
||||
Add(rt, RoutePrefixPublic, "authenticate", []string{"POST", "OPTIONS"}, nil, auth.Login)
|
||||
Add(rt, RoutePrefixPublic, "validate", []string{"GET", "OPTIONS"}, nil, auth.ValidateToken)
|
||||
Add(rt, RoutePrefixPublic, "forgot", []string{"POST", "OPTIONS"}, nil, user.ForgotPassword)
|
||||
Add(rt, RoutePrefixPublic, "reset/{token}", []string{"POST", "OPTIONS"}, nil, user.ResetPassword)
|
||||
Add(rt, RoutePrefixPublic, "share/{spaceID}", []string{"POST", "OPTIONS"}, nil, space.AcceptInvitation)
|
||||
Add(rt, RoutePrefixPublic, "attachments/{orgID}/{attachmentID}", []string{"GET", "OPTIONS"}, nil, attachment.Download)
|
||||
AddPublic(rt, "authenticate/keycloak", []string{"POST", "OPTIONS"}, nil, keycloak.Authenticate)
|
||||
AddPublic(rt, "authenticate", []string{"POST", "OPTIONS"}, nil, auth.Login)
|
||||
AddPublic(rt, "validate", []string{"GET", "OPTIONS"}, nil, auth.ValidateToken)
|
||||
AddPublic(rt, "forgot", []string{"POST", "OPTIONS"}, nil, user.ForgotPassword)
|
||||
AddPublic(rt, "reset/{token}", []string{"POST", "OPTIONS"}, nil, user.ResetPassword)
|
||||
AddPublic(rt, "share/{spaceID}", []string{"POST", "OPTIONS"}, nil, space.AcceptInvitation)
|
||||
AddPublic(rt, "attachments/{orgID}/{attachmentID}", []string{"GET", "OPTIONS"}, nil, attachment.Download)
|
||||
|
||||
//**************************************************
|
||||
// Secured private routes (require authentication)
|
||||
//**************************************************
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "import/folder/{folderID}", []string{"POST", "OPTIONS"}, nil, conversion.UploadConvert)
|
||||
AddPrivate(rt, "import/folder/{folderID}", []string{"POST", "OPTIONS"}, nil, conversion.UploadConvert)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "documents", []string{"GET", "OPTIONS"}, nil, document.BySpace)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}", []string{"GET", "OPTIONS"}, nil, document.Get)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}", []string{"PUT", "OPTIONS"}, nil, document.Update)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}", []string{"DELETE", "OPTIONS"}, nil, document.Delete)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/level", []string{"POST", "OPTIONS"}, nil, page.ChangePageLevel)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/sequence", []string{"POST", "OPTIONS"}, nil, page.ChangePageSequence)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}/revisions", []string{"GET", "OPTIONS"}, nil, page.GetRevisions)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}/revisions/{revisionID}", []string{"GET", "OPTIONS"}, nil, page.GetDiff)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}/revisions/{revisionID}", []string{"POST", "OPTIONS"}, nil, page.Rollback)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/revisions", []string{"GET", "OPTIONS"}, nil, page.GetDocumentRevisions)
|
||||
AddPrivate(rt, "documents", []string{"GET", "OPTIONS"}, nil, document.BySpace)
|
||||
AddPrivate(rt, "documents/{documentID}", []string{"GET", "OPTIONS"}, nil, document.Get)
|
||||
AddPrivate(rt, "documents/{documentID}", []string{"PUT", "OPTIONS"}, nil, document.Update)
|
||||
AddPrivate(rt, "documents/{documentID}", []string{"DELETE", "OPTIONS"}, nil, document.Delete)
|
||||
AddPrivate(rt, "documents/{documentID}/pages/level", []string{"POST", "OPTIONS"}, nil, page.ChangePageLevel)
|
||||
AddPrivate(rt, "documents/{documentID}/pages/sequence", []string{"POST", "OPTIONS"}, nil, page.ChangePageSequence)
|
||||
AddPrivate(rt, "documents/{documentID}/pages/{pageID}/revisions", []string{"GET", "OPTIONS"}, nil, page.GetRevisions)
|
||||
AddPrivate(rt, "documents/{documentID}/pages/{pageID}/revisions/{revisionID}", []string{"GET", "OPTIONS"}, nil, page.GetDiff)
|
||||
AddPrivate(rt, "documents/{documentID}/pages/{pageID}/revisions/{revisionID}", []string{"POST", "OPTIONS"}, nil, page.Rollback)
|
||||
AddPrivate(rt, "documents/{documentID}/revisions", []string{"GET", "OPTIONS"}, nil, page.GetDocumentRevisions)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages", []string{"GET", "OPTIONS"}, nil, page.GetPages)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}", []string{"PUT", "OPTIONS"}, nil, page.Update)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}", []string{"DELETE", "OPTIONS"}, nil, page.Delete)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages", []string{"DELETE", "OPTIONS"}, nil, page.DeletePages)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}", []string{"GET", "OPTIONS"}, nil, page.GetPage)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages", []string{"POST", "OPTIONS"}, nil, page.Add)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/attachments", []string{"GET", "OPTIONS"}, nil, attachment.Get)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/attachments/{attachmentID}", []string{"DELETE", "OPTIONS"}, nil, attachment.Delete)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/attachments", []string{"POST", "OPTIONS"}, nil, attachment.Add)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}/meta", []string{"GET", "OPTIONS"}, nil, page.GetMeta)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}/copy/{targetID}", []string{"POST", "OPTIONS"}, nil, page.Copy)
|
||||
AddPrivate(rt, "documents/{documentID}/pages", []string{"GET", "OPTIONS"}, nil, page.GetPages)
|
||||
AddPrivate(rt, "documents/{documentID}/pages/{pageID}", []string{"PUT", "OPTIONS"}, nil, page.Update)
|
||||
AddPrivate(rt, "documents/{documentID}/pages/{pageID}", []string{"DELETE", "OPTIONS"}, nil, page.Delete)
|
||||
AddPrivate(rt, "documents/{documentID}/pages", []string{"DELETE", "OPTIONS"}, nil, page.DeletePages)
|
||||
AddPrivate(rt, "documents/{documentID}/pages/{pageID}", []string{"GET", "OPTIONS"}, nil, page.GetPage)
|
||||
AddPrivate(rt, "documents/{documentID}/pages", []string{"POST", "OPTIONS"}, nil, page.Add)
|
||||
AddPrivate(rt, "documents/{documentID}/attachments", []string{"GET", "OPTIONS"}, nil, attachment.Get)
|
||||
AddPrivate(rt, "documents/{documentID}/attachments/{attachmentID}", []string{"DELETE", "OPTIONS"}, nil, attachment.Delete)
|
||||
AddPrivate(rt, "documents/{documentID}/attachments", []string{"POST", "OPTIONS"}, nil, attachment.Add)
|
||||
AddPrivate(rt, "documents/{documentID}/pages/{pageID}/meta", []string{"GET", "OPTIONS"}, nil, page.GetMeta)
|
||||
AddPrivate(rt, "documents/{documentID}/pages/{pageID}/copy/{targetID}", []string{"POST", "OPTIONS"}, nil, page.Copy)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "organizations/{orgID}", []string{"GET", "OPTIONS"}, nil, organization.Get)
|
||||
Add(rt, RoutePrefixPrivate, "organizations/{orgID}", []string{"PUT", "OPTIONS"}, nil, organization.Update)
|
||||
AddPrivate(rt, "organizations/{orgID}", []string{"GET", "OPTIONS"}, nil, organization.Get)
|
||||
AddPrivate(rt, "organizations/{orgID}", []string{"PUT", "OPTIONS"}, nil, organization.Update)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}", []string{"DELETE", "OPTIONS"}, nil, space.Delete)
|
||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}/move/{moveToId}", []string{"DELETE", "OPTIONS"}, nil, space.Remove)
|
||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}/invitation", []string{"POST", "OPTIONS"}, nil, space.Invite)
|
||||
Add(rt, RoutePrefixPrivate, "space/manage", []string{"GET", "OPTIONS"}, nil, space.GetAll)
|
||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}", []string{"GET", "OPTIONS"}, nil, space.Get)
|
||||
Add(rt, RoutePrefixPrivate, "space", []string{"GET", "OPTIONS"}, nil, space.GetViewable)
|
||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}", []string{"PUT", "OPTIONS"}, nil, space.Update)
|
||||
Add(rt, RoutePrefixPrivate, "space", []string{"POST", "OPTIONS"}, nil, space.Add)
|
||||
AddPrivate(rt, "space/{spaceID}", []string{"DELETE", "OPTIONS"}, nil, space.Delete)
|
||||
AddPrivate(rt, "space/{spaceID}/move/{moveToId}", []string{"DELETE", "OPTIONS"}, nil, space.Remove)
|
||||
AddPrivate(rt, "space/{spaceID}/invitation", []string{"POST", "OPTIONS"}, nil, space.Invite)
|
||||
AddPrivate(rt, "space/manage", []string{"GET", "OPTIONS"}, nil, space.GetAll)
|
||||
AddPrivate(rt, "space/{spaceID}", []string{"GET", "OPTIONS"}, nil, space.Get)
|
||||
AddPrivate(rt, "space", []string{"GET", "OPTIONS"}, nil, space.GetViewable)
|
||||
AddPrivate(rt, "space/{spaceID}", []string{"PUT", "OPTIONS"}, nil, space.Update)
|
||||
AddPrivate(rt, "space", []string{"POST", "OPTIONS"}, nil, space.Add)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "category/space/{spaceID}/summary", []string{"GET", "OPTIONS"}, nil, category.GetSummary)
|
||||
Add(rt, RoutePrefixPrivate, "category/document/{documentID}", []string{"GET", "OPTIONS"}, nil, category.GetDocumentCategoryMembership)
|
||||
Add(rt, RoutePrefixPrivate, "category/space/{spaceID}", []string{"GET", "OPTIONS"}, []string{"filter", "all"}, category.GetAll)
|
||||
Add(rt, RoutePrefixPrivate, "category/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.Get)
|
||||
Add(rt, RoutePrefixPrivate, "category/member/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.GetSpaceCategoryMembers)
|
||||
Add(rt, RoutePrefixPrivate, "category/member", []string{"POST", "OPTIONS"}, nil, category.SetDocumentCategoryMembership)
|
||||
Add(rt, RoutePrefixPrivate, "category/{categoryID}", []string{"PUT", "OPTIONS"}, nil, category.Update)
|
||||
Add(rt, RoutePrefixPrivate, "category/{categoryID}", []string{"DELETE", "OPTIONS"}, nil, category.Delete)
|
||||
Add(rt, RoutePrefixPrivate, "category", []string{"POST", "OPTIONS"}, nil, category.Add)
|
||||
AddPrivate(rt, "category/space/{spaceID}/summary", []string{"GET", "OPTIONS"}, nil, category.GetSummary)
|
||||
AddPrivate(rt, "category/document/{documentID}", []string{"GET", "OPTIONS"}, nil, category.GetDocumentCategoryMembership)
|
||||
AddPrivate(rt, "category/space/{spaceID}", []string{"GET", "OPTIONS"}, []string{"filter", "all"}, category.GetAll)
|
||||
AddPrivate(rt, "category/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.Get)
|
||||
AddPrivate(rt, "category/member/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.GetSpaceCategoryMembers)
|
||||
AddPrivate(rt, "category/member", []string{"POST", "OPTIONS"}, nil, category.SetDocumentCategoryMembership)
|
||||
AddPrivate(rt, "category/{categoryID}", []string{"PUT", "OPTIONS"}, nil, category.Update)
|
||||
AddPrivate(rt, "category/{categoryID}", []string{"DELETE", "OPTIONS"}, nil, category.Delete)
|
||||
AddPrivate(rt, "category", []string{"POST", "OPTIONS"}, nil, category.Add)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "users/{userID}/password", []string{"POST", "OPTIONS"}, nil, user.ChangePassword)
|
||||
Add(rt, RoutePrefixPrivate, "users", []string{"POST", "OPTIONS"}, nil, user.Add)
|
||||
Add(rt, RoutePrefixPrivate, "users/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, user.GetSpaceUsers)
|
||||
Add(rt, RoutePrefixPrivate, "users", []string{"GET", "OPTIONS"}, nil, user.GetOrganizationUsers)
|
||||
Add(rt, RoutePrefixPrivate, "users/{userID}", []string{"GET", "OPTIONS"}, nil, user.Get)
|
||||
Add(rt, RoutePrefixPrivate, "users/{userID}", []string{"PUT", "OPTIONS"}, nil, user.Update)
|
||||
Add(rt, RoutePrefixPrivate, "users/{userID}", []string{"DELETE", "OPTIONS"}, nil, user.Delete)
|
||||
Add(rt, RoutePrefixPrivate, "users/sync", []string{"GET", "OPTIONS"}, nil, keycloak.Sync)
|
||||
Add(rt, RoutePrefixPrivate, "users/match", []string{"POST", "OPTIONS"}, nil, user.MatchUsers)
|
||||
Add(rt, RoutePrefixPrivate, "users/import", []string{"POST", "OPTIONS"}, nil, user.BulkImport)
|
||||
AddPrivate(rt, "users/{userID}/password", []string{"POST", "OPTIONS"}, nil, user.ChangePassword)
|
||||
AddPrivate(rt, "users", []string{"POST", "OPTIONS"}, nil, user.Add)
|
||||
AddPrivate(rt, "users/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, user.GetSpaceUsers)
|
||||
AddPrivate(rt, "users", []string{"GET", "OPTIONS"}, nil, user.GetOrganizationUsers)
|
||||
AddPrivate(rt, "users/{userID}", []string{"GET", "OPTIONS"}, nil, user.Get)
|
||||
AddPrivate(rt, "users/{userID}", []string{"PUT", "OPTIONS"}, nil, user.Update)
|
||||
AddPrivate(rt, "users/{userID}", []string{"DELETE", "OPTIONS"}, nil, user.Delete)
|
||||
AddPrivate(rt, "users/sync", []string{"GET", "OPTIONS"}, nil, keycloak.Sync)
|
||||
AddPrivate(rt, "users/match", []string{"POST", "OPTIONS"}, nil, user.MatchUsers)
|
||||
AddPrivate(rt, "users/import", []string{"POST", "OPTIONS"}, nil, user.BulkImport)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "search", []string{"POST", "OPTIONS"}, nil, document.SearchDocuments)
|
||||
AddPrivate(rt, "search", []string{"POST", "OPTIONS"}, nil, document.SearchDocuments)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "templates", []string{"POST", "OPTIONS"}, nil, template.SaveAs)
|
||||
Add(rt, RoutePrefixPrivate, "templates/{templateID}/folder/{folderID}", []string{"POST", "OPTIONS"}, []string{"type", "saved"}, template.Use)
|
||||
Add(rt, RoutePrefixPrivate, "templates/{folderID}", []string{"GET", "OPTIONS"}, nil, template.SavedList)
|
||||
AddPrivate(rt, "templates", []string{"POST", "OPTIONS"}, nil, template.SaveAs)
|
||||
AddPrivate(rt, "templates/{templateID}/folder/{folderID}", []string{"POST", "OPTIONS"}, []string{"type", "saved"}, template.Use)
|
||||
AddPrivate(rt, "templates/{folderID}", []string{"GET", "OPTIONS"}, nil, template.SavedList)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "sections", []string{"GET", "OPTIONS"}, nil, section.GetSections)
|
||||
Add(rt, RoutePrefixPrivate, "sections", []string{"POST", "OPTIONS"}, nil, section.RunSectionCommand)
|
||||
Add(rt, RoutePrefixPrivate, "sections/refresh", []string{"GET", "OPTIONS"}, nil, section.RefreshSections)
|
||||
Add(rt, RoutePrefixPrivate, "sections/blocks/space/{folderID}", []string{"GET", "OPTIONS"}, nil, block.GetBySpace)
|
||||
Add(rt, RoutePrefixPrivate, "sections/blocks/{blockID}", []string{"GET", "OPTIONS"}, nil, block.Get)
|
||||
Add(rt, RoutePrefixPrivate, "sections/blocks/{blockID}", []string{"PUT", "OPTIONS"}, nil, block.Update)
|
||||
Add(rt, RoutePrefixPrivate, "sections/blocks/{blockID}", []string{"DELETE", "OPTIONS"}, nil, block.Delete)
|
||||
Add(rt, RoutePrefixPrivate, "sections/blocks", []string{"POST", "OPTIONS"}, nil, block.Add)
|
||||
AddPrivate(rt, "sections", []string{"GET", "OPTIONS"}, nil, section.GetSections)
|
||||
AddPrivate(rt, "sections", []string{"POST", "OPTIONS"}, nil, section.RunSectionCommand)
|
||||
AddPrivate(rt, "sections/refresh", []string{"GET", "OPTIONS"}, nil, section.RefreshSections)
|
||||
AddPrivate(rt, "sections/blocks/space/{folderID}", []string{"GET", "OPTIONS"}, nil, block.GetBySpace)
|
||||
AddPrivate(rt, "sections/blocks/{blockID}", []string{"GET", "OPTIONS"}, nil, block.Get)
|
||||
AddPrivate(rt, "sections/blocks/{blockID}", []string{"PUT", "OPTIONS"}, nil, block.Update)
|
||||
AddPrivate(rt, "sections/blocks/{blockID}", []string{"DELETE", "OPTIONS"}, nil, block.Delete)
|
||||
AddPrivate(rt, "sections/blocks", []string{"POST", "OPTIONS"}, nil, block.Add)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "links/{folderID}/{documentID}/{pageID}", []string{"GET", "OPTIONS"}, nil, link.GetLinkCandidates)
|
||||
Add(rt, RoutePrefixPrivate, "links", []string{"GET", "OPTIONS"}, nil, link.SearchLinkCandidates)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/links", []string{"GET", "OPTIONS"}, nil, document.DocumentLinks)
|
||||
AddPrivate(rt, "links/{folderID}/{documentID}/{pageID}", []string{"GET", "OPTIONS"}, nil, link.GetLinkCandidates)
|
||||
AddPrivate(rt, "links", []string{"GET", "OPTIONS"}, nil, link.SearchLinkCandidates)
|
||||
AddPrivate(rt, "documents/{documentID}/links", []string{"GET", "OPTIONS"}, nil, document.DocumentLinks)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "global/smtp", []string{"GET", "OPTIONS"}, nil, setting.SMTP)
|
||||
Add(rt, RoutePrefixPrivate, "global/smtp", []string{"PUT", "OPTIONS"}, nil, setting.SetSMTP)
|
||||
Add(rt, RoutePrefixPrivate, "global/license", []string{"GET", "OPTIONS"}, nil, setting.License)
|
||||
Add(rt, RoutePrefixPrivate, "global/license", []string{"PUT", "OPTIONS"}, nil, setting.SetLicense)
|
||||
Add(rt, RoutePrefixPrivate, "global/auth", []string{"GET", "OPTIONS"}, nil, setting.AuthConfig)
|
||||
Add(rt, RoutePrefixPrivate, "global/auth", []string{"PUT", "OPTIONS"}, nil, setting.SetAuthConfig)
|
||||
AddPrivate(rt, "global/smtp", []string{"GET", "OPTIONS"}, nil, setting.SMTP)
|
||||
AddPrivate(rt, "global/smtp", []string{"PUT", "OPTIONS"}, nil, setting.SetSMTP)
|
||||
AddPrivate(rt, "global/license", []string{"GET", "OPTIONS"}, nil, setting.License)
|
||||
AddPrivate(rt, "global/license", []string{"PUT", "OPTIONS"}, nil, setting.SetLicense)
|
||||
AddPrivate(rt, "global/auth", []string{"GET", "OPTIONS"}, nil, setting.AuthConfig)
|
||||
AddPrivate(rt, "global/auth", []string{"PUT", "OPTIONS"}, nil, setting.SetAuthConfig)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "pin/{userID}", []string{"POST", "OPTIONS"}, nil, pin.Add)
|
||||
Add(rt, RoutePrefixPrivate, "pin/{userID}", []string{"GET", "OPTIONS"}, nil, pin.GetUserPins)
|
||||
Add(rt, RoutePrefixPrivate, "pin/{userID}/sequence", []string{"POST", "OPTIONS"}, nil, pin.UpdatePinSequence)
|
||||
Add(rt, RoutePrefixPrivate, "pin/{userID}/{pinID}", []string{"DELETE", "OPTIONS"}, nil, pin.DeleteUserPin)
|
||||
AddPrivate(rt, "pin/{userID}", []string{"POST", "OPTIONS"}, nil, pin.Add)
|
||||
AddPrivate(rt, "pin/{userID}", []string{"GET", "OPTIONS"}, nil, pin.GetUserPins)
|
||||
AddPrivate(rt, "pin/{userID}/sequence", []string{"POST", "OPTIONS"}, nil, pin.UpdatePinSequence)
|
||||
AddPrivate(rt, "pin/{userID}/{pinID}", []string{"DELETE", "OPTIONS"}, nil, pin.DeleteUserPin)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "group/{groupID}/members", []string{"GET", "OPTIONS"}, nil, group.GetGroupMembers)
|
||||
Add(rt, RoutePrefixPrivate, "group", []string{"POST", "OPTIONS"}, nil, group.Add)
|
||||
Add(rt, RoutePrefixPrivate, "group", []string{"GET", "OPTIONS"}, nil, group.Groups)
|
||||
Add(rt, RoutePrefixPrivate, "group/{groupID}", []string{"PUT", "OPTIONS"}, nil, group.Update)
|
||||
Add(rt, RoutePrefixPrivate, "group/{groupID}", []string{"DELETE", "OPTIONS"}, nil, group.Delete)
|
||||
Add(rt, RoutePrefixPrivate, "group/{groupID}/join/{userID}", []string{"POST", "OPTIONS"}, nil, group.JoinGroup)
|
||||
Add(rt, RoutePrefixPrivate, "group/{groupID}/leave/{userID}", []string{"DELETE", "OPTIONS"}, nil, group.LeaveGroup)
|
||||
AddPrivate(rt, "group/{groupID}/members", []string{"GET", "OPTIONS"}, nil, group.GetGroupMembers)
|
||||
AddPrivate(rt, "group", []string{"POST", "OPTIONS"}, nil, group.Add)
|
||||
AddPrivate(rt, "group", []string{"GET", "OPTIONS"}, nil, group.Groups)
|
||||
AddPrivate(rt, "group/{groupID}", []string{"PUT", "OPTIONS"}, nil, group.Update)
|
||||
AddPrivate(rt, "group/{groupID}", []string{"DELETE", "OPTIONS"}, nil, group.Delete)
|
||||
AddPrivate(rt, "group/{groupID}/join/{userID}", []string{"POST", "OPTIONS"}, nil, group.JoinGroup)
|
||||
AddPrivate(rt, "group/{groupID}/leave/{userID}", []string{"DELETE", "OPTIONS"}, nil, group.LeaveGroup)
|
||||
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/permissions", []string{"GET", "OPTIONS"}, nil, permission.GetDocumentPermissions)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/permissions", []string{"PUT", "OPTIONS"}, nil, permission.SetDocumentPermissions)
|
||||
Add(rt, RoutePrefixPrivate, "documents/{documentID}/permissions/user", []string{"GET", "OPTIONS"}, nil, permission.GetUserDocumentPermissions)
|
||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions", []string{"PUT", "OPTIONS"}, nil, permission.SetSpacePermissions)
|
||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions/user", []string{"GET", "OPTIONS"}, nil, permission.GetUserSpacePermissions)
|
||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions", []string{"GET", "OPTIONS"}, nil, permission.GetSpacePermissions)
|
||||
Add(rt, RoutePrefixPrivate, "category/{categoryID}/permission", []string{"PUT", "OPTIONS"}, nil, permission.SetCategoryPermissions)
|
||||
Add(rt, RoutePrefixPrivate, "category/{categoryID}/permission", []string{"GET", "OPTIONS"}, nil, permission.GetCategoryPermissions)
|
||||
Add(rt, RoutePrefixPrivate, "category/{categoryID}/user", []string{"GET", "OPTIONS"}, nil, permission.GetCategoryViewers)
|
||||
AddPrivate(rt, "documents/{documentID}/permissions", []string{"GET", "OPTIONS"}, nil, permission.GetDocumentPermissions)
|
||||
AddPrivate(rt, "documents/{documentID}/permissions", []string{"PUT", "OPTIONS"}, nil, permission.SetDocumentPermissions)
|
||||
AddPrivate(rt, "documents/{documentID}/permissions/user", []string{"GET", "OPTIONS"}, nil, permission.GetUserDocumentPermissions)
|
||||
AddPrivate(rt, "space/{spaceID}/permissions", []string{"PUT", "OPTIONS"}, nil, permission.SetSpacePermissions)
|
||||
AddPrivate(rt, "space/{spaceID}/permissions/user", []string{"GET", "OPTIONS"}, nil, permission.GetUserSpacePermissions)
|
||||
AddPrivate(rt, "space/{spaceID}/permissions", []string{"GET", "OPTIONS"}, nil, permission.GetSpacePermissions)
|
||||
AddPrivate(rt, "category/{categoryID}/permission", []string{"PUT", "OPTIONS"}, nil, permission.SetCategoryPermissions)
|
||||
AddPrivate(rt, "category/{categoryID}/permission", []string{"GET", "OPTIONS"}, nil, permission.GetCategoryPermissions)
|
||||
AddPrivate(rt, "category/{categoryID}/user", []string{"GET", "OPTIONS"}, nil, permission.GetCategoryViewers)
|
||||
|
||||
// fetch methods exist to speed up UI rendering by returning data in bulk
|
||||
Add(rt, RoutePrefixPrivate, "fetch/category/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.FetchSpaceData)
|
||||
Add(rt, RoutePrefixPrivate, "fetch/document/{documentID}", []string{"GET", "OPTIONS"}, nil, document.FetchDocumentData)
|
||||
Add(rt, RoutePrefixPrivate, "fetch/page/{documentID}", []string{"GET", "OPTIONS"}, nil, page.FetchPages)
|
||||
AddPrivate(rt, "fetch/category/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.FetchSpaceData)
|
||||
AddPrivate(rt, "fetch/document/{documentID}", []string{"GET", "OPTIONS"}, nil, document.FetchDocumentData)
|
||||
AddPrivate(rt, "fetch/page/{documentID}", []string{"GET", "OPTIONS"}, nil, page.FetchPages)
|
||||
|
||||
Add(rt, RoutePrefixRoot, "robots.txt", []string{"GET", "OPTIONS"}, nil, meta.RobotsTxt)
|
||||
Add(rt, RoutePrefixRoot, "sitemap.xml", []string{"GET", "OPTIONS"}, nil, meta.Sitemap)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue