1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-08-02 20:15:26 +02:00

rollback page revisions

This commit is contained in:
Harvey Kandola 2016-11-30 17:56:36 -08:00
parent ae03928569
commit 87d1334280
130 changed files with 39899 additions and 34 deletions

View file

@ -26,6 +26,7 @@ import (
"github.com/documize/community/core/log"
"github.com/documize/community/core/section/provider"
"github.com/documize/community/core/utility"
htmldiff "github.com/documize/html-diff"
"github.com/gorilla/mux"
)
@ -645,3 +646,232 @@ func GetDocumentPageMeta(w http.ResponseWriter, r *http.Request) {
writeSuccessBytes(w, json)
}
/********************
* Page Revisions
********************/
// GetDocumentRevisions returns all changes for a document.
func GetDocumentRevisions(w http.ResponseWriter, r *http.Request) {
method := "GetDocumentPageRevisions"
p := request.GetPersister(r)
params := mux.Vars(r)
documentID := params["documentID"]
if len(documentID) == 0 {
writeMissingDataError(w, method, "documentID")
return
}
if !p.CanViewDocument(documentID) {
writeForbiddenError(w)
return
}
revisions, err := p.GetDocumentRevisions(documentID)
payload, err := json.Marshal(revisions)
if err != nil {
writeJSONMarshalError(w, method, "revision", err)
return
}
writeSuccessBytes(w, payload)
}
// GetDocumentPageRevisions returns all changes for a given page.
func GetDocumentPageRevisions(w http.ResponseWriter, r *http.Request) {
method := "GetDocumentPageRevisions"
p := request.GetPersister(r)
params := mux.Vars(r)
documentID := params["documentID"]
if len(documentID) == 0 {
writeMissingDataError(w, method, "documentID")
return
}
if !p.CanViewDocument(documentID) {
writeForbiddenError(w)
return
}
pageID := params["pageID"]
if len(pageID) == 0 {
writeMissingDataError(w, method, "pageID")
return
}
revisions, err := p.GetPageRevisions(pageID)
payload, err := json.Marshal(revisions)
if err != nil {
writeJSONMarshalError(w, method, "revision", err)
return
}
writeSuccessBytes(w, payload)
}
// GetDocumentPageDiff returns HTML diff between two revisions of a given page.
func GetDocumentPageDiff(w http.ResponseWriter, r *http.Request) {
method := "GetDocumentPageDiff"
p := request.GetPersister(r)
params := mux.Vars(r)
documentID := params["documentID"]
if len(documentID) == 0 {
writeMissingDataError(w, method, "documentID")
return
}
if !p.CanViewDocument(documentID) {
writeForbiddenError(w)
return
}
pageID := params["pageID"]
if len(pageID) == 0 {
writeMissingDataError(w, method, "pageID")
return
}
revisionID := params["revisionID"]
if len(revisionID) == 0 {
writeMissingDataError(w, method, "revisionID")
return
}
page, err := p.GetPage(pageID)
if err == sql.ErrNoRows {
writeNotFoundError(w, method, revisionID)
return
}
revision, err := p.GetPageRevision(revisionID)
latestHTML := page.Body
previousHTML := revision.Body
var result []byte
var cfg = &htmldiff.Config{
Granularity: 5,
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;"}},
CleanTags: []string{"documize"},
}
res, err := cfg.HTMLdiff([]string{latestHTML, previousHTML})
if err != nil {
writeServerError(w, method, err)
return
}
result = []byte(res[0])
_, err = w.Write(result)
log.IfErr(err)
}
// RollbackDocumentPage rolls-back to a specific page revision.
func RollbackDocumentPage(w http.ResponseWriter, r *http.Request) {
method := "RollbackDocumentPage"
p := request.GetPersister(r)
params := mux.Vars(r)
documentID := params["documentID"]
if len(documentID) == 0 {
writeMissingDataError(w, method, "documentID")
return
}
pageID := params["pageID"]
if len(pageID) == 0 {
writeMissingDataError(w, method, "pageID")
return
}
revisionID := params["revisionID"]
if len(revisionID) == 0 {
writeMissingDataError(w, method, "revisionID")
return
}
if !p.CanChangeDocument(documentID) {
writeForbiddenError(w)
return
}
tx, err := request.Db.Beginx()
if err != nil {
writeTransactionError(w, method, err)
return
}
p.Context.Transaction = tx
// fetch page
page, err := p.GetPage(pageID)
if err != nil {
writeGeneralSQLError(w, method, err)
return
}
// fetch page meta
meta, err := p.GetPageMeta(pageID)
if err != nil {
writeGeneralSQLError(w, method, err)
return
}
// fetch revision
revision, err := p.GetPageRevision(revisionID)
if err != nil {
writeGeneralSQLError(w, method, err)
return
}
// roll back page
page.Body = revision.Body
refID := util.UniqueID()
err = p.UpdatePage(page, refID, p.Context.UserID, false)
if err != nil {
log.IfErr(tx.Rollback())
writeGeneralSQLError(w, method, err)
return
}
// roll back page meta
meta.Config = revision.Config
meta.RawBody = revision.RawBody
err = p.UpdatePageMeta(meta, false)
if err != nil {
log.IfErr(tx.Rollback())
writeGeneralSQLError(w, method, err)
return
}
log.IfErr(tx.Commit())
payload, err := json.Marshal(page)
if err != nil {
writeJSONMarshalError(w, method, "revision", err)
return
}
writeSuccessBytes(w, payload)
}

