1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-21 14:19:43 +02:00

Record document search history

This commit is contained in:
McMatts 2018-03-30 17:03:18 +01:00
parent 4816cf73c0
commit a6828e6b7f
8 changed files with 110 additions and 37 deletions

View file

@ -52,9 +52,9 @@ Space view.
## Latest version ## 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 ## OS support
@ -69,7 +69,7 @@ Documize runs on the following:
Documize is built with the following technologies: Documize is built with the following technologies:
- EmberJS (v2.18.0) - EmberJS (v2.18.0)
- Go (v1.10) - Go (v1.10.1)
...and supports the following databases: ...and supports the following databases:
@ -77,7 +77,7 @@ Documize is built with the following technologies:
- Percona (v5.7.16-10+) - Percona (v5.7.16-10+)
- MariaDB (10.3.0+) - MariaDB (10.3.0+)
Coming soon, PostgreSQL and Microsoft SQL Server support. Coming soon, PostgreSQL and Microsoft SQL Server database support.
## Authentication options ## Authentication options

View 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

View file

@ -34,8 +34,8 @@ func (s Scope) RecordUserActivity(ctx domain.RequestContext, activity activity.U
activity.UserID = ctx.UserID activity.UserID = ctx.UserID
activity.Created = time.Now().UTC() activity.Created = time.Now().UTC()
_, err = ctx.Transaction.Exec("INSERT INTO useractivity (orgid, userid, labelid, documentid, pageid, sourcetype, activitytype, created) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", _, 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.Created) activity.OrgID, activity.UserID, activity.LabelID, activity.DocumentID, activity.PageID, activity.SourceType, activity.ActivityType, activity.Metadata, activity.Created)
if err != nil { if err != nil {
err = errors.Wrap(err, "execute record user activity") 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. // GetDocumentActivity returns the metadata for a specified document.
func (s Scope) GetDocumentActivity(ctx domain.RequestContext, id string) (a []activity.DocumentActivity, err error) { 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(u.firstname, 'Anonymous') AS firstname, IFNULL(u.lastname, 'Viewer') AS lastname,
IFNULL(p.title, '') as pagetitle IFNULL(p.title, '') as pagetitle
FROM useractivity a FROM useractivity a

View file

@ -401,20 +401,44 @@ func (h *Handler) SearchDocuments(w http.ResponseWriter, r *http.Request) {
// Put in slugs for easy UI display of search URL // Put in slugs for easy UI display of search URL
for key, result := range results { for key, result := range results {
result.DocumentSlug = stringutil.MakeSlug(result.Document) results[key].DocumentSlug = stringutil.MakeSlug(result.Document)
result.SpaceSlug = stringutil.MakeSlug(result.Space) results[key].SpaceSlug = stringutil.MakeSlug(result.Space)
results[key] = result
} }
if len(results) == 0 { // Record user search history
results = []search.QueryResult{} go h.recordSearchActivity(ctx, results)
}
h.Store.Audit.Record(ctx, audit.EventTypeSearch) h.Store.Audit.Record(ctx, audit.EventTypeSearch)
response.WriteJSON(w, results) 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. // FetchDocumentData returns all document data in single API call.
func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) { func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
method := "document.FetchDocumentData" method := "document.FetchDocumentData"

View file

@ -204,6 +204,10 @@ func (s Scope) Documents(ctx domain.RequestContext, q search.QueryOptions) (resu
results = append(results, r4...) results = append(results, r4...)
} }
if len(results) == 0 {
results = []search.QueryResult{}
}
return 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 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 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)` 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.template = 0
AND d.labelid IN AND d.labelid IN
( (
SELECT refid FROM label WHERE orgid=? SELECT refid FROM label WHERE orgid=? AND refid IN
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 permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space'
UNION ALL UNION ALL
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' 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 ?` AND s.content LIKE ?`

View file

@ -41,7 +41,7 @@ func main() {
// product details // product details
rt.Product = env.ProdInfo{} rt.Product = env.ProdInfo{}
rt.Product.Major = "1" rt.Product.Major = "1"
rt.Product.Minor = "60" rt.Product.Minor = "61"
rt.Product.Patch = "0" rt.Product.Patch = "0"
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch) rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
rt.Product.Edition = "Community" rt.Product.Edition = "Community"

View file

@ -1,6 +1,6 @@
{ {
"name": "documize", "name": "documize",
"version": "1.60.0", "version": "1.61.0",
"description": "The Document IDE", "description": "The Document IDE",
"private": true, "private": true,
"repository": "", "repository": "",

View file

@ -23,8 +23,11 @@ type UserActivity struct {
PageID string `json:"pageId"` PageID string `json:"pageId"`
ActivityType Type `json:"activityType"` ActivityType Type `json:"activityType"`
SourceType SourceType `json:"sourceType"` SourceType SourceType `json:"sourceType"`
SourceName string `json:"sourceName"` Metadata string `json:"metadata"`
Created time.Time `json:"created"` 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. // DocumentActivity represents an activity taken against a document.
@ -57,50 +60,57 @@ const (
// SourceTypePage indicates activity against a document page. // SourceTypePage indicates activity against a document page.
SourceTypePage SourceType = 3 SourceTypePage SourceType = 3
// SourceTypeSearch indicates activity on search page.
SourceTypeSearch SourceType = 4
) )
const ( const (
// TypeCreated records user document creation // TypeCreated records user object creation (document or space).
TypeCreated Type = 1 TypeCreated Type = 1
// TypeRead states user has read document // TypeRead states user has consumed object (document or space).
TypeRead Type = 2 TypeRead Type = 2
// TypeEdited states user has editing document // TypeEdited states user has editing document.
TypeEdited Type = 3 TypeEdited Type = 3
// TypeDeleted records user deleting space/document // TypeDeleted records user deleting space/document.
TypeDeleted Type = 4 TypeDeleted Type = 4
// TypeArchived records user archiving space/document // TypeArchived records user archiving space/document.
TypeArchived Type = 5 TypeArchived Type = 5
// TypeApproved records user approval of document // TypeApproved records user approval of document.
TypeApproved Type = 6 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 TypeReverted Type = 7
// TypePublishedTemplate records user creating new document template // TypePublishedTemplate records user creating new document template.
TypePublishedTemplate Type = 8 TypePublishedTemplate Type = 8
// TypePublishedBlock records user creating reusable content block // TypePublishedBlock records user creating reusable content block.
TypePublishedBlock Type = 9 TypePublishedBlock Type = 9
// TypeCommented records user providing document feedback // TypeCommented records user providing document feedback.
TypeCommented Type = 10 TypeCommented Type = 10
// TypeRejected records user rejecting document // TypeRejected records user rejecting document.
TypeRejected Type = 11 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 TypeSentSecureLink Type = 12
// TypeDraft records user marking space/document as draft // TypeDraft records user marking space/document as draft.
TypeDraft Type = 13 TypeDraft Type = 13
// TypeVersioned records user creating new document version // TypeVersioned records user creating new document version.
TypeVersioned Type = 14 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 // TypeName returns one-work descriptor for activity type
@ -130,6 +140,12 @@ func TypeName(t Type) string {
return "Reject" return "Reject"
case TypeSentSecureLink: case TypeSentSecureLink:
return "Share" return "Share"
case TypeDraft:
return "Draft"
case TypeVersioned:
return "Version"
case TypeSearched:
return "Search"
} }
return "" return ""