From c27de6bcab12983170c4b40f9377a8c2460003de Mon Sep 17 00:00:00 2001 From: Harvey Kandola Date: Tue, 25 Oct 2016 15:56:08 -0700 Subject: [PATCH] link persistence --- .../components/section/wysiwyg/type-editor.js | 4 - app/app/services/link.js | 29 +++++--- app/app/styles/view/document/edit-tools.scss | 2 +- .../components/document/edit-tools.hbs | 2 +- core/api/endpoint/link_endpoint.go | 14 ++-- core/api/entity/objects.go | 30 ++++---- core/api/request/link.go | 74 +++++-------------- core/api/request/page.go | 22 ++++++ core/api/util/links.go | 73 ++++++++++++++++++ core/database/scripts/autobuild/db_00000.sql | 8 +- core/database/scripts/autobuild/db_00004.sql | 6 +- 11 files changed, 160 insertions(+), 104 deletions(-) create mode 100644 core/api/util/links.go diff --git a/app/app/components/section/wysiwyg/type-editor.js b/app/app/components/section/wysiwyg/type-editor.js index 2ad5e6b7..c8219199 100644 --- a/app/app/components/section/wysiwyg/type-editor.js +++ b/app/app/components/section/wysiwyg/type-editor.js @@ -115,7 +115,3 @@ export default Ember.Component.extend({ } } }); - -// editor.insertContent(' It\'s my button! '); -// Selects the first paragraph found -// tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]); diff --git a/app/app/services/link.js b/app/app/services/link.js index 4c5a2790..224d7838 100644 --- a/app/app/services/link.js +++ b/app/app/services/link.js @@ -39,34 +39,41 @@ export default Ember.Service.extend({ href = `/link/${link.linkType}/${link.id}`; } if (link.linkType === "file") { - href = `${endpoint}/public/attachments/${orgId}/${link.attachmentId}`; + href = `${endpoint}/public/attachments/${orgId}/${link.targetId}`; } if (link.linkType === "document") { href = `/link/${link.linkType}/${link.id}`; } - result = `${link.title}`; + result = `${link.title}`; - console.log(link); - console.log(result); return result; } }); /* +link handler + - implement link redirect handler -- + - for documents: client-side detect + - for sections: + - for attachments: direct link + - + +onDelete document/section/file: + - mark link table row as ORPHAN + - doc view: meta data fetch to load orphaned content + +Keyword search results - docs, section, files + we should not redirect to a link that is in the same document! - what happens if we delete attachment? - UpdatePage(): find and persist links from saved content - - - 1. We need to deal with links server-side 2. We need to click on links in the browser and 'navigate' to linked content - - +editor.insertContent(' It\'s my button! '); +Selects the first paragraph found +tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]); */ diff --git a/app/app/styles/view/document/edit-tools.scss b/app/app/styles/view/document/edit-tools.scss index 59e78607..14ecdd56 100644 --- a/app/app/styles/view/document/edit-tools.scss +++ b/app/app/styles/view/document/edit-tools.scss @@ -14,7 +14,7 @@ .link-item { margin: 0; - padding: 5px 0; + padding: 2px 0; font-size: 0.9rem; color: $color-gray; cursor: pointer; diff --git a/app/app/templates/components/document/edit-tools.hbs b/app/app/templates/components/document/edit-tools.hbs index 890ef044..068f0885 100644 --- a/app/app/templates/components/document/edit-tools.hbs +++ b/app/app/templates/components/document/edit-tools.hbs @@ -28,7 +28,7 @@ {{#each candidates.attachments as |a|}} diff --git a/core/api/endpoint/link_endpoint.go b/core/api/endpoint/link_endpoint.go index 1472ffa9..74e83436 100644 --- a/core/api/endpoint/link_endpoint.go +++ b/core/api/endpoint/link_endpoint.go @@ -69,7 +69,7 @@ func GetLinkCandidates(w http.ResponseWriter, r *http.Request) { c := entity.LinkCandidate{ RefID: util.UniqueID(), DocumentID: documentID, - PageID: p.RefID, + TargetID: p.RefID, LinkType: "section", Title: p.Title, } @@ -94,12 +94,12 @@ func GetLinkCandidates(w http.ResponseWriter, r *http.Request) { for _, f := range files { c := entity.LinkCandidate{ - RefID: util.UniqueID(), - DocumentID: documentID, - AttachmentID: f.RefID, - LinkType: "file", - Title: f.Filename, - AttachmentExtension: f.Extension, + RefID: util.UniqueID(), + DocumentID: documentID, + TargetID: f.RefID, + LinkType: "file", + Title: f.Filename, + Context: f.Extension, } fc = append(fc, c) diff --git a/core/api/entity/objects.go b/core/api/entity/objects.go index 7c00bdd7..d99d273a 100644 --- a/core/api/entity/objects.go +++ b/core/api/entity/objects.go @@ -346,25 +346,21 @@ type SitemapDocument struct { // Link defines a reference between a section and another document/section/attachment. type Link struct { BaseEntity - OrgID string `json:"orgId"` - UserID string `json:"userId"` - LinkType string `json:"linkType"` - SourceID string `json:"sourceId"` - DocumentID string `json:"documentId"` - PageID string `json:"pageId"` - AttachmentID string `json:"attachmentId"` - Orphan bool `json:"orphan"` + OrgID string `json:"orgId"` + UserID string `json:"userId"` + LinkType string `json:"linkType"` + SourceID string `json:"sourceId"` + DocumentID string `json:"documentId"` + TargetID string `json:"targetId"` + Orphan bool `json:"orphan"` } // LinkCandidate defines a potential link to a document/section/attachment. type LinkCandidate struct { - RefID string `json:"id"` - OrgID string `json:"orgId"` - LinkType string `json:"linkType"` - DocumentID string `json:"documentId"` - PageID string `json:"pageId"` - AttachmentID string `json:"attachmentId"` - AttachmentExtension string `json:"attachmentExtension"` - Title string `json:"title"` // what we label the link - Context string `json:"context"` // additional context (e.g. excerpt, parent) + RefID string `json:"id"` + LinkType string `json:"linkType"` + DocumentID string `json:"documentId"` + TargetID string `json:"targetId"` + Title string `json:"title"` // what we label the link + Context string `json:"context"` // additional context (e.g. excerpt, parent, file extension) } diff --git a/core/api/request/link.go b/core/api/request/link.go index 0f829aea..2e20ba93 100644 --- a/core/api/request/link.go +++ b/core/api/request/link.go @@ -20,14 +20,14 @@ import ( "github.com/documize/community/core/utility" ) -// AddLink inserts wiki-link into the store. +// AddContentLink inserts wiki-link into the store. // These links exist when content references another document or content. -func (p *Persister) AddLink(l entity.Link) (err error) { +func (p *Persister) AddContentLink(l entity.Link) (err error) { l.UserID = p.Context.UserID l.Created = time.Now().UTC() l.Revised = time.Now().UTC() - stmt, err := p.Context.Transaction.Preparex("INSERT INTO link (refid, orgid, userid, sourceid, documentid, pageid, linktype, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)") + stmt, err := p.Context.Transaction.Preparex("INSERT INTO link (refid, orgid, userid, sourceid, documentid, targetid, linktype, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)") defer utility.Close(stmt) if err != nil { @@ -35,7 +35,7 @@ func (p *Persister) AddLink(l entity.Link) (err error) { return } - _, err = stmt.Exec(l.RefID, l.OrgID, l.UserID, l.SourceID, l.DocumentID, l.PageID, l.LinkType, l.Created, l.Revised) + _, err = stmt.Exec(l.RefID, l.OrgID, l.UserID, l.SourceID, l.DocumentID, l.TargetID, l.LinkType, l.Created, l.Revised) if err != nil { log.Error("Unable to execute insert for link", err) @@ -49,7 +49,7 @@ func (p *Persister) AddLink(l entity.Link) (err error) { func (p *Persister) GetReferencedLinks(sectionID string) (links []entity.Link, err error) { err = nil - sql := "SELECT id,refid,orgid,userid,sourceid,documentid,sectionid,linktype,orphan,created,revised from link WHERE orgid=? AND sourceid=?" + sql := "SELECT id,refid,orgid,userid,sourceid,documentid,targetid,linktype,orphan,created,revised from link WHERE orgid=? AND sourceid=?" err = Db.Select(&links, sql, p.Context.OrgID, sectionID) @@ -61,11 +61,11 @@ func (p *Persister) GetReferencedLinks(sectionID string) (links []entity.Link, e return } -// GetLinksToSection returns all links that are linking to the specified section. -func (p *Persister) GetLinksToSection(sectionID string) (links []entity.Link, err error) { +// GetContentLinksForSection returns all links that are linking to the specified section. +func (p *Persister) GetContentLinksForSection(sectionID string) (links []entity.Link, err error) { err = nil - sql := "SELECT id,refid,orgid,userid,sourceid,documentid,sectionid,linktype,orphan,created,revised from link WHERE orgid=? AND sectionid=?" + sql := "SELECT id,refid,orgid,userid,sourceid,documentid,targetid,linktype,orphan,created,revised from link WHERE orgid=? AND sectionid=?" err = Db.Select(&links, sql, p.Context.OrgID, sectionID) @@ -77,11 +77,11 @@ func (p *Persister) GetLinksToSection(sectionID string) (links []entity.Link, er return } -// GetLinksToDocument returns all links that are linking to the specified document. -func (p *Persister) GetLinksToDocument(documentID string) (links []entity.Link, err error) { +// GetContentLinksForDocument returns all links that are linking to the specified document. +func (p *Persister) GetContentLinksForDocument(documentID string) (links []entity.Link, err error) { err = nil - sql := "SELECT id,refid,orgid,userid,sourceid,documentid,sectionid,linktype,orphan,created,revised from link WHERE orgid=? AND documentid=?" + sql := "SELECT id,refid,orgid,userid,sourceid,documentid,targetid,linktype,orphan,created,revised from link WHERE orgid=? AND documentid=?" err = Db.Select(&links, sql, p.Context.OrgID, documentID) @@ -93,8 +93,8 @@ func (p *Persister) GetLinksToDocument(documentID string) (links []entity.Link, return } -// MarkLinkAsOrphan marks the link record as being invalid. -func (p *Persister) MarkLinkAsOrphan(l entity.Link) (err error) { +// MarkOrphanContentLink marks the link record as being invalid. +func (p *Persister) MarkOrphanContentLink(l entity.Link) (err error) { l.Orphan = true l.Revised = time.Now().UTC() @@ -116,50 +116,12 @@ func (p *Persister) MarkLinkAsOrphan(l entity.Link) (err error) { return } +// DeleteSourceLinks removes saved links for given source. +func (p *Persister) DeleteSourceLinks(sourceID string) (rows int64, err error) { + return p.Base.DeleteWhere(p.Context.Transaction, fmt.Sprintf("DELETE FROM link WHERE orgid=\"%s\" AND sourceid=\"%s\"", p.Context.OrgID, sourceID)) +} + // DeleteLink removes saved link from the store. func (p *Persister) DeleteLink(id string) (rows int64, err error) { return p.Base.DeleteConstrained(p.Context.Transaction, "link", p.Context.OrgID, id) } - -// GetLinkCandidates returns matching results based upon specified parameters. -// func (p *Persister) GetLinkCandidates(keywords string) (c []entity.LinkCandidate, err error) { -// err = nil -// -// sql := "SELECT id,refid,orgid,userid,sourceid,documentid,sectionid,linktype,orphan,created,revised from link WHERE orgid=? AND sectionid=?" -// -// err = Db.Select(&links, sql, p.Context.OrgID, sectionID) -// -// if err != nil { -// log.Error(fmt.Sprintf("Unable to execute select links for org %s", p.Context.OrgID), err) -// return -// } -// -// return -// } -// -// package main -// -// import ( -// "fmt" -// "regexp" -// ) -// -// var imgRE = regexp.MustCompile(`]+\bhref=["']([^"']+)["']`) -// -// func findImages(htm string) []string { -// imgs := imgRE.FindAllStringSubmatch(htm, -1) -// out := make([]string, len(imgs)) -// for i := range out { -// out[i] = imgs[i][1] -// } -// return out -// } -// -// func main() { -// fmt.Printf("%q", findImages(data)) -// } -// -// const data = ` -//

