1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2025-01-06 10:00:26 +00:00

Implement backlinks core (index, i12n and viewing)

This commit is contained in:
Mikhail Chekan 2021-09-01 01:27:52 +08:00 committed by Timur Ismagilov
parent 0b2e52b99b
commit 820de6a0aa
9 changed files with 542 additions and 269 deletions

153
hyphae/backlinks.go Normal file
View File

@ -0,0 +1,153 @@
package hyphae
import (
"os"
"sync"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycomarkup"
"github.com/bouncepaw/mycomarkup/blocks"
"github.com/bouncepaw/mycomarkup/links"
"github.com/bouncepaw/mycomarkup/mycocontext"
)
// Using set here seems like the most appropriate solution
type linkSet map[string]struct{}
var backlinkIndex = make(map[string]linkSet)
var backlinkIndexMutex = sync.Mutex{}
// IndexBacklinks traverses all text hyphae, extracts links from them and forms an initial index
func IndexBacklinks() {
// It is safe to ignore the mutex, because there is only one worker.
src := FilterTextHyphae(YieldExistingHyphae())
for h := range src {
fileContentsT, errT := os.ReadFile(h.TextPath)
if errT == nil {
links := ExtractHyphaLinksFromContent(h.Name, string(fileContentsT))
for _, link := range links {
if _, exists := backlinkIndex[link]; !exists {
backlinkIndex[link] = make(linkSet)
}
backlinkIndex[link][h.Name] = struct{}{}
}
}
}
}
func YieldHyphaBacklinks(query string) <-chan string {
hyphaName := util.CanonicalName(query)
out := make(chan string)
sorted := PathographicSort(out)
go func() {
links := backlinkIndex[hyphaName]
for link := range links {
out <- link
}
close(out)
}()
return sorted
}
// YieldHyphaLinks extracts hypha links from a desired hypha and iterates over them
func YieldHyphaLinks(query string) <-chan string {
// That is merely a debug function, but it could be useful.
// Should we extract them into link-specific subfile? -- chekoopa
hyphaName := util.CanonicalName(query)
out := make(chan string)
go func() {
links := ExtractHyphaLinks(hyphaName)
for _, link := range links {
out <- link
}
close(out)
}()
return out
}
// ExtractHyphaLinks extracts hypha links from a desired hypha
func ExtractHyphaLinks(hyphaName string) []string {
var h = ByName(hyphaName)
if h.Exists {
fileContentsT, errT := os.ReadFile(h.TextPath)
if errT == nil {
return ExtractHyphaLinksFromContent(hyphaName, string(fileContentsT))
}
}
return make([]string, 0)
}
// ExtractHyphaLinksFromContent extracts hypha links from a provided text
func ExtractHyphaLinksFromContent(hyphaName string, contents string) []string {
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, contents)
linkVisitor, getLinks := LinkVisitor(ctx)
mycomarkup.BlockTree(ctx, linkVisitor)
foundLinks := getLinks()
var result []string
for _, link := range foundLinks {
if link.OfKind(links.LinkLocalHypha) {
result = append(result, link.TargetHypha())
}
}
return result
}
// LinkVisitor creates a visitor which extracts all the links
func LinkVisitor(ctx mycocontext.Context) (
visitor func(block blocks.Block),
result func() []links.Link,
) {
var (
collected []links.Link
)
var extractBlock func(block blocks.Block)
extractBlock = func(block blocks.Block) {
// fmt.Println(reflect.TypeOf(block))
switch b := block.(type) {
case blocks.Paragraph:
extractBlock(b.Formatted)
case blocks.Heading:
extractBlock(b.GetContents())
case blocks.List:
for _, item := range b.Items {
for _, sub := range item.Contents {
extractBlock(sub)
}
}
case blocks.Img:
for _, entry := range b.Entries {
extractBlock(entry)
}
case blocks.ImgEntry:
collected = append(collected, *b.Srclink)
case blocks.Transclusion:
link := *links.From(b.Target, "", ctx.HyphaName())
collected = append(collected, link)
case blocks.LaunchPad:
for _, rocket := range b.Rockets {
extractBlock(rocket)
}
case blocks.Formatted:
for _, line := range b.Lines {
for _, span := range line {
switch s := span.(type) {
case blocks.InlineLink:
collected = append(collected, *s.Link)
}
}
}
case blocks.RocketLink:
if !b.IsEmpty {
collected = append(collected, b.Link)
}
}
}
visitor = func(block blocks.Block) {
extractBlock(block)
}
result = func() []links.Link {
return collected
}
return
}

