1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 13:19:43 +02:00
documize/domain/page/endpoint.go

1470 lines
37 KiB
Go
Raw Normal View History

2017-08-01 15:35:13 +01:00
// 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 page
import (
"database/sql"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"github.com/documize/community/core/env"
"github.com/documize/community/core/request"
"github.com/documize/community/core/response"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain"
"github.com/documize/community/domain/link"
2017-09-18 17:53:42 +01:00
"github.com/documize/community/domain/permission"
2017-08-02 15:58:39 +01:00
indexer "github.com/documize/community/domain/search"
2017-08-01 15:35:13 +01:00
"github.com/documize/community/domain/section/provider"
"github.com/documize/community/domain/store"
2017-08-01 15:35:13 +01:00
"github.com/documize/community/model/activity"
"github.com/documize/community/model/audit"
dm "github.com/documize/community/model/doc"
2017-08-01 15:35:13 +01:00
"github.com/documize/community/model/page"
2018-01-10 16:07:17 +00:00
pm "github.com/documize/community/model/permission"
"github.com/documize/community/model/user"
2018-01-10 16:07:17 +00:00
"github.com/documize/community/model/workflow"
2017-08-01 15:35:13 +01:00
htmldiff "github.com/documize/html-diff"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
Store *store.Store
2017-08-02 15:58:39 +01:00
Indexer indexer.Indexer
2017-08-01 15:35:13 +01:00
}
// Add inserts new section into document.
func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
method := "page.add"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.IsValid(ctx) {
2017-08-01 15:35:13 +01:00
response.WriteBadLicense(w)
return
}
2018-01-10 16:07:17 +00:00
// check param
2017-08-01 15:35:13 +01:00
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
2018-01-10 16:07:17 +00:00
// read payload
2017-08-01 15:35:13 +01:00
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
model := new(page.NewPage)
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
if model.Page.DocumentID != documentID {
response.WriteBadRequestError(w, method, "documentID mismatch")
return
}
if model.Meta.DocumentID != documentID {
response.WriteBadRequestError(w, method, "documentID mismatch")
return
}
2018-01-10 16:07:17 +00:00
// Check protection and approval process
document, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
h.Runtime.Log.Error(method, err)
return
}
// Protect locked
if document.Protection == workflow.ProtectionLock {
response.WriteForbiddenError(w)
h.Runtime.Log.Info("attempted write to locked document")
return
}
// Check edit permission
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
response.WriteForbiddenError(w)
return
}
// if document review process then we must mark page as pending
if document.Protection == workflow.ProtectionReview {
if model.Page.RelativeID == "" {
model.Page.Status = workflow.ChangePendingNew
} else {
model.Page.Status = workflow.ChangePending
}
} else {
model.Page.RelativeID = ""
2018-01-10 16:07:17 +00:00
model.Page.Status = workflow.ChangePublished
}
2017-08-01 15:35:13 +01:00
pageID := uniqueid.Generate()
model.Page.RefID = pageID
2018-09-19 16:03:29 +01:00
model.Meta.SectionID = pageID
2017-08-01 15:35:13 +01:00
model.Meta.OrgID = ctx.OrgID // required for Render call below
model.Meta.UserID = ctx.UserID // required for Render call below
model.Page.SetDefaults()
model.Meta.SetDefaults()
doc, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2017-08-02 15:26:31 +01:00
output, ok := provider.Render(model.Page.ContentType, provider.NewContext(model.Meta.OrgID, model.Meta.UserID, ctx), model.Meta.Config, model.Meta.RawBody)
2017-08-01 15:35:13 +01:00
if !ok {
h.Runtime.Log.Info("provider.Render could not find: " + model.Page.ContentType)
}
model.Page.Body = output
err = h.Store.Page.Add(ctx, *model)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-09-19 16:03:29 +01:00
if len(model.Page.TemplateID) > 0 {
h.Store.Block.IncrementUsage(ctx, model.Page.TemplateID)
2017-08-01 15:35:13 +01:00
}
2018-03-15 17:11:53 +00:00
// Draft actions are not logged
if doc.Lifecycle == workflow.LifecycleLive {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
2018-09-19 16:03:29 +01:00
SpaceID: doc.SpaceID,
2018-03-15 17:11:53 +00:00
DocumentID: model.Page.DocumentID,
2018-09-19 16:03:29 +01:00
SectionID: model.Page.RefID,
2018-03-15 17:11:53 +00:00
SourceType: activity.SourceTypePage,
ActivityType: activity.TypeCreated})
}
2017-08-01 15:35:13 +01:00
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSectionAdd)
2017-08-01 15:35:13 +01:00
np, _ := h.Store.Page.Get(ctx, pageID)
if doc.Lifecycle == workflow.LifecycleLive {
go h.Indexer.IndexContent(ctx, np)
} else {
go h.Indexer.DeleteDocument(ctx, doc.RefID)
}
2017-08-01 15:35:13 +01:00
response.WriteJSON(w, np)
}
// GetPage gets specified page for document.
func (h *Handler) GetPage(w http.ResponseWriter, r *http.Request) {
method := "page.GetPage"
ctx := domain.GetRequestContext(r)
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
pageID := request.Param(r, "pageID")
if len(pageID) == 0 {
response.WriteMissingDataError(w, method, "pageID")
return
}
2017-09-18 17:53:42 +01:00
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
2017-08-01 15:35:13 +01:00
response.WriteForbiddenError(w)
return
}
page, err := h.Store.Page.Get(ctx, pageID)
if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, documentID)
return
}
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
if page.DocumentID != documentID {
response.WriteBadRequestError(w, method, "documentID mismatch")
return
}
response.WriteJSON(w, page)
}
// GetPages gets all pages for document.
func (h *Handler) GetPages(w http.ResponseWriter, r *http.Request) {
2017-12-10 14:05:23 +00:00
method := "page.GetPages"
2017-08-01 15:35:13 +01:00
ctx := domain.GetRequestContext(r)
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
2017-09-18 17:53:42 +01:00
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
2017-08-01 15:35:13 +01:00
response.WriteForbiddenError(w)
return
}
var pages []page.Page
var err error
content := request.Query(r, "content")
if len(content) > 0 {
pages, err = h.Store.Page.GetPagesWithoutContent(ctx, documentID)
} else {
pages, err = h.Store.Page.GetPages(ctx, documentID)
}
if len(pages) == 0 {
pages = []page.Page{}
}
2017-12-10 14:05:23 +00:00
page.Numberize(pages)
2017-08-01 15:35:13 +01:00
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
}
response.WriteJSON(w, pages)
}
2018-01-10 16:07:17 +00:00
// GetMeta gets page meta data for specified document page.
func (h *Handler) GetMeta(w http.ResponseWriter, r *http.Request) {
method := "page.meta"
ctx := domain.GetRequestContext(r)
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
pageID := request.Param(r, "pageID")
if len(pageID) == 0 {
response.WriteMissingDataError(w, method, "pageID")
return
}
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
response.WriteForbiddenError(w)
return
}
meta, err := h.Store.Page.GetPageMeta(ctx, pageID)
if err == sql.ErrNoRows {
h.Runtime.Log.Info(method + " no record")
meta = page.Meta{}
response.WriteJSON(w, meta)
return
}
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if meta.DocumentID != documentID {
response.WriteBadRequestError(w, method, "documentID mismatch")
h.Runtime.Log.Error(method, err)
return
}
response.WriteJSON(w, meta)
}
// 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.
2018-01-10 16:07:17 +00:00
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
method := "page.update"
2017-08-01 15:35:13 +01:00
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.IsValid(ctx) {
2017-08-01 15:35:13 +01:00
response.WriteBadLicense(w)
return
}
2018-01-10 16:07:17 +00:00
// Check params
2017-08-01 15:35:13 +01:00
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
pageID := request.Param(r, "pageID")
if len(pageID) == 0 {
response.WriteMissingDataError(w, method, "pageID")
return
}
2018-01-10 16:07:17 +00:00
// Read payload
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, "Bad request body")
h.Runtime.Log.Error(method, err)
return
}
model := new(page.NewPage)
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
h.Runtime.Log.Error(method, err)
return
}
if model.Page.RefID != pageID || model.Page.DocumentID != documentID {
response.WriteBadRequestError(w, method, err.Error())
2017-08-01 15:35:13 +01:00
return
}
2018-01-10 16:07:17 +00:00
// Check protection and approval process
2017-08-01 15:35:13 +01:00
doc, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
2018-01-10 16:07:17 +00:00
response.WriteBadRequestError(w, method, err.Error())
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-01-10 16:07:17 +00:00
if doc.Protection == workflow.ProtectionLock {
response.WriteForbiddenError(w)
h.Runtime.Log.Info("attempted write to locked document")
return
}
// Check edit permission
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
response.WriteForbiddenError(w)
return
}
2017-08-01 15:35:13 +01:00
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-01-10 16:07:17 +00:00
model.Page.SetDefaults()
model.Meta.SetDefaults()
oldPageMeta, err := h.Store.Page.GetPageMeta(ctx, pageID)
2017-08-01 15:35:13 +01:00
if err != nil {
ctx.Transaction.Rollback()
2018-01-10 16:07:17 +00:00
response.WriteBadRequestError(w, method, err.Error())
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-01-10 16:07:17 +00:00
output, ok := provider.Render(model.Page.ContentType, provider.NewContext(model.Meta.OrgID, oldPageMeta.UserID, ctx), model.Meta.Config, model.Meta.RawBody)
if !ok {
h.Runtime.Log.Info("provider.Render could not find: " + model.Page.ContentType)
2017-08-01 15:35:13 +01:00
}
2018-01-10 16:07:17 +00:00
model.Page.Body = output
refID := uniqueid.Generate()
skipRevision := false
skipRevision, err = strconv.ParseBool(request.Query(r, "r"))
// We don't track revisions for non-published pages
if model.Page.Status != workflow.ChangePublished {
skipRevision = true
}
// We only track revisions for live documents
if doc.Lifecycle != workflow.LifecycleLive {
skipRevision = true
}
2018-01-10 16:07:17 +00:00
err = h.Store.Page.Update(ctx, model.Page, refID, ctx.UserID, skipRevision)
2017-08-01 15:35:13 +01:00
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
2018-01-10 16:07:17 +00:00
h.Runtime.Log.Error(method, err)
return
}
err = h.Store.Page.UpdateMeta(ctx, model.Meta, true) // change the UserID to the current one
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-03-15 17:11:53 +00:00
// Draft edits are not logged
if doc.Lifecycle == workflow.LifecycleLive {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
2018-09-19 16:03:29 +01:00
SpaceID: doc.SpaceID,
2018-03-15 17:11:53 +00:00
DocumentID: model.Page.DocumentID,
2018-09-19 16:03:29 +01:00
SectionID: model.Page.RefID,
2018-03-15 17:11:53 +00:00
SourceType: activity.SourceTypePage,
ActivityType: activity.TypeEdited})
}
2017-08-01 15:35:13 +01:00
2018-01-10 16:07:17 +00:00
h.Store.Audit.Record(ctx, audit.EventTypeSectionUpdate)
2017-08-01 15:35:13 +01:00
2018-01-10 16:07:17 +00:00
// find any content links in the HTML
links := link.GetContentLinks(model.Page.Body)
2017-08-01 15:35:13 +01:00
2018-01-10 16:07:17 +00:00
// get a copy of previously saved links
previousLinks, _ := h.Store.Link.GetPageLinks(ctx, model.Page.DocumentID, model.Page.RefID)
2017-08-01 15:35:13 +01:00
2018-01-10 16:07:17 +00:00
// delete previous content links for this page
_, _ = h.Store.Link.DeleteSourcePageLinks(ctx, model.Page.RefID)
2017-08-01 15:35:13 +01:00
2018-01-10 16:07:17 +00:00
// save latest content links for this page
for _, link := range links {
link.Orphan = false
link.OrgID = ctx.OrgID
link.UserID = ctx.UserID
link.SourceDocumentID = model.Page.DocumentID
2018-09-19 16:03:29 +01:00
link.SourceSectionID = model.Page.RefID
2018-01-10 16:07:17 +00:00
if link.LinkType == "document" || link.LinkType == "network" {
2018-01-10 16:07:17 +00:00
link.TargetID = ""
}
if link.LinkType != "network" {
link.ExternalID = ""
}
2018-01-10 16:07:17 +00:00
// We check if there was a previously saved version of this link.
// If we find one, we carry forward the orphan flag.
for _, p := range previousLinks {
if link.LinkType == p.LinkType && link.TargetID == p.TargetID && link.LinkType != "network" {
link.Orphan = p.Orphan
break
}
if link.LinkType == p.LinkType && link.ExternalID == p.ExternalID && link.LinkType == "network" {
2018-01-10 16:07:17 +00:00
link.Orphan = p.Orphan
break
}
}
// save
err := h.Store.Link.Add(ctx, link)
if err != nil {
h.Runtime.Log.Error(fmt.Sprintf("Unable to insert content links for page %s", model.Page.RefID), err)
}
}
2017-08-01 15:35:13 +01:00
ctx.Transaction.Commit()
if doc.Lifecycle == workflow.LifecycleLive {
go h.Indexer.IndexContent(ctx, model.Page)
} else {
go h.Indexer.DeleteDocument(ctx, doc.RefID)
}
2018-01-10 16:07:17 +00:00
updatedPage, err := h.Store.Page.Get(ctx, pageID)
response.WriteJSON(w, updatedPage)
2017-08-01 15:35:13 +01:00
}
// Delete a page.
2018-01-10 16:07:17 +00:00
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
method := "page.delete"
2017-08-01 15:35:13 +01:00
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.IsValid(ctx) {
2017-08-01 15:35:13 +01:00
response.WriteBadLicense(w)
return
}
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
2018-01-10 16:07:17 +00:00
pageID := request.Param(r, "pageID")
if len(pageID) == 0 {
response.WriteMissingDataError(w, method, "pageID")
return
}
doc, err := h.Store.Document.Get(ctx, documentID)
2017-08-01 15:35:13 +01:00
if err != nil {
2018-01-10 16:07:17 +00:00
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-01-24 13:23:11 +00:00
p, err := h.Store.Page.Get(ctx, pageID)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
2018-01-24 13:23:11 +00:00
// you can delete your own pending page
ownPending := false
if (p.Status == workflow.ChangePending || p.Status == workflow.ChangePendingNew) && p.UserID == ctx.UserID {
ownPending = true
2018-01-10 16:07:17 +00:00
}
2018-01-24 13:23:11 +00:00
// if not own page then check permission
if !ownPending {
ok, _ := h.workflowPermitsChange(doc, ctx)
if !ok {
response.WriteForbiddenError(w)
2018-01-24 13:23:11 +00:00
h.Runtime.Log.Info("attempted delete section on locked document")
return
}
}
2018-01-10 16:07:17 +00:00
ctx.Transaction, err = h.Runtime.Db.Beginx()
2017-08-01 15:35:13 +01:00
if err != nil {
2018-01-10 16:07:17 +00:00
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-09-19 16:03:29 +01:00
if len(p.TemplateID) > 0 {
h.Store.Block.DecrementUsage(ctx, p.TemplateID)
2018-01-10 16:07:17 +00:00
}
_, err = h.Store.Page.Delete(ctx, documentID, pageID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
2018-03-15 17:11:53 +00:00
// Draft actions are not logged
if doc.Lifecycle == workflow.LifecycleLive {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
2018-09-19 16:03:29 +01:00
SpaceID: doc.SpaceID,
2018-03-15 17:11:53 +00:00
DocumentID: documentID,
2018-09-19 16:03:29 +01:00
SectionID: pageID,
2018-03-15 17:11:53 +00:00
SourceType: activity.SourceTypePage,
ActivityType: activity.TypeDeleted})
}
2018-01-10 16:07:17 +00:00
go h.Indexer.DeleteContent(ctx, pageID)
h.Store.Link.DeleteSourcePageLinks(ctx, pageID)
h.Store.Link.MarkOrphanPageLink(ctx, pageID)
h.Store.Page.DeletePageRevisions(ctx, pageID)
ctx.Transaction.Commit()
2018-01-19 11:36:38 +00:00
h.Store.Audit.Record(ctx, audit.EventTypeSectionDelete)
2017-08-01 15:35:13 +01:00
2018-01-19 11:36:38 +00:00
// Re-level all pages in document
h.LevelizeDocument(ctx, documentID)
2017-08-01 15:35:13 +01:00
response.WriteEmpty(w)
}
2018-01-10 16:07:17 +00:00
// DeletePages batch deletes pages.
func (h *Handler) DeletePages(w http.ResponseWriter, r *http.Request) {
method := "page.delete.pages"
2017-08-01 15:35:13 +01:00
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.IsValid(ctx) {
2017-08-01 15:35:13 +01:00
response.WriteBadLicense(w)
return
}
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
2018-01-10 16:07:17 +00:00
response.WriteBadRequestError(w, method, "Bad body")
2017-08-01 15:35:13 +01:00
return
}
2018-01-10 16:07:17 +00:00
model := new([]page.LevelRequest)
2017-08-01 15:35:13 +01:00
err = json.Unmarshal(body, &model)
2018-01-10 16:07:17 +00:00
if err != nil {
response.WriteBadRequestError(w, method, "JSON marshal")
2017-08-01 15:35:13 +01:00
return
}
doc, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-01-10 16:07:17 +00:00
for _, page := range *model {
2018-09-19 16:03:29 +01:00
pageData, err := h.Store.Page.Get(ctx, page.SectionID)
2018-01-10 16:07:17 +00:00
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
2017-08-01 15:35:13 +01:00
2018-01-24 13:23:11 +00:00
ownPending := false
if (pageData.Status == workflow.ChangePending || pageData.Status == workflow.ChangePendingNew) && pageData.UserID == ctx.UserID {
ownPending = true
}
// if not own page then check permission
if !ownPending {
ok, _ := h.workflowPermitsChange(doc, ctx)
if !ok {
ctx.Transaction.Rollback()
response.WriteForbiddenError(w)
h.Runtime.Log.Info("attempted delete section on locked document")
return
}
}
2018-09-19 16:03:29 +01:00
if len(pageData.TemplateID) > 0 {
h.Store.Block.DecrementUsage(ctx, pageData.TemplateID)
2018-01-10 16:07:17 +00:00
}
2017-08-01 15:35:13 +01:00
2018-09-19 16:03:29 +01:00
_, err = h.Store.Page.Delete(ctx, documentID, page.SectionID)
2018-01-10 16:07:17 +00:00
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
2018-09-19 16:03:29 +01:00
go h.Indexer.DeleteContent(ctx, page.SectionID)
2018-01-10 16:07:17 +00:00
2018-09-19 16:03:29 +01:00
h.Store.Link.DeleteSourcePageLinks(ctx, page.SectionID)
2018-01-10 16:07:17 +00:00
2018-09-19 16:03:29 +01:00
h.Store.Link.MarkOrphanPageLink(ctx, page.SectionID)
2018-01-10 16:07:17 +00:00
2018-09-19 16:03:29 +01:00
h.Store.Page.DeletePageRevisions(ctx, page.SectionID)
2018-01-19 11:36:38 +00:00
2018-03-15 17:11:53 +00:00
// Draft actions are not logged
if doc.Lifecycle == workflow.LifecycleLive {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
2018-09-19 16:03:29 +01:00
SpaceID: doc.SpaceID,
2018-03-15 17:11:53 +00:00
DocumentID: documentID,
2018-09-19 16:03:29 +01:00
SectionID: page.SectionID,
2018-03-15 17:11:53 +00:00
SourceType: activity.SourceTypePage,
ActivityType: activity.TypeDeleted})
}
2017-08-01 15:35:13 +01:00
}
2018-01-19 11:36:38 +00:00
ctx.Transaction.Commit()
2017-08-01 15:35:13 +01:00
2018-01-10 16:07:17 +00:00
h.Store.Audit.Record(ctx, audit.EventTypeSectionDelete)
// Re-level all pages in document
2018-01-19 11:36:38 +00:00
h.LevelizeDocument(ctx, documentID)
2017-08-01 15:35:13 +01:00
2018-01-10 16:07:17 +00:00
response.WriteEmpty(w)
2017-08-01 15:35:13 +01:00
}
2018-01-10 16:07:17 +00:00
//**************************************************
// Table of Contents
//**************************************************
2017-08-01 15:35:13 +01:00
// ChangePageSequence will swap page sequence for a given number of pages.
func (h *Handler) ChangePageSequence(w http.ResponseWriter, r *http.Request) {
method := "page.sequence"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.IsValid(ctx) {
2017-08-01 15:35:13 +01:00
response.WriteBadLicense(w)
return
}
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
doc, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
ok, err := h.workflowPermitsChange(doc, ctx)
if !ok {
2017-08-01 15:35:13 +01:00
response.WriteForbiddenError(w)
h.Runtime.Log.Info("attempted to chaneg page sequence on protected document")
2017-08-01 15:35:13 +01:00
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2017-12-11 09:47:37 +00:00
model := new([]page.SequenceRequest)
2017-08-01 15:35:13 +01:00
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
for _, p := range *model {
2018-09-19 16:03:29 +01:00
err = h.Store.Page.UpdateSequence(ctx, documentID, p.SectionID, p.Sequence)
2017-08-01 15:35:13 +01:00
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
}
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSectionResequence)
2017-08-01 15:35:13 +01:00
response.WriteEmpty(w)
}
// ChangePageLevel handles page indent/outdent changes.
func (h *Handler) ChangePageLevel(w http.ResponseWriter, r *http.Request) {
method := "page.level"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.IsValid(ctx) {
2017-08-01 15:35:13 +01:00
response.WriteBadLicense(w)
return
}
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
doc, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
2017-08-01 15:35:13 +01:00
ok, err := h.workflowPermitsChange(doc, ctx)
if !ok {
2017-08-01 15:35:13 +01:00
response.WriteForbiddenError(w)
h.Runtime.Log.Info("attempted to chaneg page level on protected document")
2017-08-01 15:35:13 +01:00
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2017-12-11 09:47:37 +00:00
model := new([]page.LevelRequest)
2017-08-01 15:35:13 +01:00
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
for _, p := range *model {
2018-09-19 16:03:29 +01:00
err = h.Store.Page.UpdateLevel(ctx, documentID, p.SectionID, p.Level)
2017-08-01 15:35:13 +01:00
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
}
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSectionResequence)
2017-08-01 15:35:13 +01:00
response.WriteEmpty(w)
}
//**************************************************
// Copy Move Page
//**************************************************
// Copy copies page to either same or different document.
func (h *Handler) Copy(w http.ResponseWriter, r *http.Request) {
method := "page.targets"
ctx := domain.GetRequestContext(r)
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
pageID := request.Param(r, "pageID")
if len(pageID) == 0 {
response.WriteMissingDataError(w, method, "pageID")
return
}
targetID := request.Param(r, "targetID")
if len(targetID) == 0 {
response.WriteMissingDataError(w, method, "targetID")
return
}
// permission
2017-09-18 17:53:42 +01:00
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
2017-08-01 15:35:13 +01:00
response.WriteForbiddenError(w)
return
}
// fetch data
doc, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-01-10 16:07:17 +00:00
// workflow check
if doc.Protection == workflow.ProtectionLock || doc.Protection == workflow.ProtectionReview {
response.WriteForbiddenError(w)
return
}
2017-08-01 15:35:13 +01:00
p, err := h.Store.Page.Get(ctx, pageID)
if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, documentID)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
pageMeta, err := h.Store.Page.GetPageMeta(ctx, pageID)
if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, documentID)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
newPageID := uniqueid.Generate()
p.RefID = newPageID
p.Level = 1
p.Sequence = 0
p.DocumentID = targetID
p.UserID = ctx.UserID
pageMeta.DocumentID = targetID
2018-09-19 16:03:29 +01:00
pageMeta.SectionID = newPageID
2017-08-01 15:35:13 +01:00
pageMeta.UserID = ctx.UserID
model := new(page.NewPage)
model.Meta = pageMeta
model.Page = p
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
err = h.Store.Page.Add(ctx, *model)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-09-19 16:03:29 +01:00
if len(model.Page.TemplateID) > 0 {
h.Store.Block.IncrementUsage(ctx, model.Page.TemplateID)
2017-08-01 15:35:13 +01:00
}
2018-03-15 17:11:53 +00:00
// Log t actions are not logged
if doc.Lifecycle == workflow.LifecycleLive {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
2018-09-19 16:03:29 +01:00
SpaceID: doc.SpaceID,
2018-03-15 17:11:53 +00:00
DocumentID: targetID,
2018-09-19 16:03:29 +01:00
SectionID: newPageID,
2018-03-15 17:11:53 +00:00
SourceType: activity.SourceTypePage,
ActivityType: activity.TypeCreated})
}
2017-08-01 15:35:13 +01:00
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSectionCopy)
2017-08-01 15:35:13 +01:00
np, _ := h.Store.Page.Get(ctx, pageID)
response.WriteJSON(w, np)
}
//**************************************************
// Revisions
//**************************************************
// GetDocumentRevisions returns all changes for a document.
func (h *Handler) GetDocumentRevisions(w http.ResponseWriter, r *http.Request) {
method := "page.document.revisions"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.IsValid(ctx) {
2017-08-01 15:35:13 +01:00
response.WriteBadLicense(w)
return
}
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
2017-09-18 17:53:42 +01:00
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
2017-08-01 15:35:13 +01:00
response.WriteForbiddenError(w)
return
}
revisions, _ := h.Store.Page.GetDocumentRevisions(ctx, documentID)
if len(revisions) == 0 {
revisions = []page.Revision{}
}
h.Store.Audit.Record(ctx, audit.EventTypeDocumentRevisions)
response.WriteJSON(w, revisions)
}
// GetRevisions returns all changes for a given page.
func (h *Handler) GetRevisions(w http.ResponseWriter, r *http.Request) {
method := "page.revisions"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.IsValid(ctx) {
2017-08-01 15:35:13 +01:00
response.WriteBadLicense(w)
return
}
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
2017-09-18 17:53:42 +01:00
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
2017-08-01 15:35:13 +01:00
response.WriteForbiddenError(w)
return
}
pageID := request.Param(r, "pageID")
if len(pageID) == 0 {
response.WriteMissingDataError(w, method, "pageID")
return
}
revisions, _ := h.Store.Page.GetPageRevisions(ctx, pageID)
if len(revisions) == 0 {
revisions = []page.Revision{}
}
response.WriteJSON(w, revisions)
}
// GetDiff returns HTML diff between two revisions of a given page.
func (h *Handler) GetDiff(w http.ResponseWriter, r *http.Request) {
method := "page.diff"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.IsValid(ctx) {
2017-08-01 15:35:13 +01:00
response.WriteBadLicense(w)
return
}
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
pageID := request.Param(r, "pageID")
if len(pageID) == 0 {
response.WriteMissingDataError(w, method, "pageID")
return
}
revisionID := request.Param(r, "revisionID")
if len(revisionID) == 0 {
response.WriteMissingDataError(w, method, "revisionID")
return
}
2017-09-18 17:53:42 +01:00
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
2017-08-01 15:35:13 +01:00
response.WriteForbiddenError(w)
return
}
p, err := h.Store.Page.Get(ctx, pageID)
if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, pageID)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
revision, _ := h.Store.Page.GetPageRevision(ctx, revisionID)
latestHTML := p.Body
previousHTML := revision.Body
var result []byte
var cfg = &htmldiff.Config{
Granularity: 5,
DeletedSpan: []htmldiff.Attribute{{Key: "style", Val: "background-color: palegreen;"}},
InsertedSpan: []htmldiff.Attribute{{Key: "style", Val: "background-color: lightpink; text-decoration: line-through;"}},
2017-08-01 15:35:13 +01:00
ReplacedSpan: []htmldiff.Attribute{{Key: "style", Val: "background-color: lightskyblue;"}},
CleanTags: []string{"documize"},
}
// InsertedSpan: []htmldiff.Attribute{{Key: "style", Val: "background-color: palegreen;"}},
// DeletedSpan: []htmldiff.Attribute{{Key: "style", Val: "background-color: lightpink; text-decoration: line-through;"}},
// ReplacedSpan: []htmldiff.Attribute{{Key: "style", Val: "background-color: lightskyblue;"}},
2017-08-01 15:35:13 +01:00
// res, err := cfg.HTMLdiff([]string{latestHTML, previousHTML})
res, err := cfg.HTMLdiff([]string{previousHTML, latestHTML})
2017-08-01 15:35:13 +01:00
if err != nil {
response.WriteServerError(w, method, err)
return
}
result = []byte(res[0])
w.Write(result)
}
// Rollback rolls back to a specific page revision.
func (h *Handler) Rollback(w http.ResponseWriter, r *http.Request) {
method := "page.rollback"
ctx := domain.GetRequestContext(r)
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
pageID := request.Param(r, "pageID")
if len(pageID) == 0 {
response.WriteMissingDataError(w, method, "pageID")
return
}
revisionID := request.Param(r, "revisionID")
if len(revisionID) == 0 {
response.WriteMissingDataError(w, method, "revisionID")
return
}
doc, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
ok, err := h.workflowPermitsChange(doc, ctx)
if !ok {
2017-08-01 15:35:13 +01:00
response.WriteForbiddenError(w)
h.Runtime.Log.Info("attempted to chaneg page sequence on protected document")
2017-08-01 15:35:13 +01:00
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
p, err := h.Store.Page.Get(ctx, pageID)
if err != nil {
ctx.Transaction.Rollback()
2017-08-01 15:35:13 +01:00
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
meta, err := h.Store.Page.GetPageMeta(ctx, pageID)
if err != nil {
ctx.Transaction.Rollback()
2017-08-01 15:35:13 +01:00
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
revision, err := h.Store.Page.GetPageRevision(ctx, revisionID)
if err != nil {
ctx.Transaction.Rollback()
2017-08-01 15:35:13 +01:00
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
// roll back page
p.Body = revision.Body
refID := uniqueid.Generate()
err = h.Store.Page.Update(ctx, p, refID, ctx.UserID, false)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
// roll back page meta
meta.Config = revision.Config
meta.RawBody = revision.RawBody
err = h.Store.Page.UpdateMeta(ctx, meta, false)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
2017-08-03 10:00:24 +01:00
h.Runtime.Log.Error(method, err)
2017-08-01 15:35:13 +01:00
return
}
2018-03-15 17:11:53 +00:00
// Draft actions are not logged
if doc.Lifecycle == workflow.LifecycleLive {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
2018-09-19 16:03:29 +01:00
SpaceID: doc.SpaceID,
2018-03-15 17:11:53 +00:00
DocumentID: p.DocumentID,
2018-09-19 16:03:29 +01:00
SectionID: p.RefID,
2018-03-15 17:11:53 +00:00
SourceType: activity.SourceTypePage,
ActivityType: activity.TypeReverted})
}
2017-08-01 15:35:13 +01:00
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSectionRollback)
2017-08-01 15:35:13 +01:00
response.WriteJSON(w, p)
}
2018-01-10 16:07:17 +00:00
//**************************************************
// Bulk data fetching (reduce network traffic)
//**************************************************
// FetchPages returns all page data for given document: page, meta data, pending changes.
func (h *Handler) FetchPages(w http.ResponseWriter, r *http.Request) {
method := "page.FetchPages"
ctx := domain.GetRequestContext(r)
model := []page.BulkRequest{}
// check params
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
// Who referred user this document (e.g. search page).
source := request.Query(r, "source")
2018-01-10 16:07:17 +00:00
doc, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// published pages and new pages awaiting approval
pages, err := h.Store.Page.GetPages(ctx, documentID)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if len(pages) == 0 {
pages = []page.Page{}
}
// unpublished pages
unpublished, err := h.Store.Page.GetUnpublishedPages(ctx, documentID)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if len(unpublished) == 0 {
unpublished = []page.Page{}
}
// meta for all pages
meta, err := h.Store.Page.GetDocumentPageMeta(ctx, documentID, false)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if len(meta) == 0 {
meta = []page.Meta{}
}
// permissions
2018-09-19 16:03:29 +01:00
perms, err := h.Store.Permission.GetUserSpacePermissions(ctx, doc.SpaceID)
2018-01-10 16:07:17 +00:00
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if len(perms) == 0 {
perms = []pm.Permission{}
}
permissions := pm.DecodeUserPermissions(perms)
roles, err := h.Store.Permission.GetUserDocumentPermissions(ctx, doc.RefID)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if len(roles) == 0 {
roles = []pm.Permission{}
}
docRoles := pm.DecodeUserDocumentPermissions(roles)
// check document view permissions
if !permissions.SpaceView && !permissions.SpaceManage && !permissions.SpaceOwner {
response.WriteForbiddenError(w)
return
}
// process published pages
for _, p := range pages {
// only send back pages that user can see
process := false
forcePending := false
if process == false && p.Status == workflow.ChangePublished {
process = true
}
if process == false && p.Status == workflow.ChangePendingNew && p.RelativeID == "" && p.UserID == ctx.UserID {
process = true
forcePending = true // user has newly created page which should be treated as pending
}
if process == false && p.Status == workflow.ChangeUnderReview && p.RelativeID == "" && p.UserID == ctx.UserID {
process = true
forcePending = true // user has newly created page which should be treated as pending
}
if process == false && p.Status == workflow.ChangeUnderReview && p.RelativeID == "" && (permissions.DocumentApprove || docRoles.DocumentRoleApprove) {
process = true
forcePending = true // user has newly created page which should be treated as pending
}
if process {
d := page.BulkRequest{}
2018-01-19 16:01:22 +00:00
d.ID = fmt.Sprintf("container-%s", p.RefID)
2018-01-10 16:07:17 +00:00
d.Page = p
for _, m := range meta {
2018-09-19 16:03:29 +01:00
if p.RefID == m.SectionID {
2018-01-10 16:07:17 +00:00
d.Meta = m
break
}
}
d.Pending = []page.PendingPage{}
// process pending pages
for _, up := range unpublished {
if up.RelativeID == p.RefID {
ud := page.PendingPage{}
ud.Page = up
for _, m := range meta {
2018-09-19 16:03:29 +01:00
if up.RefID == m.SectionID {
2018-01-10 16:07:17 +00:00
ud.Meta = m
break
}
}
owner, err := h.Store.User.Get(ctx, up.UserID)
if err == nil {
ud.Owner = owner.Fullname()
}
d.Pending = append(d.Pending, ud)
}
}
// Handle situation where we need approval, and user has created new page
if forcePending && len(d.Pending) == 0 && doc.Protection == workflow.ProtectionReview {
ud := page.PendingPage{}
ud.Page = d.Page
ud.Meta = d.Meta
owner, err := h.Store.User.Get(ctx, d.Page.UserID)
if err == nil {
ud.Owner = owner.Fullname()
}
d.Pending = append(d.Pending, ud)
}
model = append(model, d)
}
}
// Attach numbers to pages, 1.1, 2.1.1 etc.
t := []page.Page{}
for _, i := range model {
t = append(t, i.Page)
}
page.Numberize(t)
for i, j := range t {
model[i].Page = j
2018-01-24 13:23:11 +00:00
// pending pages get same numbering
for k := range model[i].Pending {
model[i].Pending[k].Page.Numbering = j.Numbering
}
2018-01-10 16:07:17 +00:00
}
// If we have source, record document access via source.
if len(source) > 0 {
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
h.Runtime.Log.Error(method, err)
} else {
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
2018-09-19 16:03:29 +01:00
SpaceID: doc.SpaceID,
DocumentID: doc.RefID,
Metadata: source, // deliberate
SourceType: activity.SourceTypeSearch, // deliberate
ActivityType: activity.TypeRead})
if err != nil {
ctx.Transaction.Rollback()
h.Runtime.Log.Error(method, err)
}
ctx.Transaction.Commit()
}
}
2018-01-10 16:07:17 +00:00
// deliver payload
response.WriteJSON(w, model)
}
func (h *Handler) workflowPermitsChange(doc dm.Document, ctx domain.RequestContext) (ok bool, err error) {
if doc.Protection == workflow.ProtectionNone {
if !permission.CanChangeDocument(ctx, *h.Store, doc.RefID) {
h.Runtime.Log.Info("attempted forbidden action on document")
return false, nil
}
return true, nil
}
// If locked document then no can do
if doc.Protection == workflow.ProtectionLock {
h.Runtime.Log.Info("attempted action on locked document")
return false, err
}
// If approval workflow then only approvers can delete page
if doc.Protection == workflow.ProtectionReview {
2018-09-19 16:03:29 +01:00
approvers, err := permission.GetUsersWithDocumentPermission(ctx, *h.Store, doc.SpaceID, doc.RefID, pm.DocumentApprove)
if err != nil {
h.Runtime.Log.Error("workflowAllowsChange", err)
return false, err
}
if user.Exists(approvers, ctx.UserID) {
h.Runtime.Log.Info("attempted action on document when not approver")
return true, nil
}
return false, nil
}
return true, nil
}