1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
mycorrhiza
|
||||
hyphae/*.gog
|
||||
|
5
Makefile
@ -1,9 +1,12 @@
|
||||
run: build
|
||||
./mycorrhiza metarrhiza
|
||||
|
||||
run_with_fixed_auth: build
|
||||
auth_run: build
|
||||
./mycorrhiza -auth-method fixed metarrhiza
|
||||
|
||||
gemini_run: build
|
||||
./mycorrhiza -gemini-cert-path "." metarrhiza
|
||||
|
||||
build:
|
||||
go generate
|
||||
go build .
|
||||
|
@ -1,4 +1,4 @@
|
||||
# 🍄 MycorrhizaWiki 0.12
|
||||
# 🍄 MycorrhizaWiki 0.13
|
||||
A wiki engine.
|
||||
|
||||
[Main wiki](https://mycorrhiza.lesarbr.es)
|
||||
@ -25,6 +25,8 @@ Options:
|
||||
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
|
||||
@ -57,6 +59,7 @@ Options:
|
||||
* Hyphae can be renamed (recursive renaming of subhyphae is also supported)
|
||||
* Light on resources
|
||||
* Authorization with pre-set credentials
|
||||
* Basic Gemini protocol support
|
||||
|
||||
## Contributing
|
||||
Help is always needed. We have a [tg chat](https://t.me/mycorrhizadev) where some development is coordinated. You can also sponsor on [boosty](https://boosty.to/bouncepaw). Feel free to open an issue or contact directly.
|
||||
|
@ -1,58 +1,124 @@
|
||||
// Code generated by qtc from "asset.qtpl". DO NOT EDIT.
|
||||
// Code generated by qtc from "assets.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line templates/asset.qtpl:1
|
||||
package templates
|
||||
//line assets/assets.qtpl:1
|
||||
package assets
|
||||
|
||||
//line templates/asset.qtpl:1
|
||||
//line assets/assets.qtpl:1
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line templates/asset.qtpl:1
|
||||
//line assets/assets.qtpl:1
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line templates/asset.qtpl:1
|
||||
//line assets/assets.qtpl:1
|
||||
func StreamDefaultCSS(qw422016 *qt422016.Writer) {
|
||||
//line templates/asset.qtpl:1
|
||||
//line assets/assets.qtpl:1
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/asset.qtpl:2
|
||||
qw422016.N().S(`/* Layout stuff */
|
||||
@media screen and (min-width: 800px) {
|
||||
main { padding:1rem 2rem; margin: 0 auto; width: 800px; }
|
||||
.hypha-tabs { padding: 1rem 2rem; margin: 0 auto; width: 800px; }
|
||||
header { margin: 0 auto; width: 800px; }
|
||||
//line assets/assets.qtpl:2
|
||||
qw422016.N().S(`.amnt-grid { display: grid; grid-template-columns: 1fr 1fr; }
|
||||
.upload-binary__input { display: block; margin: .25rem 0; }
|
||||
|
||||
.modal__title { font-size: 2rem; }
|
||||
.modal__title_small { font-size: 1.5rem; }
|
||||
.modal__confirmation-msg { margin: 0 0 .5rem 0; }
|
||||
.modal__action { display: inline-block; font-size: 1rem; padding: .25rem; border-radius: .25rem; }
|
||||
.modal__submit { border: 1px #999 solid; }
|
||||
.modal__cancel { border: 1px #999 dashed; text-decoration: none; }
|
||||
|
||||
.hypha-list { padding-left: 0; }
|
||||
.hypha-list__entry { list-style-type: none; }
|
||||
.hypha-list__link { text-decoration: none; display: inline-block; padding: .25rem; }
|
||||
.hypha-list__link:hover { text-decoration: underline; }
|
||||
.hypha-list__amnt-type { font-size: smaller; color: #999; }
|
||||
|
||||
/* General element positions, from small to big */
|
||||
/* Phones and whatnot */
|
||||
.layout { display: grid; row-gap: 1rem; }
|
||||
header { width: 100%; margin-bottom: 1rem; }
|
||||
.header-links__list, .hypha-tabs__flex { margin: 0; padding: 0; display: flex; flex-wrap: wrap; }
|
||||
.header-links__entry, .hypha-tabs__tab { list-style-type: none; }
|
||||
|
||||
.header-links__entry { margin-right: .5rem; }
|
||||
.header-links__entry_user { font-style:italic; }
|
||||
.header-links__link { display: inline-block; padding: .25rem; text-decoration: none; }
|
||||
|
||||
.hypha-tabs { padding: 0; margin: 0; }
|
||||
.hypha-tabs__tab { margin-right: .5rem; padding: 0; }
|
||||
.hypha-tabs__link { display: inline-block; padding: .25rem; text-decoration: none; }
|
||||
.hypha-tabs__selection { display: inline-block; padding: .25rem; font-weight: bold; }
|
||||
|
||||
.layout-card li { list-style-type: none; }
|
||||
.backlinks__list { padding: 0; margin: 0; }
|
||||
.backlinks__link { text-decoration: none; display: block; padding: .25rem; padding-left: 1.25rem; }
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.amnt-grid { grid-template-columns: 1fr; }
|
||||
.layout { grid-template-column: auto; grid-template-row: auto auto auto; }
|
||||
.main-width { width: 100%; }
|
||||
main { padding: 1rem; margin: 0; }
|
||||
}
|
||||
|
||||
/* No longer a phone but still small screen: draw normal tabs, center main */
|
||||
@media screen and (min-width: 801px) {
|
||||
.main-width { padding: 1rem 2rem; width: 800px; margin: 0 auto; }
|
||||
main { border-radius: .25rem; }
|
||||
.layout-card { width: 800px; margin: 0 auto; }
|
||||
|
||||
.header-links { padding: 0; }
|
||||
.header-links__entry { margin-right: 1.5rem; }
|
||||
.header-links__entry_user { margin: 0 2rem 0 auto; }
|
||||
.header-links__entry:nth-of-type(1),
|
||||
.hypha-tabs__tab:nth-of-type(1) { margin-left: 2rem; }
|
||||
.hypha-tabs__tab { margin-right: 1.5rem; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.2); border-bottom: 2px #ddd solid; padding: 0 .5rem; }
|
||||
|
||||
.hypha-tabs { padding: 0; }
|
||||
.hypha-tabs__tab { border-radius: .25rem .25rem 0 0; margin-right: 0; }
|
||||
.hypha-tabs__selection, .hypha-tabs__link { padding: .25rem .5rem; }
|
||||
|
||||
.header-links__entry:nth-of-type(1), .hypha-tabs__tab:nth-of-type(1) { margin-left: 2rem; }
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
main { padding: 1rem; margin: 0; width: 100%; }
|
||||
.hypha-tabs{ padding: 1rem; margin: 0; width: 100%; }
|
||||
.hypha-tabs__tab { box-shadow: none; margin-right: .5rem; padding: .25rem .5rem; }
|
||||
header { width: 100%; }
|
||||
.header-links__entry { margin-right: .5rem; }
|
||||
|
||||
/* Wide enough to fit two columns ok */
|
||||
@media screen and (min-width: 1100px) {
|
||||
.layout { display: grid; grid-template-columns: auto 1fr; column-gap: 1rem; margin: 0 1rem; row-gap: 1rem; }
|
||||
.main-width { margin: 0; }
|
||||
main { grid-column: 1 / span 1; grid-row: 1 / span 2; }
|
||||
.relative-hyphae { grid-column: 2 / span 1; grid-row: 1 / span 1; }
|
||||
.layout-card { width: 100%; }
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1250px) {
|
||||
.layout { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); }
|
||||
.layout-card { max-width: 16rem; }
|
||||
.main-width { margin: 0 auto; }
|
||||
.backlinks { grid-column: 1 / span 1; margin-right: 0; }
|
||||
main { grid-column: 2 / span 1; }
|
||||
.relative-hyphae { grid-column: 3 / span 1; margin-left: 0; }
|
||||
|
||||
.backlinks__title { text-align: right; }
|
||||
.backlinks__link { text-align: right; padding-right: 1.25rem; padding-left: .25rem; }
|
||||
}
|
||||
|
||||
*, *::before, *::after {box-sizing: border-box;}
|
||||
html { height:100%; padding:0; }
|
||||
body {height:100%; margin:0; font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;}
|
||||
main {border-radius: 0 0 .25rem .25rem; }
|
||||
body {height:100%; margin:0; }
|
||||
body, input { font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;}
|
||||
main > form {margin-bottom:1rem;}
|
||||
textarea {font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;}
|
||||
.edit_no-preview {height:100%;}
|
||||
.edit_with-preview .edit-form textarea { min-height: 500px; }
|
||||
|
||||
.edit { min-height: 80vh; }
|
||||
.edit__title { margin-top: 0; }
|
||||
.edit__preview { border: 2px dashed #ddd; }
|
||||
.edit-form {height:90%;}
|
||||
.edit-form textarea {width:100%;height:90%;}
|
||||
.edit-form {height:70vh;}
|
||||
.edit-form textarea {width:100%;height:95%;}
|
||||
.edit-form__save { font-weight: bold; }
|
||||
|
||||
.icon {margin-right: .25rem; vertical-align: bottom; }
|
||||
|
||||
main h1:not(.navi-title) {font-size:1.7rem;}
|
||||
@ -67,7 +133,10 @@ blockquote { margin-left: 0; padding-left: 1rem; }
|
||||
.wikilink_mailto::before { content: url("/static/icon/mailto"); }
|
||||
|
||||
article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
|
||||
article h1, article h2, article h3, article h4, article h5, article h6 { margin: 1.5rem 0 0 0; }
|
||||
main h1, main h2, main h3, main h4, main h5, main h6 { margin: 1.5rem 0 0 0; }
|
||||
.heading__link { text-decoration: none; display: inline-block; }
|
||||
.heading__link::after { width: 1rem; content: "§"; color: transparent; }
|
||||
.heading__link:hover::after, .heading__link:active::after { color: #999; }
|
||||
article p { margin: .5rem 0; }
|
||||
article ul, ol { padding-left: 1.5rem; margin: .5rem 0; }
|
||||
article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; }
|
||||
@ -85,6 +154,7 @@ article pre.codeblock { padding:.5rem; white-space: pre-wrap; border-radius: .25
|
||||
.binary-container_with-video video,
|
||||
.binary-container_with-audio audio {width: 100%}
|
||||
|
||||
.subhyphae__title { padding-bottom: .5rem; clear: both; }
|
||||
.navi-title { padding-bottom: .5rem; margin: .25rem 0; }
|
||||
.navi-title a {text-decoration:none; }
|
||||
.navi-title__separator { margin: 0 .25rem; }
|
||||
@ -101,19 +171,11 @@ figcaption { padding-bottom: .5rem; }
|
||||
|
||||
#new-name {width:100%;}
|
||||
|
||||
header { margin-bottom: .5rem; }
|
||||
.header-links__entry_user { font-style:italic; }
|
||||
.header-links__link { text-decoration: none; display: block; width: 100%; height: 100%; padding: .25rem; }
|
||||
.hypha-tabs { padding: 0; }
|
||||
.header-links__list, .hypha-tabs__flex { margin: 0; padding: 0; display: flex; flex-wrap: wrap; }
|
||||
.header-links__entry, .hypha-tabs__tab { list-style-type: none; }
|
||||
.hypha-tabs__tab a { text-decoration: none; }
|
||||
.hypha-tabs__tab_active { font-weight: bold; }
|
||||
|
||||
.rc-entry { display: grid; list-style-type: none; padding: .25rem; grid-template-columns: 1fr 1fr; }
|
||||
.rc-entry { display: grid; list-style-type: none; padding: .25rem; grid-template-columns: 1fr 1fr; border-radius: .25rem; }
|
||||
.rc-entry__time { font-style: italic; }
|
||||
.rc-entry__hash { font-style: italic; text-align: right; }
|
||||
.rc-entry__links { grid-column: 1 / span 2; }
|
||||
.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; }
|
||||
@ -132,6 +194,22 @@ table { border: #ddd 1px solid; border-radius: .25rem; min-width: 4rem; }
|
||||
td { padding: .25rem; }
|
||||
caption { caption-side: top; font-size: small; }
|
||||
|
||||
.subhyphae__list, .subhyphae__list ul { display: flex; padding: 0; margin: 0; flex-wrap: wrap; }
|
||||
.subhyphae__entry { list-style-type: none; border: 1px solid #999; padding: 0; margin: .125rem; border-radius: .25rem; }
|
||||
.subhyphae__link { display: block; padding: .25rem; text-decoration: none; }
|
||||
.subhyphae__link:hover { background: #eee; }
|
||||
|
||||
.navitree { padding: 0; margin: 0; }
|
||||
.navitree__entry { }
|
||||
.navitree > .navitree__entry > a::before { display: inline-block; width: .5rem; color: #999; margin: 0 .25rem; }
|
||||
.navitree > .navitree__entry_infertile > a::before { content: " "} /* nbsp, careful */
|
||||
.navitree > .navitree__sibling_fertile > a::before { content: "▸"}
|
||||
.navitree__trunk { border-left: 1px #999 solid; }
|
||||
.navitree__link { text-decoration: none; display: block; padding: .25rem; }
|
||||
.navitree__entry_this > span { display: block; padding: .25rem; font-weight: bold; }
|
||||
.navitree__entry_this > span::before { content: " "; display: inline-block; width: 1rem; }
|
||||
|
||||
|
||||
/* Color stuff */
|
||||
/* Lighter stuff #eee */
|
||||
article code,
|
||||
@ -142,11 +220,24 @@ article .codeblock,
|
||||
.prevnext__el,
|
||||
table { background-color: #eee; }
|
||||
|
||||
.hypha-tabs__tab { background-color: #eee; }
|
||||
.hypha-tabs__tab a { color: black; }
|
||||
.hypha-tabs__tab_active { border-bottom: 2px white solid; background: white; }
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.hypha-tabs { background-color: white; }
|
||||
.hypha-tabs__tab { box-shadow: none; }
|
||||
.hypha-tabs,
|
||||
.hypha-tabs__tab { background-color: white; }
|
||||
}
|
||||
|
||||
@media screen and (min-width: 801px) {
|
||||
.hypha-tabs__tab { border: 1px #ddd solid; }
|
||||
.hypha-tabs__tab_active { border-bottom: 1px white solid; }
|
||||
}
|
||||
|
||||
.layout-card { border-radius: .25rem; background-color: white; }
|
||||
.layout-card__title { font-size: 1rem; margin: 0; padding: .25rem .5rem; border-radius: .25rem .25rem 0 0; }
|
||||
.layout-card__title { background-color: #eee; }
|
||||
|
||||
/* Other stuff */
|
||||
html { background-color: #ddd;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='42' height='44' viewBox='0 0 42 44' xmlns='http://www.w3.org/2000/svg'%3E%3Cg id='Page-1' fill='none' fill-rule='evenodd'%3E%3Cg id='brick-wall' fill='%23bbbbbb' fill-opacity='0.4'%3E%3Cpath d='M0 0h42v44H0V0zm1 1h40v20H1V1zM0 23h20v20H0V23zm22 0h20v20H22V23z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
||||
@ -154,11 +245,7 @@ background-image: url("data:image/svg+xml,%3Csvg width='42' height='44' viewBox=
|
||||
header { background-color: #bbb; }
|
||||
.header-links__link { color: black; }
|
||||
.header-links__link:hover { background-color: #eee; }
|
||||
|
||||
main, .hypha-tabs__tab { background-color: white; }
|
||||
.hypha-tabs__tab { clip-path: inset(-20px -20px 0 -20px); }
|
||||
.hypha-tabs__tab a { color: black; }
|
||||
.hypha-tabs__tab_active { border-bottom: 2px white solid; }
|
||||
main { background-color: white; }
|
||||
|
||||
blockquote { border-left: 4px black solid; }
|
||||
.wikilink_new {color:#a55858;}
|
||||
@ -169,21 +256,24 @@ blockquote { border-left: 4px black solid; }
|
||||
.upload-amnt { border: #eee 1px solid; }
|
||||
td { border: #ddd 1px solid; }
|
||||
|
||||
.navitree__link:hover, .backlinks__link:hover { background-color: #eee; }
|
||||
|
||||
/* Dark theme! */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html { background: #222; color: #ddd; }
|
||||
main, article, .hypha-tabs__tab, header { background-color: #343434; color: #ddd; }
|
||||
main, article, .hypha-tabs__tab, header, .layout-card { background-color: #343434; color: #ddd; }
|
||||
|
||||
a, .wikilink_external { color: #f1fa8c; }
|
||||
a:visited, .wikilink_external:visited { color: #ffb86c; }
|
||||
.wikilink_new, .wikilink_new:visited { color: #dd4444; }
|
||||
.navitree__link:hover, .backlinks__link:hover { background-color: #444; }
|
||||
|
||||
.header-links__link, .header-links__link:visited,
|
||||
.prevnext__el, .prevnext__el:visited { color: #ddd; }
|
||||
.header-links__link:hover { background-color: #444; }
|
||||
|
||||
.hypha-tabs__tab a, .hypha-tabs__tab { color: #ddd; background-color: #232323; border: 0; }
|
||||
.hypha-tabs__tab_active { background-color: #343434; }
|
||||
.layout-card__title, .hypha-tabs__tab_active { background-color: #343434; }
|
||||
|
||||
blockquote { border-left: 4px #ddd solid; }
|
||||
|
||||
@ -206,169 +296,170 @@ mark { background: rgba(130, 80, 30, 5); color: inherit; }
|
||||
}
|
||||
}
|
||||
|
||||
.backlinks { display: none; }
|
||||
`)
|
||||
//line templates/asset.qtpl:2
|
||||
//line assets/assets.qtpl:2
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
func WriteDefaultCSS(qq422016 qtio422016.Writer) {
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
StreamDefaultCSS(qw422016)
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
func DefaultCSS() string {
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
WriteDefaultCSS(qb422016)
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
return qs422016
|
||||
//line templates/asset.qtpl:3
|
||||
//line assets/assets.qtpl:3
|
||||
}
|
||||
|
||||
// Next three are from https://remixicon.com/
|
||||
|
||||
//line templates/asset.qtpl:6
|
||||
//line assets/assets.qtpl:6
|
||||
func StreamIconHTTP(qw422016 *qt422016.Writer) {
|
||||
//line templates/asset.qtpl:6
|
||||
//line assets/assets.qtpl:6
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/asset.qtpl:7
|
||||
//line assets/assets.qtpl:7
|
||||
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 templates/asset.qtpl:7
|
||||
//line assets/assets.qtpl:7
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
func WriteIconHTTP(qq422016 qtio422016.Writer) {
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
StreamIconHTTP(qw422016)
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
func IconHTTP() string {
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
WriteIconHTTP(qb422016)
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
return qs422016
|
||||
//line templates/asset.qtpl:8
|
||||
//line assets/assets.qtpl:8
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:10
|
||||
//line assets/assets.qtpl:10
|
||||
func StreamIconGemini(qw422016 *qt422016.Writer) {
|
||||
//line templates/asset.qtpl:10
|
||||
//line assets/assets.qtpl:10
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/asset.qtpl:11
|
||||
//line assets/assets.qtpl:11
|
||||
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 templates/asset.qtpl:11
|
||||
//line assets/assets.qtpl:11
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
func WriteIconGemini(qq422016 qtio422016.Writer) {
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
StreamIconGemini(qw422016)
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
func IconGemini() string {
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
WriteIconGemini(qb422016)
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
return qs422016
|
||||
//line templates/asset.qtpl:12
|
||||
//line assets/assets.qtpl:12
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:14
|
||||
//line assets/assets.qtpl:14
|
||||
func StreamIconMailto(qw422016 *qt422016.Writer) {
|
||||
//line templates/asset.qtpl:14
|
||||
//line assets/assets.qtpl:14
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/asset.qtpl:15
|
||||
//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 templates/asset.qtpl:15
|
||||
//line assets/assets.qtpl:15
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
func WriteIconMailto(qq422016 qtio422016.Writer) {
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
StreamIconMailto(qw422016)
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
func IconMailto() string {
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
WriteIconMailto(qb422016)
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
return qs422016
|
||||
//line templates/asset.qtpl:16
|
||||
//line assets/assets.qtpl:16
|
||||
}
|
||||
|
||||
// This is a modified version of https://www.svgrepo.com/svg/232085/rat
|
||||
|
||||
//line templates/asset.qtpl:19
|
||||
//line assets/assets.qtpl:19
|
||||
func StreamIconGopher(qw422016 *qt422016.Writer) {
|
||||
//line templates/asset.qtpl:19
|
||||
//line assets/assets.qtpl:19
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/asset.qtpl:20
|
||||
//line assets/assets.qtpl:20
|
||||
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
|
||||
@ -381,34 +472,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 templates/asset.qtpl:20
|
||||
//line assets/assets.qtpl:20
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
func WriteIconGopher(qq422016 qtio422016.Writer) {
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
StreamIconGopher(qw422016)
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
}
|
||||
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
func IconGopher() string {
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
WriteIconGopher(qb422016)
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
return qs422016
|
||||
//line templates/asset.qtpl:21
|
||||
//line assets/assets.qtpl:21
|
||||
}
|
@ -1,33 +1,99 @@
|
||||
/* Layout stuff */
|
||||
@media screen and (min-width: 800px) {
|
||||
main { padding:1rem 2rem; margin: 0 auto; width: 800px; }
|
||||
.hypha-tabs { padding: 1rem 2rem; margin: 0 auto; width: 800px; }
|
||||
header { margin: 0 auto; width: 800px; }
|
||||
.amnt-grid { display: grid; grid-template-columns: 1fr 1fr; }
|
||||
.upload-binary__input { display: block; margin: .25rem 0; }
|
||||
|
||||
.modal__title { font-size: 2rem; }
|
||||
.modal__title_small { font-size: 1.5rem; }
|
||||
.modal__confirmation-msg { margin: 0 0 .5rem 0; }
|
||||
.modal__action { display: inline-block; font-size: 1rem; padding: .25rem; border-radius: .25rem; }
|
||||
.modal__submit { border: 1px #999 solid; }
|
||||
.modal__cancel { border: 1px #999 dashed; text-decoration: none; }
|
||||
|
||||
.hypha-list { padding-left: 0; }
|
||||
.hypha-list__entry { list-style-type: none; }
|
||||
.hypha-list__link { text-decoration: none; display: inline-block; padding: .25rem; }
|
||||
.hypha-list__link:hover { text-decoration: underline; }
|
||||
.hypha-list__amnt-type { font-size: smaller; color: #999; }
|
||||
|
||||
/* General element positions, from small to big */
|
||||
/* Phones and whatnot */
|
||||
.layout { display: grid; row-gap: 1rem; }
|
||||
header { width: 100%; margin-bottom: 1rem; }
|
||||
.header-links__list, .hypha-tabs__flex { margin: 0; padding: 0; display: flex; flex-wrap: wrap; }
|
||||
.header-links__entry, .hypha-tabs__tab { list-style-type: none; }
|
||||
|
||||
.header-links__entry { margin-right: .5rem; }
|
||||
.header-links__entry_user { font-style:italic; }
|
||||
.header-links__link { display: inline-block; padding: .25rem; text-decoration: none; }
|
||||
|
||||
.hypha-tabs { padding: 0; margin: 0; }
|
||||
.hypha-tabs__tab { margin-right: .5rem; padding: 0; }
|
||||
.hypha-tabs__link { display: inline-block; padding: .25rem; text-decoration: none; }
|
||||
.hypha-tabs__selection { display: inline-block; padding: .25rem; font-weight: bold; }
|
||||
|
||||
.layout-card li { list-style-type: none; }
|
||||
.backlinks__list { padding: 0; margin: 0; }
|
||||
.backlinks__link { text-decoration: none; display: block; padding: .25rem; padding-left: 1.25rem; }
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.amnt-grid { grid-template-columns: 1fr; }
|
||||
.layout { grid-template-column: auto; grid-template-row: auto auto auto; }
|
||||
.main-width { width: 100%; }
|
||||
main { padding: 1rem; margin: 0; }
|
||||
}
|
||||
|
||||
/* No longer a phone but still small screen: draw normal tabs, center main */
|
||||
@media screen and (min-width: 801px) {
|
||||
.main-width { padding: 1rem 2rem; width: 800px; margin: 0 auto; }
|
||||
main { border-radius: .25rem; }
|
||||
.layout-card { width: 800px; margin: 0 auto; }
|
||||
|
||||
.header-links { padding: 0; }
|
||||
.header-links__entry { margin-right: 1.5rem; }
|
||||
.header-links__entry_user { margin: 0 2rem 0 auto; }
|
||||
.header-links__entry:nth-of-type(1),
|
||||
.hypha-tabs__tab:nth-of-type(1) { margin-left: 2rem; }
|
||||
.hypha-tabs__tab { margin-right: 1.5rem; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.2); border-bottom: 2px #ddd solid; padding: 0 .5rem; }
|
||||
|
||||
.hypha-tabs { padding: 0; }
|
||||
.hypha-tabs__tab { border-radius: .25rem .25rem 0 0; margin-right: 0; }
|
||||
.hypha-tabs__selection, .hypha-tabs__link { padding: .25rem .5rem; }
|
||||
|
||||
.header-links__entry:nth-of-type(1), .hypha-tabs__tab:nth-of-type(1) { margin-left: 2rem; }
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
main { padding: 1rem; margin: 0; width: 100%; }
|
||||
.hypha-tabs{ padding: 1rem; margin: 0; width: 100%; }
|
||||
.hypha-tabs__tab { box-shadow: none; margin-right: .5rem; padding: .25rem .5rem; }
|
||||
header { width: 100%; }
|
||||
.header-links__entry { margin-right: .5rem; }
|
||||
|
||||
/* Wide enough to fit two columns ok */
|
||||
@media screen and (min-width: 1100px) {
|
||||
.layout { display: grid; grid-template-columns: auto 1fr; column-gap: 1rem; margin: 0 1rem; row-gap: 1rem; }
|
||||
.main-width { margin: 0; }
|
||||
main { grid-column: 1 / span 1; grid-row: 1 / span 2; }
|
||||
.relative-hyphae { grid-column: 2 / span 1; grid-row: 1 / span 1; }
|
||||
.layout-card { width: 100%; }
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1250px) {
|
||||
.layout { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); }
|
||||
.layout-card { max-width: 16rem; }
|
||||
.main-width { margin: 0 auto; }
|
||||
.backlinks { grid-column: 1 / span 1; margin-right: 0; }
|
||||
main { grid-column: 2 / span 1; }
|
||||
.relative-hyphae { grid-column: 3 / span 1; margin-left: 0; }
|
||||
|
||||
.backlinks__title { text-align: right; }
|
||||
.backlinks__link { text-align: right; padding-right: 1.25rem; padding-left: .25rem; }
|
||||
}
|
||||
|
||||
*, *::before, *::after {box-sizing: border-box;}
|
||||
html { height:100%; padding:0; }
|
||||
body {height:100%; margin:0; font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;}
|
||||
main {border-radius: 0 0 .25rem .25rem; }
|
||||
body {height:100%; margin:0; }
|
||||
body, input { font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;}
|
||||
main > form {margin-bottom:1rem;}
|
||||
textarea {font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;}
|
||||
.edit_no-preview {height:100%;}
|
||||
.edit_with-preview .edit-form textarea { min-height: 500px; }
|
||||
|
||||
.edit { min-height: 80vh; }
|
||||
.edit__title { margin-top: 0; }
|
||||
.edit__preview { border: 2px dashed #ddd; }
|
||||
.edit-form {height:90%;}
|
||||
.edit-form textarea {width:100%;height:90%;}
|
||||
.edit-form {height:70vh;}
|
||||
.edit-form textarea {width:100%;height:95%;}
|
||||
.edit-form__save { font-weight: bold; }
|
||||
|
||||
.icon {margin-right: .25rem; vertical-align: bottom; }
|
||||
|
||||
main h1:not(.navi-title) {font-size:1.7rem;}
|
||||
@ -42,7 +108,10 @@ blockquote { margin-left: 0; padding-left: 1rem; }
|
||||
.wikilink_mailto::before { content: url("/static/icon/mailto"); }
|
||||
|
||||
article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
|
||||
article h1, article h2, article h3, article h4, article h5, article h6 { margin: 1.5rem 0 0 0; }
|
||||
main h1, main h2, main h3, main h4, main h5, main h6 { margin: 1.5rem 0 0 0; }
|
||||
.heading__link { text-decoration: none; display: inline-block; }
|
||||
.heading__link::after { width: 1rem; content: "§"; color: transparent; }
|
||||
.heading__link:hover::after, .heading__link:active::after { color: #999; }
|
||||
article p { margin: .5rem 0; }
|
||||
article ul, ol { padding-left: 1.5rem; margin: .5rem 0; }
|
||||
article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; }
|
||||
@ -60,6 +129,7 @@ article pre.codeblock { padding:.5rem; white-space: pre-wrap; border-radius: .25
|
||||
.binary-container_with-video video,
|
||||
.binary-container_with-audio audio {width: 100%}
|
||||
|
||||
.subhyphae__title { padding-bottom: .5rem; clear: both; }
|
||||
.navi-title { padding-bottom: .5rem; margin: .25rem 0; }
|
||||
.navi-title a {text-decoration:none; }
|
||||
.navi-title__separator { margin: 0 .25rem; }
|
||||
@ -76,19 +146,11 @@ figcaption { padding-bottom: .5rem; }
|
||||
|
||||
#new-name {width:100%;}
|
||||
|
||||
header { margin-bottom: .5rem; }
|
||||
.header-links__entry_user { font-style:italic; }
|
||||
.header-links__link { text-decoration: none; display: block; width: 100%; height: 100%; padding: .25rem; }
|
||||
.hypha-tabs { padding: 0; }
|
||||
.header-links__list, .hypha-tabs__flex { margin: 0; padding: 0; display: flex; flex-wrap: wrap; }
|
||||
.header-links__entry, .hypha-tabs__tab { list-style-type: none; }
|
||||
.hypha-tabs__tab a { text-decoration: none; }
|
||||
.hypha-tabs__tab_active { font-weight: bold; }
|
||||
|
||||
.rc-entry { display: grid; list-style-type: none; padding: .25rem; grid-template-columns: 1fr 1fr; }
|
||||
.rc-entry { display: grid; list-style-type: none; padding: .25rem; grid-template-columns: 1fr 1fr; border-radius: .25rem; }
|
||||
.rc-entry__time { font-style: italic; }
|
||||
.rc-entry__hash { font-style: italic; text-align: right; }
|
||||
.rc-entry__links { grid-column: 1 / span 2; }
|
||||
.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; }
|
||||
@ -107,6 +169,22 @@ table { border: #ddd 1px solid; border-radius: .25rem; min-width: 4rem; }
|
||||
td { padding: .25rem; }
|
||||
caption { caption-side: top; font-size: small; }
|
||||
|
||||
.subhyphae__list, .subhyphae__list ul { display: flex; padding: 0; margin: 0; flex-wrap: wrap; }
|
||||
.subhyphae__entry { list-style-type: none; border: 1px solid #999; padding: 0; margin: .125rem; border-radius: .25rem; }
|
||||
.subhyphae__link { display: block; padding: .25rem; text-decoration: none; }
|
||||
.subhyphae__link:hover { background: #eee; }
|
||||
|
||||
.navitree { padding: 0; margin: 0; }
|
||||
.navitree__entry { }
|
||||
.navitree > .navitree__entry > a::before { display: inline-block; width: .5rem; color: #999; margin: 0 .25rem; }
|
||||
.navitree > .navitree__entry_infertile > a::before { content: " "} /* nbsp, careful */
|
||||
.navitree > .navitree__sibling_fertile > a::before { content: "▸"}
|
||||
.navitree__trunk { border-left: 1px #999 solid; }
|
||||
.navitree__link { text-decoration: none; display: block; padding: .25rem; }
|
||||
.navitree__entry_this > span { display: block; padding: .25rem; font-weight: bold; }
|
||||
.navitree__entry_this > span::before { content: " "; display: inline-block; width: 1rem; }
|
||||
|
||||
|
||||
/* Color stuff */
|
||||
/* Lighter stuff #eee */
|
||||
article code,
|
||||
@ -117,11 +195,24 @@ article .codeblock,
|
||||
.prevnext__el,
|
||||
table { background-color: #eee; }
|
||||
|
||||
.hypha-tabs__tab { background-color: #eee; }
|
||||
.hypha-tabs__tab a { color: black; }
|
||||
.hypha-tabs__tab_active { border-bottom: 2px white solid; background: white; }
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.hypha-tabs { background-color: white; }
|
||||
.hypha-tabs__tab { box-shadow: none; }
|
||||
.hypha-tabs,
|
||||
.hypha-tabs__tab { background-color: white; }
|
||||
}
|
||||
|
||||
@media screen and (min-width: 801px) {
|
||||
.hypha-tabs__tab { border: 1px #ddd solid; }
|
||||
.hypha-tabs__tab_active { border-bottom: 1px white solid; }
|
||||
}
|
||||
|
||||
.layout-card { border-radius: .25rem; background-color: white; }
|
||||
.layout-card__title { font-size: 1rem; margin: 0; padding: .25rem .5rem; border-radius: .25rem .25rem 0 0; }
|
||||
.layout-card__title { background-color: #eee; }
|
||||
|
||||
/* Other stuff */
|
||||
html { background-color: #ddd;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='42' height='44' viewBox='0 0 42 44' xmlns='http://www.w3.org/2000/svg'%3E%3Cg id='Page-1' fill='none' fill-rule='evenodd'%3E%3Cg id='brick-wall' fill='%23bbbbbb' fill-opacity='0.4'%3E%3Cpath d='M0 0h42v44H0V0zm1 1h40v20H1V1zM0 23h20v20H0V23zm22 0h20v20H22V23z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
||||
@ -129,11 +220,7 @@ background-image: url("data:image/svg+xml,%3Csvg width='42' height='44' viewBox=
|
||||
header { background-color: #bbb; }
|
||||
.header-links__link { color: black; }
|
||||
.header-links__link:hover { background-color: #eee; }
|
||||
|
||||
main, .hypha-tabs__tab { background-color: white; }
|
||||
.hypha-tabs__tab { clip-path: inset(-20px -20px 0 -20px); }
|
||||
.hypha-tabs__tab a { color: black; }
|
||||
.hypha-tabs__tab_active { border-bottom: 2px white solid; }
|
||||
main { background-color: white; }
|
||||
|
||||
blockquote { border-left: 4px black solid; }
|
||||
.wikilink_new {color:#a55858;}
|
||||
@ -144,21 +231,24 @@ blockquote { border-left: 4px black solid; }
|
||||
.upload-amnt { border: #eee 1px solid; }
|
||||
td { border: #ddd 1px solid; }
|
||||
|
||||
.navitree__link:hover, .backlinks__link:hover { background-color: #eee; }
|
||||
|
||||
/* Dark theme! */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html { background: #222; color: #ddd; }
|
||||
main, article, .hypha-tabs__tab, header { background-color: #343434; color: #ddd; }
|
||||
main, article, .hypha-tabs__tab, header, .layout-card { background-color: #343434; color: #ddd; }
|
||||
|
||||
a, .wikilink_external { color: #f1fa8c; }
|
||||
a:visited, .wikilink_external:visited { color: #ffb86c; }
|
||||
.wikilink_new, .wikilink_new:visited { color: #dd4444; }
|
||||
.navitree__link:hover, .backlinks__link:hover { background-color: #444; }
|
||||
|
||||
.header-links__link, .header-links__link:visited,
|
||||
.prevnext__el, .prevnext__el:visited { color: #ddd; }
|
||||
.header-links__link:hover { background-color: #444; }
|
||||
|
||||
.hypha-tabs__tab a, .hypha-tabs__tab { color: #ddd; background-color: #232323; border: 0; }
|
||||
.hypha-tabs__tab_active { background-color: #343434; }
|
||||
.layout-card__title, .hypha-tabs__tab_active { background-color: #343434; }
|
||||
|
||||
blockquote { border-left: 4px #ddd solid; }
|
||||
|
||||
@ -181,3 +271,4 @@ mark { background: rgba(130, 80, 30, 5); color: inherit; }
|
||||
}
|
||||
}
|
||||
|
||||
.backlinks { display: none; }
|
Before Width: | Height: | Size: 473 B After Width: | Height: | Size: 473 B |
Before Width: | Height: | Size: 951 B After Width: | Height: | Size: 951 B |
Before Width: | Height: | Size: 627 B After Width: | Height: | Size: 627 B |
Before Width: | Height: | Size: 261 B After Width: | Height: | Size: 261 B |
7
flag.go
@ -19,6 +19,7 @@ func init() {
|
||||
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.")
|
||||
}
|
||||
|
||||
// Do the things related to cli args and die maybe
|
||||
@ -41,9 +42,9 @@ func parseCliArgs() {
|
||||
util.URL = "http://0.0.0.0:" + util.ServerPort
|
||||
}
|
||||
|
||||
util.HomePage = CanonicalName(util.HomePage)
|
||||
util.UserHypha = CanonicalName(util.UserHypha)
|
||||
util.HeaderLinksHypha = CanonicalName(util.HeaderLinksHypha)
|
||||
util.HomePage = util.CanonicalName(util.HomePage)
|
||||
util.UserHypha = util.CanonicalName(util.UserHypha)
|
||||
util.HeaderLinksHypha = util.CanonicalName(util.HeaderLinksHypha)
|
||||
|
||||
switch util.AuthMethod {
|
||||
case "none":
|
||||
|
84
gemini.go
Normal file
@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509/pkix"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"git.sr.ht/~adnano/go-gemini"
|
||||
"git.sr.ht/~adnano/go-gemini/certificate"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func geminiHomeHypha(w *gemini.ResponseWriter, rq *gemini.Request) {
|
||||
log.Println(rq.URL)
|
||||
w.Write([]byte(`# MycorrhizaWiki
|
||||
|
||||
You have successfully served the wiki through Gemini. Currently, support is really work-in-progress; you should resort to using Mycorrhiza through the web protocols.
|
||||
|
||||
Visit home hypha:
|
||||
=> /hypha/` + util.HomePage))
|
||||
}
|
||||
|
||||
func geminiHypha(w *gemini.ResponseWriter, rq *gemini.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = geminiHyphaNameFromRq(rq, "page", "hypha")
|
||||
h = hyphae.ByName(hyphaName)
|
||||
hasAmnt = h.Exists && h.BinaryPath != ""
|
||||
contents string
|
||||
)
|
||||
if h.Exists {
|
||||
fileContentsT, errT := ioutil.ReadFile(h.TextPath)
|
||||
if errT == nil {
|
||||
md := markup.Doc(hyphaName, string(fileContentsT))
|
||||
contents = md.AsGemtext()
|
||||
}
|
||||
}
|
||||
if hasAmnt {
|
||||
w.Write([]byte("This hypha has an attachment\n"))
|
||||
}
|
||||
w.Write([]byte(contents))
|
||||
}
|
||||
|
||||
func handleGemini() {
|
||||
if util.GeminiCertPath == "" {
|
||||
return
|
||||
}
|
||||
certPath, err := filepath.Abs(util.GeminiCertPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var server gemini.Server
|
||||
server.ReadTimeout = 30 * time.Second
|
||||
server.WriteTimeout = 1 * time.Minute
|
||||
if err := server.Certificates.Load(certPath); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
server.CreateCertificate = func(hostname string) (tls.Certificate, error) {
|
||||
return certificate.Create(certificate.CreateOptions{
|
||||
Subject: pkix.Name{
|
||||
CommonName: hostname,
|
||||
},
|
||||
DNSNames: []string{hostname},
|
||||
Duration: 365 * 24 * time.Hour,
|
||||
})
|
||||
}
|
||||
|
||||
var mux gemini.ServeMux
|
||||
mux.HandleFunc("/", geminiHomeHypha)
|
||||
mux.HandleFunc("/hypha/", geminiHypha)
|
||||
mux.HandleFunc("/page/", geminiHypha)
|
||||
|
||||
server.Handle("localhost", &mux)
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
2
go.mod
@ -3,8 +3,10 @@ module github.com/bouncepaw/mycorrhiza
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
git.sr.ht/~adnano/go-gemini v0.1.13
|
||||
github.com/adrg/xdg v0.2.2
|
||||
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
@ -1,3 +1,5 @@
|
||||
git.sr.ht/~adnano/go-gemini v0.1.13 h1:vzKkkVrOzMpfJ1AAeE/PChg0Rw5Zf+9HrnwsgVxXUT4=
|
||||
git.sr.ht/~adnano/go-gemini v0.1.13/go.mod h1:If1VxEWcZDrRt5FeAFnGTcM2Ud1E3BXs3VJ5rnZWKq0=
|
||||
github.com/adrg/xdg v0.2.2 h1:A7ZHKRz5KGOLJX/bg7IPzStryhvCzAE1wX+KWawPiAo=
|
||||
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=
|
||||
@ -33,3 +35,5 @@ 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=
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
@ -77,8 +76,8 @@ func (rev Revision) TimeString() string {
|
||||
return rev.Time.Format(time.RFC822)
|
||||
}
|
||||
|
||||
// HyphaeLinks returns a comma-separated list of hyphae that were affected by this revision as HTML string.
|
||||
func (rev Revision) HyphaeLinks() (html string) {
|
||||
// HyphaeLinksHTML returns a comma-separated list of hyphae that were affected by this revision as HTML string.
|
||||
func (rev Revision) HyphaeLinksHTML() (html string) {
|
||||
hyphae := rev.hyphaeAffected()
|
||||
for i, hyphaName := range hyphae {
|
||||
if i > 0 {
|
||||
@ -92,7 +91,7 @@ func (rev Revision) HyphaeLinks() (html string) {
|
||||
func (rev *Revision) descriptionForFeed() (html string) {
|
||||
return fmt.Sprintf(
|
||||
`<p>%s</p>
|
||||
<p><b>Hyphae affected:</b> %s</p>`, rev.Message, rev.HyphaeLinks())
|
||||
<p><b>Hyphae affected:</b> %s</p>`, rev.Message, rev.HyphaeLinksHTML())
|
||||
}
|
||||
|
||||
// Try and guess what link is the most important by looking at the message.
|
||||
@ -111,23 +110,6 @@ func (rev *Revision) bestLink() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (rev Revision) RecentChangesEntry() (html string) {
|
||||
if user.AuthUsed && rev.Username != "anon" {
|
||||
return fmt.Sprintf(`
|
||||
<li class="rc-entry__time"><time>%[1]s</time></li>
|
||||
<li class="rc-entry__hash">%[2]s</li>
|
||||
<li class="rc-entry__links">%[5]s</li>
|
||||
<li class="rc-entry__msg">%[6]s <span class="rc-entry__author">by <a href="/page/%[3]s/%[4]s" rel="author">%[4]s</a></span></li>
|
||||
`, rev.TimeString(), rev.Hash, util.UserHypha, rev.Username, rev.HyphaeLinks(), rev.Message)
|
||||
}
|
||||
return fmt.Sprintf(`
|
||||
<li class="rc-entry__time"><time>%[1]s</time></li>
|
||||
<li class="rc-entry__hash">%[2]s</li>
|
||||
<li class="rc-entry__links">%[3]s</li>
|
||||
<li class="rc-entry__msg">%[4]s</li>
|
||||
`, rev.TimeString(), rev.Hash, rev.HyphaeLinks(), rev.Message)
|
||||
}
|
||||
|
||||
// Path to git executable. Set at init()
|
||||
var gitpath string
|
||||
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/templates"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
"github.com/gorilla/feeds"
|
||||
)
|
||||
@ -61,7 +60,7 @@ func RecentChangesJSON() (string, error) {
|
||||
return recentChangesFeed().ToJSON()
|
||||
}
|
||||
|
||||
func RecentChanges(n int) string {
|
||||
func RecentChanges(n int) []Revision {
|
||||
var (
|
||||
out, err = gitsh(
|
||||
"log", "--oneline", "--no-merges",
|
||||
@ -75,11 +74,7 @@ func RecentChanges(n int) string {
|
||||
revs = append(revs, parseRevisionLine(line))
|
||||
}
|
||||
}
|
||||
entries := make([]string, len(revs))
|
||||
for i, rev := range revs {
|
||||
entries[i] = rev.RecentChangesEntry()
|
||||
}
|
||||
return templates.RecentChangesHTML(entries, n)
|
||||
return revs
|
||||
}
|
||||
|
||||
// FileChanged tells you if the file has been changed.
|
||||
@ -177,6 +172,6 @@ func parseRevisionLine(line string) Revision {
|
||||
|
||||
// See how the file with `filepath` looked at commit with `hash`.
|
||||
func FileAtRevision(filepath, hash string) (string, error) {
|
||||
out, err := gitsh("show", hash+":"+filepath)
|
||||
out, err := gitsh("show", hash+":"+strings.TrimPrefix(filepath, util.WikiDir+"/"))
|
||||
return out.String(), err
|
||||
}
|
||||
|
@ -59,12 +59,16 @@ func (hop *HistoryOp) gitop(args ...string) *HistoryOp {
|
||||
return hop
|
||||
}
|
||||
|
||||
// WithError appends the `err` to the list of errors.
|
||||
func (hop *HistoryOp) WithError(err error) *HistoryOp {
|
||||
// WithErr appends the `err` to the list of errors.
|
||||
func (hop *HistoryOp) WithErr(err error) *HistoryOp {
|
||||
hop.Errs = append(hop.Errs, err)
|
||||
return hop
|
||||
}
|
||||
|
||||
func (hop *HistoryOp) WithErrAbort(err error) *HistoryOp {
|
||||
return hop.WithErr(err).Abort()
|
||||
}
|
||||
|
||||
// WithFilesRemoved git-rm-s all passed `paths`. Paths can be rooted or not. Paths that are empty strings are ignored.
|
||||
func (hop *HistoryOp) WithFilesRemoved(paths ...string) *HistoryOp {
|
||||
args := []string{"rm", "--quiet", "--"}
|
||||
@ -134,3 +138,11 @@ func (hop *HistoryOp) WithUser(u *user.User) *HistoryOp {
|
||||
}
|
||||
return hop
|
||||
}
|
||||
|
||||
func (hop *HistoryOp) HasErrors() bool {
|
||||
return len(hop.Errs) > 0
|
||||
}
|
||||
|
||||
func (hop *HistoryOp) FirstErrorText() string {
|
||||
return hop.Errs[0].Error()
|
||||
}
|
||||
|
33
http_admin.go
Normal file
@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/views"
|
||||
)
|
||||
|
||||
// This is not init(), because user.AuthUsed is not set at init-stage.
|
||||
func initAdmin() {
|
||||
if user.AuthUsed {
|
||||
http.HandleFunc("/admin", handlerAdmin)
|
||||
http.HandleFunc("/admin/shutdown", handlerAdminShutdown)
|
||||
}
|
||||
}
|
||||
|
||||
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
if user.CanProceed(rq, "admin") {
|
||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(base("Admin panel", views.AdminPanelHTML(), user.FromRequest(rq))))
|
||||
}
|
||||
}
|
||||
|
||||
func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
if user.CanProceed(rq, "admin/shutdown") && rq.Method == "POST" {
|
||||
log.Fatal("An admin commanded the wiki to shutdown")
|
||||
}
|
||||
}
|
11
http_auth.go
@ -4,8 +4,9 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/templates"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
"github.com/bouncepaw/mycorrhiza/views"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -28,7 +29,7 @@ func handlerLogout(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println("Unknown user tries to log out")
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
}
|
||||
w.Write([]byte(base("Logout?", templates.LogoutHTML(can), u)))
|
||||
w.Write([]byte(base("Logout?", views.LogoutHTML(can), u)))
|
||||
}
|
||||
|
||||
func handlerLogoutConfirm(w http.ResponseWriter, rq *http.Request) {
|
||||
@ -39,12 +40,12 @@ func handlerLogoutConfirm(w http.ResponseWriter, rq *http.Request) {
|
||||
func handlerLoginData(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
username = CanonicalName(rq.PostFormValue("username"))
|
||||
username = util.CanonicalName(rq.PostFormValue("username"))
|
||||
password = rq.PostFormValue("password")
|
||||
err = user.LoginDataHTTP(w, rq, username, password)
|
||||
)
|
||||
if err != "" {
|
||||
w.Write([]byte(base(err, templates.LoginErrorHTML(err), user.EmptyUser())))
|
||||
w.Write([]byte(base(err, views.LoginErrorHTML(err), user.EmptyUser())))
|
||||
} else {
|
||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||
}
|
||||
@ -58,5 +59,5 @@ func handlerLogin(w http.ResponseWriter, rq *http.Request) {
|
||||
} else {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
}
|
||||
w.Write([]byte(base("Login", templates.LoginHTML(), user.EmptyUser())))
|
||||
w.Write([]byte(base("Login", views.LoginHTML(), user.EmptyUser())))
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/templates"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
"github.com/bouncepaw/mycorrhiza/views"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -35,7 +35,7 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println("Found", len(revs), "revisions for", hyphaName)
|
||||
|
||||
util.HTTP200Page(w,
|
||||
base(hyphaName, templates.HistoryHTML(rq, hyphaName, list), user.FromRequest(rq)))
|
||||
base(hyphaName, views.HistoryHTML(rq, hyphaName, list), user.FromRequest(rq)))
|
||||
}
|
||||
|
||||
// Recent changes
|
||||
@ -46,7 +46,7 @@ func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
|
||||
n, err = strconv.Atoi(noPrefix)
|
||||
)
|
||||
if err == nil && n < 101 {
|
||||
util.HTTP200Page(w, base(strconv.Itoa(n)+" recent changes", history.RecentChanges(n), user.FromRequest(rq)))
|
||||
util.HTTP200Page(w, base(strconv.Itoa(n)+" recent changes", views.RecentChangesHTML(n), user.FromRequest(rq)))
|
||||
} else {
|
||||
http.Redirect(w, rq, "/recent-changes/20", http.StatusSeeOther)
|
||||
}
|
||||
|
352
http_mutators.go
@ -5,10 +5,13 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/templates"
|
||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
"github.com/bouncepaw/mycorrhiza/views"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -25,182 +28,140 @@ func init() {
|
||||
http.HandleFunc("/unattach-confirm/", handlerUnattachConfirm)
|
||||
}
|
||||
|
||||
func handlerUnattachAsk(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "unattach-ask")
|
||||
hd, isOld = HyphaStorage[hyphaName]
|
||||
hasAmnt = hd != nil && hd.binaryPath != ""
|
||||
)
|
||||
if !hasAmnt {
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Cannot unattach", "No attachment attached yet, therefore you cannot unattach")
|
||||
log.Println("Rejected (no amnt):", rq.URL)
|
||||
return
|
||||
} else if ok := user.CanProceed(rq, "unattach-confirm"); !ok {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a trusted editor to unattach attachments")
|
||||
log.Println("Rejected (no rights):", rq.URL)
|
||||
return
|
||||
}
|
||||
util.HTTP200Page(w, base("Unattach "+hyphaName+"?", templates.UnattachAskHTML(rq, hyphaName, isOld), user.FromRequest(rq)))
|
||||
}
|
||||
|
||||
func handlerUnattachConfirm(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "unattach-confirm")
|
||||
hyphaData, isOld = HyphaStorage[hyphaName]
|
||||
hasAmnt = hyphaData != nil && hyphaData.binaryPath != ""
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if !u.CanProceed("unattach-confirm") {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a trusted editor to unattach attachments")
|
||||
log.Println("Rejected (no rights):", rq.URL)
|
||||
return
|
||||
}
|
||||
if !hasAmnt {
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Cannot unattach", "No attachment attached yet, therefore you cannot unattach")
|
||||
log.Println("Rejected (no amnt):", rq.URL)
|
||||
return
|
||||
} else if !isOld {
|
||||
// The precondition is to have the hypha in the first place.
|
||||
HttpErr(w, http.StatusPreconditionFailed, hyphaName,
|
||||
"Error: no such hypha",
|
||||
"Could not unattach this hypha because it does not exist")
|
||||
return
|
||||
}
|
||||
if hop := hyphaData.UnattachHypha(hyphaName, u); len(hop.Errs) != 0 {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
"Error: could not unattach hypha",
|
||||
fmt.Sprintf("Could not unattach this hypha due to internal errors. Server errors: <code>%v</code>", hop.Errs))
|
||||
return
|
||||
}
|
||||
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func handlerRenameAsk(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "rename-ask")
|
||||
_, isOld = HyphaStorage[hyphaName]
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if !u.CanProceed("rename-confirm") {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a trusted editor to rename pages.")
|
||||
log.Println("Rejected", rq.URL)
|
||||
return
|
||||
}
|
||||
util.HTTP200Page(w, base("Rename "+hyphaName+"?", templates.RenameAskHTML(rq, hyphaName, isOld), u))
|
||||
}
|
||||
|
||||
func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "rename-confirm")
|
||||
_, isOld = HyphaStorage[hyphaName]
|
||||
newName = CanonicalName(rq.PostFormValue("new-name"))
|
||||
_, newNameIsUsed = HyphaStorage[newName]
|
||||
recursive = rq.PostFormValue("recursive") == "true"
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
switch {
|
||||
case !u.CanProceed("rename-confirm"):
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a trusted editor to rename pages.")
|
||||
log.Println("Rejected", rq.URL)
|
||||
case newNameIsUsed:
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Error: hypha exists",
|
||||
fmt.Sprintf("Hypha named <a href='/page/%s'>%s</a> already exists.", hyphaName, hyphaName))
|
||||
case newName == "":
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Error: no name",
|
||||
"No new name is given.")
|
||||
case !isOld:
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Error: no such hypha",
|
||||
"Cannot rename a hypha that does not exist yet.")
|
||||
case !HyphaPattern.MatchString(newName):
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Error: invalid name",
|
||||
"Invalid new name. Names cannot contain characters <code>^?!:#@><*|\"\\'&%</code>")
|
||||
default:
|
||||
if hop := RenameHypha(hyphaName, newName, recursive, u); len(hop.Errs) != 0 {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
"Error: could not rename hypha",
|
||||
fmt.Sprintf("Could not rename this hypha due to an internal error. Server errors: <code>%v</code>", hop.Errs))
|
||||
} else {
|
||||
http.Redirect(w, rq, "/page/"+newName, http.StatusSeeOther)
|
||||
func factoryHandlerAsker(
|
||||
actionPath string,
|
||||
asker func(*user.User, *hyphae.Hypha) (error, string),
|
||||
succTitleTemplate string,
|
||||
succPageTemplate func(*http.Request, string, bool) string,
|
||||
) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, actionPath)
|
||||
h = hyphae.ByName(hyphaName)
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if err, errtitle := asker(u, h); err != nil {
|
||||
HttpErr(
|
||||
w,
|
||||
http.StatusInternalServerError,
|
||||
hyphaName,
|
||||
errtitle,
|
||||
err.Error())
|
||||
return
|
||||
}
|
||||
util.HTTP200Page(
|
||||
w,
|
||||
base(
|
||||
fmt.Sprintf(succTitleTemplate, hyphaName),
|
||||
succPageTemplate(rq, hyphaName, h.Exists),
|
||||
u))
|
||||
}
|
||||
}
|
||||
|
||||
// handlerDeleteAsk shows a delete dialog.
|
||||
func handlerDeleteAsk(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "delete-ask")
|
||||
_, isOld = HyphaStorage[hyphaName]
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if !u.CanProceed("delete-ask") {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a moderator to delete pages.")
|
||||
log.Println("Rejected", rq.URL)
|
||||
return
|
||||
var handlerUnattachAsk = factoryHandlerAsker(
|
||||
"unattach-ask",
|
||||
shroom.CanUnattach,
|
||||
"Unattach %s?",
|
||||
views.UnattachAskHTML,
|
||||
)
|
||||
|
||||
var handlerDeleteAsk = factoryHandlerAsker(
|
||||
"delete-ask",
|
||||
shroom.CanDelete,
|
||||
"Delete %s?",
|
||||
views.DeleteAskHTML,
|
||||
)
|
||||
|
||||
var handlerRenameAsk = factoryHandlerAsker(
|
||||
"rename-ask",
|
||||
shroom.CanRename,
|
||||
"Rename %s?",
|
||||
views.RenameAskHTML,
|
||||
)
|
||||
|
||||
func factoryHandlerConfirmer(
|
||||
actionPath string,
|
||||
confirmer func(*hyphae.Hypha, *user.User, *http.Request) (*history.HistoryOp, string),
|
||||
) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, actionPath)
|
||||
h = hyphae.ByName(hyphaName)
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if hop, errtitle := confirmer(h, u, rq); hop.HasErrors() {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
errtitle,
|
||||
hop.FirstErrorText())
|
||||
return
|
||||
}
|
||||
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
|
||||
}
|
||||
util.HTTP200Page(w, base("Delete "+hyphaName+"?", templates.DeleteAskHTML(rq, hyphaName, isOld), u))
|
||||
}
|
||||
|
||||
// handlerDeleteConfirm deletes a hypha for sure
|
||||
func handlerDeleteConfirm(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "delete-confirm")
|
||||
hyphaData, isOld = HyphaStorage[hyphaName]
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if !u.CanProceed("delete-confirm") {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a moderator to delete pages.")
|
||||
log.Println("Rejected", rq.URL)
|
||||
return
|
||||
}
|
||||
if !isOld {
|
||||
// The precondition is to have the hypha in the first place.
|
||||
HttpErr(w, http.StatusPreconditionFailed, hyphaName,
|
||||
"Error: no such hypha",
|
||||
"Could not delete this hypha because it does not exist.")
|
||||
return
|
||||
}
|
||||
if hop := hyphaData.DeleteHypha(hyphaName, u); len(hop.Errs) != 0 {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
"Error: could not delete hypha",
|
||||
fmt.Sprintf("Could not delete this hypha due to internal errors. Server errors: <code>%v</code>", hop.Errs))
|
||||
return
|
||||
}
|
||||
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
||||
}
|
||||
var handlerUnattachConfirm = factoryHandlerConfirmer(
|
||||
"unattach-confirm",
|
||||
func(h *hyphae.Hypha, u *user.User, _ *http.Request) (*history.HistoryOp, string) {
|
||||
return shroom.UnattachHypha(u, h)
|
||||
},
|
||||
)
|
||||
|
||||
var handlerDeleteConfirm = factoryHandlerConfirmer(
|
||||
"delete-confirm",
|
||||
func(h *hyphae.Hypha, u *user.User, _ *http.Request) (*history.HistoryOp, string) {
|
||||
return shroom.DeleteHypha(u, h)
|
||||
},
|
||||
)
|
||||
|
||||
var handlerRenameConfirm = factoryHandlerConfirmer(
|
||||
"rename-confirm",
|
||||
func(oldHypha *hyphae.Hypha, u *user.User, rq *http.Request) (*history.HistoryOp, string) {
|
||||
var (
|
||||
newName = util.CanonicalName(rq.PostFormValue("new-name"))
|
||||
recursive = rq.PostFormValue("recursive") == "true"
|
||||
newHypha = hyphae.ByName(newName)
|
||||
)
|
||||
return shroom.RenameHypha(oldHypha, newHypha, recursive, u)
|
||||
},
|
||||
)
|
||||
|
||||
// handlerEdit shows the edit form. It doesn't edit anything actually.
|
||||
func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "edit")
|
||||
hyphaData, isOld = HyphaStorage[hyphaName]
|
||||
warning string
|
||||
textAreaFill string
|
||||
err error
|
||||
u = user.FromRequest(rq)
|
||||
hyphaName = HyphaNameFromRq(rq, "edit")
|
||||
h = hyphae.ByName(hyphaName)
|
||||
warning string
|
||||
textAreaFill string
|
||||
err error
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if !u.CanProceed("edit") {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be an editor to edit pages.")
|
||||
log.Println("Rejected", rq.URL)
|
||||
if err, errtitle := shroom.CanEdit(u, h); err != nil {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
errtitle,
|
||||
err.Error())
|
||||
return
|
||||
}
|
||||
if isOld {
|
||||
textAreaFill, err = FetchTextPart(hyphaData)
|
||||
if h.Exists {
|
||||
textAreaFill, err = shroom.FetchTextPart(h)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", "Could not fetch text data")
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
"Error",
|
||||
"Could not fetch text data")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
warning = `<p>You are creating a new hypha.</p>`
|
||||
warning = `<p class="warning warning_new-hypha">You are creating a new hypha.</p>`
|
||||
}
|
||||
util.HTTP200Page(w, base("Edit "+hyphaName, templates.EditHTML(rq, hyphaName, textAreaFill, warning), u))
|
||||
util.HTTP200Page(
|
||||
w,
|
||||
base(
|
||||
"Edit "+hyphaName,
|
||||
views.EditHTML(rq, hyphaName, textAreaFill, warning),
|
||||
u))
|
||||
}
|
||||
|
||||
// handlerUploadText uploads a new text part for the hypha.
|
||||
@ -208,62 +169,79 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "upload-text")
|
||||
h = hyphae.ByName(hyphaName)
|
||||
textData = rq.PostFormValue("text")
|
||||
action = rq.PostFormValue("action")
|
||||
u = user.FromRequest(rq)
|
||||
hop *history.HistoryOp
|
||||
errtitle string
|
||||
)
|
||||
if !u.CanProceed("upload-text") {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be an editor to edit pages.")
|
||||
log.Println("Rejected", rq.URL)
|
||||
return
|
||||
}
|
||||
if textData == "" {
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Error", "No text data passed")
|
||||
return
|
||||
|
||||
if action != "Preview" {
|
||||
hop, errtitle = shroom.UploadText(h, []byte(textData), u)
|
||||
if hop.HasErrors() {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName,
|
||||
errtitle,
|
||||
hop.FirstErrorText())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if action == "Preview" {
|
||||
util.HTTP200Page(w, base("Preview "+hyphaName, templates.PreviewHTML(rq, hyphaName, textData, "", markup.Doc(hyphaName, textData).AsHTML()), u))
|
||||
} else if hop := UploadText(hyphaName, textData, u); len(hop.Errs) != 0 {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", hop.Errs[0].Error())
|
||||
util.HTTP200Page(
|
||||
w,
|
||||
base(
|
||||
"Preview "+hyphaName,
|
||||
views.PreviewHTML(
|
||||
rq,
|
||||
hyphaName,
|
||||
textData,
|
||||
"",
|
||||
markup.Doc(hyphaName, textData).AsHTML()),
|
||||
u))
|
||||
} else {
|
||||
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
||||
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
|
||||
// handlerUploadBinary uploads a new binary part for the hypha.
|
||||
func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "upload-binary")
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if !u.CanProceed("upload-binary") {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be an editor to upload attachments.")
|
||||
log.Println("Rejected", rq.URL)
|
||||
return
|
||||
}
|
||||
|
||||
rq.ParseMultipartForm(10 << 20) // Set upload limit
|
||||
file, handler, err := rq.FormFile("binary")
|
||||
if file != nil {
|
||||
defer file.Close()
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "upload-binary")
|
||||
h = hyphae.ByName(hyphaName)
|
||||
u = user.FromRequest(rq)
|
||||
file, handler, err = rq.FormFile("binary")
|
||||
)
|
||||
if err != nil {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
"Error",
|
||||
err.Error())
|
||||
}
|
||||
if err, errtitle := shroom.CanAttach(u, h); err != nil {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
errtitle,
|
||||
err.Error())
|
||||
}
|
||||
|
||||
// If file is not passed:
|
||||
if err != nil {
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Error", "No binary data passed")
|
||||
return
|
||||
}
|
||||
|
||||
// If file is passed:
|
||||
if file != nil {
|
||||
defer file.Close()
|
||||
}
|
||||
var (
|
||||
mime = handler.Header.Get("Content-Type")
|
||||
hop = UploadBinary(hyphaName, mime, file, u)
|
||||
mime = handler.Header.Get("Content-Type")
|
||||
hop, errtitle = shroom.UploadBinary(h, mime, file, u)
|
||||
)
|
||||
|
||||
if len(hop.Errs) != 0 {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", hop.Errs[0].Error())
|
||||
if hop.HasErrors() {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName, errtitle, hop.FirstErrorText())
|
||||
return
|
||||
}
|
||||
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
||||
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
|
||||
}
|
||||
|
@ -10,18 +10,35 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/templates"
|
||||
"github.com/bouncepaw/mycorrhiza/tree"
|
||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
"github.com/bouncepaw/mycorrhiza/views"
|
||||
)
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/page/", handlerPage)
|
||||
http.HandleFunc("/page/", handlerHypha)
|
||||
http.HandleFunc("/hypha/", handlerHypha)
|
||||
http.HandleFunc("/text/", handlerText)
|
||||
http.HandleFunc("/binary/", handlerBinary)
|
||||
http.HandleFunc("/rev/", handlerRevision)
|
||||
http.HandleFunc("/attachment/", handlerAttachment)
|
||||
}
|
||||
|
||||
func handlerAttachment(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "attachment")
|
||||
h = hyphae.ByName(hyphaName)
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
util.HTTP200Page(w,
|
||||
views.BaseHTML(
|
||||
fmt.Sprintf("Attachment of %s", util.BeautifulName(hyphaName)),
|
||||
views.AttachmentMenuHTML(rq, h, u),
|
||||
u))
|
||||
}
|
||||
|
||||
// handlerRevision displays a specific revision of text part a page
|
||||
@ -31,37 +48,34 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
||||
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/rev/")
|
||||
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
||||
revHash = shorterUrl[:firstSlashIndex]
|
||||
hyphaName = CanonicalName(shorterUrl[firstSlashIndex+1:])
|
||||
hyphaName = util.CanonicalName(shorterUrl[firstSlashIndex+1:])
|
||||
h = hyphae.ByName(hyphaName)
|
||||
contents = fmt.Sprintf(`<p>This hypha had no text at this revision.</p>`)
|
||||
textPath = hyphaName + ".myco"
|
||||
textContents, err = history.FileAtRevision(textPath, revHash)
|
||||
textContents, err = history.FileAtRevision(h.TextPath, revHash)
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if err == nil {
|
||||
contents = markup.Doc(hyphaName, textContents).AsHTML()
|
||||
}
|
||||
treeHTML, _, _ := tree.Tree(hyphaName, IterateHyphaNamesWith)
|
||||
page := templates.RevisionHTML(
|
||||
page := views.RevisionHTML(
|
||||
rq,
|
||||
hyphaName,
|
||||
naviTitle(hyphaName),
|
||||
h,
|
||||
contents,
|
||||
treeHTML,
|
||||
revHash,
|
||||
)
|
||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(base(hyphaName, page, u)))
|
||||
w.Write([]byte(base(util.BeautifulName(hyphaName), page, u)))
|
||||
}
|
||||
|
||||
// handlerText serves raw source text of the hypha.
|
||||
func handlerText(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
hyphaName := HyphaNameFromRq(rq, "text")
|
||||
if data, ok := HyphaStorage[hyphaName]; ok {
|
||||
log.Println("Serving", data.textPath)
|
||||
if h := hyphae.ByName(hyphaName); h.Exists {
|
||||
log.Println("Serving", h.TextPath)
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
http.ServeFile(w, rq, data.textPath)
|
||||
http.ServeFile(w, rq, h.TextPath)
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,45 +83,39 @@ func handlerText(w http.ResponseWriter, rq *http.Request) {
|
||||
func handlerBinary(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
hyphaName := HyphaNameFromRq(rq, "binary")
|
||||
if data, ok := HyphaStorage[hyphaName]; ok {
|
||||
log.Println("Serving", data.binaryPath)
|
||||
w.Header().Set("Content-Type", ExtensionToMime(filepath.Ext(data.binaryPath)))
|
||||
http.ServeFile(w, rq, data.binaryPath)
|
||||
if h := hyphae.ByName(hyphaName); h.Exists {
|
||||
log.Println("Serving", h.BinaryPath)
|
||||
w.Header().Set("Content-Type", mimetype.FromExtension(filepath.Ext(h.BinaryPath)))
|
||||
http.ServeFile(w, rq, h.BinaryPath)
|
||||
}
|
||||
}
|
||||
|
||||
// handlerPage is the main hypha action that displays the hypha and the binary upload form along with some navigation.
|
||||
func handlerPage(w http.ResponseWriter, rq *http.Request) {
|
||||
// handlerHypha is the main hypha action that displays the hypha and the binary upload form along with some navigation.
|
||||
func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "page")
|
||||
data, hyphaExists = HyphaStorage[hyphaName]
|
||||
hasAmnt = hyphaExists && data.binaryPath != ""
|
||||
contents string
|
||||
openGraph string
|
||||
u = user.FromRequest(rq)
|
||||
hyphaName = HyphaNameFromRq(rq, "page", "hypha")
|
||||
h = hyphae.ByName(hyphaName)
|
||||
contents string
|
||||
openGraph string
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if hyphaExists {
|
||||
fileContentsT, errT := ioutil.ReadFile(data.textPath)
|
||||
_, errB := os.Stat(data.binaryPath)
|
||||
if h.Exists {
|
||||
fileContentsT, errT := ioutil.ReadFile(h.TextPath)
|
||||
_, errB := os.Stat(h.BinaryPath)
|
||||
if errT == nil {
|
||||
md := markup.Doc(hyphaName, string(fileContentsT))
|
||||
contents = md.AsHTML()
|
||||
openGraph = md.OpenGraphHTML()
|
||||
}
|
||||
if !os.IsNotExist(errB) {
|
||||
contents = binaryHtmlBlock(hyphaName, data) + contents
|
||||
contents = views.AttachmentHTML(h) + contents
|
||||
}
|
||||
}
|
||||
treeHTML, prevHypha, nextHypha := tree.Tree(hyphaName, IterateHyphaNamesWith)
|
||||
util.HTTP200Page(w,
|
||||
templates.BaseHTML(
|
||||
hyphaName,
|
||||
templates.PageHTML(rq, hyphaName,
|
||||
naviTitle(hyphaName),
|
||||
contents,
|
||||
treeHTML, prevHypha, nextHypha,
|
||||
hasAmnt),
|
||||
views.BaseHTML(
|
||||
util.BeautifulName(hyphaName),
|
||||
views.HyphaHTML(rq, h, contents),
|
||||
u,
|
||||
openGraph))
|
||||
}
|
||||
|
329
hypha.go
@ -1,329 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
markup.HyphaExists = func(hyphaName string) bool {
|
||||
_, hyphaExists := HyphaStorage[hyphaName]
|
||||
return hyphaExists
|
||||
}
|
||||
markup.HyphaAccess = func(hyphaName string) (rawText, binaryBlock string, err error) {
|
||||
if hyphaData, ok := HyphaStorage[hyphaName]; ok {
|
||||
rawText, err = FetchTextPart(hyphaData)
|
||||
if hyphaData.binaryPath != "" {
|
||||
binaryBlock = binaryHtmlBlock(hyphaName, hyphaData)
|
||||
}
|
||||
} else {
|
||||
err = errors.New("Hypha " + hyphaName + " does not exist")
|
||||
}
|
||||
return
|
||||
}
|
||||
markup.HyphaIterate = IterateHyphaNamesWith
|
||||
markup.HyphaImageForOG = func(hyphaName string) string {
|
||||
if hd, isOld := GetHyphaData(hyphaName); isOld && hd.binaryPath != "" {
|
||||
return util.URL + "/binary/" + hyphaName
|
||||
}
|
||||
return util.URL + "/favicon.ico"
|
||||
}
|
||||
}
|
||||
|
||||
// GetHyphaData finds a hypha addressed by `hyphaName` and returns its `hyphaData`. `hyphaData` is set to a zero value if this hypha does not exist. `isOld` is false if this hypha does not exist.
|
||||
func GetHyphaData(hyphaName string) (hyphaData *HyphaData, isOld bool) {
|
||||
hyphaData, isOld = HyphaStorage[hyphaName]
|
||||
if hyphaData == nil {
|
||||
hyphaData = &HyphaData{}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HyphaData represents a hypha's meta information: binary and text parts rooted paths and content types.
|
||||
type HyphaData struct {
|
||||
textPath string
|
||||
binaryPath string
|
||||
}
|
||||
|
||||
// uploadHelp is a helper function for UploadText and UploadBinary
|
||||
func uploadHelp(hop *history.HistoryOp, hyphaName, ext string, data []byte, u *user.User) *history.HistoryOp {
|
||||
var (
|
||||
hyphaData, isOld = GetHyphaData(hyphaName)
|
||||
fullPath = filepath.Join(WikiDir, hyphaName+ext)
|
||||
originalFullPath = &hyphaData.textPath
|
||||
)
|
||||
if hop.Type == history.TypeEditBinary {
|
||||
originalFullPath = &hyphaData.binaryPath
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
|
||||
return hop.WithError(err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(fullPath, data, 0644); err != nil {
|
||||
return hop.WithError(err)
|
||||
}
|
||||
|
||||
if isOld && *originalFullPath != fullPath && *originalFullPath != "" {
|
||||
if err := history.Rename(*originalFullPath, fullPath); err != nil {
|
||||
return hop.WithError(err)
|
||||
}
|
||||
log.Println("Move", *originalFullPath, "to", fullPath)
|
||||
}
|
||||
// New hyphae must be added to the hypha storage
|
||||
if !isOld {
|
||||
HyphaStorage[hyphaName] = hyphaData
|
||||
hyphae.IncrementCount()
|
||||
}
|
||||
*originalFullPath = fullPath
|
||||
if isOld && hop.Type == history.TypeEditText && !history.FileChanged(fullPath) {
|
||||
return hop.Abort()
|
||||
}
|
||||
return hop.WithFiles(fullPath).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
}
|
||||
|
||||
// UploadText loads a new text part from `textData` for hypha `hyphaName`.
|
||||
func UploadText(hyphaName, textData string, u *user.User) *history.HistoryOp {
|
||||
return uploadHelp(
|
||||
history.
|
||||
Operation(history.TypeEditText).
|
||||
WithMsg(fmt.Sprintf("Edit ‘%s’", hyphaName)),
|
||||
hyphaName, ".myco", []byte(textData), u)
|
||||
}
|
||||
|
||||
// UploadBinary loads a new binary part from `file` for hypha `hyphaName` with `hd`. The contents have the specified `mime` type. It must be marked if the hypha `isOld`.
|
||||
func UploadBinary(hyphaName, mime string, file multipart.File, u *user.User) *history.HistoryOp {
|
||||
var (
|
||||
hop = history.Operation(history.TypeEditBinary).WithMsg(fmt.Sprintf("Upload binary part for ‘%s’ with type ‘%s’", hyphaName, mime))
|
||||
data, err = ioutil.ReadAll(file)
|
||||
)
|
||||
if err != nil {
|
||||
return hop.WithError(err).Apply()
|
||||
}
|
||||
return uploadHelp(hop, hyphaName, MimeToExtension(mime), data, u)
|
||||
}
|
||||
|
||||
// DeleteHypha deletes hypha and makes a history record about that.
|
||||
func (hd *HyphaData) DeleteHypha(hyphaName string, u *user.User) *history.HistoryOp {
|
||||
hop := history.Operation(history.TypeDeleteHypha).
|
||||
WithFilesRemoved(hd.textPath, hd.binaryPath).
|
||||
WithMsg(fmt.Sprintf("Delete ‘%s’", hyphaName)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
if len(hop.Errs) == 0 {
|
||||
delete(HyphaStorage, hyphaName)
|
||||
hyphae.DecrementCount()
|
||||
}
|
||||
return hop
|
||||
}
|
||||
|
||||
// UnattachHypha unattaches hypha and makes a history record about that.
|
||||
func (hd *HyphaData) UnattachHypha(hyphaName string, u *user.User) *history.HistoryOp {
|
||||
hop := history.Operation(history.TypeUnattachHypha).
|
||||
WithFilesRemoved(hd.binaryPath).
|
||||
WithMsg(fmt.Sprintf("Unattach ‘%s’", hyphaName)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
if len(hop.Errs) == 0 {
|
||||
hd, ok := HyphaStorage[hyphaName]
|
||||
if ok {
|
||||
if hd.binaryPath != "" {
|
||||
hd.binaryPath = ""
|
||||
}
|
||||
// If nothing is left of the hypha
|
||||
if hd.textPath == "" {
|
||||
delete(HyphaStorage, hyphaName)
|
||||
hyphae.DecrementCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
return hop
|
||||
}
|
||||
|
||||
func findHyphaeToRename(hyphaName string, recursive bool) []string {
|
||||
hyphae := []string{hyphaName}
|
||||
if recursive {
|
||||
hyphae = append(hyphae, util.FindSubhyphae(hyphaName, IterateHyphaNamesWith)...)
|
||||
}
|
||||
return hyphae
|
||||
}
|
||||
|
||||
func renamingPairs(hyphaNames []string, replaceName func(string) string) (map[string]string, error) {
|
||||
renameMap := make(map[string]string)
|
||||
for _, hn := range hyphaNames {
|
||||
if hd, ok := HyphaStorage[hn]; ok {
|
||||
if _, nameUsed := HyphaStorage[replaceName(hn)]; nameUsed {
|
||||
return nil, errors.New("Hypha " + replaceName(hn) + " already exists")
|
||||
}
|
||||
if hd.textPath != "" {
|
||||
renameMap[hd.textPath] = replaceName(hd.textPath)
|
||||
}
|
||||
if hd.binaryPath != "" {
|
||||
renameMap[hd.binaryPath] = replaceName(hd.binaryPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
return renameMap, nil
|
||||
}
|
||||
|
||||
// word Data is plural here
|
||||
func relocateHyphaData(hyphaNames []string, replaceName func(string) string) {
|
||||
for _, hyphaName := range hyphaNames {
|
||||
if hd, ok := HyphaStorage[hyphaName]; ok {
|
||||
hd.textPath = replaceName(hd.textPath)
|
||||
hd.binaryPath = replaceName(hd.binaryPath)
|
||||
HyphaStorage[replaceName(hyphaName)] = hd
|
||||
delete(HyphaStorage, hyphaName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RenameHypha renames hypha from old name `hyphaName` to `newName` and makes a history record about that. If `recursive` is `true`, its subhyphae will be renamed the same way.
|
||||
func RenameHypha(hyphaName, newName string, recursive bool, u *user.User) *history.HistoryOp {
|
||||
var (
|
||||
re = regexp.MustCompile(`(?i)` + hyphaName)
|
||||
replaceName = func(str string) string {
|
||||
return re.ReplaceAllString(CanonicalName(str), newName)
|
||||
}
|
||||
hyphaNames = findHyphaeToRename(hyphaName, recursive)
|
||||
renameMap, err = renamingPairs(hyphaNames, replaceName)
|
||||
renameMsg = "Rename ‘%s’ to ‘%s’"
|
||||
hop = history.Operation(history.TypeRenameHypha)
|
||||
)
|
||||
if err != nil {
|
||||
hop.Errs = append(hop.Errs, err)
|
||||
return hop
|
||||
}
|
||||
if recursive {
|
||||
renameMsg += " recursively"
|
||||
}
|
||||
hop.WithFilesRenamed(renameMap).
|
||||
WithMsg(fmt.Sprintf(renameMsg, hyphaName, newName)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
if len(hop.Errs) == 0 {
|
||||
relocateHyphaData(hyphaNames, replaceName)
|
||||
}
|
||||
return hop
|
||||
}
|
||||
|
||||
// binaryHtmlBlock creates an html block for binary part of the hypha.
|
||||
func binaryHtmlBlock(hyphaName string, hd *HyphaData) string {
|
||||
switch filepath.Ext(hd.binaryPath) {
|
||||
case ".jpg", ".gif", ".png", ".webp", ".svg", ".ico":
|
||||
return fmt.Sprintf(`
|
||||
<div class="binary-container binary-container_with-img">
|
||||
<a href="/binary/%[1]s"><img src="/binary/%[1]s"/></a>
|
||||
</div>`, hyphaName)
|
||||
case ".ogg", ".webm", ".mp4":
|
||||
return fmt.Sprintf(`
|
||||
<div class="binary-container binary-container_with-video">
|
||||
<video>
|
||||
<source src="/binary/%[1]s"/>
|
||||
<p>Your browser does not support video. See video's <a href="/binary/%[1]s">direct url</a></p>
|
||||
</video>
|
||||
`, hyphaName)
|
||||
case ".mp3":
|
||||
return fmt.Sprintf(`
|
||||
<div class="binary-container binary-container_with-audio">
|
||||
<audio>
|
||||
<source src="/binary/%[1]s"/>
|
||||
<p>Your browser does not support audio. See audio's <a href="/binary/%[1]s">direct url</a></p>
|
||||
</audio>
|
||||
`, hyphaName)
|
||||
default:
|
||||
return fmt.Sprintf(`
|
||||
<div class="binary-container binary-container_with-nothing">
|
||||
<p>This hypha's media cannot be rendered. <a href="/binary/%s">Download it</a></p>
|
||||
</div>
|
||||
`, hyphaName)
|
||||
}
|
||||
}
|
||||
|
||||
// Index finds all hypha files in the full `path` and saves them to HyphaStorage. This function is recursive.
|
||||
func Index(path string) {
|
||||
nodes, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
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 node.IsDir() && isCanonicalName(node.Name()) && node.Name() != ".git" && node.Name() != "static" {
|
||||
Index(filepath.Join(path, node.Name()))
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
hyphaPartPath = filepath.Join(path, node.Name())
|
||||
hyphaName, isText, skip = DataFromFilename(hyphaPartPath)
|
||||
hyphaData *HyphaData
|
||||
)
|
||||
if !skip {
|
||||
// Reuse the entry for existing hyphae, create a new one for those that do not exist yet.
|
||||
if hd, ok := HyphaStorage[hyphaName]; ok {
|
||||
hyphaData = hd
|
||||
} else {
|
||||
hyphaData = &HyphaData{}
|
||||
HyphaStorage[hyphaName] = hyphaData
|
||||
hyphae.IncrementCount()
|
||||
}
|
||||
if isText {
|
||||
hyphaData.textPath = hyphaPartPath
|
||||
} else {
|
||||
// Notify the user about binary part collisions. It's a design decision to just use any of them, it's the user's fault that they have screwed up the folder structure, but the engine should at least let them know, right?
|
||||
if hyphaData.binaryPath != "" {
|
||||
log.Println("There is a file collision for binary part of a hypha:", hyphaData.binaryPath, "and", hyphaPartPath, "-- going on with the latter")
|
||||
}
|
||||
hyphaData.binaryPath = hyphaPartPath
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// FetchTextPart tries to read text file in the `d`. If there is no file, empty string is returned.
|
||||
func FetchTextPart(d *HyphaData) (string, error) {
|
||||
if d.textPath == "" {
|
||||
return "", nil
|
||||
}
|
||||
_, err := os.Stat(d.textPath)
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
text, err := ioutil.ReadFile(d.textPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(text), nil
|
||||
}
|
||||
|
||||
func setHeaderLinks() {
|
||||
if userLinksHypha, ok := GetHyphaData(util.HeaderLinksHypha); !ok {
|
||||
util.SetDefaultHeaderLinks()
|
||||
} else {
|
||||
contents, err := ioutil.ReadFile(userLinksHypha.textPath)
|
||||
if err != nil || len(contents) == 0 {
|
||||
util.SetDefaultHeaderLinks()
|
||||
} else {
|
||||
text := string(contents)
|
||||
util.ParseHeaderLinks(text, markup.Rocketlink)
|
||||
}
|
||||
}
|
||||
}
|
@ -10,21 +10,21 @@ var count = struct {
|
||||
sync.Mutex
|
||||
}{}
|
||||
|
||||
// Set the value of hyphae count to zero.
|
||||
// Set the value of hyphae count to zero. Use when reloading hyphae.
|
||||
func ResetCount() {
|
||||
count.Lock()
|
||||
count.value = 0
|
||||
count.Unlock()
|
||||
count.Lock()
|
||||
count.value = 0
|
||||
count.Unlock()
|
||||
}
|
||||
|
||||
// Increment the value of hyphae count.
|
||||
// Increment the value of the hyphae counter. Use when creating new hyphae or loading hyphae from disk.
|
||||
func IncrementCount() {
|
||||
count.Lock()
|
||||
count.value++
|
||||
count.Unlock()
|
||||
}
|
||||
|
||||
// Decrement the value of hyphae count.
|
||||
// Decrement the value of the hyphae counter. Use when deleting existing hyphae.
|
||||
func DecrementCount() {
|
||||
count.Lock()
|
||||
count.value--
|
||||
@ -33,6 +33,5 @@ func DecrementCount() {
|
||||
|
||||
// Count how many hyphae there are.
|
||||
func Count() int {
|
||||
// it is concurrent-safe to not lock here, right?
|
||||
return count.value
|
||||
}
|
||||
|
66
hyphae/files.go
Normal file
@ -0,0 +1,66 @@
|
||||
package hyphae
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
// 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)
|
||||
|
||||
go func(ch chan *Hypha) {
|
||||
indexHelper(path, 0, ch)
|
||||
close(ch)
|
||||
}(ch)
|
||||
|
||||
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)
|
||||
} else {
|
||||
byNames[h.Name] = h
|
||||
IncrementCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// indexHelper finds all hypha files in the full `path` and sends them to the channel. Handling of duplicate entries and attachment and counting them is up to the caller.
|
||||
func indexHelper(path string, nestLevel uint, ch chan *Hypha) {
|
||||
nodes, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
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 node.IsDir() &&
|
||||
util.IsCanonicalName(node.Name()) &&
|
||||
node.Name() != ".git" &&
|
||||
!(nestLevel == 0 && node.Name() == "static") {
|
||||
indexHelper(filepath.Join(path, node.Name()), nestLevel+1, ch)
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
hyphaPartPath = filepath.Join(path, node.Name())
|
||||
hyphaName, isText, skip = mimetype.DataFromFilename(hyphaPartPath)
|
||||
hypha = &Hypha{Name: hyphaName, Exists: true}
|
||||
)
|
||||
if !skip {
|
||||
if isText {
|
||||
hypha.TextPath = hyphaPartPath
|
||||
} else {
|
||||
hypha.BinaryPath = hyphaPartPath
|
||||
}
|
||||
ch <- hypha
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package hyphae
|
||||
|
||||
// TODO: do
|
||||
import ()
|
||||
|
||||
type Hypha struct {
|
||||
Name string
|
||||
Exists bool
|
||||
TextPath string
|
||||
BinaryPath string
|
||||
OutLinks []string
|
||||
BackLinks []string
|
||||
}
|
||||
|
||||
// AddHypha adds a hypha named `name` with such `textPath` and `binaryPath`. Both paths can be empty. Does //not// check for hypha's existence beforehand. Count is handled.
|
||||
func AddHypha(name, textPath, binaryPath string) {
|
||||
}
|
||||
|
||||
// DeleteHypha clears both paths and all out-links from the named hypha and marks it as non-existent. It does not actually delete it from the memdb. Count is handled.
|
||||
func DeleteHypha(name string) {
|
||||
}
|
175
hyphae/hyphae.go
Normal file
@ -0,0 +1,175 @@
|
||||
// The `hyphae` package is for the Hypha type, hypha storage and stuff like that. It shall not depend on mycorrhiza modules other than util.
|
||||
package hyphae
|
||||
|
||||
import (
|
||||
"log"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// HyphaPattern is a pattern which all hyphae must match.
|
||||
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`)
|
||||
|
||||
type Hypha struct {
|
||||
sync.RWMutex
|
||||
|
||||
Name string
|
||||
Exists bool
|
||||
TextPath string
|
||||
BinaryPath string
|
||||
OutLinks []*Hypha
|
||||
BackLinks []*Hypha
|
||||
}
|
||||
|
||||
var byNames = make(map[string]*Hypha)
|
||||
var byNamesMutex = sync.Mutex{}
|
||||
|
||||
// EmptyHypha returns an empty hypha struct with given name.
|
||||
func EmptyHypha(hyphaName string) *Hypha {
|
||||
return &Hypha{
|
||||
Name: hyphaName,
|
||||
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).
|
||||
func ByName(hyphaName string) (h *Hypha) {
|
||||
h, exists := byNames[hyphaName]
|
||||
if exists {
|
||||
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)
|
||||
|
||||
byNamesMutex.Lock()
|
||||
defer byNamesMutex.Unlock()
|
||||
if hp.Exists {
|
||||
hp = h
|
||||
} else {
|
||||
h.Exists = true
|
||||
byNames[h.Name] = h
|
||||
IncrementCount()
|
||||
}
|
||||
|
||||
return !hp.Exists
|
||||
}
|
||||
|
||||
func (h *Hypha) InsertIfNew() (justCreated 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()
|
||||
delete(byNames, h.Name)
|
||||
DecrementCount()
|
||||
byNamesMutex.Unlock()
|
||||
h.Unlock()
|
||||
|
||||
for _, outlinkHypha := range h.OutLinks {
|
||||
outlinkHypha.DropBackLink(h)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hypha) RenameTo(newName string) {
|
||||
byNamesMutex.Lock()
|
||||
h.Lock()
|
||||
delete(byNames, h.Name)
|
||||
h.Name = newName
|
||||
byNames[h.Name] = h
|
||||
byNamesMutex.Unlock()
|
||||
h.Unlock()
|
||||
}
|
||||
|
||||
// MergeIn merges in content file paths from a different hypha object. Prints warnings sometimes.
|
||||
func (h *Hypha) MergeIn(oh *Hypha) {
|
||||
if h.TextPath == "" && oh.TextPath != "" {
|
||||
h.TextPath = oh.TextPath
|
||||
}
|
||||
if oh.BinaryPath != "" {
|
||||
if h.BinaryPath != "" {
|
||||
log.Println("There is a file collision for binary part of a hypha:", h.BinaryPath, "and", oh.BinaryPath, "-- going on with the latter")
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
57
hyphae/iterators.go
Normal file
@ -0,0 +1,57 @@
|
||||
// File `iterators.go` contains stuff that iterates over hyphae.
|
||||
package hyphae
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// YieldExistingHyphae iterates over all hyphae and yields all existing ones.
|
||||
func YieldExistingHyphae() chan *Hypha {
|
||||
ch := make(chan *Hypha)
|
||||
go func() {
|
||||
for _, h := range byNames {
|
||||
if h.Exists {
|
||||
ch <- h
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// FilterTextHyphae filters the source channel and yields only those hyphae than have text parts.
|
||||
func FilterTextHyphae(src chan *Hypha) chan *Hypha {
|
||||
sink := make(chan *Hypha)
|
||||
go func() {
|
||||
for h := range src {
|
||||
if h.TextPath != "" {
|
||||
sink <- h
|
||||
}
|
||||
}
|
||||
close(sink)
|
||||
}()
|
||||
return sink
|
||||
}
|
||||
|
||||
// Subhyphae returns slice of subhyphae.
|
||||
func (h *Hypha) Subhyphae() []*Hypha {
|
||||
hyphae := []*Hypha{}
|
||||
for subh := range YieldExistingHyphae() {
|
||||
if strings.HasPrefix(subh.Name, h.Name+"/") {
|
||||
hyphae = append(hyphae, subh)
|
||||
}
|
||||
}
|
||||
return hyphae
|
||||
}
|
||||
|
||||
// AreFreeNames checks if all given `hyphaNames` are not taken. If they are not taken, `ok` is true. If not, `firstFailure` is the name of the first met hypha that is not free.
|
||||
func AreFreeNames(hyphaNames ...string) (firstFailure string, ok bool) {
|
||||
for h := range YieldExistingHyphae() {
|
||||
for _, hn := range hyphaNames {
|
||||
if hn == h.Name {
|
||||
return hn, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", true
|
||||
}
|
132
link/link.go
Normal file
@ -0,0 +1,132 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
// LinkType tells what type the given link is.
|
||||
type LinkType int
|
||||
|
||||
const (
|
||||
LinkInavild LinkType = iota
|
||||
// LinkLocalRoot is a link like "/list", "/user-list", etc.
|
||||
LinkLocalRoot
|
||||
// LinkLocalHypha is a link like "test", "../test", etc.
|
||||
LinkLocalHypha
|
||||
// LinkExternal is an external link with specified protocol.
|
||||
LinkExternal
|
||||
// LinkInterwiki is currently unused.
|
||||
LinkInterwiki
|
||||
)
|
||||
|
||||
// Link is an abstraction for universal representation of links, be they links in mycomarkup links or whatever.
|
||||
type Link struct {
|
||||
// Address is what the link points to.
|
||||
Address string
|
||||
// Display is what gets nested into the <a> tag.
|
||||
Display string
|
||||
Kind LinkType
|
||||
DestinationUnknown bool
|
||||
|
||||
// #...
|
||||
Anchor string
|
||||
Protocol string
|
||||
// How the link address looked originally in source text.
|
||||
SrcAddress string
|
||||
// How the link display text looked originally in source text. May be empty.
|
||||
SrcDisplay string
|
||||
// RelativeTo is hypha name to which the link is relative to.
|
||||
RelativeTo string
|
||||
}
|
||||
|
||||
// DoubtExistence sets DestinationUnknown to true if the link is local hypha link.
|
||||
func (l *Link) DoubtExistence() {
|
||||
if l.Kind == LinkLocalHypha {
|
||||
l.DestinationUnknown = true
|
||||
}
|
||||
}
|
||||
|
||||
// Classes returns CSS class string for given link.
|
||||
func (l *Link) Classes() string {
|
||||
if l.Kind == LinkExternal {
|
||||
return fmt.Sprintf("wikilink wikilink_external wikilink_%s", l.Protocol)
|
||||
}
|
||||
classes := "wikilink wikilink_internal"
|
||||
if l.DestinationUnknown {
|
||||
classes += " wikilink_new"
|
||||
}
|
||||
return classes
|
||||
}
|
||||
|
||||
// Href returns content for the href attrubite for hyperlink. You should always use it.
|
||||
func (l *Link) Href() string {
|
||||
switch l.Kind {
|
||||
case LinkExternal, LinkLocalRoot:
|
||||
return l.Address
|
||||
default:
|
||||
return "/hypha/" + l.Address + l.Anchor
|
||||
}
|
||||
}
|
||||
|
||||
// ImgSrc returns content for src attribute of img tag. Used with `img{}`.
|
||||
func (l *Link) ImgSrc() string {
|
||||
switch l.Kind {
|
||||
case LinkExternal, LinkLocalRoot:
|
||||
return l.Address
|
||||
default:
|
||||
return "/binary/" + l.Address
|
||||
}
|
||||
}
|
||||
|
||||
// From returns a Link object given these `address` and `display` on relative to given `hyphaName`.
|
||||
func From(address, display, hyphaName string) *Link {
|
||||
address = strings.TrimSpace(address)
|
||||
link := Link{
|
||||
SrcAddress: address,
|
||||
SrcDisplay: display,
|
||||
RelativeTo: hyphaName,
|
||||
}
|
||||
|
||||
if display == "" {
|
||||
link.Display = address
|
||||
} else {
|
||||
link.Display = strings.TrimSpace(display)
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.ContainsRune(address, ':'):
|
||||
pos := strings.IndexRune(address, ':')
|
||||
link.Protocol = address[:pos]
|
||||
link.Kind = LinkExternal
|
||||
|
||||
if display == "" {
|
||||
link.Display = address[pos+1:]
|
||||
if strings.HasPrefix(link.Display, "//") && len(link.Display) > 2 {
|
||||
link.Display = link.Display[2:]
|
||||
}
|
||||
}
|
||||
link.Address = address
|
||||
case strings.HasPrefix(address, "/"):
|
||||
link.Address = address
|
||||
link.Kind = LinkLocalRoot
|
||||
case strings.HasPrefix(address, "./"):
|
||||
link.Kind = LinkLocalHypha
|
||||
link.Address = util.CanonicalName(path.Join(hyphaName, address[2:]))
|
||||
case strings.HasPrefix(address, "../"):
|
||||
link.Kind = LinkLocalHypha
|
||||
link.Address = util.CanonicalName(path.Join(path.Dir(hyphaName), address[3:]))
|
||||
case strings.HasPrefix(address, "#"):
|
||||
link.Kind = LinkLocalHypha
|
||||
link.Address = util.CanonicalName(hyphaName)
|
||||
link.Anchor = address
|
||||
default:
|
||||
link.Kind = LinkLocalHypha
|
||||
link.Address = util.CanonicalName(address)
|
||||
}
|
||||
|
||||
return &link
|
||||
}
|
94
main.go
@ -1,5 +1,6 @@
|
||||
//go:generate go get -u github.com/valyala/quicktemplate/qtc
|
||||
//go:generate qtc -dir=templates
|
||||
//go:generate qtc -dir=assets
|
||||
//go:generate qtc -dir=views
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -9,33 +10,20 @@ import (
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/assets"
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/templates"
|
||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
"github.com/bouncepaw/mycorrhiza/views"
|
||||
)
|
||||
|
||||
// WikiDir is a rooted path to the wiki storage directory.
|
||||
var WikiDir string
|
||||
|
||||
// HyphaPattern is a pattern which all hyphae must match.
|
||||
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`)
|
||||
|
||||
// HyphaStorage is a mapping between canonical hypha names and their meta information.
|
||||
var HyphaStorage = make(map[string]*HyphaData)
|
||||
|
||||
// IterateHyphaNamesWith is a closure to be passed to subpackages to let them iterate all hypha names read-only.
|
||||
func IterateHyphaNamesWith(f func(string)) {
|
||||
for hyphaName := range HyphaStorage {
|
||||
f(hyphaName)
|
||||
}
|
||||
}
|
||||
|
||||
// HttpErr is used by many handlers to signal errors in a compact way.
|
||||
func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) {
|
||||
log.Println(errMsg, "for", name)
|
||||
@ -46,7 +34,7 @@ func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) {
|
||||
base(
|
||||
title,
|
||||
fmt.Sprintf(
|
||||
`<main><p>%s. <a href="/page/%s">Go back to the hypha.<a></p></main>`,
|
||||
`<main class="main-width"><p>%s. <a href="/page/%s">Go back to the hypha.<a></p></main>`,
|
||||
errMsg,
|
||||
name,
|
||||
),
|
||||
@ -58,19 +46,11 @@ func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) {
|
||||
// Show all hyphae
|
||||
func handlerList(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
tbody string
|
||||
pageCount = hyphae.Count()
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
for hyphaName, data := range HyphaStorage {
|
||||
tbody += templates.HyphaListRowHTML(hyphaName, ExtensionToMime(filepath.Ext(data.binaryPath)), data.binaryPath != "")
|
||||
}
|
||||
util.HTTP200Page(w, base("List of pages", templates.HyphaListHTML(tbody, pageCount), u))
|
||||
util.HTTP200Page(w, base("List of pages", views.HyphaListHTML(), user.FromRequest(rq)))
|
||||
}
|
||||
|
||||
// This part is present in all html documents.
|
||||
var base = templates.BaseHTML
|
||||
var base = views.BaseHTML
|
||||
|
||||
// Reindex all hyphae by checking the wiki storage directory anew.
|
||||
func handlerReindex(w http.ResponseWriter, rq *http.Request) {
|
||||
@ -81,14 +61,15 @@ func handlerReindex(w http.ResponseWriter, rq *http.Request) {
|
||||
return
|
||||
}
|
||||
hyphae.ResetCount()
|
||||
HyphaStorage = make(map[string]*HyphaData)
|
||||
log.Println("Wiki storage directory is", WikiDir)
|
||||
log.Println("Start indexing hyphae...")
|
||||
Index(WikiDir)
|
||||
hyphae.Index(WikiDir)
|
||||
log.Println("Indexed", hyphae.Count(), "hyphae")
|
||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// Stop the wiki
|
||||
|
||||
// Update header links by reading the configured hypha, if there is any, or resorting to default values.
|
||||
func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
@ -97,7 +78,7 @@ func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println("Rejected", rq.URL)
|
||||
return
|
||||
}
|
||||
setHeaderLinks()
|
||||
shroom.SetHeaderLinks()
|
||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
@ -106,23 +87,25 @@ func handlerRandom(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var randomHyphaName string
|
||||
i := rand.Intn(hyphae.Count())
|
||||
for hyphaName := range HyphaStorage {
|
||||
for h := range hyphae.YieldExistingHyphae() {
|
||||
if i == 0 {
|
||||
randomHyphaName = hyphaName
|
||||
break
|
||||
randomHyphaName = h.Name
|
||||
}
|
||||
i--
|
||||
}
|
||||
http.Redirect(w, rq, "/page/"+randomHyphaName, http.StatusSeeOther)
|
||||
http.Redirect(w, rq, "/hypha/"+randomHyphaName, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func handlerStyle(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
if _, err := os.Stat(WikiDir + "/static/common.css"); err == nil {
|
||||
http.ServeFile(w, rq, WikiDir+"/static/common.css")
|
||||
if _, err := os.Stat(util.WikiDir + "/static/common.css"); err == nil {
|
||||
http.ServeFile(w, rq, util.WikiDir+"/static/common.css")
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "text/css;charset=utf-8")
|
||||
w.Write([]byte(templates.DefaultCSS()))
|
||||
w.Write([]byte(assets.DefaultCSS()))
|
||||
}
|
||||
if bytes, err := ioutil.ReadFile(util.WikiDir + "/static/custom.css"); err == nil {
|
||||
w.Write(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,20 +126,26 @@ func handlerIcon(w http.ResponseWriter, rq *http.Request) {
|
||||
w.Header().Set("Content-Type", "image/svg+xml")
|
||||
switch iconName {
|
||||
case "gemini":
|
||||
w.Write([]byte(templates.IconGemini()))
|
||||
w.Write([]byte(assets.IconGemini()))
|
||||
case "mailto":
|
||||
w.Write([]byte(templates.IconMailto()))
|
||||
w.Write([]byte(assets.IconMailto()))
|
||||
case "gopher":
|
||||
w.Write([]byte(templates.IconGopher()))
|
||||
w.Write([]byte(assets.IconGopher()))
|
||||
default:
|
||||
w.Write([]byte(templates.IconHTTP()))
|
||||
w.Write([]byte(assets.IconHTTP()))
|
||||
}
|
||||
}
|
||||
|
||||
func handlerAbout(w http.ResponseWriter, rq *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(base("About "+util.SiteName, templates.AboutHTML(), user.FromRequest(rq))))
|
||||
w.Write([]byte(base("About "+util.SiteName, views.AboutHTML(), user.FromRequest(rq))))
|
||||
}
|
||||
|
||||
func handlerUserList(w http.ResponseWriter, rq *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(base("User list", views.UserListHTML(), user.FromRequest(rq))))
|
||||
}
|
||||
|
||||
func handlerRobotsTxt(w http.ResponseWriter, rq *http.Request) {
|
||||
@ -177,13 +166,19 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("Wiki storage directory is", WikiDir)
|
||||
Index(WikiDir)
|
||||
hyphae.Index(WikiDir)
|
||||
log.Println("Indexed", hyphae.Count(), "hyphae")
|
||||
shroom.FindAllBacklinks()
|
||||
log.Println("Found all backlinks")
|
||||
|
||||
history.Start(WikiDir)
|
||||
setHeaderLinks()
|
||||
shroom.SetHeaderLinks()
|
||||
|
||||
// See http_readers.go for /page/, /text/, /binary/
|
||||
go handleGemini()
|
||||
|
||||
// See http_admin.go for /admin, /admin/*
|
||||
initAdmin()
|
||||
// See http_readers.go for /page/, /hypha/, /text/, /binary/, /attachment/
|
||||
// See http_mutators.go for /upload-binary/, /upload-text/, /edit/, /delete-ask/, /delete-confirm/, /rename-ask/, /rename-confirm/, /unattach-ask/, /unattach-confirm/
|
||||
// See http_auth.go for /login, /login-data, /logout, /logout-confirm
|
||||
// See http_history.go for /history/, /recent-changes
|
||||
@ -192,15 +187,16 @@ func main() {
|
||||
http.HandleFunc("/update-header-links", handlerUpdateHeaderLinks)
|
||||
http.HandleFunc("/random", handlerRandom)
|
||||
http.HandleFunc("/about", handlerAbout)
|
||||
http.HandleFunc("/user-list", handlerUserList)
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(WikiDir+"/static"))))
|
||||
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) {
|
||||
http.ServeFile(w, rq, WikiDir+"/static/favicon.ico")
|
||||
})
|
||||
http.HandleFunc("/static/common.css", handlerStyle)
|
||||
http.HandleFunc("/static/icon/", handlerIcon)
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, rq *http.Request) {
|
||||
http.Redirect(w, rq, "/page/"+util.HomePage, http.StatusSeeOther)
|
||||
})
|
||||
http.HandleFunc("/robots.txt", handlerRobotsTxt)
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, rq *http.Request) {
|
||||
http.Redirect(w, rq, "/hypha/"+util.HomePage, http.StatusSeeOther)
|
||||
})
|
||||
log.Fatal(http.ListenAndServe("0.0.0.0:"+util.ServerPort, nil))
|
||||
}
|
||||
|
34
markup/hr.go
Normal file
@ -0,0 +1,34 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// MatchesHorizontalLine checks if the string can be interpreted as suitable for rendering as <hr/>.
|
||||
//
|
||||
// The rule is: if there are more than 4 characters "-" in the string, then make it a horizontal line.
|
||||
// Otherwise it is a paragraph (<p>).
|
||||
func MatchesHorizontalLine(line string) bool {
|
||||
counter := 0
|
||||
|
||||
// Check initially that the symbol is "-". If it is not a "-", it is most likely a space or another character.
|
||||
// With unicode.IsLetter() we can separate spaces and characters.
|
||||
for _, ch := range line {
|
||||
if ch == '-' {
|
||||
counter++
|
||||
continue
|
||||
}
|
||||
// If we bump into any other character (letter) in the line, it is immediately an incorrect horizontal line.
|
||||
// There is no point in counting further, we end the loop.
|
||||
if unicode.IsLetter(ch) {
|
||||
counter = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if counter >= 4 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
@ -5,7 +5,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
"github.com/bouncepaw/mycorrhiza/link"
|
||||
)
|
||||
|
||||
var imgRe = regexp.MustCompile(`^img\s+{`)
|
||||
@ -14,44 +14,6 @@ func MatchesImg(line string) bool {
|
||||
return imgRe.MatchString(line)
|
||||
}
|
||||
|
||||
type imgEntry struct {
|
||||
trimmedPath string
|
||||
path strings.Builder
|
||||
sizeW strings.Builder
|
||||
sizeH strings.Builder
|
||||
desc strings.Builder
|
||||
}
|
||||
|
||||
func (entry *imgEntry) descriptionAsHtml(hyphaName string) (html string) {
|
||||
if entry.desc.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
lines := strings.Split(entry.desc.String(), "\n")
|
||||
for _, line := range lines {
|
||||
if line = strings.TrimSpace(line); line != "" {
|
||||
if html != "" {
|
||||
html += `<br>`
|
||||
}
|
||||
html += ParagraphToHtml(hyphaName, line)
|
||||
}
|
||||
}
|
||||
return `<figcaption>` + html + `</figcaption>`
|
||||
}
|
||||
|
||||
func (entry *imgEntry) sizeWAsAttr() string {
|
||||
if entry.sizeW.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
return ` width="` + entry.sizeW.String() + `"`
|
||||
}
|
||||
|
||||
func (entry *imgEntry) sizeHAsAttr() string {
|
||||
if entry.sizeH.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
return ` height="` + entry.sizeH.String() + `"`
|
||||
}
|
||||
|
||||
type imgState int
|
||||
|
||||
const (
|
||||
@ -71,6 +33,8 @@ type Img struct {
|
||||
|
||||
func (img *Img) pushEntry() {
|
||||
if strings.TrimSpace(img.currEntry.path.String()) != "" {
|
||||
img.currEntry.srclink = link.From(img.currEntry.path.String(), "", img.hyphaName)
|
||||
img.currEntry.srclink.DoubtExistence()
|
||||
img.entries = append(img.entries, img.currEntry)
|
||||
img.currEntry = imgEntry{}
|
||||
img.currEntry.path.Reset()
|
||||
@ -177,23 +141,6 @@ func ImgFromFirstLine(line, hyphaName string) (img *Img, shouldGoBackToNormal bo
|
||||
return img, img.Process(line)
|
||||
}
|
||||
|
||||
func (img *Img) binaryPathFor(path string) string {
|
||||
path = strings.TrimSpace(path)
|
||||
if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 {
|
||||
return path
|
||||
} else {
|
||||
return "/binary/" + xclCanonicalName(img.hyphaName, path)
|
||||
}
|
||||
}
|
||||
|
||||
func (img *Img) ogBinaryPathFor(path string) string {
|
||||
path = img.binaryPathFor(path)
|
||||
if strings.HasPrefix(path, "/binary/") {
|
||||
return util.URL + path
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (img *Img) pagePathFor(path string) string {
|
||||
path = strings.TrimSpace(path)
|
||||
if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 {
|
||||
@ -214,30 +161,18 @@ func parseDimensions(dimensions string) (sizeW, sizeH string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (img *Img) checkLinks() map[string]bool {
|
||||
m := make(map[string]bool)
|
||||
for i, entry := range img.entries {
|
||||
// Also trim them for later use
|
||||
entry.trimmedPath = strings.TrimSpace(entry.path.String())
|
||||
isAbsoluteUrl := strings.ContainsRune(entry.trimmedPath, ':')
|
||||
if !isAbsoluteUrl {
|
||||
entry.trimmedPath = canonicalName(entry.trimmedPath)
|
||||
}
|
||||
img.entries[i] = entry
|
||||
m[entry.trimmedPath] = isAbsoluteUrl
|
||||
}
|
||||
HyphaIterate(func(hyphaName string) {
|
||||
func (img *Img) markExistenceOfSrcLinks() {
|
||||
HyphaIterate(func(hn string) {
|
||||
for _, entry := range img.entries {
|
||||
if hyphaName == xclCanonicalName(img.hyphaName, entry.trimmedPath) {
|
||||
m[entry.trimmedPath] = true
|
||||
if hn == entry.srclink.Address {
|
||||
entry.srclink.DestinationUnknown = false
|
||||
}
|
||||
}
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
func (img *Img) ToHtml() (html string) {
|
||||
linkAvailabilityMap := img.checkLinks()
|
||||
img.markExistenceOfSrcLinks()
|
||||
isOneImageOnly := len(img.entries) == 1 && img.entries[0].desc.Len() == 0
|
||||
if isOneImageOnly {
|
||||
html += `<section class="img-gallery img-gallery_one-image">`
|
||||
@ -247,15 +182,19 @@ func (img *Img) ToHtml() (html string) {
|
||||
|
||||
for _, entry := range img.entries {
|
||||
html += `<figure>`
|
||||
// If is existing hypha or an external path
|
||||
if linkAvailabilityMap[entry.trimmedPath] {
|
||||
if entry.srclink.DestinationUnknown {
|
||||
html += fmt.Sprintf(
|
||||
`<a class="%s" href="%s">Hypha <i>%s</i> does not exist</a>`,
|
||||
entry.srclink.Classes(),
|
||||
entry.srclink.Href(),
|
||||
entry.srclink.Address)
|
||||
} else {
|
||||
html += fmt.Sprintf(
|
||||
`<a href="%s"><img src="%s" %s %s></a>`,
|
||||
img.pagePathFor(entry.trimmedPath),
|
||||
img.binaryPathFor(entry.trimmedPath),
|
||||
entry.sizeWAsAttr(), entry.sizeHAsAttr())
|
||||
} else { // If is a non-existent hypha
|
||||
html += fmt.Sprintf(`<a class="wikilink_new" href="%s">Hypha <em>%s</em> does not exist</a>`, img.pagePathFor(entry.trimmedPath), entry.trimmedPath)
|
||||
entry.srclink.Href(),
|
||||
entry.srclink.ImgSrc(),
|
||||
entry.sizeWAsAttr(),
|
||||
entry.sizeHAsAttr())
|
||||
}
|
||||
html += entry.descriptionAsHtml(img.hyphaName)
|
||||
html += `</figure>`
|
||||
|
45
markup/img_entry.go
Normal file
@ -0,0 +1,45 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/link"
|
||||
)
|
||||
|
||||
type imgEntry struct {
|
||||
srclink *link.Link
|
||||
path strings.Builder
|
||||
sizeW strings.Builder
|
||||
sizeH strings.Builder
|
||||
desc strings.Builder
|
||||
}
|
||||
|
||||
func (entry *imgEntry) descriptionAsHtml(hyphaName string) (html string) {
|
||||
if entry.desc.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
lines := strings.Split(entry.desc.String(), "\n")
|
||||
for _, line := range lines {
|
||||
if line = strings.TrimSpace(line); line != "" {
|
||||
if html != "" {
|
||||
html += `<br>`
|
||||
}
|
||||
html += ParagraphToHtml(hyphaName, line)
|
||||
}
|
||||
}
|
||||
return `<figcaption>` + html + `</figcaption>`
|
||||
}
|
||||
|
||||
func (entry *imgEntry) sizeWAsAttr() string {
|
||||
if entry.sizeW.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
return ` width="` + entry.sizeW.String() + `"`
|
||||
}
|
||||
|
||||
func (entry *imgEntry) sizeHAsAttr() string {
|
||||
if entry.sizeH.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
return ` height="` + entry.sizeH.String() + `"`
|
||||
}
|
@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"html"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
// HyphaExists holds function that checks that a hypha is present.
|
||||
@ -83,7 +85,8 @@ func lineToAST(line string, state *GemLexerState, ast *[]Line) {
|
||||
return strings.HasPrefix(line, token)
|
||||
}
|
||||
addHeading := func(i int) {
|
||||
addLine(fmt.Sprintf("<h%d id='%d'>%s</h%d>", i, state.id, ParagraphToHtml(state.name, line[i+1:]), i))
|
||||
id := util.LettersNumbersOnly(line[i+1:])
|
||||
addLine(fmt.Sprintf(`<h%d id='%d'>%s<a href="#%s" id="%s" class="heading__link"></a></h%d>`, i, state.id, ParagraphToHtml(state.name, line[i+1:]), id, id, i))
|
||||
}
|
||||
|
||||
// Beware! Usage of goto. Some may say it is considered evil but in this case it helped to make a better-structured code.
|
||||
@ -166,7 +169,7 @@ launchpadState:
|
||||
switch {
|
||||
case startsWith("=>"):
|
||||
href, text, class := Rocketlink(line, state.name)
|
||||
state.buf += fmt.Sprintf(` <li class="launchpad__entry"><a class="rocketlink %s" href="%s">%s</a></li>`, class, href, text)
|
||||
state.buf += fmt.Sprintf(` <li class="launchpad__entry"><a href="%s" class="rocketlink %s">%s</a></li>`, href, class, text)
|
||||
case startsWith("```"):
|
||||
state.where = "pre"
|
||||
addLine(state.buf + "</ul>")
|
||||
@ -235,7 +238,7 @@ normalState:
|
||||
case startsWith("<="):
|
||||
addParagraphIfNeeded()
|
||||
addLine(parseTransclusion(line, state.name))
|
||||
case line == "----":
|
||||
case MatchesHorizontalLine(line):
|
||||
addParagraphIfNeeded()
|
||||
*ast = append(*ast, Line{id: -1, contents: "<hr/>"})
|
||||
case MatchesImg(line):
|
||||
|
@ -1,47 +1,22 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/link"
|
||||
)
|
||||
|
||||
// LinkParts determines what href, text and class should resulting <a> have based on mycomarkup's addr, display and hypha name.
|
||||
//
|
||||
// => addr display
|
||||
// [[addr|display]]
|
||||
// TODO: deprecate
|
||||
func LinkParts(addr, display, hyphaName string) (href, text, class string) {
|
||||
if display == "" {
|
||||
text = addr
|
||||
} else {
|
||||
text = strings.TrimSpace(display)
|
||||
l := link.From(addr, display, hyphaName)
|
||||
if l.Kind == link.LinkLocalHypha && !HyphaExists(l.Address) {
|
||||
l.DestinationUnknown = true
|
||||
}
|
||||
class = "wikilink wikilink_internal"
|
||||
|
||||
switch {
|
||||
case strings.ContainsRune(addr, ':'):
|
||||
pos := strings.IndexRune(addr, ':')
|
||||
destination := addr[:pos]
|
||||
if display == "" {
|
||||
text = addr[pos+1:]
|
||||
if strings.HasPrefix(text, "//") && len(text) > 2 {
|
||||
text = text[2:]
|
||||
}
|
||||
}
|
||||
return addr, text, fmt.Sprintf("wikilink wikilink_external wikilink_%s", destination)
|
||||
case strings.HasPrefix(addr, "/"):
|
||||
return addr, text, class
|
||||
case strings.HasPrefix(addr, "./"):
|
||||
hyphaName = canonicalName(path.Join(hyphaName, addr[2:]))
|
||||
case strings.HasPrefix(addr, "../"):
|
||||
hyphaName = canonicalName(path.Join(path.Dir(hyphaName), addr[3:]))
|
||||
default:
|
||||
hyphaName = canonicalName(addr)
|
||||
}
|
||||
if !HyphaExists(hyphaName) {
|
||||
class += " wikilink_new"
|
||||
}
|
||||
return "/page/" + hyphaName, text, class
|
||||
return l.Href(), l.Display, l.Classes()
|
||||
}
|
||||
|
||||
// Parse markup line starting with "=>" according to wikilink rules.
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/link"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
@ -47,6 +48,11 @@ func (md *MycoDoc) AsHTML() string {
|
||||
return md.html
|
||||
}
|
||||
|
||||
// AsGemtext returns a gemtext representation of the document. Currently really limited, just returns source text
|
||||
func (md *MycoDoc) AsGemtext() string {
|
||||
return md.contents
|
||||
}
|
||||
|
||||
// Used to clear opengraph description from html tags. This method is usually bad because of dangers of malformed HTML, but I'm going to use it only for Mycorrhiza-generated HTML, so it's okay. The question mark is required; without it the whole string is eaten away.
|
||||
var htmlTagRe = regexp.MustCompile(`<.*?>`)
|
||||
|
||||
@ -57,15 +63,16 @@ func (md *MycoDoc) OpenGraphHTML() string {
|
||||
ogTag("title", md.hyphaName),
|
||||
ogTag("type", "article"),
|
||||
ogTag("image", md.firstImageURL),
|
||||
ogTag("url", util.URL+"/page/"+md.hyphaName),
|
||||
ogTag("url", util.URL+"/hypha/"+md.hyphaName),
|
||||
ogTag("determiner", ""),
|
||||
ogTag("description", htmlTagRe.ReplaceAllString(md.description, "")),
|
||||
}, "\n")
|
||||
}
|
||||
|
||||
func (md *MycoDoc) ogFillVars() *MycoDoc {
|
||||
md.firstImageURL = util.URL + "/favicon.ico"
|
||||
foundDesc := false
|
||||
md.firstImageURL = HyphaImageForOG(md.hyphaName)
|
||||
foundImg := false
|
||||
for _, line := range md.ast {
|
||||
switch v := line.contents.(type) {
|
||||
case string:
|
||||
@ -74,8 +81,12 @@ func (md *MycoDoc) ogFillVars() *MycoDoc {
|
||||
foundDesc = true
|
||||
}
|
||||
case Img:
|
||||
if len(v.entries) > 0 {
|
||||
md.firstImageURL = v.entries[0].path.String()
|
||||
if !foundImg && len(v.entries) > 0 {
|
||||
md.firstImageURL = v.entries[0].srclink.ImgSrc()
|
||||
if v.entries[0].srclink.Kind != link.LinkExternal {
|
||||
md.firstImageURL = util.URL + md.firstImageURL
|
||||
}
|
||||
foundImg = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,6 +164,7 @@ func crawl(name, content string) []string {
|
||||
preAcc += html.EscapeString(line)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return []string{}
|
||||
|
56
markup/outlink.go
Normal file
@ -0,0 +1,56 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/link"
|
||||
)
|
||||
|
||||
// OutLinks returns a channel of names of hyphae this mycodocument links.
|
||||
// Links include:
|
||||
// * Regular links
|
||||
// * Rocketlinks
|
||||
// * Transclusion
|
||||
// * Image galleries
|
||||
func (md *MycoDoc) OutLinks() chan string {
|
||||
ch := make(chan string)
|
||||
if !md.parsedAlready {
|
||||
md.Lex(0)
|
||||
}
|
||||
go func() {
|
||||
for _, line := range md.ast {
|
||||
switch v := line.contents.(type) {
|
||||
case string:
|
||||
if strings.HasPrefix(v, "<p") || strings.HasPrefix(v, "<ul class='launchpad'") {
|
||||
extractLinks(v, ch)
|
||||
}
|
||||
case Transclusion:
|
||||
ch <- v.name
|
||||
case Img:
|
||||
extractImageLinks(v, ch)
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
var reLinks = regexp.MustCompile(`<a href="/hypha/([^"]*)".*?</a>`)
|
||||
|
||||
func extractLinks(html string, ch chan string) {
|
||||
if results := reLinks.FindAllStringSubmatch(html, -1); results != nil {
|
||||
for _, result := range results {
|
||||
// result[0] is always present at this point and is not needed, because it is the whole matched substring (which we don't need)
|
||||
ch <- result[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func extractImageLinks(img Img, ch chan string) {
|
||||
for _, entry := range img.entries {
|
||||
if entry.srclink.Kind == link.LinkLocalHypha {
|
||||
ch <- entry.srclink.Address
|
||||
}
|
||||
}
|
||||
}
|
@ -55,7 +55,8 @@ func getLinkNode(input *bytes.Buffer, hyphaName string, isBracketedLink bool) st
|
||||
} else if isBracketedLink && b == ']' && bytes.HasPrefix(input.Bytes(), []byte{']'}) {
|
||||
input.Next(1)
|
||||
break
|
||||
} else if !isBracketedLink && unicode.IsSpace(rune(b)) {
|
||||
} else if !isBracketedLink && (unicode.IsSpace(rune(b)) || strings.ContainsRune("<>{}|\\^[]`,()", rune(b))) {
|
||||
input.UnreadByte()
|
||||
break
|
||||
} else {
|
||||
currBuf.WriteByte(b)
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit be5b922e9b564551601d21ed45bf7d9ced65c6bb
|
||||
Subproject commit e7040f3e0dc41809063b77fcbc12fe33b234ea87
|
62
mime.go
@ -1,62 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func MimeToExtension(mime string) string {
|
||||
mm := map[string]string{
|
||||
"application/octet-stream": "bin",
|
||||
"image/jpeg": "jpg",
|
||||
"image/gif": "gif",
|
||||
"image/png": "png",
|
||||
"image/webp": "webp",
|
||||
"image/svg+xml": "svg",
|
||||
"image/x-icon": "ico",
|
||||
"application/ogg": "ogg",
|
||||
"video/webm": "webm",
|
||||
"audio/mp3": "mp3",
|
||||
"video/mp4": "mp4",
|
||||
}
|
||||
if ext, ok := mm[mime]; ok {
|
||||
return "." + ext
|
||||
}
|
||||
return ".bin"
|
||||
}
|
||||
|
||||
func ExtensionToMime(ext string) string {
|
||||
mm := map[string]string{
|
||||
".bin": "application/octet-stream",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".gif": "image/gif",
|
||||
".png": "image/png",
|
||||
".webp": "image/webp",
|
||||
".svg": "image/svg+xml",
|
||||
".ico": "image/x-icon",
|
||||
".ogg": "application/ogg",
|
||||
".webm": "video/webm",
|
||||
".mp3": "audio/mp3",
|
||||
".mp4": "video/mp4",
|
||||
}
|
||||
if mime, ok := mm[ext]; ok {
|
||||
return mime
|
||||
}
|
||||
return "application/octet-stream"
|
||||
}
|
||||
|
||||
// DataFromFilename fetches all meta information from hypha content file with path `fullPath`. If it is not a content file, `skip` is true, and you are expected to ignore this file when indexing hyphae. `name` is name of the hypha to which this file relates. `isText` is true when the content file is text, false when is binary. `mimeId` is an integer representation of content type. Cast it to TextType if `isText == true`, cast it to BinaryType if `isText == false`.
|
||||
func DataFromFilename(fullPath string) (name string, isText bool, skip bool) {
|
||||
shortPath := strings.TrimPrefix(fullPath, WikiDir)[1:]
|
||||
ext := filepath.Ext(shortPath)
|
||||
name = CanonicalName(strings.TrimSuffix(shortPath, ext))
|
||||
switch ext {
|
||||
case ".myco":
|
||||
isText = true
|
||||
case "", shortPath:
|
||||
skip = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
68
mimetype/mime.go
Normal file
@ -0,0 +1,68 @@
|
||||
package mimetype
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
// ToExtension returns dotted extension for given mime-type.
|
||||
func ToExtension(mime string) string {
|
||||
if ext, ok := mapMime2Ext[mime]; ok {
|
||||
return "." + ext
|
||||
}
|
||||
return ".bin"
|
||||
}
|
||||
|
||||
// FromExtension returns mime-type for given extension. The extension must start with a dot.
|
||||
func FromExtension(ext string) string {
|
||||
if mime, ok := mapExt2Mime[ext]; ok {
|
||||
return mime
|
||||
}
|
||||
return "application/octet-stream"
|
||||
}
|
||||
|
||||
// DataFromFilename fetches all meta information from hypha content file with path `fullPath`. If it is not a content file, `skip` is true, and you are expected to ignore this file when indexing hyphae. `name` is name of the hypha to which this file relates. `isText` is true when the content file is text, false when is binary.
|
||||
func DataFromFilename(fullPath string) (name string, isText bool, skip bool) {
|
||||
shortPath := util.ShorterPath(fullPath)
|
||||
ext := filepath.Ext(shortPath)
|
||||
name = util.CanonicalName(strings.TrimSuffix(shortPath, ext))
|
||||
switch ext {
|
||||
case ".myco":
|
||||
isText = true
|
||||
case "", shortPath:
|
||||
skip = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var mapMime2Ext = map[string]string{
|
||||
"application/octet-stream": "bin",
|
||||
"image/jpeg": "jpg",
|
||||
"image/gif": "gif",
|
||||
"image/png": "png",
|
||||
"image/webp": "webp",
|
||||
"image/svg+xml": "svg",
|
||||
"image/x-icon": "ico",
|
||||
"application/ogg": "ogg",
|
||||
"video/webm": "webm",
|
||||
"audio/mp3": "mp3",
|
||||
"video/mp4": "mp4",
|
||||
}
|
||||
|
||||
var mapExt2Mime = map[string]string{
|
||||
".bin": "application/octet-stream",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".gif": "image/gif",
|
||||
".png": "image/png",
|
||||
".webp": "image/webp",
|
||||
".svg": "image/svg+xml",
|
||||
".ico": "image/x-icon",
|
||||
".ogg": "application/ogg",
|
||||
".webm": "video/webm",
|
||||
".mp3": "audio/mp3",
|
||||
".mp4": "video/mp4",
|
||||
}
|
58
name.go
@ -1,52 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"git.sr.ht/~adnano/go-gemini"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
// isCanonicalName checks if the `name` is canonical.
|
||||
func isCanonicalName(name string) bool {
|
||||
return HyphaPattern.MatchString(name)
|
||||
}
|
||||
|
||||
// CanonicalName makes sure the `name` is canonical. A name is canonical if it is lowercase and all spaces are replaced with underscores.
|
||||
func CanonicalName(name string) string {
|
||||
return strings.ToLower(strings.ReplaceAll(name, " ", "_"))
|
||||
}
|
||||
|
||||
// naviTitle turns `canonicalName` into html string with each hypha path parts higlighted as links.
|
||||
// TODO: rework as a template
|
||||
func naviTitle(canonicalName string) string {
|
||||
var (
|
||||
html = fmt.Sprintf(`<h1 class="navi-title" id="navi-title">
|
||||
<a href="/page/%s">%s</a><span aria-hidden="true" class="navi-title__colon">:</span>`, util.HomePage, util.SiteNavIcon)
|
||||
prevAcc = `/page/`
|
||||
parts = strings.Split(canonicalName, "/")
|
||||
rel = "up"
|
||||
)
|
||||
for i, part := range parts {
|
||||
if i > 0 {
|
||||
html += `<span aria-hidden="true" class="navi-title__separator">/</span>`
|
||||
// HyphaNameFromRq extracts hypha name from http request. You have to also pass the action which is embedded in the url or several actions. For url /hypha/hypha, the action would be "hypha".
|
||||
func HyphaNameFromRq(rq *http.Request, actions ...string) string {
|
||||
p := rq.URL.Path
|
||||
for _, action := range actions {
|
||||
if strings.HasPrefix(p, "/"+action+"/") {
|
||||
return util.CanonicalName(strings.TrimPrefix(p, "/"+action+"/"))
|
||||
}
|
||||
if i == len(parts)-1 {
|
||||
rel = "bookmark"
|
||||
}
|
||||
html += fmt.Sprintf(
|
||||
`<a href="%s" rel="%s">%s</a>`,
|
||||
prevAcc+part,
|
||||
rel,
|
||||
util.BeautifulName(part),
|
||||
)
|
||||
prevAcc += part + "/"
|
||||
}
|
||||
return html + "</h1>"
|
||||
panic("HyphaNameFromRq: no matching action passed")
|
||||
}
|
||||
|
||||
// HyphaNameFromRq extracts hypha name from http request. You have to also pass the action which is embedded in the url. For url /page/hypha, the action would be "page".
|
||||
func HyphaNameFromRq(rq *http.Request, action string) string {
|
||||
return CanonicalName(strings.TrimPrefix(rq.URL.Path, "/"+action+"/"))
|
||||
// geminiHyphaNameFromRq extracts hypha name from gemini request. You have to also pass the action which is embedded in the url or several actions. For url /hypha/hypha, the action would be "hypha".
|
||||
func geminiHyphaNameFromRq(rq *gemini.Request, actions ...string) string {
|
||||
p := rq.URL.Path
|
||||
for _, action := range actions {
|
||||
if strings.HasPrefix(p, "/"+action+"/") {
|
||||
return util.CanonicalName(strings.TrimPrefix(p, "/"+action+"/"))
|
||||
}
|
||||
}
|
||||
log.Fatal("HyphaNameFromRq: no matching action passed")
|
||||
return ""
|
||||
}
|
||||
|
36
shroom/backlink.go
Normal file
@ -0,0 +1,36 @@
|
||||
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())
|
||||
}
|
||||
}
|
92
shroom/can.go
Normal file
@ -0,0 +1,92 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
)
|
||||
|
||||
func canFactory(
|
||||
rejectLogger func(*hyphae.Hypha, *user.User, string),
|
||||
action string,
|
||||
dispatcher func(*hyphae.Hypha, *user.User) (string, string),
|
||||
noRightsMsg string,
|
||||
notExistsMsg string,
|
||||
careAboutExistince bool,
|
||||
) func(*user.User, *hyphae.Hypha) (error, string) {
|
||||
return func(u *user.User, h *hyphae.Hypha) (error, string) {
|
||||
if !u.CanProceed(action) {
|
||||
rejectLogger(h, u, "no rights")
|
||||
return errors.New(noRightsMsg), "Not enough rights"
|
||||
}
|
||||
|
||||
if careAboutExistince && !h.Exists {
|
||||
rejectLogger(h, u, "does not exist")
|
||||
return errors.New(notExistsMsg), "Does not exist"
|
||||
}
|
||||
|
||||
if dispatcher == nil {
|
||||
return nil, ""
|
||||
}
|
||||
errmsg, errtitle := dispatcher(h, u)
|
||||
if errtitle == "" {
|
||||
return nil, ""
|
||||
}
|
||||
return errors.New(errmsg), errtitle
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
CanDelete = canFactory(
|
||||
rejectDeleteLog,
|
||||
"delete-confirm",
|
||||
nil,
|
||||
"Not enough rights to delete, you must be a moderator",
|
||||
"Cannot delete this hypha because it does not exist",
|
||||
true,
|
||||
)
|
||||
|
||||
CanRename = canFactory(
|
||||
rejectRenameLog,
|
||||
"rename-confirm",
|
||||
nil,
|
||||
"Not enough rights to rename, you must be a trusted editor",
|
||||
"Cannot rename this hypha because it does not exist",
|
||||
true,
|
||||
)
|
||||
|
||||
CanUnattach = canFactory(
|
||||
rejectUnattachLog,
|
||||
"unattach-confirm",
|
||||
func(h *hyphae.Hypha, u *user.User) (errmsg, errtitle string) {
|
||||
if h.BinaryPath == "" {
|
||||
rejectUnattachLog(h, u, "no amnt")
|
||||
return "Cannot unattach this hypha because it has no attachment", "No attachment"
|
||||
}
|
||||
|
||||
return "", ""
|
||||
},
|
||||
"Not enough rights to unattach, you must be a trusted editor",
|
||||
"Cannot unattach this hypha because it does not exist",
|
||||
true,
|
||||
)
|
||||
|
||||
CanEdit = canFactory(
|
||||
rejectEditLog,
|
||||
"upload-text",
|
||||
nil,
|
||||
"You must be an editor to edit a hypha",
|
||||
"You cannot edit a hypha that does not exist",
|
||||
false,
|
||||
)
|
||||
|
||||
CanAttach = canFactory(
|
||||
rejectAttachLog,
|
||||
"upload-binary",
|
||||
nil,
|
||||
"You must be an editor to attach a hypha",
|
||||
"You cannot attach a hypha that does not exist",
|
||||
false,
|
||||
)
|
||||
)
|
29
shroom/delete.go
Normal file
@ -0,0 +1,29 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
)
|
||||
|
||||
// DeleteHypha deletes hypha and makes a history record about that.
|
||||
func DeleteHypha(u *user.User, h *hyphae.Hypha) (hop *history.HistoryOp, errtitle string) {
|
||||
hop = history.Operation(history.TypeDeleteHypha)
|
||||
|
||||
if err, errtitle := CanDelete(u, h); errtitle != "" {
|
||||
hop.WithErrAbort(err)
|
||||
return hop, errtitle
|
||||
}
|
||||
|
||||
hop.
|
||||
WithFilesRemoved(h.TextPath, h.BinaryPath).
|
||||
WithMsg(fmt.Sprintf("Delete ‘%s’", h.Name)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
if !hop.HasErrors() {
|
||||
h.Delete()
|
||||
}
|
||||
return hop, ""
|
||||
}
|
38
shroom/init.go
Normal file
@ -0,0 +1,38 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
"github.com/bouncepaw/mycorrhiza/views"
|
||||
)
|
||||
|
||||
func init() {
|
||||
markup.HyphaExists = func(hyphaName string) bool {
|
||||
return hyphae.ByName(hyphaName).Exists
|
||||
}
|
||||
markup.HyphaAccess = func(hyphaName string) (rawText, binaryBlock string, err error) {
|
||||
if h := hyphae.ByName(hyphaName); h.Exists {
|
||||
rawText, err = FetchTextPart(h)
|
||||
if h.BinaryPath != "" {
|
||||
binaryBlock = views.AttachmentHTML(h)
|
||||
}
|
||||
} else {
|
||||
err = errors.New("Hypha " + hyphaName + " does not exist")
|
||||
}
|
||||
return
|
||||
}
|
||||
markup.HyphaIterate = func(λ func(string)) {
|
||||
for h := range hyphae.YieldExistingHyphae() {
|
||||
λ(h.Name)
|
||||
}
|
||||
}
|
||||
markup.HyphaImageForOG = func(hyphaName string) string {
|
||||
if h := hyphae.ByName(hyphaName); h.Exists && h.BinaryPath != "" {
|
||||
return util.URL + "/binary/" + hyphaName
|
||||
}
|
||||
return util.URL + "/favicon.ico"
|
||||
}
|
||||
}
|
24
shroom/log.go
Normal file
@ -0,0 +1,24 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
)
|
||||
|
||||
func rejectDeleteLog(h *hyphae.Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject delete ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
func rejectRenameLog(h *hyphae.Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject rename ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
func rejectUnattachLog(h *hyphae.Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject unattach ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
func rejectEditLog(h *hyphae.Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject edit ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
func rejectAttachLog(h *hyphae.Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject attach ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
106
shroom/rename.go
Normal file
@ -0,0 +1,106 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func canRenameThisToThat(oh *hyphae.Hypha, nh *hyphae.Hypha, u *user.User) (err error, errtitle string) {
|
||||
if nh.Exists {
|
||||
rejectRenameLog(oh, u, fmt.Sprintf("name ‘%s’ taken already", nh.Name))
|
||||
return errors.New(fmt.Sprintf("Hypha named <a href='/hypha/%[1]s'>%[1]s</a> already exists, cannot rename", nh.Name)), "Name taken"
|
||||
}
|
||||
|
||||
if nh.Name == "" {
|
||||
rejectRenameLog(oh, u, "no new name given")
|
||||
return errors.New("No new name is given"), "No name given"
|
||||
}
|
||||
|
||||
if !hyphae.HyphaPattern.MatchString(nh.Name) {
|
||||
rejectRenameLog(oh, u, fmt.Sprintf("new name ‘%s’ invalid", nh.Name))
|
||||
return errors.New("Invalid new name. Names cannot contain characters <code>^?!:#@><*|\"\\'&%</code>"), "Invalid name"
|
||||
}
|
||||
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// RenameHypha renames hypha from old name `hyphaName` to `newName` and makes a history record about that. If `recursive` is `true`, its subhyphae will be renamed the same way.
|
||||
func RenameHypha(h *hyphae.Hypha, newHypha *hyphae.Hypha, recursive bool, u *user.User) (hop *history.HistoryOp, errtitle string) {
|
||||
newHypha.Lock()
|
||||
defer newHypha.Unlock()
|
||||
hop = history.Operation(history.TypeRenameHypha)
|
||||
|
||||
if err, errtitle := CanRename(u, h); errtitle != "" {
|
||||
hop.WithErrAbort(err)
|
||||
return hop, errtitle
|
||||
}
|
||||
if err, errtitle := canRenameThisToThat(h, newHypha, u); errtitle != "" {
|
||||
hop.WithErrAbort(err)
|
||||
return hop, errtitle
|
||||
}
|
||||
|
||||
var (
|
||||
re = regexp.MustCompile(`(?i)` + h.Name)
|
||||
replaceName = func(str string) string {
|
||||
return re.ReplaceAllString(util.CanonicalName(str), newHypha.Name)
|
||||
}
|
||||
hyphaeToRename = findHyphaeToRename(h, recursive)
|
||||
renameMap, err = renamingPairs(hyphaeToRename, replaceName)
|
||||
renameMsg = "Rename ‘%s’ to ‘%s’"
|
||||
)
|
||||
if err != nil {
|
||||
hop.Errs = append(hop.Errs, err)
|
||||
return hop, hop.FirstErrorText()
|
||||
}
|
||||
if recursive && len(hyphaeToRename) > 0 {
|
||||
renameMsg += " recursively"
|
||||
}
|
||||
hop.WithFilesRenamed(renameMap).
|
||||
WithMsg(fmt.Sprintf(renameMsg, h.Name, newHypha.Name)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
if len(hop.Errs) == 0 {
|
||||
for _, h := range hyphaeToRename {
|
||||
h.RenameTo(replaceName(h.Name))
|
||||
h.Lock()
|
||||
h.TextPath = replaceName(h.TextPath)
|
||||
h.BinaryPath = replaceName(h.BinaryPath)
|
||||
h.Unlock()
|
||||
}
|
||||
}
|
||||
return hop, ""
|
||||
}
|
||||
|
||||
func findHyphaeToRename(superhypha *hyphae.Hypha, recursive bool) []*hyphae.Hypha {
|
||||
hyphae := []*hyphae.Hypha{superhypha}
|
||||
if recursive {
|
||||
hyphae = append(hyphae, superhypha.Subhyphae()...)
|
||||
}
|
||||
return hyphae
|
||||
}
|
||||
|
||||
func renamingPairs(hyphaeToRename []*hyphae.Hypha, replaceName func(string) string) (map[string]string, error) {
|
||||
renameMap := make(map[string]string)
|
||||
newNames := make([]string, len(hyphaeToRename))
|
||||
for _, h := range hyphaeToRename {
|
||||
h.Lock()
|
||||
newNames = append(newNames, replaceName(h.Name))
|
||||
if h.TextPath != "" {
|
||||
renameMap[h.TextPath] = replaceName(h.TextPath)
|
||||
}
|
||||
if h.BinaryPath != "" {
|
||||
renameMap[h.BinaryPath] = replaceName(h.BinaryPath)
|
||||
}
|
||||
h.Unlock()
|
||||
}
|
||||
if firstFailure, ok := hyphae.AreFreeNames(newNames...); !ok {
|
||||
return nil, errors.New("Hypha " + firstFailure + " already exists")
|
||||
}
|
||||
return renameMap, nil
|
||||
}
|
41
shroom/unattach.go
Normal file
@ -0,0 +1,41 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
)
|
||||
|
||||
// UnattachHypha unattaches hypha and makes a history record about that.
|
||||
func UnattachHypha(u *user.User, h *hyphae.Hypha) (hop *history.HistoryOp, errtitle string) {
|
||||
hop = history.Operation(history.TypeUnattachHypha)
|
||||
|
||||
if err, errtitle := CanUnattach(u, h); errtitle != "" {
|
||||
hop.WithErrAbort(err)
|
||||
return hop, errtitle
|
||||
}
|
||||
|
||||
hop.
|
||||
WithFilesRemoved(h.BinaryPath).
|
||||
WithMsg(fmt.Sprintf("Unattach ‘%s’", h.Name)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
|
||||
if len(hop.Errs) > 0 {
|
||||
rejectUnattachLog(h, u, "fail")
|
||||
// FIXME: something may be wrong here
|
||||
return hop.WithErrAbort(errors.New(fmt.Sprintf("Could not unattach this hypha due to internal server errors: <code>%v</code>", hop.Errs))), "Error"
|
||||
}
|
||||
|
||||
if h.BinaryPath != "" {
|
||||
h.BinaryPath = ""
|
||||
}
|
||||
// If nothing is left of the hypha
|
||||
if h.TextPath == "" {
|
||||
h.Delete()
|
||||
}
|
||||
return hop, ""
|
||||
}
|
87
shroom/upload.go
Normal file
@ -0,0 +1,87 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func UploadText(h *hyphae.Hypha, data []byte, u *user.User) (hop *history.HistoryOp, errtitle string) {
|
||||
hop = history.Operation(history.TypeEditText)
|
||||
if h.Exists {
|
||||
hop.WithMsg(fmt.Sprintf("Edit ‘%s’", h.Name))
|
||||
} else {
|
||||
hop.WithMsg(fmt.Sprintf("Create ‘%s’", h.Name))
|
||||
}
|
||||
|
||||
if err, errtitle := CanEdit(u, h); err != nil {
|
||||
return hop.WithErrAbort(err), errtitle
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return hop.WithErrAbort(errors.New("No data passed")), "Empty"
|
||||
}
|
||||
|
||||
return uploadHelp(h, hop, ".myco", data, u)
|
||||
}
|
||||
|
||||
func UploadBinary(h *hyphae.Hypha, mime string, file multipart.File, u *user.User) (*history.HistoryOp, string) {
|
||||
var (
|
||||
hop = history.Operation(history.TypeEditBinary).WithMsg(fmt.Sprintf("Upload binary part for ‘%s’ with type ‘%s’", h.Name, mime))
|
||||
data, err = ioutil.ReadAll(file)
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return hop.WithErrAbort(err), err.Error()
|
||||
}
|
||||
if err, errtitle := CanAttach(u, h); err != nil {
|
||||
return hop.WithErrAbort(err), errtitle
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return hop.WithErrAbort(errors.New("No data passed")), "Empty"
|
||||
}
|
||||
|
||||
return uploadHelp(h, hop, mimetype.ToExtension(mime), data, u)
|
||||
}
|
||||
|
||||
// uploadHelp is a helper function for UploadText and UploadBinary
|
||||
func uploadHelp(h *hyphae.Hypha, hop *history.HistoryOp, ext string, data []byte, u *user.User) (*history.HistoryOp, string) {
|
||||
var (
|
||||
fullPath = filepath.Join(util.WikiDir, h.Name+ext)
|
||||
originalFullPath = &h.TextPath
|
||||
)
|
||||
if hop.Type == history.TypeEditBinary {
|
||||
originalFullPath = &h.BinaryPath
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
|
||||
return hop.WithErrAbort(err), err.Error()
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(fullPath, data, 0644); err != nil {
|
||||
return hop.WithErrAbort(err), err.Error()
|
||||
}
|
||||
|
||||
if h.Exists && *originalFullPath != fullPath && *originalFullPath != "" {
|
||||
if err := history.Rename(*originalFullPath, fullPath); err != nil {
|
||||
return hop.WithErrAbort(err), err.Error()
|
||||
}
|
||||
log.Println("Move", *originalFullPath, "to", fullPath)
|
||||
}
|
||||
|
||||
h.InsertIfNew()
|
||||
if h.Exists && h.TextPath != "" && hop.Type == history.TypeEditText && !history.FileChanged(fullPath) {
|
||||
return hop.Abort(), "No changes"
|
||||
}
|
||||
*originalFullPath = fullPath
|
||||
return hop.WithFiles(fullPath).WithUser(u).Apply(), ""
|
||||
}
|
38
shroom/view.go
Normal file
@ -0,0 +1,38 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
// FetchTextPart tries to read text file of the given hypha. If there is no file, empty string is returned.
|
||||
func FetchTextPart(h *hyphae.Hypha) (string, error) {
|
||||
if h.TextPath == "" {
|
||||
return "", nil
|
||||
}
|
||||
text, err := ioutil.ReadFile(h.TextPath)
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(text), nil
|
||||
}
|
||||
|
||||
func SetHeaderLinks() {
|
||||
if userLinksHypha := hyphae.ByName(util.HeaderLinksHypha); !userLinksHypha.Exists {
|
||||
util.SetDefaultHeaderLinks()
|
||||
} else {
|
||||
contents, err := ioutil.ReadFile(userLinksHypha.TextPath)
|
||||
if err != nil || len(contents) == 0 {
|
||||
util.SetDefaultHeaderLinks()
|
||||
} else {
|
||||
text := string(contents)
|
||||
util.ParseHeaderLinks(text, markup.Rocketlink)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||
|
||||
{% func LoginHTML() %}
|
||||
<main>
|
||||
<section>
|
||||
{% if user.AuthUsed %}
|
||||
<h1>Login</h1>
|
||||
<form method="post" action="/login-data" id="login-form" enctype="multipart/form-data">
|
||||
<p>Use the data you were given by an administrator.</p>
|
||||
<fieldset>
|
||||
<legend>Username</legend>
|
||||
<input type="text" required autofocus name="username" autocomplete="on">
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Password</legend>
|
||||
<input type="password" required name="password" autocomplete="on">
|
||||
</fieldset>
|
||||
<p>By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you. You will stay logged in until you log out.</p>
|
||||
<input type="submit">
|
||||
<a href="/">Cancel</a>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>Administrator of this wiki have not configured any authorization method. You can make edits anonymously.</p>
|
||||
<p><a href="/">← Go home</a></p>
|
||||
{% endif %}
|
||||
</section>
|
||||
</main>
|
||||
{% endfunc %}
|
||||
|
||||
{% func LoginErrorHTML(err string) %}
|
||||
<main>
|
||||
<section>
|
||||
{% switch err %}
|
||||
{% case "unknown username" %}
|
||||
<p class="error">Unknown username.</p>
|
||||
{% case "wrong password" %}
|
||||
<p class="error">Wrong password.</p>
|
||||
{% default %}
|
||||
<p class="error">{%s err %}</p>
|
||||
{% endswitch %}
|
||||
<p><a href="/login">← Try again</a></p>
|
||||
</section>
|
||||
</main>
|
||||
{% endfunc %}
|
||||
|
||||
{% func LogoutHTML(can bool) %}
|
||||
<main>
|
||||
<section>
|
||||
{% if can %}
|
||||
<h1>Log out?</h1>
|
||||
<p><a href="/logout-confirm"><strong>Confirm</strong></a></p>
|
||||
<p><a href="/">Cancel</a></p>
|
||||
{% else %}
|
||||
<p>You cannot log out because you are not logged in.</p>
|
||||
<p><a href="/login">Login</a></p>
|
||||
<p><a href="/login">← Home</a></p>
|
||||
{% endif %}
|
||||
</section>
|
||||
</main>
|
||||
{% endfunc %}
|
@ -1,218 +0,0 @@
|
||||
// Code generated by qtc from "auth.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line templates/auth.qtpl:1
|
||||
package templates
|
||||
|
||||
//line templates/auth.qtpl:1
|
||||
import "github.com/bouncepaw/mycorrhiza/user"
|
||||
|
||||
//line templates/auth.qtpl:3
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line templates/auth.qtpl:3
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line templates/auth.qtpl:3
|
||||
func StreamLoginHTML(qw422016 *qt422016.Writer) {
|
||||
//line templates/auth.qtpl:3
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
<section>
|
||||
`)
|
||||
//line templates/auth.qtpl:6
|
||||
if user.AuthUsed {
|
||||
//line templates/auth.qtpl:6
|
||||
qw422016.N().S(`
|
||||
<h1>Login</h1>
|
||||
<form method="post" action="/login-data" id="login-form" enctype="multipart/form-data">
|
||||
<p>Use the data you were given by an administrator.</p>
|
||||
<fieldset>
|
||||
<legend>Username</legend>
|
||||
<input type="text" required autofocus name="username" autocomplete="on">
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Password</legend>
|
||||
<input type="password" required name="password" autocomplete="on">
|
||||
</fieldset>
|
||||
<p>By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you. You will stay logged in until you log out.</p>
|
||||
<input type="submit">
|
||||
<a href="/">Cancel</a>
|
||||
</form>
|
||||
`)
|
||||
//line templates/auth.qtpl:22
|
||||
} else {
|
||||
//line templates/auth.qtpl:22
|
||||
qw422016.N().S(`
|
||||
<p>Administrator of this wiki have not configured any authorization method. You can make edits anonymously.</p>
|
||||
<p><a href="/">← Go home</a></p>
|
||||
`)
|
||||
//line templates/auth.qtpl:25
|
||||
}
|
||||
//line templates/auth.qtpl:25
|
||||
qw422016.N().S(`
|
||||
</section>
|
||||
</main>
|
||||
`)
|
||||
//line templates/auth.qtpl:28
|
||||
}
|
||||
|
||||
//line templates/auth.qtpl:28
|
||||
func WriteLoginHTML(qq422016 qtio422016.Writer) {
|
||||
//line templates/auth.qtpl:28
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/auth.qtpl:28
|
||||
StreamLoginHTML(qw422016)
|
||||
//line templates/auth.qtpl:28
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/auth.qtpl:28
|
||||
}
|
||||
|
||||
//line templates/auth.qtpl:28
|
||||
func LoginHTML() string {
|
||||
//line templates/auth.qtpl:28
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/auth.qtpl:28
|
||||
WriteLoginHTML(qb422016)
|
||||
//line templates/auth.qtpl:28
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/auth.qtpl:28
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/auth.qtpl:28
|
||||
return qs422016
|
||||
//line templates/auth.qtpl:28
|
||||
}
|
||||
|
||||
//line templates/auth.qtpl:30
|
||||
func StreamLoginErrorHTML(qw422016 *qt422016.Writer, err string) {
|
||||
//line templates/auth.qtpl:30
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
<section>
|
||||
`)
|
||||
//line templates/auth.qtpl:33
|
||||
switch err {
|
||||
//line templates/auth.qtpl:34
|
||||
case "unknown username":
|
||||
//line templates/auth.qtpl:34
|
||||
qw422016.N().S(`
|
||||
<p class="error">Unknown username.</p>
|
||||
`)
|
||||
//line templates/auth.qtpl:36
|
||||
case "wrong password":
|
||||
//line templates/auth.qtpl:36
|
||||
qw422016.N().S(`
|
||||
<p class="error">Wrong password.</p>
|
||||
`)
|
||||
//line templates/auth.qtpl:38
|
||||
default:
|
||||
//line templates/auth.qtpl:38
|
||||
qw422016.N().S(`
|
||||
<p class="error">`)
|
||||
//line templates/auth.qtpl:39
|
||||
qw422016.E().S(err)
|
||||
//line templates/auth.qtpl:39
|
||||
qw422016.N().S(`</p>
|
||||
`)
|
||||
//line templates/auth.qtpl:40
|
||||
}
|
||||
//line templates/auth.qtpl:40
|
||||
qw422016.N().S(`
|
||||
<p><a href="/login">← Try again</a></p>
|
||||
</section>
|
||||
</main>
|
||||
`)
|
||||
//line templates/auth.qtpl:44
|
||||
}
|
||||
|
||||
//line templates/auth.qtpl:44
|
||||
func WriteLoginErrorHTML(qq422016 qtio422016.Writer, err string) {
|
||||
//line templates/auth.qtpl:44
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/auth.qtpl:44
|
||||
StreamLoginErrorHTML(qw422016, err)
|
||||
//line templates/auth.qtpl:44
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/auth.qtpl:44
|
||||
}
|
||||
|
||||
//line templates/auth.qtpl:44
|
||||
func LoginErrorHTML(err string) string {
|
||||
//line templates/auth.qtpl:44
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/auth.qtpl:44
|
||||
WriteLoginErrorHTML(qb422016, err)
|
||||
//line templates/auth.qtpl:44
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/auth.qtpl:44
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/auth.qtpl:44
|
||||
return qs422016
|
||||
//line templates/auth.qtpl:44
|
||||
}
|
||||
|
||||
//line templates/auth.qtpl:46
|
||||
func StreamLogoutHTML(qw422016 *qt422016.Writer, can bool) {
|
||||
//line templates/auth.qtpl:46
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
<section>
|
||||
`)
|
||||
//line templates/auth.qtpl:49
|
||||
if can {
|
||||
//line templates/auth.qtpl:49
|
||||
qw422016.N().S(`
|
||||
<h1>Log out?</h1>
|
||||
<p><a href="/logout-confirm"><strong>Confirm</strong></a></p>
|
||||
<p><a href="/">Cancel</a></p>
|
||||
`)
|
||||
//line templates/auth.qtpl:53
|
||||
} else {
|
||||
//line templates/auth.qtpl:53
|
||||
qw422016.N().S(`
|
||||
<p>You cannot log out because you are not logged in.</p>
|
||||
<p><a href="/login">Login</a></p>
|
||||
<p><a href="/login">← Home</a></p>
|
||||
`)
|
||||
//line templates/auth.qtpl:57
|
||||
}
|
||||
//line templates/auth.qtpl:57
|
||||
qw422016.N().S(`
|
||||
</section>
|
||||
</main>
|
||||
`)
|
||||
//line templates/auth.qtpl:60
|
||||
}
|
||||
|
||||
//line templates/auth.qtpl:60
|
||||
func WriteLogoutHTML(qq422016 qtio422016.Writer, can bool) {
|
||||
//line templates/auth.qtpl:60
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/auth.qtpl:60
|
||||
StreamLogoutHTML(qw422016, can)
|
||||
//line templates/auth.qtpl:60
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/auth.qtpl:60
|
||||
}
|
||||
|
||||
//line templates/auth.qtpl:60
|
||||
func LogoutHTML(can bool) string {
|
||||
//line templates/auth.qtpl:60
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/auth.qtpl:60
|
||||
WriteLogoutHTML(qb422016, can)
|
||||
//line templates/auth.qtpl:60
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/auth.qtpl:60
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/auth.qtpl:60
|
||||
return qs422016
|
||||
//line templates/auth.qtpl:60
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
// Code generated by qtc from "common.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line templates/common.qtpl:1
|
||||
package templates
|
||||
|
||||
//line templates/common.qtpl:1
|
||||
import "net/http"
|
||||
|
||||
//line templates/common.qtpl:2
|
||||
import "github.com/bouncepaw/mycorrhiza/user"
|
||||
|
||||
//line templates/common.qtpl:3
|
||||
import "github.com/bouncepaw/mycorrhiza/util"
|
||||
|
||||
// This is the <nav> seen on top of many pages.
|
||||
|
||||
//line templates/common.qtpl:6
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line templates/common.qtpl:6
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line templates/common.qtpl:7
|
||||
type navEntry struct {
|
||||
path string
|
||||
title string
|
||||
}
|
||||
|
||||
var navEntries = []navEntry{
|
||||
{"page", "Hypha"},
|
||||
{"edit", "Edit"},
|
||||
{"text", "Raw text"},
|
||||
{"history", "History"},
|
||||
{"revision", "NOT REACHED"},
|
||||
{"rename-ask", "Rename"},
|
||||
{"delete-ask", "Delete"},
|
||||
}
|
||||
|
||||
//line templates/common.qtpl:22
|
||||
func streamnavHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, navType string, revisionHash ...string) {
|
||||
//line templates/common.qtpl:22
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/common.qtpl:24
|
||||
u := user.FromRequest(rq)
|
||||
|
||||
//line templates/common.qtpl:25
|
||||
qw422016.N().S(`
|
||||
|
||||
<nav class="hypha-tabs">
|
||||
<ul class="hypha-tabs__flex">
|
||||
`)
|
||||
//line templates/common.qtpl:29
|
||||
for _, entry := range navEntries {
|
||||
//line templates/common.qtpl:30
|
||||
if navType == "revision" && entry.path == "revision" {
|
||||
//line templates/common.qtpl:30
|
||||
qw422016.N().S(` <li class="hypha-tabs__tab hypha-tabs__tab_active">
|
||||
`)
|
||||
//line templates/common.qtpl:32
|
||||
qw422016.E().S(revisionHash[0])
|
||||
//line templates/common.qtpl:32
|
||||
qw422016.N().S(`
|
||||
</li>
|
||||
`)
|
||||
//line templates/common.qtpl:34
|
||||
} else if navType == entry.path {
|
||||
//line templates/common.qtpl:34
|
||||
qw422016.N().S(` <li class="hypha-tabs__tab hypha-tabs__tab_active">
|
||||
`)
|
||||
//line templates/common.qtpl:36
|
||||
qw422016.E().S(entry.title)
|
||||
//line templates/common.qtpl:36
|
||||
qw422016.N().S(`
|
||||
</li>
|
||||
`)
|
||||
//line templates/common.qtpl:38
|
||||
} else if entry.path != "revision" && u.CanProceed(entry.path) {
|
||||
//line templates/common.qtpl:38
|
||||
qw422016.N().S(` <li class="hypha-tabs__tab">
|
||||
<a href="/`)
|
||||
//line templates/common.qtpl:40
|
||||
qw422016.E().S(entry.path)
|
||||
//line templates/common.qtpl:40
|
||||
qw422016.N().S(`/`)
|
||||
//line templates/common.qtpl:40
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/common.qtpl:40
|
||||
qw422016.N().S(`">`)
|
||||
//line templates/common.qtpl:40
|
||||
qw422016.E().S(entry.title)
|
||||
//line templates/common.qtpl:40
|
||||
qw422016.N().S(`</a>
|
||||
</li>
|
||||
`)
|
||||
//line templates/common.qtpl:42
|
||||
}
|
||||
//line templates/common.qtpl:43
|
||||
}
|
||||
//line templates/common.qtpl:43
|
||||
qw422016.N().S(` </ul>
|
||||
</nav>
|
||||
`)
|
||||
//line templates/common.qtpl:46
|
||||
}
|
||||
|
||||
//line templates/common.qtpl:46
|
||||
func writenavHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, navType string, revisionHash ...string) {
|
||||
//line templates/common.qtpl:46
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/common.qtpl:46
|
||||
streamnavHTML(qw422016, rq, hyphaName, navType, revisionHash...)
|
||||
//line templates/common.qtpl:46
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/common.qtpl:46
|
||||
}
|
||||
|
||||
//line templates/common.qtpl:46
|
||||
func navHTML(rq *http.Request, hyphaName, navType string, revisionHash ...string) string {
|
||||
//line templates/common.qtpl:46
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/common.qtpl:46
|
||||
writenavHTML(qb422016, rq, hyphaName, navType, revisionHash...)
|
||||
//line templates/common.qtpl:46
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/common.qtpl:46
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/common.qtpl:46
|
||||
return qs422016
|
||||
//line templates/common.qtpl:46
|
||||
}
|
||||
|
||||
//line templates/common.qtpl:48
|
||||
func streamuserMenuHTML(qw422016 *qt422016.Writer, u *user.User) {
|
||||
//line templates/common.qtpl:48
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/common.qtpl:49
|
||||
if user.AuthUsed {
|
||||
//line templates/common.qtpl:49
|
||||
qw422016.N().S(`
|
||||
<li class="header-links__entry header-links__entry_user">
|
||||
`)
|
||||
//line templates/common.qtpl:51
|
||||
if u.Group == "anon" {
|
||||
//line templates/common.qtpl:51
|
||||
qw422016.N().S(`
|
||||
<a href="/login" class="header-links__link">Login</a>
|
||||
`)
|
||||
//line templates/common.qtpl:53
|
||||
} else {
|
||||
//line templates/common.qtpl:53
|
||||
qw422016.N().S(`
|
||||
<a href="/page/`)
|
||||
//line templates/common.qtpl:54
|
||||
qw422016.E().S(util.UserHypha)
|
||||
//line templates/common.qtpl:54
|
||||
qw422016.N().S(`/`)
|
||||
//line templates/common.qtpl:54
|
||||
qw422016.E().S(u.Name)
|
||||
//line templates/common.qtpl:54
|
||||
qw422016.N().S(`" class="header-links__link">`)
|
||||
//line templates/common.qtpl:54
|
||||
qw422016.E().S(u.Name)
|
||||
//line templates/common.qtpl:54
|
||||
qw422016.N().S(`</a>
|
||||
`)
|
||||
//line templates/common.qtpl:55
|
||||
}
|
||||
//line templates/common.qtpl:55
|
||||
qw422016.N().S(`
|
||||
</li>
|
||||
`)
|
||||
//line templates/common.qtpl:57
|
||||
}
|
||||
//line templates/common.qtpl:57
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/common.qtpl:58
|
||||
}
|
||||
|
||||
//line templates/common.qtpl:58
|
||||
func writeuserMenuHTML(qq422016 qtio422016.Writer, u *user.User) {
|
||||
//line templates/common.qtpl:58
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/common.qtpl:58
|
||||
streamuserMenuHTML(qw422016, u)
|
||||
//line templates/common.qtpl:58
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/common.qtpl:58
|
||||
}
|
||||
|
||||
//line templates/common.qtpl:58
|
||||
func userMenuHTML(u *user.User) string {
|
||||
//line templates/common.qtpl:58
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/common.qtpl:58
|
||||
writeuserMenuHTML(qb422016, u)
|
||||
//line templates/common.qtpl:58
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/common.qtpl:58
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/common.qtpl:58
|
||||
return qs422016
|
||||
//line templates/common.qtpl:58
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{% import "net/http" %}
|
||||
|
||||
This dialog is to be shown to a user when they try to delete a hypha.
|
||||
{% func DeleteAskHTML(rq *http.Request, hyphaName string, isOld bool) %}
|
||||
{%= navHTML(rq, hyphaName, "delete-ask") %}
|
||||
<main>
|
||||
{% if isOld %}
|
||||
<section>
|
||||
<h1>Delete {%s hyphaName %}?</h1>
|
||||
<p>Do you really want to delete hypha <em>{%s hyphaName %}</em>?</p>
|
||||
<p>In this version of MycorrhizaWiki you cannot undelete a deleted hypha but the history can still be accessed.</p>
|
||||
<p><a href="/delete-confirm/{%s hyphaName %}"><strong>Confirm</strong></a></p>
|
||||
<p><a href="/page/{%s hyphaName %}">Cancel</a></p>
|
||||
</section>
|
||||
{% else %}
|
||||
{%= cannotDeleteDueToNonExistence(hyphaName) %}
|
||||
{% endif %}
|
||||
</main>
|
||||
{% endfunc %}
|
||||
|
||||
{% func cannotDeleteDueToNonExistence(hyphaName string) %}
|
||||
<section>
|
||||
<h1>Cannot delete {%s hyphaName %}</h1>
|
||||
<p>This hypha does not exist.</p>
|
||||
<p><a href="/page/{%s hyphaName %}">Go back</a></p>
|
||||
</section>
|
||||
{% endfunc %}
|
@ -1,154 +0,0 @@
|
||||
// Code generated by qtc from "delete.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line templates/delete.qtpl:1
|
||||
package templates
|
||||
|
||||
//line templates/delete.qtpl:1
|
||||
import "net/http"
|
||||
|
||||
// This dialog is to be shown to a user when they try to delete a hypha.
|
||||
|
||||
//line templates/delete.qtpl:4
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line templates/delete.qtpl:4
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line templates/delete.qtpl:4
|
||||
func StreamDeleteAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line templates/delete.qtpl:4
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/delete.qtpl:5
|
||||
streamnavHTML(qw422016, rq, hyphaName, "delete-ask")
|
||||
//line templates/delete.qtpl:5
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
`)
|
||||
//line templates/delete.qtpl:7
|
||||
if isOld {
|
||||
//line templates/delete.qtpl:7
|
||||
qw422016.N().S(`
|
||||
<section>
|
||||
<h1>Delete `)
|
||||
//line templates/delete.qtpl:9
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/delete.qtpl:9
|
||||
qw422016.N().S(`?</h1>
|
||||
<p>Do you really want to delete hypha <em>`)
|
||||
//line templates/delete.qtpl:10
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/delete.qtpl:10
|
||||
qw422016.N().S(`</em>?</p>
|
||||
<p>In this version of MycorrhizaWiki you cannot undelete a deleted hypha but the history can still be accessed.</p>
|
||||
<p><a href="/delete-confirm/`)
|
||||
//line templates/delete.qtpl:12
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/delete.qtpl:12
|
||||
qw422016.N().S(`"><strong>Confirm</strong></a></p>
|
||||
<p><a href="/page/`)
|
||||
//line templates/delete.qtpl:13
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/delete.qtpl:13
|
||||
qw422016.N().S(`">Cancel</a></p>
|
||||
</section>
|
||||
`)
|
||||
//line templates/delete.qtpl:15
|
||||
} else {
|
||||
//line templates/delete.qtpl:15
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/delete.qtpl:16
|
||||
streamcannotDeleteDueToNonExistence(qw422016, hyphaName)
|
||||
//line templates/delete.qtpl:16
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/delete.qtpl:17
|
||||
}
|
||||
//line templates/delete.qtpl:17
|
||||
qw422016.N().S(`
|
||||
</main>
|
||||
`)
|
||||
//line templates/delete.qtpl:19
|
||||
}
|
||||
|
||||
//line templates/delete.qtpl:19
|
||||
func WriteDeleteAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line templates/delete.qtpl:19
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/delete.qtpl:19
|
||||
StreamDeleteAskHTML(qw422016, rq, hyphaName, isOld)
|
||||
//line templates/delete.qtpl:19
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/delete.qtpl:19
|
||||
}
|
||||
|
||||
//line templates/delete.qtpl:19
|
||||
func DeleteAskHTML(rq *http.Request, hyphaName string, isOld bool) string {
|
||||
//line templates/delete.qtpl:19
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/delete.qtpl:19
|
||||
WriteDeleteAskHTML(qb422016, rq, hyphaName, isOld)
|
||||
//line templates/delete.qtpl:19
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/delete.qtpl:19
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/delete.qtpl:19
|
||||
return qs422016
|
||||
//line templates/delete.qtpl:19
|
||||
}
|
||||
|
||||
//line templates/delete.qtpl:21
|
||||
func streamcannotDeleteDueToNonExistence(qw422016 *qt422016.Writer, hyphaName string) {
|
||||
//line templates/delete.qtpl:21
|
||||
qw422016.N().S(`
|
||||
<section>
|
||||
<h1>Cannot delete `)
|
||||
//line templates/delete.qtpl:23
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/delete.qtpl:23
|
||||
qw422016.N().S(`</h1>
|
||||
<p>This hypha does not exist.</p>
|
||||
<p><a href="/page/`)
|
||||
//line templates/delete.qtpl:25
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/delete.qtpl:25
|
||||
qw422016.N().S(`">Go back</a></p>
|
||||
</section>
|
||||
`)
|
||||
//line templates/delete.qtpl:27
|
||||
}
|
||||
|
||||
//line templates/delete.qtpl:27
|
||||
func writecannotDeleteDueToNonExistence(qq422016 qtio422016.Writer, hyphaName string) {
|
||||
//line templates/delete.qtpl:27
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/delete.qtpl:27
|
||||
streamcannotDeleteDueToNonExistence(qw422016, hyphaName)
|
||||
//line templates/delete.qtpl:27
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/delete.qtpl:27
|
||||
}
|
||||
|
||||
//line templates/delete.qtpl:27
|
||||
func cannotDeleteDueToNonExistence(hyphaName string) string {
|
||||
//line templates/delete.qtpl:27
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/delete.qtpl:27
|
||||
writecannotDeleteDueToNonExistence(qb422016, hyphaName)
|
||||
//line templates/delete.qtpl:27
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/delete.qtpl:27
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/delete.qtpl:27
|
||||
return qs422016
|
||||
//line templates/delete.qtpl:27
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
{% import "net/http" %}
|
||||
{% import "path" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||
|
||||
{% func HistoryHTML(rq *http.Request, hyphaName, list string) %}
|
||||
{%= navHTML(rq, hyphaName, "history") %}
|
||||
<main>
|
||||
<article class="history">
|
||||
<h1>History of {%s hyphaName %}</h1>
|
||||
{%s= list %}
|
||||
</article>
|
||||
</main>
|
||||
{% endfunc %}
|
||||
|
||||
{% func RevisionHTML(rq *http.Request, hyphaName, naviTitle, contents, tree, revHash string) %}
|
||||
{%= navHTML(rq, hyphaName, "revision", revHash) %}
|
||||
<main>
|
||||
<article>
|
||||
<p>Please note that viewing binary parts of hyphae is not supported in history for now.</p>
|
||||
{%s= naviTitle %}
|
||||
{%s= contents %}
|
||||
</article>
|
||||
<hr/>
|
||||
<aside>
|
||||
{%s= tree %}
|
||||
</aside>
|
||||
</main>
|
||||
{% endfunc %}
|
||||
|
||||
If `contents` == "", a helpful message is shown instead.
|
||||
{% func PageHTML(rq *http.Request, hyphaName, naviTitle, contents, tree, prevHyphaName, nextHyphaName string, hasAmnt bool) %}
|
||||
{%= navHTML(rq, hyphaName, "page") %}
|
||||
<main>
|
||||
<article>
|
||||
{%s= naviTitle %}
|
||||
{% if contents == "" %}
|
||||
<p>This hypha has no text. Why not <a href="/edit/{%s hyphaName %}">create it</a>?</p>
|
||||
{% else %}
|
||||
{%s= contents %}
|
||||
{% endif %}
|
||||
</article>
|
||||
<section class="prevnext">
|
||||
{% if prevHyphaName != "" %}
|
||||
<a class="prevnext__el prevnext__prev" href="/page/{%s prevHyphaName %}" rel="prev">← {%s path.Base(prevHyphaName) %}</a>
|
||||
{% endif %}
|
||||
{% if nextHyphaName != "" %}
|
||||
<a class="prevnext__el prevnext__next" href="/page/{%s nextHyphaName %}" rel="next">{%s path.Base(nextHyphaName) %} →</a>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% if u := user.FromRequest(rq); !user.AuthUsed || u.Group != "anon" %}
|
||||
<form action="/upload-binary/{%s hyphaName %}"
|
||||
method="post" enctype="multipart/form-data"
|
||||
class="upload-amnt">
|
||||
{% if hasAmnt %}
|
||||
<a class="upload-amnt__unattach" href="/unattach-ask/{%s hyphaName %}">Unattach current attachment?</a>
|
||||
{% endif %}
|
||||
<label for="upload-binary__input">Upload a new attachment</label>
|
||||
<br>
|
||||
<input type="file" id="upload-binary__input" name="binary"/>
|
||||
<input type="submit"/>
|
||||
</form>
|
||||
{% endif %}
|
||||
<aside>
|
||||
{%s= tree %}
|
||||
</aside>
|
||||
</main>
|
||||
{% endfunc %}
|
@ -1,301 +0,0 @@
|
||||
// Code generated by qtc from "http_readers.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line templates/http_readers.qtpl:1
|
||||
package templates
|
||||
|
||||
//line templates/http_readers.qtpl:1
|
||||
import "net/http"
|
||||
|
||||
//line templates/http_readers.qtpl:2
|
||||
import "path"
|
||||
|
||||
//line templates/http_readers.qtpl:3
|
||||
import "github.com/bouncepaw/mycorrhiza/user"
|
||||
|
||||
//line templates/http_readers.qtpl:5
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line templates/http_readers.qtpl:5
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line templates/http_readers.qtpl:5
|
||||
func StreamHistoryHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, list string) {
|
||||
//line templates/http_readers.qtpl:5
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/http_readers.qtpl:6
|
||||
streamnavHTML(qw422016, rq, hyphaName, "history")
|
||||
//line templates/http_readers.qtpl:6
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
<article class="history">
|
||||
<h1>History of `)
|
||||
//line templates/http_readers.qtpl:9
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_readers.qtpl:9
|
||||
qw422016.N().S(`</h1>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:10
|
||||
qw422016.N().S(list)
|
||||
//line templates/http_readers.qtpl:10
|
||||
qw422016.N().S(`
|
||||
</article>
|
||||
</main>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:13
|
||||
}
|
||||
|
||||
//line templates/http_readers.qtpl:13
|
||||
func WriteHistoryHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, list string) {
|
||||
//line templates/http_readers.qtpl:13
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/http_readers.qtpl:13
|
||||
StreamHistoryHTML(qw422016, rq, hyphaName, list)
|
||||
//line templates/http_readers.qtpl:13
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/http_readers.qtpl:13
|
||||
}
|
||||
|
||||
//line templates/http_readers.qtpl:13
|
||||
func HistoryHTML(rq *http.Request, hyphaName, list string) string {
|
||||
//line templates/http_readers.qtpl:13
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/http_readers.qtpl:13
|
||||
WriteHistoryHTML(qb422016, rq, hyphaName, list)
|
||||
//line templates/http_readers.qtpl:13
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/http_readers.qtpl:13
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/http_readers.qtpl:13
|
||||
return qs422016
|
||||
//line templates/http_readers.qtpl:13
|
||||
}
|
||||
|
||||
//line templates/http_readers.qtpl:15
|
||||
func StreamRevisionHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, tree, revHash string) {
|
||||
//line templates/http_readers.qtpl:15
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/http_readers.qtpl:16
|
||||
streamnavHTML(qw422016, rq, hyphaName, "revision", revHash)
|
||||
//line templates/http_readers.qtpl:16
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
<article>
|
||||
<p>Please note that viewing binary parts of hyphae is not supported in history for now.</p>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:20
|
||||
qw422016.N().S(naviTitle)
|
||||
//line templates/http_readers.qtpl:20
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/http_readers.qtpl:21
|
||||
qw422016.N().S(contents)
|
||||
//line templates/http_readers.qtpl:21
|
||||
qw422016.N().S(`
|
||||
</article>
|
||||
<hr/>
|
||||
<aside>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:25
|
||||
qw422016.N().S(tree)
|
||||
//line templates/http_readers.qtpl:25
|
||||
qw422016.N().S(`
|
||||
</aside>
|
||||
</main>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:28
|
||||
}
|
||||
|
||||
//line templates/http_readers.qtpl:28
|
||||
func WriteRevisionHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, tree, revHash string) {
|
||||
//line templates/http_readers.qtpl:28
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/http_readers.qtpl:28
|
||||
StreamRevisionHTML(qw422016, rq, hyphaName, naviTitle, contents, tree, revHash)
|
||||
//line templates/http_readers.qtpl:28
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/http_readers.qtpl:28
|
||||
}
|
||||
|
||||
//line templates/http_readers.qtpl:28
|
||||
func RevisionHTML(rq *http.Request, hyphaName, naviTitle, contents, tree, revHash string) string {
|
||||
//line templates/http_readers.qtpl:28
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/http_readers.qtpl:28
|
||||
WriteRevisionHTML(qb422016, rq, hyphaName, naviTitle, contents, tree, revHash)
|
||||
//line templates/http_readers.qtpl:28
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/http_readers.qtpl:28
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/http_readers.qtpl:28
|
||||
return qs422016
|
||||
//line templates/http_readers.qtpl:28
|
||||
}
|
||||
|
||||
// If `contents` == "", a helpful message is shown instead.
|
||||
|
||||
//line templates/http_readers.qtpl:31
|
||||
func StreamPageHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, tree, prevHyphaName, nextHyphaName string, hasAmnt bool) {
|
||||
//line templates/http_readers.qtpl:31
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/http_readers.qtpl:32
|
||||
streamnavHTML(qw422016, rq, hyphaName, "page")
|
||||
//line templates/http_readers.qtpl:32
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
<article>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:35
|
||||
qw422016.N().S(naviTitle)
|
||||
//line templates/http_readers.qtpl:35
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/http_readers.qtpl:36
|
||||
if contents == "" {
|
||||
//line templates/http_readers.qtpl:36
|
||||
qw422016.N().S(`
|
||||
<p>This hypha has no text. Why not <a href="/edit/`)
|
||||
//line templates/http_readers.qtpl:37
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_readers.qtpl:37
|
||||
qw422016.N().S(`">create it</a>?</p>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:38
|
||||
} else {
|
||||
//line templates/http_readers.qtpl:38
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/http_readers.qtpl:39
|
||||
qw422016.N().S(contents)
|
||||
//line templates/http_readers.qtpl:39
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/http_readers.qtpl:40
|
||||
}
|
||||
//line templates/http_readers.qtpl:40
|
||||
qw422016.N().S(`
|
||||
</article>
|
||||
<section class="prevnext">
|
||||
`)
|
||||
//line templates/http_readers.qtpl:43
|
||||
if prevHyphaName != "" {
|
||||
//line templates/http_readers.qtpl:43
|
||||
qw422016.N().S(`
|
||||
<a class="prevnext__el prevnext__prev" href="/page/`)
|
||||
//line templates/http_readers.qtpl:44
|
||||
qw422016.E().S(prevHyphaName)
|
||||
//line templates/http_readers.qtpl:44
|
||||
qw422016.N().S(`" rel="prev">← `)
|
||||
//line templates/http_readers.qtpl:44
|
||||
qw422016.E().S(path.Base(prevHyphaName))
|
||||
//line templates/http_readers.qtpl:44
|
||||
qw422016.N().S(`</a>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:45
|
||||
}
|
||||
//line templates/http_readers.qtpl:45
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/http_readers.qtpl:46
|
||||
if nextHyphaName != "" {
|
||||
//line templates/http_readers.qtpl:46
|
||||
qw422016.N().S(`
|
||||
<a class="prevnext__el prevnext__next" href="/page/`)
|
||||
//line templates/http_readers.qtpl:47
|
||||
qw422016.E().S(nextHyphaName)
|
||||
//line templates/http_readers.qtpl:47
|
||||
qw422016.N().S(`" rel="next">`)
|
||||
//line templates/http_readers.qtpl:47
|
||||
qw422016.E().S(path.Base(nextHyphaName))
|
||||
//line templates/http_readers.qtpl:47
|
||||
qw422016.N().S(` →</a>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:48
|
||||
}
|
||||
//line templates/http_readers.qtpl:48
|
||||
qw422016.N().S(`
|
||||
</section>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:50
|
||||
if u := user.FromRequest(rq); !user.AuthUsed || u.Group != "anon" {
|
||||
//line templates/http_readers.qtpl:50
|
||||
qw422016.N().S(`
|
||||
<form action="/upload-binary/`)
|
||||
//line templates/http_readers.qtpl:51
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_readers.qtpl:51
|
||||
qw422016.N().S(`"
|
||||
method="post" enctype="multipart/form-data"
|
||||
class="upload-amnt">
|
||||
`)
|
||||
//line templates/http_readers.qtpl:54
|
||||
if hasAmnt {
|
||||
//line templates/http_readers.qtpl:54
|
||||
qw422016.N().S(`
|
||||
<a class="upload-amnt__unattach" href="/unattach-ask/`)
|
||||
//line templates/http_readers.qtpl:55
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_readers.qtpl:55
|
||||
qw422016.N().S(`">Unattach current attachment?</a>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:56
|
||||
}
|
||||
//line templates/http_readers.qtpl:56
|
||||
qw422016.N().S(`
|
||||
<label for="upload-binary__input">Upload a new attachment</label>
|
||||
<br>
|
||||
<input type="file" id="upload-binary__input" name="binary"/>
|
||||
<input type="submit"/>
|
||||
</form>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:62
|
||||
}
|
||||
//line templates/http_readers.qtpl:62
|
||||
qw422016.N().S(`
|
||||
<aside>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:64
|
||||
qw422016.N().S(tree)
|
||||
//line templates/http_readers.qtpl:64
|
||||
qw422016.N().S(`
|
||||
</aside>
|
||||
</main>
|
||||
`)
|
||||
//line templates/http_readers.qtpl:67
|
||||
}
|
||||
|
||||
//line templates/http_readers.qtpl:67
|
||||
func WritePageHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, naviTitle, contents, tree, prevHyphaName, nextHyphaName string, hasAmnt bool) {
|
||||
//line templates/http_readers.qtpl:67
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/http_readers.qtpl:67
|
||||
StreamPageHTML(qw422016, rq, hyphaName, naviTitle, contents, tree, prevHyphaName, nextHyphaName, hasAmnt)
|
||||
//line templates/http_readers.qtpl:67
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/http_readers.qtpl:67
|
||||
}
|
||||
|
||||
//line templates/http_readers.qtpl:67
|
||||
func PageHTML(rq *http.Request, hyphaName, naviTitle, contents, tree, prevHyphaName, nextHyphaName string, hasAmnt bool) string {
|
||||
//line templates/http_readers.qtpl:67
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/http_readers.qtpl:67
|
||||
WritePageHTML(qb422016, rq, hyphaName, naviTitle, contents, tree, prevHyphaName, nextHyphaName, hasAmnt)
|
||||
//line templates/http_readers.qtpl:67
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/http_readers.qtpl:67
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/http_readers.qtpl:67
|
||||
return qs422016
|
||||
//line templates/http_readers.qtpl:67
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||
|
||||
{% func BaseHTML(title, body string, u *user.User, headElements ...string) %}
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="/static/common.css">
|
||||
<title>{%s title %}</title>
|
||||
{% for _, el := range headElements %}{%s= el %}{% endfor %}
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="header-links">
|
||||
<ul class="header-links__list">
|
||||
{%- for _, link := range util.HeaderLinks -%}
|
||||
<li class="header-links__entry"><a class="header-links__link" href="{%s link.Href %}">{%s link.Display %}</a></li>
|
||||
{%- endfor -%}
|
||||
{%s= userMenuHTML(u) %}
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
{%s= body %}
|
||||
</body>
|
||||
</html>
|
||||
{% endfunc %}
|
||||
|
||||
{% func HyphaListHTML(tbody string, pageCount int) %}
|
||||
<main>
|
||||
<h1>List of hyphae</h1>
|
||||
<p>This wiki has {%d pageCount %} hyphae.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Full name</th>
|
||||
<th>Binary part type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{%s= tbody %}
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
{% endfunc %}
|
||||
|
||||
{% func HyphaListRowHTML(hyphaName, binaryMime string, binaryPresent bool) %}
|
||||
<tr>
|
||||
<td><a href="/page/{%s hyphaName %}">{%s hyphaName %}</a></td>
|
||||
{% if binaryPresent %}
|
||||
<td>{%s binaryMime %}</td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfunc %}
|
||||
|
||||
{% func AboutHTML() %}
|
||||
<main>
|
||||
<section>
|
||||
<h1>About {%s util.SiteName %}</h1>
|
||||
<ul>
|
||||
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> β 0.12 indev</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>
|
||||
<li><b>Administrators:</b> {%- for i, username := range user.ListUsersWithGroup("admin") -%}
|
||||
{%- if i > 0 -%}<span aria-hidden="true">, </span>
|
||||
{%- endif -%}
|
||||
<a href="/page/{%s util.UserHypha %}/{%s username %}">{%s username %}</a>{%- endfor -%}</li>
|
||||
{%- else -%}
|
||||
<li>This wiki does not use authorization</li>
|
||||
{%- endif -%}
|
||||
</ul>
|
||||
<p>See <a href="/list">/list</a> for information about hyphae on this wiki.</p>
|
||||
</section>
|
||||
</main>
|
||||
{% endfunc %}
|
@ -1,335 +0,0 @@
|
||||
// Code generated by qtc from "http_stuff.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line templates/http_stuff.qtpl:1
|
||||
package templates
|
||||
|
||||
//line templates/http_stuff.qtpl:1
|
||||
import "github.com/bouncepaw/mycorrhiza/util"
|
||||
|
||||
//line templates/http_stuff.qtpl:2
|
||||
import "github.com/bouncepaw/mycorrhiza/user"
|
||||
|
||||
//line templates/http_stuff.qtpl:4
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line templates/http_stuff.qtpl:4
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line templates/http_stuff.qtpl:4
|
||||
func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string, u *user.User, headElements ...string) {
|
||||
//line templates/http_stuff.qtpl:4
|
||||
qw422016.N().S(`
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="/static/common.css">
|
||||
<title>`)
|
||||
//line templates/http_stuff.qtpl:10
|
||||
qw422016.E().S(title)
|
||||
//line templates/http_stuff.qtpl:10
|
||||
qw422016.N().S(`</title>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:11
|
||||
for _, el := range headElements {
|
||||
//line templates/http_stuff.qtpl:11
|
||||
qw422016.N().S(el)
|
||||
//line templates/http_stuff.qtpl:11
|
||||
}
|
||||
//line templates/http_stuff.qtpl:11
|
||||
qw422016.N().S(`
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="header-links">
|
||||
<ul class="header-links__list">
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:17
|
||||
for _, link := range util.HeaderLinks {
|
||||
//line templates/http_stuff.qtpl:17
|
||||
qw422016.N().S(` <li class="header-links__entry"><a class="header-links__link" href="`)
|
||||
//line templates/http_stuff.qtpl:18
|
||||
qw422016.E().S(link.Href)
|
||||
//line templates/http_stuff.qtpl:18
|
||||
qw422016.N().S(`">`)
|
||||
//line templates/http_stuff.qtpl:18
|
||||
qw422016.E().S(link.Display)
|
||||
//line templates/http_stuff.qtpl:18
|
||||
qw422016.N().S(`</a></li>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:19
|
||||
}
|
||||
//line templates/http_stuff.qtpl:19
|
||||
qw422016.N().S(` `)
|
||||
//line templates/http_stuff.qtpl:20
|
||||
qw422016.N().S(userMenuHTML(u))
|
||||
//line templates/http_stuff.qtpl:20
|
||||
qw422016.N().S(`
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:24
|
||||
qw422016.N().S(body)
|
||||
//line templates/http_stuff.qtpl:24
|
||||
qw422016.N().S(`
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:27
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:27
|
||||
func WriteBaseHTML(qq422016 qtio422016.Writer, title, body string, u *user.User, headElements ...string) {
|
||||
//line templates/http_stuff.qtpl:27
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/http_stuff.qtpl:27
|
||||
StreamBaseHTML(qw422016, title, body, u, headElements...)
|
||||
//line templates/http_stuff.qtpl:27
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/http_stuff.qtpl:27
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:27
|
||||
func BaseHTML(title, body string, u *user.User, headElements ...string) string {
|
||||
//line templates/http_stuff.qtpl:27
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/http_stuff.qtpl:27
|
||||
WriteBaseHTML(qb422016, title, body, u, headElements...)
|
||||
//line templates/http_stuff.qtpl:27
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/http_stuff.qtpl:27
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/http_stuff.qtpl:27
|
||||
return qs422016
|
||||
//line templates/http_stuff.qtpl:27
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:29
|
||||
func StreamHyphaListHTML(qw422016 *qt422016.Writer, tbody string, pageCount int) {
|
||||
//line templates/http_stuff.qtpl:29
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
<h1>List of hyphae</h1>
|
||||
<p>This wiki has `)
|
||||
//line templates/http_stuff.qtpl:32
|
||||
qw422016.N().D(pageCount)
|
||||
//line templates/http_stuff.qtpl:32
|
||||
qw422016.N().S(` hyphae.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Full name</th>
|
||||
<th>Binary part type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:41
|
||||
qw422016.N().S(tbody)
|
||||
//line templates/http_stuff.qtpl:41
|
||||
qw422016.N().S(`
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:45
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:45
|
||||
func WriteHyphaListHTML(qq422016 qtio422016.Writer, tbody string, pageCount int) {
|
||||
//line templates/http_stuff.qtpl:45
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/http_stuff.qtpl:45
|
||||
StreamHyphaListHTML(qw422016, tbody, pageCount)
|
||||
//line templates/http_stuff.qtpl:45
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/http_stuff.qtpl:45
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:45
|
||||
func HyphaListHTML(tbody string, pageCount int) string {
|
||||
//line templates/http_stuff.qtpl:45
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/http_stuff.qtpl:45
|
||||
WriteHyphaListHTML(qb422016, tbody, pageCount)
|
||||
//line templates/http_stuff.qtpl:45
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/http_stuff.qtpl:45
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/http_stuff.qtpl:45
|
||||
return qs422016
|
||||
//line templates/http_stuff.qtpl:45
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:47
|
||||
func StreamHyphaListRowHTML(qw422016 *qt422016.Writer, hyphaName, binaryMime string, binaryPresent bool) {
|
||||
//line templates/http_stuff.qtpl:47
|
||||
qw422016.N().S(`
|
||||
<tr>
|
||||
<td><a href="/page/`)
|
||||
//line templates/http_stuff.qtpl:49
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_stuff.qtpl:49
|
||||
qw422016.N().S(`">`)
|
||||
//line templates/http_stuff.qtpl:49
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_stuff.qtpl:49
|
||||
qw422016.N().S(`</a></td>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:50
|
||||
if binaryPresent {
|
||||
//line templates/http_stuff.qtpl:50
|
||||
qw422016.N().S(`
|
||||
<td>`)
|
||||
//line templates/http_stuff.qtpl:51
|
||||
qw422016.E().S(binaryMime)
|
||||
//line templates/http_stuff.qtpl:51
|
||||
qw422016.N().S(`</td>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:52
|
||||
} else {
|
||||
//line templates/http_stuff.qtpl:52
|
||||
qw422016.N().S(`
|
||||
<td></td>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:54
|
||||
}
|
||||
//line templates/http_stuff.qtpl:54
|
||||
qw422016.N().S(`
|
||||
</tr>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:56
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:56
|
||||
func WriteHyphaListRowHTML(qq422016 qtio422016.Writer, hyphaName, binaryMime string, binaryPresent bool) {
|
||||
//line templates/http_stuff.qtpl:56
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/http_stuff.qtpl:56
|
||||
StreamHyphaListRowHTML(qw422016, hyphaName, binaryMime, binaryPresent)
|
||||
//line templates/http_stuff.qtpl:56
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/http_stuff.qtpl:56
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:56
|
||||
func HyphaListRowHTML(hyphaName, binaryMime string, binaryPresent bool) string {
|
||||
//line templates/http_stuff.qtpl:56
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/http_stuff.qtpl:56
|
||||
WriteHyphaListRowHTML(qb422016, hyphaName, binaryMime, binaryPresent)
|
||||
//line templates/http_stuff.qtpl:56
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/http_stuff.qtpl:56
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/http_stuff.qtpl:56
|
||||
return qs422016
|
||||
//line templates/http_stuff.qtpl:56
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:58
|
||||
func StreamAboutHTML(qw422016 *qt422016.Writer) {
|
||||
//line templates/http_stuff.qtpl:58
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
<section>
|
||||
<h1>About `)
|
||||
//line templates/http_stuff.qtpl:61
|
||||
qw422016.E().S(util.SiteName)
|
||||
//line templates/http_stuff.qtpl:61
|
||||
qw422016.N().S(`</h1>
|
||||
<ul>
|
||||
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> β 0.12 indev</li>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:64
|
||||
if user.AuthUsed {
|
||||
//line templates/http_stuff.qtpl:64
|
||||
qw422016.N().S(` <li><b>User count:</b> `)
|
||||
//line templates/http_stuff.qtpl:65
|
||||
qw422016.N().D(user.Count())
|
||||
//line templates/http_stuff.qtpl:65
|
||||
qw422016.N().S(`</li>
|
||||
<li><b>Home page:</b> <a href="/">`)
|
||||
//line templates/http_stuff.qtpl:66
|
||||
qw422016.E().S(util.HomePage)
|
||||
//line templates/http_stuff.qtpl:66
|
||||
qw422016.N().S(`</a></li>
|
||||
<li><b>Administrators:</b>`)
|
||||
//line templates/http_stuff.qtpl:67
|
||||
for i, username := range user.ListUsersWithGroup("admin") {
|
||||
//line templates/http_stuff.qtpl:68
|
||||
if i > 0 {
|
||||
//line templates/http_stuff.qtpl:68
|
||||
qw422016.N().S(`<span aria-hidden="true">, </span>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:69
|
||||
}
|
||||
//line templates/http_stuff.qtpl:69
|
||||
qw422016.N().S(` <a href="/page/`)
|
||||
//line templates/http_stuff.qtpl:70
|
||||
qw422016.E().S(util.UserHypha)
|
||||
//line templates/http_stuff.qtpl:70
|
||||
qw422016.N().S(`/`)
|
||||
//line templates/http_stuff.qtpl:70
|
||||
qw422016.E().S(username)
|
||||
//line templates/http_stuff.qtpl:70
|
||||
qw422016.N().S(`">`)
|
||||
//line templates/http_stuff.qtpl:70
|
||||
qw422016.E().S(username)
|
||||
//line templates/http_stuff.qtpl:70
|
||||
qw422016.N().S(`</a>`)
|
||||
//line templates/http_stuff.qtpl:70
|
||||
}
|
||||
//line templates/http_stuff.qtpl:70
|
||||
qw422016.N().S(`</li>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:71
|
||||
} else {
|
||||
//line templates/http_stuff.qtpl:71
|
||||
qw422016.N().S(` <li>This wiki does not use authorization</li>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:73
|
||||
}
|
||||
//line templates/http_stuff.qtpl:73
|
||||
qw422016.N().S(` </ul>
|
||||
<p>See <a href="/list">/list</a> for information about hyphae on this wiki.</p>
|
||||
</section>
|
||||
</main>
|
||||
`)
|
||||
//line templates/http_stuff.qtpl:78
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:78
|
||||
func WriteAboutHTML(qq422016 qtio422016.Writer) {
|
||||
//line templates/http_stuff.qtpl:78
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/http_stuff.qtpl:78
|
||||
StreamAboutHTML(qw422016)
|
||||
//line templates/http_stuff.qtpl:78
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/http_stuff.qtpl:78
|
||||
}
|
||||
|
||||
//line templates/http_stuff.qtpl:78
|
||||
func AboutHTML() string {
|
||||
//line templates/http_stuff.qtpl:78
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/http_stuff.qtpl:78
|
||||
WriteAboutHTML(qb422016)
|
||||
//line templates/http_stuff.qtpl:78
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/http_stuff.qtpl:78
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/http_stuff.qtpl:78
|
||||
return qs422016
|
||||
//line templates/http_stuff.qtpl:78
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
// Code generated by qtc from "recent_changes.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line templates/recent_changes.qtpl:1
|
||||
package templates
|
||||
|
||||
//line templates/recent_changes.qtpl:1
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line templates/recent_changes.qtpl:1
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line templates/recent_changes.qtpl:1
|
||||
func StreamRecentChangesHTML(qw422016 *qt422016.Writer, changes []string, n int) {
|
||||
//line templates/recent_changes.qtpl:1
|
||||
qw422016.N().S(`
|
||||
<main class="recent-changes">
|
||||
<h1>Recent Changes</h1>
|
||||
<p><a href="/">← Back</a></p>
|
||||
|
||||
<nav class="recent-changes__count">
|
||||
See
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:8
|
||||
for _, m := range []int{20, 0, 50, 0, 100} {
|
||||
//line templates/recent_changes.qtpl:8
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:9
|
||||
switch m {
|
||||
//line templates/recent_changes.qtpl:10
|
||||
case 0:
|
||||
//line templates/recent_changes.qtpl:10
|
||||
qw422016.N().S(`
|
||||
<span aria-hidden="true">|</span>
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:12
|
||||
case n:
|
||||
//line templates/recent_changes.qtpl:12
|
||||
qw422016.N().S(`
|
||||
<b>`)
|
||||
//line templates/recent_changes.qtpl:13
|
||||
qw422016.N().D(n)
|
||||
//line templates/recent_changes.qtpl:13
|
||||
qw422016.N().S(`</b>
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:14
|
||||
default:
|
||||
//line templates/recent_changes.qtpl:14
|
||||
qw422016.N().S(`
|
||||
<a href="/recent-changes/`)
|
||||
//line templates/recent_changes.qtpl:15
|
||||
qw422016.N().D(m)
|
||||
//line templates/recent_changes.qtpl:15
|
||||
qw422016.N().S(`">`)
|
||||
//line templates/recent_changes.qtpl:15
|
||||
qw422016.N().D(m)
|
||||
//line templates/recent_changes.qtpl:15
|
||||
qw422016.N().S(`</a>
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:16
|
||||
}
|
||||
//line templates/recent_changes.qtpl:16
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:17
|
||||
}
|
||||
//line templates/recent_changes.qtpl:17
|
||||
qw422016.N().S(`
|
||||
recent changes
|
||||
</nav>
|
||||
|
||||
<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 templates/recent_changes.qtpl:28
|
||||
qw422016.N().S(`
|
||||
|
||||
<section class="recent-changes__list" role="feed">
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:31
|
||||
if len(changes) == 0 {
|
||||
//line templates/recent_changes.qtpl:31
|
||||
qw422016.N().S(`
|
||||
<p>Could not find any recent changes.</p>
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:33
|
||||
} else {
|
||||
//line templates/recent_changes.qtpl:33
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:34
|
||||
for i, entry := range changes {
|
||||
//line templates/recent_changes.qtpl:34
|
||||
qw422016.N().S(`
|
||||
<ul class="recent-changes__entry rc-entry" role="article"
|
||||
aria-setsize="`)
|
||||
//line templates/recent_changes.qtpl:36
|
||||
qw422016.N().D(n)
|
||||
//line templates/recent_changes.qtpl:36
|
||||
qw422016.N().S(`" aria-posinset="`)
|
||||
//line templates/recent_changes.qtpl:36
|
||||
qw422016.N().D(i)
|
||||
//line templates/recent_changes.qtpl:36
|
||||
qw422016.N().S(`">
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:37
|
||||
qw422016.N().S(entry)
|
||||
//line templates/recent_changes.qtpl:37
|
||||
qw422016.N().S(`
|
||||
</ul>
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:39
|
||||
}
|
||||
//line templates/recent_changes.qtpl:39
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:40
|
||||
}
|
||||
//line templates/recent_changes.qtpl:40
|
||||
qw422016.N().S(`
|
||||
</section>
|
||||
</main>
|
||||
`)
|
||||
//line templates/recent_changes.qtpl:43
|
||||
}
|
||||
|
||||
//line templates/recent_changes.qtpl:43
|
||||
func WriteRecentChangesHTML(qq422016 qtio422016.Writer, changes []string, n int) {
|
||||
//line templates/recent_changes.qtpl:43
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/recent_changes.qtpl:43
|
||||
StreamRecentChangesHTML(qw422016, changes, n)
|
||||
//line templates/recent_changes.qtpl:43
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/recent_changes.qtpl:43
|
||||
}
|
||||
|
||||
//line templates/recent_changes.qtpl:43
|
||||
func RecentChangesHTML(changes []string, n int) string {
|
||||
//line templates/recent_changes.qtpl:43
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/recent_changes.qtpl:43
|
||||
WriteRecentChangesHTML(qb422016, changes, n)
|
||||
//line templates/recent_changes.qtpl:43
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/recent_changes.qtpl:43
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/recent_changes.qtpl:43
|
||||
return qs422016
|
||||
//line templates/recent_changes.qtpl:43
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
{% import "net/http" %}
|
||||
This dialog is to be shown to a user when they try to rename a hypha.
|
||||
{% func RenameAskHTML(rq *http.Request, hyphaName string, isOld bool) %}
|
||||
{%= navHTML(rq, hyphaName, "rename-ask") %}
|
||||
<main>
|
||||
{%- if isOld -%}
|
||||
<section>
|
||||
<h1>Rename {%s hyphaName %}</h1>
|
||||
<form action="/rename-confirm/{%s hyphaName %}" method="post" enctype="multipart/form-data">
|
||||
<fieldset>
|
||||
<legend>New name</legend>
|
||||
<input type="text" value="{%s hyphaName %}" required autofocus id="new-name" name="new-name"/>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Settings</legend>
|
||||
<input type="checkbox" id="recursive" name="recursive" value="true" checked/>
|
||||
<label for="recursive">Keep subhyphae</label>
|
||||
</fieldset>
|
||||
|
||||
<p>If you rename this hypha, all incoming links and all relative outcoming links will break. You will also lose all history for the new name. Rename carefully.</p>
|
||||
<input type="submit"/>
|
||||
</form>
|
||||
</section>
|
||||
{%- else -%}
|
||||
{%= cannotRenameDueToNonExistence(hyphaName) %}
|
||||
{%- endif -%}
|
||||
</main>
|
||||
{% endfunc %}
|
||||
|
||||
{% func cannotRenameDueToNonExistence(hyphaName string) %}
|
||||
<section>
|
||||
<h1>Cannot rename {%s hyphaName %}</h1>
|
||||
<p>This hypha does not exist.</p>
|
||||
<p><a href="/page/{%s hyphaName %}">Go back</a></p>
|
||||
</section>
|
||||
{% endfunc %}
|
@ -1,158 +0,0 @@
|
||||
// Code generated by qtc from "rename.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line templates/rename.qtpl:1
|
||||
package templates
|
||||
|
||||
//line templates/rename.qtpl:1
|
||||
import "net/http"
|
||||
|
||||
// This dialog is to be shown to a user when they try to rename a hypha.
|
||||
|
||||
//line templates/rename.qtpl:3
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line templates/rename.qtpl:3
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line templates/rename.qtpl:3
|
||||
func StreamRenameAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line templates/rename.qtpl:3
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/rename.qtpl:4
|
||||
streamnavHTML(qw422016, rq, hyphaName, "rename-ask")
|
||||
//line templates/rename.qtpl:4
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
`)
|
||||
//line templates/rename.qtpl:6
|
||||
if isOld {
|
||||
//line templates/rename.qtpl:6
|
||||
qw422016.N().S(` <section>
|
||||
<h1>Rename `)
|
||||
//line templates/rename.qtpl:8
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/rename.qtpl:8
|
||||
qw422016.N().S(`</h1>
|
||||
<form action="/rename-confirm/`)
|
||||
//line templates/rename.qtpl:9
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/rename.qtpl:9
|
||||
qw422016.N().S(`" method="post" enctype="multipart/form-data">
|
||||
<fieldset>
|
||||
<legend>New name</legend>
|
||||
<input type="text" value="`)
|
||||
//line templates/rename.qtpl:12
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/rename.qtpl:12
|
||||
qw422016.N().S(`" required autofocus id="new-name" name="new-name"/>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Settings</legend>
|
||||
<input type="checkbox" id="recursive" name="recursive" value="true" checked/>
|
||||
<label for="recursive">Keep subhyphae</label>
|
||||
</fieldset>
|
||||
|
||||
<p>If you rename this hypha, all incoming links and all relative outcoming links will break. You will also lose all history for the new name. Rename carefully.</p>
|
||||
<input type="submit"/>
|
||||
</form>
|
||||
</section>
|
||||
`)
|
||||
//line templates/rename.qtpl:25
|
||||
} else {
|
||||
//line templates/rename.qtpl:25
|
||||
qw422016.N().S(` `)
|
||||
//line templates/rename.qtpl:26
|
||||
streamcannotRenameDueToNonExistence(qw422016, hyphaName)
|
||||
//line templates/rename.qtpl:26
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/rename.qtpl:27
|
||||
}
|
||||
//line templates/rename.qtpl:27
|
||||
qw422016.N().S(`</main>
|
||||
`)
|
||||
//line templates/rename.qtpl:29
|
||||
}
|
||||
|
||||
//line templates/rename.qtpl:29
|
||||
func WriteRenameAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line templates/rename.qtpl:29
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/rename.qtpl:29
|
||||
StreamRenameAskHTML(qw422016, rq, hyphaName, isOld)
|
||||
//line templates/rename.qtpl:29
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/rename.qtpl:29
|
||||
}
|
||||
|
||||
//line templates/rename.qtpl:29
|
||||
func RenameAskHTML(rq *http.Request, hyphaName string, isOld bool) string {
|
||||
//line templates/rename.qtpl:29
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/rename.qtpl:29
|
||||
WriteRenameAskHTML(qb422016, rq, hyphaName, isOld)
|
||||
//line templates/rename.qtpl:29
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/rename.qtpl:29
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/rename.qtpl:29
|
||||
return qs422016
|
||||
//line templates/rename.qtpl:29
|
||||
}
|
||||
|
||||
//line templates/rename.qtpl:31
|
||||
func streamcannotRenameDueToNonExistence(qw422016 *qt422016.Writer, hyphaName string) {
|
||||
//line templates/rename.qtpl:31
|
||||
qw422016.N().S(`
|
||||
<section>
|
||||
<h1>Cannot rename `)
|
||||
//line templates/rename.qtpl:33
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/rename.qtpl:33
|
||||
qw422016.N().S(`</h1>
|
||||
<p>This hypha does not exist.</p>
|
||||
<p><a href="/page/`)
|
||||
//line templates/rename.qtpl:35
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/rename.qtpl:35
|
||||
qw422016.N().S(`">Go back</a></p>
|
||||
</section>
|
||||
`)
|
||||
//line templates/rename.qtpl:37
|
||||
}
|
||||
|
||||
//line templates/rename.qtpl:37
|
||||
func writecannotRenameDueToNonExistence(qq422016 qtio422016.Writer, hyphaName string) {
|
||||
//line templates/rename.qtpl:37
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/rename.qtpl:37
|
||||
streamcannotRenameDueToNonExistence(qw422016, hyphaName)
|
||||
//line templates/rename.qtpl:37
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/rename.qtpl:37
|
||||
}
|
||||
|
||||
//line templates/rename.qtpl:37
|
||||
func cannotRenameDueToNonExistence(hyphaName string) string {
|
||||
//line templates/rename.qtpl:37
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/rename.qtpl:37
|
||||
writecannotRenameDueToNonExistence(qb422016, hyphaName)
|
||||
//line templates/rename.qtpl:37
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/rename.qtpl:37
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/rename.qtpl:37
|
||||
return qs422016
|
||||
//line templates/rename.qtpl:37
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
{% import "net/http" %}
|
||||
{% func UnattachAskHTML(rq *http.Request, hyphaName string, isOld bool) %}
|
||||
<main>
|
||||
{%= navHTML(rq, hyphaName, "unattach-ask") %}
|
||||
{%- if isOld -%}
|
||||
<section>
|
||||
<h1>Unattach {%s hyphaName %}?</h1>
|
||||
<p>Do you really want to unattach hypha <em>{%s hyphaName %}</em>?</p>
|
||||
<p><a href="/unattach-confirm/{%s hyphaName %}"><strong>Confirm</strong></a></p>
|
||||
<p><a href="/page/{%s hyphaName %}">Cancel</a></p>
|
||||
</section>
|
||||
{%- else -%}
|
||||
{%= cannotUnattachDueToNonExistence(hyphaName) %}
|
||||
{%- endif -%}
|
||||
</main>
|
||||
{% endfunc %}
|
||||
|
||||
{% func cannotUnattachDueToNonExistence(hyphaName string) %}
|
||||
<section>
|
||||
<h1>Cannot unattach {%s hyphaName %}</h1>
|
||||
<p>This hypha does not exist.</p>
|
||||
<p><a href="/page/{%s hyphaName %}">Go back</a></p>
|
||||
</section>
|
||||
{% endfunc %}
|
@ -1,148 +0,0 @@
|
||||
// Code generated by qtc from "unattach.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line templates/unattach.qtpl:1
|
||||
package templates
|
||||
|
||||
//line templates/unattach.qtpl:1
|
||||
import "net/http"
|
||||
|
||||
//line templates/unattach.qtpl:2
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line templates/unattach.qtpl:2
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line templates/unattach.qtpl:2
|
||||
func StreamUnattachAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line templates/unattach.qtpl:2
|
||||
qw422016.N().S(`
|
||||
<main>
|
||||
`)
|
||||
//line templates/unattach.qtpl:4
|
||||
streamnavHTML(qw422016, rq, hyphaName, "unattach-ask")
|
||||
//line templates/unattach.qtpl:4
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/unattach.qtpl:5
|
||||
if isOld {
|
||||
//line templates/unattach.qtpl:5
|
||||
qw422016.N().S(` <section>
|
||||
<h1>Unattach `)
|
||||
//line templates/unattach.qtpl:7
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/unattach.qtpl:7
|
||||
qw422016.N().S(`?</h1>
|
||||
<p>Do you really want to unattach hypha <em>`)
|
||||
//line templates/unattach.qtpl:8
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/unattach.qtpl:8
|
||||
qw422016.N().S(`</em>?</p>
|
||||
<p><a href="/unattach-confirm/`)
|
||||
//line templates/unattach.qtpl:9
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/unattach.qtpl:9
|
||||
qw422016.N().S(`"><strong>Confirm</strong></a></p>
|
||||
<p><a href="/page/`)
|
||||
//line templates/unattach.qtpl:10
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/unattach.qtpl:10
|
||||
qw422016.N().S(`">Cancel</a></p>
|
||||
</section>
|
||||
`)
|
||||
//line templates/unattach.qtpl:12
|
||||
} else {
|
||||
//line templates/unattach.qtpl:12
|
||||
qw422016.N().S(` `)
|
||||
//line templates/unattach.qtpl:13
|
||||
streamcannotUnattachDueToNonExistence(qw422016, hyphaName)
|
||||
//line templates/unattach.qtpl:13
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/unattach.qtpl:14
|
||||
}
|
||||
//line templates/unattach.qtpl:14
|
||||
qw422016.N().S(`</main>
|
||||
`)
|
||||
//line templates/unattach.qtpl:16
|
||||
}
|
||||
|
||||
//line templates/unattach.qtpl:16
|
||||
func WriteUnattachAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line templates/unattach.qtpl:16
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/unattach.qtpl:16
|
||||
StreamUnattachAskHTML(qw422016, rq, hyphaName, isOld)
|
||||
//line templates/unattach.qtpl:16
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/unattach.qtpl:16
|
||||
}
|
||||
|
||||
//line templates/unattach.qtpl:16
|
||||
func UnattachAskHTML(rq *http.Request, hyphaName string, isOld bool) string {
|
||||
//line templates/unattach.qtpl:16
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/unattach.qtpl:16
|
||||
WriteUnattachAskHTML(qb422016, rq, hyphaName, isOld)
|
||||
//line templates/unattach.qtpl:16
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/unattach.qtpl:16
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/unattach.qtpl:16
|
||||
return qs422016
|
||||
//line templates/unattach.qtpl:16
|
||||
}
|
||||
|
||||
//line templates/unattach.qtpl:18
|
||||
func streamcannotUnattachDueToNonExistence(qw422016 *qt422016.Writer, hyphaName string) {
|
||||
//line templates/unattach.qtpl:18
|
||||
qw422016.N().S(`
|
||||
<section>
|
||||
<h1>Cannot unattach `)
|
||||
//line templates/unattach.qtpl:20
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/unattach.qtpl:20
|
||||
qw422016.N().S(`</h1>
|
||||
<p>This hypha does not exist.</p>
|
||||
<p><a href="/page/`)
|
||||
//line templates/unattach.qtpl:22
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/unattach.qtpl:22
|
||||
qw422016.N().S(`">Go back</a></p>
|
||||
</section>
|
||||
`)
|
||||
//line templates/unattach.qtpl:24
|
||||
}
|
||||
|
||||
//line templates/unattach.qtpl:24
|
||||
func writecannotUnattachDueToNonExistence(qq422016 qtio422016.Writer, hyphaName string) {
|
||||
//line templates/unattach.qtpl:24
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/unattach.qtpl:24
|
||||
streamcannotUnattachDueToNonExistence(qw422016, hyphaName)
|
||||
//line templates/unattach.qtpl:24
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/unattach.qtpl:24
|
||||
}
|
||||
|
||||
//line templates/unattach.qtpl:24
|
||||
func cannotUnattachDueToNonExistence(hyphaName string) string {
|
||||
//line templates/unattach.qtpl:24
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/unattach.qtpl:24
|
||||
writecannotUnattachDueToNonExistence(qb422016, hyphaName)
|
||||
//line templates/unattach.qtpl:24
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/unattach.qtpl:24
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/unattach.qtpl:24
|
||||
return qs422016
|
||||
//line templates/unattach.qtpl:24
|
||||
}
|
210
tree/tree.go
@ -6,103 +6,135 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
// If Name == "", the tree is empty.
|
||||
type tree struct {
|
||||
name string
|
||||
exists bool
|
||||
prevSibling string
|
||||
nextSibling string
|
||||
siblings []string
|
||||
descendants []*tree
|
||||
root bool
|
||||
hyphaIterator func(func(string))
|
||||
type sibling struct {
|
||||
name string
|
||||
hasChildren bool
|
||||
}
|
||||
|
||||
func (s *sibling) checkThisChild(hyphaName string) {
|
||||
if !s.hasChildren && path.Dir(hyphaName) == s.name {
|
||||
s.hasChildren = true
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sibling) asHTML() string {
|
||||
class := "navitree__entry navitree__sibling"
|
||||
if s.hasChildren {
|
||||
class += " navitree__sibling_fertile navitree__entry_fertile"
|
||||
} else {
|
||||
class += " navitree__sibling_infertile navitree__entry_infertile"
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`<li class="%s"><a class="navitree__link" href="/hypha/%s">%s</a></li>`,
|
||||
class,
|
||||
s.name,
|
||||
util.BeautifulName(path.Base(s.name)),
|
||||
)
|
||||
}
|
||||
|
||||
type mainFamilyMember struct {
|
||||
name string
|
||||
children []*mainFamilyMember
|
||||
}
|
||||
|
||||
func (m *mainFamilyMember) checkThisChild(hyphaName string) (adopted bool) {
|
||||
if path.Dir(hyphaName) == m.name {
|
||||
m.children = append(m.children, &mainFamilyMember{
|
||||
name: hyphaName,
|
||||
children: make([]*mainFamilyMember, 0),
|
||||
})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *mainFamilyMember) asHTML() string {
|
||||
if len(m.children) == 0 {
|
||||
return fmt.Sprintf(`<li class="subhyphae__entry"><a class="subhyphae__link" href="/hypha/%s">%s</a></li>`, m.name, util.BeautifulName(path.Base(m.name)))
|
||||
}
|
||||
sort.Slice(m.children, func(i, j int) bool {
|
||||
return m.children[i].name < m.children[j].name
|
||||
})
|
||||
html := fmt.Sprintf(`<li class="subhyphae__entry"><a class="subhyphae__link" href="/hypha/%s">%s</a><ul>`, m.name, util.BeautifulName(path.Base(m.name)))
|
||||
for _, child := range m.children {
|
||||
html += child.asHTML()
|
||||
}
|
||||
return html + `</li></ul></li>`
|
||||
}
|
||||
|
||||
func mainFamilyFromPool(hyphaName string, subhyphaePool map[string]bool) *mainFamilyMember {
|
||||
var (
|
||||
nestLevel = strings.Count(hyphaName, "/")
|
||||
adopted = make([]*mainFamilyMember, 0)
|
||||
)
|
||||
for subhyphaName, _ := range subhyphaePool {
|
||||
subnestLevel := strings.Count(subhyphaName, "/")
|
||||
if subnestLevel-1 == nestLevel && path.Dir(subhyphaName) == hyphaName {
|
||||
delete(subhyphaePool, subhyphaName)
|
||||
adopted = append(adopted, mainFamilyFromPool(subhyphaName, subhyphaePool))
|
||||
}
|
||||
}
|
||||
return &mainFamilyMember{name: hyphaName, children: adopted}
|
||||
}
|
||||
|
||||
func subhyphaeMatrix(hyphaName string, subhyphaePool map[string]bool) string {
|
||||
var html string
|
||||
children := mainFamilyFromPool(hyphaName, subhyphaePool).children
|
||||
sort.Slice(children, func(i, j int) bool {
|
||||
return children[i].name < children[j].name
|
||||
})
|
||||
for _, child := range children {
|
||||
html += child.asHTML()
|
||||
}
|
||||
return html
|
||||
}
|
||||
|
||||
// Tree generates a tree for `hyphaName` as html and returns next and previous hyphae if any.
|
||||
func Tree(hyphaName string, hyphaIterator func(func(string))) (html, prev, next string) {
|
||||
t := &tree{name: hyphaName, root: true, hyphaIterator: hyphaIterator}
|
||||
t.fill()
|
||||
return t.asHtml(), util.BeautifulName(t.prevSibling), util.BeautifulName(t.nextSibling)
|
||||
}
|
||||
|
||||
// subtree adds a descendant tree to `t` and returns that tree.
|
||||
func (t *tree) fork(descendantName string) *tree {
|
||||
subt := &tree{
|
||||
name: descendantName,
|
||||
root: false,
|
||||
hyphaIterator: t.hyphaIterator,
|
||||
}
|
||||
t.descendants = append(t.descendants, subt)
|
||||
return subt
|
||||
}
|
||||
|
||||
// Compare current prev next hyphae and decide if any of them should be set to `name2`.
|
||||
func (t *tree) prevNextDetermine(name2 string) {
|
||||
if name2 < t.name && (name2 > t.prevSibling || t.prevSibling == "") {
|
||||
t.prevSibling = name2
|
||||
} else if name2 > t.name && (name2 < t.nextSibling || t.nextSibling == "") {
|
||||
t.nextSibling = name2
|
||||
}
|
||||
}
|
||||
|
||||
// Compares names and does something with them, may generate a subtree.
|
||||
func (t *tree) compareNamesAndAppend(name2 string) {
|
||||
switch {
|
||||
case t.name == name2:
|
||||
t.exists = true
|
||||
case t.root && path.Dir(t.name) == path.Dir(name2):
|
||||
t.prevNextDetermine(name2)
|
||||
t.siblings = append(t.siblings, name2)
|
||||
case t.name == path.Dir(name2):
|
||||
t.fork(name2).fill()
|
||||
}
|
||||
}
|
||||
|
||||
// Fills t.siblings and t.descendants, sorts them and does the same to the descendants.
|
||||
func (t *tree) fill() {
|
||||
t.hyphaIterator(func(hyphaName string) {
|
||||
t.compareNamesAndAppend(hyphaName)
|
||||
})
|
||||
sort.Strings(t.siblings)
|
||||
sort.Slice(t.descendants, func(i, j int) bool {
|
||||
return t.descendants[i].name < t.descendants[j].name
|
||||
})
|
||||
}
|
||||
|
||||
// asHtml returns HTML representation of a tree.
|
||||
// It applies itself recursively on the tree's children.
|
||||
func (t *tree) asHtml() (html string) {
|
||||
if t.root {
|
||||
html += navitreeEntry(t.name, "navitree__pagename")
|
||||
} else {
|
||||
html += navitreeEntry(t.name, "navitree__name")
|
||||
}
|
||||
|
||||
for _, subtree := range t.descendants {
|
||||
html += subtree.asHtml()
|
||||
}
|
||||
|
||||
if t.root {
|
||||
for _, siblingName := range t.siblings {
|
||||
html += navitreeEntry(siblingName, "navitree__sibling")
|
||||
func Tree(hyphaName string) (relatives, subhyphae, prev, next string) {
|
||||
var (
|
||||
// One of the siblings is the hypha with name `hyphaName`
|
||||
siblings = findSiblings(hyphaName)
|
||||
subhyphaePool = make(map[string]bool)
|
||||
I int
|
||||
)
|
||||
for h := range hyphae.YieldExistingHyphae() {
|
||||
for _, s := range siblings {
|
||||
s.checkThisChild(h.Name)
|
||||
}
|
||||
if strings.HasPrefix(h.Name, hyphaName+"/") {
|
||||
subhyphaePool[h.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
return `<ul class="navitree__node">` + html + `</ul>`
|
||||
for i, s := range siblings {
|
||||
if s.name == hyphaName {
|
||||
I = i
|
||||
relatives += fmt.Sprintf(`<li class="navitree__entry navitree__entry_this"><span>%s</span></li>`, util.BeautifulName(path.Base(hyphaName)))
|
||||
} else {
|
||||
relatives += s.asHTML()
|
||||
}
|
||||
}
|
||||
if I != 0 {
|
||||
prev = siblings[I-1].name
|
||||
}
|
||||
if I != len(siblings)-1 {
|
||||
next = siblings[I+1].name
|
||||
}
|
||||
return fmt.Sprintf(`<ul class="navitree">%s</ul>`, relatives), subhyphaeMatrix(hyphaName, subhyphaePool), prev, next
|
||||
}
|
||||
|
||||
// Strip hypha name from all ancestor names, replace _ with spaces, title case
|
||||
func beautifulName(uglyName string) string {
|
||||
return strings.Title(strings.ReplaceAll(path.Base(uglyName), "_", " "))
|
||||
}
|
||||
|
||||
// navitreeEntry is a small utility function that makes generating html easier.
|
||||
func navitreeEntry(name, class string) string {
|
||||
return fmt.Sprintf(`<li class="navitree__entry %s">
|
||||
<a class="navitree__link" href="/page/%s">%s</a>
|
||||
</li>
|
||||
`, class, name, beautifulName(name))
|
||||
func findSiblings(hyphaName string) []*sibling {
|
||||
siblings := []*sibling{&sibling{name: hyphaName, hasChildren: true}}
|
||||
for h := range hyphae.YieldExistingHyphae() {
|
||||
if path.Dir(hyphaName) == path.Dir(h.Name) && hyphaName != h.Name {
|
||||
siblings = append(siblings, &sibling{name: h.Name, hasChildren: false})
|
||||
}
|
||||
}
|
||||
sort.Slice(siblings, func(i, j int) bool {
|
||||
return siblings[i].name < siblings[j].name
|
||||
})
|
||||
return siblings
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ var minimalRights = map[string]int{
|
||||
"delete-ask": 3,
|
||||
"delete-confirm": 3,
|
||||
"reindex": 4,
|
||||
"admin/shutdown": 4,
|
||||
}
|
||||
|
||||
// Group — Right
|
||||
|
@ -8,16 +8,25 @@ var AuthUsed bool
|
||||
var users sync.Map
|
||||
var tokens sync.Map
|
||||
|
||||
func YieldUsers() chan *User {
|
||||
ch := make(chan *User)
|
||||
go func(ch chan *User) {
|
||||
users.Range(func(_, v interface{}) bool {
|
||||
ch <- v.(*User)
|
||||
return true
|
||||
})
|
||||
close(ch)
|
||||
}(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
func ListUsersWithGroup(group string) []string {
|
||||
usersWithTheGroup := []string{}
|
||||
users.Range(func(_, v interface{}) bool {
|
||||
userobj := v.(*User)
|
||||
|
||||
if userobj.Group == group {
|
||||
usersWithTheGroup = append(usersWithTheGroup, userobj.Name)
|
||||
for u := range YieldUsers() {
|
||||
if u.Group == group {
|
||||
usersWithTheGroup = append(usersWithTheGroup, u.Name)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return usersWithTheGroup
|
||||
}
|
||||
|
||||
|
45
util/util.go
@ -4,7 +4,9 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -18,8 +20,27 @@ var (
|
||||
HeaderLinksHypha string
|
||||
AuthMethod string
|
||||
FixedCredentialsPath string
|
||||
GeminiCertPath string
|
||||
)
|
||||
|
||||
// LettersNumbersOnly keeps letters and numbers only in the given string.
|
||||
func LettersNumbersOnly(s string) string {
|
||||
var (
|
||||
ret strings.Builder
|
||||
usedUnderscore bool
|
||||
)
|
||||
for _, r := range s {
|
||||
if unicode.IsLetter(r) || unicode.IsNumber(r) {
|
||||
ret.WriteRune(r)
|
||||
usedUnderscore = false
|
||||
} else if !usedUnderscore {
|
||||
ret.WriteRune('_')
|
||||
usedUnderscore = true
|
||||
}
|
||||
}
|
||||
return strings.Trim(ret.String(), "_")
|
||||
}
|
||||
|
||||
// ShorterPath is used by handlerList to display shorter path to the files. It simply strips WikiDir.
|
||||
func ShorterPath(path string) string {
|
||||
if strings.HasPrefix(path, WikiDir) {
|
||||
@ -39,17 +60,6 @@ func HTTP200Page(w http.ResponseWriter, page string) {
|
||||
w.Write([]byte(page))
|
||||
}
|
||||
|
||||
// FindSubhyphae finds names of existing hyphae given the `hyphaIterator`.
|
||||
func FindSubhyphae(hyphaName string, hyphaIterator func(func(string))) []string {
|
||||
subhyphae := make([]string, 0)
|
||||
hyphaIterator(func(otherHyphaName string) {
|
||||
if strings.HasPrefix(otherHyphaName, hyphaName+"/") {
|
||||
subhyphae = append(subhyphae, otherHyphaName)
|
||||
}
|
||||
})
|
||||
return subhyphae
|
||||
}
|
||||
|
||||
func RandomString(n int) (string, error) {
|
||||
bytes := make([]byte, n)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
@ -65,3 +75,16 @@ func BeautifulName(uglyName string) string {
|
||||
}
|
||||
return strings.Title(strings.ReplaceAll(uglyName, "_", " "))
|
||||
}
|
||||
|
||||
// CanonicalName makes sure the `name` is canonical. A name is canonical if it is lowercase and all spaces are replaced with underscores.
|
||||
func CanonicalName(name string) string {
|
||||
return strings.ToLower(strings.ReplaceAll(name, " ", "_"))
|
||||
}
|
||||
|
||||
// HyphaPattern is a pattern which all hyphae must match.
|
||||
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`)
|
||||
|
||||
// IsCanonicalName checks if the `name` is canonical.
|
||||
func IsCanonicalName(name string) bool {
|
||||
return HyphaPattern.MatchString(name)
|
||||
}
|
||||
|
68
views/auth.qtpl
Normal file
@ -0,0 +1,68 @@
|
||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||
|
||||
{% func LoginHTML() %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<section>
|
||||
{% if user.AuthUsed %}
|
||||
<form class="modal" method="post" action="/login-data" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
||||
<fieldset class="modal__fieldset">
|
||||
<legend class="modal__title">Log in to {%s util.SiteName %}</legend>
|
||||
<p>Use the data you were given by an administrator.</p>
|
||||
<label for="login-form__username">Username</label>
|
||||
<br>
|
||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
||||
<br>
|
||||
<label for="login-form__password">Password</label>
|
||||
<br>
|
||||
<input type="password" required name="password" autocomplete="current-password">
|
||||
<p>By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you. You will stay logged in until you log out.</p>
|
||||
<input class="modal__action modal__submit" type="submit">
|
||||
<a class="modal__action modal__cancel" href="/">Cancel</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p>
|
||||
<p><a class="modal__cancel" href="/">← Go home</a></p>
|
||||
{% endif %}
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
{% func LoginErrorHTML(err string) %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<section>
|
||||
{% switch err %}
|
||||
{% case "unknown username" %}
|
||||
<p class="error">Unknown username.</p>
|
||||
{% case "wrong password" %}
|
||||
<p class="error">Wrong password.</p>
|
||||
{% default %}
|
||||
<p class="error">{%s err %}</p>
|
||||
{% endswitch %}
|
||||
<p><a href="/login">← Try again</a></p>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
{% func LogoutHTML(can bool) %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<section>
|
||||
{% if can %}
|
||||
<h1>Log out?</h1>
|
||||
<p><a href="/logout-confirm"><strong>Confirm</strong></a></p>
|
||||
<p><a href="/">Cancel</a></p>
|
||||
{% else %}
|
||||
<p>You cannot log out because you are not logged in.</p>
|
||||
<p><a href="/login">Login</a></p>
|
||||
<p><a href="/login">← Home</a></p>
|
||||
{% endif %}
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
232
views/auth.qtpl.go
Normal file
@ -0,0 +1,232 @@
|
||||
// Code generated by qtc from "auth.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line views/auth.qtpl:1
|
||||
package views
|
||||
|
||||
//line views/auth.qtpl:1
|
||||
import "github.com/bouncepaw/mycorrhiza/user"
|
||||
|
||||
//line views/auth.qtpl:2
|
||||
import "github.com/bouncepaw/mycorrhiza/util"
|
||||
|
||||
//line views/auth.qtpl:4
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line views/auth.qtpl:4
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line views/auth.qtpl:4
|
||||
func StreamLoginHTML(qw422016 *qt422016.Writer) {
|
||||
//line views/auth.qtpl:4
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<section>
|
||||
`)
|
||||
//line views/auth.qtpl:8
|
||||
if user.AuthUsed {
|
||||
//line views/auth.qtpl:8
|
||||
qw422016.N().S(`
|
||||
<form class="modal" method="post" action="/login-data" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
||||
<fieldset class="modal__fieldset">
|
||||
<legend class="modal__title">Log in to `)
|
||||
//line views/auth.qtpl:11
|
||||
qw422016.E().S(util.SiteName)
|
||||
//line views/auth.qtpl:11
|
||||
qw422016.N().S(`</legend>
|
||||
<p>Use the data you were given by an administrator.</p>
|
||||
<label for="login-form__username">Username</label>
|
||||
<br>
|
||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
||||
<br>
|
||||
<label for="login-form__password">Password</label>
|
||||
<br>
|
||||
<input type="password" required name="password" autocomplete="current-password">
|
||||
<p>By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you. You will stay logged in until you log out.</p>
|
||||
<input class="modal__action modal__submit" type="submit">
|
||||
<a class="modal__action modal__cancel" href="/">Cancel</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
`)
|
||||
//line views/auth.qtpl:25
|
||||
} else {
|
||||
//line views/auth.qtpl:25
|
||||
qw422016.N().S(`
|
||||
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p>
|
||||
<p><a class="modal__cancel" href="/">← Go home</a></p>
|
||||
`)
|
||||
//line views/auth.qtpl:28
|
||||
}
|
||||
//line views/auth.qtpl:28
|
||||
qw422016.N().S(`
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/auth.qtpl:32
|
||||
}
|
||||
|
||||
//line views/auth.qtpl:32
|
||||
func WriteLoginHTML(qq422016 qtio422016.Writer) {
|
||||
//line views/auth.qtpl:32
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/auth.qtpl:32
|
||||
StreamLoginHTML(qw422016)
|
||||
//line views/auth.qtpl:32
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/auth.qtpl:32
|
||||
}
|
||||
|
||||
//line views/auth.qtpl:32
|
||||
func LoginHTML() string {
|
||||
//line views/auth.qtpl:32
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/auth.qtpl:32
|
||||
WriteLoginHTML(qb422016)
|
||||
//line views/auth.qtpl:32
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/auth.qtpl:32
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/auth.qtpl:32
|
||||
return qs422016
|
||||
//line views/auth.qtpl:32
|
||||
}
|
||||
|
||||
//line views/auth.qtpl:34
|
||||
func StreamLoginErrorHTML(qw422016 *qt422016.Writer, err string) {
|
||||
//line views/auth.qtpl:34
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<section>
|
||||
`)
|
||||
//line views/auth.qtpl:38
|
||||
switch err {
|
||||
//line views/auth.qtpl:39
|
||||
case "unknown username":
|
||||
//line views/auth.qtpl:39
|
||||
qw422016.N().S(`
|
||||
<p class="error">Unknown username.</p>
|
||||
`)
|
||||
//line views/auth.qtpl:41
|
||||
case "wrong password":
|
||||
//line views/auth.qtpl:41
|
||||
qw422016.N().S(`
|
||||
<p class="error">Wrong password.</p>
|
||||
`)
|
||||
//line views/auth.qtpl:43
|
||||
default:
|
||||
//line views/auth.qtpl:43
|
||||
qw422016.N().S(`
|
||||
<p class="error">`)
|
||||
//line views/auth.qtpl:44
|
||||
qw422016.E().S(err)
|
||||
//line views/auth.qtpl:44
|
||||
qw422016.N().S(`</p>
|
||||
`)
|
||||
//line views/auth.qtpl:45
|
||||
}
|
||||
//line views/auth.qtpl:45
|
||||
qw422016.N().S(`
|
||||
<p><a href="/login">← Try again</a></p>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/auth.qtpl:50
|
||||
}
|
||||
|
||||
//line views/auth.qtpl:50
|
||||
func WriteLoginErrorHTML(qq422016 qtio422016.Writer, err string) {
|
||||
//line views/auth.qtpl:50
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/auth.qtpl:50
|
||||
StreamLoginErrorHTML(qw422016, err)
|
||||
//line views/auth.qtpl:50
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/auth.qtpl:50
|
||||
}
|
||||
|
||||
//line views/auth.qtpl:50
|
||||
func LoginErrorHTML(err string) string {
|
||||
//line views/auth.qtpl:50
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/auth.qtpl:50
|
||||
WriteLoginErrorHTML(qb422016, err)
|
||||
//line views/auth.qtpl:50
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/auth.qtpl:50
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/auth.qtpl:50
|
||||
return qs422016
|
||||
//line views/auth.qtpl:50
|
||||
}
|
||||
|
||||
//line views/auth.qtpl:52
|
||||
func StreamLogoutHTML(qw422016 *qt422016.Writer, can bool) {
|
||||
//line views/auth.qtpl:52
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<section>
|
||||
`)
|
||||
//line views/auth.qtpl:56
|
||||
if can {
|
||||
//line views/auth.qtpl:56
|
||||
qw422016.N().S(`
|
||||
<h1>Log out?</h1>
|
||||
<p><a href="/logout-confirm"><strong>Confirm</strong></a></p>
|
||||
<p><a href="/">Cancel</a></p>
|
||||
`)
|
||||
//line views/auth.qtpl:60
|
||||
} else {
|
||||
//line views/auth.qtpl:60
|
||||
qw422016.N().S(`
|
||||
<p>You cannot log out because you are not logged in.</p>
|
||||
<p><a href="/login">Login</a></p>
|
||||
<p><a href="/login">← Home</a></p>
|
||||
`)
|
||||
//line views/auth.qtpl:64
|
||||
}
|
||||
//line views/auth.qtpl:64
|
||||
qw422016.N().S(`
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/auth.qtpl:68
|
||||
}
|
||||
|
||||
//line views/auth.qtpl:68
|
||||
func WriteLogoutHTML(qq422016 qtio422016.Writer, can bool) {
|
||||
//line views/auth.qtpl:68
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/auth.qtpl:68
|
||||
StreamLogoutHTML(qw422016, can)
|
||||
//line views/auth.qtpl:68
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/auth.qtpl:68
|
||||
}
|
||||
|
||||
//line views/auth.qtpl:68
|
||||
func LogoutHTML(can bool) string {
|
||||
//line views/auth.qtpl:68
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/auth.qtpl:68
|
||||
WriteLogoutHTML(qb422016, can)
|
||||
//line views/auth.qtpl:68
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/auth.qtpl:68
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/auth.qtpl:68
|
||||
return qs422016
|
||||
//line views/auth.qtpl:68
|
||||
}
|
@ -1,5 +1,11 @@
|
||||
{% func RecentChangesHTML(changes []string, n int) %}
|
||||
<main class="recent-changes">
|
||||
{% import "net/http" %}
|
||||
|
||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/history" %}
|
||||
|
||||
{% func RecentChangesHTML(n int) %}
|
||||
<div class="layout">
|
||||
<main class="main-width recent-changes">
|
||||
<h1>Recent Changes</h1>
|
||||
<p><a href="/">← Back</a></p>
|
||||
|
||||
@ -27,6 +33,9 @@
|
||||
How come? I'll add the role anyway. -- bouncepaw
|
||||
{% endcomment %}
|
||||
|
||||
{% code
|
||||
changes := history.RecentChanges(n)
|
||||
%}
|
||||
<section class="recent-changes__list" role="feed">
|
||||
{% if len(changes) == 0 %}
|
||||
<p>Could not find any recent changes.</p>
|
||||
@ -34,10 +43,30 @@
|
||||
{% for i, entry := range changes %}
|
||||
<ul class="recent-changes__entry rc-entry" role="article"
|
||||
aria-setsize="{%d n %}" aria-posinset="{%d i %}">
|
||||
{%s= entry %}
|
||||
{%s= recentChangesEntry(entry) %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
{% func recentChangesEntry(rev history.Revision) %}
|
||||
<li class="rc-entry__time"><time>{%s rev.TimeString() %}</time></li>
|
||||
<li class="rc-entry__hash">{%s rev.Hash %}</li>
|
||||
<li class="rc-entry__links">{%s= rev.HyphaeLinksHTML() %}</li>
|
||||
<li class="rc-entry__msg">{%s rev.Message %} {% if rev.Username != "anon" %}<span class="rc-entry__author">by <a href="/hypha/{%s util.UserHypha %}/{%s rev.Username %}" rel="author">{%s rev.Username %}</a></span>{% endif %}</li>
|
||||
{% endfunc %}
|
||||
|
||||
{% func HistoryHTML(rq *http.Request, hyphaName, list string) %}
|
||||
{%= NavHTML(rq, hyphaName, "history") %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<article class="history">
|
||||
<h1>History of {%s util.BeautifulName(hyphaName) %}</h1>
|
||||
{%s= list %}
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
305
views/history.qtpl.go
Normal file
@ -0,0 +1,305 @@
|
||||
// Code generated by qtc from "history.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line views/history.qtpl:1
|
||||
package views
|
||||
|
||||
//line views/history.qtpl:1
|
||||
import "net/http"
|
||||
|
||||
//line views/history.qtpl:3
|
||||
import "github.com/bouncepaw/mycorrhiza/util"
|
||||
|
||||
//line views/history.qtpl:4
|
||||
import "github.com/bouncepaw/mycorrhiza/history"
|
||||
|
||||
//line views/history.qtpl:6
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line views/history.qtpl:6
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line views/history.qtpl:6
|
||||
func StreamRecentChangesHTML(qw422016 *qt422016.Writer, n int) {
|
||||
//line views/history.qtpl:6
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width recent-changes">
|
||||
<h1>Recent Changes</h1>
|
||||
<p><a href="/">← Back</a></p>
|
||||
|
||||
<nav class="recent-changes__count">
|
||||
See
|
||||
`)
|
||||
//line views/history.qtpl:14
|
||||
for _, m := range []int{20, 0, 50, 0, 100} {
|
||||
//line views/history.qtpl:14
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/history.qtpl:15
|
||||
switch m {
|
||||
//line views/history.qtpl:16
|
||||
case 0:
|
||||
//line views/history.qtpl:16
|
||||
qw422016.N().S(`
|
||||
<span aria-hidden="true">|</span>
|
||||
`)
|
||||
//line views/history.qtpl:18
|
||||
case n:
|
||||
//line views/history.qtpl:18
|
||||
qw422016.N().S(`
|
||||
<b>`)
|
||||
//line views/history.qtpl:19
|
||||
qw422016.N().D(n)
|
||||
//line views/history.qtpl:19
|
||||
qw422016.N().S(`</b>
|
||||
`)
|
||||
//line views/history.qtpl:20
|
||||
default:
|
||||
//line views/history.qtpl:20
|
||||
qw422016.N().S(`
|
||||
<a href="/recent-changes/`)
|
||||
//line views/history.qtpl:21
|
||||
qw422016.N().D(m)
|
||||
//line views/history.qtpl:21
|
||||
qw422016.N().S(`">`)
|
||||
//line views/history.qtpl:21
|
||||
qw422016.N().D(m)
|
||||
//line views/history.qtpl:21
|
||||
qw422016.N().S(`</a>
|
||||
`)
|
||||
//line views/history.qtpl:22
|
||||
}
|
||||
//line views/history.qtpl:22
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/history.qtpl:23
|
||||
}
|
||||
//line views/history.qtpl:23
|
||||
qw422016.N().S(`
|
||||
recent changes
|
||||
</nav>
|
||||
|
||||
<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
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line views/history.qtpl:37
|
||||
changes := history.RecentChanges(n)
|
||||
|
||||
//line views/history.qtpl:38
|
||||
qw422016.N().S(`
|
||||
<section class="recent-changes__list" role="feed">
|
||||
`)
|
||||
//line views/history.qtpl:40
|
||||
if len(changes) == 0 {
|
||||
//line views/history.qtpl:40
|
||||
qw422016.N().S(`
|
||||
<p>Could not find any recent changes.</p>
|
||||
`)
|
||||
//line views/history.qtpl:42
|
||||
} else {
|
||||
//line views/history.qtpl:42
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/history.qtpl:43
|
||||
for i, entry := range changes {
|
||||
//line views/history.qtpl:43
|
||||
qw422016.N().S(`
|
||||
<ul class="recent-changes__entry rc-entry" role="article"
|
||||
aria-setsize="`)
|
||||
//line views/history.qtpl:45
|
||||
qw422016.N().D(n)
|
||||
//line views/history.qtpl:45
|
||||
qw422016.N().S(`" aria-posinset="`)
|
||||
//line views/history.qtpl:45
|
||||
qw422016.N().D(i)
|
||||
//line views/history.qtpl:45
|
||||
qw422016.N().S(`">
|
||||
`)
|
||||
//line views/history.qtpl:46
|
||||
qw422016.N().S(recentChangesEntry(entry))
|
||||
//line views/history.qtpl:46
|
||||
qw422016.N().S(`
|
||||
</ul>
|
||||
`)
|
||||
//line views/history.qtpl:48
|
||||
}
|
||||
//line views/history.qtpl:48
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/history.qtpl:49
|
||||
}
|
||||
//line views/history.qtpl:49
|
||||
qw422016.N().S(`
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/history.qtpl:53
|
||||
}
|
||||
|
||||
//line views/history.qtpl:53
|
||||
func WriteRecentChangesHTML(qq422016 qtio422016.Writer, n int) {
|
||||
//line views/history.qtpl:53
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/history.qtpl:53
|
||||
StreamRecentChangesHTML(qw422016, n)
|
||||
//line views/history.qtpl:53
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/history.qtpl:53
|
||||
}
|
||||
|
||||
//line views/history.qtpl:53
|
||||
func RecentChangesHTML(n int) string {
|
||||
//line views/history.qtpl:53
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/history.qtpl:53
|
||||
WriteRecentChangesHTML(qb422016, n)
|
||||
//line views/history.qtpl:53
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/history.qtpl:53
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/history.qtpl:53
|
||||
return qs422016
|
||||
//line views/history.qtpl:53
|
||||
}
|
||||
|
||||
//line views/history.qtpl:55
|
||||
func streamrecentChangesEntry(qw422016 *qt422016.Writer, rev history.Revision) {
|
||||
//line views/history.qtpl:55
|
||||
qw422016.N().S(`
|
||||
<li class="rc-entry__time"><time>`)
|
||||
//line views/history.qtpl:56
|
||||
qw422016.E().S(rev.TimeString())
|
||||
//line views/history.qtpl:56
|
||||
qw422016.N().S(`</time></li>
|
||||
<li class="rc-entry__hash">`)
|
||||
//line views/history.qtpl:57
|
||||
qw422016.E().S(rev.Hash)
|
||||
//line views/history.qtpl:57
|
||||
qw422016.N().S(`</li>
|
||||
<li class="rc-entry__links">`)
|
||||
//line views/history.qtpl:58
|
||||
qw422016.N().S(rev.HyphaeLinksHTML())
|
||||
//line views/history.qtpl:58
|
||||
qw422016.N().S(`</li>
|
||||
<li class="rc-entry__msg">`)
|
||||
//line views/history.qtpl:59
|
||||
qw422016.E().S(rev.Message)
|
||||
//line views/history.qtpl:59
|
||||
qw422016.N().S(` `)
|
||||
//line views/history.qtpl:59
|
||||
if rev.Username != "anon" {
|
||||
//line views/history.qtpl:59
|
||||
qw422016.N().S(`<span class="rc-entry__author">by <a href="/hypha/`)
|
||||
//line views/history.qtpl:59
|
||||
qw422016.E().S(util.UserHypha)
|
||||
//line views/history.qtpl:59
|
||||
qw422016.N().S(`/`)
|
||||
//line views/history.qtpl:59
|
||||
qw422016.E().S(rev.Username)
|
||||
//line views/history.qtpl:59
|
||||
qw422016.N().S(`" rel="author">`)
|
||||
//line views/history.qtpl:59
|
||||
qw422016.E().S(rev.Username)
|
||||
//line views/history.qtpl:59
|
||||
qw422016.N().S(`</a></span>`)
|
||||
//line views/history.qtpl:59
|
||||
}
|
||||
//line views/history.qtpl:59
|
||||
qw422016.N().S(`</li>
|
||||
`)
|
||||
//line views/history.qtpl:60
|
||||
}
|
||||
|
||||
//line views/history.qtpl:60
|
||||
func writerecentChangesEntry(qq422016 qtio422016.Writer, rev history.Revision) {
|
||||
//line views/history.qtpl:60
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/history.qtpl:60
|
||||
streamrecentChangesEntry(qw422016, rev)
|
||||
//line views/history.qtpl:60
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/history.qtpl:60
|
||||
}
|
||||
|
||||
//line views/history.qtpl:60
|
||||
func recentChangesEntry(rev history.Revision) string {
|
||||
//line views/history.qtpl:60
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/history.qtpl:60
|
||||
writerecentChangesEntry(qb422016, rev)
|
||||
//line views/history.qtpl:60
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/history.qtpl:60
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/history.qtpl:60
|
||||
return qs422016
|
||||
//line views/history.qtpl:60
|
||||
}
|
||||
|
||||
//line views/history.qtpl:62
|
||||
func StreamHistoryHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, list string) {
|
||||
//line views/history.qtpl:62
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/history.qtpl:63
|
||||
StreamNavHTML(qw422016, rq, hyphaName, "history")
|
||||
//line views/history.qtpl:63
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<article class="history">
|
||||
<h1>History of `)
|
||||
//line views/history.qtpl:67
|
||||
qw422016.E().S(util.BeautifulName(hyphaName))
|
||||
//line views/history.qtpl:67
|
||||
qw422016.N().S(`</h1>
|
||||
`)
|
||||
//line views/history.qtpl:68
|
||||
qw422016.N().S(list)
|
||||
//line views/history.qtpl:68
|
||||
qw422016.N().S(`
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/history.qtpl:72
|
||||
}
|
||||
|
||||
//line views/history.qtpl:72
|
||||
func WriteHistoryHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, list string) {
|
||||
//line views/history.qtpl:72
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/history.qtpl:72
|
||||
StreamHistoryHTML(qw422016, rq, hyphaName, list)
|
||||
//line views/history.qtpl:72
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/history.qtpl:72
|
||||
}
|
||||
|
||||
//line views/history.qtpl:72
|
||||
func HistoryHTML(rq *http.Request, hyphaName, list string) string {
|
||||
//line views/history.qtpl:72
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/history.qtpl:72
|
||||
WriteHistoryHTML(qb422016, rq, hyphaName, list)
|
||||
//line views/history.qtpl:72
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/history.qtpl:72
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/history.qtpl:72
|
||||
return qs422016
|
||||
//line views/history.qtpl:72
|
||||
}
|
81
views/hypha.qtpl
Normal file
@ -0,0 +1,81 @@
|
||||
{% import "path/filepath" %}
|
||||
{% import "strings" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||
|
||||
{% func NaviTitleHTML(h *hyphae.Hypha) %}
|
||||
{% code
|
||||
var (
|
||||
prevAcc = "/hypha/"
|
||||
parts = strings.Split(h.Name, "/")
|
||||
)
|
||||
%}
|
||||
<h1 class="navi-title">
|
||||
{% stripspace %}
|
||||
<a href="/hypha/{%s util.HomePage %}">
|
||||
{%-s= util.SiteNavIcon -%}
|
||||
<span aria-hidden="true" class="navi-title__colon">:</span>
|
||||
</a>
|
||||
|
||||
{% for i, part := range parts %}
|
||||
{% if i > 0 %}
|
||||
<span aria-hidden="true" class="navi-title__separator">/</span>
|
||||
{% endif %}
|
||||
|
||||
<a href="{%s prevAcc + part %}"
|
||||
rel="{% if i == len(parts) - 1 %}bookmark{% else %}up{% endif %}">
|
||||
{%s= util.BeautifulName(part) %}
|
||||
</a>
|
||||
{% code prevAcc += part + "/" %}
|
||||
{% endfor %}
|
||||
{% endstripspace %}
|
||||
</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) %}
|
||||
|
||||
{% case ".jpg", ".gif", ".png", ".webp", ".svg", ".ico" %}
|
||||
<div class="binary-container binary-container_with-img">
|
||||
<a href="/binary/{%s= h.Name %}"><img src="/binary/{%s= h.Name %}"/></a>
|
||||
</div>
|
||||
|
||||
{% case ".ogg", ".webm", ".mp4" %}
|
||||
<div class="binary-container binary-container_with-video">
|
||||
<video controls>
|
||||
<source src="/binary/{%s= h.Name %}"/>
|
||||
<p>Your browser does not support video. <a href="/binary/{%s= h.Name %}">Download video</a></p>
|
||||
</video>
|
||||
</div>
|
||||
|
||||
{% case ".mp3" %}
|
||||
<div class="binary-container binary-container_with-audio">
|
||||
<audio controls>
|
||||
<source src="/binary/{%s= h.Name %}"/>
|
||||
<p>Your browser does not support audio. <a href="/binary/{%s= h.Name %}">Download audio</a></p>
|
||||
</audio>
|
||||
</div>
|
||||
|
||||
{% default %}
|
||||
<div class="binary-container binary-container_with-nothing">
|
||||
<p><a href="/binary/{%s= h.Name %}">Download media</a></p>
|
||||
</div>
|
||||
{% endswitch %}
|
||||
{% endfunc %}
|
297
views/hypha.qtpl.go
Normal file
@ -0,0 +1,297 @@
|
||||
// Code generated by qtc from "hypha.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line views/hypha.qtpl:1
|
||||
package views
|
||||
|
||||
//line views/hypha.qtpl:1
|
||||
import "path/filepath"
|
||||
|
||||
//line views/hypha.qtpl:2
|
||||
import "strings"
|
||||
|
||||
//line views/hypha.qtpl:3
|
||||
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
|
||||
//line views/hypha.qtpl:4
|
||||
import "github.com/bouncepaw/mycorrhiza/util"
|
||||
|
||||
//line views/hypha.qtpl:6
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line views/hypha.qtpl:6
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line views/hypha.qtpl:6
|
||||
func StreamNaviTitleHTML(qw422016 *qt422016.Writer, h *hyphae.Hypha) {
|
||||
//line views/hypha.qtpl:6
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/hypha.qtpl:8
|
||||
var (
|
||||
prevAcc = "/hypha/"
|
||||
parts = strings.Split(h.Name, "/")
|
||||
)
|
||||
|
||||
//line views/hypha.qtpl:12
|
||||
qw422016.N().S(`
|
||||
<h1 class="navi-title">
|
||||
`)
|
||||
//line views/hypha.qtpl:14
|
||||
qw422016.N().S(`<a href="/hypha/`)
|
||||
//line views/hypha.qtpl:15
|
||||
qw422016.E().S(util.HomePage)
|
||||
//line views/hypha.qtpl:15
|
||||
qw422016.N().S(`">`)
|
||||
//line views/hypha.qtpl:16
|
||||
qw422016.N().S(util.SiteNavIcon)
|
||||
//line views/hypha.qtpl:16
|
||||
qw422016.N().S(`<span aria-hidden="true" class="navi-title__colon">:</span></a>`)
|
||||
//line views/hypha.qtpl:20
|
||||
for i, part := range parts {
|
||||
//line views/hypha.qtpl:21
|
||||
if i > 0 {
|
||||
//line views/hypha.qtpl:21
|
||||
qw422016.N().S(`<span aria-hidden="true" class="navi-title__separator">/</span>`)
|
||||
//line views/hypha.qtpl:23
|
||||
}
|
||||
//line views/hypha.qtpl:23
|
||||
qw422016.N().S(`<a href="`)
|
||||
//line views/hypha.qtpl:25
|
||||
qw422016.E().S(prevAcc + part)
|
||||
//line views/hypha.qtpl:25
|
||||
qw422016.N().S(`"rel="`)
|
||||
//line views/hypha.qtpl:26
|
||||
if i == len(parts)-1 {
|
||||
//line views/hypha.qtpl:26
|
||||
qw422016.N().S(`bookmark`)
|
||||
//line views/hypha.qtpl:26
|
||||
} else {
|
||||
//line views/hypha.qtpl:26
|
||||
qw422016.N().S(`up`)
|
||||
//line views/hypha.qtpl:26
|
||||
}
|
||||
//line views/hypha.qtpl:26
|
||||
qw422016.N().S(`">`)
|
||||
//line views/hypha.qtpl:27
|
||||
qw422016.N().S(util.BeautifulName(part))
|
||||
//line views/hypha.qtpl:27
|
||||
qw422016.N().S(`</a>`)
|
||||
//line views/hypha.qtpl:29
|
||||
prevAcc += part + "/"
|
||||
|
||||
//line views/hypha.qtpl:30
|
||||
}
|
||||
//line views/hypha.qtpl:31
|
||||
qw422016.N().S(`
|
||||
</h1>
|
||||
`)
|
||||
//line views/hypha.qtpl:33
|
||||
}
|
||||
|
||||
//line views/hypha.qtpl:33
|
||||
func WriteNaviTitleHTML(qq422016 qtio422016.Writer, h *hyphae.Hypha) {
|
||||
//line views/hypha.qtpl:33
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/hypha.qtpl:33
|
||||
StreamNaviTitleHTML(qw422016, h)
|
||||
//line views/hypha.qtpl:33
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/hypha.qtpl:33
|
||||
}
|
||||
|
||||
//line views/hypha.qtpl:33
|
||||
func NaviTitleHTML(h *hyphae.Hypha) string {
|
||||
//line views/hypha.qtpl:33
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/hypha.qtpl:33
|
||||
WriteNaviTitleHTML(qb422016, h)
|
||||
//line views/hypha.qtpl:33
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/hypha.qtpl:33
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/hypha.qtpl:33
|
||||
return qs422016
|
||||
//line views/hypha.qtpl:33
|
||||
}
|
||||
|
||||
//line views/hypha.qtpl:35
|
||||
func StreamBackLinksHTML(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
|
||||
switch filepath.Ext(h.BinaryPath) {
|
||||
//line views/hypha.qtpl:55
|
||||
case ".jpg", ".gif", ".png", ".webp", ".svg", ".ico":
|
||||
//line views/hypha.qtpl:55
|
||||
qw422016.N().S(`
|
||||
<div class="binary-container binary-container_with-img">
|
||||
<a href="/binary/`)
|
||||
//line views/hypha.qtpl:57
|
||||
qw422016.N().S(h.Name)
|
||||
//line views/hypha.qtpl:57
|
||||
qw422016.N().S(`"><img src="/binary/`)
|
||||
//line views/hypha.qtpl:57
|
||||
qw422016.N().S(h.Name)
|
||||
//line views/hypha.qtpl:57
|
||||
qw422016.N().S(`"/></a>
|
||||
</div>
|
||||
|
||||
`)
|
||||
//line views/hypha.qtpl:60
|
||||
case ".ogg", ".webm", ".mp4":
|
||||
//line views/hypha.qtpl:60
|
||||
qw422016.N().S(`
|
||||
<div class="binary-container binary-container_with-video">
|
||||
<video controls>
|
||||
<source src="/binary/`)
|
||||
//line views/hypha.qtpl:63
|
||||
qw422016.N().S(h.Name)
|
||||
//line views/hypha.qtpl:63
|
||||
qw422016.N().S(`"/>
|
||||
<p>Your browser does not support video. <a href="/binary/`)
|
||||
//line views/hypha.qtpl:64
|
||||
qw422016.N().S(h.Name)
|
||||
//line views/hypha.qtpl:64
|
||||
qw422016.N().S(`">Download video</a></p>
|
||||
</video>
|
||||
</div>
|
||||
|
||||
`)
|
||||
//line views/hypha.qtpl:68
|
||||
case ".mp3":
|
||||
//line views/hypha.qtpl:68
|
||||
qw422016.N().S(`
|
||||
<div class="binary-container binary-container_with-audio">
|
||||
<audio controls>
|
||||
<source src="/binary/`)
|
||||
//line views/hypha.qtpl:71
|
||||
qw422016.N().S(h.Name)
|
||||
//line views/hypha.qtpl:71
|
||||
qw422016.N().S(`"/>
|
||||
<p>Your browser does not support audio. <a href="/binary/`)
|
||||
//line views/hypha.qtpl:72
|
||||
qw422016.N().S(h.Name)
|
||||
//line views/hypha.qtpl:72
|
||||
qw422016.N().S(`">Download audio</a></p>
|
||||
</audio>
|
||||
</div>
|
||||
|
||||
`)
|
||||
//line views/hypha.qtpl:76
|
||||
default:
|
||||
//line views/hypha.qtpl:76
|
||||
qw422016.N().S(`
|
||||
<div class="binary-container binary-container_with-nothing">
|
||||
<p><a href="/binary/`)
|
||||
//line views/hypha.qtpl:78
|
||||
qw422016.N().S(h.Name)
|
||||
//line views/hypha.qtpl:78
|
||||
qw422016.N().S(`">Download media</a></p>
|
||||
</div>
|
||||
`)
|
||||
//line views/hypha.qtpl:80
|
||||
}
|
||||
//line views/hypha.qtpl:80
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/hypha.qtpl:81
|
||||
}
|
||||
|
||||
//line views/hypha.qtpl:81
|
||||
func WriteAttachmentHTML(qq422016 qtio422016.Writer, h *hyphae.Hypha) {
|
||||
//line views/hypha.qtpl:81
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/hypha.qtpl:81
|
||||
StreamAttachmentHTML(qw422016, h)
|
||||
//line views/hypha.qtpl:81
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/hypha.qtpl:81
|
||||
}
|
||||
|
||||
//line views/hypha.qtpl:81
|
||||
func AttachmentHTML(h *hyphae.Hypha) string {
|
||||
//line views/hypha.qtpl:81
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/hypha.qtpl:81
|
||||
WriteAttachmentHTML(qb422016, h)
|
||||
//line views/hypha.qtpl:81
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/hypha.qtpl:81
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/hypha.qtpl:81
|
||||
return qs422016
|
||||
//line views/hypha.qtpl:81
|
||||
}
|
63
views/modal.qtpl
Normal file
@ -0,0 +1,63 @@
|
||||
{% import "net/http" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||
|
||||
{% func DeleteAskHTML(rq *http.Request, hyphaName string, isOld bool) %}
|
||||
{%= NavHTML(rq, hyphaName, "delete-ask") %}
|
||||
{%= modalBegin(
|
||||
"delete-confirm",
|
||||
hyphaName,
|
||||
"",
|
||||
"Delete "+util.BeautifulName(hyphaName)+"?") %}
|
||||
{%= modalReallyWant(hyphaName, "unattach") %}
|
||||
<p>In this version of MycorrhizaWiki you cannot undelete a deleted hypha but the history can still be accessed.</p>
|
||||
{%= modalEnd(hyphaName, true) %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func UnattachAskHTML(rq *http.Request, hyphaName string, isOld bool) %}
|
||||
{%= NavHTML(rq, hyphaName, "unattach-ask") %}
|
||||
{%= modalBegin(
|
||||
"unattach",
|
||||
hyphaName,
|
||||
"",
|
||||
"Unattach "+util.BeautifulName(hyphaName)+"?") %}
|
||||
{%= modalReallyWant(hyphaName, "unattach") %}
|
||||
{%= modalEnd(hyphaName, true) %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func RenameAskHTML(rq *http.Request, hyphaName string, isOld bool) %}
|
||||
{%= NavHTML(rq, hyphaName, "rename-ask") %}
|
||||
{%= modalBegin(
|
||||
"rename-confirm",
|
||||
hyphaName,
|
||||
` method="post" enctype="multipart/form-data"`,
|
||||
"Rename "+util.BeautifulName(hyphaName)) %}
|
||||
<label for="new-name">New name</label>
|
||||
<input type="text" value="{%s hyphaName %}" required autofocus id="new-name" name="new-name"/>
|
||||
|
||||
<input type="checkbox" id="recursive" name="recursive" value="true" checked/>
|
||||
<label for="recursive">Rename subhyphae too</label>
|
||||
|
||||
<p>If you rename this hypha, all incoming links and all relative outcoming links will break. You will also lose all history for the new name. Rename carefully.</p>
|
||||
{%= modalEnd(hyphaName, false) %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func modalReallyWant(hyphaName, verb string) %}
|
||||
<p class="modal__confirmation-msg">Do you really want to {%s verb %} hypha <em>{%s hyphaName %}</em>?</p>
|
||||
{% endfunc %}
|
||||
|
||||
{% func modalBegin(path, hyphaName, formAttrs, legend string) %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<form class="modal" action="/{%s path %}/{%s hyphaName %}"{%s= formAttrs %}>
|
||||
<fieldset class="modal__fieldset">
|
||||
<legend class="modal__title">{%s= legend %}</legend>
|
||||
{% endfunc %}
|
||||
|
||||
{% func modalEnd(hyphaName string, shouldFocusOnConfirm bool) %}
|
||||
<input type="submit" value="Confirm" class="modal__action modal__submit" {% if shouldFocusOnConfirm %}autofocus{% endif %}>
|
||||
<a href="/hypha/{%s hyphaName %}" class="modal__action modal__cancel">Cancel</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
355
views/modal.qtpl.go
Normal file
@ -0,0 +1,355 @@
|
||||
// Code generated by qtc from "modal.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line views/modal.qtpl:1
|
||||
package views
|
||||
|
||||
//line views/modal.qtpl:1
|
||||
import "net/http"
|
||||
|
||||
//line views/modal.qtpl:2
|
||||
import "github.com/bouncepaw/mycorrhiza/util"
|
||||
|
||||
//line views/modal.qtpl:4
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line views/modal.qtpl:4
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line views/modal.qtpl:4
|
||||
func StreamDeleteAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line views/modal.qtpl:4
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:5
|
||||
StreamNavHTML(qw422016, rq, hyphaName, "delete-ask")
|
||||
//line views/modal.qtpl:5
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:6
|
||||
streammodalBegin(qw422016,
|
||||
"delete-confirm",
|
||||
hyphaName,
|
||||
"",
|
||||
"Delete "+util.BeautifulName(hyphaName)+"?")
|
||||
//line views/modal.qtpl:10
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:11
|
||||
streammodalReallyWant(qw422016, hyphaName, "unattach")
|
||||
//line views/modal.qtpl:11
|
||||
qw422016.N().S(`
|
||||
<p>In this version of MycorrhizaWiki you cannot undelete a deleted hypha but the history can still be accessed.</p>
|
||||
`)
|
||||
//line views/modal.qtpl:13
|
||||
streammodalEnd(qw422016, hyphaName, true)
|
||||
//line views/modal.qtpl:13
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:14
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:14
|
||||
func WriteDeleteAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line views/modal.qtpl:14
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/modal.qtpl:14
|
||||
StreamDeleteAskHTML(qw422016, rq, hyphaName, isOld)
|
||||
//line views/modal.qtpl:14
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/modal.qtpl:14
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:14
|
||||
func DeleteAskHTML(rq *http.Request, hyphaName string, isOld bool) string {
|
||||
//line views/modal.qtpl:14
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/modal.qtpl:14
|
||||
WriteDeleteAskHTML(qb422016, rq, hyphaName, isOld)
|
||||
//line views/modal.qtpl:14
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/modal.qtpl:14
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/modal.qtpl:14
|
||||
return qs422016
|
||||
//line views/modal.qtpl:14
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:16
|
||||
func StreamUnattachAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line views/modal.qtpl:16
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:17
|
||||
StreamNavHTML(qw422016, rq, hyphaName, "unattach-ask")
|
||||
//line views/modal.qtpl:17
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:18
|
||||
streammodalBegin(qw422016,
|
||||
"unattach",
|
||||
hyphaName,
|
||||
"",
|
||||
"Unattach "+util.BeautifulName(hyphaName)+"?")
|
||||
//line views/modal.qtpl:22
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:23
|
||||
streammodalReallyWant(qw422016, hyphaName, "unattach")
|
||||
//line views/modal.qtpl:23
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:24
|
||||
streammodalEnd(qw422016, hyphaName, true)
|
||||
//line views/modal.qtpl:24
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:25
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:25
|
||||
func WriteUnattachAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line views/modal.qtpl:25
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/modal.qtpl:25
|
||||
StreamUnattachAskHTML(qw422016, rq, hyphaName, isOld)
|
||||
//line views/modal.qtpl:25
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/modal.qtpl:25
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:25
|
||||
func UnattachAskHTML(rq *http.Request, hyphaName string, isOld bool) string {
|
||||
//line views/modal.qtpl:25
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/modal.qtpl:25
|
||||
WriteUnattachAskHTML(qb422016, rq, hyphaName, isOld)
|
||||
//line views/modal.qtpl:25
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/modal.qtpl:25
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/modal.qtpl:25
|
||||
return qs422016
|
||||
//line views/modal.qtpl:25
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:27
|
||||
func StreamRenameAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line views/modal.qtpl:27
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:28
|
||||
StreamNavHTML(qw422016, rq, hyphaName, "rename-ask")
|
||||
//line views/modal.qtpl:28
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:29
|
||||
streammodalBegin(qw422016,
|
||||
"rename-confirm",
|
||||
hyphaName,
|
||||
` method="post" enctype="multipart/form-data"`,
|
||||
"Rename "+util.BeautifulName(hyphaName))
|
||||
//line views/modal.qtpl:33
|
||||
qw422016.N().S(`
|
||||
<label for="new-name">New name</label>
|
||||
<input type="text" value="`)
|
||||
//line views/modal.qtpl:35
|
||||
qw422016.E().S(hyphaName)
|
||||
//line views/modal.qtpl:35
|
||||
qw422016.N().S(`" required autofocus id="new-name" name="new-name"/>
|
||||
|
||||
<input type="checkbox" id="recursive" name="recursive" value="true" checked/>
|
||||
<label for="recursive">Rename subhyphae too</label>
|
||||
|
||||
<p>If you rename this hypha, all incoming links and all relative outcoming links will break. You will also lose all history for the new name. Rename carefully.</p>
|
||||
`)
|
||||
//line views/modal.qtpl:41
|
||||
streammodalEnd(qw422016, hyphaName, false)
|
||||
//line views/modal.qtpl:41
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/modal.qtpl:42
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:42
|
||||
func WriteRenameAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string, isOld bool) {
|
||||
//line views/modal.qtpl:42
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/modal.qtpl:42
|
||||
StreamRenameAskHTML(qw422016, rq, hyphaName, isOld)
|
||||
//line views/modal.qtpl:42
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/modal.qtpl:42
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:42
|
||||
func RenameAskHTML(rq *http.Request, hyphaName string, isOld bool) string {
|
||||
//line views/modal.qtpl:42
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/modal.qtpl:42
|
||||
WriteRenameAskHTML(qb422016, rq, hyphaName, isOld)
|
||||
//line views/modal.qtpl:42
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/modal.qtpl:42
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/modal.qtpl:42
|
||||
return qs422016
|
||||
//line views/modal.qtpl:42
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:44
|
||||
func streammodalReallyWant(qw422016 *qt422016.Writer, hyphaName, verb string) {
|
||||
//line views/modal.qtpl:44
|
||||
qw422016.N().S(`
|
||||
<p class="modal__confirmation-msg">Do you really want to `)
|
||||
//line views/modal.qtpl:45
|
||||
qw422016.E().S(verb)
|
||||
//line views/modal.qtpl:45
|
||||
qw422016.N().S(` hypha <em>`)
|
||||
//line views/modal.qtpl:45
|
||||
qw422016.E().S(hyphaName)
|
||||
//line views/modal.qtpl:45
|
||||
qw422016.N().S(`</em>?</p>
|
||||
`)
|
||||
//line views/modal.qtpl:46
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:46
|
||||
func writemodalReallyWant(qq422016 qtio422016.Writer, hyphaName, verb string) {
|
||||
//line views/modal.qtpl:46
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/modal.qtpl:46
|
||||
streammodalReallyWant(qw422016, hyphaName, verb)
|
||||
//line views/modal.qtpl:46
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/modal.qtpl:46
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:46
|
||||
func modalReallyWant(hyphaName, verb string) string {
|
||||
//line views/modal.qtpl:46
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/modal.qtpl:46
|
||||
writemodalReallyWant(qb422016, hyphaName, verb)
|
||||
//line views/modal.qtpl:46
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/modal.qtpl:46
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/modal.qtpl:46
|
||||
return qs422016
|
||||
//line views/modal.qtpl:46
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:48
|
||||
func streammodalBegin(qw422016 *qt422016.Writer, path, hyphaName, formAttrs, legend string) {
|
||||
//line views/modal.qtpl:48
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<form class="modal" action="/`)
|
||||
//line views/modal.qtpl:51
|
||||
qw422016.E().S(path)
|
||||
//line views/modal.qtpl:51
|
||||
qw422016.N().S(`/`)
|
||||
//line views/modal.qtpl:51
|
||||
qw422016.E().S(hyphaName)
|
||||
//line views/modal.qtpl:51
|
||||
qw422016.N().S(`"`)
|
||||
//line views/modal.qtpl:51
|
||||
qw422016.N().S(formAttrs)
|
||||
//line views/modal.qtpl:51
|
||||
qw422016.N().S(`>
|
||||
<fieldset class="modal__fieldset">
|
||||
<legend class="modal__title">`)
|
||||
//line views/modal.qtpl:53
|
||||
qw422016.N().S(legend)
|
||||
//line views/modal.qtpl:53
|
||||
qw422016.N().S(`</legend>
|
||||
`)
|
||||
//line views/modal.qtpl:54
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:54
|
||||
func writemodalBegin(qq422016 qtio422016.Writer, path, hyphaName, formAttrs, legend string) {
|
||||
//line views/modal.qtpl:54
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/modal.qtpl:54
|
||||
streammodalBegin(qw422016, path, hyphaName, formAttrs, legend)
|
||||
//line views/modal.qtpl:54
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/modal.qtpl:54
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:54
|
||||
func modalBegin(path, hyphaName, formAttrs, legend string) string {
|
||||
//line views/modal.qtpl:54
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/modal.qtpl:54
|
||||
writemodalBegin(qb422016, path, hyphaName, formAttrs, legend)
|
||||
//line views/modal.qtpl:54
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/modal.qtpl:54
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/modal.qtpl:54
|
||||
return qs422016
|
||||
//line views/modal.qtpl:54
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:56
|
||||
func streammodalEnd(qw422016 *qt422016.Writer, hyphaName string, shouldFocusOnConfirm bool) {
|
||||
//line views/modal.qtpl:56
|
||||
qw422016.N().S(`
|
||||
<input type="submit" value="Confirm" class="modal__action modal__submit" `)
|
||||
//line views/modal.qtpl:57
|
||||
if shouldFocusOnConfirm {
|
||||
//line views/modal.qtpl:57
|
||||
qw422016.N().S(`autofocus`)
|
||||
//line views/modal.qtpl:57
|
||||
}
|
||||
//line views/modal.qtpl:57
|
||||
qw422016.N().S(`>
|
||||
<a href="/hypha/`)
|
||||
//line views/modal.qtpl:58
|
||||
qw422016.E().S(hyphaName)
|
||||
//line views/modal.qtpl:58
|
||||
qw422016.N().S(`" class="modal__action modal__cancel">Cancel</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/modal.qtpl:63
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:63
|
||||
func writemodalEnd(qq422016 qtio422016.Writer, hyphaName string, shouldFocusOnConfirm bool) {
|
||||
//line views/modal.qtpl:63
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/modal.qtpl:63
|
||||
streammodalEnd(qw422016, hyphaName, shouldFocusOnConfirm)
|
||||
//line views/modal.qtpl:63
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/modal.qtpl:63
|
||||
}
|
||||
|
||||
//line views/modal.qtpl:63
|
||||
func modalEnd(hyphaName string, shouldFocusOnConfirm bool) string {
|
||||
//line views/modal.qtpl:63
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/modal.qtpl:63
|
||||
writemodalEnd(qb422016, hyphaName, shouldFocusOnConfirm)
|
||||
//line views/modal.qtpl:63
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/modal.qtpl:63
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/modal.qtpl:63
|
||||
return qs422016
|
||||
//line views/modal.qtpl:63
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
{% import "net/http" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||
|
||||
{% func EditHTML(rq *http.Request, hyphaName, textAreaFill, warning string) %}
|
||||
{%s= navHTML(rq, hyphaName, "edit") %}
|
||||
<main class="edit edit_no-preview">
|
||||
<h1>Edit {%s hyphaName %}</h1>
|
||||
{%s= NavHTML(rq, hyphaName, "edit") %}
|
||||
<div class="layout">
|
||||
<main class="main-width edit edit_no-preview">
|
||||
<h1 class="edit__title">Edit {%s util.BeautifulName(hyphaName) %}</h1>
|
||||
{%s= warning %}
|
||||
<form method="post" class="edit-form"
|
||||
action="/upload-text/{%s hyphaName %}">
|
||||
@ -14,12 +16,14 @@
|
||||
<a href="/page/{%s hyphaName %}" class="edit-form__cancel">Cancel</a>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
{% func PreviewHTML(rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) %}
|
||||
{%s= navHTML(rq, hyphaName, "edit") %}
|
||||
<main class="edit edit_with-preview">
|
||||
<h1>Edit {%s hyphaName %} (preview)</h1>
|
||||
{%s= NavHTML(rq, hyphaName, "edit") %}
|
||||
<div class="layout">
|
||||
<main class="main-width edit edit_with-preview">
|
||||
<h1>Edit {%s util.BeautifulName(hyphaName) %} (preview)</h1>
|
||||
{%s= warning %}
|
||||
<form method="post" class="edit-form"
|
||||
action="/upload-text/{%s hyphaName %}">
|
||||
@ -32,4 +36,5 @@
|
||||
<p class="warning">Note that the hypha is not saved yet. You can preview the changes ↓</p>
|
||||
<section class="edit__preview">{%s= renderedPage %}</section>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
@ -1,169 +1,176 @@
|
||||
// Code generated by qtc from "http_mutators.qtpl". DO NOT EDIT.
|
||||
// Code generated by qtc from "mutators.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line templates/http_mutators.qtpl:1
|
||||
package templates
|
||||
//line views/mutators.qtpl:1
|
||||
package views
|
||||
|
||||
//line templates/http_mutators.qtpl:1
|
||||
//line views/mutators.qtpl:1
|
||||
import "net/http"
|
||||
|
||||
//line templates/http_mutators.qtpl:3
|
||||
//line views/mutators.qtpl:2
|
||||
import "github.com/bouncepaw/mycorrhiza/util"
|
||||
|
||||
//line views/mutators.qtpl:4
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line templates/http_mutators.qtpl:3
|
||||
//line views/mutators.qtpl:4
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line templates/http_mutators.qtpl:3
|
||||
//line views/mutators.qtpl:4
|
||||
func StreamEditHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string) {
|
||||
//line templates/http_mutators.qtpl:3
|
||||
//line views/mutators.qtpl:4
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/http_mutators.qtpl:4
|
||||
qw422016.N().S(navHTML(rq, hyphaName, "edit"))
|
||||
//line templates/http_mutators.qtpl:4
|
||||
//line views/mutators.qtpl:5
|
||||
qw422016.N().S(NavHTML(rq, hyphaName, "edit"))
|
||||
//line views/mutators.qtpl:5
|
||||
qw422016.N().S(`
|
||||
<main class="edit edit_no-preview">
|
||||
<h1>Edit `)
|
||||
//line templates/http_mutators.qtpl:6
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_mutators.qtpl:6
|
||||
<div class="layout">
|
||||
<main class="main-width edit edit_no-preview">
|
||||
<h1 class="edit__title">Edit `)
|
||||
//line views/mutators.qtpl:8
|
||||
qw422016.E().S(util.BeautifulName(hyphaName))
|
||||
//line views/mutators.qtpl:8
|
||||
qw422016.N().S(`</h1>
|
||||
`)
|
||||
//line templates/http_mutators.qtpl:7
|
||||
//line views/mutators.qtpl:9
|
||||
qw422016.N().S(warning)
|
||||
//line templates/http_mutators.qtpl:7
|
||||
//line views/mutators.qtpl:9
|
||||
qw422016.N().S(`
|
||||
<form method="post" class="edit-form"
|
||||
action="/upload-text/`)
|
||||
//line templates/http_mutators.qtpl:9
|
||||
//line views/mutators.qtpl:11
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_mutators.qtpl:9
|
||||
//line views/mutators.qtpl:11
|
||||
qw422016.N().S(`">
|
||||
<textarea name="text">`)
|
||||
//line templates/http_mutators.qtpl:10
|
||||
//line views/mutators.qtpl:12
|
||||
qw422016.E().S(textAreaFill)
|
||||
//line templates/http_mutators.qtpl:10
|
||||
//line views/mutators.qtpl:12
|
||||
qw422016.N().S(`</textarea>
|
||||
<br/>
|
||||
<input type="submit" name="action" value="Save" class="edit-form__save"/>
|
||||
<input type="submit" name="action" value="Preview" class="edit-form__preview">
|
||||
<a href="/page/`)
|
||||
//line templates/http_mutators.qtpl:14
|
||||
//line views/mutators.qtpl:16
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_mutators.qtpl:14
|
||||
//line views/mutators.qtpl:16
|
||||
qw422016.N().S(`" class="edit-form__cancel">Cancel</a>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
}
|
||||
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
func WriteEditHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string) {
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
StreamEditHTML(qw422016, rq, hyphaName, textAreaFill, warning)
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
}
|
||||
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
func EditHTML(rq *http.Request, hyphaName, textAreaFill, warning string) string {
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
WriteEditHTML(qb422016, rq, hyphaName, textAreaFill, warning)
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
return qs422016
|
||||
//line templates/http_mutators.qtpl:17
|
||||
//line views/mutators.qtpl:20
|
||||
}
|
||||
|
||||
//line templates/http_mutators.qtpl:19
|
||||
//line views/mutators.qtpl:22
|
||||
func StreamPreviewHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) {
|
||||
//line templates/http_mutators.qtpl:19
|
||||
//line views/mutators.qtpl:22
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/http_mutators.qtpl:20
|
||||
qw422016.N().S(navHTML(rq, hyphaName, "edit"))
|
||||
//line templates/http_mutators.qtpl:20
|
||||
//line views/mutators.qtpl:23
|
||||
qw422016.N().S(NavHTML(rq, hyphaName, "edit"))
|
||||
//line views/mutators.qtpl:23
|
||||
qw422016.N().S(`
|
||||
<main class="edit edit_with-preview">
|
||||
<div class="layout">
|
||||
<main class="main-width edit edit_with-preview">
|
||||
<h1>Edit `)
|
||||
//line templates/http_mutators.qtpl:22
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_mutators.qtpl:22
|
||||
//line views/mutators.qtpl:26
|
||||
qw422016.E().S(util.BeautifulName(hyphaName))
|
||||
//line views/mutators.qtpl:26
|
||||
qw422016.N().S(` (preview)</h1>
|
||||
`)
|
||||
//line templates/http_mutators.qtpl:23
|
||||
//line views/mutators.qtpl:27
|
||||
qw422016.N().S(warning)
|
||||
//line templates/http_mutators.qtpl:23
|
||||
//line views/mutators.qtpl:27
|
||||
qw422016.N().S(`
|
||||
<form method="post" class="edit-form"
|
||||
action="/upload-text/`)
|
||||
//line templates/http_mutators.qtpl:25
|
||||
//line views/mutators.qtpl:29
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_mutators.qtpl:25
|
||||
//line views/mutators.qtpl:29
|
||||
qw422016.N().S(`">
|
||||
<textarea name="text">`)
|
||||
//line templates/http_mutators.qtpl:26
|
||||
//line views/mutators.qtpl:30
|
||||
qw422016.E().S(textAreaFill)
|
||||
//line templates/http_mutators.qtpl:26
|
||||
//line views/mutators.qtpl:30
|
||||
qw422016.N().S(`</textarea>
|
||||
<br/>
|
||||
<input type="submit" name="action" value="Save" class="edit-form__save"/>
|
||||
<input type="submit" name="action" value="Preview" class="edit-form__preview">
|
||||
<a href="/page/`)
|
||||
//line templates/http_mutators.qtpl:30
|
||||
//line views/mutators.qtpl:34
|
||||
qw422016.E().S(hyphaName)
|
||||
//line templates/http_mutators.qtpl:30
|
||||
//line views/mutators.qtpl:34
|
||||
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">`)
|
||||
//line templates/http_mutators.qtpl:33
|
||||
//line views/mutators.qtpl:37
|
||||
qw422016.N().S(renderedPage)
|
||||
//line templates/http_mutators.qtpl:33
|
||||
//line views/mutators.qtpl:37
|
||||
qw422016.N().S(`</section>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
}
|
||||
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
func WritePreviewHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) {
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
StreamPreviewHTML(qw422016, rq, hyphaName, textAreaFill, warning, renderedPage)
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
}
|
||||
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
func PreviewHTML(rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) string {
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
WritePreviewHTML(qb422016, rq, hyphaName, textAreaFill, warning, renderedPage)
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
return qs422016
|
||||
//line templates/http_mutators.qtpl:35
|
||||
//line views/mutators.qtpl:40
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
{% import "net/http" %}
|
||||
{% import "strings" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||
|
||||
@ -11,33 +12,33 @@ type navEntry struct {
|
||||
var navEntries = []navEntry{
|
||||
{"page", "Hypha"},
|
||||
{"edit", "Edit"},
|
||||
{"text", "Raw text"},
|
||||
{"attachment", "Attachment"},
|
||||
{"history", "History"},
|
||||
{"revision", "NOT REACHED"},
|
||||
{"rename-ask", "Rename"},
|
||||
{"delete-ask", "Delete"},
|
||||
{"text", "Raw text"},
|
||||
}
|
||||
%}
|
||||
|
||||
{% func navHTML(rq *http.Request, hyphaName, navType string, revisionHash ...string) %}
|
||||
{% func NavHTML(rq *http.Request, hyphaName, navType string, revisionHash ...string) %}
|
||||
{% code
|
||||
u := user.FromRequest(rq)
|
||||
%}
|
||||
|
||||
<nav class="hypha-tabs">
|
||||
<nav class="hypha-tabs main-width">
|
||||
<ul class="hypha-tabs__flex">
|
||||
{%- for _, entry := range navEntries -%}
|
||||
{%- if navType == "revision" && entry.path == "revision" -%}
|
||||
<li class="hypha-tabs__tab hypha-tabs__tab_active">
|
||||
{%s revisionHash[0] %}
|
||||
<span class="hypha-tabs__selection">{%s revisionHash[0] %}</span>
|
||||
</li>
|
||||
{%- elseif navType == entry.path -%}
|
||||
<li class="hypha-tabs__tab hypha-tabs__tab_active">
|
||||
{%s entry.title %}
|
||||
<span class="hypha-tabs__selection">{%s entry.title %}</span>
|
||||
</li>
|
||||
{%- elseif entry.path != "revision" && u.CanProceed(entry.path) -%}
|
||||
<li class="hypha-tabs__tab">
|
||||
<a href="/{%s entry.path %}/{%s hyphaName %}">{%s entry.title %}</a>
|
||||
<a class="hypha-tabs__link" href="/{%s entry.path %}/{%s hyphaName %}">{%s entry.title %}</a>
|
||||
</li>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
@ -45,7 +46,7 @@ var navEntries = []navEntry{
|
||||
</nav>
|
||||
{% endfunc %}
|
||||
|
||||
{% func userMenuHTML(u *user.User) %}
|
||||
{% func UserMenuHTML(u *user.User) %}
|
||||
{% if user.AuthUsed %}
|
||||
<li class="header-links__entry header-links__entry_user">
|
||||
{% if u.Group == "anon" %}
|
||||
@ -57,3 +58,22 @@ var navEntries = []navEntry{
|
||||
{% endif %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func RelativeHyphaeHTML(relatives string) %}
|
||||
<aside class="relative-hyphae layout-card">
|
||||
<h2 class="relative-hyphae__title layout-card__title">Relative hyphae</h2>
|
||||
{%s= relatives %}
|
||||
</aside>
|
||||
{% endfunc %}
|
||||
|
||||
{% func SubhyphaeHTML(subhyphae string) %}
|
||||
{% if strings.TrimSpace(subhyphae) != "" %}
|
||||
<section class="subhyphae">
|
||||
<h2 class="subhyphae__title">Subhyphae</h2>
|
||||
<nav class="subhyphae__nav">
|
||||
<ul class="subhyphae__list">
|
||||
{%s= subhyphae %}
|
||||
</ul>
|
||||
</nav>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endfunc %}
|
315
views/nav.qtpl.go
Normal file
@ -0,0 +1,315 @@
|
||||
// Code generated by qtc from "nav.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line views/nav.qtpl:1
|
||||
package views
|
||||
|
||||
//line views/nav.qtpl:1
|
||||
import "net/http"
|
||||
|
||||
//line views/nav.qtpl:2
|
||||
import "strings"
|
||||
|
||||
//line views/nav.qtpl:3
|
||||
import "github.com/bouncepaw/mycorrhiza/user"
|
||||
|
||||
//line views/nav.qtpl:4
|
||||
import "github.com/bouncepaw/mycorrhiza/util"
|
||||
|
||||
// This is the <nav> seen on top of many pages.
|
||||
|
||||
//line views/nav.qtpl:7
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line views/nav.qtpl:7
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line views/nav.qtpl:8
|
||||
type navEntry struct {
|
||||
path string
|
||||
title string
|
||||
}
|
||||
|
||||
var navEntries = []navEntry{
|
||||
{"page", "Hypha"},
|
||||
{"edit", "Edit"},
|
||||
{"attachment", "Attachment"},
|
||||
{"history", "History"},
|
||||
{"revision", "NOT REACHED"},
|
||||
{"rename-ask", "Rename"},
|
||||
{"delete-ask", "Delete"},
|
||||
{"text", "Raw text"},
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:24
|
||||
func StreamNavHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, navType string, revisionHash ...string) {
|
||||
//line views/nav.qtpl:24
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/nav.qtpl:26
|
||||
u := user.FromRequest(rq)
|
||||
|
||||
//line views/nav.qtpl:27
|
||||
qw422016.N().S(`
|
||||
<nav class="hypha-tabs main-width">
|
||||
<ul class="hypha-tabs__flex">
|
||||
`)
|
||||
//line views/nav.qtpl:30
|
||||
for _, entry := range navEntries {
|
||||
//line views/nav.qtpl:31
|
||||
if navType == "revision" && entry.path == "revision" {
|
||||
//line views/nav.qtpl:31
|
||||
qw422016.N().S(` <li class="hypha-tabs__tab hypha-tabs__tab_active">
|
||||
<span class="hypha-tabs__selection">`)
|
||||
//line views/nav.qtpl:33
|
||||
qw422016.E().S(revisionHash[0])
|
||||
//line views/nav.qtpl:33
|
||||
qw422016.N().S(`</span>
|
||||
</li>
|
||||
`)
|
||||
//line views/nav.qtpl:35
|
||||
} else if navType == entry.path {
|
||||
//line views/nav.qtpl:35
|
||||
qw422016.N().S(` <li class="hypha-tabs__tab hypha-tabs__tab_active">
|
||||
<span class="hypha-tabs__selection">`)
|
||||
//line views/nav.qtpl:37
|
||||
qw422016.E().S(entry.title)
|
||||
//line views/nav.qtpl:37
|
||||
qw422016.N().S(`</span>
|
||||
</li>
|
||||
`)
|
||||
//line views/nav.qtpl:39
|
||||
} else if entry.path != "revision" && u.CanProceed(entry.path) {
|
||||
//line views/nav.qtpl:39
|
||||
qw422016.N().S(` <li class="hypha-tabs__tab">
|
||||
<a class="hypha-tabs__link" href="/`)
|
||||
//line views/nav.qtpl:41
|
||||
qw422016.E().S(entry.path)
|
||||
//line views/nav.qtpl:41
|
||||
qw422016.N().S(`/`)
|
||||
//line views/nav.qtpl:41
|
||||
qw422016.E().S(hyphaName)
|
||||
//line views/nav.qtpl:41
|
||||
qw422016.N().S(`">`)
|
||||
//line views/nav.qtpl:41
|
||||
qw422016.E().S(entry.title)
|
||||
//line views/nav.qtpl:41
|
||||
qw422016.N().S(`</a>
|
||||
</li>
|
||||
`)
|
||||
//line views/nav.qtpl:43
|
||||
}
|
||||
//line views/nav.qtpl:44
|
||||
}
|
||||
//line views/nav.qtpl:44
|
||||
qw422016.N().S(` </ul>
|
||||
</nav>
|
||||
`)
|
||||
//line views/nav.qtpl:47
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:47
|
||||
func WriteNavHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, navType string, revisionHash ...string) {
|
||||
//line views/nav.qtpl:47
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/nav.qtpl:47
|
||||
StreamNavHTML(qw422016, rq, hyphaName, navType, revisionHash...)
|
||||
//line views/nav.qtpl:47
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/nav.qtpl:47
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:47
|
||||
func NavHTML(rq *http.Request, hyphaName, navType string, revisionHash ...string) string {
|
||||
//line views/nav.qtpl:47
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/nav.qtpl:47
|
||||
WriteNavHTML(qb422016, rq, hyphaName, navType, revisionHash...)
|
||||
//line views/nav.qtpl:47
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/nav.qtpl:47
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/nav.qtpl:47
|
||||
return qs422016
|
||||
//line views/nav.qtpl:47
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:49
|
||||
func StreamUserMenuHTML(qw422016 *qt422016.Writer, u *user.User) {
|
||||
//line views/nav.qtpl:49
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/nav.qtpl:50
|
||||
if user.AuthUsed {
|
||||
//line views/nav.qtpl:50
|
||||
qw422016.N().S(`
|
||||
<li class="header-links__entry header-links__entry_user">
|
||||
`)
|
||||
//line views/nav.qtpl:52
|
||||
if u.Group == "anon" {
|
||||
//line views/nav.qtpl:52
|
||||
qw422016.N().S(`
|
||||
<a href="/login" class="header-links__link">Login</a>
|
||||
`)
|
||||
//line views/nav.qtpl:54
|
||||
} else {
|
||||
//line views/nav.qtpl:54
|
||||
qw422016.N().S(`
|
||||
<a href="/page/`)
|
||||
//line views/nav.qtpl:55
|
||||
qw422016.E().S(util.UserHypha)
|
||||
//line views/nav.qtpl:55
|
||||
qw422016.N().S(`/`)
|
||||
//line views/nav.qtpl:55
|
||||
qw422016.E().S(u.Name)
|
||||
//line views/nav.qtpl:55
|
||||
qw422016.N().S(`" class="header-links__link">`)
|
||||
//line views/nav.qtpl:55
|
||||
qw422016.E().S(u.Name)
|
||||
//line views/nav.qtpl:55
|
||||
qw422016.N().S(`</a>
|
||||
`)
|
||||
//line views/nav.qtpl:56
|
||||
}
|
||||
//line views/nav.qtpl:56
|
||||
qw422016.N().S(`
|
||||
</li>
|
||||
`)
|
||||
//line views/nav.qtpl:58
|
||||
}
|
||||
//line views/nav.qtpl:58
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/nav.qtpl:59
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:59
|
||||
func WriteUserMenuHTML(qq422016 qtio422016.Writer, u *user.User) {
|
||||
//line views/nav.qtpl:59
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/nav.qtpl:59
|
||||
StreamUserMenuHTML(qw422016, u)
|
||||
//line views/nav.qtpl:59
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/nav.qtpl:59
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:59
|
||||
func UserMenuHTML(u *user.User) string {
|
||||
//line views/nav.qtpl:59
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/nav.qtpl:59
|
||||
WriteUserMenuHTML(qb422016, u)
|
||||
//line views/nav.qtpl:59
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/nav.qtpl:59
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/nav.qtpl:59
|
||||
return qs422016
|
||||
//line views/nav.qtpl:59
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:61
|
||||
func StreamRelativeHyphaeHTML(qw422016 *qt422016.Writer, relatives string) {
|
||||
//line views/nav.qtpl:61
|
||||
qw422016.N().S(`
|
||||
<aside class="relative-hyphae layout-card">
|
||||
<h2 class="relative-hyphae__title layout-card__title">Relative hyphae</h2>
|
||||
`)
|
||||
//line views/nav.qtpl:64
|
||||
qw422016.N().S(relatives)
|
||||
//line views/nav.qtpl:64
|
||||
qw422016.N().S(`
|
||||
</aside>
|
||||
`)
|
||||
//line views/nav.qtpl:66
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:66
|
||||
func WriteRelativeHyphaeHTML(qq422016 qtio422016.Writer, relatives string) {
|
||||
//line views/nav.qtpl:66
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/nav.qtpl:66
|
||||
StreamRelativeHyphaeHTML(qw422016, relatives)
|
||||
//line views/nav.qtpl:66
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/nav.qtpl:66
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:66
|
||||
func RelativeHyphaeHTML(relatives string) string {
|
||||
//line views/nav.qtpl:66
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/nav.qtpl:66
|
||||
WriteRelativeHyphaeHTML(qb422016, relatives)
|
||||
//line views/nav.qtpl:66
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/nav.qtpl:66
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/nav.qtpl:66
|
||||
return qs422016
|
||||
//line views/nav.qtpl:66
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:68
|
||||
func StreamSubhyphaeHTML(qw422016 *qt422016.Writer, subhyphae string) {
|
||||
//line views/nav.qtpl:68
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/nav.qtpl:69
|
||||
if strings.TrimSpace(subhyphae) != "" {
|
||||
//line views/nav.qtpl:69
|
||||
qw422016.N().S(`
|
||||
<section class="subhyphae">
|
||||
<h2 class="subhyphae__title">Subhyphae</h2>
|
||||
<nav class="subhyphae__nav">
|
||||
<ul class="subhyphae__list">
|
||||
`)
|
||||
//line views/nav.qtpl:74
|
||||
qw422016.N().S(subhyphae)
|
||||
//line views/nav.qtpl:74
|
||||
qw422016.N().S(`
|
||||
</ul>
|
||||
</nav>
|
||||
</section>
|
||||
`)
|
||||
//line views/nav.qtpl:78
|
||||
}
|
||||
//line views/nav.qtpl:78
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/nav.qtpl:79
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:79
|
||||
func WriteSubhyphaeHTML(qq422016 qtio422016.Writer, subhyphae string) {
|
||||
//line views/nav.qtpl:79
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/nav.qtpl:79
|
||||
StreamSubhyphaeHTML(qw422016, subhyphae)
|
||||
//line views/nav.qtpl:79
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/nav.qtpl:79
|
||||
}
|
||||
|
||||
//line views/nav.qtpl:79
|
||||
func SubhyphaeHTML(subhyphae string) string {
|
||||
//line views/nav.qtpl:79
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/nav.qtpl:79
|
||||
WriteSubhyphaeHTML(qb422016, subhyphae)
|
||||
//line views/nav.qtpl:79
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/nav.qtpl:79
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/nav.qtpl:79
|
||||
return qs422016
|
||||
//line views/nav.qtpl:79
|
||||
}
|
131
views/readers.qtpl
Normal file
@ -0,0 +1,131 @@
|
||||
{% import "net/http" %}
|
||||
{% import "strings" %}
|
||||
{% import "path" %}
|
||||
{% import "os" %}
|
||||
|
||||
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/mimetype" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/tree" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||
|
||||
{% func AttachmentMenuHTML(rq *http.Request, h *hyphae.Hypha, u *user.User) %}
|
||||
{%= NavHTML(rq, h.Name, "attachment") %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<h1>Attachment of {%s util.BeautifulName(h.Name) %}</h1>
|
||||
{% if h.BinaryPath == "" %}
|
||||
<p class="warning">This hypha has no attachment, you can upload it here.</p>
|
||||
{% else %}
|
||||
<p class="warning">You can manage the hypha's attachment on this page.</p>
|
||||
{% endif %}
|
||||
|
||||
<section class="amnt-grid">
|
||||
|
||||
{% if h.BinaryPath != "" %}
|
||||
{% code
|
||||
mime := mimetype.FromExtension(path.Ext(h.BinaryPath))
|
||||
fileinfo, err := os.Stat(h.BinaryPath) %}
|
||||
{% if err == nil %}
|
||||
<fieldset class="amnt-menu-block">
|
||||
<legend class="modal__title modal__title_small">Stat</legend>
|
||||
<p class="modal__confirmation-msg"><b>File size:</b> {%dl fileinfo.Size() %} bytes</p>
|
||||
<p><b>MIME type:</b> {%s mime %}</p>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
|
||||
{% if strings.HasPrefix(mime, "image/") %}
|
||||
<fieldset class="amnt-menu-block">
|
||||
<legend class="modal__title modal__title_small">Include</legend>
|
||||
<p class="modal__confirmation-msg">This attachment is an image. To include it n a hypha, use a syntax like this:</p>
|
||||
<pre class="codebleck"><code>img { {%s h.Name %} }</code></pre>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if u.CanProceed("upload-binary") %}
|
||||
<form action="/upload-binary/{%s h.Name %}"
|
||||
method="post" enctype="multipart/form-data"
|
||||
class="modal amnt-menu-block">
|
||||
<fieldset class="modal__fieldset upload-binary">
|
||||
<legend class="modal__title modal__title_small">Attach</legend>
|
||||
<p class="modal__confirmation-msg">You can upload a new attachment. Please do not upload too big pictures unless you need to because may not want to wait for big pictures to load.</p>
|
||||
<input type="file" class="upload-binary__input" name="binary">
|
||||
<input type="submit" class="modal__action modal__submit">
|
||||
</fieldset>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% if h.BinaryPath != "" && u.CanProceed("unattach-confirm") %}
|
||||
<form action="/unattach-confirm/{%s h.Name %}" method="post" class="modal amnt-menu-block">
|
||||
<fieldset class="modal__fieldset">
|
||||
<legend class="modal__title modal__title_small">Unattach</legend>
|
||||
<p class="modal__confirmation-msg">Please note that you don't have to unattach before uploading a new attachment.</p>
|
||||
<input type="submit" class="modal__action modal__submit">
|
||||
</fieldset>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
If `contents` == "", a helpful message is shown instead.
|
||||
{% func HyphaHTML(rq *http.Request, h *hyphae.Hypha, contents string) %}
|
||||
{% code
|
||||
relatives, subhyphae, prevHyphaName, nextHyphaName := tree.Tree(h.Name)
|
||||
%}
|
||||
{%= NavHTML(rq, h.Name, "page") %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<article>
|
||||
{%s= NaviTitleHTML(h) %}
|
||||
{% if contents == "" %}
|
||||
<p>This hypha has no text. Why not <a href="/edit/{%s h.Name %}">create it</a>?</p>
|
||||
{% if u := user.FromRequest(rq); (!user.AuthUsed || u.Group != "anon") && !h.Exists %}
|
||||
<form action="/upload-binary/{%s h.Name %}"
|
||||
method="post" enctype="multipart/form-data"
|
||||
class="upload-binary">
|
||||
<label for="upload-binary__input">Upload an attachment:</label>
|
||||
<input type="file" id="upload-binary__input" name="binary">
|
||||
<input type="submit">
|
||||
</form>
|
||||
<br>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{%s= contents %}
|
||||
{% endif %}
|
||||
</article>
|
||||
<section class="prevnext">
|
||||
{% if prevHyphaName != "" %}
|
||||
<a class="prevnext__el prevnext__prev" href="/hypha/{%s prevHyphaName %}" rel="prev">← {%s util.BeautifulName(path.Base(prevHyphaName)) %}</a>
|
||||
{% endif %}
|
||||
{% if nextHyphaName != "" %}
|
||||
<a class="prevnext__el prevnext__next" href="/hypha/{%s nextHyphaName %}" rel="next">{%s util.BeautifulName(path.Base(nextHyphaName)) %} →</a>
|
||||
{% endif %}
|
||||
</section>
|
||||
{%= SubhyphaeHTML(subhyphae) %}
|
||||
</main>
|
||||
{%= RelativeHyphaeHTML(relatives) %}
|
||||
{%= BackLinksHTML(h) %}
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
{% func RevisionHTML(rq *http.Request, h *hyphae.Hypha, contents, revHash string) %}
|
||||
{% code
|
||||
relatives, subhyphae, _, _ := tree.Tree(h.Name)
|
||||
%}
|
||||
{%= NavHTML(rq, h.Name, "revision", revHash) %}
|
||||
<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>
|
||||
{%s= NaviTitleHTML(h) %}
|
||||
{%s= contents %}
|
||||
</article>
|
||||
{%= SubhyphaeHTML(subhyphae) %}
|
||||
</main>
|
||||
{%= RelativeHyphaeHTML(relatives) %}
|
||||
</div>
|
||||
{% endfunc %}
|
454
views/readers.qtpl.go
Normal file
@ -0,0 +1,454 @@
|
||||
// Code generated by qtc from "readers.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line views/readers.qtpl:1
|
||||
package views
|
||||
|
||||
//line views/readers.qtpl:1
|
||||
import "net/http"
|
||||
|
||||
//line views/readers.qtpl:2
|
||||
import "strings"
|
||||
|
||||
//line views/readers.qtpl:3
|
||||
import "path"
|
||||
|
||||
//line views/readers.qtpl:4
|
||||
import "os"
|
||||
|
||||
//line views/readers.qtpl:6
|
||||
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
|
||||
//line views/readers.qtpl:7
|
||||
import "github.com/bouncepaw/mycorrhiza/mimetype"
|
||||
|
||||
//line views/readers.qtpl:8
|
||||
import "github.com/bouncepaw/mycorrhiza/tree"
|
||||
|
||||
//line views/readers.qtpl:9
|
||||
import "github.com/bouncepaw/mycorrhiza/user"
|
||||
|
||||
//line views/readers.qtpl:10
|
||||
import "github.com/bouncepaw/mycorrhiza/util"
|
||||
|
||||
//line views/readers.qtpl:12
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line views/readers.qtpl:12
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line views/readers.qtpl:12
|
||||
func StreamAttachmentMenuHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User) {
|
||||
//line views/readers.qtpl:12
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:13
|
||||
StreamNavHTML(qw422016, rq, h.Name, "attachment")
|
||||
//line views/readers.qtpl:13
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<h1>Attachment of `)
|
||||
//line views/readers.qtpl:16
|
||||
qw422016.E().S(util.BeautifulName(h.Name))
|
||||
//line views/readers.qtpl:16
|
||||
qw422016.N().S(`</h1>
|
||||
`)
|
||||
//line views/readers.qtpl:17
|
||||
if h.BinaryPath == "" {
|
||||
//line views/readers.qtpl:17
|
||||
qw422016.N().S(`
|
||||
<p class="warning">This hypha has no attachment, you can upload it here.</p>
|
||||
`)
|
||||
//line views/readers.qtpl:19
|
||||
} else {
|
||||
//line views/readers.qtpl:19
|
||||
qw422016.N().S(`
|
||||
<p class="warning">You can manage the hypha's attachment on this page.</p>
|
||||
`)
|
||||
//line views/readers.qtpl:21
|
||||
}
|
||||
//line views/readers.qtpl:21
|
||||
qw422016.N().S(`
|
||||
|
||||
<section class="amnt-grid">
|
||||
|
||||
`)
|
||||
//line views/readers.qtpl:25
|
||||
if h.BinaryPath != "" {
|
||||
//line views/readers.qtpl:25
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:27
|
||||
mime := mimetype.FromExtension(path.Ext(h.BinaryPath))
|
||||
fileinfo, err := os.Stat(h.BinaryPath)
|
||||
|
||||
//line views/readers.qtpl:28
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:29
|
||||
if err == nil {
|
||||
//line views/readers.qtpl:29
|
||||
qw422016.N().S(`
|
||||
<fieldset class="amnt-menu-block">
|
||||
<legend class="modal__title modal__title_small">Stat</legend>
|
||||
<p class="modal__confirmation-msg"><b>File size:</b> `)
|
||||
//line views/readers.qtpl:32
|
||||
qw422016.N().DL(fileinfo.Size())
|
||||
//line views/readers.qtpl:32
|
||||
qw422016.N().S(` bytes</p>
|
||||
<p><b>MIME type:</b> `)
|
||||
//line views/readers.qtpl:33
|
||||
qw422016.E().S(mime)
|
||||
//line views/readers.qtpl:33
|
||||
qw422016.N().S(`</p>
|
||||
</fieldset>
|
||||
`)
|
||||
//line views/readers.qtpl:35
|
||||
}
|
||||
//line views/readers.qtpl:35
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line views/readers.qtpl:37
|
||||
if strings.HasPrefix(mime, "image/") {
|
||||
//line views/readers.qtpl:37
|
||||
qw422016.N().S(`
|
||||
<fieldset class="amnt-menu-block">
|
||||
<legend class="modal__title modal__title_small">Include</legend>
|
||||
<p class="modal__confirmation-msg">This attachment is an image. To include it n a hypha, use a syntax like this:</p>
|
||||
<pre class="codebleck"><code>img { `)
|
||||
//line views/readers.qtpl:41
|
||||
qw422016.E().S(h.Name)
|
||||
//line views/readers.qtpl:41
|
||||
qw422016.N().S(` }</code></pre>
|
||||
</fieldset>
|
||||
`)
|
||||
//line views/readers.qtpl:43
|
||||
}
|
||||
//line views/readers.qtpl:43
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:44
|
||||
}
|
||||
//line views/readers.qtpl:44
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line views/readers.qtpl:46
|
||||
if u.CanProceed("upload-binary") {
|
||||
//line views/readers.qtpl:46
|
||||
qw422016.N().S(`
|
||||
<form action="/upload-binary/`)
|
||||
//line views/readers.qtpl:47
|
||||
qw422016.E().S(h.Name)
|
||||
//line views/readers.qtpl:47
|
||||
qw422016.N().S(`"
|
||||
method="post" enctype="multipart/form-data"
|
||||
class="modal amnt-menu-block">
|
||||
<fieldset class="modal__fieldset upload-binary">
|
||||
<legend class="modal__title modal__title_small">Attach</legend>
|
||||
<p class="modal__confirmation-msg">You can upload a new attachment. Please do not upload too big pictures unless you need to because may not want to wait for big pictures to load.</p>
|
||||
<input type="file" class="upload-binary__input" name="binary">
|
||||
<input type="submit" class="modal__action modal__submit">
|
||||
</fieldset>
|
||||
</form>
|
||||
`)
|
||||
//line views/readers.qtpl:57
|
||||
}
|
||||
//line views/readers.qtpl:57
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line views/readers.qtpl:59
|
||||
if h.BinaryPath != "" && u.CanProceed("unattach-confirm") {
|
||||
//line views/readers.qtpl:59
|
||||
qw422016.N().S(`
|
||||
<form action="/unattach-confirm/`)
|
||||
//line views/readers.qtpl:60
|
||||
qw422016.E().S(h.Name)
|
||||
//line views/readers.qtpl:60
|
||||
qw422016.N().S(`" method="post" class="modal amnt-menu-block">
|
||||
<fieldset class="modal__fieldset">
|
||||
<legend class="modal__title modal__title_small">Unattach</legend>
|
||||
<p class="modal__confirmation-msg">Please note that you don't have to unattach before uploading a new attachment.</p>
|
||||
<input type="submit" class="modal__action modal__submit">
|
||||
</fieldset>
|
||||
</form>
|
||||
`)
|
||||
//line views/readers.qtpl:67
|
||||
}
|
||||
//line views/readers.qtpl:67
|
||||
qw422016.N().S(`
|
||||
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/readers.qtpl:72
|
||||
}
|
||||
|
||||
//line views/readers.qtpl:72
|
||||
func WriteAttachmentMenuHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User) {
|
||||
//line views/readers.qtpl:72
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/readers.qtpl:72
|
||||
StreamAttachmentMenuHTML(qw422016, rq, h, u)
|
||||
//line views/readers.qtpl:72
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/readers.qtpl:72
|
||||
}
|
||||
|
||||
//line views/readers.qtpl:72
|
||||
func AttachmentMenuHTML(rq *http.Request, h *hyphae.Hypha, u *user.User) string {
|
||||
//line views/readers.qtpl:72
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/readers.qtpl:72
|
||||
WriteAttachmentMenuHTML(qb422016, rq, h, u)
|
||||
//line views/readers.qtpl:72
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/readers.qtpl:72
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/readers.qtpl:72
|
||||
return qs422016
|
||||
//line views/readers.qtpl:72
|
||||
}
|
||||
|
||||
// If `contents` == "", a helpful message is shown instead.
|
||||
|
||||
//line views/readers.qtpl:75
|
||||
func StreamHyphaHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, contents string) {
|
||||
//line views/readers.qtpl:75
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:77
|
||||
relatives, subhyphae, prevHyphaName, nextHyphaName := tree.Tree(h.Name)
|
||||
|
||||
//line views/readers.qtpl:78
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:79
|
||||
StreamNavHTML(qw422016, rq, h.Name, "page")
|
||||
//line views/readers.qtpl:79
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<article>
|
||||
`)
|
||||
//line views/readers.qtpl:83
|
||||
qw422016.N().S(NaviTitleHTML(h))
|
||||
//line views/readers.qtpl:83
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:84
|
||||
if contents == "" {
|
||||
//line views/readers.qtpl:84
|
||||
qw422016.N().S(`
|
||||
<p>This hypha has no text. Why not <a href="/edit/`)
|
||||
//line views/readers.qtpl:85
|
||||
qw422016.E().S(h.Name)
|
||||
//line views/readers.qtpl:85
|
||||
qw422016.N().S(`">create it</a>?</p>
|
||||
`)
|
||||
//line views/readers.qtpl:86
|
||||
if u := user.FromRequest(rq); (!user.AuthUsed || u.Group != "anon") && !h.Exists {
|
||||
//line views/readers.qtpl:86
|
||||
qw422016.N().S(`
|
||||
<form action="/upload-binary/`)
|
||||
//line views/readers.qtpl:87
|
||||
qw422016.E().S(h.Name)
|
||||
//line views/readers.qtpl:87
|
||||
qw422016.N().S(`"
|
||||
method="post" enctype="multipart/form-data"
|
||||
class="upload-binary">
|
||||
<label for="upload-binary__input">Upload an attachment:</label>
|
||||
<input type="file" id="upload-binary__input" name="binary">
|
||||
<input type="submit">
|
||||
</form>
|
||||
<br>
|
||||
`)
|
||||
//line views/readers.qtpl:95
|
||||
}
|
||||
//line views/readers.qtpl:95
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:96
|
||||
} else {
|
||||
//line views/readers.qtpl:96
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:97
|
||||
qw422016.N().S(contents)
|
||||
//line views/readers.qtpl:97
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:98
|
||||
}
|
||||
//line views/readers.qtpl:98
|
||||
qw422016.N().S(`
|
||||
</article>
|
||||
<section class="prevnext">
|
||||
`)
|
||||
//line views/readers.qtpl:101
|
||||
if prevHyphaName != "" {
|
||||
//line views/readers.qtpl:101
|
||||
qw422016.N().S(`
|
||||
<a class="prevnext__el prevnext__prev" href="/hypha/`)
|
||||
//line views/readers.qtpl:102
|
||||
qw422016.E().S(prevHyphaName)
|
||||
//line views/readers.qtpl:102
|
||||
qw422016.N().S(`" rel="prev">← `)
|
||||
//line views/readers.qtpl:102
|
||||
qw422016.E().S(util.BeautifulName(path.Base(prevHyphaName)))
|
||||
//line views/readers.qtpl:102
|
||||
qw422016.N().S(`</a>
|
||||
`)
|
||||
//line views/readers.qtpl:103
|
||||
}
|
||||
//line views/readers.qtpl:103
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:104
|
||||
if nextHyphaName != "" {
|
||||
//line views/readers.qtpl:104
|
||||
qw422016.N().S(`
|
||||
<a class="prevnext__el prevnext__next" href="/hypha/`)
|
||||
//line views/readers.qtpl:105
|
||||
qw422016.E().S(nextHyphaName)
|
||||
//line views/readers.qtpl:105
|
||||
qw422016.N().S(`" rel="next">`)
|
||||
//line views/readers.qtpl:105
|
||||
qw422016.E().S(util.BeautifulName(path.Base(nextHyphaName)))
|
||||
//line views/readers.qtpl:105
|
||||
qw422016.N().S(` →</a>
|
||||
`)
|
||||
//line views/readers.qtpl:106
|
||||
}
|
||||
//line views/readers.qtpl:106
|
||||
qw422016.N().S(`
|
||||
</section>
|
||||
`)
|
||||
//line views/readers.qtpl:108
|
||||
StreamSubhyphaeHTML(qw422016, subhyphae)
|
||||
//line views/readers.qtpl:108
|
||||
qw422016.N().S(`
|
||||
</main>
|
||||
`)
|
||||
//line views/readers.qtpl:110
|
||||
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:113
|
||||
func WriteHyphaHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, contents string) {
|
||||
//line views/readers.qtpl:113
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/readers.qtpl:113
|
||||
StreamHyphaHTML(qw422016, rq, h, contents)
|
||||
//line views/readers.qtpl:113
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/readers.qtpl:113
|
||||
}
|
||||
|
||||
//line views/readers.qtpl:113
|
||||
func HyphaHTML(rq *http.Request, h *hyphae.Hypha, contents string) string {
|
||||
//line views/readers.qtpl:113
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/readers.qtpl:113
|
||||
WriteHyphaHTML(qb422016, rq, h, contents)
|
||||
//line views/readers.qtpl:113
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/readers.qtpl:113
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/readers.qtpl:113
|
||||
return qs422016
|
||||
//line views/readers.qtpl:113
|
||||
}
|
||||
|
||||
//line views/readers.qtpl:115
|
||||
func StreamRevisionHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, contents, revHash string) {
|
||||
//line views/readers.qtpl:115
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:117
|
||||
relatives, subhyphae, _, _ := tree.Tree(h.Name)
|
||||
|
||||
//line views/readers.qtpl:118
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:119
|
||||
StreamNavHTML(qw422016, rq, h.Name, "revision", revHash)
|
||||
//line views/readers.qtpl:119
|
||||
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
|
||||
qw422016.N().S(NaviTitleHTML(h))
|
||||
//line views/readers.qtpl:124
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line views/readers.qtpl:125
|
||||
qw422016.N().S(contents)
|
||||
//line views/readers.qtpl:125
|
||||
qw422016.N().S(`
|
||||
</article>
|
||||
`)
|
||||
//line views/readers.qtpl:127
|
||||
StreamSubhyphaeHTML(qw422016, subhyphae)
|
||||
//line views/readers.qtpl:127
|
||||
qw422016.N().S(`
|
||||
</main>
|
||||
`)
|
||||
//line views/readers.qtpl:129
|
||||
StreamRelativeHyphaeHTML(qw422016, relatives)
|
||||
//line views/readers.qtpl:129
|
||||
qw422016.N().S(`
|
||||
</div>
|
||||
`)
|
||||
//line views/readers.qtpl:131
|
||||
}
|
||||
|
||||
//line views/readers.qtpl:131
|
||||
func WriteRevisionHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, contents, revHash string) {
|
||||
//line views/readers.qtpl:131
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/readers.qtpl:131
|
||||
StreamRevisionHTML(qw422016, rq, h, contents, revHash)
|
||||
//line views/readers.qtpl:131
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/readers.qtpl:131
|
||||
}
|
||||
|
||||
//line views/readers.qtpl:131
|
||||
func RevisionHTML(rq *http.Request, h *hyphae.Hypha, contents, revHash string) string {
|
||||
//line views/readers.qtpl:131
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/readers.qtpl:131
|
||||
WriteRevisionHTML(qb422016, rq, h, contents, revHash)
|
||||
//line views/readers.qtpl:131
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/readers.qtpl:131
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/readers.qtpl:131
|
||||
return qs422016
|
||||
//line views/readers.qtpl:131
|
||||
}
|
147
views/stuff.qtpl
Normal file
@ -0,0 +1,147 @@
|
||||
{% import "path/filepath" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||
|
||||
{% func BaseHTML(title, body string, u *user.User, headElements ...string) %}
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="/static/common.css">
|
||||
<title>{%s title %}</title>
|
||||
{% for _, el := range headElements %}{%s= el %}{% endfor %}
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="header-links main-width">
|
||||
<ul class="header-links__list">
|
||||
{%- for _, link := range util.HeaderLinks -%}
|
||||
<li class="header-links__entry"><a class="header-links__link" href="{%s link.Href %}">{%s link.Display %}</a></li>
|
||||
{%- endfor -%}
|
||||
{%s= UserMenuHTML(u) %}
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
{%s= body %}
|
||||
</body>
|
||||
</html>
|
||||
{% endfunc %}
|
||||
|
||||
{% func UserListHTML() %}
|
||||
<div class="layout">
|
||||
<main class="main-width user-list">
|
||||
<h1>List of users</h1>
|
||||
{% code
|
||||
var (
|
||||
admins = make([]string, 0)
|
||||
moderators = make([]string, 0)
|
||||
editors = make([]string, 0)
|
||||
)
|
||||
for u := range user.YieldUsers() {
|
||||
switch u.Group {
|
||||
case "admin":
|
||||
admins = append(admins, u.Name)
|
||||
case "moderator":
|
||||
moderators = append(moderators, u.Name)
|
||||
case "editor", "trusted":
|
||||
editors = append(editors, u.Name)
|
||||
}
|
||||
}
|
||||
%}
|
||||
<section>
|
||||
<h2>Admins</h2>
|
||||
<ol>{% for _, name := range admins %}
|
||||
<li><a href="/page/{%s util.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
||||
{% endfor %}</ol>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Moderators</h2>
|
||||
<ol>{% for _, name := range moderators %}
|
||||
<li><a href="/page/{%s util.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
||||
{% endfor %}</ol>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Editors</h2>
|
||||
<ol>{% for _, name := range editors %}
|
||||
<li><a href="/page/{%s util.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
||||
{% endfor %}</ol>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
{% func HyphaListHTML() %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<h1>List of hyphae</h1>
|
||||
<p>This wiki has {%d hyphae.Count() %} hyphae.</p>
|
||||
<ul class="hypha-list">
|
||||
{% for h := range hyphae.YieldExistingHyphae() %}
|
||||
<li class="hypha-list__entry">
|
||||
<a class="hypha-list__link" href="/hypha/{%s h.Name %}">{%s util.BeautifulName(h.Name) %}</a>
|
||||
{% if h.BinaryPath != "" %}
|
||||
<span class="hypha-list__amnt-type">{%s filepath.Ext(h.BinaryPath)[1:] %}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
{% func AboutHTML() %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<section>
|
||||
<h1>About {%s util.SiteName %}</h1>
|
||||
<ul>
|
||||
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> β 0.13</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>
|
||||
<li><b>Administrators:</b> {%- for i, username := range user.ListUsersWithGroup("admin") -%}
|
||||
{%- if i > 0 -%}<span aria-hidden="true">, </span>
|
||||
{%- endif -%}
|
||||
<a href="/page/{%s util.UserHypha %}/{%s username %}">{%s username %}</a>{%- endfor -%}</li>
|
||||
{%- else -%}
|
||||
<li>This wiki does not use authorization</li>
|
||||
{%- endif -%}
|
||||
</ul>
|
||||
<p>See <a href="/list">/list</a> for information about hyphae on this wiki.</p>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
||||
|
||||
{% func AdminPanelHTML() %}
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<h1>Admininstrative functions</h1>
|
||||
<section>
|
||||
<h2>Safe things</h2>
|
||||
<ul>
|
||||
<li><a href="/about">About this wiki<a></li>
|
||||
<li><a href="/user-list">User list</a></li>
|
||||
<li><a href="/update-header-links">Update header links</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Dangerous things</h2>
|
||||
<form action="/admin/shutdown" method="POST" style="float:left">
|
||||
<fieldset>
|
||||
<legend>Shutdown wiki</legend>
|
||||
<input type="submit">
|
||||
</fieldset>
|
||||
</form>
|
||||
<form action="/reindex" method="GET" style="float:left">
|
||||
<fieldset>
|
||||
<legend>Reindex hyphae</legend>
|
||||
<input type="submit">
|
||||
</fieldset>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
{% endfunc %}
|
502
views/stuff.qtpl.go
Normal file
@ -0,0 +1,502 @@
|
||||
// Code generated by qtc from "stuff.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line views/stuff.qtpl:1
|
||||
package views
|
||||
|
||||
//line views/stuff.qtpl:1
|
||||
import "path/filepath"
|
||||
|
||||
//line views/stuff.qtpl:2
|
||||
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
|
||||
//line views/stuff.qtpl:3
|
||||
import "github.com/bouncepaw/mycorrhiza/user"
|
||||
|
||||
//line views/stuff.qtpl:4
|
||||
import "github.com/bouncepaw/mycorrhiza/util"
|
||||
|
||||
//line views/stuff.qtpl:6
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line views/stuff.qtpl:6
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line views/stuff.qtpl:6
|
||||
func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string, u *user.User, headElements ...string) {
|
||||
//line views/stuff.qtpl:6
|
||||
qw422016.N().S(`
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="/static/common.css">
|
||||
<title>`)
|
||||
//line views/stuff.qtpl:13
|
||||
qw422016.E().S(title)
|
||||
//line views/stuff.qtpl:13
|
||||
qw422016.N().S(`</title>
|
||||
`)
|
||||
//line views/stuff.qtpl:14
|
||||
for _, el := range headElements {
|
||||
//line views/stuff.qtpl:14
|
||||
qw422016.N().S(el)
|
||||
//line views/stuff.qtpl:14
|
||||
}
|
||||
//line views/stuff.qtpl:14
|
||||
qw422016.N().S(`
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="header-links main-width">
|
||||
<ul class="header-links__list">
|
||||
`)
|
||||
//line views/stuff.qtpl:20
|
||||
for _, link := range util.HeaderLinks {
|
||||
//line views/stuff.qtpl:20
|
||||
qw422016.N().S(` <li class="header-links__entry"><a class="header-links__link" href="`)
|
||||
//line views/stuff.qtpl:21
|
||||
qw422016.E().S(link.Href)
|
||||
//line views/stuff.qtpl:21
|
||||
qw422016.N().S(`">`)
|
||||
//line views/stuff.qtpl:21
|
||||
qw422016.E().S(link.Display)
|
||||
//line views/stuff.qtpl:21
|
||||
qw422016.N().S(`</a></li>
|
||||
`)
|
||||
//line views/stuff.qtpl:22
|
||||
}
|
||||
//line views/stuff.qtpl:22
|
||||
qw422016.N().S(` `)
|
||||
//line views/stuff.qtpl:23
|
||||
qw422016.N().S(UserMenuHTML(u))
|
||||
//line views/stuff.qtpl:23
|
||||
qw422016.N().S(`
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
`)
|
||||
//line views/stuff.qtpl:27
|
||||
qw422016.N().S(body)
|
||||
//line views/stuff.qtpl:27
|
||||
qw422016.N().S(`
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
//line views/stuff.qtpl:30
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:30
|
||||
func WriteBaseHTML(qq422016 qtio422016.Writer, title, body string, u *user.User, headElements ...string) {
|
||||
//line views/stuff.qtpl:30
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/stuff.qtpl:30
|
||||
StreamBaseHTML(qw422016, title, body, u, headElements...)
|
||||
//line views/stuff.qtpl:30
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/stuff.qtpl:30
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:30
|
||||
func BaseHTML(title, body string, u *user.User, headElements ...string) string {
|
||||
//line views/stuff.qtpl:30
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/stuff.qtpl:30
|
||||
WriteBaseHTML(qb422016, title, body, u, headElements...)
|
||||
//line views/stuff.qtpl:30
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/stuff.qtpl:30
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/stuff.qtpl:30
|
||||
return qs422016
|
||||
//line views/stuff.qtpl:30
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:32
|
||||
func StreamUserListHTML(qw422016 *qt422016.Writer) {
|
||||
//line views/stuff.qtpl:32
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width user-list">
|
||||
<h1>List of users</h1>
|
||||
`)
|
||||
//line views/stuff.qtpl:37
|
||||
var (
|
||||
admins = make([]string, 0)
|
||||
moderators = make([]string, 0)
|
||||
editors = make([]string, 0)
|
||||
)
|
||||
for u := range user.YieldUsers() {
|
||||
switch u.Group {
|
||||
case "admin":
|
||||
admins = append(admins, u.Name)
|
||||
case "moderator":
|
||||
moderators = append(moderators, u.Name)
|
||||
case "editor", "trusted":
|
||||
editors = append(editors, u.Name)
|
||||
}
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:52
|
||||
qw422016.N().S(`
|
||||
<section>
|
||||
<h2>Admins</h2>
|
||||
<ol>`)
|
||||
//line views/stuff.qtpl:55
|
||||
for _, name := range admins {
|
||||
//line views/stuff.qtpl:55
|
||||
qw422016.N().S(`
|
||||
<li><a href="/page/`)
|
||||
//line views/stuff.qtpl:56
|
||||
qw422016.E().S(util.UserHypha)
|
||||
//line views/stuff.qtpl:56
|
||||
qw422016.N().S(`/`)
|
||||
//line views/stuff.qtpl:56
|
||||
qw422016.E().S(name)
|
||||
//line views/stuff.qtpl:56
|
||||
qw422016.N().S(`">`)
|
||||
//line views/stuff.qtpl:56
|
||||
qw422016.E().S(name)
|
||||
//line views/stuff.qtpl:56
|
||||
qw422016.N().S(`</a></li>
|
||||
`)
|
||||
//line views/stuff.qtpl:57
|
||||
}
|
||||
//line views/stuff.qtpl:57
|
||||
qw422016.N().S(`</ol>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Moderators</h2>
|
||||
<ol>`)
|
||||
//line views/stuff.qtpl:61
|
||||
for _, name := range moderators {
|
||||
//line views/stuff.qtpl:61
|
||||
qw422016.N().S(`
|
||||
<li><a href="/page/`)
|
||||
//line views/stuff.qtpl:62
|
||||
qw422016.E().S(util.UserHypha)
|
||||
//line views/stuff.qtpl:62
|
||||
qw422016.N().S(`/`)
|
||||
//line views/stuff.qtpl:62
|
||||
qw422016.E().S(name)
|
||||
//line views/stuff.qtpl:62
|
||||
qw422016.N().S(`">`)
|
||||
//line views/stuff.qtpl:62
|
||||
qw422016.E().S(name)
|
||||
//line views/stuff.qtpl:62
|
||||
qw422016.N().S(`</a></li>
|
||||
`)
|
||||
//line views/stuff.qtpl:63
|
||||
}
|
||||
//line views/stuff.qtpl:63
|
||||
qw422016.N().S(`</ol>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Editors</h2>
|
||||
<ol>`)
|
||||
//line views/stuff.qtpl:67
|
||||
for _, name := range editors {
|
||||
//line views/stuff.qtpl:67
|
||||
qw422016.N().S(`
|
||||
<li><a href="/page/`)
|
||||
//line views/stuff.qtpl:68
|
||||
qw422016.E().S(util.UserHypha)
|
||||
//line views/stuff.qtpl:68
|
||||
qw422016.N().S(`/`)
|
||||
//line views/stuff.qtpl:68
|
||||
qw422016.E().S(name)
|
||||
//line views/stuff.qtpl:68
|
||||
qw422016.N().S(`">`)
|
||||
//line views/stuff.qtpl:68
|
||||
qw422016.E().S(name)
|
||||
//line views/stuff.qtpl:68
|
||||
qw422016.N().S(`</a></li>
|
||||
`)
|
||||
//line views/stuff.qtpl:69
|
||||
}
|
||||
//line views/stuff.qtpl:69
|
||||
qw422016.N().S(`</ol>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/stuff.qtpl:73
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:73
|
||||
func WriteUserListHTML(qq422016 qtio422016.Writer) {
|
||||
//line views/stuff.qtpl:73
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/stuff.qtpl:73
|
||||
StreamUserListHTML(qw422016)
|
||||
//line views/stuff.qtpl:73
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/stuff.qtpl:73
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:73
|
||||
func UserListHTML() string {
|
||||
//line views/stuff.qtpl:73
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/stuff.qtpl:73
|
||||
WriteUserListHTML(qb422016)
|
||||
//line views/stuff.qtpl:73
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/stuff.qtpl:73
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/stuff.qtpl:73
|
||||
return qs422016
|
||||
//line views/stuff.qtpl:73
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:75
|
||||
func StreamHyphaListHTML(qw422016 *qt422016.Writer) {
|
||||
//line views/stuff.qtpl:75
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<h1>List of hyphae</h1>
|
||||
<p>This wiki has `)
|
||||
//line views/stuff.qtpl:79
|
||||
qw422016.N().D(hyphae.Count())
|
||||
//line views/stuff.qtpl:79
|
||||
qw422016.N().S(` hyphae.</p>
|
||||
<ul class="hypha-list">
|
||||
`)
|
||||
//line views/stuff.qtpl:81
|
||||
for h := range hyphae.YieldExistingHyphae() {
|
||||
//line views/stuff.qtpl:81
|
||||
qw422016.N().S(`
|
||||
<li class="hypha-list__entry">
|
||||
<a class="hypha-list__link" href="/hypha/`)
|
||||
//line views/stuff.qtpl:83
|
||||
qw422016.E().S(h.Name)
|
||||
//line views/stuff.qtpl:83
|
||||
qw422016.N().S(`">`)
|
||||
//line views/stuff.qtpl:83
|
||||
qw422016.E().S(util.BeautifulName(h.Name))
|
||||
//line views/stuff.qtpl:83
|
||||
qw422016.N().S(`</a>
|
||||
`)
|
||||
//line views/stuff.qtpl:84
|
||||
if h.BinaryPath != "" {
|
||||
//line views/stuff.qtpl:84
|
||||
qw422016.N().S(`
|
||||
<span class="hypha-list__amnt-type">`)
|
||||
//line views/stuff.qtpl:85
|
||||
qw422016.E().S(filepath.Ext(h.BinaryPath)[1:])
|
||||
//line views/stuff.qtpl:85
|
||||
qw422016.N().S(`</span>
|
||||
`)
|
||||
//line views/stuff.qtpl:86
|
||||
}
|
||||
//line views/stuff.qtpl:86
|
||||
qw422016.N().S(`
|
||||
</li>
|
||||
`)
|
||||
//line views/stuff.qtpl:88
|
||||
}
|
||||
//line views/stuff.qtpl:88
|
||||
qw422016.N().S(`
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/stuff.qtpl:92
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:92
|
||||
func WriteHyphaListHTML(qq422016 qtio422016.Writer) {
|
||||
//line views/stuff.qtpl:92
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/stuff.qtpl:92
|
||||
StreamHyphaListHTML(qw422016)
|
||||
//line views/stuff.qtpl:92
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/stuff.qtpl:92
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:92
|
||||
func HyphaListHTML() string {
|
||||
//line views/stuff.qtpl:92
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/stuff.qtpl:92
|
||||
WriteHyphaListHTML(qb422016)
|
||||
//line views/stuff.qtpl:92
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/stuff.qtpl:92
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/stuff.qtpl:92
|
||||
return qs422016
|
||||
//line views/stuff.qtpl:92
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:94
|
||||
func StreamAboutHTML(qw422016 *qt422016.Writer) {
|
||||
//line views/stuff.qtpl:94
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<section>
|
||||
<h1>About `)
|
||||
//line views/stuff.qtpl:98
|
||||
qw422016.E().S(util.SiteName)
|
||||
//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>
|
||||
`)
|
||||
//line views/stuff.qtpl:101
|
||||
if user.AuthUsed {
|
||||
//line views/stuff.qtpl:101
|
||||
qw422016.N().S(` <li><b>User count:</b> `)
|
||||
//line views/stuff.qtpl:102
|
||||
qw422016.N().D(user.Count())
|
||||
//line views/stuff.qtpl:102
|
||||
qw422016.N().S(`</li>
|
||||
<li><b>Home page:</b> <a href="/">`)
|
||||
//line views/stuff.qtpl:103
|
||||
qw422016.E().S(util.HomePage)
|
||||
//line views/stuff.qtpl:103
|
||||
qw422016.N().S(`</a></li>
|
||||
<li><b>Administrators:</b>`)
|
||||
//line views/stuff.qtpl:104
|
||||
for i, username := range user.ListUsersWithGroup("admin") {
|
||||
//line views/stuff.qtpl:105
|
||||
if i > 0 {
|
||||
//line views/stuff.qtpl:105
|
||||
qw422016.N().S(`<span aria-hidden="true">, </span>
|
||||
`)
|
||||
//line views/stuff.qtpl:106
|
||||
}
|
||||
//line views/stuff.qtpl:106
|
||||
qw422016.N().S(` <a href="/page/`)
|
||||
//line views/stuff.qtpl:107
|
||||
qw422016.E().S(util.UserHypha)
|
||||
//line views/stuff.qtpl:107
|
||||
qw422016.N().S(`/`)
|
||||
//line views/stuff.qtpl:107
|
||||
qw422016.E().S(username)
|
||||
//line views/stuff.qtpl:107
|
||||
qw422016.N().S(`">`)
|
||||
//line views/stuff.qtpl:107
|
||||
qw422016.E().S(username)
|
||||
//line views/stuff.qtpl:107
|
||||
qw422016.N().S(`</a>`)
|
||||
//line views/stuff.qtpl:107
|
||||
}
|
||||
//line views/stuff.qtpl:107
|
||||
qw422016.N().S(`</li>
|
||||
`)
|
||||
//line views/stuff.qtpl:108
|
||||
} else {
|
||||
//line views/stuff.qtpl:108
|
||||
qw422016.N().S(` <li>This wiki does not use authorization</li>
|
||||
`)
|
||||
//line views/stuff.qtpl:110
|
||||
}
|
||||
//line views/stuff.qtpl:110
|
||||
qw422016.N().S(` </ul>
|
||||
<p>See <a href="/list">/list</a> for information about hyphae on this wiki.</p>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/stuff.qtpl:116
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:116
|
||||
func WriteAboutHTML(qq422016 qtio422016.Writer) {
|
||||
//line views/stuff.qtpl:116
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/stuff.qtpl:116
|
||||
StreamAboutHTML(qw422016)
|
||||
//line views/stuff.qtpl:116
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/stuff.qtpl:116
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:116
|
||||
func AboutHTML() string {
|
||||
//line views/stuff.qtpl:116
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/stuff.qtpl:116
|
||||
WriteAboutHTML(qb422016)
|
||||
//line views/stuff.qtpl:116
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/stuff.qtpl:116
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/stuff.qtpl:116
|
||||
return qs422016
|
||||
//line views/stuff.qtpl:116
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:118
|
||||
func StreamAdminPanelHTML(qw422016 *qt422016.Writer) {
|
||||
//line views/stuff.qtpl:118
|
||||
qw422016.N().S(`
|
||||
<div class="layout">
|
||||
<main class="main-width">
|
||||
<h1>Admininstrative functions</h1>
|
||||
<section>
|
||||
<h2>Safe things</h2>
|
||||
<ul>
|
||||
<li><a href="/about">About this wiki<a></li>
|
||||
<li><a href="/user-list">User list</a></li>
|
||||
<li><a href="/update-header-links">Update header links</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Dangerous things</h2>
|
||||
<form action="/admin/shutdown" method="POST" style="float:left">
|
||||
<fieldset>
|
||||
<legend>Shutdown wiki</legend>
|
||||
<input type="submit">
|
||||
</fieldset>
|
||||
</form>
|
||||
<form action="/reindex" method="GET" style="float:left">
|
||||
<fieldset>
|
||||
<legend>Reindex hyphae</legend>
|
||||
<input type="submit">
|
||||
</fieldset>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
`)
|
||||
//line views/stuff.qtpl:147
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:147
|
||||
func WriteAdminPanelHTML(qq422016 qtio422016.Writer) {
|
||||
//line views/stuff.qtpl:147
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line views/stuff.qtpl:147
|
||||
StreamAdminPanelHTML(qw422016)
|
||||
//line views/stuff.qtpl:147
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line views/stuff.qtpl:147
|
||||
}
|
||||
|
||||
//line views/stuff.qtpl:147
|
||||
func AdminPanelHTML() string {
|
||||
//line views/stuff.qtpl:147
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line views/stuff.qtpl:147
|
||||
WriteAdminPanelHTML(qb422016)
|
||||
//line views/stuff.qtpl:147
|
||||
qs422016 := string(qb422016.B)
|
||||
//line views/stuff.qtpl:147
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line views/stuff.qtpl:147
|
||||
return qs422016
|
||||
//line views/stuff.qtpl:147
|
||||
}
|