View File

@ -37,6 +37,7 @@ func main() {
// Init the subsystems:
hyphae.Index(files.HyphaeDir())
hyphae.IndexBacklinks()
user.InitUserDatabase()
history.Start()
history.InitGitRepo()

View File

@ -11,10 +11,10 @@
.modal__title_small { font-size: 1.5rem; }
.modal__confirmation-msg { margin: 0 0 .5rem 0; }
.hypha-list, .title-search__results { padding-left: 0; }
.hypha-list__entry, .title-search__entry { list-style-type: none; }
.hypha-list__link, .title-search__link { text-decoration: none; display: inline-block; padding: .25rem; }
.hypha-list__link:hover, .title-search__link:hover { text-decoration: underline; }
.hypha-list, .title-search__results, .backlinks__list { padding-left: 0; }
.hypha-list__entry, .title-search__entry, .backlinks__entry { list-style-type: none; }
.hypha-list__link, .title-search__link, .backlinks__link { text-decoration: none; display: inline-block; padding: .25rem; }
.hypha-list__link:hover, .title-search__link:hover, .backlinks__link:hover { text-decoration: underline; }
.hypha-list__amnt-type { font-size: smaller; color: #999; }
/* General element positions, from small to big */
@ -23,8 +23,6 @@
header { width: 100%; margin-bottom: 1rem; }
.layout-card li { list-style-type: none; }
.backlinks__list { padding: 0; margin: 0; }
.backlinks__link { text-decoration: none; display: block; padding: .25rem; padding-left: 1.25rem; }
@media screen and (max-width: 800px) {
.amnt-grid { grid-template-columns: 1fr; }
@ -65,13 +63,9 @@ header { width: 100%; margin-bottom: 1rem; }
.layout { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); }
.layout-card {max-width: 18rem;}
.main-width { margin: 0 auto; }
.backlinks { grid-column: 1 / span 1; margin-right: 0; }
main { grid-column: 2 / span 1; }
.sibling-hyphae, .edit-toolbar, .help-topics { grid-column: 3 / span 1; margin-left: 0; }
.edit-toolbar__buttons { grid-template-columns: 1fr; }
.backlinks__title { text-align: right; }
.backlinks__link { text-align: right; padding-right: 1.25rem; padding-left: .25rem; }
}
@media screen and (min-width: 1400px) {
@ -221,7 +215,7 @@ blockquote { border-left: 2px #999 solid; }
.upload-amnt { border: #eee 1px solid; }
td { border: #ddd 1px solid; }
.sibling-hyphae__link:hover, .backlinks__link:hover { background-color: #eee; }
.sibling-hyphae__link:hover { background-color: #eee; }
/* Dark theme! */
@media (prefers-color-scheme: dark) {
@ -231,7 +225,7 @@ main, article, header, .layout-card { background-color: #343434; color: #ddd; }
a, .wikilink_external { color: #f1fa8c; }
a:visited, .wikilink_external:visited { color: #ffb86c; }
.wikilink_new, .wikilink_new:visited { color: #dd4444; }
.subhyphae__link:hover, .sibling-hyphae__link:hover, .backlinks__link:hover { background-color: #444; }
.subhyphae__link:hover, .sibling-hyphae__link:hover { background-color: #444; }
.prevnext__el, .prevnext__el:visited { color: #ddd; }

View File

@ -22,6 +22,7 @@
{%= hyphaInfoEntry(h, u, "delete-ask", "Delete") %}
{%= hyphaInfoEntry(h, u, "text", "View markup") %}
{%= hyphaInfoEntry(h, u, "attachment", "Manage attachment") %}
{%= hyphaInfoEntry(h, u, "backlinks", "Backlinks") %}
</ul>
</nav>
{% endfunc %}

View File

@ -128,133 +128,138 @@ func streamhyphaInfo(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hyph
//line views/nav.qtpl:24
streamhyphaInfoEntry(qw422016, h, u, "attachment", "Manage attachment")
//line views/nav.qtpl:24
qw422016.N().S(`
`)
//line views/nav.qtpl:25
streamhyphaInfoEntry(qw422016, h, u, "backlinks", "Backlinks")
//line views/nav.qtpl:25
qw422016.N().S(`
</ul>
</nav>
`)
//line views/nav.qtpl:27
//line views/nav.qtpl:28
}
//line views/nav.qtpl:27
//line views/nav.qtpl:28
func writehyphaInfo(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha) {
//line views/nav.qtpl:27
//line views/nav.qtpl:28
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/nav.qtpl:27
//line views/nav.qtpl:28
streamhyphaInfo(qw422016, rq, h)
//line views/nav.qtpl:27
//line views/nav.qtpl:28
qt422016.ReleaseWriter(qw422016)
//line views/nav.qtpl:27
//line views/nav.qtpl:28
}
//line views/nav.qtpl:27
//line views/nav.qtpl:28
func hyphaInfo(rq *http.Request, h *hyphae.Hypha) string {
//line views/nav.qtpl:27
//line views/nav.qtpl:28
qb422016 := qt422016.AcquireByteBuffer()
//line views/nav.qtpl:27
//line views/nav.qtpl:28
writehyphaInfo(qb422016, rq, h)
//line views/nav.qtpl:27
//line views/nav.qtpl:28
qs422016 := string(qb422016.B)
//line views/nav.qtpl:27
//line views/nav.qtpl:28
qt422016.ReleaseByteBuffer(qb422016)
//line views/nav.qtpl:27
//line views/nav.qtpl:28
return qs422016
//line views/nav.qtpl:27
//line views/nav.qtpl:28
}
//line views/nav.qtpl:29
//line views/nav.qtpl:30
func streamsiblingHyphaeHTML(qw422016 *qt422016.Writer, siblings string) {
//line views/nav.qtpl:29
//line views/nav.qtpl:30
qw422016.N().S(`
<aside class="sibling-hyphae layout-card">
<h2 class="sibling-hyphae__title layout-card__title">Sibling hyphae</h2>
`)
//line views/nav.qtpl:32
//line views/nav.qtpl:33
qw422016.N().S(siblings)
//line views/nav.qtpl:32
//line views/nav.qtpl:33
qw422016.N().S(`
</aside>
`)
//line views/nav.qtpl:34
//line views/nav.qtpl:35
}
//line views/nav.qtpl:34
//line views/nav.qtpl:35
func writesiblingHyphaeHTML(qq422016 qtio422016.Writer, siblings string) {
//line views/nav.qtpl:34
//line views/nav.qtpl:35
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/nav.qtpl:34
//line views/nav.qtpl:35
streamsiblingHyphaeHTML(qw422016, siblings)
//line views/nav.qtpl:34
//line views/nav.qtpl:35
qt422016.ReleaseWriter(qw422016)
//line views/nav.qtpl:34
//line views/nav.qtpl:35
}
//line views/nav.qtpl:34
//line views/nav.qtpl:35
func siblingHyphaeHTML(siblings string) string {
//line views/nav.qtpl:34
//line views/nav.qtpl:35
qb422016 := qt422016.AcquireByteBuffer()
//line views/nav.qtpl:34
//line views/nav.qtpl:35
writesiblingHyphaeHTML(qb422016, siblings)
//line views/nav.qtpl:34
//line views/nav.qtpl:35
qs422016 := string(qb422016.B)
//line views/nav.qtpl:34
//line views/nav.qtpl:35
qt422016.ReleaseByteBuffer(qb422016)
//line views/nav.qtpl:34
//line views/nav.qtpl:35
return qs422016
//line views/nav.qtpl:34
//line views/nav.qtpl:35
}
//line views/nav.qtpl:36
//line views/nav.qtpl:37
func StreamSubhyphaeHTML(qw422016 *qt422016.Writer, subhyphae string) {
//line views/nav.qtpl:36
//line views/nav.qtpl:37
qw422016.N().S(`
`)
//line views/nav.qtpl:37
//line views/nav.qtpl:38
if strings.TrimSpace(subhyphae) != "" {
//line views/nav.qtpl:37
//line views/nav.qtpl:38
qw422016.N().S(`
<section class="subhyphae">
<h2 class="subhyphae__title">Subhyphae</h2>
<nav class="subhyphae__nav">
<ul class="subhyphae__list">
`)
//line views/nav.qtpl:42
//line views/nav.qtpl:43
qw422016.N().S(subhyphae)
//line views/nav.qtpl:42
//line views/nav.qtpl:43
qw422016.N().S(`
</ul>
</nav>
</section>
`)
//line views/nav.qtpl:46
//line views/nav.qtpl:47
}
//line views/nav.qtpl:46
//line views/nav.qtpl:47
qw422016.N().S(`
`)
//line views/nav.qtpl:47
//line views/nav.qtpl:48
}
//line views/nav.qtpl:47
//line views/nav.qtpl:48
func WriteSubhyphaeHTML(qq422016 qtio422016.Writer, subhyphae string) {
//line views/nav.qtpl:47
//line views/nav.qtpl:48
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/nav.qtpl:47
//line views/nav.qtpl:48
StreamSubhyphaeHTML(qw422016, subhyphae)
//line views/nav.qtpl:47
//line views/nav.qtpl:48
qt422016.ReleaseWriter(qw422016)
//line views/nav.qtpl:47
//line views/nav.qtpl:48
}
//line views/nav.qtpl:47
//line views/nav.qtpl:48
func SubhyphaeHTML(subhyphae string) string {
//line views/nav.qtpl:47
//line views/nav.qtpl:48
qb422016 := qt422016.AcquireByteBuffer()
//line views/nav.qtpl:47
//line views/nav.qtpl:48
WriteSubhyphaeHTML(qb422016, subhyphae)
//line views/nav.qtpl:47
//line views/nav.qtpl:48
qs422016 := string(qb422016.B)
//line views/nav.qtpl:47
//line views/nav.qtpl:48
qt422016.ReleaseByteBuffer(qb422016)
//line views/nav.qtpl:47
//line views/nav.qtpl:48
return qs422016
//line views/nav.qtpl:47
//line views/nav.qtpl:48
}

View File

@ -120,6 +120,21 @@ It outputs a poorly formatted JSON, but it works and is valid.
}
{% endfunc %}
{% func BacklinksHTML(query string, generator func(string) <-chan string) %}
<div class="layout">
<main class="main-width backlinks">
<h1>Backlinks to {%s query %}</h1>
<p>Hyphae which have a link to the selected hypha are listed below.</p>
<ul class="backlinks__list">
{% for hyphaName := range generator(query) %}
<li class="backlinks__entry">
<a class="backlinks__link wikilink" href="/hypha/{%s hyphaName %}">{%s util.BeautifulName(hyphaName) %}</a>
</li>
{% endfor %}
</main>
</div>
{% endfunc %}
{% func HelpHTML(content string) %}
<div class="layout">
<main class="main-width help">

View File

@ -397,95 +397,161 @@ func TitleSearchJSON(query string, generator func(string) <-chan string) string
}
//line views/stuff.qtpl:123
func StreamHelpHTML(qw422016 *qt422016.Writer, content string) {
func StreamBacklinksHTML(qw422016 *qt422016.Writer, query string, generator func(string) <-chan string) {
//line views/stuff.qtpl:123
qw422016.N().S(`
<div class="layout">
<main class="main-width backlinks">
<h1>Backlinks to `)
//line views/stuff.qtpl:126
qw422016.E().S(query)
//line views/stuff.qtpl:126
qw422016.N().S(`</h1>
<p>Hyphae which have a link to the selected hypha are listed below.</p>
<ul class="backlinks__list">
`)
//line views/stuff.qtpl:129
for hyphaName := range generator(query) {
//line views/stuff.qtpl:129
qw422016.N().S(`
<li class="backlinks__entry">
<a class="backlinks__link wikilink" href="/hypha/`)
//line views/stuff.qtpl:131
qw422016.E().S(hyphaName)
//line views/stuff.qtpl:131
qw422016.N().S(`">`)
//line views/stuff.qtpl:131
qw422016.E().S(util.BeautifulName(hyphaName))
//line views/stuff.qtpl:131
qw422016.N().S(`</a>
</li>
`)
//line views/stuff.qtpl:133
}
//line views/stuff.qtpl:133
qw422016.N().S(`
</main>
</div>
`)
//line views/stuff.qtpl:136
}
//line views/stuff.qtpl:136
func WriteBacklinksHTML(qq422016 qtio422016.Writer, query string, generator func(string) <-chan string) {
//line views/stuff.qtpl:136
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:136
StreamBacklinksHTML(qw422016, query, generator)
//line views/stuff.qtpl:136
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:136
}
//line views/stuff.qtpl:136
func BacklinksHTML(query string, generator func(string) <-chan string) string {
//line views/stuff.qtpl:136
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:136
WriteBacklinksHTML(qb422016, query, generator)
//line views/stuff.qtpl:136
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:136
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:136
return qs422016
//line views/stuff.qtpl:136
}
//line views/stuff.qtpl:138
func StreamHelpHTML(qw422016 *qt422016.Writer, content string) {
//line views/stuff.qtpl:138
qw422016.N().S(`
<div class="layout">
<main class="main-width help">
<article>
`)
//line views/stuff.qtpl:127
//line views/stuff.qtpl:142
qw422016.N().S(content)
//line views/stuff.qtpl:127
//line views/stuff.qtpl:142
qw422016.N().S(`
</article>
</main>
`)
//line views/stuff.qtpl:130
//line views/stuff.qtpl:145
qw422016.N().S(helpTopicsHTML())
//line views/stuff.qtpl:130
//line views/stuff.qtpl:145
qw422016.N().S(`
</div>
`)
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
}
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
func WriteHelpHTML(qq422016 qtio422016.Writer, content string) {
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
StreamHelpHTML(qw422016, content)
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
}
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
func HelpHTML(content string) string {
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
WriteHelpHTML(qb422016, content)
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
return qs422016
//line views/stuff.qtpl:132
//line views/stuff.qtpl:147
}
//line views/stuff.qtpl:134
//line views/stuff.qtpl:149
func StreamHelpEmptyErrorHTML(qw422016 *qt422016.Writer) {
//line views/stuff.qtpl:134
//line views/stuff.qtpl:149
qw422016.N().S(`
<h1>This entry does not exist!</h1>
<p>Try finding a different entry that would help you.</p>
<p>If you want to write this entry by yourself, consider <a class="wikilink wikilink_external wikilink_https" href="https://github.com/bouncepaw/mycorrhiza">contributing</a> to Mycorrhiza Wiki directly.</p>
`)
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
}
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
func WriteHelpEmptyErrorHTML(qq422016 qtio422016.Writer) {
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
StreamHelpEmptyErrorHTML(qw422016)
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
}
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
func HelpEmptyErrorHTML() string {
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
WriteHelpEmptyErrorHTML(qb422016)
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
return qs422016
//line views/stuff.qtpl:138
//line views/stuff.qtpl:153
}
//line views/stuff.qtpl:140
//line views/stuff.qtpl:155
func streamhelpTopicsHTML(qw422016 *qt422016.Writer) {
//line views/stuff.qtpl:140
//line views/stuff.qtpl:155
qw422016.N().S(`
<aside class="help-topics layout-card">
<h2 class="layout-card__title">Help topics</h2>
@ -520,87 +586,87 @@ func streamhelpTopicsHTML(qw422016 *qt422016.Writer) {
</ul>
</aside>
`)
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
}
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
func writehelpTopicsHTML(qq422016 qtio422016.Writer) {
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
streamhelpTopicsHTML(qw422016)
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
}
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
func helpTopicsHTML() string {
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
writehelpTopicsHTML(qb422016)
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
return qs422016
//line views/stuff.qtpl:173
//line views/stuff.qtpl:188
}
//line views/stuff.qtpl:175
//line views/stuff.qtpl:190
func streamhelpTopicBadgeHTML(qw422016 *qt422016.Writer, lang, topic string) {
//line views/stuff.qtpl:175
//line views/stuff.qtpl:190
qw422016.N().S(`
<a class="help-topic-badge" href="/help/`)
//line views/stuff.qtpl:176
//line views/stuff.qtpl:191
qw422016.E().S(lang)
//line views/stuff.qtpl:176
//line views/stuff.qtpl:191
qw422016.N().S(`/`)
//line views/stuff.qtpl:176
//line views/stuff.qtpl:191
qw422016.E().S(topic)
//line views/stuff.qtpl:176
//line views/stuff.qtpl:191
qw422016.N().S(`">?</a>
`)
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
}
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
func writehelpTopicBadgeHTML(qq422016 qtio422016.Writer, lang, topic string) {
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
streamhelpTopicBadgeHTML(qw422016, lang, topic)
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
}
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
func helpTopicBadgeHTML(lang, topic string) string {
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
writehelpTopicBadgeHTML(qb422016, lang, topic)
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
return qs422016
//line views/stuff.qtpl:177
//line views/stuff.qtpl:192
}
//line views/stuff.qtpl:179
//line views/stuff.qtpl:194
func StreamUserListHTML(qw422016 *qt422016.Writer) {
//line views/stuff.qtpl:179
//line views/stuff.qtpl:194
qw422016.N().S(`
<div class="layout">
<main class="main-width user-list">
<h1>List of users</h1>
`)
//line views/stuff.qtpl:184
//line views/stuff.qtpl:199
var (
admins = make([]string, 0)
moderators = make([]string, 0)
@ -617,345 +683,345 @@ func StreamUserListHTML(qw422016 *qt422016.Writer) {
}
}
//line views/stuff.qtpl:199
//line views/stuff.qtpl:214
qw422016.N().S(`
<section>
<h2>Admins</h2>
<ol>`)
//line views/stuff.qtpl:202
//line views/stuff.qtpl:217
for _, name := range admins {
//line views/stuff.qtpl:202
//line views/stuff.qtpl:217
qw422016.N().S(`
<li><a href="/hypha/`)
//line views/stuff.qtpl:203
//line views/stuff.qtpl:218
qw422016.E().S(cfg.UserHypha)
//line views/stuff.qtpl:203
//line views/stuff.qtpl:218
qw422016.N().S(`/`)
//line views/stuff.qtpl:203
//line views/stuff.qtpl:218
qw422016.E().S(name)
//line views/stuff.qtpl:203
//line views/stuff.qtpl:218
qw422016.N().S(`">`)
//line views/stuff.qtpl:203
//line views/stuff.qtpl:218
qw422016.E().S(name)
//line views/stuff.qtpl:203
//line views/stuff.qtpl:218
qw422016.N().S(`</a></li>
`)
//line views/stuff.qtpl:204
//line views/stuff.qtpl:219
}
//line views/stuff.qtpl:204
//line views/stuff.qtpl:219
qw422016.N().S(`</ol>
</section>
<section>
<h2>Moderators</h2>
<ol>`)
//line views/stuff.qtpl:208
//line views/stuff.qtpl:223
for _, name := range moderators {
//line views/stuff.qtpl:208
//line views/stuff.qtpl:223
qw422016.N().S(`
<li><a href="/hypha/`)
//line views/stuff.qtpl:209
//line views/stuff.qtpl:224
qw422016.E().S(cfg.UserHypha)
//line views/stuff.qtpl:209
//line views/stuff.qtpl:224
qw422016.N().S(`/`)
//line views/stuff.qtpl:209
//line views/stuff.qtpl:224
qw422016.E().S(name)
//line views/stuff.qtpl:209
//line views/stuff.qtpl:224
qw422016.N().S(`">`)
//line views/stuff.qtpl:209
//line views/stuff.qtpl:224
qw422016.E().S(name)
//line views/stuff.qtpl:209
//line views/stuff.qtpl:224
qw422016.N().S(`</a></li>
`)
//line views/stuff.qtpl:210
//line views/stuff.qtpl:225
}
//line views/stuff.qtpl:210
//line views/stuff.qtpl:225
qw422016.N().S(`</ol>
</section>
<section>
<h2>Editors</h2>
<ol>`)
//line views/stuff.qtpl:214
//line views/stuff.qtpl:229
for _, name := range editors {
//line views/stuff.qtpl:214
//line views/stuff.qtpl:229
qw422016.N().S(`
<li><a href="/hypha/`)
//line views/stuff.qtpl:215
//line views/stuff.qtpl:230
qw422016.E().S(cfg.UserHypha)
//line views/stuff.qtpl:215
//line views/stuff.qtpl:230
qw422016.N().S(`/`)
//line views/stuff.qtpl:215
//line views/stuff.qtpl:230
qw422016.E().S(name)
//line views/stuff.qtpl:215
//line views/stuff.qtpl:230
qw422016.N().S(`">`)
//line views/stuff.qtpl:215
//line views/stuff.qtpl:230
qw422016.E().S(name)
//line views/stuff.qtpl:215
//line views/stuff.qtpl:230
qw422016.N().S(`</a></li>
`)
//line views/stuff.qtpl:216
//line views/stuff.qtpl:231
}
//line views/stuff.qtpl:216
//line views/stuff.qtpl:231
qw422016.N().S(`</ol>
</section>
</main>
</div>
`)
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
}
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
func WriteUserListHTML(qq422016 qtio422016.Writer) {
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
StreamUserListHTML(qw422016)
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
}
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
func UserListHTML() string {
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
WriteUserListHTML(qb422016)
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
return qs422016
//line views/stuff.qtpl:220
//line views/stuff.qtpl:235
}
//line views/stuff.qtpl:222
//line views/stuff.qtpl:237
func StreamHyphaListHTML(qw422016 *qt422016.Writer) {
//line views/stuff.qtpl:222
//line views/stuff.qtpl:237
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<h1>List of hyphae</h1>
<p>This wiki has `)
//line views/stuff.qtpl:226
//line views/stuff.qtpl:241
qw422016.N().D(hyphae.Count())
//line views/stuff.qtpl:226
//line views/stuff.qtpl:241
qw422016.N().S(` hyphae.</p>
<ul class="hypha-list">
`)
//line views/stuff.qtpl:228
//line views/stuff.qtpl:243
for h := range hyphae.YieldExistingHyphae() {
//line views/stuff.qtpl:228
//line views/stuff.qtpl:243
qw422016.N().S(`
<li class="hypha-list__entry">
<a class="hypha-list__link" href="/hypha/`)
//line views/stuff.qtpl:230
//line views/stuff.qtpl:245
qw422016.E().S(h.Name)
//line views/stuff.qtpl:230
//line views/stuff.qtpl:245
qw422016.N().S(`">`)
//line views/stuff.qtpl:230
//line views/stuff.qtpl:245
qw422016.E().S(util.BeautifulName(h.Name))
//line views/stuff.qtpl:230
//line views/stuff.qtpl:245
qw422016.N().S(`</a>
`)
//line views/stuff.qtpl:231
//line views/stuff.qtpl:246
if h.BinaryPath != "" {
//line views/stuff.qtpl:231
//line views/stuff.qtpl:246
qw422016.N().S(`
<span class="hypha-list__amnt-type">`)
//line views/stuff.qtpl:232
//line views/stuff.qtpl:247
qw422016.E().S(filepath.Ext(h.BinaryPath)[1:])
//line views/stuff.qtpl:232
//line views/stuff.qtpl:247
qw422016.N().S(`</span>
`)
//line views/stuff.qtpl:233
//line views/stuff.qtpl:248
}
//line views/stuff.qtpl:233
//line views/stuff.qtpl:248
qw422016.N().S(`
</li>
`)
//line views/stuff.qtpl:235
//line views/stuff.qtpl:250
}
//line views/stuff.qtpl:235
//line views/stuff.qtpl:250
qw422016.N().S(`
</ul>
</main>
</div>
`)
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
}
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
func WriteHyphaListHTML(qq422016 qtio422016.Writer) {
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
StreamHyphaListHTML(qw422016)
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
}
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
func HyphaListHTML() string {
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
WriteHyphaListHTML(qb422016)
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
return qs422016
//line views/stuff.qtpl:239
//line views/stuff.qtpl:254
}
//line views/stuff.qtpl:241
//line views/stuff.qtpl:256
func StreamAboutHTML(qw422016 *qt422016.Writer) {
//line views/stuff.qtpl:241
//line views/stuff.qtpl:256
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<section>
<h1>About `)
//line views/stuff.qtpl:245
//line views/stuff.qtpl:260
qw422016.E().S(cfg.WikiName)
//line views/stuff.qtpl:245
//line views/stuff.qtpl:260
qw422016.N().S(`</h1>
<ul>
<li><b><a href="https://mycorrhiza.wiki">Mycorrhiza Wiki</a> version:</b> 1.5.0</li>
`)
//line views/stuff.qtpl:248
//line views/stuff.qtpl:263
if cfg.UseAuth {
//line views/stuff.qtpl:248
//line views/stuff.qtpl:263
qw422016.N().S(` <li><b>User count:</b> `)
//line views/stuff.qtpl:249
//line views/stuff.qtpl:264
qw422016.N().DUL(user.Count())
//line views/stuff.qtpl:249
//line views/stuff.qtpl:264
qw422016.N().S(`</li>
<li><b>Home page:</b> <a href="/">`)
//line views/stuff.qtpl:250
//line views/stuff.qtpl:265
qw422016.E().S(cfg.HomeHypha)
//line views/stuff.qtpl:250
//line views/stuff.qtpl:265
qw422016.N().S(`</a></li>
<li><b>Administrators:</b>`)
//line views/stuff.qtpl:251
//line views/stuff.qtpl:266
for i, username := range user.ListUsersWithGroup("admin") {
//line views/stuff.qtpl:252
//line views/stuff.qtpl:267
if i > 0 {
//line views/stuff.qtpl:252
//line views/stuff.qtpl:267
qw422016.N().S(`<span aria-hidden="true">, </span>
`)
//line views/stuff.qtpl:253
//line views/stuff.qtpl:268
}
//line views/stuff.qtpl:253
//line views/stuff.qtpl:268
qw422016.N().S(` <a href="/hypha/`)
//line views/stuff.qtpl:254
//line views/stuff.qtpl:269
qw422016.E().S(cfg.UserHypha)
//line views/stuff.qtpl:254
//line views/stuff.qtpl:269
qw422016.N().S(`/`)
//line views/stuff.qtpl:254
//line views/stuff.qtpl:269
qw422016.E().S(username)
//line views/stuff.qtpl:254
//line views/stuff.qtpl:269
qw422016.N().S(`">`)
//line views/stuff.qtpl:254
//line views/stuff.qtpl:269
qw422016.E().S(username)
//line views/stuff.qtpl:254
//line views/stuff.qtpl:269
qw422016.N().S(`</a>`)
//line views/stuff.qtpl:254
//line views/stuff.qtpl:269
}
//line views/stuff.qtpl:254
//line views/stuff.qtpl:269
qw422016.N().S(`</li>
`)
//line views/stuff.qtpl:255
//line views/stuff.qtpl:270
} else {
//line views/stuff.qtpl:255
//line views/stuff.qtpl:270
qw422016.N().S(` <li>This wiki does not use authorization</li>
`)
//line views/stuff.qtpl:257
//line views/stuff.qtpl:272
}
//line views/stuff.qtpl:257
//line views/stuff.qtpl:272
qw422016.N().S(` </ul>
<p>See <a href="/list">/list</a> for information about hyphae on this wiki.</p>
</section>
</main>
</div>
`)
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
}
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
func WriteAboutHTML(qq422016 qtio422016.Writer) {
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
StreamAboutHTML(qw422016)
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
}
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
func AboutHTML() string {
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
WriteAboutHTML(qb422016)
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
return qs422016
//line views/stuff.qtpl:263
//line views/stuff.qtpl:278
}
//line views/stuff.qtpl:265
//line views/stuff.qtpl:280
func StreamCommonScripts(qw422016 *qt422016.Writer) {
//line views/stuff.qtpl:265
//line views/stuff.qtpl:280
qw422016.N().S(`
`)
//line views/stuff.qtpl:266
//line views/stuff.qtpl:281
for _, scriptPath := range cfg.CommonScripts {
//line views/stuff.qtpl:266
//line views/stuff.qtpl:281
qw422016.N().S(`
<script src="`)
//line views/stuff.qtpl:267
//line views/stuff.qtpl:282
qw422016.E().S(scriptPath)
//line views/stuff.qtpl:267
//line views/stuff.qtpl:282
qw422016.N().S(`"></script>
`)
//line views/stuff.qtpl:268
//line views/stuff.qtpl:283
}
//line views/stuff.qtpl:268
//line views/stuff.qtpl:283
qw422016.N().S(`
`)
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
}
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
func WriteCommonScripts(qq422016 qtio422016.Writer) {
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
StreamCommonScripts(qw422016)
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
}
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
func CommonScripts() string {
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
WriteCommonScripts(qb422016)
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
return qs422016
//line views/stuff.qtpl:269
//line views/stuff.qtpl:284
}

37
web/backlinks.go Normal file
View File

@ -0,0 +1,37 @@
package web
import (
"fmt"
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
)
func initBacklinks(r *mux.Router) {
r.PathPrefix("/backlinks/").HandlerFunc(handlerBacklinks)
r.PathPrefix("/backlinks-json/").HandlerFunc(handlerBacklinksJSON)
}
// handlerBacklinks lists all backlinks to a hypha.
func handlerBacklinks(w http.ResponseWriter, rq *http.Request) {
hyphaName := util.HyphaNameFromRq(rq, "backlinks")
util.HTTP200Page(w, views.BaseHTML(
fmt.Sprintf("Backlinks to %s", util.BeautifulName(hyphaName)),
views.BacklinksHTML(hyphaName, hyphae.YieldHyphaBacklinks),
user.FromRequest(rq)))
}
func handlerBacklinksJSON(w http.ResponseWriter, rq *http.Request) {
hyphaName := util.HyphaNameFromRq(rq, "backlinks")
w.WriteHeader(http.StatusOK)
_, _ = io.WriteString(
w,
views.TitleSearchJSON(hyphaName, hyphae.YieldHyphaBacklinks),
)
}

View File

@ -103,6 +103,7 @@ func Handler() http.Handler {
initHistory(wikiRouter)
initStuff(wikiRouter)
initSearch(wikiRouter)
initBacklinks(wikiRouter)
// Admin routes.
if cfg.UseAuth {