dfdfdf

-// -// ` diff --git a/core/api/request/page.go b/core/api/request/page.go index a8c869be..ece2c741 100644 --- a/core/api/request/page.go +++ b/core/api/request/page.go @@ -20,6 +20,7 @@ import ( "github.com/documize/community/core/api/endpoint/models" "github.com/documize/community/core/api/entity" + "github.com/documize/community/core/api/util" "github.com/documize/community/core/log" "github.com/documize/community/core/utility" ) @@ -286,6 +287,27 @@ func (p *Persister) UpdatePage(page entity.Page, refID, userID string, skipRevis //} //} + // fimnd any content links + links := util.GetContentLinks(page.Body) + + // delete previous content links for this page + _, _ = p.DeleteSourceLinks(page.RefID) + + // save latest content links for this page + for _, link := range links { + link.OrgID = p.Context.OrgID + link.UserID = p.Context.UserID + link.SourceID = page.RefID + link.Orphan = false + + err := p.AddContentLink(link) + + if err != nil { + log.Error(fmt.Sprintf("Unable to insert content links for page %s", page.RefID), err) + return err + } + } + p.Base.Audit(p.Context, "update-page", page.DocumentID, page.RefID) return diff --git a/core/api/util/links.go b/core/api/util/links.go new file mode 100644 index 00000000..afa8ddb4 --- /dev/null +++ b/core/api/util/links.go @@ -0,0 +1,73 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// https://documize.com + +package util + +import ( + "strings" + + "golang.org/x/net/html" + + "github.com/documize/community/core/api/entity" +) + +// GetContentLinks returns Documize generated links. +// such links have an identifying attribute e.g. tag + isAnchor := t.Data == "a" + if !isAnchor { + continue + } + + // Extract the content link + ok, link := getLink(t) + if ok { + links = append(links, link) + } + } + } +} + +// Helper function to pull the href attribute from a Token +func getLink(t html.Token) (ok bool, link entity.Link) { + ok = false + + // Iterate over all of the Token's attributes until we find an "href" + for _, a := range t.Attr { + switch a.Key { + case "data-documize": + ok = true + case "data-link-id": + link.RefID = strings.TrimSpace(a.Val) + case "data-link-document-id": + link.DocumentID = strings.TrimSpace(a.Val) + case "data-link-target-id": + link.TargetID = strings.TrimSpace(a.Val) + case "data-link-type": + link.LinkType = strings.TrimSpace(a.Val) + } + } + + return +} diff --git a/core/database/scripts/autobuild/db_00000.sql b/core/database/scripts/autobuild/db_00000.sql index 0b92e6dc..79098323 100644 --- a/core/database/scripts/autobuild/db_00000.sql +++ b/core/database/scripts/autobuild/db_00000.sql @@ -319,14 +319,14 @@ CREATE TABLE IF NOT EXISTS `link` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `refid` CHAR(16) NOT NULL COLLATE utf8_bin, `orgid` CHAR(16) NOT NULL COLLATE utf8_bin, - `sourceid` CHAR(16) NOT NULL COLLATE utf8_bin, - `documentid` CHAR(16) NOT NULL COLLATE utf8_bin, - `sectionid` CHAR(16) DEFAULT '' COLLATE utf8_bin, `userid` CHAR(16) DEFAULT '' COLLATE utf8_bin, + `sourceid` CHAR(16) NOT NULL COLLATE utf8_bin, `linktype` CHAR(16) NOT NULL COLLATE utf8_bin, + `documentid` CHAR(16) NOT NULL COLLATE utf8_bin, + `targetid` CHAR(16) DEFAULT '' COLLATE utf8_bin, `orphan` BOOL NOT NULL DEFAULT 0, `created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - `revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + `revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT pk_id PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = InnoDB; diff --git a/core/database/scripts/autobuild/db_00004.sql b/core/database/scripts/autobuild/db_00004.sql index 1dc15b0d..3872dc4b 100644 --- a/core/database/scripts/autobuild/db_00004.sql +++ b/core/database/scripts/autobuild/db_00004.sql @@ -5,11 +5,11 @@ CREATE TABLE IF NOT EXISTS `link` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `refid` CHAR(16) NOT NULL COLLATE utf8_bin, `orgid` CHAR(16) NOT NULL COLLATE utf8_bin, - `sourceid` CHAR(16) NOT NULL COLLATE utf8_bin, - `documentid` CHAR(16) NOT NULL COLLATE utf8_bin, - `sectionid` CHAR(16) DEFAULT '' COLLATE utf8_bin, `userid` CHAR(16) DEFAULT '' COLLATE utf8_bin, + `sourceid` CHAR(16) NOT NULL COLLATE utf8_bin, `linktype` CHAR(16) NOT NULL COLLATE utf8_bin, + `documentid` CHAR(16) NOT NULL COLLATE utf8_bin, + `targetid` CHAR(16) DEFAULT '' COLLATE utf8_bin, `orphan` BOOL NOT NULL DEFAULT 0, `created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,