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

Install helps docs as part of onboarding process

Our own docs are installed as sample data!

Refactored search reindexing code.
This commit is contained in:
HarveyKandola 2019-06-24 17:01:56 +01:00
parent 411f64c359
commit 3621e2fb79
26 changed files with 1957 additions and 1280 deletions

View file

@ -202,100 +202,11 @@ func (h *Handler) Sitemap(w http.ResponseWriter, r *http.Request) {
response.WriteBytes(w, buffer.Bytes())
}
// Reindex indexes all documents and attachments.
func (h *Handler) Reindex(w http.ResponseWriter, r *http.Request) {
ctx := domain.GetRequestContext(r)
if !ctx.GlobalAdmin {
response.WriteForbiddenError(w)
h.Runtime.Log.Info(fmt.Sprintf("%s attempted search reindex", ctx.UserID))
return
}
go h.rebuildSearchIndex(ctx)
response.WriteEmpty(w)
}
// rebuildSearchIndex indexes all documents and attachments.
func (h *Handler) rebuildSearchIndex(ctx domain.RequestContext) {
method := "meta.rebuildSearchIndex"
docs, err := h.Store.Meta.Documents(ctx)
if err != nil {
h.Runtime.Log.Error(method, err)
return
}
h.Runtime.Log.Info(fmt.Sprintf("Search re-index started for %d documents", len(docs)))
for i := range docs {
d := docs[i]
dc, err := h.Store.Meta.Document(ctx, d)
if err != nil {
h.Runtime.Log.Error(method, err)
// continue
}
at, err := h.Store.Meta.Attachments(ctx, d)
if err != nil {
h.Runtime.Log.Error(method, err)
// continue
}
h.Indexer.IndexDocument(ctx, dc, at)
pages, err := h.Store.Meta.Pages(ctx, d)
if err != nil {
h.Runtime.Log.Error(method, err)
// continue
}
for j := range pages {
h.Indexer.IndexContent(ctx, pages[j])
}
// Log process every N documents.
if i%100 == 0 {
h.Runtime.Log.Info(fmt.Sprintf("Search re-indexed %d documents...", i))
}
}
h.Runtime.Log.Info(fmt.Sprintf("Search re-index finished for %d documents", len(docs)))
}
// SearchStatus returns state of search index
func (h *Handler) SearchStatus(w http.ResponseWriter, r *http.Request) {
method := "meta.SearchStatus"
ctx := domain.GetRequestContext(r)
if !ctx.GlobalAdmin {
response.WriteForbiddenError(w)
h.Runtime.Log.Info(fmt.Sprintf("%s attempted get of search status", ctx.UserID))
return
}
count, err := h.Store.Meta.SearchIndexCount(ctx)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
var ss = searchStatus{Entries: count}
response.WriteJSON(w, ss)
}
type sitemapItem struct {
URL string
Date string
}
type searchStatus struct {
Entries int `json:"entries"`
}
// Themes returns list of installed UI themes.
func (h *Handler) Themes(w http.ResponseWriter, r *http.Request) {
type theme struct {

View file

@ -0,0 +1 @@
[{"id":"bh2sku21b54as00dsbk0","created":"2019-01-21T13:34:49Z","revised":"2019-01-21T13:34:49Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WNEpptWJ9AABRnha","category":"Maintenance"},{"id":"bh2skra1b54as00dsbjg","created":"2019-01-21T13:34:37Z","revised":"2019-01-21T13:34:37Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WNEpptWJ9AABRnha","category":"Setup"},{"id":"bh2s7ja1b54as00dsbj0","created":"2019-01-21T13:06:21Z","revised":"2019-01-21T13:06:21Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WNEpptWJ9AABRnha","category":"Users \u0026 Groups"},{"id":"bh2s7hi1b54as00dsbig","created":"2019-01-21T13:06:14Z","revised":"2019-01-21T13:06:14Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WNEpptWJ9AABRnha","category":"Integrations"},{"id":"bh2r4sq1b54as00dsb3g","created":"2019-01-21T11:52:20Z","revised":"2019-01-21T11:52:20Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WNEpptWJ9AABRnha","category":"Authentication"},{"id":"WtXOU7dMOwABe2UO","created":"2018-04-17T10:37:07Z","revised":"2018-04-17T10:37:07Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WtXNJ7dMOwABe2UK","category":"Enterprise"},{"id":"bgtmocgjkjf0hvnjli30","created":"2019-01-13T16:49:54Z","revised":"2019-01-13T16:49:54Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"betnq7venbkq5cjhoc0g","category":"Breaking Changes"},{"id":"bgtmo7ojkjf0hvnjli2g","created":"2019-01-13T16:49:36Z","revised":"2019-01-13T16:49:36Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"betnq7venbkq5cjhoc0g","category":"New Features"},{"id":"bgtmo50jkjf0hvnjli20","created":"2019-01-13T16:49:25Z","revised":"2019-01-13T16:49:25Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"betnq7venbkq5cjhoc0g","category":"Enhancements"},{"id":"bgtmo3gjkjf0hvnjli1g","created":"2019-01-13T16:49:18Z","revised":"2019-01-13T16:49:18Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"betnq7venbkq5cjhoc0g","category":"Bug Fixes"},{"id":"bgtmo1ojkjf0hvnjli10","created":"2019-01-13T16:49:11Z","revised":"2019-01-13T16:49:11Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"betnq7venbkq5cjhoc0g","category":"Community Edition"},{"id":"bgtmnvojkjf0hvnjli0g","created":"2019-01-13T16:49:03Z","revised":"2019-01-13T16:49:03Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"betnq7venbkq5cjhoc0g","category":"Enterprise Edition"}]

File diff suppressed because one or more lines are too long

1
domain/onboard/dmz_doc.json Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
[{"id":"WQnJXPMKrwABT4Nf","created":"2018-03-27T16:56:30Z","revised":"2018-03-27T16:56:30Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"VzO9ZqMOCgABGyfW","userId":"w4Nm8Fgw","linkType":"document","sourceDocumentId":"V16L08ucxwABhZF6","sourcePageId":"WQnJMvMKrwABT4Na","targetDocumentId":"VzSL8cVZ4QAB2B4Y","targetId":"","externalId":"","orphan":false},{"id":"Wt9Cf3cHWQABMuQa","created":"2018-04-24T14:43:20Z","revised":"2018-04-24T14:43:20Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WtXNJ7dMOwABe2UK","userId":"w4Nm8Fgw","linkType":"file","sourceDocumentId":"WtWbRbdMOwABe2SK","sourcePageId":"WtW317dMOwABe2TF","targetDocumentId":"WtWbRbdMOwABe2SK","targetId":"Wt9CaXcHWQABMuQM","externalId":"","orphan":false},{"id":"TtwPpzAUe4YeHYkC","created":"2018-07-11T00:17:14Z","revised":"2018-07-11T00:17:14Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"Wr5izCXOmgABcntP","userId":"w4Nm8Fgw","linkType":"network","sourceDocumentId":"W0VMb2zXqwABN4rn","sourcePageId":"W0VMdWzXqwABN4ro","targetDocumentId":"W0VMb2zXqwABN4rn","targetId":"","externalId":"","orphan":false},{"id":"W316i7J3xwAB8UmW","created":"2018-08-22T15:00:36Z","revised":"2018-08-22T15:00:36Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"VzO9ZqMOCgABGyfW","userId":"w4Nm8Fgw","linkType":"document","sourceDocumentId":"W31Sv7J3xwAB8Ult","sourcePageId":"W314jLJ3xwAB8UmL","targetDocumentId":"W316KbJ3xwAB8UmO","targetId":"","externalId":"","orphan":false},{"id":"bh7hek8cm9o1pnrmkf80","created":"2019-01-28T14:58:27Z","revised":"2019-01-28T14:58:27Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WOzFU_MXigAB6sIH","userId":"w4Nm8Fgw","linkType":"document","sourceDocumentId":"bh70rt0cm9o1pnrmk8o0","sourcePageId":"bh7hce0cm9o1pnrmkf5g","targetDocumentId":"bh5ikpa1b54as00dt4o0","targetId":"","externalId":"","orphan":false},{"id":"bh7lj28cm9o1pnrmkit0","created":"2019-01-28T19:35:44Z","revised":"2019-01-28T19:35:44Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WOzFU_MXigAB6sIH","userId":"w4Nm8Fgw","linkType":"section","sourceDocumentId":"WOvEC_MXigAB6sE1","sourcePageId":"WOyuEvMXigAB6sF_","targetDocumentId":"WOvEC_MXigAB6sE1","targetId":"WOytzvMXigAB6sFl","externalId":"","orphan":false},{"id":"bh7lq58cm9o1pnrmkj50","created":"2019-01-28T19:50:55Z","revised":"2019-01-28T19:50:55Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WtXNJ7dMOwABe2UK","userId":"w4Nm8Fgw","linkType":"document","sourceDocumentId":"bh7lnugcm9o1pnrmkj1g","sourcePageId":"bh7lp0gcm9o1pnrmkj3g","targetDocumentId":"WtWbRbdMOwABe2SK","targetId":"","externalId":"","orphan":false},{"id":"bh8337gcm9o1pnrmknm0","created":"2019-01-29T11:11:07Z","revised":"2019-01-29T11:11:07Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"WOzFU_MXigAB6sIH","userId":"w4Nm8Fgw","linkType":"section","sourceDocumentId":"bh82vl8cm9o1pnrmkngg","sourcePageId":"bh8307ocm9o1pnrmknk0","targetDocumentId":"WOvEC_MXigAB6sE1","targetId":"bh7ll9gcm9o1pnrmkj00","externalId":"","orphan":false},{"id":"W317bLJ3xwAB8Umf","created":"2019-03-05T09:01:06Z","revised":"2019-03-05T09:01:06Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"VzO9ZqMOCgABGyfW","userId":"w4Nm8Fgw","linkType":"document","sourceDocumentId":"W31Sv7J3xwAB8Ult","sourcePageId":"W317UrJ3xwAB8Uma","targetDocumentId":"V16L08ucxwABhZF6","targetId":"","externalId":"","orphan":false},{"id":"bj3gka67m02fu20qv23g","created":"2019-04-29T14:34:03Z","revised":"2019-04-29T14:34:03Z","orgId":"bk54k53c1osmepq14jq0","spaceId":"VzO9ZqMOCgABGyfW","userId":"w4Nm8Fgw","linkType":"document","sourceDocumentId":"V16L08ucxwABhZF6","sourcePageId":"WQnJtPMKrwABT4Ni","targetDocumentId":"VzSL8cVZ4QAB2B4Y","targetId":"","externalId":"","orphan":false}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
domain/onboard/dmz_space.json Executable file
View file

@ -0,0 +1 @@
[{"id":"leFaXwlU","created":"2016-05-11T23:03:47Z","revised":"2019-03-14T15:39:39Z","name":"Extending, Compiling \u0026 Architecture","desc":"Building integrations, compiling from source and understanding product architecture","orgId":"bk54k53c1osmepq14jq0","userId":"w4Nm8Fgw","labelId":"bgt3n9bgt73a1fb2llp0","spaceType":1,"countCategory":0,"countContent":4,"icon":"dmeta-meta-devops","lifecycle":0,"likes":"Did this help you?"},{"id":"betnq7venbkq5cjhoc0g","created":"2018-10-08T15:51:59Z","revised":"2019-06-11T10:33:44Z","name":"Release Notes","desc":"Documentation for what has changed within Documize including new features, enhancements, fixes and underlying technology stack improvements","orgId":"bk54k53c1osmepq14jq0","userId":"w4Nm8Fgw","labelId":"bgt3l3rgt73a1fb2llog","spaceType":1,"countCategory":6,"countContent":6,"icon":"dmeta-meta-announce","lifecycle":0,"likes":""},{"id":"WtXNJ7dMOwABe2UK","created":"2018-04-17T10:32:07Z","revised":"2019-01-12T19:10:37Z","name":"API","desc":"Programmatically access and manipulate Documize user data and configuration options","orgId":"bk54k53c1osmepq14jq0","userId":"w4Nm8Fgw","labelId":"bgt3n9bgt73a1fb2llp0","spaceType":1,"countCategory":1,"countContent":6,"icon":"dmeta-meta-flow","lifecycle":0,"likes":"Did this help you?"},{"id":"WNEpptWJ9AABRnha","created":"2017-03-21T13:24:55Z","revised":"2019-01-22T14:03:30Z","name":"Administration Guides","desc":"Managing all aspects of your Documize instance","orgId":"bk54k53c1osmepq14jq0","userId":"w4Nm8Fgw","labelId":"bgt3l3rgt73a1fb2llog","spaceType":1,"countCategory":5,"countContent":13,"icon":"dmeta-meta-tune","lifecycle":0,"likes":"Did this help you?"}]

View file

@ -0,0 +1 @@
[{"id":"bgt3n9bgt73a1fb2llp0","created":"2019-01-12T19:10:29Z","revised":"2019-01-12T19:12:53Z","orgId":"bk54k53c1osmepq14jq0","name":"Developers","color":"#880e4f"},{"id":"bgt3l3rgt73a1fb2llog","created":"2019-01-12T19:05:52Z","revised":"2019-01-12T19:12:50Z","orgId":"bk54k53c1osmepq14jq0","name":"Administration","color":"#ef6c00"},{"id":"bgt3kubgt73a1fb2llo0","created":"2019-01-12T19:05:30Z","revised":"2019-01-12T19:05:30Z","orgId":"bk54k53c1osmepq14jq0","name":"Getting Started","color":"#2e7d32"}]

367
domain/onboard/endpoint.go Normal file
View file

@ -0,0 +1,367 @@
// 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 onboard handles the setup of sample data for a new Documize instance.
package onboard
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"github.com/pkg/errors"
"github.com/documize/community/core/env"
"github.com/documize/community/core/response"
"github.com/documize/community/domain"
indexer "github.com/documize/community/domain/search"
"github.com/documize/community/domain/store"
om "github.com/documize/community/model/onboard"
"github.com/documize/community/model/permission"
"github.com/documize/community/server/web"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
Store *store.Store
Indexer indexer.Indexer
}
// InstallSample inserts sample data into database.
func (h *Handler) InstallSample(w http.ResponseWriter, r *http.Request) {
ctx := domain.GetRequestContext(r)
// Only proceed if we are in good standing.
if !h.Runtime.Product.IsValid(ctx) {
response.WriteBadLicense(w)
return
}
if !ctx.Administrator || !ctx.Authenticated || !ctx.GlobalAdmin {
response.WriteForbiddenError(w)
return
}
// Only proceed if we have no spaces and documents.
// This prevents sample data restore inside existing live instance.
spaces, docs := h.Store.Onboard.ContentCounts()
if spaces > 0 || docs > 0 {
h.Runtime.Log.Info("Unable to install sample data when database contains spaces/docs")
response.WriteForbiddenError(w)
return
}
// Load sample data from embedded assets.
data := h.loadSampleData()
if data.LoadFailure {
response.WriteError(w, "Unable to unpack sample data")
h.Runtime.Log.Info("Unable to unpack sample data")
return
}
data.Context = ctx
err := h.processSampleData(data)
if err != nil {
response.WriteError(w, "Unable to process sample data")
h.Runtime.Log.Error("Unable to process sample data", err)
return
}
h.Runtime.Log.Info("Onboarding complete")
h.Runtime.Log.Info("Building search index")
go h.Indexer.Rebuild(ctx)
response.WriteEmpty(w)
}
// Read sample data that is stored as embedded asset.
func (h *Handler) loadSampleData() (data om.SampleData) {
h.loadFile(data, "dmz_category.json", &data.Category)
h.loadFile(data, "dmz_category_member.json", &data.CategoryMember)
h.loadFile(data, "dmz_doc.json", &data.Document)
h.loadFile(data, "dmz_doc_attachment.json", &data.DocumentAttachment)
h.loadFile(data, "dmz_doc_link.json", &data.DocumentLink)
h.loadFile(data, "dmz_section.json", &data.Section)
h.loadFile(data, "dmz_section_meta.json", &data.SectionMeta)
h.loadFile(data, "dmz_space.json", &data.Space)
h.loadFile(data, "dmz_space_label.json", &data.SpaceLabel)
return
}
func (h *Handler) loadFile(data om.SampleData, filename string, v interface{}) {
err := h.unpackFile(filename, &v)
if err != nil {
data.LoadFailure = true
}
}
// Reads file and unmarshals content as JSON.
func (h *Handler) unpackFile(filename string, v interface{}) (err error) {
data, err := web.Embed.Asset("bindata/onboard/" + filename)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("missing %s", filename))
h.Runtime.Log.Error("failed to load file", err)
return
}
err = json.Unmarshal(data, &v)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("failed to read %s as JSON", filename))
h.Runtime.Log.Error("failed to load file", err)
return
}
return nil
}
// Insert data into database using sample data loaded from embedded assets.
func (h *Handler) processSampleData(data om.SampleData) (err error) {
data.Context.Transaction, _ = h.Runtime.StartTx(sql.LevelReadUncommitted)
// Space Label.
h.Runtime.Log.Info(fmt.Sprintf("Installing space label (%d)", len(data.SpaceLabel)))
for i := range data.SpaceLabel {
_, err = data.Context.Transaction.Exec(h.Runtime.Db.Rebind(`
INSERT INTO dmz_space_label
(c_refid, c_orgid, c_name, c_color, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?)`),
data.SpaceLabel[i].RefID, data.Context.OrgID, data.SpaceLabel[i].Name,
data.SpaceLabel[i].Color, data.SpaceLabel[i].Created, data.SpaceLabel[i].Revised)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert space label %s", data.SpaceLabel[i].RefID))
return
}
}
// Space.
h.Runtime.Log.Info(fmt.Sprintf("Installing space (%d)", len(data.Space)))
for i := range data.Space {
_, err = data.Context.Transaction.Exec(h.Runtime.Db.Rebind(`
INSERT INTO dmz_space
(c_refid, c_name, c_orgid, c_userid, c_type, c_lifecycle,
c_likes, c_icon, c_desc, c_count_category, c_count_content,
c_labelid, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
data.Space[i].RefID, data.Space[i].Name, data.Context.OrgID,
data.Context.UserID, data.Space[i].Type, data.Space[i].Lifecycle,
data.Space[i].Likes, data.Space[i].Icon, data.Space[i].Description,
data.Space[i].CountCategory, data.Space[i].CountContent,
data.Space[i].LabelID, data.Space[i].Created, data.Space[i].Revised)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert space record %s", data.Space[i].RefID))
return
}
}
h.Runtime.Log.Info(fmt.Sprintf("Installing category (%d)", len(data.Category)))
for i := range data.Category {
_, err = data.Context.Transaction.Exec(h.Runtime.Db.Rebind(`
INSERT INTO dmz_category (c_refid, c_orgid, c_spaceid, c_name, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?)`),
data.Category[i].RefID, data.Context.OrgID, data.Category[i].SpaceID, data.Category[i].Name,
data.Category[i].Created, data.Category[i].Revised)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert category %s", data.Category[i].RefID))
return
}
}
h.Runtime.Log.Info(fmt.Sprintf("Installing category member (%d)", len(data.CategoryMember)))
for i := range data.CategoryMember {
_, err = data.Context.Transaction.Exec(h.Runtime.Db.Rebind(`
INSERT INTO dmz_category_member
(c_refid, c_orgid, c_categoryid, c_spaceid, c_docid, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?)`),
data.CategoryMember[i].RefID, data.Context.OrgID, data.CategoryMember[i].CategoryID,
data.CategoryMember[i].SpaceID, data.CategoryMember[i].DocumentID,
data.CategoryMember[i].Created, data.CategoryMember[i].Revised)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert category %s", data.Category[i].RefID))
return
}
}
// Assign permissions per space space.
perm := permission.Permission{}
perm.OrgID = data.Context.OrgID
perm.Who = permission.UserPermission
perm.WhoID = data.Context.UserID
perm.Scope = permission.ScopeRow
perm.Location = permission.LocationSpace
for i := range data.Space {
perm.RefID = data.Space[i].RefID
perm.Action = "" // we send array for actions below
err = h.Store.Permission.AddPermissions(data.Context, perm,
permission.SpaceOwner, permission.SpaceManage, permission.SpaceView,
permission.DocumentAdd, permission.DocumentCopy, permission.DocumentDelete,
permission.DocumentEdit, permission.DocumentMove,
permission.DocumentTemplate, permission.DocumentApprove,
permission.DocumentVersion, permission.DocumentLifecycle)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert space permission %s", data.Space[i].RefID))
return
}
}
// Assign permissions per category.
for i := range data.Category {
pc := permission.Permission{}
pc.OrgID = data.Context.OrgID
pc.Who = permission.UserPermission
pc.WhoID = data.Context.UserID
pc.Scope = permission.ScopeRow
pc.Location = permission.LocationCategory
pc.RefID = data.Category[i].RefID
pc.Action = permission.CategoryView
err = h.Store.Permission.AddPermission(data.Context, pc)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert category permission %s", data.Category[i].RefID))
return
}
}
h.Runtime.Log.Info(fmt.Sprintf("Installing document (%d)", len(data.Document)))
for i := range data.Document {
_, err = data.Context.Transaction.Exec(h.Runtime.Db.Rebind(`
INSERT INTO dmz_doc
(c_refid, c_orgid, c_spaceid, c_userid, c_job, c_location,
c_name, c_desc, c_slug, c_tags, c_template, c_protection, c_approval,
c_lifecycle, c_versioned, c_versionid, c_versionorder, c_groupid, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
data.Document[i].RefID, data.Context.OrgID, data.Document[i].SpaceID,
data.Context.UserID, data.Document[i].Job,
data.Document[i].Location, data.Document[i].Name, data.Document[i].Excerpt,
data.Document[i].Slug, data.Document[i].Tags,
data.Document[i].Template, data.Document[i].Protection,
data.Document[i].Approval, data.Document[i].Lifecycle,
data.Document[i].Versioned, data.Document[i].VersionID,
data.Document[i].VersionOrder, data.Document[i].GroupID,
data.Document[i].Created, data.Document[i].Revised)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert document %s", data.Document[i].RefID))
return
}
}
h.Runtime.Log.Info(fmt.Sprintf("Installing document attachment (%d)", len(data.DocumentAttachment)))
for i := range data.DocumentAttachment {
_, err = data.Context.Transaction.Exec(h.Runtime.Db.Rebind(`
INSERT INTO dmz_doc_attachment
(c_refid, c_orgid, c_docid, c_sectionid, c_job, c_fileid,
c_filename, c_data, c_extension, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
data.DocumentAttachment[i].RefID, data.Context.OrgID,
data.DocumentAttachment[i].DocumentID, data.DocumentAttachment[i].SectionID,
data.DocumentAttachment[i].Job, data.DocumentAttachment[i].FileID,
data.DocumentAttachment[i].Filename,
data.DocumentAttachment[i].Data, data.DocumentAttachment[i].Extension,
data.DocumentAttachment[i].Created, data.DocumentAttachment[i].Revised)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert document attachment %s", data.DocumentAttachment[i].RefID))
return
}
}
h.Runtime.Log.Info(fmt.Sprintf("Installing document link (%d)", len(data.DocumentLink)))
for i := range data.DocumentLink {
_, err = data.Context.Transaction.Exec(h.Runtime.Db.Rebind(`
INSERT INTO dmz_doc_link
(c_refid, c_orgid, c_spaceid, c_userid, c_sourcedocid, c_sourcesectionid,
c_targetdocid, c_targetid, c_externalid, c_type, c_orphan, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
data.DocumentLink[i].RefID, data.Context.OrgID, data.DocumentLink[i].SpaceID,
data.Context.UserID, data.DocumentLink[i].SourceDocumentID, data.DocumentLink[i].SourceSectionID,
data.DocumentLink[i].TargetDocumentID, data.DocumentLink[i].TargetID, data.DocumentLink[i].ExternalID,
data.DocumentLink[i].LinkType, data.DocumentLink[i].Orphan,
data.DocumentLink[i].Created, data.DocumentLink[i].Revised)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert document link %s", data.DocumentLink[i].RefID))
return
}
}
h.Runtime.Log.Info(fmt.Sprintf("Installing section (%d)", len(data.Section)))
for i := range data.Section {
_, err = data.Context.Transaction.Exec(h.Runtime.Db.Rebind(`
INSERT INTO dmz_section
(c_refid, c_orgid, c_docid, c_userid, c_contenttype, c_type, c_level, c_name, c_body,
c_revisions, c_sequence, c_templateid, c_status, c_relativeid, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
data.Section[i].RefID, data.Context.OrgID, data.Section[i].DocumentID,
data.Context.UserID,
data.Section[i].ContentType, data.Section[i].Type,
data.Section[i].Level, data.Section[i].Name,
data.Section[i].Body, data.Section[i].Revisions,
data.Section[i].Sequence, data.Section[i].TemplateID,
data.Section[i].Status, data.Section[i].RelativeID,
data.Section[i].Created, data.Section[i].Revised)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert section %s", data.Section[i].RefID))
return
}
}
h.Runtime.Log.Info(fmt.Sprintf("Installing section meta (%d)", len(data.SectionMeta)))
for i := range data.SectionMeta {
_, err = data.Context.Transaction.Exec(h.Runtime.Db.Rebind(`
INSERT INTO dmz_section_meta
(c_sectionid, c_orgid, c_userid, c_docid, c_rawbody,
c_config, c_external, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),
data.SectionMeta[i].SectionID, data.Context.OrgID, data.Context.UserID,
data.SectionMeta[i].DocumentID,
data.SectionMeta[i].RawBody, data.SectionMeta[i].Config,
data.SectionMeta[i].ExternalSource,
data.SectionMeta[i].Created, data.SectionMeta[i].Revised)
if err != nil {
h.Runtime.Rollback(data.Context.Transaction)
err = errors.Wrap(err, fmt.Sprintf("unable to insert section meta %s", data.SectionMeta[i].SectionID))
return
}
}
ok := h.Runtime.Commit(data.Context.Transaction)
if !ok {
h.Runtime.Rollback(data.Context.Transaction)
return
}
// Build search index
return nil
}

46
domain/onboard/store.go Normal file
View file

@ -0,0 +1,46 @@
// 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 onboard handles the setup of sample data for a new Documize instance.
package onboard
import (
"github.com/documize/community/domain/store"
)
// Store provides data access to version information.
type Store struct {
store.Context
}
// ContentCounts returns the number of spaces and documents.
func (s Store) ContentCounts() (spaces, docs int) {
// By default we assume there is content in case of error condition.
spaces = 10
docs = 10
var m int
var err error
row := s.Runtime.Db.QueryRow("SELECT COUNT(*) FROM dmz_space")
err = row.Scan(&m)
if err == nil {
spaces = m
}
row = s.Runtime.Db.QueryRow("SELECT COUNT(*) FROM dmz_doc")
err = row.Scan(&m)
if err == nil {
docs = m
}
return
}

71
domain/search/endpoint.go Normal file
View file

@ -0,0 +1,71 @@
// 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 search
import (
"fmt"
"net/http"
"github.com/documize/community/core/env"
"github.com/documize/community/core/response"
"github.com/documize/community/domain"
"github.com/documize/community/domain/store"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
Store *store.Store
Indexer Indexer
}
// Reindex indexes all documents and attachments.
func (h *Handler) Reindex(w http.ResponseWriter, r *http.Request) {
ctx := domain.GetRequestContext(r)
if !ctx.GlobalAdmin {
response.WriteForbiddenError(w)
h.Runtime.Log.Info(fmt.Sprintf("%s attempted search reindex", ctx.UserID))
return
}
go h.Indexer.Rebuild(ctx)
response.WriteEmpty(w)
}
// Status returns state of search index
func (h *Handler) Status(w http.ResponseWriter, r *http.Request) {
method := "meta.SearchStatus"
ctx := domain.GetRequestContext(r)
if !ctx.GlobalAdmin {
response.WriteForbiddenError(w)
h.Runtime.Log.Info(fmt.Sprintf("%s attempted get of search status", ctx.UserID))
return
}
count, err := h.Store.Meta.SearchIndexCount(ctx)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
var ss = searchStatus{Entries: count}
response.WriteJSON(w, ss)
}
type searchStatus struct {
Entries int `json:"entries"`
}

View file

@ -13,6 +13,7 @@ package search
import (
"database/sql"
"fmt"
"github.com/documize/community/domain"
"github.com/documize/community/model/attachment"
@ -118,6 +119,53 @@ func (m *Indexer) DeleteContent(ctx domain.RequestContext, pageID string) {
m.runtime.Commit(ctx.Transaction)
}
// Rebuild recreates all search indexes.
func (m *Indexer) Rebuild(ctx domain.RequestContext) {
method := "search.rebuildSearchIndex"
docs, err := m.store.Meta.Documents(ctx)
if err != nil {
m.runtime.Log.Error(method, err)
return
}
m.runtime.Log.Info(fmt.Sprintf("Search re-index started for %d documents", len(docs)))
for i := range docs {
d := docs[i]
dc, err := m.store.Meta.Document(ctx, d)
if err != nil {
m.runtime.Log.Error(method, err)
// continue
}
at, err := m.store.Meta.Attachments(ctx, d)
if err != nil {
m.runtime.Log.Error(method, err)
// continue
}
m.IndexDocument(ctx, dc, at)
pages, err := m.store.Meta.Pages(ctx, d)
if err != nil {
m.runtime.Log.Error(method, err)
// continue
}
for j := range pages {
m.IndexContent(ctx, pages[j])
}
// Log process every N documents.
if i%100 == 0 {
m.runtime.Log.Info(fmt.Sprintf("Search re-indexed %d documents...", i))
}
}
m.runtime.Log.Info(fmt.Sprintf("Search re-index finished for %d documents", len(docs)))
}
// FilterCategoryProtected removes search results that cannot be seen by user
// due to document cateogory viewing permissions.
func FilterCategoryProtected(results []sm.QueryResult, cats []category.Category, members []category.Member) (filtered []sm.QueryResult) {

View file

@ -53,6 +53,7 @@ type Store struct {
Setting SettingStorer
Space SpaceStorer
User UserStorer
Onboard OnboardStorer
}
// SpaceStorer defines required methods for space management
@ -317,3 +318,8 @@ type LabelStorer interface {
Delete(ctx domain.RequestContext, id string) (rows int64, err error)
RemoveReference(ctx domain.RequestContext, labelID string) (err error)
}
// OnboardStorer defines required methods for enterprise customer onboarding process.
type OnboardStorer interface {
ContentCounts() (spaces, docs int)
}