1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-23 15:19:42 +02:00

link persistence

This commit is contained in:
Harvey Kandola 2016-10-25 15:56:08 -07:00
parent 4a17acce11
commit c27de6bcab
11 changed files with 160 additions and 104 deletions

View file

@ -115,7 +115,3 @@ export default Ember.Component.extend({
} }
} }
}); });
// editor.insertContent('&nbsp;<b>It\'s my button!</b>&nbsp;');
// Selects the first paragraph found
// tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);

View file

@ -39,34 +39,41 @@ export default Ember.Service.extend({
href = `/link/${link.linkType}/${link.id}`; href = `/link/${link.linkType}/${link.id}`;
} }
if (link.linkType === "file") { if (link.linkType === "file") {
href = `${endpoint}/public/attachments/${orgId}/${link.attachmentId}`; href = `${endpoint}/public/attachments/${orgId}/${link.targetId}`;
} }
if (link.linkType === "document") { if (link.linkType === "document") {
href = `/link/${link.linkType}/${link.id}`; href = `/link/${link.linkType}/${link.id}`;
} }
result = `<a data-link-id='${link.id}' data-link-type='${link.linkType}' href='${href}'>${link.title}</a>`; result = `<a data-documize='true' data-link-id='${link.id}' data-link-document-id='${link.documentId}' data-link-target-id='${link.targetId}' data-link-type='${link.linkType}' href='${href}'>${link.title}</a>`;
console.log(link);
console.log(result);
return 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! we should not redirect to a link that is in the same document!
what happens if we delete attachment? what happens if we delete attachment?
UpdatePage(): find and persist links from saved content UpdatePage(): find and persist links from saved content
1. We need to deal with links server-side 1. We need to deal with links server-side
2. We need to click on links in the browser and 'navigate' to linked content 2. We need to click on links in the browser and 'navigate' to linked content
editor.insertContent('&nbsp;<b>It\'s my button!</b>&nbsp;');
Selects the first paragraph found
tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);
*/ */

View file

