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}`;
}
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 = `<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;
}
});
/*
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('&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 {
margin: 0;
padding: 5px 0;
padding: 2px 0;
font-size: 0.9rem;
color: $color-gray;
cursor: pointer;

View file

@ -28,7 +28,7 @@
{{#each candidates.attachments as |a|}}
<li class="link-item" {{ action 'setSelection' a }}>
{{#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 }}
{{/ui/ui-checkbox}}
</li>

View file

@ -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)

View file

@ -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)
}

View file

@ -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(`<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/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

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,
`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,

View file

@ -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,