mirror of
https://github.com/documize/community.git
synced 2025-07-22 22:59:43 +02:00
link persistence
This commit is contained in:
parent
4a17acce11
commit
c27de6bcab
11 changed files with 160 additions and 104 deletions
|
@ -115,7 +115,3 @@ export default Ember.Component.extend({
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
// editor.insertContent(' <b>It\'s my button!</b> ');
|
||||
// Selects the first paragraph found
|
||||
// tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);
|
||||
|
|
|
@ -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(' <b>It\'s my button!</b> ');
|
||||
Selects the first paragraph found
|
||||
tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);
|
||||
*/
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
.link-item {
|
||||
margin: 0;
|
||||
padding: 5px 0;
|
||||
padding: 2px 0;
|
||||
font-size: 0.9rem;
|
||||
color: $color-gray;
|
||||
cursor: pointer;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
@ -96,10 +96,10 @@ func GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
|
|||
c := entity.LinkCandidate{
|
||||
RefID: util.UniqueID(),
|
||||
DocumentID: documentID,
|
||||
AttachmentID: f.RefID,
|
||||
TargetID: f.RefID,
|
||||
LinkType: "file",
|
||||
Title: f.Filename,
|
||||
AttachmentExtension: f.Extension,
|
||||
Context: f.Extension,
|
||||
}
|
||||
|
||||
fc = append(fc, c)
|
||||
|
|
|
@ -351,20 +351,16 @@ type Link struct {
|
|||
LinkType string `json:"linkType"`
|
||||
SourceID string `json:"sourceId"`
|
||||
DocumentID string `json:"documentId"`
|
||||
PageID string `json:"pageId"`
|
||||
AttachmentID string `json:"attachmentId"`
|
||||
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"`
|
||||
TargetID string `json:"targetId"`
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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?>
|
||||
// `
|
||||
|
|
|
@ -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
73
core/api/util/links.go
Normal 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
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue