diff --git a/.gitignore b/.gitignore index 9632b28..f6d437f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ mycorrhiza hyphae/*.gog + +# go editors and IDEA folders +.idea/ +.vscode/ diff --git a/Makefile b/Makefile index 0b9db7e..c48dc4f 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index d7269e6..72405b7 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/assets/assets.qtpl b/assets/assets.qtpl index 2e6b4f2..a3a56f6 100644 --- a/assets/assets.qtpl +++ b/assets/assets.qtpl @@ -1,3 +1,11 @@ +{%- func HelpMessage() -%} +Usage of %s: +{%- endfunc -%} + +{%- func ExampleConfig() -%} +{% cat "config.ini" %} +{%- endfunc -%} + {% func DefaultCSS() %} {% cat "default.css" %} {% endfunc %} diff --git a/assets/assets.qtpl.go b/assets/assets.qtpl.go index 160549b..fb79b57 100644 --- a/assets/assets.qtpl.go +++ b/assets/assets.qtpl.go @@ -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(` `) -//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(` `) -//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(` -`) -//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(` +`) +//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(` `) -//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 } diff --git a/assets/config.ini b/assets/config.ini new file mode 100644 index 0000000..0ad67b9 --- /dev/null +++ b/assets/config.ini @@ -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 diff --git a/assets/default.css b/assets/default.css index 9c93b48..8700c5a 100644 --- a/assets/default.css +++ b/assets/default.css @@ -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; } diff --git a/flag.go b/flag.go index 77a7d05..b3be793 100644 --- a/flag.go +++ b/flag.go @@ -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) } } diff --git a/go.mod b/go.mod index 26dbe9f..060d073 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 0aa3df2..544a723 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/history/history.go b/history/history.go index d02e156..a1e897b 100644 --- a/history/history.go +++ b/history/history.go @@ -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) { diff --git a/history/information.go b/history/information.go index f3cf8a7..cd22f0c 100644 --- a/history/information.go +++ b/history/information.go @@ -143,7 +143,7 @@ func (rev *Revision) asHistoryEntry(hyphaName string) (html string) {
  • - %[3]s + %[3]s %[4]s %[5]s
  • @@ -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 +} diff --git a/http_admin.go b/http_admin.go index 2b20fb5..2b95e22 100644 --- a/http_admin.go +++ b/http_admin.go @@ -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) + } +} diff --git a/http_readers.go b/http_readers.go index a75b682..597d8b8 100644 --- a/http_readers.go +++ b/http_readers.go @@ -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)) + } } diff --git a/hyphae/files.go b/hyphae/files.go index 56ccda5..ad98fd8 100644 --- a/hyphae/files.go +++ b/hyphae/files.go @@ -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" && diff --git a/hyphae/hyphae.go b/hyphae/hyphae.go index 59e2763..aa9e991 100644 --- a/hyphae/hyphae.go +++ b/hyphae/hyphae.go @@ -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() } diff --git a/link/link.go b/link/link.go index 6fe3323..d547df2 100644 --- a/link/link.go +++ b/link/link.go @@ -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, ':') diff --git a/main.go b/main.go index f8780b1..b9ff7d1 100644 --- a/main.go +++ b/main.go @@ -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() diff --git a/markup/img_test.go b/markup/img_test.go deleted file mode 100644 index f2e2044..0000000 --- a/markup/img_test.go +++ /dev/null @@ -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]) - } - } -} diff --git a/markup/lexer_test.go b/markup/lexer_test.go deleted file mode 100644 index b19bec2..0000000 --- a/markup/lexer_test.go +++ /dev/null @@ -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, "

    1

    "}, - {2, "

    2

    "}, - {3, "

    3

    "}, - {4, "
    quote
    "}, - {5, ``}, - {6, "

    text

    "}, - {7, "

    more text

    "}, - {8, `

    some link

    `}, - {9, ``}, - {10, `
    => preformatted text
    -where markup is not lexed
    `}, - {11, `

    linking

    `}, - {12, "

    text

    "}, - {13, `
    ()
    -/\
    `}, - {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"}, - }, - }}, - }) -} diff --git a/markup/outlink.go b/markup/outlink.go index 0580371..0f4203a 100644 --- a/markup/outlink.go +++ b/markup/outlink.go @@ -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 { diff --git a/markup/paragraph_test.go b/markup/paragraph_test.go deleted file mode 100644 index 71bd5fc..0000000 --- a/markup/paragraph_test.go +++ /dev/null @@ -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//", "italic"}, - {"Embedded //italic//", "Embedded italic"}, - {"double //italian// //text//", "double italian text"}, - {"it has `mono`", "it has mono"}, - {"it has ~~strike~~", "it has strike"}, - {"this is a left **bold", "this is a left bold"}, - {"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 simple 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 } -} diff --git a/markup/xclusion_test.go b/markup/xclusion_test.go deleted file mode 100644 index d61a2ad..0000000 --- a/markup/xclusion_test.go +++ /dev/null @@ -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}) -} diff --git a/metarrhiza b/metarrhiza index e7040f3..133b268 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit e7040f3e0dc41809063b77fcbc12fe33b234ea87 +Subproject commit 133b2689fbd67cad274f1c10fc6cbe8adbfc156a diff --git a/shroom/backlink.go b/shroom/backlink.go deleted file mode 100644 index 860ba56..0000000 --- a/shroom/backlink.go +++ /dev/null @@ -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()) - } -} diff --git a/shroom/can.go b/shroom/can.go index 152d896..3e0c67f 100644 --- a/shroom/can.go +++ b/shroom/can.go @@ -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" } diff --git a/user/files.go b/user/files.go index 3235497..a4fa7a0 100644 --- a/user/files.go +++ b/user/files.go @@ -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 } diff --git a/user/user.go b/user/user.go index aa7a74c..e126d76 100644 --- a/user/user.go +++ b/user/user.go @@ -26,6 +26,7 @@ var minimalRights = map[string]int{ "delete-ask": 3, "delete-confirm": 3, "reindex": 4, + "admin": 4, "admin/shutdown": 4, } diff --git a/user/users.go b/user/users.go index 847d130..9a56212 100644 --- a/user/users.go +++ b/user/users.go @@ -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() } diff --git a/util/config.go b/util/config.go new file mode 100644 index 0000000..0cf2687 --- /dev/null +++ b/util/config.go @@ -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 +} diff --git a/util/util.go b/util/util.go index 6b7590f..1c42d40 100644 --- a/util/util.go +++ b/util/util.go @@ -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") diff --git a/views/history.qtpl b/views/history.qtpl index 7b5b0ef..cf645be 100644 --- a/views/history.qtpl +++ b/views/history.qtpl @@ -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") %} +
    +
    +
    +

    Diff {%s util.BeautifulName(h.Name) %} at {%s hash %}

    +
    {%s text %}
    +
    +
    +
    +{% endfunc %} + {% func RecentChangesHTML(n int) %}
    @@ -27,7 +48,7 @@

    Subscribe via RSS, Atom or JSON feed.

    {% 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 diff --git a/views/history.qtpl.go b/views/history.qtpl.go index 6d81497..d61101f 100644 --- a/views/history.qtpl.go +++ b/views/history.qtpl.go @@ -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(` +
    +
    +
    +

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

    +
    `)
    +//line views/history.qtpl:21
    +	qw422016.E().S(text)
    +//line views/history.qtpl:21
    +	qw422016.N().S(`
    +
    +
    +
    +`) +//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(`
    @@ -38,51 +112,51 @@ func StreamRecentChangesHTML(qw422016 *qt422016.Writer, n int) { @@ -90,216 +164,216 @@ func StreamRecentChangesHTML(qw422016 *qt422016.Writer, n int) {

    Subscribe via RSS, Atom or JSON feed.

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

    Could not find any recent changes.

    `) -//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(`
      `) -//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(`
    `) -//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(`
    `) -//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(`
  • `) -//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(`
  • `) -//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(`
  • `) -//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(``) -//line views/history.qtpl:59 +//line views/history.qtpl:80 } -//line views/history.qtpl:59 +//line views/history.qtpl:80 qw422016.N().S(`
  • `) -//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(`

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

    `) -//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(`
    `) -//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 } diff --git a/views/hypha.qtpl b/views/hypha.qtpl index a2d74ba..74d6ae6 100644 --- a/views/hypha.qtpl +++ b/views/hypha.qtpl @@ -32,23 +32,6 @@ {% endfunc %} -{% func BackLinksHTML(h *hyphae.Hypha) %} - -{% endfunc %} - {% func AttachmentHTML(h *hyphae.Hypha) %} {% switch filepath.Ext(h.BinaryPath) %} diff --git a/views/hypha.qtpl.go b/views/hypha.qtpl.go index 499b9aa..13bcc52 100644 --- a/views/hypha.qtpl.go +++ b/views/hypha.qtpl.go @@ -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(` - -`) -//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(`
    `) -//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(`
    `) -//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(`
    `) -//line views/hypha.qtpl:76 +//line views/hypha.qtpl:59 default: -//line views/hypha.qtpl:76 +//line views/hypha.qtpl:59 qw422016.N().S(` `) -//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 } diff --git a/views/mutators.qtpl b/views/mutators.qtpl index 233c3d5..ff7cbf0 100644 --- a/views/mutators.qtpl +++ b/views/mutators.qtpl @@ -34,7 +34,7 @@ Cancel

    Note that the hypha is not saved yet. You can preview the changes ↓

    -
    {%s= renderedPage %}
    +
    {%s= renderedPage %}
    {% endfunc %} diff --git a/views/mutators.qtpl.go b/views/mutators.qtpl.go index 5510ceb..23ac4ea 100644 --- a/views/mutators.qtpl.go +++ b/views/mutators.qtpl.go @@ -138,11 +138,11 @@ func StreamPreviewHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, t qw422016.N().S(`" class="edit-form__cancel">Cancel

    Note that the hypha is not saved yet. You can preview the changes ↓

    -
    `) +
    `) //line views/mutators.qtpl:37 qw422016.N().S(renderedPage) //line views/mutators.qtpl:37 - qw422016.N().S(`
    + qw422016.N().S(` `) diff --git a/views/readers.qtpl b/views/readers.qtpl index 08e6eb9..cf7c478 100644 --- a/views/readers.qtpl +++ b/views/readers.qtpl @@ -108,7 +108,6 @@ If `contents` == "", a helpful message is shown instead. {%= SubhyphaeHTML(subhyphae) %} {%= RelativeHyphaeHTML(relatives) %} -{%= BackLinksHTML(h) %} {% endfunc %} diff --git a/views/readers.qtpl.go b/views/readers.qtpl.go index f9921e4..ef9c851 100644 --- a/views/readers.qtpl.go +++ b/views/readers.qtpl.go @@ -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(` `) -//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(`

    Please note that viewing binary parts of hyphae is not supported in history for now.

    `) -//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(`
    `) -//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(`
    `) -//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(`
    `) -//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 } diff --git a/views/stuff.qtpl b/views/stuff.qtpl index 4359734..f1ce50f 100644 --- a/views/stuff.qtpl +++ b/views/stuff.qtpl @@ -97,7 +97,7 @@ for u := range user.YieldUsers() {

    About {%s util.SiteName %}