2016-07-07 18:54:16 -07: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 request
import (
"fmt"
"strings"
"time"
2016-07-20 15:58:37 +01:00
"github.com/documize/community/core/api/endpoint/models"
"github.com/documize/community/core/api/entity"
2016-10-25 15:56:08 -07:00
"github.com/documize/community/core/api/util"
2016-07-20 15:58:37 +01:00
"github.com/documize/community/core/log"
"github.com/documize/community/core/utility"
2016-11-21 19:27:18 -08:00
"github.com/jmoiron/sqlx"
2016-07-07 18:54:16 -07:00
)
// AddPage inserts the given page into the page table, adds that page to the queue of pages to index and audits that the page has been added.
func ( p * Persister ) AddPage ( model models . PageModel ) ( err error ) {
err = nil
model . Page . OrgID = p . Context . OrgID
2016-11-10 15:06:59 -08:00
model . Page . UserID = p . Context . UserID
2016-07-07 18:54:16 -07:00
model . Page . Created = time . Now ( ) . UTC ( )
model . Page . Revised = time . Now ( ) . UTC ( )
model . Meta . OrgID = p . Context . OrgID
model . Meta . UserID = p . Context . UserID
model . Meta . DocumentID = model . Page . DocumentID
model . Meta . Created = time . Now ( ) . UTC ( )
model . Meta . Revised = time . Now ( ) . UTC ( )
2016-11-10 15:06:59 -08:00
if model . Page . IsSectionType ( ) {
// Get maximum page sequence number and increment
row := Db . QueryRow ( "SELECT max(sequence) FROM page WHERE orgid=? AND documentid=? AND pagetype='section'" , p . Context . OrgID , model . Page . DocumentID )
var maxSeq float64
err = row . Scan ( & maxSeq )
if err != nil {
2016-11-12 12:09:05 -08:00
maxSeq = 2048
2016-11-10 15:06:59 -08:00
}
2016-07-07 18:54:16 -07:00
model . Page . Sequence = maxSeq * 2
}
2016-11-09 15:16:44 -08:00
stmt , err := p . Context . Transaction . Preparex ( "INSERT INTO page (refid, orgid, documentid, userid, contenttype, pagetype, level, title, body, revisions, sequence, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" )
2016-07-07 18:54:16 -07:00
defer utility . Close ( stmt )
if err != nil {
log . Error ( "Unable to prepare insert for page" , err )
return
}
2016-11-09 15:16:44 -08:00
_ , err = stmt . Exec ( model . Page . RefID , model . Page . OrgID , model . Page . DocumentID , model . Page . UserID , model . Page . ContentType , model . Page . PageType , model . Page . Level , model . Page . Title , model . Page . Body , model . Page . Revisions , model . Page . Sequence , model . Page . Created , model . Page . Revised )
2016-07-07 18:54:16 -07:00
if err != nil {
log . Error ( "Unable to execute insert for page" , err )
return
}
err = searches . Add ( & databaseRequest { OrgID : p . Context . OrgID } , model . Page , model . Page . RefID )
stmt2 , err := p . Context . Transaction . Preparex ( "INSERT INTO pagemeta (pageid, orgid, userid, documentid, rawbody, config, externalsource, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" )
defer utility . Close ( stmt2 )
if err != nil {
log . Error ( "Unable to prepare insert for page meta" , err )
return
}
_ , err = stmt2 . Exec ( model . Meta . PageID , model . Meta . OrgID , model . Meta . UserID , model . Meta . DocumentID , model . Meta . RawBody , model . Meta . Config , model . Meta . ExternalSource , model . Meta . Created , model . Meta . Revised )
if err != nil {
log . Error ( "Unable to execute insert for page meta" , err )
return
}
p . Base . Audit ( p . Context , "add-page" , model . Page . DocumentID , model . Page . RefID )
return
}
// GetPage returns the pageID page record from the page table.
func ( p * Persister ) GetPage ( pageID string ) ( page entity . Page , err error ) {
err = nil
2016-11-09 15:16:44 -08:00
stmt , err := Db . Preparex ( "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.pagetype, a.level, a.sequence, a.title, a.body, a.revisions, a.created, a.revised FROM page a WHERE a.orgid=? AND a.refid=?" )
2016-07-07 18:54:16 -07:00
defer utility . Close ( stmt )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to prepare select for page %s" , pageID ) , err )
return
}
err = stmt . Get ( & page , p . Context . OrgID , pageID )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute select for page %s" , pageID ) , err )
return
}
return
}
// GetPages returns a slice containing all the page records for a given documentID, in presentation sequence.
func ( p * Persister ) GetPages ( documentID string ) ( pages [ ] entity . Page , err error ) {
err = nil
2016-11-09 15:16:44 -08:00
err = Db . Select ( & pages , "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.pagetype, a.level, a.sequence, a.title, a.body, a.revisions, a.created, a.revised FROM page a WHERE a.orgid=? AND a.documentid=? ORDER BY a.sequence" , p . Context . OrgID , documentID )
2016-07-07 18:54:16 -07:00
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute select pages for org %s and document %s" , p . Context . OrgID , documentID ) , err )
return
}
return
}
// GetPagesWhereIn returns a slice, in presentation sequence, containing those page records for a given documentID
// where their refid is in the comma-separated list passed as inPages.
func ( p * Persister ) GetPagesWhereIn ( documentID , inPages string ) ( pages [ ] entity . Page , err error ) {
err = nil
args := [ ] interface { } { p . Context . OrgID , documentID }
tempValues := strings . Split ( inPages , "," )
2016-11-09 15:16:44 -08:00
sql := "SELECT a.id, a.refid, a.orgid, a.documentid, a.userid, a.contenttype, a.pagetype, a.level, a.sequence, a.title, a.body, a.revisions, a.created, a.revised FROM page a WHERE a.orgid=? AND a.documentid=? AND a.refid IN (?" + strings . Repeat ( ",?" , len ( tempValues ) - 1 ) + ") ORDER BY sequence"
2016-07-07 18:54:16 -07:00
inValues := make ( [ ] interface { } , len ( tempValues ) )
for i , v := range tempValues {
inValues [ i ] = interface { } ( v )
}
args = append ( args , inValues ... )
stmt , err := Db . Preparex ( sql )
defer utility . Close ( stmt )
if err != nil {
log . Error ( fmt . Sprintf ( "Failed to prepare select pages for org %s and document %s where in %s" , p . Context . OrgID , documentID , inPages ) , err )
return
}
rows , err := stmt . Queryx ( args ... )
if err != nil {
log . Error ( fmt . Sprintf ( "Failed to execute select pages for org %s and document %s where in %s" , p . Context . OrgID , documentID , inPages ) , err )
return
}
defer utility . Close ( rows )
for rows . Next ( ) {
page := entity . Page { }
err = rows . StructScan ( & page )
if err != nil {
log . Error ( fmt . Sprintf ( "Failed to scan row: select pages for org %s and document %s where in %s" , p . Context . OrgID , documentID , inPages ) , err )
return
}
pages = append ( pages , page )
}
if err != nil {
log . Error ( fmt . Sprintf ( "Failed to execute select pages for org %s and document %s where in %s" , p . Context . OrgID , documentID , inPages ) , err )
return
}
return
}
// GetPagesWithoutContent returns a slice containing all the page records for a given documentID, in presentation sequence,
// but without the body field (which holds the HTML content).
func ( p * Persister ) GetPagesWithoutContent ( documentID string ) ( pages [ ] entity . Page , err error ) {
2016-11-09 15:16:44 -08:00
err = Db . Select ( & pages , "SELECT id, refid, orgid, documentid, userid, contenttype, pagetype, sequence, level, title, revisions, created, revised FROM page WHERE orgid=? AND documentid=? ORDER BY sequence" , p . Context . OrgID , documentID )
2016-07-07 18:54:16 -07:00
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute select pages for org %s and document %s" , p . Context . OrgID , documentID ) , err )
return
}
return
}
// UpdatePage saves changes to the database and handles recording of revisions.
// Not all updates result in a revision being recorded hence the parameter.
func ( p * Persister ) UpdatePage ( page entity . Page , refID , userID string , skipRevision bool ) ( err error ) {
err = nil
page . Revised = time . Now ( ) . UTC ( )
// Store revision history
if ! skipRevision {
var stmt * sqlx . Stmt
2016-11-09 15:16:44 -08:00
stmt , err = p . Context . Transaction . Preparex ( "INSERT INTO revision (refid, orgid, documentid, ownerid, pageid, userid, contenttype, pagetype, title, body, rawbody, config, created, revised) SELECT ? as refid, a.orgid, a.documentid, a.userid as ownerid, a.refid as pageid, ? as userid, a.contenttype, a.pagetype, a.title, a.body, b.rawbody, b.config, ? as created, ? as revised FROM page a, pagemeta b WHERE a.refid=? AND a.refid=b.pageid" )
2016-07-07 18:54:16 -07:00
defer utility . Close ( stmt )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to prepare insert for page revision %s" , page . RefID ) , err )
return err
}
_ , err = stmt . Exec ( refID , userID , time . Now ( ) . UTC ( ) , time . Now ( ) . UTC ( ) , page . RefID )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute insert for page revision %s" , page . RefID ) , err )
return err
}
}
// Update page
var stmt2 * sqlx . NamedStmt
stmt2 , err = p . Context . Transaction . PrepareNamed ( "UPDATE page SET documentid=:documentid, level=:level, title=:title, body=:body, revisions=:revisions, sequence=:sequence, revised=:revised WHERE orgid=:orgid AND refid=:refid" )
defer utility . Close ( stmt2 )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to prepare update for page %s" , page . RefID ) , err )
return
}
_ , err = stmt2 . Exec ( & page )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute update for page %s" , page . RefID ) , err )
return
}
err = searches . Update ( & databaseRequest { OrgID : p . Context . OrgID } , page )
if err != nil {
log . Error ( "Unable to update for searching" , err )
return
}
// Update revisions counter
if ! skipRevision {
stmt3 , err := p . Context . Transaction . Preparex ( "UPDATE page SET revisions=revisions+1 WHERE orgid=? AND refid=?" )
defer utility . Close ( stmt3 )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to prepare revisions counter update for page %s" , page . RefID ) , err )
return err
}
_ , err = stmt3 . Exec ( p . Context . OrgID , page . RefID )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute revisions counter update for page %s" , page . RefID ) , err )
return err
}
}
2016-10-26 17:31:05 -07:00
// find any content links in the HTML
2016-10-25 15:56:08 -07:00
links := util . GetContentLinks ( page . Body )
2016-10-27 16:53:36 -07:00
// get a copy of previously saved links
previousLinks , _ := p . GetPageLinks ( page . DocumentID , page . RefID )
2016-10-25 15:56:08 -07:00
// delete previous content links for this page
2016-10-27 15:44:40 -07:00
_ , _ = p . DeleteSourcePageLinks ( page . RefID )
2016-10-25 15:56:08 -07:00
// save latest content links for this page
for _ , link := range links {
2016-10-27 15:44:40 -07:00
link . Orphan = false
2016-10-25 15:56:08 -07:00
link . OrgID = p . Context . OrgID
link . UserID = p . Context . UserID
2016-10-27 15:44:40 -07:00
link . SourceDocumentID = page . DocumentID
2016-10-27 16:53:36 -07:00
link . SourcePageID = page . RefID
if link . LinkType == "document" {
link . TargetID = ""
}
2016-10-27 15:44:40 -07:00
2016-10-27 16:53:36 -07: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 . TargetID == p . TargetID && link . LinkType == p . LinkType {
link . Orphan = p . Orphan
break
}
2016-10-27 15:44:40 -07:00
}
2016-10-25 15:56:08 -07:00
2016-10-27 16:53:36 -07:00
// save
2016-10-25 15:56:08 -07:00
err := p . AddContentLink ( link )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to insert content links for page %s" , page . RefID ) , err )
return err
}
}
2016-07-07 18:54:16 -07:00
p . Base . Audit ( p . Context , "update-page" , page . DocumentID , page . RefID )
return
}
// UpdatePageMeta persists meta information associated with a document page.
2016-07-15 16:54:07 +01:00
func ( p * Persister ) UpdatePageMeta ( meta entity . PageMeta , updateUserID bool ) ( err error ) {
2016-07-07 18:54:16 -07:00
err = nil
meta . Revised = time . Now ( ) . UTC ( )
2016-11-30 17:56:36 -08:00
2016-07-07 18:54:16 -07:00
if updateUserID {
2016-07-15 16:54:07 +01:00
meta . UserID = p . Context . UserID
2016-07-07 18:54:16 -07:00
}
var stmt * sqlx . NamedStmt
stmt , err = p . Context . Transaction . PrepareNamed ( "UPDATE pagemeta SET userid=:userid, documentid=:documentid, rawbody=:rawbody, config=:config, externalsource=:externalsource, revised=:revised WHERE orgid=:orgid AND pageid=:pageid" )
defer utility . Close ( stmt )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to prepare update for page meta %s" , meta . PageID ) , err )
return
}
_ , err = stmt . Exec ( & meta )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute update for page meta %s" , meta . PageID ) , err )
return
}
return
}
// UpdatePageSequence changes the presentation sequence of the pageID page in the document.
// It then propagates that change into the search table and audits that it has occurred.
func ( p * Persister ) UpdatePageSequence ( documentID , pageID string , sequence float64 ) ( err error ) {
stmt , err := p . Context . Transaction . Preparex ( "UPDATE page SET sequence=? WHERE orgid=? AND refid=?" )
defer utility . Close ( stmt )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to prepare update for page %s" , pageID ) , err )
return
}
_ , err = stmt . Exec ( sequence , p . Context . OrgID , pageID )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute update for page %s" , pageID ) , err )
return
}
err = searches . UpdateSequence ( & databaseRequest { OrgID : p . Context . OrgID } , documentID , pageID , sequence )
p . Base . Audit ( p . Context , "re-sequence-page" , "" , pageID )
return
}
// UpdatePageLevel changes the heading level of the pageID page in the document.
// It then propagates that change into the search table and audits that it has occurred.
func ( p * Persister ) UpdatePageLevel ( documentID , pageID string , level int ) ( err error ) {
stmt , err := p . Context . Transaction . Preparex ( "UPDATE page SET level=? WHERE orgid=? AND refid=?" )
defer utility . Close ( stmt )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to prepare update for page %s" , pageID ) , err )
return
}
_ , err = stmt . Exec ( level , p . Context . OrgID , pageID )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute update for page %s" , pageID ) , err )
return
}
err = searches . UpdateLevel ( & databaseRequest { OrgID : p . Context . OrgID } , documentID , pageID , level )
p . Base . Audit ( p . Context , "re-level-page" , "" , pageID )
return
}
// DeletePage deletes the pageID page in the document.
// It then propagates that change into the search table, adds a delete the page revisions history, and audits that the page has been removed.
func ( p * Persister ) DeletePage ( documentID , pageID string ) ( rows int64 , err error ) {
rows , err = p . Base . DeleteConstrained ( p . Context . Transaction , "page" , p . Context . OrgID , pageID )
if err == nil {
_ , err = p . Base . DeleteWhere ( p . Context . Transaction , fmt . Sprintf ( "DELETE FROM pagemeta WHERE orgid='%s' AND pageid='%s'" , p . Context . OrgID , pageID ) )
_ , err = searches . Delete ( & databaseRequest { OrgID : p . Context . OrgID } , documentID , pageID )
2016-10-27 15:44:40 -07:00
// delete content links from this page
_ , err = p . DeleteSourcePageLinks ( pageID )
// mark as orphan links to this page
err = p . MarkOrphanPageLink ( pageID )
2016-11-30 17:56:36 -08:00
// nuke revisions
_ , err = p . DeletePageRevisions ( pageID )
2016-07-07 18:54:16 -07:00
p . Base . Audit ( p . Context , "remove-page" , documentID , pageID )
}
return
}
// GetPageMeta returns the meta information associated with the page.
func ( p * Persister ) GetPageMeta ( pageID string ) ( meta entity . PageMeta , err error ) {
err = nil
stmt , err := Db . Preparex ( "SELECT id, pageid, orgid, userid, documentid, rawbody, coalesce(config,JSON_UNQUOTE('{}')) as config, externalsource, created, revised FROM pagemeta WHERE orgid=? AND pageid=?" )
defer utility . Close ( stmt )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to prepare select for pagemeta %s" , pageID ) , err )
return
}
err = stmt . Get ( & meta , p . Context . OrgID , pageID )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute select for pagemeta %s" , pageID ) , err )
return
}
return
}
// GetDocumentPageMeta returns the meta information associated with a document.
func ( p * Persister ) GetDocumentPageMeta ( documentID string , externalSourceOnly bool ) ( meta [ ] entity . PageMeta , err error ) {
err = nil
filter := ""
if externalSourceOnly {
filter = " AND externalsource=1"
}
err = Db . Select ( & meta , "SELECT id, pageid, orgid, userid, documentid, rawbody, coalesce(config,JSON_UNQUOTE('{}')) as config, externalsource, created, revised FROM pagemeta WHERE orgid=? AND documentid=?" + filter , p . Context . OrgID , documentID )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute select document page meta for org %s and document %s" , p . Context . OrgID , documentID ) , err )
return
}
return
}
2016-11-30 17:56:36 -08:00
/ * * * * * * * * * * * * * * * * * * * *
* Page Revisions
* * * * * * * * * * * * * * * * * * * * /
// GetPageRevision returns the revisionID page revision record.
func ( p * Persister ) GetPageRevision ( revisionID string ) ( revision entity . Revision , err error ) {
2016-11-30 18:36:22 -08:00
stmt , err := Db . Preparex ( "SELECT id, refid, orgid, documentid, ownerid, pageid, userid, contenttype, pagetype, title, body, coalesce(rawbody, '') as rawbody, coalesce(config,JSON_UNQUOTE('{}')) as config, created, revised FROM revision WHERE orgid=? and refid=?" )
2016-11-30 17:56:36 -08:00
defer utility . Close ( stmt )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to prepare select for revision %s" , revisionID ) , err )
return
}
err = stmt . Get ( & revision , p . Context . OrgID , revisionID )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute select for revision %s" , revisionID ) , err )
return
}
return
}
// GetDocumentRevisions returns a slice of page revision records for a given document, in the order they were created.
// Then audits that the get-page-revisions action has occurred.
func ( p * Persister ) GetDocumentRevisions ( documentID string ) ( revisions [ ] entity . Revision , err error ) {
err = Db . Select ( & revisions , "SELECT a.id, a.refid, a.orgid, a.documentid, a.ownerid, a.pageid, a.userid, a.contenttype, a.pagetype, a.title, /*a.body, a.rawbody, a.config,*/ a.created, a.revised, coalesce(b.email,'') as email, coalesce(b.firstname,'') as firstname, coalesce(b.lastname,'') as lastname, coalesce(b.initials,'') as initials, coalesce(p.revisions, 0) as revisions FROM revision a LEFT JOIN user b ON a.userid=b.refid LEFT JOIN page p ON a.pageid=p.refid WHERE a.orgid=? AND a.documentid=? AND a.pagetype='section' ORDER BY a.id DESC" , p . Context . OrgID , documentID )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute select revisions for org %s and document %s" , p . Context . OrgID , documentID ) , err )
return
}
if len ( revisions ) == 0 {
revisions = [ ] entity . Revision { }
}
p . Base . Audit ( p . Context , "get-document-revisions" , documentID , "" )
return
}
// GetPageRevisions returns a slice of page revision records for a given pageID, in the order they were created.
// Then audits that the get-page-revisions action has occurred.
func ( p * Persister ) GetPageRevisions ( pageID string ) ( revisions [ ] entity . Revision , err error ) {
err = Db . Select ( & revisions , "SELECT a.id, a.refid, a.orgid, a.documentid, a.ownerid, a.pageid, a.userid, a.contenttype, a.pagetype, a.title, /*a.body, a.rawbody, a.config,*/ a.created, a.revised, coalesce(b.email,'') as email, coalesce(b.firstname,'') as firstname, coalesce(b.lastname,'') as lastname, coalesce(b.initials,'') as initials FROM revision a LEFT JOIN user b ON a.userid=b.refid WHERE a.orgid=? AND a.pageid=? AND a.pagetype='section' ORDER BY a.id DESC" , p . Context . OrgID , pageID )
if err != nil {
log . Error ( fmt . Sprintf ( "Unable to execute select revisions for org %s and page %s" , p . Context . OrgID , pageID ) , err )
return
}
p . Base . Audit ( p . Context , "get-page-revisions" , "" , pageID )
return
}
// DeletePageRevisions deletes all of the page revision records for a given pageID.
func ( p * Persister ) DeletePageRevisions ( pageID string ) ( rows int64 , err error ) {
rows , err = p . Base . DeleteWhere ( p . Context . Transaction , fmt . Sprintf ( "DELETE FROM revision WHERE orgid='%s' AND pageid='%s'" , p . Context . OrgID , pageID ) )
return
}