View file

@ -159,6 +159,11 @@ func init() {
log.IfErr(Add(RoutePrefixPrivate, "documents/{documentID}/pages/level", []string{"POST", "OPTIONS"}, nil, ChangeDocumentPageLevel))
log.IfErr(Add(RoutePrefixPrivate, "documents/{documentID}/pages/sequence", []string{"POST", "OPTIONS"}, nil, ChangeDocumentPageSequence))
log.IfErr(Add(RoutePrefixPrivate, "documents/{documentID}/pages/batch", []string{"POST", "OPTIONS"}, nil, GetDocumentPagesBatch))
log.IfErr(Add(RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}/revisions", []string{"GET", "OPTIONS"}, nil, GetDocumentPageRevisions))
log.IfErr(Add(RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}/revisions/{revisionID}", []string{"GET", "OPTIONS"}, nil, GetDocumentPageDiff))
log.IfErr(Add(RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}/revisions/{revisionID}", []string{"POST", "OPTIONS"}, nil, RollbackDocumentPage))
log.IfErr(Add(RoutePrefixPrivate, "documents/{documentID}/revisions", []string{"GET", "OPTIONS"}, nil, GetDocumentRevisions))
log.IfErr(Add(RoutePrefixPrivate, "documents/{documentID}/pages", []string{"GET", "OPTIONS"}, nil, GetDocumentPages))
log.IfErr(Add(RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}", []string{"PUT", "OPTIONS"}, nil, UpdateDocumentPage))
log.IfErr(Add(RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}", []string{"DELETE", "OPTIONS"}, nil, DeleteDocumentPage))

View file

@ -238,6 +238,27 @@ func (p *PageMeta) SetDefaults() {
}
}
// Revision holds the previous version of a Page.
type Revision struct {
BaseEntity
OrgID string `json:"orgId"`
DocumentID string `json:"documentId"`
PageID string `json:"pageId"`
OwnerID string `json:"ownerId"`
UserID string `json:"userId"`
ContentType string `json:"contentType"`
PageType string `json:"pageType"`
Title string `json:"title"`
Body string `json:"body"`
RawBody string `json:"rawBody"`
Config string `json:"config"`
Email string `json:"email"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Initials string `json:"initials"`
Revisions int `json:"revisions"`
}
// DocumentMeta details who viewed the document.
type DocumentMeta struct {
Viewers []DocumentMetaViewer `json:"viewers"`

View file

@ -258,36 +258,6 @@ func (p *Persister) UpdatePage(page entity.Page, refID, userID string, skipRevis
}
}
//if page.Level == 1 { // may need to update the document name
//var doc entity.Document
//stmt4, err := p.Context.Transaction.Preparex("SELECT id, refid, orgid, labelid, job, location, title, excerpt, slug, tags, template, created, revised FROM document WHERE refid=?")
//defer utility.Close(stmt4)
//if err != nil {
//log.Error(fmt.Sprintf("Unable to prepare pagemanager doc query for Id %s", page.DocumentID), err)
//return err
//}
//err = stmt4.Get(&doc, page.DocumentID)
//if err != nil {
//log.Error(fmt.Sprintf("Unable to execute pagemanager document query for Id %s", page.DocumentID), err)
//return err
//}
//if doc.Title != page.Title {
//doc.Title = page.Title
//doc.Revised = page.Revised
//err = p.UpdateDocument(doc)
//if err != nil {
//log.Error(fmt.Sprintf("Unable to update document when page 1 altered DocumentId %s", page.DocumentID), err)
//return err
//}
//}
//}
// find any content links in the HTML
links := util.GetContentLinks(page.Body)
@ -336,6 +306,7 @@ func (p *Persister) UpdatePage(page entity.Page, refID, userID string, skipRevis
func (p *Persister) UpdatePageMeta(meta entity.PageMeta, updateUserID bool) (err error) {
err = nil
meta.Revised = time.Now().UTC()
if updateUserID {
meta.UserID = p.Context.UserID
}
@ -424,6 +395,9 @@ func (p *Persister) DeletePage(documentID, pageID string) (rows int64, err error
// mark as orphan links to this page
err = p.MarkOrphanPageLink(pageID)
// nuke revisions
_, err = p.DeletePageRevisions(pageID)
p.Base.Audit(p.Context, "remove-page", documentID, pageID)
}
@ -469,3 +443,68 @@ func (p *Persister) GetDocumentPageMeta(documentID string, externalSourceOnly bo
return
}
/********************
* Page Revisions
********************/
// GetPageRevision returns the revisionID page revision record.
func (p *Persister) GetPageRevision(revisionID string) (revision entity.Revision, err error) {
stmt, err := Db.Preparex("SELECT id, refid, orgid, documentid, ownerid, pageid, userid, contenttype, pagetype, title, body, rawbody, coalesce(config,JSON_UNQUOTE('{}')) as config, created, revised FROM revision WHERE orgid=? and refid=?")
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
}