mirror of
https://github.com/documize/community.git
synced 2025-07-21 06:09:42 +02:00
Record document search history
This commit is contained in:
parent
4816cf73c0
commit
a6828e6b7f
8 changed files with 110 additions and 37 deletions
|
@ -52,9 +52,9 @@ Space view.
|
|||
|
||||
## Latest version
|
||||
|
||||
[Community edition: v1.60.0](https://github.com/documize/community/releases)
|
||||
[Community edition: v1.61.0](https://github.com/documize/community/releases)
|
||||
|
||||
[Enterprise edition: v1.62.0](https://documize.com/downloads)
|
||||
[Enterprise edition: v1.63.0](https://documize.com/downloads)
|
||||
|
||||
## OS support
|
||||
|
||||
|
@ -69,7 +69,7 @@ Documize runs on the following:
|
|||
Documize is built with the following technologies:
|
||||
|
||||
- EmberJS (v2.18.0)
|
||||
- Go (v1.10)
|
||||
- Go (v1.10.1)
|
||||
|
||||
...and supports the following databases:
|
||||
|
||||
|
@ -77,7 +77,7 @@ Documize is built with the following technologies:
|
|||
- Percona (v5.7.16-10+)
|
||||
- MariaDB (10.3.0+)
|
||||
|
||||
Coming soon, PostgreSQL and Microsoft SQL Server support.
|
||||
Coming soon, PostgreSQL and Microsoft SQL Server database support.
|
||||
|
||||
## Authentication options
|
||||
|
||||
|
|
28
core/database/scripts/autobuild/db_00020.sql
Normal file
28
core/database/scripts/autobuild/db_00020.sql
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* enterprise edition */
|
||||
|
||||
-- content analytics
|
||||
ALTER TABLE useractivity ADD COLUMN `metadata` VARCHAR(1000) NOT NULL DEFAULT '' AFTER `activitytype`;
|
||||
|
||||
-- content likes/feedback
|
||||
-- DROP TABLE IF EXISTS `vote`;
|
||||
|
||||
-- CREATE TABLE IF NOT EXISTS `vote` (
|
||||
-- `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
-- `refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||
-- `orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||
-- `documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||
-- `userid` CHAR(16) NOT NULL DEFAULT '' COLLATE utf8_bin,
|
||||
-- `vote` INT NOT NULL DEFAULT 0,
|
||||
-- `comment` VARCHAR(300) NOT NULL DEFAULT '',
|
||||
-- `created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
-- `revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
-- UNIQUE INDEX `idx_vote_id` (`id` ASC),
|
||||
-- INDEX `idx_vote_refid` (`refid` ASC),
|
||||
-- INDEX `idx_vote_documentid` (`documentid` ASC),
|
||||
-- INDEX `idx_vote_orgid` (`orgid` ASC))
|
||||
-- DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
|
||||
-- ENGINE = MyISAM;
|
||||
|
||||
-- CREATE INDEX idx_vote_1 ON vaote(orgid,documentid);
|
||||
|
||||
-- deprecations
|
|
@ -34,8 +34,8 @@ func (s Scope) RecordUserActivity(ctx domain.RequestContext, activity activity.U
|
|||
activity.UserID = ctx.UserID
|
||||
activity.Created = time.Now().UTC()
|
||||
|
||||
_, err = ctx.Transaction.Exec("INSERT INTO useractivity (orgid, userid, labelid, documentid, pageid, sourcetype, activitytype, created) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
activity.OrgID, activity.UserID, activity.LabelID, activity.DocumentID, activity.PageID, activity.SourceType, activity.ActivityType, activity.Created)
|
||||
_, err = ctx.Transaction.Exec("INSERT INTO useractivity (orgid, userid, labelid, documentid, pageid, sourcetype, activitytype, metadata, created) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
activity.OrgID, activity.UserID, activity.LabelID, activity.DocumentID, activity.PageID, activity.SourceType, activity.ActivityType, activity.Metadata, activity.Created)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute record user activity")
|
||||
|
@ -46,7 +46,7 @@ func (s Scope) RecordUserActivity(ctx domain.RequestContext, activity activity.U
|
|||
|
||||
// GetDocumentActivity returns the metadata for a specified document.
|
||||
func (s Scope) GetDocumentActivity(ctx domain.RequestContext, id string) (a []activity.DocumentActivity, err error) {
|
||||
qry := `SELECT a.id, DATE(a.created) as created, a.orgid, IFNULL(a.userid, '') AS userid, a.labelid, a.documentid, a.pageid, a.activitytype,
|
||||
qry := `SELECT a.id, DATE(a.created) as created, a.orgid, IFNULL(a.userid, '') AS userid, a.labelid, a.documentid, a.pageid, a.activitytype, a.metadata,
|
||||
IFNULL(u.firstname, 'Anonymous') AS firstname, IFNULL(u.lastname, 'Viewer') AS lastname,
|
||||
IFNULL(p.title, '') as pagetitle
|
||||
FROM useractivity a
|
||||
|
|
|
@ -401,20 +401,44 @@ func (h *Handler) SearchDocuments(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Put in slugs for easy UI display of search URL
|
||||
for key, result := range results {
|
||||
result.DocumentSlug = stringutil.MakeSlug(result.Document)
|
||||
result.SpaceSlug = stringutil.MakeSlug(result.Space)
|
||||
results[key] = result
|
||||
results[key].DocumentSlug = stringutil.MakeSlug(result.Document)
|
||||
results[key].SpaceSlug = stringutil.MakeSlug(result.Space)
|
||||
}
|
||||
|
||||
if len(results) == 0 {
|
||||
results = []search.QueryResult{}
|
||||
}
|
||||
// Record user search history
|
||||
go h.recordSearchActivity(ctx, results)
|
||||
|
||||
h.Store.Audit.Record(ctx, audit.EventTypeSearch)
|
||||
|
||||
response.WriteJSON(w, results)
|
||||
}
|
||||
|
||||
func (h *Handler) recordSearchActivity(ctx domain.RequestContext, q []search.QueryResult) {
|
||||
method := "recordSearchActivity"
|
||||
var err error
|
||||
|
||||
ctx.Transaction, err = h.Runtime.Db.Beginx()
|
||||
if err != nil {
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
|
||||
for i := range q {
|
||||
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
LabelID: q[i].SpaceID,
|
||||
DocumentID: q[i].DocumentID,
|
||||
SourceType: activity.SourceTypeSearch,
|
||||
ActivityType: activity.TypeSearched})
|
||||
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
h.Runtime.Log.Error(method, err)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
}
|
||||
|
||||
// FetchDocumentData returns all document data in single API call.
|
||||
func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
|
||||
method := "document.FetchDocumentData"
|
||||
|
|
|
@ -204,6 +204,10 @@ func (s Scope) Documents(ctx domain.RequestContext, q search.QueryOptions) (resu
|
|||
results = append(results, r4...)
|
||||
}
|
||||
|
||||
if len(results) == 0 {
|
||||
results = []search.QueryResult{}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -227,9 +231,10 @@ func (s Scope) matchFullText(ctx domain.RequestContext, keywords, itemType strin
|
|||
(
|
||||
SELECT refid FROM label WHERE orgid=? AND refid IN
|
||||
(
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space'
|
||||
UNION ALL
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND r.userid=?
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role'
|
||||
AND p.location='space' AND (r.userid=? OR r.userid='0')
|
||||
)
|
||||
)
|
||||
AND MATCH(s.content) AGAINST(? IN BOOLEAN MODE)`
|
||||
|
@ -280,13 +285,13 @@ func (s Scope) matchLike(ctx domain.RequestContext, keywords, itemType string) (
|
|||
-- AND d.template = 0
|
||||
AND d.labelid IN
|
||||
(
|
||||
SELECT refid FROM label WHERE orgid=?
|
||||
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view'
|
||||
SELECT refid FROM label WHERE orgid=? AND refid IN
|
||||
(
|
||||
SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space'
|
||||
UNION ALL
|
||||
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role'
|
||||
AND p.location='space' AND p.action='view' AND (r.userid=? OR r.userid='0')
|
||||
))
|
||||
AND p.location='space' AND (r.userid=? OR r.userid='0')
|
||||
)
|
||||
)
|
||||
AND s.content LIKE ?`
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ func main() {
|
|||
// product details
|
||||
rt.Product = env.ProdInfo{}
|
||||
rt.Product.Major = "1"
|
||||
rt.Product.Minor = "60"
|
||||
rt.Product.Minor = "61"
|
||||
rt.Product.Patch = "0"
|
||||
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
|
||||
rt.Product.Edition = "Community"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "documize",
|
||||
"version": "1.60.0",
|
||||
"version": "1.61.0",
|
||||
"description": "The Document IDE",
|
||||
"private": true,
|
||||
"repository": "",
|
||||
|
|
|
@ -23,8 +23,11 @@ type UserActivity struct {
|
|||
PageID string `json:"pageId"`
|
||||
ActivityType Type `json:"activityType"`
|
||||
SourceType SourceType `json:"sourceType"`
|
||||
SourceName string `json:"sourceName"`
|
||||
Metadata string `json:"metadata"`
|
||||
Created time.Time `json:"created"`
|
||||
|
||||
// Read-only outbound fields (e.g. for UI display)
|
||||
SourceName string `json:"sourceName"`
|
||||
}
|
||||
|
||||
// DocumentActivity represents an activity taken against a document.
|
||||
|
@ -57,50 +60,57 @@ const (
|
|||
|
||||
// SourceTypePage indicates activity against a document page.
|
||||
SourceTypePage SourceType = 3
|
||||
|
||||
// SourceTypeSearch indicates activity on search page.
|
||||
SourceTypeSearch SourceType = 4
|
||||
)
|
||||
|
||||
const (
|
||||
// TypeCreated records user document creation
|
||||
// TypeCreated records user object creation (document or space).
|
||||
TypeCreated Type = 1
|
||||
|
||||
// TypeRead states user has read document
|
||||
// TypeRead states user has consumed object (document or space).
|
||||
TypeRead Type = 2
|
||||
|
||||
// TypeEdited states user has editing document
|
||||
// TypeEdited states user has editing document.
|
||||
TypeEdited Type = 3
|
||||
|
||||
// TypeDeleted records user deleting space/document
|
||||
// TypeDeleted records user deleting space/document.
|
||||
TypeDeleted Type = 4
|
||||
|
||||
// TypeArchived records user archiving space/document
|
||||
// TypeArchived records user archiving space/document.
|
||||
TypeArchived Type = 5
|
||||
|
||||
// TypeApproved records user approval of document
|
||||
// TypeApproved records user approval of document.
|
||||
TypeApproved Type = 6
|
||||
|
||||
// TypeReverted records user content roll-back to previous version
|
||||
// TypeReverted records user content roll-back to previous document version.
|
||||
TypeReverted Type = 7
|
||||
|
||||
// TypePublishedTemplate records user creating new document template
|
||||
// TypePublishedTemplate records user creating new document template.
|
||||
TypePublishedTemplate Type = 8
|
||||
|
||||
// TypePublishedBlock records user creating reusable content block
|
||||
// TypePublishedBlock records user creating reusable content block.
|
||||
TypePublishedBlock Type = 9
|
||||
|
||||
// TypeCommented records user providing document feedback
|
||||
// TypeCommented records user providing document feedback.
|
||||
TypeCommented Type = 10
|
||||
|
||||
// TypeRejected records user rejecting document
|
||||
// TypeRejected records user rejecting document.
|
||||
TypeRejected Type = 11
|
||||
|
||||
// TypeSentSecureLink records user sending secure document link to email address(es)
|
||||
// TypeSentSecureLink records user sending secure document link via email.
|
||||
TypeSentSecureLink Type = 12
|
||||
|
||||
// TypeDraft records user marking space/document as draft
|
||||
// TypeDraft records user marking space/document as draft.
|
||||
TypeDraft Type = 13
|
||||
|
||||
// TypeVersioned records user creating new document version
|
||||
// TypeVersioned records user creating new document version.
|
||||
TypeVersioned Type = 14
|
||||
|
||||
// TypeSearched records user performing document keyword search.
|
||||
// Metadata field should contain search terms.
|
||||
TypeSearched Type = 15
|
||||
)
|
||||
|
||||
// TypeName returns one-work descriptor for activity type
|
||||
|
@ -130,6 +140,12 @@ func TypeName(t Type) string {
|
|||
return "Reject"
|
||||
case TypeSentSecureLink:
|
||||
return "Share"
|
||||
case TypeDraft:
|
||||
return "Draft"
|
||||
case TypeVersioned:
|
||||
return "Version"
|
||||
case TypeSearched:
|
||||
return "Search"
|
||||
}
|
||||
|
||||
return ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue