1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-10-30 11:46:16 +00:00

Merge pull request #46 from bouncepaw/0.14

1.0
This commit is contained in:
Timur Ismagilov 2021-03-14 20:35:25 +05:00 committed by GitHub
commit 24d79dac8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 826 additions and 849 deletions

4
.gitignore vendored
View File

@ -1,2 +1,6 @@
mycorrhiza
hyphae/*.gog
# go editors and IDEA folders
.idea/
.vscode/

View File

@ -1,11 +1,8 @@
run: build
./mycorrhiza metarrhiza
auth_run: build
./mycorrhiza -auth-method fixed metarrhiza
gemini_run: build
./mycorrhiza -gemini-cert-path "." metarrhiza
config_run: build
./mycorrhiza -config-path "assets/config.ini" metarrhiza
build:
go generate

View File

@ -1,10 +1,10 @@
# 🍄 MycorrhizaWiki 0.13
# 🍄 MycorrhizaWiki
A wiki engine.
[Main wiki](https://mycorrhiza.lesarbr.es)
## Building
Also see [detailed instructions](https://mycorrhiza.lesarbr.es/page/deploy) on wiki.
Also see [detailed instructions](https://mycorrhiza.lesarbr.es/hypha/deploy) on wiki.
```sh
git clone --recurse-submodules https://github.com/bouncepaw/mycorrhiza
cd mycorrhiza
@ -21,26 +21,10 @@ mycorrhiza [OPTIONS...] WIKI_PATH
WIKI_PATH must be a path to git repository which you want to be a wiki.
Options:
-auth-method string
What auth method to use. Variants: "none", "fixed" (default "none")
-fixed-credentials-path string
Used when -auth-method=fixed. Path to file with user credentials. (default "mycocredentials.json")
-gemini-cert-path string
Directory where you store Gemini certificates. Leave empty if you don't want to use Gemini.
-header-links-hypha string
Optional hypha that overrides the header links
-home string
The home page (default "home")
-icon string
What to show in the navititle in the beginning, before the colon (default "🍄")
-name string
What is the name of your wiki (default "wiki")
-port string
Port to serve the wiki at (default "1737")
-url string
URL at which your wiki can be found. Used to generate feeds (default "http://0.0.0.0:$port")
-user-hypha string
Hypha which is a superhypha of all user pages (default "u")
-config-path string
Path to a configuration file. Leave empty if you don't want to use it.
-print-example-config
If true, print an example configuration file contents and exit. You can save the output to a file and base your own configuration on it.
```
## Features

View File

@ -1,3 +1,11 @@
{%- func HelpMessage() -%}
Usage of %s:
{%- endfunc -%}
{%- func ExampleConfig() -%}
{% cat "config.ini" %}
{%- endfunc -%}
{% func DefaultCSS() %}
{% cat "default.css" %}
{% endfunc %}

View File

@ -18,11 +18,97 @@ var (
)
//line assets/assets.qtpl:1
func StreamDefaultCSS(qw422016 *qt422016.Writer) {
func StreamHelpMessage(qw422016 *qt422016.Writer) {
//line assets/assets.qtpl:1
qw422016.N().S(`Usage of %s:
`)
//line assets/assets.qtpl:3
}
//line assets/assets.qtpl:3
func WriteHelpMessage(qq422016 qtio422016.Writer) {
//line assets/assets.qtpl:3
qw422016 := qt422016.AcquireWriter(qq422016)
//line assets/assets.qtpl:3
StreamHelpMessage(qw422016)
//line assets/assets.qtpl:3
qt422016.ReleaseWriter(qw422016)
//line assets/assets.qtpl:3
}
//line assets/assets.qtpl:3
func HelpMessage() string {
//line assets/assets.qtpl:3
qb422016 := qt422016.AcquireByteBuffer()
//line assets/assets.qtpl:3
WriteHelpMessage(qb422016)
//line assets/assets.qtpl:3
qs422016 := string(qb422016.B)
//line assets/assets.qtpl:3
qt422016.ReleaseByteBuffer(qb422016)
//line assets/assets.qtpl:3
return qs422016
//line assets/assets.qtpl:3
}
//line assets/assets.qtpl:5
func StreamExampleConfig(qw422016 *qt422016.Writer) {
//line assets/assets.qtpl:6
qw422016.N().S(`WikiName = My wiki
NaviTitleIcon = 🐑
[Hyphae]
HomeHypha = home
UserHypha = u
HeaderLinksHypha = header-links
[Network]
HTTPPort = 8080
URL = https://wiki
GeminiCertificatePath = /home/wiki/gemcerts
[Authorization]
UseFixedAuth = true
FixedAuthCredentialsPath = /home/wiki/mycocredentials.json
`)
//line assets/assets.qtpl:6
qw422016.N().S(`
`)
//line assets/assets.qtpl:2
//line assets/assets.qtpl:7
}
//line assets/assets.qtpl:7
func WriteExampleConfig(qq422016 qtio422016.Writer) {
//line assets/assets.qtpl:7
qw422016 := qt422016.AcquireWriter(qq422016)
//line assets/assets.qtpl:7
StreamExampleConfig(qw422016)
//line assets/assets.qtpl:7
qt422016.ReleaseWriter(qw422016)
//line assets/assets.qtpl:7
}
//line assets/assets.qtpl:7
func ExampleConfig() string {
//line assets/assets.qtpl:7
qb422016 := qt422016.AcquireByteBuffer()
//line assets/assets.qtpl:7
WriteExampleConfig(qb422016)
//line assets/assets.qtpl:7
qs422016 := string(qb422016.B)
//line assets/assets.qtpl:7
qt422016.ReleaseByteBuffer(qb422016)
//line assets/assets.qtpl:7
return qs422016
//line assets/assets.qtpl:7
}
//line assets/assets.qtpl:9
func StreamDefaultCSS(qw422016 *qt422016.Writer) {
//line assets/assets.qtpl:9
qw422016.N().S(`
`)
//line assets/assets.qtpl:10
qw422016.N().S(`.amnt-grid { display: grid; grid-template-columns: 1fr 1fr; }
.upload-binary__input { display: block; margin: .25rem 0; }
@ -61,7 +147,7 @@ header { width: 100%; margin-bottom: 1rem; }
@media screen and (max-width: 800px) {
.amnt-grid { grid-template-columns: 1fr; }
.layout { grid-template-column: auto; grid-template-row: auto auto auto; }
.layout { grid-template-columns: auto; grid-template-rows: auto auto auto; }
.main-width { width: 100%; }
main { padding: 1rem; margin: 0; }
}
@ -178,7 +264,7 @@ figcaption { padding-bottom: .5rem; }
.rc-entry__links, .rc-entry__msg { grid-column: 1 / span 2; }
.rc-entry__author { font-style: italic; }
.prevnext__el { display: block-inline; min-width: 40%; padding: .5rem; margin-bottom: .25rem; text-decoration: none; border-radius: .25rem; }
.prevnext__el { display: inline-block; min-width: 40%; padding: .5rem; margin-bottom: .25rem; text-decoration: none; border-radius: .25rem; }
.prevnext__prev { float: left; }
.prevnext__next { float: right; text-align: right; }
@ -295,171 +381,169 @@ mark { background: rgba(130, 80, 30, 5); color: inherit; }
.hypha-tabs { background-color: #232323; }
}
}
.backlinks { display: none; }
`)
//line assets/assets.qtpl:2
//line assets/assets.qtpl:10
qw422016.N().S(`
`)
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
}
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
func WriteDefaultCSS(qq422016 qtio422016.Writer) {
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
qw422016 := qt422016.AcquireWriter(qq422016)
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
StreamDefaultCSS(qw422016)
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
qt422016.ReleaseWriter(qw422016)
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
}
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
func DefaultCSS() string {
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
qb422016 := qt422016.AcquireByteBuffer()
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
WriteDefaultCSS(qb422016)
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
qs422016 := string(qb422016.B)
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
qt422016.ReleaseByteBuffer(qb422016)
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
return qs422016
//line assets/assets.qtpl:3
//line assets/assets.qtpl:11
}
// Next three are from https://remixicon.com/
//line assets/assets.qtpl:6
//line assets/assets.qtpl:14
func StreamIconHTTP(qw422016 *qt422016.Writer) {
//line assets/assets.qtpl:6
//line assets/assets.qtpl:14
qw422016.N().S(`
`)
//line assets/assets.qtpl:7
//line assets/assets.qtpl:15
qw422016.N().S(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="#999" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"/></svg>
`)
//line assets/assets.qtpl:7
//line assets/assets.qtpl:15
qw422016.N().S(`
`)
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
}
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
func WriteIconHTTP(qq422016 qtio422016.Writer) {
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
qw422016 := qt422016.AcquireWriter(qq422016)
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
StreamIconHTTP(qw422016)
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
qt422016.ReleaseWriter(qw422016)
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
}
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
func IconHTTP() string {
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
qb422016 := qt422016.AcquireByteBuffer()
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
WriteIconHTTP(qb422016)
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
qs422016 := string(qb422016.B)
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
qt422016.ReleaseByteBuffer(qb422016)
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
return qs422016
//line assets/assets.qtpl:8
//line assets/assets.qtpl:16
}
//line assets/assets.qtpl:10
//line assets/assets.qtpl:18
func StreamIconGemini(qw422016 *qt422016.Writer) {
//line assets/assets.qtpl:10
//line assets/assets.qtpl:18
qw422016.N().S(`
`)
//line assets/assets.qtpl:11
//line assets/assets.qtpl:19
qw422016.N().S(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="#999" d="M15.502 20A6.523 6.523 0 0 1 12 23.502 6.523 6.523 0 0 1 8.498 20h2.26c.326.489.747.912 1.242 1.243.495-.33.916-.754 1.243-1.243h2.259zM18 14.805l2 2.268V19H4v-1.927l2-2.268V9c0-3.483 2.504-6.447 6-7.545C15.496 2.553 18 5.517 18 9v5.805zM17.27 17L16 15.56V9c0-2.318-1.57-4.43-4-5.42C9.57 4.57 8 6.681 8 9v6.56L6.73 17h10.54zM12 11a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></svg>
`)
//line assets/assets.qtpl:11
qw422016.N().S(`
`)
//line assets/assets.qtpl:12
}
//line assets/assets.qtpl:12
func WriteIconGemini(qq422016 qtio422016.Writer) {
//line assets/assets.qtpl:12
qw422016 := qt422016.AcquireWriter(qq422016)
//line assets/assets.qtpl:12
StreamIconGemini(qw422016)
//line assets/assets.qtpl:12
qt422016.ReleaseWriter(qw422016)
//line assets/assets.qtpl:12
}
//line assets/assets.qtpl:12
func IconGemini() string {
//line assets/assets.qtpl:12
qb422016 := qt422016.AcquireByteBuffer()
//line assets/assets.qtpl:12
WriteIconGemini(qb422016)
//line assets/assets.qtpl:12
qs422016 := string(qb422016.B)
//line assets/assets.qtpl:12
qt422016.ReleaseByteBuffer(qb422016)
//line assets/assets.qtpl:12
return qs422016
//line assets/assets.qtpl:12
}
//line assets/assets.qtpl:14
func StreamIconMailto(qw422016 *qt422016.Writer) {
//line assets/assets.qtpl:14
qw422016.N().S(`
`)
//line assets/assets.qtpl:15
qw422016.N().S(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="#999" d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm17 4.238l-7.928 7.1L4 7.216V19h16V7.238zM4.511 5l7.55 6.662L19.502 5H4.511z"/></svg>
`)
//line assets/assets.qtpl:15
qw422016.N().S(`
`)
//line assets/assets.qtpl:16
}
//line assets/assets.qtpl:16
func WriteIconMailto(qq422016 qtio422016.Writer) {
//line assets/assets.qtpl:16
qw422016 := qt422016.AcquireWriter(qq422016)
//line assets/assets.qtpl:16
StreamIconMailto(qw422016)
//line assets/assets.qtpl:16
qt422016.ReleaseWriter(qw422016)
//line assets/assets.qtpl:16
}
//line assets/assets.qtpl:16
func IconMailto() string {
//line assets/assets.qtpl:16
qb422016 := qt422016.AcquireByteBuffer()
//line assets/assets.qtpl:16
WriteIconMailto(qb422016)
//line assets/assets.qtpl:16
qs422016 := string(qb422016.B)
//line assets/assets.qtpl:16
qt422016.ReleaseByteBuffer(qb422016)
//line assets/assets.qtpl:16
return qs422016
//line assets/assets.qtpl:16
}
// This is a modified version of https://www.svgrepo.com/svg/232085/rat
//line assets/assets.qtpl:19
func StreamIconGopher(qw422016 *qt422016.Writer) {
//line assets/assets.qtpl:19
qw422016.N().S(`
`)
//line assets/assets.qtpl:20
}
//line assets/assets.qtpl:20
func WriteIconGemini(qq422016 qtio422016.Writer) {
//line assets/assets.qtpl:20
qw422016 := qt422016.AcquireWriter(qq422016)
//line assets/assets.qtpl:20
StreamIconGemini(qw422016)
//line assets/assets.qtpl:20
qt422016.ReleaseWriter(qw422016)
//line assets/assets.qtpl:20
}
//line assets/assets.qtpl:20
func IconGemini() string {
//line assets/assets.qtpl:20
qb422016 := qt422016.AcquireByteBuffer()
//line assets/assets.qtpl:20
WriteIconGemini(qb422016)
//line assets/assets.qtpl:20
qs422016 := string(qb422016.B)
//line assets/assets.qtpl:20
qt422016.ReleaseByteBuffer(qb422016)
//line assets/assets.qtpl:20
return qs422016
//line assets/assets.qtpl:20
}
//line assets/assets.qtpl:22
func StreamIconMailto(qw422016 *qt422016.Writer) {
//line assets/assets.qtpl:22
qw422016.N().S(`
`)
//line assets/assets.qtpl:23
qw422016.N().S(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="#999" d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm17 4.238l-7.928 7.1L4 7.216V19h16V7.238zM4.511 5l7.55 6.662L19.502 5H4.511z"/></svg>
`)
//line assets/assets.qtpl:23
qw422016.N().S(`
`)
//line assets/assets.qtpl:24
}
//line assets/assets.qtpl:24
func WriteIconMailto(qq422016 qtio422016.Writer) {
//line assets/assets.qtpl:24
qw422016 := qt422016.AcquireWriter(qq422016)
//line assets/assets.qtpl:24
StreamIconMailto(qw422016)
//line assets/assets.qtpl:24
qt422016.ReleaseWriter(qw422016)
//line assets/assets.qtpl:24
}
//line assets/assets.qtpl:24
func IconMailto() string {
//line assets/assets.qtpl:24
qb422016 := qt422016.AcquireByteBuffer()
//line assets/assets.qtpl:24
WriteIconMailto(qb422016)
//line assets/assets.qtpl:24
qs422016 := string(qb422016.B)
//line assets/assets.qtpl:24
qt422016.ReleaseByteBuffer(qb422016)
//line assets/assets.qtpl:24
return qs422016
//line assets/assets.qtpl:24
}
// This is a modified version of https://www.svgrepo.com/svg/232085/rat
//line assets/assets.qtpl:27
func StreamIconGopher(qw422016 *qt422016.Writer) {
//line assets/assets.qtpl:27
qw422016.N().S(`
`)
//line assets/assets.qtpl:28
qw422016.N().S(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16">
<path fill="#999" d="M447.238,204.944v-70.459c0-8.836-7.164-16-16-16c-34.051,0-64.414,21.118-75.079,55.286
C226.094,41.594,0,133.882,0,319.435c0,0.071,0.01,0.14,0.011,0.21c0.116,44.591,36.423,80.833,81.04,80.833h171.203
@ -472,34 +556,34 @@ c55.425-8.382,107.014,29.269,115.759,84.394H295.484z"/>
<circle fill="#999" cx="415.238" cy="260.05" r="21.166"/>
</svg>
`)
//line assets/assets.qtpl:20
//line assets/assets.qtpl:28
qw422016.N().S(`
`)
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
}
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
func WriteIconGopher(qq422016 qtio422016.Writer) {
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
qw422016 := qt422016.AcquireWriter(qq422016)
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
StreamIconGopher(qw422016)
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
qt422016.ReleaseWriter(qw422016)
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
}
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
func IconGopher() string {
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
qb422016 := qt422016.AcquireByteBuffer()
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
WriteIconGopher(qb422016)
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
qs422016 := string(qb422016.B)
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
qt422016.ReleaseByteBuffer(qb422016)
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
return qs422016
//line assets/assets.qtpl:21
//line assets/assets.qtpl:29
}

16
assets/config.ini Normal file
View File

@ -0,0 +1,16 @@
WikiName = My wiki
NaviTitleIcon = 🐑
[Hyphae]
HomeHypha = home
UserHypha = u
HeaderLinksHypha = header-links
[Network]
HTTPPort = 8080
URL = https://wiki
GeminiCertificatePath = /home/wiki/gemcerts
[Authorization]
UseFixedAuth = true
FixedAuthCredentialsPath = /home/wiki/mycocredentials.json

View File

@ -36,7 +36,7 @@ header { width: 100%; margin-bottom: 1rem; }
@media screen and (max-width: 800px) {
.amnt-grid { grid-template-columns: 1fr; }
.layout { grid-template-column: auto; grid-template-row: auto auto auto; }
.layout { grid-template-columns: auto; grid-template-rows: auto auto auto; }
.main-width { width: 100%; }
main { padding: 1rem; margin: 0; }
}
@ -153,7 +153,7 @@ figcaption { padding-bottom: .5rem; }
.rc-entry__links, .rc-entry__msg { grid-column: 1 / span 2; }
.rc-entry__author { font-style: italic; }
.prevnext__el { display: block-inline; min-width: 40%; padding: .5rem; margin-bottom: .25rem; text-decoration: none; border-radius: .25rem; }
.prevnext__el { display: inline-block; min-width: 40%; padding: .5rem; margin-bottom: .25rem; text-decoration: none; border-radius: .25rem; }
.prevnext__prev { float: left; }
.prevnext__next { float: right; text-align: right; }
@ -270,5 +270,3 @@ mark { background: rgba(130, 80, 30, 5); color: inherit; }
.hypha-tabs { background-color: #232323; }
}
}
.backlinks { display: none; }

54
flag.go
View File

@ -2,24 +2,39 @@ package main
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"github.com/bouncepaw/mycorrhiza/assets"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
)
var printExampleConfig bool
func init() {
flag.StringVar(&util.URL, "url", "http://0.0.0.0:$port", "URL at which your wiki can be found. Used to generate feeds and social media previews")
flag.StringVar(&util.ServerPort, "port", "1737", "Port to serve the wiki at using HTTP")
flag.StringVar(&util.HomePage, "home", "home", "The home page name")
flag.StringVar(&util.SiteNavIcon, "icon", "🍄", "What to show in the navititle in the beginning, before the colon")
flag.StringVar(&util.SiteName, "name", "wiki", "What is the name of your wiki")
flag.StringVar(&util.UserHypha, "user-hypha", "u", "Hypha which is a superhypha of all user pages")
flag.StringVar(&util.AuthMethod, "auth-method", "none", "What auth method to use. Variants: \"none\", \"fixed\"")
flag.StringVar(&util.FixedCredentialsPath, "fixed-credentials-path", "mycocredentials.json", "Used when -auth-method=fixed. Path to file with user credentials.")
flag.StringVar(&util.HeaderLinksHypha, "header-links-hypha", "", "Optional hypha that overrides the header links")
flag.StringVar(&util.GeminiCertPath, "gemini-cert-path", "", "Directory where you store Gemini certificates. Leave empty if you don't want to use Gemini.")
// flag.StringVar(&util.URL, "url", "http://0.0.0.0:$port", "URL at which your wiki can be found. Used to generate feeds and social media previews")
// flag.StringVar(&util.ServerPort, "port", "1737", "Port to serve the wiki at using HTTP")
// flag.StringVar(&util.HomePage, "home", "home", "The home page name")
// flag.StringVar(&util.SiteNavIcon, "icon", "🍄", "What to show in the navititle in the beginning, before the colon")
// flag.StringVar(&util.SiteName, "name", "wiki", "What is the name of your wiki")
// flag.StringVar(&util.UserHypha, "user-hypha", "u", "Hypha which is a superhypha of all user pages")
// flag.StringVar(&util.AuthMethod, "auth-method", "none", "What auth method to use. Variants: \"none\", \"fixed\"")
// flag.StringVar(&util.FixedCredentialsPath, "fixed-credentials-path", "mycocredentials.json", "Used when -auth-method=fixed. Path to file with user credentials.")
// flag.StringVar(&util.HeaderLinksHypha, "header-links-hypha", "", "Optional hypha that overrides the header links")
// flag.StringVar(&util.GeminiCertPath, "gemini-cert-path", "", "Directory where you store Gemini certificates. Leave empty if you don't want to use Gemini.")
flag.StringVar(&util.ConfigFilePath, "config-path", "", "Path to a configuration file. Leave empty if you don't want to use it.")
flag.BoolVar(&printExampleConfig, "print-example-config", false, "If true, print an example configuration file contents and exit. You can save the output to a file and base your own configuration on it.")
flag.Usage = func() {
fmt.Fprintf(
flag.CommandLine.Output(),
assets.HelpMessage(),
os.Args[0],
)
flag.PrintDefaults()
}
}
// Do the things related to cli args and die maybe
@ -27,10 +42,18 @@ func parseCliArgs() {
flag.Parse()
args := flag.Args()
if printExampleConfig {
fmt.Printf(assets.ExampleConfig())
os.Exit(0)
}
if len(args) == 0 {
log.Fatal("Error: pass a wiki directory")
}
// It is ok if the path is ""
util.ReadConfigFile(util.ConfigFilePath)
var err error
WikiDir, err = filepath.Abs(args[0])
util.WikiDir = WikiDir
@ -38,20 +61,15 @@ func parseCliArgs() {
log.Fatal(err)
}
if util.URL == "http://0.0.0.0:$port" {
if util.URL == "" {
util.URL = "http://0.0.0.0:" + util.ServerPort
}
util.HomePage = util.CanonicalName(util.HomePage)
util.UserHypha = util.CanonicalName(util.UserHypha)
util.HeaderLinksHypha = util.CanonicalName(util.HeaderLinksHypha)
switch util.AuthMethod {
case "none":
case "fixed":
user.AuthUsed = true
user.AuthUsed = util.UseFixedAuth
if user.AuthUsed && util.FixedCredentialsPath != "" {
user.ReadUsersFromFilesystem()
default:
log.Fatal("Error: unknown auth method:", util.AuthMethod)
}
}

2
go.mod
View File

@ -5,8 +5,8 @@ go 1.14
require (
git.sr.ht/~adnano/go-gemini v0.1.13
github.com/adrg/xdg v0.2.2
github.com/go-ini/ini v1.62.0 // indirect
github.com/gorilla/feeds v1.1.1
github.com/kr/pretty v0.2.1 // indirect
github.com/valyala/quicktemplate v1.6.3
tildegit.org/solderpunk/gemcert v0.0.0-20200801165357-fc14deb27512 // indirect
)

4
go.sum
View File

@ -5,6 +5,8 @@ github.com/adrg/xdg v0.2.2/go.mod h1:7I2hH/IT30IsupOpKZ5ue7/qNi3CoKzD6tL3HwpaRMQ
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU=
github.com/go-ini/ini v1.62.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY=
github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA=
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
@ -35,5 +37,3 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
tildegit.org/solderpunk/gemcert v0.0.0-20200801165357-fc14deb27512 h1:reGEt1vmGompn/6FitHdBatILTsK9CYnQOCw3weoW/s=
tildegit.org/solderpunk/gemcert v0.0.0-20200801165357-fc14deb27512/go.mod h1:gqBK7AJ5wPR1bpFOuPmlQObYxwXrFdZmNb2vdzquqoA=

View File

@ -13,11 +13,22 @@ import (
"github.com/bouncepaw/mycorrhiza/util"
)
// Path to git executable. Set at init()
var gitpath string
var renameMsgPattern = regexp.MustCompile(`^Rename (.*) to .*`)
// Start initializes git credentials.
// Start finds git and initializes git credentials.
func Start(wikiDir string) {
_, err := gitsh("config", "user.name", "wikimind")
path, err := exec.LookPath("git")
if err != nil {
log.Fatal("Cound not find the git executable. Check your $PATH.")
} else {
log.Println("Git path is", path)
}
gitpath = path
_, err = gitsh("config", "user.name", "wikimind")
if err != nil {
log.Fatal(err)
}
@ -110,20 +121,6 @@ func (rev *Revision) bestLink() string {
}
}
// Path to git executable. Set at init()
var gitpath string
func init() {
path, err := exec.LookPath("git")
if err != nil {
log.Fatal("Cound not find the git executable. Check your $PATH.")
} else {
log.Println("Git path is", path)
}
gitpath = path
}
// I pronounce it as [gɪt͡ʃ].
// gitsh is async-safe, therefore all other git-related functions in this module are too.
func gitsh(args ...string) (out bytes.Buffer, err error) {

View File

@ -143,7 +143,7 @@ func (rev *Revision) asHistoryEntry(hyphaName string) (html string) {
<li class="history__entry">
<a class="history-entry" href="/rev/%[3]s/%[1]s">
<time class="history-entry__time">%[2]s</time>
<span class="history-entry__hash">%[3]s</span>
<span class="history-entry__hash"><a href="/primitive-diff/%[3]s/%[1]s">%[3]s</a></span>
<span class="history-entry__msg">%[4]s</span>
</a>%[5]s
</li>
@ -175,3 +175,8 @@ func FileAtRevision(filepath, hash string) (string, error) {
out, err := gitsh("show", hash+":"+strings.TrimPrefix(filepath, util.WikiDir+"/"))
return out.String(), err
}
func PrimitiveDiffAtRevision(filepath, hash string) (string, error) {
out, err := gitsh("diff", "--unified=1", "--no-color", hash+"~", hash, "--", filepath)
return out.String(), err
}

View File

@ -5,6 +5,7 @@ import (
"net/http"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
)
@ -13,6 +14,7 @@ func initAdmin() {
if user.AuthUsed {
http.HandleFunc("/admin", handlerAdmin)
http.HandleFunc("/admin/shutdown", handlerAdminShutdown)
http.HandleFunc("/admin/reindex-users", handlerAdminReindexUsers)
}
}
@ -31,3 +33,11 @@ func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
log.Fatal("An admin commanded the wiki to shutdown")
}
}
func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
log.Println(rq.URL)
if user.CanProceed(rq, "admin") && rq.Method == "POST" {
user.ReadUsersFromFilesystem()
http.Redirect(w, rq, "/hypha/"+util.UserHypha, http.StatusSeeOther)
}
}

View File

@ -24,6 +24,7 @@ func init() {
http.HandleFunc("/text/", handlerText)
http.HandleFunc("/binary/", handlerBinary)
http.HandleFunc("/rev/", handlerRevision)
http.HandleFunc("/primitive-diff/", handlerPrimitiveDiff)
http.HandleFunc("/attachment/", handlerAttachment)
}
@ -41,6 +42,23 @@ func handlerAttachment(w http.ResponseWriter, rq *http.Request) {
u))
}
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
log.Println(rq.URL)
var (
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/primitive-diff/")
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
revHash = shorterUrl[:firstSlashIndex]
hyphaName = util.CanonicalName(shorterUrl[firstSlashIndex+1:])
h = hyphae.ByName(hyphaName)
u = user.FromRequest(rq)
)
util.HTTP200Page(w,
views.BaseHTML(
fmt.Sprintf("Diff of %s at %s", hyphaName, revHash),
views.PrimitiveDiffHTML(rq, h, u, revHash),
u))
}
// handlerRevision displays a specific revision of text part a page
func handlerRevision(w http.ResponseWriter, rq *http.Request) {
log.Println(rq.URL)
@ -112,10 +130,19 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
contents = views.AttachmentHTML(h) + contents
}
}
util.HTTP200Page(w,
views.BaseHTML(
util.BeautifulName(hyphaName),
views.HyphaHTML(rq, h, contents),
u,
openGraph))
if contents == "" {
util.HTTP404Page(w,
views.BaseHTML(
util.BeautifulName(hyphaName),
views.HyphaHTML(rq, h, contents),
u,
openGraph))
} else {
util.HTTP200Page(w,
views.BaseHTML(
util.BeautifulName(hyphaName),
views.HyphaHTML(rq, h, contents),
u,
openGraph))
}
}

View File

@ -11,8 +11,6 @@ import (
// Index finds all hypha files in the full `path` and saves them to the hypha storage.
func Index(path string) {
byNamesMutex.Lock()
defer byNamesMutex.Unlock()
byNames = make(map[string]*Hypha)
ch := make(chan *Hypha, 5)
@ -23,11 +21,10 @@ func Index(path string) {
for h := range ch {
// At this time it is safe to ignore the mutex, because there is only one worker.
if oldHypha, ok := byNames[h.Name]; ok {
oldHypha.MergeIn(h)
if oh := ByName(h.Name); oh.Exists {
oh.MergeIn(h)
} else {
byNames[h.Name] = h
IncrementCount()
h.Insert()
}
}
}
@ -40,7 +37,7 @@ func indexHelper(path string, nestLevel uint, ch chan *Hypha) {
}
for _, node := range nodes {
// If this hypha looks like it can be a hypha path, go deeper. Do not touch the .git and static folders for they have an admnistrative importance!
// If this hypha looks like it can be a hypha path, go deeper. Do not touch the .git and static folders for they have an administrative importance!
if node.IsDir() &&
util.IsCanonicalName(node.Name()) &&
node.Name() != ".git" &&

View File

@ -13,12 +13,10 @@ var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`)
type Hypha struct {
sync.RWMutex
Name string
Name string // Canonical name
Exists bool
TextPath string
BinaryPath string
OutLinks []*Hypha
BackLinks []*Hypha
TextPath string // == "" => no text part
BinaryPath string // == "" => no attachment
}
var byNames = make(map[string]*Hypha)
@ -31,56 +29,48 @@ func EmptyHypha(hyphaName string) *Hypha {
Exists: false,
TextPath: "",
BinaryPath: "",
OutLinks: make([]*Hypha, 0),
BackLinks: make([]*Hypha, 0),
}
}
// ByName returns a hypha by name. If h.Exists, the returned hypha pointer is known to be part of the hypha index (byNames map).
// ByName returns a hypha by name. It may have been recorded to the storage.
func ByName(hyphaName string) (h *Hypha) {
h, exists := byNames[hyphaName]
if exists {
h, recorded := byNames[hyphaName]
if recorded {
return h
}
return EmptyHypha(hyphaName)
}
// Insert inserts the hypha into the storage. It overwrites the previous record, if there was any, and returns false. If the was no previous record, return true.
func (h *Hypha) Insert() (justCreated bool) {
hp := ByName(h.Name)
func storeHypha(h *Hypha) {
byNamesMutex.Lock()
defer byNamesMutex.Unlock()
if hp.Exists {
hp = h
byNames[h.Name] = h
byNamesMutex.Unlock()
h.Lock()
h.Exists = true
h.Unlock()
}
// Insert inserts the hypha into the storage. A previous record is used if possible. Count incrementation is done if needed.
func (h *Hypha) Insert() (justRecorded bool) {
hp, recorded := byNames[h.Name]
if recorded {
hp.MergeIn(h)
} else {
h.Exists = true
byNames[h.Name] = h
storeHypha(h)
IncrementCount()
}
return !hp.Exists
return !recorded
}
func (h *Hypha) InsertIfNew() (justCreated bool) {
func (h *Hypha) InsertIfNew() (justRecorded bool) {
if !h.Exists {
return h.Insert()
}
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()
@ -88,10 +78,6 @@ func (h *Hypha) Delete() {
DecrementCount()
byNamesMutex.Unlock()
h.Unlock()
for _, outlinkHypha := range h.OutLinks {
outlinkHypha.DropBackLink(h)
}
}
func (h *Hypha) RenameTo(newName string) {
@ -106,6 +92,10 @@ func (h *Hypha) RenameTo(newName string) {
// MergeIn merges in content file paths from a different hypha object. Prints warnings sometimes.
func (h *Hypha) MergeIn(oh *Hypha) {
if h == oh {
return
}
h.Lock()
if h.TextPath == "" && oh.TextPath != "" {
h.TextPath = oh.TextPath
}
@ -115,61 +105,5 @@ func (h *Hypha) MergeIn(oh *Hypha) {
}
h.BinaryPath = oh.BinaryPath
}
}
// ## Link related stuff
// Notes in pseudocode and whatnot:
// * (Reader h) does not mutate h => safe
// * (Rename h) reuses the same hypha object => safe
// * (Unattach h) and (Attach h) do not change (Backlinks h) => safe
// * (Delete h) does not change (Backlinks h), but changes (Outlinks h), removing h from them => make it safe
// * (Unattach h) and (Attach h) => h may start or stop existing => may change (Outlinks h) => make it safe
// * (Edit h) => h may start existing => may change (Backlinks h) => make it safe
// * (Edit h) may add or remove h to or from (Outlinks h) => make it safe
func (h *Hypha) AddOutLink(oh *Hypha) (added bool) {
h.Lock()
defer h.Unlock()
for _, outlink := range h.OutLinks {
if outlink == oh {
return false
}
}
h.OutLinks = append(h.OutLinks, oh)
return true
}
func (h *Hypha) AddBackLink(bh *Hypha) (added bool) {
h.Lock()
defer h.Unlock()
for _, backlink := range h.BackLinks {
if backlink == h {
return false
}
}
h.BackLinks = append(h.BackLinks, bh)
return true
}
func (h *Hypha) DropBackLink(bh *Hypha) {
h.Lock()
defer h.Unlock()
if len(h.BackLinks) <= 1 {
h.BackLinks = make([]*Hypha, 0)
return
}
lastBackLinkIndex := len(h.BackLinks)
for i, backlink := range h.BackLinks {
if backlink == bh {
if i != lastBackLinkIndex {
h.BackLinks[i] = h.BackLinks[lastBackLinkIndex]
}
h.BackLinks = h.BackLinks[:lastBackLinkIndex]
return
}
}
h.Unlock()
}

View File

@ -12,7 +12,7 @@ import (
type LinkType int
const (
LinkInavild LinkType = iota
LinkInvalid LinkType = iota
// LinkLocalRoot is a link like "/list", "/user-list", etc.
LinkLocalRoot
// LinkLocalHypha is a link like "test", "../test", etc.
@ -97,6 +97,11 @@ func From(address, display, hyphaName string) *Link {
link.Display = strings.TrimSpace(display)
}
if pos := strings.IndexRune(address, '#'); pos != -1 {
link.Anchor = address[pos:]
address = address[:pos]
}
switch {
case strings.ContainsRune(address, ':'):
pos := strings.IndexRune(address, ':')

16
main.go
View File

@ -85,8 +85,16 @@ func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
// Redirect to a random hypha.
func handlerRandom(w http.ResponseWriter, rq *http.Request) {
log.Println(rq.URL)
var randomHyphaName string
i := rand.Intn(hyphae.Count())
var (
randomHyphaName string
amountOfHyphae int = hyphae.Count()
)
if amountOfHyphae == 0 {
HttpErr(w, http.StatusNotFound, util.HomePage, "There are no hyphae",
"It is not possible to display a random hypha because the wiki does not contain any hyphae")
return
}
i := rand.Intn(amountOfHyphae)
for h := range hyphae.YieldExistingHyphae() {
if i == 0 {
randomHyphaName = h.Name
@ -160,16 +168,14 @@ Crawl-delay: 5`))
}
func main() {
log.Println("Running MycorrhizaWiki β")
parseCliArgs()
log.Println("Running MycorrhizaWiki β")
if err := os.Chdir(WikiDir); err != nil {
log.Fatal(err)
}
log.Println("Wiki storage directory is", WikiDir)
hyphae.Index(WikiDir)
log.Println("Indexed", hyphae.Count(), "hyphae")
shroom.FindAllBacklinks()
log.Println("Found all backlinks")
history.Start(WikiDir)
shroom.SetHeaderLinks()

View File

@ -1,47 +0,0 @@
package markup
import (
"fmt"
"testing"
)
func TestParseStartOfEntry(t *testing.T) {
img := ImgFromFirstLine("img {", "h")
tests := []struct {
line string
entry imgEntry
followedByDesc bool
}{
{"apple", imgEntry{"/binary/apple", "", "", ""}, false},
{"pear|", imgEntry{"/binary/pear", "", "", ""}, false},
{"яблоко| 30*60", imgEntry{"/binary/яблоко", "30", "60", ""}, false},
{"груша | 65 ", imgEntry{"/binary/груша", "65", "", ""}, false},
{"жеронимо | 30 { full desc }", imgEntry{"/binary/жеронимо", "30", "", " full desc "}, false},
{"жорно жованна | *5555 {partial description", imgEntry{"/binary/жорноованна", "", "5555", "partial description"}, true},
{"иноске | {full}", imgEntry{"/binary/иноске", "", "", "full"}, false},
{"j|{partial", imgEntry{"/binary/j", "", "", "partial"}, true},
}
for _, triplet := range tests {
entry, followedByDesc := img.parseStartOfEntry(triplet.line)
if entry != triplet.entry || followedByDesc != triplet.followedByDesc {
t.Error(fmt.Sprintf("%q:%q != %q; %v != %v", triplet.line, entry, triplet.entry, followedByDesc, triplet.followedByDesc))
}
}
}
func TestParseDimensions(t *testing.T) {
tests := [][]string{
{"500", "500", ""},
{"3em", "3em", ""},
{"500*", "500", ""},
{"*500", "", "500"},
{"800*520", "800", "520"},
{"17%*5rem", "17%", "5rem"},
}
for _, triplet := range tests {
sizeH, sizeV := parseDimensions(triplet[0])
if sizeH != triplet[1] || sizeV != triplet[2] {
t.Error(sizeH, "*", sizeV, " != ", triplet[1], "*", triplet[2])
}
}
}

View File

@ -1,67 +0,0 @@
package markup
import (
"fmt"
"io/ioutil"
"reflect"
"testing"
)
// TODO: move test markup docs to files, perhaps? These strings sure are ugly
func TestLex(t *testing.T) {
check := func(name, content string, expectedAst []Line) {
if ast := lex(name, content); !reflect.DeepEqual(ast, expectedAst) {
if len(ast) != len(expectedAst) {
t.Error("Expected and generated AST length of", name, "do not match. Printed generated AST.")
for _, l := range ast {
fmt.Printf("%d: %s\n", l.id, l.contents)
}
return
}
for i, e := range ast {
if !reflect.DeepEqual(e, expectedAst[i]) {
t.Error(fmt.Sprintf("Expected: %q\nGot:%q", expectedAst[i], e))
}
}
}
}
contentsB, err := ioutil.ReadFile("testdata/test.myco")
if err != nil {
t.Error("Could not read test markup file!")
}
contents := string(contentsB)
check("Apple", contents, []Line{
{1, "<h1 id='1'>1</h1>"},
{2, "<h2 id='2'>2</h2>"},
{3, "<h3 id='3'>3</h3>"},
{4, "<blockquote id='4'>quote</blockquote>"},
{5, `<ul id='5'>
<li>li 1</li>
<li>li 2</li>
</ul>`},
{6, "<p id='6'>text</p>"},
{7, "<p id='7'>more text</p>"},
{8, `<p><a id='8' class='rocketlink wikilink_internal' href="/page/pear">some link</a></p>`},
{9, `<ul id='9'>
<li>lin&#34;+</li>
</ul>`},
{10, `<pre id='10' alt='alt text goes here' class='codeblock'><code>=&gt; preformatted text
where markup is not lexed</code></pre>`},
{11, `<p><a id='11' class='rocketlink wikilink_internal' href="/page/linking">linking</a></p>`},
{12, "<p id='12'>text</p>"},
{13, `<pre id='13' alt='' class='codeblock'><code>()
/\</code></pre>`},
{14, Transclusion{"apple", 1, 3}},
{15, Img{
hyphaName: "Apple",
inDesc: false,
entries: []imgEntry{
{"/binary/hypha1", "", "", ""},
{"/binary/hypha2", "", "", ""},
{"/binary/hypha3", "60", "", ""},
{"/binary/hypha4", "", "", " line1\nline2\n"},
{"/binary/hypha5", "", "", "\nstate of minnesota\n"},
},
}},
})
}

View File

@ -13,6 +13,7 @@ import (
// * Rocketlinks
// * Transclusion
// * Image galleries
// Not needed anymore, I guess.
func (md *MycoDoc) OutLinks() chan string {
ch := make(chan string)
if !md.parsedAlready {

View File

@ -1,50 +0,0 @@
package markup
import (
"fmt"
"testing"
)
/*
func TestGetTextNode(t *testing.T) {
tests := [][]string{
// input textNode rest
{"barab", "barab", ""},
{"test, ", "test", ", "},
{"/test/", "", "/test/"},
{"\\/test/", "/test", "/"},
{"test \\/ar", "test /ar", ""},
{"test //italian// test", "test ", "//italian// test"},
}
for _, triplet := range tests {
a, b := getTextNode([]byte(triplet[0]))
if a != triplet[1] || string(b) != triplet[2] {
t.Error(fmt.Sprintf("Wanted: %q\nGot: %q %q", triplet, a, b))
}
}
}
*/
func TestParagraphToHtml(t *testing.T) {
tests := [][]string{
{"a simple paragraph", "a simple paragraph"},
{"//italic//", "<em>italic</em>"},
{"Embedded //italic//", "Embedded <em>italic</em>"},
{"double //italian// //text//", "double <em>italian</em> <em>text</em>"},
{"it has `mono`", "it has <code>mono</code>"},
{"it has ~~strike~~", "it has <s>strike</s>"},
{"this is a left **bold", "this is a left <strong>bold</strong>"},
{"this line has a ,comma, two of them", "this line has a ,comma, two of them"},
{"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."},
{"A [[simple]] link", `A <a href="/page/simple" class="wikilink_internal">simple</a> link`},
}
for _, test := range tests {
if ParagraphToHtml("Apple", test[0]) != test[1] {
t.Error(fmt.Sprintf("%q: Wanted %q, got %q", test[0], test[1], ParagraphToHtml("Apple", test[0])))
}
}
}
func init() {
HyphaExists = func(_ string) bool { return true }
}

View File

@ -1,22 +0,0 @@
package markup
import (
"testing"
)
func TestParseTransclusion(t *testing.T) {
check := func(line string, expectedXclusion Transclusion) {
if xcl := parseTransclusion(line, "t"); xcl != expectedXclusion {
t.Error(line, "; got:", xcl, "wanted:", expectedXclusion)
}
}
check("<= ", Transclusion{"", -9, -9})
check("<=hypha", Transclusion{"hypha", 0, 0})
check("<= hypha\t", Transclusion{"hypha", 0, 0})
check("<= hypha :", Transclusion{"hypha", 0, 0})
check("<= hypha : ..", Transclusion{"hypha", 0, 0})
check("<= hypha : 3", Transclusion{"hypha", 3, 3})
check("<= hypha : 3..", Transclusion{"hypha", 3, 0})
check("<= hypha : ..3", Transclusion{"hypha", 0, 3})
check("<= hypha : 3..4", Transclusion{"hypha", 3, 4})
}

@ -1 +1 @@
Subproject commit e7040f3e0dc41809063b77fcbc12fe33b234ea87
Subproject commit 133b2689fbd67cad274f1c10fc6cbe8adbfc156a

View File

@ -1,36 +0,0 @@
package shroom
import (
"io/ioutil"
"log"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/markup"
)
// FindAllBacklinks iterates over all hyphae that have text parts, sets their outlinks and then sets backlinks.
func FindAllBacklinks() {
for h := range hyphae.FilterTextHyphae(hyphae.YieldExistingHyphae()) {
findBacklinkWorker(h)
}
}
func findBacklinkWorker(h *hyphae.Hypha) {
var (
textContents, err = ioutil.ReadFile(h.TextPath)
)
if err == nil {
for outlink := range markup.Doc(h.Name, string(textContents)).OutLinks() {
outlinkHypha := hyphae.ByName(outlink)
if outlinkHypha == h {
break
}
outlinkHypha.AddBackLink(h)
outlinkHypha.InsertIfNewKeepExistence()
h.AddOutLink(outlinkHypha)
}
} else {
log.Println("Error when reading text contents of %s: %s", h.Name, err.Error())
}
}

View File

@ -13,7 +13,7 @@ func canFactory(
dispatcher func(*hyphae.Hypha, *user.User) (string, string),
noRightsMsg string,
notExistsMsg string,
careAboutExistince bool,
careAboutExistence bool,
) func(*user.User, *hyphae.Hypha) (error, string) {
return func(u *user.User, h *hyphae.Hypha) (error, string) {
if !u.CanProceed(action) {
@ -21,7 +21,7 @@ func canFactory(
return errors.New(noRightsMsg), "Not enough rights"
}
if careAboutExistince && !h.Exists {
if careAboutExistence && !h.Exists {
rejectLogger(h, u, "does not exist")
return errors.New(notExistsMsg), "Does not exist"
}

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"io/ioutil"
"log"
"strings"
"os"
"github.com/adrg/xdg"
@ -62,10 +63,10 @@ func readTokensToUsers() {
func tokenStoragePath() string {
dir, err := xdg.DataFile("mycorrhiza/tokens.json")
if err != nil {
// Yes, it is unix-only, but function above rarely fails, so this block is probably never reached.
path := "/home/" + os.Getenv("HOME") + "/.local/share/mycorrhiza/tokens.json"
os.MkdirAll(path, 0777)
return path
log.Fatal(err)
}
if strings.HasPrefix(dir, util.WikiDir) {
log.Fatal("Error: Wiki storage directory includes private config files")
}
return dir
}

View File

@ -26,6 +26,7 @@ var minimalRights = map[string]int{
"delete-ask": 3,
"delete-confirm": 3,
"reindex": 4,
"admin": 4,
"admin/shutdown": 4,
}

View File

@ -66,10 +66,10 @@ func userByName(username string) *User {
func commenceSession(username, token string) {
tokens.Store(token, username)
go dumpTokens()
dumpTokens()
}
func terminateSession(token string) {
tokens.Delete(token)
go dumpTokens()
dumpTokens()
}

73
util/config.go Normal file
View File

@ -0,0 +1,73 @@
package util
import (
"log"
"strconv"
"github.com/go-ini/ini"
)
type Config struct {
WikiName string
NaviTitleIcon string
Hyphae
Network
Authorization
}
type Hyphae struct {
HomeHypha string
UserHypha string
HeaderLinksHypha string
}
type Network struct {
HTTPPort uint64
URL string
GeminiCertificatePath string
}
type Authorization struct {
UseFixedAuth bool
FixedAuthCredentialsPath string
}
func ReadConfigFile(path string) {
cfg := &Config{
WikiName: "MycorrhizaWiki",
NaviTitleIcon: "🍄",
Hyphae: Hyphae{
HomeHypha: "home",
UserHypha: "u",
HeaderLinksHypha: "",
},
Network: Network{
HTTPPort: 1737,
URL: "",
GeminiCertificatePath: "",
},
Authorization: Authorization{
UseFixedAuth: false,
FixedAuthCredentialsPath: "",
},
}
if path != "" {
log.Println("Loading config at", path)
err := ini.MapTo(cfg, path)
if err != nil {
log.Fatal(err)
}
}
SiteName = cfg.WikiName
SiteNavIcon = cfg.NaviTitleIcon
HomePage = cfg.HomeHypha
UserHypha = cfg.UserHypha
HeaderLinksHypha = cfg.HeaderLinksHypha
ServerPort = strconv.FormatUint(cfg.HTTPPort, 10)
URL = cfg.URL
GeminiCertPath = cfg.GeminiCertificatePath
UseFixedAuth = cfg.UseFixedAuth
FixedCredentialsPath = cfg.FixedAuthCredentialsPath
}

View File

@ -9,18 +9,24 @@ import (
"unicode"
)
// TODO: make names match to fields of config file
var (
URL string
ServerPort string
HomePage string
SiteNavIcon string
SiteName string
WikiDir string
UserHypha string
HeaderLinksHypha string
AuthMethod string
SiteName string
SiteNavIcon string
HomePage string
UserHypha string
HeaderLinksHypha string
ServerPort string
URL string
GeminiCertPath string
UseFixedAuth bool
FixedCredentialsPath string
GeminiCertPath string
WikiDir string
ConfigFilePath string
)
// LettersNumbersOnly keeps letters and numbers only in the given string.
@ -53,6 +59,13 @@ func ShorterPath(path string) string {
return path
}
// HTTP404Page writes a 404 error in the status, needed when no content is found on the page.
func HTTP404Page(w http.ResponseWriter, page string) {
w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(page))
}
// HTTP200Page wraps some frequently used things for successful 200 responses.
func HTTP200Page(w http.ResponseWriter, page string) {
w.Header().Set("Content-Type", "text/html;charset=utf-8")

View File

@ -1,8 +1,29 @@
{% import "net/http" %}
{% import "github.com/bouncepaw/mycorrhiza/util" %}
{% import "github.com/bouncepaw/mycorrhiza/user" %}
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
{% import "github.com/bouncepaw/mycorrhiza/history" %}
{% func PrimitiveDiffHTML(rq *http.Request, h *hyphae.Hypha, u *user.User, hash string) %}
{% code
text, err := history.PrimitiveDiffAtRevision(h.TextPath, hash)
if err != nil {
text = err.Error()
}
%}
{%= NavHTML(rq, h.Name, "history") %}
<div class="layout">
<main class="main-width">
<article>
<h1>Diff {%s util.BeautifulName(h.Name) %} at {%s hash %}</h1>
<pre class="codeblock"><code>{%s text %}</code></pre>
</article>
</main>
</div>
{% endfunc %}
{% func RecentChangesHTML(n int) %}
<div class="layout">
<main class="main-width recent-changes">
@ -27,7 +48,7 @@
<p><img class="icon" width="20" height="20" src="https://upload.wikimedia.org/wikipedia/commons/4/46/Generic_Feed-icon.svg">Subscribe via <a href="/recent-changes-rss">RSS</a>, <a href="/recent-changes-atom">Atom</a> or <a href="/recent-changes-json">JSON feed</a>.</p>
{% comment %}
Here I am, willing to add some accesibility using ARIA. Turns out,
Here I am, willing to add some accessibility using ARIA. Turns out,
role="feed" is not supported in any screen reader as of September
2020. At least web search says so. Even JAWS doesn't support it!
How come? I'll add the role anyway. -- bouncepaw

View File

@ -11,24 +11,98 @@ import "net/http"
import "github.com/bouncepaw/mycorrhiza/util"
//line views/history.qtpl:4
import "github.com/bouncepaw/mycorrhiza/history"
import "github.com/bouncepaw/mycorrhiza/user"
//line views/history.qtpl:5
import "github.com/bouncepaw/mycorrhiza/hyphae"
//line views/history.qtpl:6
import "github.com/bouncepaw/mycorrhiza/history"
//line views/history.qtpl:9
import (
qtio422016 "io"
qt422016 "github.com/valyala/quicktemplate"
)
//line views/history.qtpl:6
//line views/history.qtpl:9
var (
_ = qtio422016.Copy
_ = qt422016.AcquireByteBuffer
)
//line views/history.qtpl:6
//line views/history.qtpl:9
func StreamPrimitiveDiffHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User, hash string) {
//line views/history.qtpl:9
qw422016.N().S(`
`)
//line views/history.qtpl:11
text, err := history.PrimitiveDiffAtRevision(h.TextPath, hash)
if err != nil {
text = err.Error()
}
//line views/history.qtpl:15
qw422016.N().S(`
`)
//line views/history.qtpl:16
StreamNavHTML(qw422016, rq, h.Name, "history")
//line views/history.qtpl:16
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<article>
<h1>Diff `)
//line views/history.qtpl:20
qw422016.E().S(util.BeautifulName(h.Name))
//line views/history.qtpl:20
qw422016.N().S(` at `)
//line views/history.qtpl:20
qw422016.E().S(hash)
//line views/history.qtpl:20
qw422016.N().S(`</h1>
<pre class="codeblock"><code>`)
//line views/history.qtpl:21
qw422016.E().S(text)
//line views/history.qtpl:21
qw422016.N().S(`</code></pre>
</article>
</main>
</div>
`)
//line views/history.qtpl:25
}
//line views/history.qtpl:25
func WritePrimitiveDiffHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User, hash string) {
//line views/history.qtpl:25
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/history.qtpl:25
StreamPrimitiveDiffHTML(qw422016, rq, h, u, hash)
//line views/history.qtpl:25
qt422016.ReleaseWriter(qw422016)
//line views/history.qtpl:25
}
//line views/history.qtpl:25
func PrimitiveDiffHTML(rq *http.Request, h *hyphae.Hypha, u *user.User, hash string) string {
//line views/history.qtpl:25
qb422016 := qt422016.AcquireByteBuffer()
//line views/history.qtpl:25
WritePrimitiveDiffHTML(qb422016, rq, h, u, hash)
//line views/history.qtpl:25
qs422016 := string(qb422016.B)
//line views/history.qtpl:25
qt422016.ReleaseByteBuffer(qb422016)
//line views/history.qtpl:25
return qs422016
//line views/history.qtpl:25
}
//line views/history.qtpl:27
func StreamRecentChangesHTML(qw422016 *qt422016.Writer, n int) {
//line views/history.qtpl:6
//line views/history.qtpl:27
qw422016.N().S(`
<div class="layout">
<main class="main-width recent-changes">
@ -38,51 +112,51 @@ func StreamRecentChangesHTML(qw422016 *qt422016.Writer, n int) {
<nav class="recent-changes__count">
See
`)
//line views/history.qtpl:14
//line views/history.qtpl:35
for _, m := range []int{20, 0, 50, 0, 100} {
//line views/history.qtpl:14
//line views/history.qtpl:35
qw422016.N().S(`
`)
//line views/history.qtpl:15
//line views/history.qtpl:36
switch m {
//line views/history.qtpl:16
//line views/history.qtpl:37
case 0:
//line views/history.qtpl:16
//line views/history.qtpl:37
qw422016.N().S(`
<span aria-hidden="true">|</span>
`)
//line views/history.qtpl:18
//line views/history.qtpl:39
case n:
//line views/history.qtpl:18
//line views/history.qtpl:39
qw422016.N().S(`
<b>`)
//line views/history.qtpl:19
//line views/history.qtpl:40
qw422016.N().D(n)
//line views/history.qtpl:19
//line views/history.qtpl:40
qw422016.N().S(`</b>
`)
//line views/history.qtpl:20
//line views/history.qtpl:41
default:
//line views/history.qtpl:20
//line views/history.qtpl:41
qw422016.N().S(`
<a href="/recent-changes/`)
//line views/history.qtpl:21
//line views/history.qtpl:42
qw422016.N().D(m)
//line views/history.qtpl:21
//line views/history.qtpl:42
qw422016.N().S(`">`)
//line views/history.qtpl:21
//line views/history.qtpl:42
qw422016.N().D(m)
//line views/history.qtpl:21
//line views/history.qtpl:42
qw422016.N().S(`</a>
`)
//line views/history.qtpl:22
//line views/history.qtpl:43
}
//line views/history.qtpl:22
//line views/history.qtpl:43
qw422016.N().S(`
`)
//line views/history.qtpl:23
//line views/history.qtpl:44
}
//line views/history.qtpl:23
//line views/history.qtpl:44
qw422016.N().S(`
recent changes
</nav>
@ -90,216 +164,216 @@ func StreamRecentChangesHTML(qw422016 *qt422016.Writer, n int) {
<p><img class="icon" width="20" height="20" src="https://upload.wikimedia.org/wikipedia/commons/4/46/Generic_Feed-icon.svg">Subscribe via <a href="/recent-changes-rss">RSS</a>, <a href="/recent-changes-atom">Atom</a> or <a href="/recent-changes-json">JSON feed</a>.</p>
`)
//line views/history.qtpl:34
//line views/history.qtpl:55
qw422016.N().S(`
`)
//line views/history.qtpl:37
//line views/history.qtpl:58
changes := history.RecentChanges(n)
//line views/history.qtpl:38
//line views/history.qtpl:59
qw422016.N().S(`
<section class="recent-changes__list" role="feed">
`)
//line views/history.qtpl:40
//line views/history.qtpl:61
if len(changes) == 0 {
//line views/history.qtpl:40
//line views/history.qtpl:61
qw422016.N().S(`
<p>Could not find any recent changes.</p>
`)
//line views/history.qtpl:42
//line views/history.qtpl:63
} else {
//line views/history.qtpl:42
//line views/history.qtpl:63
qw422016.N().S(`
`)
//line views/history.qtpl:43
//line views/history.qtpl:64
for i, entry := range changes {
//line views/history.qtpl:43
//line views/history.qtpl:64
qw422016.N().S(`
<ul class="recent-changes__entry rc-entry" role="article"
aria-setsize="`)
//line views/history.qtpl:45
//line views/history.qtpl:66
qw422016.N().D(n)
//line views/history.qtpl:45
//line views/history.qtpl:66
qw422016.N().S(`" aria-posinset="`)
//line views/history.qtpl:45
//line views/history.qtpl:66
qw422016.N().D(i)
//line views/history.qtpl:45
//line views/history.qtpl:66
qw422016.N().S(`">
`)
//line views/history.qtpl:46
//line views/history.qtpl:67
qw422016.N().S(recentChangesEntry(entry))
//line views/history.qtpl:46
//line views/history.qtpl:67
qw422016.N().S(`
</ul>
`)
//line views/history.qtpl:48
//line views/history.qtpl:69
}
//line views/history.qtpl:48
//line views/history.qtpl:69
qw422016.N().S(`
`)
//line views/history.qtpl:49
//line views/history.qtpl:70
}
//line views/history.qtpl:49
//line views/history.qtpl:70
qw422016.N().S(`
</section>
</main>
</div>
`)
//line views/history.qtpl:53
//line views/history.qtpl:74
}
//line views/history.qtpl:53
//line views/history.qtpl:74
func WriteRecentChangesHTML(qq422016 qtio422016.Writer, n int) {
//line views/history.qtpl:53
//line views/history.qtpl:74
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/history.qtpl:53
//line views/history.qtpl:74
StreamRecentChangesHTML(qw422016, n)
//line views/history.qtpl:53
//line views/history.qtpl:74
qt422016.ReleaseWriter(qw422016)
//line views/history.qtpl:53
//line views/history.qtpl:74
}
//line views/history.qtpl:53
//line views/history.qtpl:74
func RecentChangesHTML(n int) string {
//line views/history.qtpl:53
//line views/history.qtpl:74
qb422016 := qt422016.AcquireByteBuffer()
//line views/history.qtpl:53
//line views/history.qtpl:74
WriteRecentChangesHTML(qb422016, n)
//line views/history.qtpl:53
//line views/history.qtpl:74
qs422016 := string(qb422016.B)
//line views/history.qtpl:53
//line views/history.qtpl:74
qt422016.ReleaseByteBuffer(qb422016)
//line views/history.qtpl:53
//line views/history.qtpl:74
return qs422016
//line views/history.qtpl:53
//line views/history.qtpl:74
}
//line views/history.qtpl:55
//line views/history.qtpl:76
func streamrecentChangesEntry(qw422016 *qt422016.Writer, rev history.Revision) {
//line views/history.qtpl:55
//line views/history.qtpl:76
qw422016.N().S(`
<li class="rc-entry__time"><time>`)
//line views/history.qtpl:56
//line views/history.qtpl:77
qw422016.E().S(rev.TimeString())
//line views/history.qtpl:56
//line views/history.qtpl:77
qw422016.N().S(`</time></li>
<li class="rc-entry__hash">`)
//line views/history.qtpl:57
//line views/history.qtpl:78
qw422016.E().S(rev.Hash)
//line views/history.qtpl:57
//line views/history.qtpl:78
qw422016.N().S(`</li>
<li class="rc-entry__links">`)
//line views/history.qtpl:58
//line views/history.qtpl:79
qw422016.N().S(rev.HyphaeLinksHTML())
//line views/history.qtpl:58
//line views/history.qtpl:79
qw422016.N().S(`</li>
<li class="rc-entry__msg">`)
//line views/history.qtpl:59
//line views/history.qtpl:80
qw422016.E().S(rev.Message)
//line views/history.qtpl:59
//line views/history.qtpl:80
qw422016.N().S(` `)
//line views/history.qtpl:59
//line views/history.qtpl:80
if rev.Username != "anon" {
//line views/history.qtpl:59
//line views/history.qtpl:80
qw422016.N().S(`<span class="rc-entry__author">by <a href="/hypha/`)
//line views/history.qtpl:59
//line views/history.qtpl:80
qw422016.E().S(util.UserHypha)
//line views/history.qtpl:59
//line views/history.qtpl:80
qw422016.N().S(`/`)
//line views/history.qtpl:59
//line views/history.qtpl:80
qw422016.E().S(rev.Username)
//line views/history.qtpl:59
//line views/history.qtpl:80
qw422016.N().S(`" rel="author">`)
//line views/history.qtpl:59
//line views/history.qtpl:80
qw422016.E().S(rev.Username)
//line views/history.qtpl:59
//line views/history.qtpl:80
qw422016.N().S(`</a></span>`)
//line views/history.qtpl:59
//line views/history.qtpl:80
}
//line views/history.qtpl:59
//line views/history.qtpl:80
qw422016.N().S(`</li>
`)
//line views/history.qtpl:60
//line views/history.qtpl:81
}
//line views/history.qtpl:60
//line views/history.qtpl:81
func writerecentChangesEntry(qq422016 qtio422016.Writer, rev history.Revision) {
//line views/history.qtpl:60
//line views/history.qtpl:81
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/history.qtpl:60
//line views/history.qtpl:81
streamrecentChangesEntry(qw422016, rev)
//line views/history.qtpl:60
//line views/history.qtpl:81
qt422016.ReleaseWriter(qw422016)
//line views/history.qtpl:60
//line views/history.qtpl:81
}
//line views/history.qtpl:60
//line views/history.qtpl:81
func recentChangesEntry(rev history.Revision) string {
//line views/history.qtpl:60
//line views/history.qtpl:81
qb422016 := qt422016.AcquireByteBuffer()
//line views/history.qtpl:60
//line views/history.qtpl:81
writerecentChangesEntry(qb422016, rev)
//line views/history.qtpl:60
//line views/history.qtpl:81
qs422016 := string(qb422016.B)
//line views/history.qtpl:60
//line views/history.qtpl:81
qt422016.ReleaseByteBuffer(qb422016)
//line views/history.qtpl:60
//line views/history.qtpl:81
return qs422016
//line views/history.qtpl:60
//line views/history.qtpl:81
}
//line views/history.qtpl:62
//line views/history.qtpl:83
func StreamHistoryHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, list string) {
//line views/history.qtpl:62
//line views/history.qtpl:83
qw422016.N().S(`
`)
//line views/history.qtpl:63
//line views/history.qtpl:84
StreamNavHTML(qw422016, rq, hyphaName, "history")
//line views/history.qtpl:63
//line views/history.qtpl:84
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<article class="history">
<h1>History of `)
//line views/history.qtpl:67
//line views/history.qtpl:88
qw422016.E().S(util.BeautifulName(hyphaName))
//line views/history.qtpl:67
//line views/history.qtpl:88
qw422016.N().S(`</h1>
`)
//line views/history.qtpl:68
//line views/history.qtpl:89
qw422016.N().S(list)
//line views/history.qtpl:68
//line views/history.qtpl:89
qw422016.N().S(`
</article>
</main>
</div>
`)
//line views/history.qtpl:72
//line views/history.qtpl:93
}
//line views/history.qtpl:72
//line views/history.qtpl:93
func WriteHistoryHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, list string) {
//line views/history.qtpl:72
//line views/history.qtpl:93
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/history.qtpl:72
//line views/history.qtpl:93
StreamHistoryHTML(qw422016, rq, hyphaName, list)
//line views/history.qtpl:72
//line views/history.qtpl:93
qt422016.ReleaseWriter(qw422016)
//line views/history.qtpl:72
//line views/history.qtpl:93
}
//line views/history.qtpl:72
//line views/history.qtpl:93
func HistoryHTML(rq *http.Request, hyphaName, list string) string {
//line views/history.qtpl:72
//line views/history.qtpl:93
qb422016 := qt422016.AcquireByteBuffer()
//line views/history.qtpl:72
//line views/history.qtpl:93
WriteHistoryHTML(qb422016, rq, hyphaName, list)
//line views/history.qtpl:72
//line views/history.qtpl:93
qs422016 := string(qb422016.B)
//line views/history.qtpl:72
//line views/history.qtpl:93
qt422016.ReleaseByteBuffer(qb422016)
//line views/history.qtpl:72
//line views/history.qtpl:93
return qs422016
//line views/history.qtpl:72
//line views/history.qtpl:93
}

View File

@ -32,23 +32,6 @@
</h1>
{% endfunc %}
{% func BackLinksHTML(h *hyphae.Hypha) %}
<aside class="backlinks layout-card">
<h2 class="backlinks__title layout-card__title">Backlinks</h2>
<nav class="backlinks__nav">
<ul class="backlinks__list">
{% for _, backlink := range h.BackLinks %}
<li class="backlinks__entry">
<a class="backlinks__link" href="/hypha/{%s backlink.Name %}">
{%s util.BeautifulName(filepath.Base(backlink.Name)) %}
</a>
</li>
{% endfor %}
</ul>
</nav>
</aside>
{% endfunc %}
{% func AttachmentHTML(h *hyphae.Hypha) %}
{% switch filepath.Ext(h.BinaryPath) %}

View File

@ -123,175 +123,111 @@ func NaviTitleHTML(h *hyphae.Hypha) string {
}
//line views/hypha.qtpl:35
func StreamBackLinksHTML(qw422016 *qt422016.Writer, h *hyphae.Hypha) {
func StreamAttachmentHTML(qw422016 *qt422016.Writer, h *hyphae.Hypha) {
//line views/hypha.qtpl:35
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 views/hypha.qtpl:40
for _, backlink := range h.BackLinks {
//line views/hypha.qtpl:40
qw422016.N().S(`
<li class="backlinks__entry">
<a class="backlinks__link" href="/hypha/`)
//line views/hypha.qtpl:42
qw422016.E().S(backlink.Name)
//line views/hypha.qtpl:42
qw422016.N().S(`">
`)
//line views/hypha.qtpl:43
qw422016.E().S(util.BeautifulName(filepath.Base(backlink.Name)))
//line views/hypha.qtpl:43
qw422016.N().S(`
</a>
</li>
`)
//line views/hypha.qtpl:46
}
//line views/hypha.qtpl:46
qw422016.N().S(`
</ul>
</nav>
</aside>
`)
//line views/hypha.qtpl:50
}
//line views/hypha.qtpl:50
func WriteBackLinksHTML(qq422016 qtio422016.Writer, h *hyphae.Hypha) {
//line views/hypha.qtpl:50
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/hypha.qtpl:50
StreamBackLinksHTML(qw422016, h)
//line views/hypha.qtpl:50
qt422016.ReleaseWriter(qw422016)
//line views/hypha.qtpl:50
}
//line views/hypha.qtpl:50
func BackLinksHTML(h *hyphae.Hypha) string {
//line views/hypha.qtpl:50
qb422016 := qt422016.AcquireByteBuffer()
//line views/hypha.qtpl:50
WriteBackLinksHTML(qb422016, h)
//line views/hypha.qtpl:50
qs422016 := string(qb422016.B)
//line views/hypha.qtpl:50
qt422016.ReleaseByteBuffer(qb422016)
//line views/hypha.qtpl:50
return qs422016
//line views/hypha.qtpl:50
}
//line views/hypha.qtpl:52
func StreamAttachmentHTML(qw422016 *qt422016.Writer, h *hyphae.Hypha) {
//line views/hypha.qtpl:52
qw422016.N().S(`
`)
//line views/hypha.qtpl:53
//line views/hypha.qtpl:36
switch filepath.Ext(h.BinaryPath) {
//line views/hypha.qtpl:55
//line views/hypha.qtpl:38
case ".jpg", ".gif", ".png", ".webp", ".svg", ".ico":
//line views/hypha.qtpl:55
//line views/hypha.qtpl:38
qw422016.N().S(`
<div class="binary-container binary-container_with-img">
<a href="/binary/`)
//line views/hypha.qtpl:57
//line views/hypha.qtpl:40
qw422016.N().S(h.Name)
//line views/hypha.qtpl:57
//line views/hypha.qtpl:40
qw422016.N().S(`"><img src="/binary/`)
//line views/hypha.qtpl:57
//line views/hypha.qtpl:40
qw422016.N().S(h.Name)
//line views/hypha.qtpl:57
//line views/hypha.qtpl:40
qw422016.N().S(`"/></a>
</div>
`)
//line views/hypha.qtpl:60
//line views/hypha.qtpl:43
case ".ogg", ".webm", ".mp4":
//line views/hypha.qtpl:60
//line views/hypha.qtpl:43
qw422016.N().S(`
<div class="binary-container binary-container_with-video">
<video controls>
<source src="/binary/`)
//line views/hypha.qtpl:63
//line views/hypha.qtpl:46
qw422016.N().S(h.Name)
//line views/hypha.qtpl:63
//line views/hypha.qtpl:46
qw422016.N().S(`"/>
<p>Your browser does not support video. <a href="/binary/`)
//line views/hypha.qtpl:64
//line views/hypha.qtpl:47
qw422016.N().S(h.Name)
//line views/hypha.qtpl:64
//line views/hypha.qtpl:47
qw422016.N().S(`">Download video</a></p>
</video>
</div>
`)
//line views/hypha.qtpl:68
//line views/hypha.qtpl:51
case ".mp3":
//line views/hypha.qtpl:68
//line views/hypha.qtpl:51
qw422016.N().S(`
<div class="binary-container binary-container_with-audio">
<audio controls>
<source src="/binary/`)
//line views/hypha.qtpl:71
//line views/hypha.qtpl:54
qw422016.N().S(h.Name)
//line views/hypha.qtpl:71
//line views/hypha.qtpl:54
qw422016.N().S(`"/>
<p>Your browser does not support audio. <a href="/binary/`)
//line views/hypha.qtpl:72
//line views/hypha.qtpl:55
qw422016.N().S(h.Name)
//line views/hypha.qtpl:72
//line views/hypha.qtpl:55
qw422016.N().S(`">Download audio</a></p>
</audio>
</div>
`)
//line views/hypha.qtpl:76
//line views/hypha.qtpl:59
default:
//line views/hypha.qtpl:76
//line views/hypha.qtpl:59
qw422016.N().S(`
<div class="binary-container binary-container_with-nothing">
<p><a href="/binary/`)
//line views/hypha.qtpl:78
//line views/hypha.qtpl:61
qw422016.N().S(h.Name)
//line views/hypha.qtpl:78
//line views/hypha.qtpl:61
qw422016.N().S(`">Download media</a></p>
</div>
`)
//line views/hypha.qtpl:80
//line views/hypha.qtpl:63
}
//line views/hypha.qtpl:80
//line views/hypha.qtpl:63
qw422016.N().S(`
`)
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
}
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
func WriteAttachmentHTML(qq422016 qtio422016.Writer, h *hyphae.Hypha) {
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
StreamAttachmentHTML(qw422016, h)
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
qt422016.ReleaseWriter(qw422016)
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
}
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
func AttachmentHTML(h *hyphae.Hypha) string {
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
qb422016 := qt422016.AcquireByteBuffer()
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
WriteAttachmentHTML(qb422016, h)
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
qs422016 := string(qb422016.B)
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
qt422016.ReleaseByteBuffer(qb422016)
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
return qs422016
//line views/hypha.qtpl:81
//line views/hypha.qtpl:64
}

View File

@ -34,7 +34,7 @@
<a href="/page/{%s hyphaName %}" class="edit-form__cancel">Cancel</a>
</form>
<p class="warning">Note that the hypha is not saved yet. You can preview the changes ↓</p>
<section class="edit__preview">{%s= renderedPage %}</section>
<article class="edit__preview">{%s= renderedPage %}</article>
</main>
</div>
{% endfunc %}

View File

@ -138,11 +138,11 @@ func StreamPreviewHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, t
qw422016.N().S(`" class="edit-form__cancel">Cancel</a>
</form>
<p class="warning">Note that the hypha is not saved yet. You can preview the changes </p>
<section class="edit__preview">`)
<article class="edit__preview">`)
//line views/mutators.qtpl:37
qw422016.N().S(renderedPage)
//line views/mutators.qtpl:37
qw422016.N().S(`</section>
qw422016.N().S(`</article>
</main>
</div>
`)

View File

@ -108,7 +108,6 @@ If `contents` == "", a helpful message is shown instead.
{%= SubhyphaeHTML(subhyphae) %}
</main>
{%= RelativeHyphaeHTML(relatives) %}
{%= BackLinksHTML(h) %}
</div>
{% endfunc %}

View File

@ -345,110 +345,105 @@ func StreamHyphaHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hyph
StreamRelativeHyphaeHTML(qw422016, relatives)
//line views/readers.qtpl:110
qw422016.N().S(`
`)
//line views/readers.qtpl:111
StreamBackLinksHTML(qw422016, h)
//line views/readers.qtpl:111
qw422016.N().S(`
</div>
`)
//line views/readers.qtpl:113
//line views/readers.qtpl:112
}
//line views/readers.qtpl:113
//line views/readers.qtpl:112
func WriteHyphaHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, contents string) {
//line views/readers.qtpl:113
//line views/readers.qtpl:112
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/readers.qtpl:113
//line views/readers.qtpl:112
StreamHyphaHTML(qw422016, rq, h, contents)
//line views/readers.qtpl:113
//line views/readers.qtpl:112
qt422016.ReleaseWriter(qw422016)
//line views/readers.qtpl:113
//line views/readers.qtpl:112
}
//line views/readers.qtpl:113
//line views/readers.qtpl:112
func HyphaHTML(rq *http.Request, h *hyphae.Hypha, contents string) string {
//line views/readers.qtpl:113
//line views/readers.qtpl:112
qb422016 := qt422016.AcquireByteBuffer()
//line views/readers.qtpl:113
//line views/readers.qtpl:112
WriteHyphaHTML(qb422016, rq, h, contents)
//line views/readers.qtpl:113
//line views/readers.qtpl:112
qs422016 := string(qb422016.B)
//line views/readers.qtpl:113
//line views/readers.qtpl:112
qt422016.ReleaseByteBuffer(qb422016)
//line views/readers.qtpl:113
//line views/readers.qtpl:112
return qs422016
//line views/readers.qtpl:113
//line views/readers.qtpl:112
}
//line views/readers.qtpl:115
//line views/readers.qtpl:114
func StreamRevisionHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, contents, revHash string) {
//line views/readers.qtpl:115
//line views/readers.qtpl:114
qw422016.N().S(`
`)
//line views/readers.qtpl:117
//line views/readers.qtpl:116
relatives, subhyphae, _, _ := tree.Tree(h.Name)
//line views/readers.qtpl:118
//line views/readers.qtpl:117
qw422016.N().S(`
`)
//line views/readers.qtpl:119
//line views/readers.qtpl:118
StreamNavHTML(qw422016, rq, h.Name, "revision", revHash)
//line views/readers.qtpl:119
//line views/readers.qtpl:118
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<article>
<p>Please note that viewing binary parts of hyphae is not supported in history for now.</p>
`)
//line views/readers.qtpl:124
//line views/readers.qtpl:123
qw422016.N().S(NaviTitleHTML(h))
//line views/readers.qtpl:124
//line views/readers.qtpl:123
qw422016.N().S(`
`)
//line views/readers.qtpl:125
//line views/readers.qtpl:124
qw422016.N().S(contents)
//line views/readers.qtpl:125
//line views/readers.qtpl:124
qw422016.N().S(`
</article>
`)
//line views/readers.qtpl:127
//line views/readers.qtpl:126
StreamSubhyphaeHTML(qw422016, subhyphae)
//line views/readers.qtpl:127
//line views/readers.qtpl:126
qw422016.N().S(`
</main>
`)
//line views/readers.qtpl:129
//line views/readers.qtpl:128
StreamRelativeHyphaeHTML(qw422016, relatives)
//line views/readers.qtpl:129
//line views/readers.qtpl:128
qw422016.N().S(`
</div>
`)
//line views/readers.qtpl:131
//line views/readers.qtpl:130
}
//line views/readers.qtpl:131
//line views/readers.qtpl:130
func WriteRevisionHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, contents, revHash string) {
//line views/readers.qtpl:131
//line views/readers.qtpl:130
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/readers.qtpl:131
//line views/readers.qtpl:130
StreamRevisionHTML(qw422016, rq, h, contents, revHash)
//line views/readers.qtpl:131
//line views/readers.qtpl:130
qt422016.ReleaseWriter(qw422016)
//line views/readers.qtpl:131
//line views/readers.qtpl:130
}
//line views/readers.qtpl:131
//line views/readers.qtpl:130
func RevisionHTML(rq *http.Request, h *hyphae.Hypha, contents, revHash string) string {
//line views/readers.qtpl:131
//line views/readers.qtpl:130
qb422016 := qt422016.AcquireByteBuffer()
//line views/readers.qtpl:131
//line views/readers.qtpl:130
WriteRevisionHTML(qb422016, rq, h, contents, revHash)
//line views/readers.qtpl:131
//line views/readers.qtpl:130
qs422016 := string(qb422016.B)
//line views/readers.qtpl:131
//line views/readers.qtpl:130
qt422016.ReleaseByteBuffer(qb422016)
//line views/readers.qtpl:131
//line views/readers.qtpl:130
return qs422016
//line views/readers.qtpl:131
//line views/readers.qtpl:130
}

View File

@ -97,7 +97,7 @@ for u := range user.YieldUsers() {
<section>
<h1>About {%s util.SiteName %}</h1>
<ul>
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> β 0.13</li>
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> 1.0.0</li>
{%- if user.AuthUsed -%}
<li><b>User count:</b> {%d user.Count() %}</li>
<li><b>Home page:</b> <a href="/">{%s util.HomePage %}</a></li>
@ -118,7 +118,7 @@ for u := range user.YieldUsers() {
{% func AdminPanelHTML() %}
<div class="layout">
<main class="main-width">
<h1>Admininstrative functions</h1>
<h1>Administrative functions</h1>
<section>
<h2>Safe things</h2>
<ul>
@ -141,6 +141,12 @@ for u := range user.YieldUsers() {
<input type="submit">
</fieldset>
</form>
<form action="/admin/reindex-users" method="POST" style="float:left">
<fieldset>
<legend>Reindex users</legend>
<input type="submit">
</fieldset>
</form>
</section>
</main>
</div>

View File

@ -352,7 +352,7 @@ func StreamAboutHTML(qw422016 *qt422016.Writer) {
//line views/stuff.qtpl:98
qw422016.N().S(`</h1>
<ul>
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> β 0.13 indev</li>
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> β 0.13</li>
`)
//line views/stuff.qtpl:101
if user.AuthUsed {
@ -445,7 +445,7 @@ func StreamAdminPanelHTML(qw422016 *qt422016.Writer) {
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<h1>Admininstrative functions</h1>
<h1>Administrative functions</h1>
<section>
<h2>Safe things</h2>
<ul>
@ -468,35 +468,41 @@ func StreamAdminPanelHTML(qw422016 *qt422016.Writer) {
<input type="submit">
</fieldset>
</form>
<form action="/admin/reindex-users" method="POST" style="float:left">
<fieldset>
<legend>Reindex users</legend>
<input type="submit">
</fieldset>
</form>
</section>
</main>
</div>
`)
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
}
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
func WriteAdminPanelHTML(qq422016 qtio422016.Writer) {
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
StreamAdminPanelHTML(qw422016)
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
}
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
func AdminPanelHTML() string {
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
WriteAdminPanelHTML(qb422016)
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
return qs422016
//line views/stuff.qtpl:147
//line views/stuff.qtpl:153
}