@ -14,7 +14,7 @@
.link-item { .link-item {
margin: 0; margin: 0;
padding: 5px 0; padding: 2px 0;
font-size: 0.9rem; font-size: 0.9rem;
color: $color-gray; color: $color-gray;
cursor: pointer; cursor: pointer;

View file

@ -28,7 +28,7 @@
{{#each candidates.attachments as |a|}} {{#each candidates.attachments as |a|}}
<li class="link-item" {{ action 'setSelection' a }}> <li class="link-item" {{ action 'setSelection' a }}>
{{#ui/ui-checkbox selected=a.selected}} {{#ui/ui-checkbox selected=a.selected}}
<img class="icon" src="/assets/img/attachments/{{document/file-icon a.attachmentExtension}}" /> <img class="icon" src="/assets/img/attachments/{{document/file-icon a.context}}" />
{{ a.title }} {{ a.title }}
{{/ui/ui-checkbox}} {{/ui/ui-checkbox}}
</li> </li>

View file

@ -69,7 +69,7 @@ func GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
c := entity.LinkCandidate{ c := entity.LinkCandidate{
RefID: util.UniqueID(), RefID: util.UniqueID(),
DocumentID: documentID, DocumentID: documentID,
PageID: p.RefID, TargetID: p.RefID,
LinkType: "section", LinkType: "section",
Title: p.Title, Title: p.Title,
} }
@ -96,10 +96,10 @@ func GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
c := entity.LinkCandidate{ c := entity.LinkCandidate{
RefID: util.UniqueID(), RefID: util.UniqueID(),
DocumentID: documentID, DocumentID: documentID,
AttachmentID: f.RefID, TargetID: f.RefID,
LinkType: "file", LinkType: "file",
Title: f.Filename, Title: f.Filename,
AttachmentExtension: f.Extension, Context: f.Extension,
} }
fc = append(fc, c) fc = append(fc, c)

View file

@ -351,20 +351,16 @@ type Link struct {
LinkType string `json:"linkType"` LinkType string `json:"linkType"`
SourceID string `json:"sourceId"` SourceID string `json:"sourceId"`
DocumentID string `json:"documentId"` DocumentID string `json:"documentId"`
PageID string `json:"pageId"` TargetID string `json:"targetId"`
AttachmentID string `json:"attachmentId"`
Orphan bool `json:"orphan"` Orphan bool `json:"orphan"`
} }
// LinkCandidate defines a potential link to a document/section/attachment. // LinkCandidate defines a potential link to a document/section/attachment.
type LinkCandidate struct { type LinkCandidate struct {
RefID string `json:"id"` RefID string `json:"id"`
OrgID string `json:"orgId"`
LinkType string `json:"linkType"` LinkType string `json:"linkType"`
DocumentID string `json:"documentId"` DocumentID string `json:"documentId"`
PageID string `json:"pageId"` TargetID string `json:"targetId"`
AttachmentID string `json:"attachmentId"`
AttachmentExtension string `json:"attachmentExtension"`
Title string `json:"title"` // what we label the link Title string `json:"title"` // what we label the link
Context string `json:"context"` // additional context (e.g. excerpt, parent) Context string `json:"context"` // additional context (e.g. excerpt, parent, file extension)
} }

View file

@ -20,14 +20,14 @@ import (
"github.com/documize/community/core/utility" "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. // 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.UserID = p.Context.UserID
l.Created = time.Now().UTC() l.Created = time.Now().UTC()
l.Revised = 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) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -35,7 +35,7 @@ func (p *Persister) AddLink(l entity.Link) (err error) {
return 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 { if err != nil {
log.Error("Unable to execute insert for link", err) 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) { func (p *Persister) GetReferencedLinks(sectionID string) (links []entity.Link, err error) {
err = nil 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) err = Db.Select(&links, sql, p.Context.OrgID, sectionID)
@ -61,11 +61,11 @@ func (p *Persister) GetReferencedLinks(sectionID string) (links []entity.Link, e
return return
} }
// GetLinksToSection returns all links that are linking to the specified section. // GetContentLinksForSection returns all links that are linking to the specified section.
func (p *Persister) GetLinksToSection(sectionID string) (links []entity.Link, err error) { func (p *Persister) GetContentLinksForSection(sectionID string) (links []entity.Link, err error) {
err = nil 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) err = Db.Select(&links, sql, p.Context.OrgID, sectionID)
@ -77,11 +77,11 @@ func (p *Persister) GetLinksToSection(sectionID string) (links []entity.Link, er
return return
} }
// GetLinksToDocument returns all links that are linking to the specified document. // GetContentLinksForDocument returns all links that are linking to the specified document.
func (p *Persister) GetLinksToDocument(documentID string) (links []entity.Link, err error) { func (p *Persister) GetContentLinksForDocument(documentID string) (links []entity.Link, err error) {
err = nil 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) err = Db.Select(&links, sql, p.Context.OrgID, documentID)
@ -93,8 +93,8 @@ func (p *Persister) GetLinksToDocument(documentID string) (links []entity.Link,
return return
} }
// MarkLinkAsOrphan marks the link record as being invalid. // MarkOrphanContentLink marks the link record as being invalid.
func (p *Persister) MarkLinkAsOrphan(l entity.Link) (err error) { func (p *Persister) MarkOrphanContentLink(l entity.Link) (err error) {
l.Orphan = true l.Orphan = true
l.Revised = time.Now().UTC() l.Revised = time.Now().UTC()
@ -116,50 +116,12 @@ func (p *Persister) MarkLinkAsOrphan(l entity.Link) (err error) {
return 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. // DeleteLink removes saved link from the store.
func (p *Persister) DeleteLink(id string) (rows int64, err error) { func (p *Persister) DeleteLink(id string) (rows int64, err error) {
return p.Base.DeleteConstrained(p.Context.Transaction, "link", p.Context.OrgID, id) 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(`<a[^>]+\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 = `
// <p>dfdfdf</p><a href="/link/section/34354"><x><z?>
// <a czx zcxz href='/link/file/file.exe'><x><z?>
// `

View file

@ -20,6 +20,7 @@ import (
"github.com/documize/community/core/api/endpoint/models" "github.com/documize/community/core/api/endpoint/models"
"github.com/documize/community/core/api/entity" "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/log"
"github.com/documize/community/core/utility" "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) p.Base.Audit(p.Context, "update-page", page.DocumentID, page.RefID)
return return

73
core/api/util/links.go Normal file
View file

@ -0,0 +1,73 @@
// 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 util
import (
"strings"
"golang.org/x/net/html"
"github.com/documize/community/core/api/entity"
)
// GetContentLinks returns Documize generated <a> links.
// such links have an identifying attribute e.g. <a data-documize='true'...
func GetContentLinks(body string) (links []entity.Link) {
z := html.NewTokenizer(strings.NewReader(body))
for {
tt := z.Next()
switch {
case tt == html.ErrorToken:
// End of the document, we're done
return
case tt == html.StartTagToken:
t := z.Token()
// Check if the token is an <a> 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
}

View file

@ -319,11 +319,11 @@ CREATE TABLE IF NOT EXISTS `link` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`refid` CHAR(16) NOT NULL COLLATE utf8_bin, `refid` CHAR(16) NOT NULL COLLATE utf8_bin,
`orgid` 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, `userid` CHAR(16) DEFAULT '' COLLATE utf8_bin,
`sourceid` CHAR(16) NOT NULL COLLATE utf8_bin,
`linktype` 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, `orphan` BOOL NOT NULL DEFAULT 0,
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

View file

@ -5,11 +5,11 @@ CREATE TABLE IF NOT EXISTS `link` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`refid` CHAR(16) NOT NULL COLLATE utf8_bin, `refid` CHAR(16) NOT NULL COLLATE utf8_bin,
`orgid` 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, `userid` CHAR(16) DEFAULT '' COLLATE utf8_bin,
`sourceid` CHAR(16) NOT NULL COLLATE utf8_bin,
`linktype` 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, `orphan` BOOL NOT NULL DEFAULT 0,
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,