mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-12-12 05:20:26 +00:00
Find backlinks on wiki start
This commit is contained in:
parent
9d89923ee5
commit
d9cc757546
@ -110,6 +110,7 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
||||
naviTitle(hyphaName),
|
||||
contents,
|
||||
treeHTML,
|
||||
h.BackLinkEntriesHTML(),
|
||||
prevHypha, nextHypha,
|
||||
hasAmnt),
|
||||
u,
|
||||
|
72
hyphae/backlink.go
Normal file
72
hyphae/backlink.go
Normal file
@ -0,0 +1,72 @@
|
||||
package hyphae
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/link"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func (h *Hypha) BackLinkEntriesHTML() (html string) {
|
||||
for _, backlinkHypha := range h.BackLinks {
|
||||
_ = link.Link{}
|
||||
html += fmt.Sprintf(`<li class="backlinks__entry">
|
||||
<a class="backlinks__link" href="/hypha/%s">%s</a>`, backlinkHypha.Name, util.BeautifulName(backlinkHypha.Name))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Hypha) outlinksThis(oh *Hypha) bool {
|
||||
for _, outlink := range h.OutLinks {
|
||||
if outlink == oh {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *Hypha) backlinkedBy(oh *Hypha) bool {
|
||||
for _, backlink := range h.BackLinks {
|
||||
if backlink == oh {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FindAllBacklinks iterates over all hyphae that have text parts, sets their outlinks and then sets backlinks.
|
||||
func FindAllBacklinks() {
|
||||
for h := range FilterTextHyphae(YieldExistingHyphae()) {
|
||||
findBacklinkWorker(h)
|
||||
}
|
||||
}
|
||||
|
||||
func findBacklinkWorker(h *Hypha) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
textContents, err := ioutil.ReadFile(h.TextPath)
|
||||
if err == nil {
|
||||
for outlink := range markup.Doc(h.Name, string(textContents)).OutLinks() {
|
||||
outlink := outlink
|
||||
outlinkHypha := ByName(outlink)
|
||||
if outlinkHypha == h {
|
||||
continue
|
||||
}
|
||||
|
||||
outlinkHypha.Lock()
|
||||
if !outlinkHypha.backlinkedBy(h) {
|
||||
outlinkHypha.BackLinks = append(outlinkHypha.BackLinks, h)
|
||||
outlinkHypha.InsertIfNewKeepExistence()
|
||||
}
|
||||
outlinkHypha.Unlock()
|
||||
|
||||
// Insert outlinkHypha if unique
|
||||
if !h.outlinksThis(outlinkHypha) {
|
||||
h.OutLinks = append(h.OutLinks, outlinkHypha)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -49,8 +49,8 @@ type Hypha struct {
|
||||
Exists bool
|
||||
TextPath string
|
||||
BinaryPath string
|
||||
OutLinks []*Hypha // not used yet
|
||||
BackLinks []*Hypha // not used yet
|
||||
OutLinks []*Hypha
|
||||
BackLinks []*Hypha
|
||||
}
|
||||
|
||||
var byNames = make(map[string]*Hypha)
|
||||
@ -59,17 +59,31 @@ var byNamesMutex = sync.Mutex{}
|
||||
// YieldExistingHyphae iterates over all hyphae and yields all existing ones.
|
||||
func YieldExistingHyphae() chan *Hypha {
|
||||
ch := make(chan *Hypha)
|
||||
go func(ch chan *Hypha) {
|
||||
go func() {
|
||||
for _, h := range byNames {
|
||||
if h.Exists {
|
||||
ch <- h
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// FilterTextHyphae filters the source channel and yields only those hyphae than have text parts.
|
||||
func FilterTextHyphae(src chan *Hypha) chan *Hypha {
|
||||
sink := make(chan *Hypha)
|
||||
go func() {
|
||||
for h := range src {
|
||||
if h.TextPath != "" {
|
||||
sink <- h
|
||||
}
|
||||
}
|
||||
close(sink)
|
||||
}()
|
||||
return sink
|
||||
}
|
||||
|
||||
// Subhyphae returns slice of subhyphae.
|
||||
func (h *Hypha) Subhyphae() []*Hypha {
|
||||
hyphae := []*Hypha{}
|
||||
@ -138,6 +152,18 @@ func (h *Hypha) InsertIfNew() (justCreated bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *Hypha) InsertIfNewKeepExistence() {
|
||||
hp := ByName(h.Name)
|
||||
|
||||
byNamesMutex.Lock()
|
||||
defer byNamesMutex.Unlock()
|
||||
if hp.Exists {
|
||||
hp = h
|
||||
} else {
|
||||
byNames[h.Name] = h
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hypha) delete() {
|
||||
byNamesMutex.Lock()
|
||||
h.Lock()
|
||||
|
2
main.go
2
main.go
@ -175,6 +175,8 @@ func main() {
|
||||
log.Println("Wiki storage directory is", WikiDir)
|
||||
hyphae.Index(WikiDir)
|
||||
log.Println("Indexed", hyphae.Count(), "hyphae")
|
||||
hyphae.FindAllBacklinks()
|
||||
log.Println("Found all backlinks")
|
||||
|
||||
history.Start(WikiDir)
|
||||
hyphae.SetHeaderLinks()
|
||||
|
@ -166,7 +166,7 @@ launchpadState:
|
||||
switch {
|
||||
case startsWith("=>"):
|
||||
href, text, class := Rocketlink(line, state.name)
|
||||
state.buf += fmt.Sprintf(` <li class="launchpad__entry"><a class="rocketlink %s" href="%s">%s</a></li>`, class, href, text)
|
||||
state.buf += fmt.Sprintf(` <li class="launchpad__entry"><a href="%s" class="rocketlink %s">%s</a></li>`, href, class, text)
|
||||
case startsWith("```"):
|
||||
state.where = "pre"
|
||||
addLine(state.buf + "</ul>")
|
||||
|
56
markup/outlink.go
Normal file
56
markup/outlink.go
Normal file
@ -0,0 +1,56 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/link"
|
||||
)
|
||||
|
||||
// OutLinks returns a channel of names of hyphae this mycodocument links.
|
||||
// Links include:
|
||||
// * Regular links
|
||||
// * Rocketlinks
|
||||
// * Transclusion
|
||||
// * Image galleries
|
||||
func (md *MycoDoc) OutLinks() chan string {
|
||||
ch := make(chan string)
|
||||
if !md.parsedAlready {
|
||||
md.Lex(0)
|
||||
}
|
||||
go func() {
|
||||
for _, line := range md.ast {
|
||||
switch v := line.contents.(type) {
|
||||
case string:
|
||||
if strings.HasPrefix(v, "<p") || strings.HasPrefix(v, "<ul class='launchpad'") {
|
||||
extractLinks(v, ch)
|
||||
}
|
||||
case Transclusion:
|
||||
ch <- v.name
|
||||
case Img:
|
||||
extractImageLinks(v, ch)
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
var reLinks = regexp.MustCompile(`<a href="/hypha/([^"]*)".*?</a>`)
|
||||
|
||||
func extractLinks(html string, ch chan string) {
|
||||
if results := reLinks.FindAllStringSubmatch(html, -1); results != nil {
|
||||
for _, result := range results {
|
||||
// result[0] is always present at this point and is not needed, because it is the whole matched substring (which we don't need)
|
||||
ch <- result[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func extractImageLinks(img Img, ch chan string) {
|
||||
for _, entry := range img.entries {
|
||||
if entry.srclink.Kind == link.LinkLocalHypha {
|
||||
ch <- entry.srclink.Address
|
||||
}
|
||||
}
|
||||
}
|
@ -58,7 +58,18 @@ var navEntries = []navEntry{
|
||||
|
||||
{% func relativeHyphae(relatives string) %}
|
||||
<aside class="relative-hyphae layout-card">
|
||||
<h1 class="relative-hyphae__title layout-card__title">Relative hyphae</h1>
|
||||
<h2 class="relative-hyphae__title layout-card__title">Relative hyphae</h2>
|
||||
{%s= relatives %}
|
||||
</aside>
|
||||
{% endfunc %}
|
||||
|
||||
{% func backlinks(backlinkEntries string) %}
|
||||
<aside class="backlinks layout-card">
|
||||
<h2 class="backlinks__title layout-card__title">Backlinks</h2>
|
||||
<nav class="backlinks__nav">
|
||||
<ul class="backlinks__list">
|
||||
{%s= backlinkEntries %}
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
{% endfunc %}
|
||||
|
@ -217,7 +217,7 @@ func streamrelativeHyphae(qw422016 *qt422016.Writer, relatives string) {
|
||||
//line templates/common.qtpl:59
|
||||
qw422016.N().S(`
|
||||
<aside class="relative-hyphae layout-card">
|
||||
<h1 class="relative-hyphae__title layout-card__title">Relative hyphae</h1>
|
||||
<h2 class="relative-hyphae__title layout-card__title">Relative hyphae</h2>
|
||||
`)
|
||||
//line templates/common.qtpl:62
|
||||
qw422016.N().S(relatives)
|
||||
@ -253,3 +253,49 @@ func relativeHyphae(relatives string) string {
|
||||
return qs422016
|
||||
//line templates/common.qtpl:64
|
||||
}
|
||||
|
||||
//line templates/common.qtpl:66
|
||||
func streambacklinks(qw422016 *qt422016.Writer, backlinkEntries string) {
|
||||
//line templates/common.qtpl:66
|
||||
qw422016.N().S(`
|
||||
<aside class="backlinks layout-card">
|
||||
<h2 class="backlinks__title layout-card__title">Backlinks</h2>
|
||||
<nav class="backlinks__nav">
|
||||
<ul class="backlinks__list">
|
||||
`)
|
||||
//line templates/common.qtpl:71
|
||||
qw422016.N().S(backlinkEntries)
|
||||
//line templates/common.qtpl:71
|
||||
qw422016.N().S(`
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
`)
|
||||
//line templates/common.qtpl:75
|
||||
}
|
||||
|
||||
//line templates/common.qtpl:75
|
||||
func writebacklinks(qq422016 qtio422016.Writer, backlinkEntries string) {
|
||||
//line templates/common.qtpl:75
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/common.qtpl:75
|
||||
streambacklinks(qw422016, backlinkEntries)
|
||||
//line templates/common.qtpl:75
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/common.qtpl:75
|
||||
}
|
||||
|
||||
//line templates/common.qtpl:75
|
||||
func backlinks(backlinkEntries string) string {
|
||||
//line templates/common.qtpl:75
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/common.qtpl:75
|
||||
writebacklinks(qb422016, backlinkEntries)
|
||||
//line templates/common.qtpl:75
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/common.qtpl:75
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/common.qtpl:75
|
||||
return qs422016
|
||||
//line templates/common.qtpl:75
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
{% endfunc %}
|
||||
|
||||
If `contents` == "", a helpful message is shown instead.
|
||||
{% func PageHTML(rq *http.Request, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName string, hasAmnt bool) %}
|
||||
{% func PageHTML(rq *http.Request, hyphaName, naviTitle, contents, relatives, backlinkEntries, prevHyphaName, nextHyphaName string, hasAmnt bool) %}
|
||||
{%= navHTML(rq, hyphaName, "page") %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
@ -65,5 +65,6 @@ If `contents` == "", a helpful message is shown instead.
|
||||
{% endif %}
|
||||
</main>
|
||||
{%= relativeHyphae(relatives) %}
|
||||
{%= backlinks(backlinkEntries) %}
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
@ -148,7 +148,7 @@ func RevisionHTML(rq *http.Request, hyphaName, naviTitle, contents, relatives, r
|
||||
// If `contents` == "", a helpful message is shown instead.
|
||||
|
||||
//line templates/readers.qtpl:33
|
||||
func StreamPageHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName string, hasAmnt bool) {
|
||||
func StreamPageHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, relatives, backlinkEntries, prevHyphaName, nextHyphaName string, hasAmnt bool) {
|
||||
//line templates/readers.qtpl:33
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
@ -273,33 +273,38 @@ func StreamPageHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, navi
|
||||
streamrelativeHyphae(qw422016, relatives)
|
||||
//line templates/readers.qtpl:67
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/readers.qtpl:68
|
||||
streambacklinks(qw422016, backlinkEntries)
|
||||
//line templates/readers.qtpl:68
|
||||
qw422016.N().S(`
|
||||
</div>
|
||||
`)
|
||||
//line templates/readers.qtpl:69
|
||||
//line templates/readers.qtpl:70
|
||||
}
|
||||
|
||||
//line templates/readers.qtpl:69
|
||||
func WritePageHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName string, hasAmnt bool) {
|
||||
//line templates/readers.qtpl:69
|
||||
//line templates/readers.qtpl:70
|
||||
func WritePageHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, relatives, backlinkEntries, prevHyphaName, nextHyphaName string, hasAmnt bool) {
|
||||
//line templates/readers.qtpl:70
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/readers.qtpl:69
|
||||
StreamPageHTML(qw422016, rq, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName, hasAmnt)
|
||||
//line templates/readers.qtpl:69
|
||||
//line templates/readers.qtpl:70
|
||||
StreamPageHTML(qw422016, rq, hyphaName, naviTitle, contents, relatives, backlinkEntries, prevHyphaName, nextHyphaName, hasAmnt)
|
||||
//line templates/readers.qtpl:70
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/readers.qtpl:69
|
||||
//line templates/readers.qtpl:70
|
||||
}
|
||||
|
||||
//line templates/readers.qtpl:69
|
||||
func PageHTML(rq *http.Request, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName string, hasAmnt bool) string {
|
||||
//line templates/readers.qtpl:69
|
||||
//line templates/readers.qtpl:70
|
||||
func PageHTML(rq *http.Request, hyphaName, naviTitle, contents, relatives, backlinkEntries, prevHyphaName, nextHyphaName string, hasAmnt bool) string {
|
||||
//line templates/readers.qtpl:70
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/readers.qtpl:69
|
||||
WritePageHTML(qb422016, rq, hyphaName, naviTitle, contents, relatives, prevHyphaName, nextHyphaName, hasAmnt)
|
||||
//line templates/readers.qtpl:69
|
||||
//line templates/readers.qtpl:70
|
||||
WritePageHTML(qb422016, rq, hyphaName, naviTitle, contents, relatives, backlinkEntries, prevHyphaName, nextHyphaName, hasAmnt)
|
||||
//line templates/readers.qtpl:70
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/readers.qtpl:69
|
||||
//line templates/readers.qtpl:70
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/readers.qtpl:69
|
||||
//line templates/readers.qtpl:70
|
||||
return qs422016
|
||||
//line templates/readers.qtpl:69
|
||||
//line templates/readers.qtpl:70
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user