1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-21 22:29:41 +02:00

block code moved to new API

This commit is contained in:
Harvey Kandola 2017-08-01 11:33:16 +01:00
parent 7faf6d6cff
commit f7bcf1366c
9 changed files with 463 additions and 26 deletions

230
domain/block/endpoint.go Normal file
View file

@ -0,0 +1,230 @@
// 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 block
import (
"encoding/json"
"io/ioutil"
"net/http"
"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/document"
"github.com/documize/community/model/audit"
"github.com/documize/community/model/block"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
Store *domain.Store
}
// Add inserts new reusable content block into database.
func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
method := "block.add"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w)
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
b := block.Block{}
err = json.Unmarshal(body, &b)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
if !document.CanUploadDocument(ctx, *h.Store, b.LabelID) {
response.WriteForbiddenError(w)
return
}
b.RefID = uniqueid.Generate()
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
err = h.Store.Block.Add(ctx, b)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
h.Store.Audit.Record(ctx, audit.EventTypeBlockAdd)
ctx.Transaction.Commit()
b, err = h.Store.Block.Get(ctx, b.RefID)
if err != nil {
response.WriteServerError(w, method, err)
return
}
response.WriteJSON(w, b)
}
// Get returns requested reusable content block.
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
method := "block.add"
ctx := domain.GetRequestContext(r)
blockID := request.Param(r, "blockID")
if len(blockID) == 0 {
response.WriteMissingDataError(w, method, "blockID")
return
}
b, err := h.Store.Block.Get(ctx, blockID)
if err != nil {
response.WriteServerError(w, method, err)
return
}
response.WriteJSON(w, b)
}
// GetBySpace returns available reusable content blocks for the space.
func (h *Handler) GetBySpace(w http.ResponseWriter, r *http.Request) {
method := "block.space"
ctx := domain.GetRequestContext(r)
folderID := request.Param(r, "folderID")
if len(folderID) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
var b []block.Block
var err error
b, err = h.Store.Block.GetBySpace(ctx, folderID)
if len(b) == 0 {
b = []block.Block{}
}
if err != nil {
response.WriteServerError(w, method, err)
return
}
response.WriteJSON(w, b)
}
// Update inserts new reusable content block into database.
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
method := "block.update"
ctx := domain.GetRequestContext(r)
blockID := request.Param(r, "blockID")
if len(blockID) == 0 {
response.WriteMissingDataError(w, method, "blockID")
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, "Bad payload")
return
}
b := block.Block{}
err = json.Unmarshal(body, &b)
if err != nil {
response.WriteBadRequestError(w, method, "Bad payload")
return
}
b.RefID = blockID
if !document.CanUploadDocument(ctx, *h.Store, b.LabelID) {
response.WriteForbiddenError(w)
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
err = h.Store.Block.Update(ctx, b)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
h.Store.Audit.Record(ctx, audit.EventTypeBlockUpdate)
ctx.Transaction.Commit()
response.WriteEmpty(w)
}
// Delete removes requested reusable content block.
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
method := "block.update"
ctx := domain.GetRequestContext(r)
blockID := request.Param(r, "blockID")
if len(blockID) == 0 {
response.WriteMissingDataError(w, method, "blockID")
return
}
var err error
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
_, err = h.Store.Block.Delete(ctx, blockID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
err = h.Store.Block.RemoveReference(ctx, blockID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
h.Store.Audit.Record(ctx, audit.EventTypeBlockDelete)
ctx.Transaction.Commit()
response.WriteEmpty(w)
}

175
domain/block/mysql/store.go Normal file
View 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 mysql
import (
"database/sql"
"time"
"github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/domain"
"github.com/documize/community/domain/store/mysql"
"github.com/documize/community/model/block"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
)
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// Add saves reusable content block.
func (s Scope) Add(ctx domain.RequestContext, b block.Block) (err error) {
b.OrgID = ctx.OrgID
b.UserID = ctx.UserID
b.Created = time.Now().UTC()
b.Revised = time.Now().UTC()
stmt, err := ctx.Transaction.Preparex("INSERT INTO block (refid, orgid, labelid, userid, contenttype, pagetype, title, body, excerpt, rawbody, config, externalsource, used, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, "prepare insert block")
return
}
_, err = stmt.Exec(b.RefID, b.OrgID, b.LabelID, b.UserID, b.ContentType, b.PageType, b.Title, b.Body, b.Excerpt, b.RawBody, b.Config, b.ExternalSource, b.Used, b.Created, b.Revised)
if err != nil {
err = errors.Wrap(err, "execute insert block")
return
}
return
}
// Get returns requested reusable content block.
func (s Scope) Get(ctx domain.RequestContext, id string) (b block.Block, err error) {
stmt, err := s.Runtime.Db.Preparex("SELECT a.id, a.refid, a.orgid, a.labelid, a.userid, a.contenttype, a.pagetype, a.title, a.body, a.excerpt, a.rawbody, a.config, a.externalsource, a.used, a.created, a.revised, b.firstname, b.lastname FROM block a LEFT JOIN user b ON a.userid = b.refid WHERE a.orgid=? AND a.refid=?")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, "prepare select block")
return
}
err = stmt.Get(&b, ctx.OrgID, id)
if err != nil {
err = errors.Wrap(err, "execute select block")
return
}
return
}
// GetBySpace returns all reusable content scoped to given space.
func (s Scope) GetBySpace(ctx domain.RequestContext, spaceID string) (b []block.Block, err error) {
err = s.Runtime.Db.Select(&b, "SELECT a.id, a.refid, a.orgid, a.labelid, a.userid, a.contenttype, a.pagetype, a.title, a.body, a.excerpt, a.rawbody, a.config, a.externalsource, a.used, a.created, a.revised, b.firstname, b.lastname FROM block a LEFT JOIN user b ON a.userid = b.refid WHERE a.orgid=? AND a.labelid=? ORDER BY a.title", ctx.OrgID, spaceID)
if err != nil {
err = errors.Wrap(err, "select space blocks")
return
}
return
}
// IncrementUsage increments usage counter for content block.
func (s Scope) IncrementUsage(ctx domain.RequestContext, id string) (err error) {
stmt, err := ctx.Transaction.Preparex("UPDATE block SET used=used+1, revised=? WHERE orgid=? AND refid=?")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, "prepare increment block usage")
return
}
_, err = stmt.Exec(time.Now().UTC(), ctx.OrgID, id)
if err != nil {
err = errors.Wrap(err, "execute increment block usage")
return
}
return
}
// DecrementUsage decrements usage counter for content block.
func (s Scope) DecrementUsage(ctx domain.RequestContext, id string) (err error) {
stmt, err := ctx.Transaction.Preparex("UPDATE block SET used=used-1, revised=? WHERE orgid=? AND refid=?")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, "prepare decrement block usage")
return
}
_, err = stmt.Exec(time.Now().UTC(), ctx.OrgID, id)
if err != nil {
err = errors.Wrap(err, "execute decrement block usage")
return
}
return
}
// RemoveReference clears page.blockid for given blockID.
func (s Scope) RemoveReference(ctx domain.RequestContext, id string) (err error) {
stmt, err := ctx.Transaction.Preparex("UPDATE page SET blockid='', revised=? WHERE orgid=? AND blockid=?")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, "prepare remove block ref")
return
}
_, err = stmt.Exec(time.Now().UTC(), ctx.OrgID, id)
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
err = errors.Wrap(err, "execute remove block ref")
return
}
return
}
// Update updates existing reusable content block item.
func (s Scope) Update(ctx domain.RequestContext, b block.Block) (err error) {
b.Revised = time.Now().UTC()
var stmt *sqlx.NamedStmt
stmt, err = ctx.Transaction.PrepareNamed("UPDATE block SET title=:title, body=:body, excerpt=:excerpt, rawbody=:rawbody, config=:config, revised=:revised WHERE orgid=:orgid AND refid=:refid")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, "prepare update block")
return
}
_, err = stmt.Exec(&b)
if err != nil {
err = errors.Wrap(err, "execute update block")
return
}
return
}
// Delete removes reusable content block from database.
func (s Scope) Delete(ctx domain.RequestContext, id string) (rows int64, err error) {
b := mysql.BaseQuery{}
return b.DeleteConstrained(ctx.Transaction, "block", ctx.OrgID, id)
}

View file

@ -9,7 +9,7 @@
// //
// https://documize.com // https://documize.com
package document package mysql
import ( import (
"fmt" "fmt"

View file

@ -9,7 +9,7 @@
// //
// https://documize.com // https://documize.com
package link package mysql
import ( import (
"fmt" "fmt"

View file

@ -17,6 +17,7 @@ import (
"github.com/documize/community/model/activity" "github.com/documize/community/model/activity"
"github.com/documize/community/model/attachment" "github.com/documize/community/model/attachment"
"github.com/documize/community/model/audit" "github.com/documize/community/model/audit"
"github.com/documize/community/model/block"
"github.com/documize/community/model/doc" "github.com/documize/community/model/doc"
"github.com/documize/community/model/link" "github.com/documize/community/model/link"
"github.com/documize/community/model/org" "github.com/documize/community/model/org"
@ -43,6 +44,7 @@ type Store struct {
Activity ActivityStorer Activity ActivityStorer
Search SearchStorer Search SearchStorer
Indexer Indexer Indexer Indexer
Block BlockStorer
} }
// SpaceStorer defines required methods for space management // SpaceStorer defines required methods for space management
@ -205,3 +207,15 @@ type Indexer interface {
UpdateLevel(ctx RequestContext, documentID, pageID string, level int) (err error) UpdateLevel(ctx RequestContext, documentID, pageID string, level int) (err error)
Delete(ctx RequestContext, documentID, pageID string) (err error) Delete(ctx RequestContext, documentID, pageID string) (err error)
} }
// BlockStorer defines required methods for persisting reusable content blocks
type BlockStorer interface {
Add(ctx RequestContext, b block.Block) (err error)
Get(ctx RequestContext, id string) (b block.Block, err error)
GetBySpace(ctx RequestContext, spaceID string) (b []block.Block, err error)
IncrementUsage(ctx RequestContext, id string) (err error)
DecrementUsage(ctx RequestContext, id string) (err error)
RemoveReference(ctx RequestContext, id string) (err error)
Update(ctx RequestContext, b block.Block) (err error)
Delete(ctx RequestContext, id string) (rows int64, err error)
}

View file

@ -18,6 +18,7 @@ import (
account "github.com/documize/community/domain/account/mysql" account "github.com/documize/community/domain/account/mysql"
attachment "github.com/documize/community/domain/attachment/mysql" attachment "github.com/documize/community/domain/attachment/mysql"
audit "github.com/documize/community/domain/audit/mysql" audit "github.com/documize/community/domain/audit/mysql"
block "github.com/documize/community/domain/block/mysql"
doc "github.com/documize/community/domain/document/mysql" doc "github.com/documize/community/domain/document/mysql"
link "github.com/documize/community/domain/link/mysql" link "github.com/documize/community/domain/link/mysql"
org "github.com/documize/community/domain/organization/mysql" org "github.com/documize/community/domain/organization/mysql"
@ -43,6 +44,7 @@ func AttachStore(r *env.Runtime, s *domain.Store) {
s.Link = link.Scope{Runtime: r} s.Link = link.Scope{Runtime: r}
s.Page = page.Scope{Runtime: r} s.Page = page.Scope{Runtime: r}
s.Search = search.Scope{Runtime: r} s.Search = search.Scope{Runtime: r}
s.Block = block.Scope{Runtime: r}
} }
// https://github.com/golang-sql/sqlexp/blob/c2488a8be21d20d31abf0d05c2735efd2d09afe4/quoter.go#L46 // https://github.com/golang-sql/sqlexp/blob/c2488a8be21d20d31abf0d05c2735efd2d09afe4/quoter.go#L46

33
model/block/block.go Normal file
View file

@ -0,0 +1,33 @@
// 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 block
import "github.com/documize/community/model"
// Block represents a section that has been published as a reusable content block.
type Block struct {
model.BaseEntity
OrgID string `json:"orgId"`
LabelID string `json:"folderId"`
UserID string `json:"userId"`
ContentType string `json:"contentType"`
PageType string `json:"pageType"`
Title string `json:"title"`
Body string `json:"body"`
Excerpt string `json:"excerpt"`
RawBody string `json:"rawBody"` // a blob of data
Config string `json:"config"` // JSON based custom config for this type
ExternalSource bool `json:"externalSource"` // true indicates data sourced externally
Used uint64 `json:"used"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
}

View file

@ -95,22 +95,3 @@ type Revision struct {
Initials string `json:"initials"` Initials string `json:"initials"`
Revisions int `json:"revisions"` Revisions int `json:"revisions"`
} }
// Block represents a section that has been published as a reusable content block.
type Block struct {
model.BaseEntity
OrgID string `json:"orgId"`
LabelID string `json:"folderId"`
UserID string `json:"userId"`
ContentType string `json:"contentType"`
PageType string `json:"pageType"`
Title string `json:"title"`
Body string `json:"body"`
Excerpt string `json:"excerpt"`
RawBody string `json:"rawBody"` // a blob of data
Config string `json:"config"` // JSON based custom config for this type
ExternalSource bool `json:"externalSource"` // true indicates data sourced externally
Used uint64 `json:"used"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
}

View file

@ -19,6 +19,7 @@ import (
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/attachment" "github.com/documize/community/domain/attachment"
"github.com/documize/community/domain/auth" "github.com/documize/community/domain/auth"
"github.com/documize/community/domain/block"
"github.com/documize/community/domain/document" "github.com/documize/community/domain/document"
"github.com/documize/community/domain/link" "github.com/documize/community/domain/link"
"github.com/documize/community/domain/meta" "github.com/documize/community/domain/meta"
@ -44,6 +45,7 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) {
user := user.Handler{Runtime: rt, Store: s} user := user.Handler{Runtime: rt, Store: s}
link := link.Handler{Runtime: rt, Store: s} link := link.Handler{Runtime: rt, Store: s}
space := space.Handler{Runtime: rt, Store: s} space := space.Handler{Runtime: rt, Store: s}
block := block.Handler{Runtime: rt, Store: s}
setting := setting.Handler{Runtime: rt, Store: s} setting := setting.Handler{Runtime: rt, Store: s}
document := document.Handler{Runtime: rt, Store: s, Indexer: indexer} document := document.Handler{Runtime: rt, Store: s, Indexer: indexer}
attachment := attachment.Handler{Runtime: rt, Store: s} attachment := attachment.Handler{Runtime: rt, Store: s}
@ -134,11 +136,11 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) {
Add(rt, RoutePrefixPrivate, "sections", []string{"GET", "OPTIONS"}, nil, endpoint.GetSections) Add(rt, RoutePrefixPrivate, "sections", []string{"GET", "OPTIONS"}, nil, endpoint.GetSections)
Add(rt, RoutePrefixPrivate, "sections", []string{"POST", "OPTIONS"}, nil, endpoint.RunSectionCommand) Add(rt, RoutePrefixPrivate, "sections", []string{"POST", "OPTIONS"}, nil, endpoint.RunSectionCommand)
Add(rt, RoutePrefixPrivate, "sections/refresh", []string{"GET", "OPTIONS"}, nil, endpoint.RefreshSections) Add(rt, RoutePrefixPrivate, "sections/refresh", []string{"GET", "OPTIONS"}, nil, endpoint.RefreshSections)
Add(rt, RoutePrefixPrivate, "sections/blocks/space/{folderID}", []string{"GET", "OPTIONS"}, nil, endpoint.GetBlocksForSpace) Add(rt, RoutePrefixPrivate, "sections/blocks/space/{folderID}", []string{"GET", "OPTIONS"}, nil, block.GetBySpace)
Add(rt, RoutePrefixPrivate, "sections/blocks/{blockID}", []string{"GET", "OPTIONS"}, nil, endpoint.GetBlock) Add(rt, RoutePrefixPrivate, "sections/blocks/{blockID}", []string{"GET", "OPTIONS"}, nil, block.Get)
Add(rt, RoutePrefixPrivate, "sections/blocks/{blockID}", []string{"PUT", "OPTIONS"}, nil, endpoint.UpdateBlock) Add(rt, RoutePrefixPrivate, "sections/blocks/{blockID}", []string{"PUT", "OPTIONS"}, nil, block.Update)
Add(rt, RoutePrefixPrivate, "sections/blocks/{blockID}", []string{"DELETE", "OPTIONS"}, nil, endpoint.DeleteBlock) Add(rt, RoutePrefixPrivate, "sections/blocks/{blockID}", []string{"DELETE", "OPTIONS"}, nil, block.Delete)
Add(rt, RoutePrefixPrivate, "sections/blocks", []string{"POST", "OPTIONS"}, nil, endpoint.AddBlock) Add(rt, RoutePrefixPrivate, "sections/blocks", []string{"POST", "OPTIONS"}, nil, block.Add)
Add(rt, RoutePrefixPrivate, "sections/targets", []string{"GET", "OPTIONS"}, nil, endpoint.GetPageMoveCopyTargets) Add(rt, RoutePrefixPrivate, "sections/targets", []string{"GET", "OPTIONS"}, nil, endpoint.GetPageMoveCopyTargets)
Add(rt, RoutePrefixPrivate, "links/{folderID}/{documentID}/{pageID}", []string{"GET", "OPTIONS"}, nil, link.GetLinkCandidates) Add(rt, RoutePrefixPrivate, "links/{folderID}/{documentID}/{pageID}", []string{"GET", "OPTIONS"}, nil, link.GetLinkCandidates)