mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-01-31 19:39:09 +00:00
commit
f493c89ebb
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,6 +1,11 @@
|
|||||||
mycorrhiza
|
mycorrhiza
|
||||||
hyphae/*.gog
|
metarrhiza
|
||||||
|
example-wiki
|
||||||
|
|
||||||
# go editors and IDEA folders
|
# VScode and IDEA folders
|
||||||
.idea/
|
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
9
.idea/codeStyles/Project.xml
generated
Normal file
9
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<option name="OTHER_INDENT_OPTIONS">
|
||||||
|
<value>
|
||||||
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="CssUnknownTarget" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonAsciiCharacters" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="CHECK_FOR_NOT_ASCII_IDENTIFIER_NAME" value="false" />
|
||||||
|
<option name="CHECK_FOR_DIFFERENT_LANGUAGES_IN_IDENTIFIER_NAME" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/mycorrhiza.iml" filepath="$PROJECT_DIR$/.idea/mycorrhiza.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
9
.idea/mycorrhiza.iml
generated
Normal file
9
.idea/mycorrhiza.iml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
7
.idea/vcs.xml
generated
Normal file
7
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/metarrhiza" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
8
Makefile
8
Makefile
@ -1,11 +1,13 @@
|
|||||||
|
WIKI=~/src/example-wiki
|
||||||
|
|
||||||
run: build
|
run: build
|
||||||
./mycorrhiza metarrhiza
|
./mycorrhiza ${WIKI}
|
||||||
|
|
||||||
config_run: build
|
config_run: build
|
||||||
./mycorrhiza -config-path "assets/config.ini" metarrhiza
|
./mycorrhiza -config-path "assets/config.ini" ${WIKI}
|
||||||
|
|
||||||
devconfig_run: build
|
devconfig_run: build
|
||||||
./mycorrhiza -config-path "assets/devconfig.ini" metarrhiza
|
./mycorrhiza -config-path "assets/devconfig.ini" ${WIKI}
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go generate
|
go generate
|
||||||
|
12
README.md
12
README.md
@ -1,18 +1,10 @@
|
|||||||
# 🍄 MycorrhizaWiki 1.1
|
# 🍄 MycorrhizaWiki 1.2
|
||||||
A wiki engine.
|
A wiki engine.
|
||||||
|
|
||||||
[Main wiki](https://mycorrhiza.lesarbr.es)
|
[Main wiki](https://mycorrhiza.lesarbr.es)
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
Also see [detailed instructions](https://mycorrhiza.lesarbr.es/hypha/guide/deployment) on wiki.
|
See [the guide](https://mycorrhiza.lesarbr.es/hypha/guide/deployment) on the wiki.
|
||||||
```sh
|
|
||||||
git clone --recurse-submodules https://github.com/bouncepaw/mycorrhiza
|
|
||||||
cd mycorrhiza
|
|
||||||
make
|
|
||||||
# That make will:
|
|
||||||
# * run the default wiki. You can edit it right away.
|
|
||||||
# * create an executable called `mycorrhiza`. Run it with path to your wiki.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
|
@ -113,16 +113,18 @@ func StreamDefaultCSS(qw422016 *qt422016.Writer) {
|
|||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line assets/assets.qtpl:10
|
//line assets/assets.qtpl:10
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`.non-existent-hypha { }
|
||||||
|
.non-existent-hypha__ways { display: flex; flex-direction: column; width: 100%; margin: 0 0 1rem 0;}
|
||||||
|
.non-existent-hypha__way { border: 1px #999 solid; border-radius: .25rem; padding: .25rem; }
|
||||||
|
.non-existent-hypha__title { margin-bottom: 1rem; }
|
||||||
|
.non-existent-hypha__subtitle { margin: 0; }
|
||||||
|
|
||||||
.amnt-grid { display: grid; grid-template-columns: 1fr 1fr; }
|
.amnt-grid { display: grid; grid-template-columns: 1fr 1fr; }
|
||||||
.upload-binary__input { display: block; margin: .25rem 0; }
|
#upload-binary__input { display: block; margin: .25rem 0 .25rem 0; }
|
||||||
|
|
||||||
.modal__title { font-size: 2rem; }
|
.modal__title { font-size: 2rem; }
|
||||||
.modal__title_small { font-size: 1.5rem; }
|
.modal__title_small { font-size: 1.5rem; }
|
||||||
.modal__confirmation-msg { margin: 0 0 .5rem 0; }
|
.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 { padding-left: 0; }
|
||||||
.hypha-list__entry { list-style-type: none; }
|
.hypha-list__entry { list-style-type: none; }
|
||||||
@ -138,7 +140,7 @@ header { width: 100%; margin-bottom: 1rem; }
|
|||||||
.header-links__entry, .hypha-tabs__tab { list-style-type: none; }
|
.header-links__entry, .hypha-tabs__tab { list-style-type: none; }
|
||||||
|
|
||||||
.header-links__entry { margin-right: .5rem; }
|
.header-links__entry { margin-right: .5rem; }
|
||||||
.header-links__entry_user { font-style:italic; }
|
.header-links__entry_user, .header-links__entry_register { font-style:italic; }
|
||||||
.header-links__link { display: inline-block; padding: .25rem; text-decoration: none; }
|
.header-links__link { display: inline-block; padding: .25rem; text-decoration: none; }
|
||||||
|
|
||||||
.hypha-tabs { padding: 0; margin: 0; }
|
.hypha-tabs { padding: 0; margin: 0; }
|
||||||
@ -157,6 +159,12 @@ header { width: 100%; margin-bottom: 1rem; }
|
|||||||
main { padding: 1rem; margin: 0; }
|
main { padding: 1rem; margin: 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 500px) {
|
||||||
|
.non-existent-hypha__way { flex: 1; margin-right: .5rem; }
|
||||||
|
.non-existent-hypha__ways { flex-direction: row; }
|
||||||
|
.non-existent-hypha__way:last-child { margin-right: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
/* No longer a phone but still small screen: draw normal tabs, center main */
|
/* No longer a phone but still small screen: draw normal tabs, center main */
|
||||||
@media screen and (min-width: 801px) {
|
@media screen and (min-width: 801px) {
|
||||||
.main-width { padding: 1rem 2rem; width: 800px; margin: 0 auto; }
|
.main-width { padding: 1rem 2rem; width: 800px; margin: 0 auto; }
|
||||||
@ -225,15 +233,15 @@ textarea {font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;
|
|||||||
.icon {margin-right: .25rem; vertical-align: bottom; }
|
.icon {margin-right: .25rem; vertical-align: bottom; }
|
||||||
|
|
||||||
main h1:not(.navi-title) {font-size:1.7rem;}
|
main h1:not(.navi-title) {font-size:1.7rem;}
|
||||||
blockquote { margin-left: 0; padding-left: 1rem; }
|
blockquote { margin: 0; padding-left: .75rem; }
|
||||||
.wikilink_external::before { display: inline-block; width: 18px; height: 16px; vertical-align: sub; }
|
.wikilink_external::before { display: inline-block; width: 18px; height: 16px; vertical-align: sub; }
|
||||||
/* .wikilink_external { padding-left: 16px; } */
|
/* .wikilink_external { padding-left: 16px; } */
|
||||||
.wikilink_gopher::before { content: url("/static/icon/gopher"); }
|
.wikilink_gopher::before { content: url("/assets/icon/gopher"); }
|
||||||
.wikilink_http::before { content: url("/static/icon/http"); }
|
.wikilink_http::before { content: url("/assets/icon/http"); }
|
||||||
.wikilink_https::before { content: url("/static/icon/http"); }
|
.wikilink_https::before { content: url("/assets/icon/http"); }
|
||||||
/* .wikilink_https { background: transparent url("/static/icon/http") center left no-repeat; } */
|
/* .wikilink_https { background: transparent url("/assets/icon/http") center left no-repeat; } */
|
||||||
.wikilink_gemini::before { content: url("/static/icon/gemini"); }
|
.wikilink_gemini::before { content: url("/assets/icon/gemini"); }
|
||||||
.wikilink_mailto::before { content: url("/static/icon/mailto"); }
|
.wikilink_mailto::before { content: url("/assets/icon/mailto"); }
|
||||||
|
|
||||||
article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
|
article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
|
||||||
main h1, main h2, main h3, main h4, main h5, main 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; }
|
||||||
@ -242,7 +250,7 @@ main h1, main h2, main h3, main h4, main h5, main h6 { margin: 1.5rem 0 0 0; }
|
|||||||
.heading__link:hover::after, .heading__link:active::after { color: #999; }
|
.heading__link:hover::after, .heading__link:active::after { color: #999; }
|
||||||
article p { margin: .5rem 0; }
|
article p { margin: .5rem 0; }
|
||||||
article ul, ol { padding-left: 1.5rem; margin: .5rem 0; }
|
article ul, ol { padding-left: 1.5rem; margin: .5rem 0; }
|
||||||
article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; }
|
article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; font-family: 'Menlo', 'PT Mono', monospace; }
|
||||||
article pre.codeblock { padding:.5rem; white-space: pre-wrap; border-radius: .25rem;}
|
article pre.codeblock { padding:.5rem; white-space: pre-wrap; border-radius: .25rem;}
|
||||||
.codeblock code {padding:0; font-size:15px;}
|
.codeblock code {padding:0; font-size:15px;}
|
||||||
.transclusion { border-radius: .25rem; }
|
.transclusion { border-radius: .25rem; }
|
||||||
@ -253,9 +261,11 @@ article pre.codeblock { padding:.5rem; white-space: pre-wrap; border-radius: .25
|
|||||||
/* Derived from https://commons.wikimedia.org/wiki/File:U%2B21D2.svg */
|
/* Derived from https://commons.wikimedia.org/wiki/File:U%2B21D2.svg */
|
||||||
.launchpad__entry { list-style-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.0' width='25' height='12'%3E%3Cg transform='scale(0.7,0.8) translate(-613.21429,-421)'%3E%3Cpath fill='%23999' d='M 638.06773,429.49751 L 631.01022,436.87675 L 630.1898,436.02774 L 632.416,433.30375 L 613.46876,433.30375 L 613.46876,431.66382 L 633.82089,431.66382 L 635.57789,429.5261 L 633.79229,427.35979 L 613.46876,427.35979 L 613.46876,425.71985 L 632.416,425.71985 L 630.1898,422.99587 L 631.01022,422.08788 L 638.06773,429.49751 z '/%3E%3C/g%3E%3C/svg%3E"); }
|
.launchpad__entry { list-style-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.0' width='25' height='12'%3E%3Cg transform='scale(0.7,0.8) translate(-613.21429,-421)'%3E%3Cpath fill='%23999' d='M 638.06773,429.49751 L 631.01022,436.87675 L 630.1898,436.02774 L 632.416,433.30375 L 613.46876,433.30375 L 613.46876,431.66382 L 633.82089,431.66382 L 635.57789,429.5261 L 633.79229,427.35979 L 613.46876,427.35979 L 613.46876,425.71985 L 632.416,425.71985 L 630.1898,422.99587 L 631.01022,422.08788 L 638.06773,429.49751 z '/%3E%3C/g%3E%3C/svg%3E"); }
|
||||||
|
|
||||||
|
.binary-container { width: 100%; }
|
||||||
|
.binary-container > a { display: flex; justify-content: center; }
|
||||||
.binary-container_with-img img,
|
.binary-container_with-img img,
|
||||||
.binary-container_with-video video,
|
.binary-container_with-video video,
|
||||||
.binary-container_with-audio audio {width: 100%}
|
.binary-container_with-audio audio {max-height: 30em; width: auto; }
|
||||||
|
|
||||||
.subhyphae__title { padding-bottom: .5rem; clear: both; }
|
.subhyphae__title { padding-bottom: .5rem; clear: both; }
|
||||||
.navi-title { padding-bottom: .5rem; margin: .25rem 0; }
|
.navi-title { padding-bottom: .5rem; margin: .25rem 0; }
|
||||||
@ -309,9 +319,16 @@ caption { caption-side: top; font-size: small; }
|
|||||||
.relative-hyphae__entry_this { padding: .25rem .5rem; font-weight: bold; }
|
.relative-hyphae__entry_this { padding: .25rem .5rem; font-weight: bold; }
|
||||||
.relative-hyphae__link { text-decoration: none; display: block; padding: .25rem .5rem; }
|
.relative-hyphae__link { text-decoration: none; display: block; padding: .25rem .5rem; }
|
||||||
|
|
||||||
|
::-webkit-file-upload-button,
|
||||||
|
.btn { line-height: normal; display: inline-block; border: 1px #999 solid; border-radius: .25rem; text-decoration: none; padding: .25rem; font-size: 1rem; margin: 0; }
|
||||||
|
.btn_weak { border: 1px #999 dashed; }
|
||||||
|
|
||||||
/* Color stuff */
|
/* Color stuff */
|
||||||
/* Lighter stuff #eee */
|
/* Lighter stuff #eee */
|
||||||
|
::-webkit-file-upload-button, .btn { background-color: #eee; color: black; }
|
||||||
|
.btn:visited { color: black; }
|
||||||
|
.btn_weak { background-color: transparent; }
|
||||||
|
|
||||||
article code,
|
article code,
|
||||||
article .codeblock,
|
article .codeblock,
|
||||||
.transclusion,
|
.transclusion,
|
||||||
@ -336,18 +353,17 @@ table { background-color: #eee; }
|
|||||||
|
|
||||||
.layout-card { border-radius: .25rem; background-color: white; }
|
.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 { font-size: 1rem; margin: 0; padding: .25rem .5rem; border-radius: .25rem .25rem 0 0; }
|
||||||
.layout-card__title { background-color: #eee; }
|
.layout-card__title { border-bottom: 1px solid #eee; }
|
||||||
|
|
||||||
/* Other stuff */
|
/* Other stuff */
|
||||||
html { background-color: #ddd;
|
html { background-color: #eee;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='199' viewBox='0 0 100 199'%3E%3Cg fill='%23bbbbbb' %3E%3Cpath d='M0 199V0h1v1.99L100 199h-1.12L1 4.22V199H0zM100 2h-.12l-1-2H100v2z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
|
}
|
||||||
} /* heropatterns.com */
|
header { background-color: #eee; }
|
||||||
header { background-color: #bbb; }
|
|
||||||
.header-links__link { color: black; }
|
.header-links__link { color: black; }
|
||||||
.header-links__link:hover { background-color: #eee; }
|
.header-links__link:hover { background-color: #ddd; }
|
||||||
main { background-color: white; }
|
main { background-color: white; }
|
||||||
|
|
||||||
blockquote { border-left: 4px black solid; }
|
blockquote { border-left: 2px #999 solid; }
|
||||||
.wikilink_new {color:#a55858;}
|
.wikilink_new {color:#a55858;}
|
||||||
.transclusion code, .transclusion .codeblock {background-color:#ddd;}
|
.transclusion code, .transclusion .codeblock {background-color:#ddd;}
|
||||||
.transclusion__link { color: black; }
|
.transclusion__link { color: black; }
|
||||||
@ -375,9 +391,12 @@ a:visited, .wikilink_external:visited { color: #ffb86c; }
|
|||||||
.hypha-tabs__tab a, .hypha-tabs__tab { color: #ddd; background-color: #232323; border: 0; }
|
.hypha-tabs__tab a, .hypha-tabs__tab { color: #ddd; background-color: #232323; border: 0; }
|
||||||
.layout-card__title, .hypha-tabs__tab_active { background-color: #343434; }
|
.layout-card__title, .hypha-tabs__tab_active { background-color: #343434; }
|
||||||
|
|
||||||
blockquote { border-left: 4px #ddd solid; }
|
|
||||||
|
|
||||||
.transclusion .transclusion__link { color: #ddd; }
|
.transclusion .transclusion__link { color: #ddd; }
|
||||||
|
|
||||||
|
input[type="text"], input[type="password"],
|
||||||
|
::-webkit-file-upload-button,
|
||||||
|
.btn,
|
||||||
article code,
|
article code,
|
||||||
article .codeblock,
|
article .codeblock,
|
||||||
.transclusion,
|
.transclusion,
|
||||||
@ -388,6 +407,11 @@ article .codeblock,
|
|||||||
.upload-amnt,
|
.upload-amnt,
|
||||||
textarea,
|
textarea,
|
||||||
table { border: 0; background-color: #444444; color: #ddd; }
|
table { border: 0; background-color: #444444; color: #ddd; }
|
||||||
|
.btn:visited { color: #ddd;}
|
||||||
|
|
||||||
|
.btn { border: #444 solid 1px; border-radius: .25rem; }
|
||||||
|
.btn_weak { background-color: transparent; }
|
||||||
|
|
||||||
.transclusion code,
|
.transclusion code,
|
||||||
.transclusion .codeblock { background-color: #454545; }
|
.transclusion .codeblock { background-color: #454545; }
|
||||||
mark { background: rgba(130, 80, 30, 5); color: inherit; }
|
mark { background: rgba(130, 80, 30, 5); color: inherit; }
|
||||||
@ -484,7 +508,7 @@ const wrapBold = selectionWrapper(2, '**'),
|
|||||||
//line assets/assets.qtpl:14
|
//line assets/assets.qtpl:14
|
||||||
qw422016.N().S(`'),
|
qw422016.N().S(`'),
|
||||||
wrapHighlighted = selectionWrapper(2, '!!'),
|
wrapHighlighted = selectionWrapper(2, '!!'),
|
||||||
wrapLifted = selectionWrapper(1, '^'),
|
wrapLifted = selectionWrapper(2, '^^'),
|
||||||
wrapLowered = selectionWrapper(2, ',,'),
|
wrapLowered = selectionWrapper(2, ',,'),
|
||||||
wrapStrikethrough = selectionWrapper(2, '~~'),
|
wrapStrikethrough = selectionWrapper(2, '~~'),
|
||||||
wrapLink = selectionWrapper(2, '[[', ']]')
|
wrapLink = selectionWrapper(2, '[[', ']]')
|
||||||
@ -529,6 +553,11 @@ function insertDate() {
|
|||||||
textInserter(date)()
|
textInserter(date)()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function insertTimeUTC() {
|
||||||
|
let time = new Date().toISOString().substring(11, 19) + " UTC"
|
||||||
|
textInserter(time)()
|
||||||
|
}
|
||||||
|
|
||||||
function insertUserlink() {
|
function insertUserlink() {
|
||||||
const userlink = document.querySelector('.header-links__entry_user a')
|
const userlink = document.querySelector('.header-links__entry_user a')
|
||||||
const userHypha = userlink.getAttribute('href').substring(7) // no /hypha/
|
const userHypha = userlink.getAttribute('href').substring(7) // no /hypha/
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
|
.non-existent-hypha { }
|
||||||
|
.non-existent-hypha__ways { display: flex; flex-direction: column; width: 100%; margin: 0 0 1rem 0;}
|
||||||
|
.non-existent-hypha__way { border: 1px #999 solid; border-radius: .25rem; padding: .25rem; }
|
||||||
|
.non-existent-hypha__title { margin-bottom: 1rem; }
|
||||||
|
.non-existent-hypha__subtitle { margin: 0; }
|
||||||
|
|
||||||
.amnt-grid { display: grid; grid-template-columns: 1fr 1fr; }
|
.amnt-grid { display: grid; grid-template-columns: 1fr 1fr; }
|
||||||
.upload-binary__input { display: block; margin: .25rem 0; }
|
#upload-binary__input { display: block; margin: .25rem 0 .25rem 0; }
|
||||||
|
|
||||||
.modal__title { font-size: 2rem; }
|
.modal__title { font-size: 2rem; }
|
||||||
.modal__title_small { font-size: 1.5rem; }
|
.modal__title_small { font-size: 1.5rem; }
|
||||||
.modal__confirmation-msg { margin: 0 0 .5rem 0; }
|
.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 { padding-left: 0; }
|
||||||
.hypha-list__entry { list-style-type: none; }
|
.hypha-list__entry { list-style-type: none; }
|
||||||
@ -23,7 +25,7 @@ header { width: 100%; margin-bottom: 1rem; }
|
|||||||
.header-links__entry, .hypha-tabs__tab { list-style-type: none; }
|
.header-links__entry, .hypha-tabs__tab { list-style-type: none; }
|
||||||
|
|
||||||
.header-links__entry { margin-right: .5rem; }
|
.header-links__entry { margin-right: .5rem; }
|
||||||
.header-links__entry_user { font-style:italic; }
|
.header-links__entry_user, .header-links__entry_register { font-style:italic; }
|
||||||
.header-links__link { display: inline-block; padding: .25rem; text-decoration: none; }
|
.header-links__link { display: inline-block; padding: .25rem; text-decoration: none; }
|
||||||
|
|
||||||
.hypha-tabs { padding: 0; margin: 0; }
|
.hypha-tabs { padding: 0; margin: 0; }
|
||||||
@ -42,6 +44,12 @@ header { width: 100%; margin-bottom: 1rem; }
|
|||||||
main { padding: 1rem; margin: 0; }
|
main { padding: 1rem; margin: 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 500px) {
|
||||||
|
.non-existent-hypha__way { flex: 1; margin-right: .5rem; }
|
||||||
|
.non-existent-hypha__ways { flex-direction: row; }
|
||||||
|
.non-existent-hypha__way:last-child { margin-right: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
/* No longer a phone but still small screen: draw normal tabs, center main */
|
/* No longer a phone but still small screen: draw normal tabs, center main */
|
||||||
@media screen and (min-width: 801px) {
|
@media screen and (min-width: 801px) {
|
||||||
.main-width { padding: 1rem 2rem; width: 800px; margin: 0 auto; }
|
.main-width { padding: 1rem 2rem; width: 800px; margin: 0 auto; }
|
||||||
@ -110,15 +118,15 @@ textarea {font-size:16px; font-family: 'PT Sans', 'Liberation Sans', sans-serif;
|
|||||||
.icon {margin-right: .25rem; vertical-align: bottom; }
|
.icon {margin-right: .25rem; vertical-align: bottom; }
|
||||||
|
|
||||||
main h1:not(.navi-title) {font-size:1.7rem;}
|
main h1:not(.navi-title) {font-size:1.7rem;}
|
||||||
blockquote { margin-left: 0; padding-left: 1rem; }
|
blockquote { margin: 0; padding-left: .75rem; }
|
||||||
.wikilink_external::before { display: inline-block; width: 18px; height: 16px; vertical-align: sub; }
|
.wikilink_external::before { display: inline-block; width: 18px; height: 16px; vertical-align: sub; }
|
||||||
/* .wikilink_external { padding-left: 16px; } */
|
/* .wikilink_external { padding-left: 16px; } */
|
||||||
.wikilink_gopher::before { content: url("/static/icon/gopher"); }
|
.wikilink_gopher::before { content: url("/assets/icon/gopher"); }
|
||||||
.wikilink_http::before { content: url("/static/icon/http"); }
|
.wikilink_http::before { content: url("/assets/icon/http"); }
|
||||||
.wikilink_https::before { content: url("/static/icon/http"); }
|
.wikilink_https::before { content: url("/assets/icon/http"); }
|
||||||
/* .wikilink_https { background: transparent url("/static/icon/http") center left no-repeat; } */
|
/* .wikilink_https { background: transparent url("/assets/icon/http") center left no-repeat; } */
|
||||||
.wikilink_gemini::before { content: url("/static/icon/gemini"); }
|
.wikilink_gemini::before { content: url("/assets/icon/gemini"); }
|
||||||
.wikilink_mailto::before { content: url("/static/icon/mailto"); }
|
.wikilink_mailto::before { content: url("/assets/icon/mailto"); }
|
||||||
|
|
||||||
article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
|
article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
|
||||||
main h1, main h2, main h3, main h4, main h5, main 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; }
|
||||||
@ -127,7 +135,7 @@ main h1, main h2, main h3, main h4, main h5, main h6 { margin: 1.5rem 0 0 0; }
|
|||||||
.heading__link:hover::after, .heading__link:active::after { color: #999; }
|
.heading__link:hover::after, .heading__link:active::after { color: #999; }
|
||||||
article p { margin: .5rem 0; }
|
article p { margin: .5rem 0; }
|
||||||
article ul, ol { padding-left: 1.5rem; margin: .5rem 0; }
|
article ul, ol { padding-left: 1.5rem; margin: .5rem 0; }
|
||||||
article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; }
|
article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; font-family: 'Menlo', 'PT Mono', monospace; }
|
||||||
article pre.codeblock { padding:.5rem; white-space: pre-wrap; border-radius: .25rem;}
|
article pre.codeblock { padding:.5rem; white-space: pre-wrap; border-radius: .25rem;}
|
||||||
.codeblock code {padding:0; font-size:15px;}
|
.codeblock code {padding:0; font-size:15px;}
|
||||||
.transclusion { border-radius: .25rem; }
|
.transclusion { border-radius: .25rem; }
|
||||||
@ -138,9 +146,11 @@ article pre.codeblock { padding:.5rem; white-space: pre-wrap; border-radius: .25
|
|||||||
/* Derived from https://commons.wikimedia.org/wiki/File:U%2B21D2.svg */
|
/* Derived from https://commons.wikimedia.org/wiki/File:U%2B21D2.svg */
|
||||||
.launchpad__entry { list-style-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.0' width='25' height='12'%3E%3Cg transform='scale(0.7,0.8) translate(-613.21429,-421)'%3E%3Cpath fill='%23999' d='M 638.06773,429.49751 L 631.01022,436.87675 L 630.1898,436.02774 L 632.416,433.30375 L 613.46876,433.30375 L 613.46876,431.66382 L 633.82089,431.66382 L 635.57789,429.5261 L 633.79229,427.35979 L 613.46876,427.35979 L 613.46876,425.71985 L 632.416,425.71985 L 630.1898,422.99587 L 631.01022,422.08788 L 638.06773,429.49751 z '/%3E%3C/g%3E%3C/svg%3E"); }
|
.launchpad__entry { list-style-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.0' width='25' height='12'%3E%3Cg transform='scale(0.7,0.8) translate(-613.21429,-421)'%3E%3Cpath fill='%23999' d='M 638.06773,429.49751 L 631.01022,436.87675 L 630.1898,436.02774 L 632.416,433.30375 L 613.46876,433.30375 L 613.46876,431.66382 L 633.82089,431.66382 L 635.57789,429.5261 L 633.79229,427.35979 L 613.46876,427.35979 L 613.46876,425.71985 L 632.416,425.71985 L 630.1898,422.99587 L 631.01022,422.08788 L 638.06773,429.49751 z '/%3E%3C/g%3E%3C/svg%3E"); }
|
||||||
|
|
||||||
|
.binary-container { width: 100%; }
|
||||||
|
.binary-container > a { display: flex; justify-content: center; }
|
||||||
.binary-container_with-img img,
|
.binary-container_with-img img,
|
||||||
.binary-container_with-video video,
|
.binary-container_with-video video,
|
||||||
.binary-container_with-audio audio {width: 100%}
|
.binary-container_with-audio audio {max-height: 30em; width: auto; }
|
||||||
|
|
||||||
.subhyphae__title { padding-bottom: .5rem; clear: both; }
|
.subhyphae__title { padding-bottom: .5rem; clear: both; }
|
||||||
.navi-title { padding-bottom: .5rem; margin: .25rem 0; }
|
.navi-title { padding-bottom: .5rem; margin: .25rem 0; }
|
||||||
@ -166,7 +176,7 @@ figcaption { padding-bottom: .5rem; }
|
|||||||
.rc-entry__links, .rc-entry__msg { grid-column: 1 / span 2; }
|
.rc-entry__links, .rc-entry__msg { grid-column: 1 / span 2; }
|
||||||
.rc-entry__author { font-style: italic; }
|
.rc-entry__author { font-style: italic; }
|
||||||
|
|
||||||
.prevnext__el { display: inline-block; min-width: 40%; padding: .5rem; margin-bottom: .25rem; text-decoration: none; border-radius: .25rem; }
|
.prevnext__el { display: inline-block; min-width: 40%; padding: .5rem; margin-bottom: .25rem; text-decoration: none; border-radius: .25rem; max-width: 49%; }
|
||||||
.prevnext__prev { float: left; }
|
.prevnext__prev { float: left; }
|
||||||
.prevnext__next { float: right; text-align: right; }
|
.prevnext__next { float: right; text-align: right; }
|
||||||
|
|
||||||
@ -194,9 +204,16 @@ caption { caption-side: top; font-size: small; }
|
|||||||
.relative-hyphae__entry_this { padding: .25rem .5rem; font-weight: bold; }
|
.relative-hyphae__entry_this { padding: .25rem .5rem; font-weight: bold; }
|
||||||
.relative-hyphae__link { text-decoration: none; display: block; padding: .25rem .5rem; }
|
.relative-hyphae__link { text-decoration: none; display: block; padding: .25rem .5rem; }
|
||||||
|
|
||||||
|
::-webkit-file-upload-button,
|
||||||
|
.btn { line-height: normal; display: inline-block; border: 1px #999 solid; border-radius: .25rem; text-decoration: none; padding: .25rem; font-size: 1rem; margin: 0; }
|
||||||
|
.btn_weak { border: 1px #999 dashed; }
|
||||||
|
|
||||||
/* Color stuff */
|
/* Color stuff */
|
||||||
/* Lighter stuff #eee */
|
/* Lighter stuff #eee */
|
||||||
|
::-webkit-file-upload-button, .btn { background-color: #eee; color: black; }
|
||||||
|
.btn:visited { color: black; }
|
||||||
|
.btn_weak { background-color: transparent; }
|
||||||
|
|
||||||
article code,
|
article code,
|
||||||
article .codeblock,
|
article .codeblock,
|
||||||
.transclusion,
|
.transclusion,
|
||||||
@ -221,18 +238,17 @@ table { background-color: #eee; }
|
|||||||
|
|
||||||
.layout-card { border-radius: .25rem; background-color: white; }
|
.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 { font-size: 1rem; margin: 0; padding: .25rem .5rem; border-radius: .25rem .25rem 0 0; }
|
||||||
.layout-card__title { background-color: #eee; }
|
.layout-card__title { border-bottom: 1px solid #eee; }
|
||||||
|
|
||||||
/* Other stuff */
|
/* Other stuff */
|
||||||
html { background-color: #ddd;
|
html { background-color: #eee;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='199' viewBox='0 0 100 199'%3E%3Cg fill='%23bbbbbb' %3E%3Cpath d='M0 199V0h1v1.99L100 199h-1.12L1 4.22V199H0zM100 2h-.12l-1-2H100v2z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
|
}
|
||||||
} /* heropatterns.com */
|
header { background-color: #eee; }
|
||||||
header { background-color: #bbb; }
|
|
||||||
.header-links__link { color: black; }
|
.header-links__link { color: black; }
|
||||||
.header-links__link:hover { background-color: #eee; }
|
.header-links__link:hover { background-color: #ddd; }
|
||||||
main { background-color: white; }
|
main { background-color: white; }
|
||||||
|
|
||||||
blockquote { border-left: 4px black solid; }
|
blockquote { border-left: 2px #999 solid; }
|
||||||
.wikilink_new {color:#a55858;}
|
.wikilink_new {color:#a55858;}
|
||||||
.transclusion code, .transclusion .codeblock {background-color:#ddd;}
|
.transclusion code, .transclusion .codeblock {background-color:#ddd;}
|
||||||
.transclusion__link { color: black; }
|
.transclusion__link { color: black; }
|
||||||
@ -260,9 +276,12 @@ a:visited, .wikilink_external:visited { color: #ffb86c; }
|
|||||||
.hypha-tabs__tab a, .hypha-tabs__tab { color: #ddd; background-color: #232323; border: 0; }
|
.hypha-tabs__tab a, .hypha-tabs__tab { color: #ddd; background-color: #232323; border: 0; }
|
||||||
.layout-card__title, .hypha-tabs__tab_active { background-color: #343434; }
|
.layout-card__title, .hypha-tabs__tab_active { background-color: #343434; }
|
||||||
|
|
||||||
blockquote { border-left: 4px #ddd solid; }
|
|
||||||
|
|
||||||
.transclusion .transclusion__link { color: #ddd; }
|
.transclusion .transclusion__link { color: #ddd; }
|
||||||
|
|
||||||
|
input[type="text"], input[type="password"],
|
||||||
|
::-webkit-file-upload-button,
|
||||||
|
.btn,
|
||||||
article code,
|
article code,
|
||||||
article .codeblock,
|
article .codeblock,
|
||||||
.transclusion,
|
.transclusion,
|
||||||
@ -273,6 +292,11 @@ article .codeblock,
|
|||||||
.upload-amnt,
|
.upload-amnt,
|
||||||
textarea,
|
textarea,
|
||||||
table { border: 0; background-color: #444444; color: #ddd; }
|
table { border: 0; background-color: #444444; color: #ddd; }
|
||||||
|
.btn:visited { color: #ddd;}
|
||||||
|
|
||||||
|
.btn { border: #444 solid 1px; border-radius: .25rem; }
|
||||||
|
.btn_weak { background-color: transparent; }
|
||||||
|
|
||||||
.transclusion code,
|
.transclusion code,
|
||||||
.transclusion .codeblock { background-color: #454545; }
|
.transclusion .codeblock { background-color: #454545; }
|
||||||
mark { background: rgba(130, 80, 30, 5); color: inherit; }
|
mark { background: rgba(130, 80, 30, 5); color: inherit; }
|
||||||
|
@ -2,7 +2,7 @@ WikiName = Mycorrhiza (dev)
|
|||||||
NaviTitleIcon = 🧑💻
|
NaviTitleIcon = 🧑💻
|
||||||
|
|
||||||
[Hyphae]
|
[Hyphae]
|
||||||
HomeHypha = home
|
HomeHypha = mycorrhiza_wiki
|
||||||
UserHypha = u
|
UserHypha = u
|
||||||
HeaderLinksHypha = header-links
|
HeaderLinksHypha = header-links
|
||||||
|
|
||||||
@ -17,3 +17,8 @@ FixedAuthCredentialsPath = mycocredentials.json
|
|||||||
UseRegistration = true
|
UseRegistration = true
|
||||||
RegistrationCredentialsPath = mycoregistration.json
|
RegistrationCredentialsPath = mycoregistration.json
|
||||||
LimitRegistration = 3
|
LimitRegistration = 3
|
||||||
|
|
||||||
|
[CustomScripts]
|
||||||
|
OmnipresentScripts = https://lesarbr.es/do-the-roll.js
|
||||||
|
ViewScripts = https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/components/prism-core.min.js,https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/plugins/autoloader/prism-autoloader.min.js
|
||||||
|
EditScripts = https://example.org
|
@ -42,7 +42,7 @@ const wrapBold = selectionWrapper(2, '**'),
|
|||||||
wrapItalic = selectionWrapper(2, '//'),
|
wrapItalic = selectionWrapper(2, '//'),
|
||||||
wrapMonospace = selectionWrapper(1, '`'),
|
wrapMonospace = selectionWrapper(1, '`'),
|
||||||
wrapHighlighted = selectionWrapper(2, '!!'),
|
wrapHighlighted = selectionWrapper(2, '!!'),
|
||||||
wrapLifted = selectionWrapper(1, '^'),
|
wrapLifted = selectionWrapper(2, '^^'),
|
||||||
wrapLowered = selectionWrapper(2, ',,'),
|
wrapLowered = selectionWrapper(2, ',,'),
|
||||||
wrapStrikethrough = selectionWrapper(2, '~~'),
|
wrapStrikethrough = selectionWrapper(2, '~~'),
|
||||||
wrapLink = selectionWrapper(2, '[[', ']]')
|
wrapLink = selectionWrapper(2, '[[', ']]')
|
||||||
@ -63,6 +63,11 @@ function insertDate() {
|
|||||||
textInserter(date)()
|
textInserter(date)()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function insertTimeUTC() {
|
||||||
|
let time = new Date().toISOString().substring(11, 19) + " UTC"
|
||||||
|
textInserter(time)()
|
||||||
|
}
|
||||||
|
|
||||||
function insertUserlink() {
|
function insertUserlink() {
|
||||||
const userlink = document.querySelector('.header-links__entry_user a')
|
const userlink = document.querySelector('.header-links__entry_user a')
|
||||||
const userHypha = userlink.getAttribute('href').substring(7) // no /hypha/
|
const userHypha = userlink.getAttribute('href').substring(7) // no /hypha/
|
||||||
|
157
cfg/config.go
Normal file
157
cfg/config.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
// Package cfg contains global variables that represent the current wiki configuration, including CLI options, configuration file values and header links.
|
||||||
|
package cfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-ini/ini"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These variables represent the configuration. You are not meant to modify them after they were set.
|
||||||
|
//
|
||||||
|
// See https://mycorrhiza.lesarbr.es/hypha/configuration/fields for their docs.
|
||||||
|
var (
|
||||||
|
WikiName string
|
||||||
|
NaviTitleIcon string
|
||||||
|
|
||||||
|
HomeHypha string
|
||||||
|
UserHypha string
|
||||||
|
HeaderLinksHypha string
|
||||||
|
|
||||||
|
HTTPPort string
|
||||||
|
URL string
|
||||||
|
GeminiCertificatePath string
|
||||||
|
|
||||||
|
UseFixedAuth bool
|
||||||
|
FixedAuthCredentialsPath string
|
||||||
|
UseRegistration bool
|
||||||
|
RegistrationCredentialsPath string
|
||||||
|
LimitRegistration int
|
||||||
|
|
||||||
|
OmnipresentScripts []string
|
||||||
|
ViewScripts []string
|
||||||
|
EditScripts []string
|
||||||
|
)
|
||||||
|
|
||||||
|
// These variables are set before reading the config file, they are set in main.parseCliArgs.
|
||||||
|
var (
|
||||||
|
// WikiDir is a full path to the wiki storage directory, which also must be a git repo.
|
||||||
|
WikiDir string
|
||||||
|
// ConfigFilePath is a path to the config file. Its value is used when calling ReadConfigFile.
|
||||||
|
ConfigFilePath string
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config represents a Mycorrhiza wiki configuration file. This type is used only when reading configs.
|
||||||
|
type Config struct {
|
||||||
|
WikiName string
|
||||||
|
NaviTitleIcon string
|
||||||
|
Hyphae
|
||||||
|
Network
|
||||||
|
Authorization
|
||||||
|
CustomScripts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hyphae is a section of Config which has fields related to special hyphae.
|
||||||
|
type Hyphae struct {
|
||||||
|
HomeHypha string
|
||||||
|
UserHypha string
|
||||||
|
HeaderLinksHypha string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network is a section of Config that has fields related to network stuff: HTTP and Gemini.
|
||||||
|
type Network struct {
|
||||||
|
HTTPPort uint64
|
||||||
|
URL string
|
||||||
|
GeminiCertificatePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomScripts is a section with paths to JavaScript files that are loaded on specified pages.
|
||||||
|
type CustomScripts struct {
|
||||||
|
// OmnipresentScripts: everywhere...
|
||||||
|
OmnipresentScripts []string `delim:","`
|
||||||
|
// ViewScripts: /hypha, /rev
|
||||||
|
ViewScripts []string `delim:","`
|
||||||
|
// Edit: /edit
|
||||||
|
EditScripts []string `delim:","`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorization is a section of Config that has fields related to authorization and authentication.
|
||||||
|
type Authorization struct {
|
||||||
|
UseFixedAuth bool
|
||||||
|
FixedAuthCredentialsPath string
|
||||||
|
|
||||||
|
UseRegistration bool
|
||||||
|
RegistrationCredentialsPath string
|
||||||
|
LimitRegistration uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadConfigFile reads a config on the given path and stores the configuration. Call it sometime during the initialization.
|
||||||
|
//
|
||||||
|
// Note that it may log.Fatal.
|
||||||
|
func ReadConfigFile() {
|
||||||
|
cfg := &Config{
|
||||||
|
WikiName: "MycorrhizaWiki",
|
||||||
|
NaviTitleIcon: "🍄",
|
||||||
|
Hyphae: Hyphae{
|
||||||
|
HomeHypha: "home",
|
||||||
|
UserHypha: "u",
|
||||||
|
HeaderLinksHypha: "",
|
||||||
|
},
|
||||||
|
Network: Network{
|
||||||
|
HTTPPort: 1737,
|
||||||
|
URL: "",
|
||||||
|
GeminiCertificatePath: "",
|
||||||
|
},
|
||||||
|
Authorization: Authorization{
|
||||||
|
UseFixedAuth: false,
|
||||||
|
FixedAuthCredentialsPath: "",
|
||||||
|
|
||||||
|
UseRegistration: false,
|
||||||
|
RegistrationCredentialsPath: "",
|
||||||
|
LimitRegistration: 0,
|
||||||
|
},
|
||||||
|
CustomScripts: CustomScripts{
|
||||||
|
OmnipresentScripts: []string{},
|
||||||
|
ViewScripts: []string{},
|
||||||
|
EditScripts: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if ConfigFilePath != "" {
|
||||||
|
path, err := filepath.Abs(ConfigFilePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("cannot expand config file path: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Loading config at", path)
|
||||||
|
err = ini.MapTo(cfg, path)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the struct to the global variables
|
||||||
|
WikiName = cfg.WikiName
|
||||||
|
NaviTitleIcon = cfg.NaviTitleIcon
|
||||||
|
HomeHypha = cfg.HomeHypha
|
||||||
|
UserHypha = cfg.UserHypha
|
||||||
|
HeaderLinksHypha = cfg.HeaderLinksHypha
|
||||||
|
HTTPPort = strconv.FormatUint(cfg.HTTPPort, 10)
|
||||||
|
URL = cfg.URL
|
||||||
|
GeminiCertificatePath = cfg.GeminiCertificatePath
|
||||||
|
UseFixedAuth = cfg.UseFixedAuth
|
||||||
|
FixedAuthCredentialsPath = cfg.FixedAuthCredentialsPath
|
||||||
|
UseRegistration = cfg.UseRegistration
|
||||||
|
RegistrationCredentialsPath = cfg.RegistrationCredentialsPath
|
||||||
|
LimitRegistration = int(cfg.LimitRegistration)
|
||||||
|
OmnipresentScripts = cfg.OmnipresentScripts
|
||||||
|
ViewScripts = cfg.ViewScripts
|
||||||
|
EditScripts = cfg.EditScripts
|
||||||
|
|
||||||
|
// This URL makes much more sense.
|
||||||
|
if URL == "" {
|
||||||
|
URL = "http://0.0.0.0:" + HTTPPort
|
||||||
|
}
|
||||||
|
}
|
50
cfg/header_links.go
Normal file
50
cfg/header_links.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package cfg
|
||||||
|
|
||||||
|
// See https://mycorrhiza.lesarbr.es/hypha/configuration/header
|
||||||
|
import (
|
||||||
|
"github.com/bouncepaw/mycomarkup/blocks"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HeaderLinks is a list off current header links. Feel free to iterate it directly but do not modify it by yourself. Call ParseHeaderLinks if you need to set new header links.
|
||||||
|
var HeaderLinks []HeaderLink
|
||||||
|
|
||||||
|
// SetDefaultHeaderLinks sets the header links to the default list of: home hypha, recent changes, hyphae list, random hypha.
|
||||||
|
func SetDefaultHeaderLinks() {
|
||||||
|
HeaderLinks = []HeaderLink{
|
||||||
|
{"/", WikiName},
|
||||||
|
{"/recent-changes", "Recent changes"},
|
||||||
|
{"/list", "All hyphae"},
|
||||||
|
{"/random", "Random"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseHeaderLinks extracts all rocketlinks from the given text and saves them as header links.
|
||||||
|
func ParseHeaderLinks(text string) {
|
||||||
|
HeaderLinks = []HeaderLink{}
|
||||||
|
for _, line := range strings.Split(text, "\n") {
|
||||||
|
// There is a false positive when parsing markup like that:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// => this is not a link, it is part of the preformatted block
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// I do not really care.
|
||||||
|
if strings.HasPrefix(line, "=>") {
|
||||||
|
rl := blocks.MakeRocketLink(line, HeaderLinksHypha)
|
||||||
|
href, display := rl.Href(), rl.Display()
|
||||||
|
HeaderLinks = append(HeaderLinks, HeaderLink{
|
||||||
|
Href: href,
|
||||||
|
Display: display,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderLink represents a header link. Header links are the links shown in the top gray bar.
|
||||||
|
type HeaderLink struct {
|
||||||
|
// Href is the URL of the link. It goes <a href="here">...</a>.
|
||||||
|
Href string
|
||||||
|
// Display is what is shown when the link is rendered. It goes <a href="...">here</a>.
|
||||||
|
Display string
|
||||||
|
}
|
31
files/Structure.md
Normal file
31
files/Structure.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# The Structure
|
||||||
|
Here I am, doing new stuff before finishing the old stuff.
|
||||||
|
|
||||||
|
See https://github.com/bouncepaw/mycorrhiza/issues/57 for the discussion.
|
||||||
|
|
||||||
|
## The idea
|
||||||
|
Instead of letting users figure everything out by themselves, we think of the best (in our opinion) file layout and force it onto the users and remove the possibility of configuring it.
|
||||||
|
|
||||||
|
## What is inside the Structure
|
||||||
|
### Root
|
||||||
|
The whole wiki is inside one directory or inside one of its subdirectories. Only the Mycorrhiza binary itself and Git (and possible future runtime dependencies) might be outside that directory. That directory (called _root directory_) can have any name.
|
||||||
|
|
||||||
|
### Subdirectories
|
||||||
|
* `wiki.git` is a valid Git repository. If it is not present or is not a valid Git repository, the engine shall fail to work. When the Wizard is implemented, the engine will offer to make the Git repository.
|
||||||
|
* `cache` contains temporary files such as user token caches. Wiki administrators can safely delete this directory and expect the wiki to continue working. In the future, stuff like pre-rendered HTML can be stored here.
|
||||||
|
* All other subdirectories are ignored.
|
||||||
|
|
||||||
|
### User configuration
|
||||||
|
* `registered-users.json` contains a JSON array of all registered users. The engine will edit this file, and the administrators should not edit by themselves, unless they really want to.
|
||||||
|
* `fixed-users.json` contains a JSON array of all fixed users. Wiki administrators will edit this file by themselves.
|
||||||
|
|
||||||
|
### Wiki configuration
|
||||||
|
* `config.ini` is the main configuration file.
|
||||||
|
|
||||||
|
### Customisation
|
||||||
|
* `favicon.ico` is the Favicon as you know it.
|
||||||
|
* `common.css` redefines the built-in CSS, the Common style.
|
||||||
|
* `custom.css` is sent to the user after the Common style.
|
||||||
|
|
||||||
|
### Meta
|
||||||
|
* `README.txt` contains a short description of the files that can be inside the Structure. A small reminder for the administrators.
|
@ -1,13 +1,14 @@
|
|||||||
|
// Package files is used to get paths to different files Mycorrhiza uses. Also see cfg.
|
||||||
package files
|
package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/adrg/xdg"
|
"github.com/adrg/xdg"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
"github.com/mitchellh/go-homedir"
|
"github.com/mitchellh/go-homedir"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,9 +18,20 @@ var paths struct {
|
|||||||
fixedCredentialsJSON string
|
fixedCredentialsJSON string
|
||||||
}
|
}
|
||||||
|
|
||||||
func TokensJSON() string { return paths.tokensJSON }
|
// TokensJSON returns a path to the JSON file where users' tokens are stored.
|
||||||
|
//
|
||||||
|
// Default path: $XDG_DATA_HOME/mycorrhiza/tokens.json
|
||||||
|
func TokensJSON() string { return paths.tokensJSON }
|
||||||
|
|
||||||
|
// RegistrationCredentialsJSON returns a path to the JSON file where registration credentials are stored.
|
||||||
|
//
|
||||||
|
// Default path: $XDG_DATA_HOME/mycorrhiza/registration.json
|
||||||
func RegistrationCredentialsJSON() string { return paths.registrationCredentialsJSON }
|
func RegistrationCredentialsJSON() string { return paths.registrationCredentialsJSON }
|
||||||
func FixedCredentialsJSON() string { return paths.fixedCredentialsJSON }
|
|
||||||
|
// FixedCredentialsJSON returns a path to the JSON file where fixed credentials are stored.
|
||||||
|
//
|
||||||
|
// There is no default path.
|
||||||
|
func FixedCredentialsJSON() string { return paths.fixedCredentialsJSON }
|
||||||
|
|
||||||
// CalculatePaths looks for all external paths and stores them. Tries its best to find any errors. It is safe it to call it multiple times in order to save new paths.
|
// CalculatePaths looks for all external paths and stores them. Tries its best to find any errors. It is safe it to call it multiple times in order to save new paths.
|
||||||
func CalculatePaths() error {
|
func CalculatePaths() error {
|
||||||
@ -49,7 +61,7 @@ func tokenStoragePath() (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(dir, util.WikiDir) {
|
if strings.HasPrefix(dir, cfg.WikiDir) {
|
||||||
return "", errors.New("wiki storage directory includes private config files")
|
return "", errors.New("wiki storage directory includes private config files")
|
||||||
}
|
}
|
||||||
return dir, nil
|
return dir, nil
|
||||||
@ -57,7 +69,7 @@ func tokenStoragePath() (string, error) {
|
|||||||
|
|
||||||
func registrationCredentialsPath() (string, error) {
|
func registrationCredentialsPath() (string, error) {
|
||||||
var err error
|
var err error
|
||||||
path := util.RegistrationCredentialsPath
|
path := cfg.RegistrationCredentialsPath
|
||||||
|
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
path, err = xdg.DataFile("mycorrhiza/registration.json")
|
path, err = xdg.DataFile("mycorrhiza/registration.json")
|
||||||
@ -81,7 +93,7 @@ func registrationCredentialsPath() (string, error) {
|
|||||||
|
|
||||||
func fixedCredentialsPath() (string, error) {
|
func fixedCredentialsPath() (string, error) {
|
||||||
var err error
|
var err error
|
||||||
path := util.FixedCredentialsPath
|
path := cfg.FixedAuthCredentialsPath
|
||||||
|
|
||||||
if len(path) > 0 {
|
if len(path) > 0 {
|
||||||
path, err = homedir.Expand(path)
|
path, err = homedir.Expand(path)
|
||||||
|
43
flag.go
43
flag.go
@ -3,30 +3,38 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/assets"
|
"github.com/bouncepaw/mycorrhiza/assets"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CLI options are read and parsed here.
|
||||||
|
|
||||||
var printExampleConfig bool
|
var printExampleConfig bool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.StringVar(&util.ConfigFilePath, "config-path", "", "Path to a configuration file. Leave empty if you don't want to use it.")
|
flag.StringVar(&cfg.ConfigFilePath, "config-path", "", "Path to a configuration file. Leave empty if you don't want to use it.")
|
||||||
flag.BoolVar(&printExampleConfig, "print-example-config", false, "If true, print an example configuration file contents and exit. You can save the output to a file and base your own configuration on it.")
|
flag.BoolVar(&printExampleConfig, "print-example-config", false, "If true, print an example configuration file contents and exit. You can save the output to a file and base your own configuration on it.")
|
||||||
flag.Usage = func() {
|
flag.Usage = printHelp
|
||||||
fmt.Fprintf(
|
|
||||||
flag.CommandLine.Output(),
|
|
||||||
assets.HelpMessage(),
|
|
||||||
os.Args[0],
|
|
||||||
)
|
|
||||||
flag.PrintDefaults()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the things related to cli args and die maybe
|
// printHelp prints the help message. The help message is stored in assets.
|
||||||
|
func printHelp() {
|
||||||
|
_, err := fmt.Fprintf(
|
||||||
|
flag.CommandLine.Output(),
|
||||||
|
assets.HelpMessage(),
|
||||||
|
os.Args[0],
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
flag.PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCliArgs parses CLI options and sets several important global variables. Call it early.
|
||||||
func parseCliArgs() {
|
func parseCliArgs() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -40,18 +48,9 @@ func parseCliArgs() {
|
|||||||
log.Fatal("Error: pass a wiki directory")
|
log.Fatal("Error: pass a wiki directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
wikiDir, err := filepath.Abs(args[0])
|
||||||
WikiDir, err = filepath.Abs(args[0])
|
cfg.WikiDir = wikiDir
|
||||||
util.WikiDir = WikiDir
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if util.URL == "" {
|
|
||||||
util.URL = "http://0.0.0.0:" + util.ServerPort
|
|
||||||
}
|
|
||||||
|
|
||||||
util.HomePage = util.CanonicalName(util.HomePage)
|
|
||||||
util.UserHypha = util.CanonicalName(util.UserHypha)
|
|
||||||
util.HeaderLinksHypha = util.CanonicalName(util.HeaderLinksHypha)
|
|
||||||
}
|
}
|
||||||
|
36
gemini.go
36
gemini.go
@ -1,29 +1,36 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
// Gemini-related stuff. This is currently a proof-of-concept implementation, no one really uses it.
|
||||||
|
// Maybe we should deprecate it until we find power to do it properly?
|
||||||
|
//
|
||||||
|
// When this stuff gets more serious, a separate module will be needed.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~adnano/go-gemini"
|
"git.sr.ht/~adnano/go-gemini"
|
||||||
"git.sr.ht/~adnano/go-gemini/certificate"
|
"git.sr.ht/~adnano/go-gemini/certificate"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/markup"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func geminiHomeHypha(w *gemini.ResponseWriter, rq *gemini.Request) {
|
func geminiHomeHypha(w *gemini.ResponseWriter, rq *gemini.Request) {
|
||||||
log.Println(rq.URL)
|
log.Println(rq.URL)
|
||||||
w.Write([]byte(`# MycorrhizaWiki
|
_, _ = io.WriteString(w, `# 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.
|
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:
|
Visit home hypha:
|
||||||
=> /hypha/` + util.HomePage))
|
=> /hypha/`+cfg.HomeHypha)
|
||||||
}
|
}
|
||||||
|
|
||||||
func geminiHypha(w *gemini.ResponseWriter, rq *gemini.Request) {
|
func geminiHypha(w *gemini.ResponseWriter, rq *gemini.Request) {
|
||||||
@ -37,21 +44,20 @@ func geminiHypha(w *gemini.ResponseWriter, rq *gemini.Request) {
|
|||||||
if h.Exists {
|
if h.Exists {
|
||||||
fileContentsT, errT := ioutil.ReadFile(h.TextPath)
|
fileContentsT, errT := ioutil.ReadFile(h.TextPath)
|
||||||
if errT == nil {
|
if errT == nil {
|
||||||
md := markup.Doc(hyphaName, string(fileContentsT))
|
contents = string(fileContentsT)
|
||||||
contents = md.AsGemtext()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if hasAmnt {
|
if hasAmnt {
|
||||||
w.Write([]byte("This hypha has an attachment\n"))
|
_, _ = io.WriteString(w, "This hypha has an attachment\n")
|
||||||
}
|
}
|
||||||
w.Write([]byte(contents))
|
_, _ = io.WriteString(w, contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleGemini() {
|
func handleGemini() {
|
||||||
if util.GeminiCertPath == "" {
|
if cfg.GeminiCertificatePath == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
certPath, err := filepath.Abs(util.GeminiCertPath)
|
certPath, err := filepath.Abs(cfg.GeminiCertificatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -82,3 +88,15 @@ func handleGemini() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 ""
|
||||||
|
}
|
||||||
|
3
go.mod
3
go.mod
@ -5,12 +5,13 @@ go 1.14
|
|||||||
require (
|
require (
|
||||||
git.sr.ht/~adnano/go-gemini v0.1.13
|
git.sr.ht/~adnano/go-gemini v0.1.13
|
||||||
github.com/adrg/xdg v0.2.2
|
github.com/adrg/xdg v0.2.2
|
||||||
|
github.com/bouncepaw/mycomarkup v0.4.5
|
||||||
github.com/go-ini/ini v1.62.0
|
github.com/go-ini/ini v1.62.0
|
||||||
github.com/gorilla/feeds v1.1.1
|
github.com/gorilla/feeds v1.1.1
|
||||||
github.com/kr/pretty v0.2.1 // indirect
|
github.com/kr/pretty v0.2.1 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||||
github.com/valyala/quicktemplate v1.6.3
|
github.com/valyala/quicktemplate v1.6.3
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
)
|
)
|
||||||
|
7
go.sum
7
go.sum
@ -3,6 +3,8 @@ git.sr.ht/~adnano/go-gemini v0.1.13/go.mod h1:If1VxEWcZDrRt5FeAFnGTcM2Ud1E3BXs3V
|
|||||||
github.com/adrg/xdg v0.2.2 h1:A7ZHKRz5KGOLJX/bg7IPzStryhvCzAE1wX+KWawPiAo=
|
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/adrg/xdg v0.2.2/go.mod h1:7I2hH/IT30IsupOpKZ5ue7/qNi3CoKzD6tL3HwpaRMQ=
|
||||||
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||||
|
github.com/bouncepaw/mycomarkup v0.4.5 h1:QBLSGmqGsb3smjAmywosio2nVL7A8sR2KF5AB38GrXc=
|
||||||
|
github.com/bouncepaw/mycomarkup v0.4.5/go.mod h1:0n6thlGGgrx2Y/2NaaUH4qHW4v1xJ+EpW7yMFUxNRIg=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU=
|
github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU=
|
||||||
@ -37,11 +39,14 @@ github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl
|
|||||||
github.com/valyala/quicktemplate v1.6.3 h1:O7EuMwuH7Q94U2CXD6sOX8AYHqQqWtmIk690IhmpkKA=
|
github.com/valyala/quicktemplate v1.6.3 h1:O7EuMwuH7Q94U2CXD6sOX8AYHqQqWtmIk690IhmpkKA=
|
||||||
github.com/valyala/quicktemplate v1.6.3/go.mod h1:fwPzK2fHuYEODzJ9pkw0ipCPNHZ2tD5KW4lOuSdPKzY=
|
github.com/valyala/quicktemplate v1.6.3/go.mod h1:fwPzK2fHuYEODzJ9pkw0ipCPNHZ2tD5KW4lOuSdPKzY=
|
||||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
// Package history provides a git wrapper.
|
||||||
package history
|
package history
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html"
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -10,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,20 +22,18 @@ var gitpath string
|
|||||||
var renameMsgPattern = regexp.MustCompile(`^Rename ‘(.*)’ to ‘.*’`)
|
var renameMsgPattern = regexp.MustCompile(`^Rename ‘(.*)’ to ‘.*’`)
|
||||||
|
|
||||||
// Start finds git and initializes git credentials.
|
// Start finds git and initializes git credentials.
|
||||||
func Start(wikiDir string) {
|
func Start() {
|
||||||
path, err := exec.LookPath("git")
|
path, err := exec.LookPath("git")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Cound not find the git executable. Check your $PATH.")
|
log.Fatal("Could not find the git executable. Check your $PATH.")
|
||||||
} else {
|
|
||||||
log.Println("Git path is", path)
|
|
||||||
}
|
}
|
||||||
gitpath = path
|
gitpath = path
|
||||||
|
|
||||||
_, err = gitsh("config", "user.name", "wikimind")
|
_, err = silentGitsh("config", "user.name", "wikimind")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = gitsh("config", "user.email", "wikimind@mycorrhiza")
|
_, err = silentGitsh("config", "user.email", "wikimind@mycorrhiza")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -44,9 +45,26 @@ type Revision struct {
|
|||||||
Username string
|
Username string
|
||||||
Time time.Time
|
Time time.Time
|
||||||
Message string
|
Message string
|
||||||
|
filesAffectedBuf []string
|
||||||
hyphaeAffectedBuf []string
|
hyphaeAffectedBuf []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filesAffected tells what files have been affected by the revision.
|
||||||
|
func (rev *Revision) filesAffected() (filenames []string) {
|
||||||
|
if nil != rev.filesAffectedBuf {
|
||||||
|
return rev.filesAffectedBuf
|
||||||
|
}
|
||||||
|
// List of files affected by this revision, one per line.
|
||||||
|
out, err := silentGitsh("diff-tree", "--no-commit-id", "--name-only", "-r", rev.Hash)
|
||||||
|
// There's an error? Well, whatever, let's just assign an empty slice, who cares.
|
||||||
|
if err != nil {
|
||||||
|
rev.filesAffectedBuf = []string{}
|
||||||
|
} else {
|
||||||
|
rev.filesAffectedBuf = strings.Split(out.String(), "\n")
|
||||||
|
}
|
||||||
|
return rev.filesAffectedBuf
|
||||||
|
}
|
||||||
|
|
||||||
// determine what hyphae were affected by this revision
|
// determine what hyphae were affected by this revision
|
||||||
func (rev *Revision) hyphaeAffected() (hyphae []string) {
|
func (rev *Revision) hyphaeAffected() (hyphae []string) {
|
||||||
if nil != rev.hyphaeAffectedBuf {
|
if nil != rev.hyphaeAffectedBuf {
|
||||||
@ -54,8 +72,6 @@ func (rev *Revision) hyphaeAffected() (hyphae []string) {
|
|||||||
}
|
}
|
||||||
hyphae = make([]string, 0)
|
hyphae = make([]string, 0)
|
||||||
var (
|
var (
|
||||||
// List of files affected by this revision, one per line.
|
|
||||||
out, err = gitsh("diff-tree", "--no-commit-id", "--name-only", "-r", rev.Hash)
|
|
||||||
// set is used to determine if a certain hypha has been already noted (hyphae are stored in 2 files at most currently).
|
// set is used to determine if a certain hypha has been already noted (hyphae are stored in 2 files at most currently).
|
||||||
set = make(map[string]bool)
|
set = make(map[string]bool)
|
||||||
isNewName = func(hyphaName string) bool {
|
isNewName = func(hyphaName string) bool {
|
||||||
@ -65,11 +81,9 @@ func (rev *Revision) hyphaeAffected() (hyphae []string) {
|
|||||||
set[hyphaName] = true
|
set[hyphaName] = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
filesAffected = rev.filesAffected()
|
||||||
)
|
)
|
||||||
if err != nil {
|
for _, filename := range filesAffected {
|
||||||
return hyphae
|
|
||||||
}
|
|
||||||
for _, filename := range strings.Split(out.String(), "\n") {
|
|
||||||
if strings.IndexRune(filename, '.') >= 0 {
|
if strings.IndexRune(filename, '.') >= 0 {
|
||||||
dotPos := strings.LastIndexByte(filename, '.')
|
dotPos := strings.LastIndexByte(filename, '.')
|
||||||
hyphaName := string([]byte(filename)[0:dotPos]) // is it safe?
|
hyphaName := string([]byte(filename)[0:dotPos]) // is it safe?
|
||||||
@ -94,15 +108,44 @@ func (rev Revision) HyphaeLinksHTML() (html string) {
|
|||||||
if i > 0 {
|
if i > 0 {
|
||||||
html += `<span aria-hidden="true">, </span>`
|
html += `<span aria-hidden="true">, </span>`
|
||||||
}
|
}
|
||||||
html += fmt.Sprintf(`<a href="/page/%[1]s">%[1]s</a>`, hyphaName)
|
html += fmt.Sprintf(`<a href="/hypha/%[1]s">%[1]s</a>`, hyphaName)
|
||||||
}
|
}
|
||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rev *Revision) descriptionForFeed() (html string) {
|
// descriptionForFeed generates a good enough HTML contents for a web feed.
|
||||||
|
func (rev *Revision) descriptionForFeed() (htmlDesc string) {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
`<p>%s</p>
|
`<p>%s</p>
|
||||||
<p><b>Hyphae affected:</b> %s</p>`, rev.Message, rev.HyphaeLinksHTML())
|
<p><b>Hyphae affected:</b> %s</p>
|
||||||
|
<pre><code>%s</code></pre>`, rev.Message, rev.HyphaeLinksHTML(), html.EscapeString(rev.textDiff()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// textDiff generates a good enough diff to display in a web feed. It is not html-escaped.
|
||||||
|
func (rev *Revision) textDiff() (diff string) {
|
||||||
|
filenames, ok := rev.mycoFiles()
|
||||||
|
if !ok {
|
||||||
|
return "No text changes"
|
||||||
|
}
|
||||||
|
for _, filename := range filenames {
|
||||||
|
text, err := PrimitiveDiffAtRevision(filename, rev.Hash)
|
||||||
|
if err != nil {
|
||||||
|
diff += "\nAn error has occured with " + filename + "\n"
|
||||||
|
}
|
||||||
|
diff += text + "\n"
|
||||||
|
}
|
||||||
|
return diff
|
||||||
|
}
|
||||||
|
|
||||||
|
// mycoFiles returns filenames of .myco file. It is not ok if there are no myco files.
|
||||||
|
func (rev *Revision) mycoFiles() (filenames []string, ok bool) {
|
||||||
|
filenames = []string{}
|
||||||
|
for _, filename := range rev.filesAffected() {
|
||||||
|
if strings.HasSuffix(filename, ".myco") {
|
||||||
|
filenames = append(filenames, filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filenames, len(filenames) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try and guess what link is the most important by looking at the message.
|
// Try and guess what link is the most important by looking at the message.
|
||||||
@ -113,11 +156,11 @@ func (rev *Revision) bestLink() string {
|
|||||||
)
|
)
|
||||||
switch {
|
switch {
|
||||||
case renameRes != nil:
|
case renameRes != nil:
|
||||||
return "/page/" + renameRes[1]
|
return "/hypha/" + renameRes[1]
|
||||||
case len(revs) == 0:
|
case len(revs) == 0:
|
||||||
return ""
|
return ""
|
||||||
default:
|
default:
|
||||||
return "/page/" + revs[0]
|
return "/hypha/" + revs[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +169,7 @@ func (rev *Revision) bestLink() string {
|
|||||||
func gitsh(args ...string) (out bytes.Buffer, err error) {
|
func gitsh(args ...string) (out bytes.Buffer, err error) {
|
||||||
fmt.Printf("$ %v\n", args)
|
fmt.Printf("$ %v\n", args)
|
||||||
cmd := exec.Command(gitpath, args...)
|
cmd := exec.Command(gitpath, args...)
|
||||||
cmd.Dir = util.WikiDir
|
cmd.Dir = cfg.WikiDir
|
||||||
|
|
||||||
b, err := cmd.CombinedOutput()
|
b, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -135,6 +178,15 @@ func gitsh(args ...string) (out bytes.Buffer, err error) {
|
|||||||
return *bytes.NewBuffer(b), err
|
return *bytes.NewBuffer(b), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// silentGitsh is like gitsh, except it writes less to the stdout.
|
||||||
|
func silentGitsh(args ...string) (out bytes.Buffer, err error) {
|
||||||
|
cmd := exec.Command(gitpath, args...)
|
||||||
|
cmd.Dir = cfg.WikiDir
|
||||||
|
|
||||||
|
b, err := cmd.CombinedOutput()
|
||||||
|
return *bytes.NewBuffer(b), err
|
||||||
|
}
|
||||||
|
|
||||||
// Convert a UNIX timestamp as string into a time. If nil is returned, it means that the timestamp could not be converted.
|
// Convert a UNIX timestamp as string into a time. If nil is returned, it means that the timestamp could not be converted.
|
||||||
func unixTimestampAsTime(ts string) *time.Time {
|
func unixTimestampAsTime(ts string) *time.Time {
|
||||||
i, err := strconv.ParseInt(ts, 10, 64)
|
i, err := strconv.ParseInt(ts, 10, 64)
|
||||||
|
@ -1,28 +1,29 @@
|
|||||||
// information.go
|
|
||||||
// Things related to gathering existing information.
|
|
||||||
package history
|
package history
|
||||||
|
|
||||||
|
// information.go
|
||||||
|
// Things related to gathering existing information.
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
"log"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
"github.com/gorilla/feeds"
|
"github.com/gorilla/feeds"
|
||||||
)
|
)
|
||||||
|
|
||||||
func recentChangesFeed() *feeds.Feed {
|
func recentChangesFeed() *feeds.Feed {
|
||||||
feed := &feeds.Feed{
|
feed := &feeds.Feed{
|
||||||
Title: "Recent changes",
|
Title: "Recent changes",
|
||||||
Link: &feeds.Link{Href: util.URL},
|
Link: &feeds.Link{Href: cfg.URL},
|
||||||
Description: "List of 30 recent changes on the wiki",
|
Description: "List of 30 recent changes on the wiki",
|
||||||
Author: &feeds.Author{Name: "Wikimind", Email: "wikimind@mycorrhiza"},
|
Author: &feeds.Author{Name: "Wikimind", Email: "wikimind@mycorrhiza"},
|
||||||
Updated: time.Now(),
|
Updated: time.Now(),
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
out, err = gitsh(
|
out, err = silentGitsh(
|
||||||
"log", "--oneline", "--no-merges",
|
"log", "--oneline", "--no-merges",
|
||||||
"--pretty=format:\"%h\t%ae\t%at\t%s\"",
|
"--pretty=format:\"%h\t%ae\t%at\t%s\"",
|
||||||
"--max-count=30",
|
"--max-count=30",
|
||||||
@ -34,6 +35,7 @@ func recentChangesFeed() *feeds.Feed {
|
|||||||
revs = append(revs, parseRevisionLine(line))
|
revs = append(revs, parseRevisionLine(line))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Printf("Found %d recent changes", len(revs))
|
||||||
for _, rev := range revs {
|
for _, rev := range revs {
|
||||||
feed.Add(&feeds.Item{
|
feed.Add(&feeds.Item{
|
||||||
Title: rev.Message,
|
Title: rev.Message,
|
||||||
@ -42,7 +44,7 @@ func recentChangesFeed() *feeds.Feed {
|
|||||||
Description: rev.descriptionForFeed(),
|
Description: rev.descriptionForFeed(),
|
||||||
Created: rev.Time,
|
Created: rev.Time,
|
||||||
Updated: rev.Time,
|
Updated: rev.Time,
|
||||||
Link: &feeds.Link{Href: util.URL + rev.bestLink()},
|
Link: &feeds.Link{Href: cfg.URL + rev.bestLink()},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return feed
|
return feed
|
||||||
@ -62,7 +64,7 @@ func RecentChangesJSON() (string, error) {
|
|||||||
|
|
||||||
func RecentChanges(n int) []Revision {
|
func RecentChanges(n int) []Revision {
|
||||||
var (
|
var (
|
||||||
out, err = gitsh(
|
out, err = silentGitsh(
|
||||||
"log", "--oneline", "--no-merges",
|
"log", "--oneline", "--no-merges",
|
||||||
"--pretty=format:\"%h\t%ae\t%at\t%s\"",
|
"--pretty=format:\"%h\t%ae\t%at\t%s\"",
|
||||||
"--max-count="+strconv.Itoa(n),
|
"--max-count="+strconv.Itoa(n),
|
||||||
@ -74,6 +76,7 @@ func RecentChanges(n int) []Revision {
|
|||||||
revs = append(revs, parseRevisionLine(line))
|
revs = append(revs, parseRevisionLine(line))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Printf("Found %d recent changes", len(revs))
|
||||||
return revs
|
return revs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +89,7 @@ func FileChanged(path string) bool {
|
|||||||
// Revisions returns slice of revisions for the given hypha name.
|
// Revisions returns slice of revisions for the given hypha name.
|
||||||
func Revisions(hyphaName string) ([]Revision, error) {
|
func Revisions(hyphaName string) ([]Revision, error) {
|
||||||
var (
|
var (
|
||||||
out, err = gitsh(
|
out, err = silentGitsh(
|
||||||
"log", "--oneline", "--no-merges",
|
"log", "--oneline", "--no-merges",
|
||||||
// Hash, author email, author time, commit msg separated by tab
|
// Hash, author email, author time, commit msg separated by tab
|
||||||
"--pretty=format:\"%h\t%ae\t%at\t%s\"",
|
"--pretty=format:\"%h\t%ae\t%at\t%s\"",
|
||||||
@ -101,6 +104,7 @@ func Revisions(hyphaName string) ([]Revision, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Printf("Found %d revisions for ‘%s’\n", len(revs), hyphaName)
|
||||||
return revs, err
|
return revs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +141,7 @@ func (rev *Revision) asHistoryEntry(hyphaName string) (html string) {
|
|||||||
author := ""
|
author := ""
|
||||||
if rev.Username != "anon" {
|
if rev.Username != "anon" {
|
||||||
author = fmt.Sprintf(`
|
author = fmt.Sprintf(`
|
||||||
<span class="history-entry__author">by <a href="/page/%[1]s/%[2]s" rel="author">%[2]s</span>`, util.UserHypha, rev.Username)
|
<span class="history-entry__author">by <a href="/page/%[1]s/%[2]s" rel="author">%[2]s</span>`, cfg.UserHypha, rev.Username)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
<li class="history__entry">
|
<li class="history__entry">
|
||||||
@ -170,13 +174,20 @@ func parseRevisionLine(line string) Revision {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// See how the file with `filepath` looked at commit with `hash`.
|
// FileAtRevision shows how the file with the given file path looked at the commit with the hash. It may return an error if git fails.
|
||||||
func FileAtRevision(filepath, hash string) (string, error) {
|
func FileAtRevision(filepath, hash string) (string, error) {
|
||||||
out, err := gitsh("show", hash+":"+strings.TrimPrefix(filepath, util.WikiDir+"/"))
|
out, err := gitsh("show", hash+":"+strings.TrimPrefix(filepath, cfg.WikiDir+"/"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
return out.String(), err
|
return out.String(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrimitiveDiffAtRevision generates a plain-text diff for the given filepath at the commit with the given hash. It may return an error if git fails.
|
||||||
func PrimitiveDiffAtRevision(filepath, hash string) (string, error) {
|
func PrimitiveDiffAtRevision(filepath, hash string) (string, error) {
|
||||||
out, err := gitsh("diff", "--unified=1", "--no-color", hash+"~", hash, "--", filepath)
|
out, err := silentGitsh("diff", "--unified=1", "--no-color", hash+"~", hash, "--", filepath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
return out.String(), err
|
return out.String(), err
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// history/operations.go
|
|
||||||
// Things related to writing history.
|
|
||||||
package history
|
package history
|
||||||
|
|
||||||
|
// history/operations.go
|
||||||
|
// Things related to writing history.
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -84,7 +84,10 @@ func (hop *HistoryOp) WithFilesRemoved(paths ...string) *HistoryOp {
|
|||||||
func (hop *HistoryOp) WithFilesRenamed(pairs map[string]string) *HistoryOp {
|
func (hop *HistoryOp) WithFilesRenamed(pairs map[string]string) *HistoryOp {
|
||||||
for from, to := range pairs {
|
for from, to := range pairs {
|
||||||
if from != "" {
|
if from != "" {
|
||||||
os.MkdirAll(filepath.Dir(to), 0777)
|
if err := os.MkdirAll(filepath.Dir(to), 0777); err != nil {
|
||||||
|
hop.Errs = append(hop.Errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if err := Rename(from, to); err != nil {
|
if err := Rename(from, to); err != nil {
|
||||||
hop.Errs = append(hop.Errs, err)
|
hop.Errs = append(hop.Errs, err)
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ func Index(path string) {
|
|||||||
h.Insert()
|
h.Insert()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Println("Indexed", Count(), "hyphae")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// 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.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// 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 is for the Hypha type, hypha storage and stuff like that. It shall not depend on mycorrhiza modules other than util.
|
||||||
package hyphae
|
package hyphae
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// HyphaPattern is a pattern which all hyphae must match.
|
// HyphaPattern is a pattern which all hyphae must match.
|
||||||
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`)
|
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}]+`)
|
||||||
|
|
||||||
type Hypha struct {
|
type Hypha struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
138
link/link.go
138
link/link.go
@ -1,138 +0,0 @@
|
|||||||
package link
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LinkType tells what type the given link is.
|
|
||||||
type LinkType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
LinkInvalid 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 + l.Anchor
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pos := strings.IndexRune(address, '#'); pos != -1 && pos != 0 {
|
|
||||||
link.Anchor = address[pos:]
|
|
||||||
address = address[:pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
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.Display += link.Anchor
|
|
||||||
}
|
|
||||||
link.Address = address
|
|
||||||
case strings.HasPrefix(address, "#"):
|
|
||||||
link.Kind = LinkLocalHypha
|
|
||||||
link.Address = util.CanonicalName(hyphaName)
|
|
||||||
link.Anchor = 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:]))
|
|
||||||
default:
|
|
||||||
link.Kind = LinkLocalHypha
|
|
||||||
link.Address = util.CanonicalName(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &link
|
|
||||||
}
|
|
215
main.go
215
main.go
@ -2,229 +2,46 @@
|
|||||||
//go:generate qtc -dir=assets
|
//go:generate qtc -dir=assets
|
||||||
//go:generate qtc -dir=views
|
//go:generate qtc -dir=views
|
||||||
//go:generate qtc -dir=tree
|
//go:generate qtc -dir=tree
|
||||||
|
// Command mycorrhiza is a program that runs a mycorrhiza wiki.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/assets"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/files"
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
"github.com/bouncepaw/mycorrhiza/shroom"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/web"
|
||||||
"github.com/bouncepaw/mycorrhiza/views"
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WikiDir is a rooted path to the wiki storage directory.
|
|
||||||
var WikiDir string
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
||||||
w.WriteHeader(status)
|
|
||||||
fmt.Fprint(
|
|
||||||
w,
|
|
||||||
base(
|
|
||||||
title,
|
|
||||||
fmt.Sprintf(
|
|
||||||
`<main class="main-width"><p>%s. <a href="/page/%s">Go back to the hypha.<a></p></main>`,
|
|
||||||
errMsg,
|
|
||||||
name,
|
|
||||||
),
|
|
||||||
user.EmptyUser(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show all hyphae
|
|
||||||
func handlerList(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
log.Println(rq.URL)
|
|
||||||
util.HTTP200Page(w, base("List of pages", views.HyphaListHTML(), user.FromRequest(rq)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// This part is present in all html documents.
|
|
||||||
var base = views.BaseHTML
|
|
||||||
|
|
||||||
// Reindex all hyphae by checking the wiki storage directory anew.
|
|
||||||
func handlerReindex(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
log.Println(rq.URL)
|
|
||||||
if ok := user.CanProceed(rq, "reindex"); !ok {
|
|
||||||
HttpErr(w, http.StatusForbidden, util.HomePage, "Not enough rights", "You must be an admin to reindex hyphae.")
|
|
||||||
log.Println("Rejected", rq.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hyphae.ResetCount()
|
|
||||||
log.Println("Wiki storage directory is", WikiDir)
|
|
||||||
log.Println("Start indexing hyphae...")
|
|
||||||
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)
|
|
||||||
if ok := user.CanProceed(rq, "update-header-links"); !ok {
|
|
||||||
HttpErr(w, http.StatusForbidden, util.HomePage, "Not enough rights", "You must be a moderator to update header links.")
|
|
||||||
log.Println("Rejected", rq.URL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
shroom.SetHeaderLinks()
|
|
||||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect to a random hypha.
|
|
||||||
func handlerRandom(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
log.Println(rq.URL)
|
|
||||||
var (
|
|
||||||
randomHyphaName string
|
|
||||||
amountOfHyphae int = hyphae.Count()
|
|
||||||
)
|
|
||||||
if amountOfHyphae == 0 {
|
|
||||||
HttpErr(w, http.StatusNotFound, util.HomePage, "There are no hyphae",
|
|
||||||
"It is not possible to display a random hypha because the wiki does not contain any hyphae")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i := rand.Intn(amountOfHyphae)
|
|
||||||
for h := range hyphae.YieldExistingHyphae() {
|
|
||||||
if i == 0 {
|
|
||||||
randomHyphaName = h.Name
|
|
||||||
}
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
http.Redirect(w, rq, "/hypha/"+randomHyphaName, http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerStyle(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
log.Println(rq.URL)
|
|
||||||
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(assets.DefaultCSS()))
|
|
||||||
}
|
|
||||||
if bytes, err := ioutil.ReadFile(util.WikiDir + "/static/custom.css"); err == nil {
|
|
||||||
w.Write(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerToolbar(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
log.Println(rq.URL)
|
|
||||||
w.Header().Set("Content-Type", "text/javascript;charset=utf-8")
|
|
||||||
w.Write([]byte(assets.ToolbarJS()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerIcon(w http.ResponseWriter, rq *http.Request) {
|
|
||||||
iconName := strings.TrimPrefix(rq.URL.Path, "/static/icon/")
|
|
||||||
if iconName == "https" {
|
|
||||||
iconName = "http"
|
|
||||||
}
|
|
||||||
files, err := ioutil.ReadDir(WikiDir + "/static/icon")
|
|
||||||
if err == nil {
|
|
||||||
for _, f := range files {
|
|
||||||
if strings.HasPrefix(f.Name(), iconName+"-protocol-icon") {
|
|
||||||
http.ServeFile(w, rq, WikiDir+"/static/icon/"+f.Name())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "image/svg+xml")
|
|
||||||
switch iconName {
|
|
||||||
case "gemini":
|
|
||||||
w.Write([]byte(assets.IconGemini()))
|
|
||||||
case "mailto":
|
|
||||||
w.Write([]byte(assets.IconMailto()))
|
|
||||||
case "gopher":
|
|
||||||
w.Write([]byte(assets.IconGopher()))
|
|
||||||
case "feed":
|
|
||||||
w.Write([]byte(assets.IconFeed()))
|
|
||||||
default:
|
|
||||||
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, 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) {
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte(
|
|
||||||
`User-agent: *
|
|
||||||
Allow: /page/
|
|
||||||
Allow: /recent-changes
|
|
||||||
Disallow: /
|
|
||||||
Crawl-delay: 5`))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
parseCliArgs()
|
parseCliArgs()
|
||||||
|
|
||||||
// It is ok if the path is ""
|
// It is ok if the path is ""
|
||||||
util.ReadConfigFile(util.ConfigFilePath)
|
cfg.ReadConfigFile()
|
||||||
|
|
||||||
if err := files.CalculatePaths(); err != nil {
|
if err := files.CalculatePaths(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Running MycorrhizaWiki")
|
log.Println("Running MycorrhizaWiki 1.2.0 indev")
|
||||||
if err := os.Chdir(WikiDir); err != nil {
|
if err := os.Chdir(cfg.WikiDir); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
log.Println("Wiki storage directory is", WikiDir)
|
log.Println("Wiki storage directory is", cfg.WikiDir)
|
||||||
hyphae.Index(WikiDir)
|
|
||||||
log.Println("Indexed", hyphae.Count(), "hyphae")
|
|
||||||
|
|
||||||
// Initialize user database
|
// Init the subsystems:
|
||||||
|
hyphae.Index(cfg.WikiDir)
|
||||||
user.InitUserDatabase()
|
user.InitUserDatabase()
|
||||||
|
history.Start()
|
||||||
history.Start(WikiDir)
|
|
||||||
shroom.SetHeaderLinks()
|
shroom.SetHeaderLinks()
|
||||||
|
|
||||||
|
// Network:
|
||||||
go handleGemini()
|
go handleGemini()
|
||||||
|
web.Init()
|
||||||
// See http_admin.go for /admin, /admin/*
|
log.Fatal(http.ListenAndServe("0.0.0.0:"+cfg.HTTPPort, nil))
|
||||||
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
|
|
||||||
http.HandleFunc("/list", handlerList)
|
|
||||||
http.HandleFunc("/reindex", handlerReindex)
|
|
||||||
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/toolbar.js", handlerToolbar)
|
|
||||||
http.HandleFunc("/static/icon/", handlerIcon)
|
|
||||||
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
34
markup/hr.go
@ -1,34 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
203
markup/img.go
203
markup/img.go
@ -1,203 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/link"
|
|
||||||
)
|
|
||||||
|
|
||||||
var imgRe = regexp.MustCompile(`^img\s+{`)
|
|
||||||
|
|
||||||
func MatchesImg(line string) bool {
|
|
||||||
return imgRe.MatchString(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
type imgState int
|
|
||||||
|
|
||||||
const (
|
|
||||||
inRoot imgState = iota
|
|
||||||
inName
|
|
||||||
inDimensionsW
|
|
||||||
inDimensionsH
|
|
||||||
inDescription
|
|
||||||
)
|
|
||||||
|
|
||||||
type Img struct {
|
|
||||||
entries []imgEntry
|
|
||||||
currEntry imgEntry
|
|
||||||
hyphaName string
|
|
||||||
state imgState
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) Process(line string) (shouldGoBackToNormal bool) {
|
|
||||||
stateToProcessor := map[imgState]func(rune) bool{
|
|
||||||
inRoot: img.processInRoot,
|
|
||||||
inName: img.processInName,
|
|
||||||
inDimensionsW: img.processInDimensionsW,
|
|
||||||
inDimensionsH: img.processInDimensionsH,
|
|
||||||
inDescription: img.processInDescription,
|
|
||||||
}
|
|
||||||
for _, r := range line {
|
|
||||||
if shouldReturnTrue := stateToProcessor[img.state](r); shouldReturnTrue {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) processInDescription(r rune) (shouldReturnTrue bool) {
|
|
||||||
switch r {
|
|
||||||
case '}':
|
|
||||||
img.state = inName
|
|
||||||
default:
|
|
||||||
img.currEntry.desc.WriteRune(r)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) processInRoot(r rune) (shouldReturnTrue bool) {
|
|
||||||
switch r {
|
|
||||||
case '}':
|
|
||||||
img.pushEntry()
|
|
||||||
return true
|
|
||||||
case '\n', '\r':
|
|
||||||
img.pushEntry()
|
|
||||||
case ' ', '\t':
|
|
||||||
default:
|
|
||||||
img.state = inName
|
|
||||||
img.currEntry = imgEntry{}
|
|
||||||
img.currEntry.path.Reset()
|
|
||||||
img.currEntry.path.WriteRune(r)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) processInName(r rune) (shouldReturnTrue bool) {
|
|
||||||
switch r {
|
|
||||||
case '}':
|
|
||||||
img.pushEntry()
|
|
||||||
return true
|
|
||||||
case '|':
|
|
||||||
img.state = inDimensionsW
|
|
||||||
case '{':
|
|
||||||
img.state = inDescription
|
|
||||||
case '\n', '\r':
|
|
||||||
img.pushEntry()
|
|
||||||
img.state = inRoot
|
|
||||||
default:
|
|
||||||
img.currEntry.path.WriteRune(r)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) processInDimensionsW(r rune) (shouldReturnTrue bool) {
|
|
||||||
switch r {
|
|
||||||
case '}':
|
|
||||||
img.pushEntry()
|
|
||||||
return true
|
|
||||||
case '*':
|
|
||||||
img.state = inDimensionsH
|
|
||||||
case ' ', '\t', '\n':
|
|
||||||
case '{':
|
|
||||||
img.state = inDescription
|
|
||||||
default:
|
|
||||||
img.currEntry.sizeW.WriteRune(r)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) processInDimensionsH(r rune) (shouldGoBackToNormal bool) {
|
|
||||||
switch r {
|
|
||||||
case '}':
|
|
||||||
img.pushEntry()
|
|
||||||
return true
|
|
||||||
case ' ', '\t', '\n':
|
|
||||||
case '{':
|
|
||||||
img.state = inDescription
|
|
||||||
default:
|
|
||||||
img.currEntry.sizeH.WriteRune(r)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ImgFromFirstLine(line, hyphaName string) (img *Img, shouldGoBackToNormal bool) {
|
|
||||||
img = &Img{
|
|
||||||
hyphaName: hyphaName,
|
|
||||||
entries: make([]imgEntry, 0),
|
|
||||||
}
|
|
||||||
line = line[strings.IndexRune(line, '{')+1:]
|
|
||||||
return img, img.Process(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) pagePathFor(path string) string {
|
|
||||||
path = strings.TrimSpace(path)
|
|
||||||
if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 {
|
|
||||||
return path
|
|
||||||
} else {
|
|
||||||
return "/page/" + xclCanonicalName(img.hyphaName, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDimensions(dimensions string) (sizeW, sizeH string) {
|
|
||||||
xIndex := strings.IndexRune(dimensions, '*')
|
|
||||||
if xIndex == -1 { // If no x in dimensions
|
|
||||||
sizeW = strings.TrimSpace(dimensions)
|
|
||||||
} else {
|
|
||||||
sizeW = strings.TrimSpace(dimensions[:xIndex])
|
|
||||||
sizeH = strings.TrimSpace(strings.TrimPrefix(dimensions, dimensions[:xIndex+1]))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) markExistenceOfSrcLinks() {
|
|
||||||
HyphaIterate(func(hn string) {
|
|
||||||
for _, entry := range img.entries {
|
|
||||||
if hn == entry.srclink.Address {
|
|
||||||
entry.srclink.DestinationUnknown = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) ToHtml() (html string) {
|
|
||||||
img.markExistenceOfSrcLinks()
|
|
||||||
isOneImageOnly := len(img.entries) == 1 && img.entries[0].desc.Len() == 0
|
|
||||||
if isOneImageOnly {
|
|
||||||
html += `<section class="img-gallery img-gallery_one-image">`
|
|
||||||
} else {
|
|
||||||
html += `<section class="img-gallery img-gallery_many-images">`
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range img.entries {
|
|
||||||
html += `<figure>`
|
|
||||||
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>`,
|
|
||||||
entry.srclink.Href(),
|
|
||||||
entry.srclink.ImgSrc(),
|
|
||||||
entry.sizeWAsAttr(),
|
|
||||||
entry.sizeHAsAttr())
|
|
||||||
}
|
|
||||||
html += entry.descriptionAsHtml(img.hyphaName)
|
|
||||||
html += `</figure>`
|
|
||||||
}
|
|
||||||
return html + `</section>`
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
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() + `"`
|
|
||||||
}
|
|
229
markup/lexer.go
229
markup/lexer.go
@ -1,229 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HyphaExists holds function that checks that a hypha is present.
|
|
||||||
var HyphaExists func(string) bool
|
|
||||||
|
|
||||||
//
|
|
||||||
var HyphaImageForOG func(string) string
|
|
||||||
|
|
||||||
// HyphaAccess holds function that accesses a hypha by its name.
|
|
||||||
var HyphaAccess func(string) (rawText, binaryHtml string, err error)
|
|
||||||
|
|
||||||
// HyphaIterate is a function that iterates all hypha names existing.
|
|
||||||
var HyphaIterate func(func(string))
|
|
||||||
|
|
||||||
// GemLexerState is used by markup parser to remember what is going on.
|
|
||||||
type GemLexerState struct {
|
|
||||||
// Name of hypha being parsed
|
|
||||||
name string
|
|
||||||
where string // "", "list", "pre"
|
|
||||||
// Line id
|
|
||||||
id int
|
|
||||||
buf string
|
|
||||||
// Temporaries
|
|
||||||
img *Img
|
|
||||||
table *Table
|
|
||||||
list *List
|
|
||||||
}
|
|
||||||
|
|
||||||
type Line struct {
|
|
||||||
id int
|
|
||||||
// interface{} may be bad. TODO: a proper type
|
|
||||||
contents interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MycoDoc) lex() (ast []Line) {
|
|
||||||
var state = GemLexerState{name: md.hyphaName}
|
|
||||||
|
|
||||||
for _, line := range append(strings.Split(md.contents, "\n"), "") {
|
|
||||||
lineToAST(line, &state, &ast)
|
|
||||||
}
|
|
||||||
return ast
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lex `line` in markup and save it to `ast` using `state`.
|
|
||||||
func lineToAST(line string, state *GemLexerState, ast *[]Line) {
|
|
||||||
addLine := func(text interface{}) {
|
|
||||||
*ast = append(*ast, Line{id: state.id, contents: text})
|
|
||||||
}
|
|
||||||
addParagraphIfNeeded := func() {
|
|
||||||
if state.where == "p" {
|
|
||||||
state.where = ""
|
|
||||||
addLine(fmt.Sprintf("<p id='%d'>%s</p>", state.id, strings.ReplaceAll(ParagraphToHtml(state.name, state.buf), "\n", "<br>")))
|
|
||||||
state.buf = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process empty lines depending on the current state
|
|
||||||
if "" == strings.TrimSpace(line) {
|
|
||||||
switch state.where {
|
|
||||||
case "pre":
|
|
||||||
state.buf += "\n"
|
|
||||||
case "launchpad":
|
|
||||||
state.where = ""
|
|
||||||
addLine(state.buf + "</ul>")
|
|
||||||
case "p":
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
startsWith := func(token string) bool {
|
|
||||||
return strings.HasPrefix(line, token)
|
|
||||||
}
|
|
||||||
addHeading := func(i int) {
|
|
||||||
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.
|
|
||||||
switch state.where {
|
|
||||||
case "img":
|
|
||||||
goto imgState
|
|
||||||
case "table":
|
|
||||||
goto tableState
|
|
||||||
case "list":
|
|
||||||
goto listState
|
|
||||||
case "pre":
|
|
||||||
goto preformattedState
|
|
||||||
case "launchpad":
|
|
||||||
goto launchpadState
|
|
||||||
default: // "p" or ""
|
|
||||||
goto normalState
|
|
||||||
}
|
|
||||||
|
|
||||||
imgState:
|
|
||||||
if shouldGoBackToNormal := state.img.Process(line); shouldGoBackToNormal {
|
|
||||||
state.where = ""
|
|
||||||
addLine(*state.img)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
tableState:
|
|
||||||
if shouldGoBackToNormal := state.table.Process(line); shouldGoBackToNormal {
|
|
||||||
state.where = ""
|
|
||||||
addLine(*state.table)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
listState:
|
|
||||||
if done := state.list.Parse(line); done {
|
|
||||||
state.list.Finalize()
|
|
||||||
state.where = ""
|
|
||||||
goto normalState
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
preformattedState:
|
|
||||||
switch {
|
|
||||||
case startsWith("```"):
|
|
||||||
state.where = ""
|
|
||||||
state.buf = strings.TrimSuffix(state.buf, "\n")
|
|
||||||
addLine(state.buf + "</code></pre>")
|
|
||||||
state.buf = ""
|
|
||||||
default:
|
|
||||||
state.buf += html.EscapeString(line) + "\n"
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
launchpadState:
|
|
||||||
switch {
|
|
||||||
case startsWith("=>"):
|
|
||||||
href, text, class := Rocketlink(line, state.name)
|
|
||||||
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>")
|
|
||||||
state.id++
|
|
||||||
state.buf = fmt.Sprintf("<pre id='%d' alt='%s' class='codeblock'><code>", state.id, strings.TrimPrefix(line, "```"))
|
|
||||||
default:
|
|
||||||
state.where = ""
|
|
||||||
addLine(state.buf + "</ul>")
|
|
||||||
goto normalState
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
normalState:
|
|
||||||
state.id++
|
|
||||||
switch {
|
|
||||||
|
|
||||||
case startsWith("```"):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
state.where = "pre"
|
|
||||||
state.buf = fmt.Sprintf("<pre id='%d' alt='%s' class='codeblock'><code>", state.id, strings.TrimPrefix(line, "```"))
|
|
||||||
|
|
||||||
case startsWith("###### "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(6)
|
|
||||||
case startsWith("##### "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(5)
|
|
||||||
case startsWith("#### "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(4)
|
|
||||||
case startsWith("### "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(3)
|
|
||||||
case startsWith("## "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(2)
|
|
||||||
case startsWith("# "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(1)
|
|
||||||
|
|
||||||
case startsWith(">"):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addLine(
|
|
||||||
fmt.Sprintf(
|
|
||||||
"<blockquote id='%d'>%s</blockquote>",
|
|
||||||
state.id,
|
|
||||||
ParagraphToHtml(state.name, remover(">")(line)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
case startsWith("=>"):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
state.where = "launchpad"
|
|
||||||
state.buf = fmt.Sprintf("<ul class='launchpad' id='%d'>\n", state.id)
|
|
||||||
goto launchpadState
|
|
||||||
|
|
||||||
case startsWith("<="):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addLine(parseTransclusion(line, state.name))
|
|
||||||
case MatchesHorizontalLine(line):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
*ast = append(*ast, Line{id: -1, contents: "<hr/>"})
|
|
||||||
case MatchesList(line):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
list, _ := NewList(line, state.name)
|
|
||||||
state.where = "list"
|
|
||||||
state.list = list
|
|
||||||
addLine(state.list)
|
|
||||||
case MatchesImg(line):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
img, shouldGoBackToNormal := ImgFromFirstLine(line, state.name)
|
|
||||||
if shouldGoBackToNormal {
|
|
||||||
addLine(*img)
|
|
||||||
} else {
|
|
||||||
state.where = "img"
|
|
||||||
state.img = img
|
|
||||||
}
|
|
||||||
case MatchesTable(line):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
state.where = "table"
|
|
||||||
state.table = TableFromFirstLine(line, state.name)
|
|
||||||
|
|
||||||
case state.where == "p":
|
|
||||||
state.buf += "\n" + line
|
|
||||||
default:
|
|
||||||
state.where = "p"
|
|
||||||
state.buf = line
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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) {
|
|
||||||
l := link.From(addr, display, hyphaName)
|
|
||||||
if l.Kind == link.LinkLocalHypha && !HyphaExists(l.Address) {
|
|
||||||
l.DestinationUnknown = true
|
|
||||||
}
|
|
||||||
return l.Href(), l.Display, l.Classes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse markup line starting with "=>" according to wikilink rules.
|
|
||||||
// See http://localhost:1737/page/wikilink
|
|
||||||
func Rocketlink(src, hyphaName string) (href, text, class string) {
|
|
||||||
src = strings.TrimSpace(src[2:]) // Drop =>
|
|
||||||
if src == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Href is text after => till first whitespace
|
|
||||||
addr := strings.Fields(src)[0]
|
|
||||||
display := strings.TrimPrefix(src, addr)
|
|
||||||
return LinkParts(addr, display, hyphaName)
|
|
||||||
}
|
|
171
markup/list.go
171
markup/list.go
@ -1,171 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseListItem(line string) (level int, offset int, ordered bool, err error) {
|
|
||||||
for line[level] == '*' {
|
|
||||||
level++
|
|
||||||
}
|
|
||||||
|
|
||||||
if line[level] == '.' {
|
|
||||||
ordered = true
|
|
||||||
offset = level + 2
|
|
||||||
} else {
|
|
||||||
ordered = false
|
|
||||||
offset = level + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if line[offset-1] != ' ' || len(line) < offset+2 || level < 1 || level > 6 {
|
|
||||||
err = errors.New("ill-formatted list item")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func MatchesList(line string) bool {
|
|
||||||
level, _, _, err := parseListItem(line)
|
|
||||||
return err == nil && level == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
type listItem struct {
|
|
||||||
content string
|
|
||||||
parent *listItem
|
|
||||||
children []*listItem
|
|
||||||
depth int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newListItem(parent *listItem) *listItem {
|
|
||||||
depth := 0
|
|
||||||
if parent != nil {
|
|
||||||
depth = parent.depth + 1
|
|
||||||
}
|
|
||||||
return &listItem{
|
|
||||||
parent: parent,
|
|
||||||
children: make([]*listItem, 0),
|
|
||||||
depth: depth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (item *listItem) renderAsHtmlTo(b *strings.Builder, hyphaName string, ordered bool) {
|
|
||||||
if len(item.content) > 0 {
|
|
||||||
b.WriteString("<li>")
|
|
||||||
b.WriteString(ParagraphToHtml(hyphaName, item.content))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(item.children) > 0 {
|
|
||||||
if ordered {
|
|
||||||
b.WriteString("<ol>")
|
|
||||||
} else {
|
|
||||||
b.WriteString("<ul>")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, child := range item.children {
|
|
||||||
child.renderAsHtmlTo(b, hyphaName, ordered)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ordered {
|
|
||||||
b.WriteString("</ol>")
|
|
||||||
} else {
|
|
||||||
b.WriteString("</ul>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(item.content) > 0 {
|
|
||||||
b.WriteString("</li>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A structure representing ordered and unordered lists in the AST.
|
|
||||||
type List struct {
|
|
||||||
curr *listItem
|
|
||||||
hyphaName string
|
|
||||||
ordered bool
|
|
||||||
finalized bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewList(line, hyphaName string) (*List, bool) {
|
|
||||||
list := &List{
|
|
||||||
hyphaName: hyphaName,
|
|
||||||
curr: newListItem(nil),
|
|
||||||
}
|
|
||||||
return list, list.Parse(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) pushItem() {
|
|
||||||
item := newListItem(list.curr)
|
|
||||||
list.curr.children = append(list.curr.children, item)
|
|
||||||
list.curr = item
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) popItem() {
|
|
||||||
if list.curr == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
list.curr = list.curr.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) balance(level int) {
|
|
||||||
for level > list.curr.depth {
|
|
||||||
list.pushItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
for level < list.curr.depth {
|
|
||||||
list.popItem()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) Parse(line string) (done bool) {
|
|
||||||
level, offset, ordered, err := parseListItem(line)
|
|
||||||
if err != nil {
|
|
||||||
list.Finalize()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// update ordered flag if the current node is the root one
|
|
||||||
// (i.e. no parsing has been done yet)
|
|
||||||
if list.curr.parent == nil {
|
|
||||||
list.ordered = ordered
|
|
||||||
}
|
|
||||||
|
|
||||||
// if list type has suddenly changed (ill-formatted list), quit
|
|
||||||
if ordered != list.ordered {
|
|
||||||
list.Finalize()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
list.balance(level)
|
|
||||||
|
|
||||||
// if the current node already has content, create a new one
|
|
||||||
// to prevent overwriting existing content (effectively creating
|
|
||||||
// a new sibling node)
|
|
||||||
if len(list.curr.content) > 0 {
|
|
||||||
list.popItem()
|
|
||||||
list.pushItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
list.curr.content = line[offset:]
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) Finalize() {
|
|
||||||
if !list.finalized {
|
|
||||||
// close all opened nodes, effectively going up to the root node
|
|
||||||
list.balance(0)
|
|
||||||
list.finalized = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) RenderAsHtml() (html string) {
|
|
||||||
// for a good measure
|
|
||||||
list.Finalize()
|
|
||||||
|
|
||||||
b := &strings.Builder{}
|
|
||||||
|
|
||||||
// fire up recursive render process
|
|
||||||
list.curr.renderAsHtmlTo(b, list.hyphaName, list.ordered)
|
|
||||||
|
|
||||||
return b.String()
|
|
||||||
}
|
|
@ -1,171 +0,0 @@
|
|||||||
// This is not done yet
|
|
||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/link"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Mycomarkup-formatted document
|
|
||||||
type MycoDoc struct {
|
|
||||||
// data
|
|
||||||
hyphaName string
|
|
||||||
contents string
|
|
||||||
// indicators
|
|
||||||
parsedAlready bool
|
|
||||||
// results
|
|
||||||
ast []Line
|
|
||||||
html string
|
|
||||||
firstImageURL string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
func Doc(hyphaName, contents string) *MycoDoc {
|
|
||||||
md := &MycoDoc{
|
|
||||||
hyphaName: hyphaName,
|
|
||||||
contents: contents,
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MycoDoc) Lex(recursionLevel int) *MycoDoc {
|
|
||||||
if !md.parsedAlready {
|
|
||||||
md.ast = md.lex()
|
|
||||||
}
|
|
||||||
md.parsedAlready = true
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// AsHtml returns an html representation of the document
|
|
||||||
func (md *MycoDoc) AsHTML() string {
|
|
||||||
md.html = Parse(md.Lex(0).ast, 0, 0, 0)
|
|
||||||
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(`<.*?>`)
|
|
||||||
|
|
||||||
// OpenGraphHTML returns an html representation of og: meta tags.
|
|
||||||
func (md *MycoDoc) OpenGraphHTML() string {
|
|
||||||
md.ogFillVars()
|
|
||||||
return strings.Join([]string{
|
|
||||||
ogTag("title", md.hyphaName),
|
|
||||||
ogTag("type", "article"),
|
|
||||||
ogTag("image", md.firstImageURL),
|
|
||||||
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
|
|
||||||
foundImg := false
|
|
||||||
for _, line := range md.ast {
|
|
||||||
switch v := line.contents.(type) {
|
|
||||||
case string:
|
|
||||||
if !foundDesc {
|
|
||||||
md.description = v
|
|
||||||
foundDesc = true
|
|
||||||
}
|
|
||||||
case Img:
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
func ogTag(property, content string) string {
|
|
||||||
return fmt.Sprintf(`<meta property="og:%s" content="%s"/>`, property, content)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The rest of this file is currently unused. TODO: use it I guess */
|
|
||||||
|
|
||||||
type BlockType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
BlockH1 = iota
|
|
||||||
BlockH2
|
|
||||||
BlockH3
|
|
||||||
BlockH4
|
|
||||||
BlockH5
|
|
||||||
BlockH6
|
|
||||||
BlockRocket
|
|
||||||
BlockPre
|
|
||||||
BlockQuote
|
|
||||||
BlockPara
|
|
||||||
)
|
|
||||||
|
|
||||||
type CrawlWhere int
|
|
||||||
|
|
||||||
const (
|
|
||||||
inSomewhere = iota
|
|
||||||
inPre
|
|
||||||
inEnd
|
|
||||||
)
|
|
||||||
|
|
||||||
func crawl(name, content string) []string {
|
|
||||||
stateStack := []CrawlWhere{inSomewhere}
|
|
||||||
|
|
||||||
startsWith := func(token string) bool {
|
|
||||||
return strings.HasPrefix(content, token)
|
|
||||||
}
|
|
||||||
|
|
||||||
pop := func() {
|
|
||||||
stateStack = stateStack[:len(stateStack)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
push := func(s CrawlWhere) {
|
|
||||||
stateStack = append(stateStack, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
readln := func(c string) (string, string) {
|
|
||||||
parts := strings.SplitN(c, "\n", 1)
|
|
||||||
return parts[0], parts[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
preAcc := ""
|
|
||||||
line := ""
|
|
||||||
|
|
||||||
for {
|
|
||||||
switch stateStack[0] {
|
|
||||||
case inSomewhere:
|
|
||||||
switch {
|
|
||||||
case startsWith("```"):
|
|
||||||
push(inPre)
|
|
||||||
_, content = readln(content)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
case inPre:
|
|
||||||
switch {
|
|
||||||
case startsWith("```"):
|
|
||||||
pop()
|
|
||||||
_, content = readln(content)
|
|
||||||
default:
|
|
||||||
line, content = readln(content)
|
|
||||||
preAcc += html.EscapeString(line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return []string{}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
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
|
|
||||||
// Not needed anymore, I guess.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,185 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
type spanTokenType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
spanTextNode = iota
|
|
||||||
spanItalic
|
|
||||||
spanBold
|
|
||||||
spanMono
|
|
||||||
spanSuper
|
|
||||||
spanSub
|
|
||||||
spanMark
|
|
||||||
spanStrike
|
|
||||||
spanLink
|
|
||||||
)
|
|
||||||
|
|
||||||
func tagFromState(stt spanTokenType, tagState map[spanTokenType]bool, tagName, originalForm string) string {
|
|
||||||
if tagState[spanMono] && (stt != spanMono) {
|
|
||||||
return originalForm
|
|
||||||
}
|
|
||||||
if tagState[stt] {
|
|
||||||
tagState[stt] = false
|
|
||||||
return fmt.Sprintf("</%s>", tagName)
|
|
||||||
} else {
|
|
||||||
tagState[stt] = true
|
|
||||||
return fmt.Sprintf("<%s>", tagName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLinkNode(input *bytes.Buffer, hyphaName string, isBracketedLink bool) string {
|
|
||||||
if isBracketedLink {
|
|
||||||
input.Next(2) // drop those [[
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
escaping = false
|
|
||||||
addrBuf = bytes.Buffer{}
|
|
||||||
displayBuf = bytes.Buffer{}
|
|
||||||
currBuf = &addrBuf
|
|
||||||
)
|
|
||||||
for input.Len() != 0 {
|
|
||||||
b, _ := input.ReadByte()
|
|
||||||
if escaping {
|
|
||||||
currBuf.WriteByte(b)
|
|
||||||
escaping = false
|
|
||||||
} else if isBracketedLink && b == '|' && currBuf == &addrBuf {
|
|
||||||
currBuf = &displayBuf
|
|
||||||
} else if isBracketedLink && b == ']' && bytes.HasPrefix(input.Bytes(), []byte{']'}) {
|
|
||||||
input.Next(1)
|
|
||||||
break
|
|
||||||
} else if !isBracketedLink && (unicode.IsSpace(rune(b)) || strings.ContainsRune("<>{}|\\^[]`,()", rune(b))) {
|
|
||||||
input.UnreadByte()
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
currBuf.WriteByte(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
href, text, class := LinkParts(addrBuf.String(), displayBuf.String(), hyphaName)
|
|
||||||
return fmt.Sprintf(`<a href="%s" class="%s">%s</a>`, href, class, html.EscapeString(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTextNode splits the `input` into two parts `textNode` and `rest` by the first encountered rune that resembles a span tag. If there is none, `textNode = input`, `rest = ""`. It handles escaping with backslash.
|
|
||||||
func getTextNode(input *bytes.Buffer) string {
|
|
||||||
var (
|
|
||||||
textNodeBuffer = bytes.Buffer{}
|
|
||||||
escaping = false
|
|
||||||
startsWith = func(t string) bool {
|
|
||||||
return bytes.HasPrefix(input.Bytes(), []byte(t))
|
|
||||||
}
|
|
||||||
couldBeLinkStart = func() bool {
|
|
||||||
return startsWith("https://") || startsWith("http://") || startsWith("gemini://") || startsWith("gopher://") || startsWith("ftp://")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// Always read the first byte in advance to avoid endless loops that kill computers (sad experience)
|
|
||||||
if input.Len() != 0 {
|
|
||||||
b, _ := input.ReadByte()
|
|
||||||
textNodeBuffer.WriteByte(b)
|
|
||||||
}
|
|
||||||
for input.Len() != 0 {
|
|
||||||
// Assume no error is possible because we check for length
|
|
||||||
b, _ := input.ReadByte()
|
|
||||||
if escaping {
|
|
||||||
textNodeBuffer.WriteByte(b)
|
|
||||||
escaping = false
|
|
||||||
} else if b == '\\' {
|
|
||||||
escaping = true
|
|
||||||
} else if strings.IndexByte("/*`^,![~", b) >= 0 {
|
|
||||||
input.UnreadByte()
|
|
||||||
break
|
|
||||||
} else if couldBeLinkStart() {
|
|
||||||
textNodeBuffer.WriteByte(b)
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
textNodeBuffer.WriteByte(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return textNodeBuffer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParagraphToHtml(hyphaName, input string) string {
|
|
||||||
var (
|
|
||||||
p = bytes.NewBufferString(input)
|
|
||||||
ret strings.Builder
|
|
||||||
// true = tag is opened, false = tag is not opened
|
|
||||||
tagState = map[spanTokenType]bool{
|
|
||||||
spanItalic: false,
|
|
||||||
spanBold: false,
|
|
||||||
spanMono: false,
|
|
||||||
spanSuper: false,
|
|
||||||
spanSub: false,
|
|
||||||
spanMark: false,
|
|
||||||
spanLink: false,
|
|
||||||
}
|
|
||||||
startsWith = func(t string) bool {
|
|
||||||
return bytes.HasPrefix(p.Bytes(), []byte(t))
|
|
||||||
}
|
|
||||||
noTagsActive = func() bool {
|
|
||||||
return !(tagState[spanItalic] || tagState[spanBold] || tagState[spanMono] || tagState[spanSuper] || tagState[spanSub] || tagState[spanMark] || tagState[spanLink])
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
for p.Len() != 0 {
|
|
||||||
switch {
|
|
||||||
case startsWith("//"):
|
|
||||||
ret.WriteString(tagFromState(spanItalic, tagState, "em", "//"))
|
|
||||||
p.Next(2)
|
|
||||||
case startsWith("**"):
|
|
||||||
ret.WriteString(tagFromState(spanBold, tagState, "strong", "**"))
|
|
||||||
p.Next(2)
|
|
||||||
case startsWith("`"):
|
|
||||||
ret.WriteString(tagFromState(spanMono, tagState, "code", "`"))
|
|
||||||
p.Next(1)
|
|
||||||
case startsWith("^"):
|
|
||||||
ret.WriteString(tagFromState(spanSuper, tagState, "sup", "^"))
|
|
||||||
p.Next(1)
|
|
||||||
case startsWith(",,"):
|
|
||||||
ret.WriteString(tagFromState(spanSub, tagState, "sub", ",,"))
|
|
||||||
p.Next(2)
|
|
||||||
case startsWith("!!"):
|
|
||||||
ret.WriteString(tagFromState(spanMark, tagState, "mark", "!!"))
|
|
||||||
p.Next(2)
|
|
||||||
case startsWith("~~"):
|
|
||||||
ret.WriteString(tagFromState(spanMark, tagState, "s", "~~"))
|
|
||||||
p.Next(2)
|
|
||||||
case startsWith("[["):
|
|
||||||
ret.WriteString(getLinkNode(p, hyphaName, true))
|
|
||||||
case (startsWith("https://") || startsWith("http://") || startsWith("gemini://") || startsWith("gopher://") || startsWith("ftp://")) && noTagsActive():
|
|
||||||
ret.WriteString(getLinkNode(p, hyphaName, false))
|
|
||||||
default:
|
|
||||||
ret.WriteString(html.EscapeString(getTextNode(p)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for stt, open := range tagState {
|
|
||||||
if open {
|
|
||||||
switch stt {
|
|
||||||
case spanItalic:
|
|
||||||
ret.WriteString(tagFromState(spanItalic, tagState, "em", "//"))
|
|
||||||
case spanBold:
|
|
||||||
ret.WriteString(tagFromState(spanBold, tagState, "strong", "**"))
|
|
||||||
case spanMono:
|
|
||||||
ret.WriteString(tagFromState(spanMono, tagState, "code", "`"))
|
|
||||||
case spanSuper:
|
|
||||||
ret.WriteString(tagFromState(spanSuper, tagState, "sup", "^"))
|
|
||||||
case spanSub:
|
|
||||||
ret.WriteString(tagFromState(spanSub, tagState, "sub", ",,"))
|
|
||||||
case spanMark:
|
|
||||||
ret.WriteString(tagFromState(spanMark, tagState, "mark", "!!"))
|
|
||||||
case spanStrike:
|
|
||||||
ret.WriteString(tagFromState(spanMark, tagState, "s", "~~"))
|
|
||||||
case spanLink:
|
|
||||||
ret.WriteString(tagFromState(spanLink, tagState, "a", "[["))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret.String()
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
const maxRecursionLevel = 3
|
|
||||||
|
|
||||||
func Parse(ast []Line, from, to int, recursionLevel int) (html string) {
|
|
||||||
if recursionLevel > maxRecursionLevel {
|
|
||||||
return "Transclusion depth limit"
|
|
||||||
}
|
|
||||||
for _, line := range ast {
|
|
||||||
if line.id >= from && (line.id <= to || to == 0) || line.id == -1 {
|
|
||||||
switch v := line.contents.(type) {
|
|
||||||
case Transclusion:
|
|
||||||
html += Transclude(v, recursionLevel)
|
|
||||||
case Img:
|
|
||||||
html += v.ToHtml()
|
|
||||||
case Table:
|
|
||||||
html += v.asHtml()
|
|
||||||
case *List:
|
|
||||||
html += v.RenderAsHtml()
|
|
||||||
case string:
|
|
||||||
html += v
|
|
||||||
default:
|
|
||||||
html += "<b class='error'>Unknown element.</b>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return html
|
|
||||||
}
|
|
231
markup/table.go
231
markup/table.go
@ -1,231 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
// "github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
var tableRe = regexp.MustCompile(`^table\s+{`)
|
|
||||||
|
|
||||||
func MatchesTable(line string) bool {
|
|
||||||
return tableRe.MatchString(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TableFromFirstLine(line, hyphaName string) *Table {
|
|
||||||
return &Table{
|
|
||||||
hyphaName: hyphaName,
|
|
||||||
caption: line[strings.IndexRune(line, '{')+1:],
|
|
||||||
rows: make([]*tableRow, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) Process(line string) (shouldGoBackToNormal bool) {
|
|
||||||
if strings.TrimSpace(line) == "}" && !t.inMultiline {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if !t.inMultiline {
|
|
||||||
t.pushRow()
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
inLink bool
|
|
||||||
skipNext bool
|
|
||||||
escaping bool
|
|
||||||
lookingForNonSpace = !t.inMultiline
|
|
||||||
countingColspan bool
|
|
||||||
)
|
|
||||||
for i, r := range line {
|
|
||||||
switch {
|
|
||||||
case skipNext:
|
|
||||||
skipNext = false
|
|
||||||
continue
|
|
||||||
|
|
||||||
case lookingForNonSpace && unicode.IsSpace(r):
|
|
||||||
case lookingForNonSpace && (r == '!' || r == '|'):
|
|
||||||
t.currCellMarker = r
|
|
||||||
t.currColspan = 1
|
|
||||||
lookingForNonSpace = false
|
|
||||||
countingColspan = true
|
|
||||||
case lookingForNonSpace:
|
|
||||||
t.currCellMarker = '^' // ^ represents implicit |, not part of syntax
|
|
||||||
t.currColspan = 1
|
|
||||||
lookingForNonSpace = false
|
|
||||||
t.currCellBuilder.WriteRune(r)
|
|
||||||
|
|
||||||
case escaping:
|
|
||||||
t.currCellBuilder.WriteRune(r)
|
|
||||||
case inLink && r == ']' && len(line)-1 > i && line[i+1] == ']':
|
|
||||||
t.currCellBuilder.WriteString("]]")
|
|
||||||
inLink = false
|
|
||||||
skipNext = true
|
|
||||||
case inLink:
|
|
||||||
t.currCellBuilder.WriteRune(r)
|
|
||||||
|
|
||||||
case t.inMultiline && r == '}':
|
|
||||||
t.inMultiline = false
|
|
||||||
case t.inMultiline && i == len(line)-1:
|
|
||||||
t.currCellBuilder.WriteRune('\n')
|
|
||||||
case t.inMultiline:
|
|
||||||
t.currCellBuilder.WriteRune(r)
|
|
||||||
|
|
||||||
// Not in multiline:
|
|
||||||
case (r == '|' || r == '!') && !countingColspan:
|
|
||||||
t.pushCell()
|
|
||||||
t.currCellMarker = r
|
|
||||||
t.currColspan = 1
|
|
||||||
countingColspan = true
|
|
||||||
case r == t.currCellMarker && (r == '|' || r == '!') && countingColspan:
|
|
||||||
t.currColspan++
|
|
||||||
case r == '{':
|
|
||||||
t.inMultiline = true
|
|
||||||
countingColspan = false
|
|
||||||
case r == '[' && len(line)-1 > i && line[i+1] == '[':
|
|
||||||
t.currCellBuilder.WriteString("[[")
|
|
||||||
inLink = true
|
|
||||||
skipNext = true
|
|
||||||
case i == len(line)-1:
|
|
||||||
t.pushCell()
|
|
||||||
default:
|
|
||||||
t.currCellBuilder.WriteRune(r)
|
|
||||||
countingColspan = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type Table struct {
|
|
||||||
// data
|
|
||||||
hyphaName string
|
|
||||||
caption string
|
|
||||||
rows []*tableRow
|
|
||||||
// state
|
|
||||||
inMultiline bool
|
|
||||||
// tmp
|
|
||||||
currCellMarker rune
|
|
||||||
currColspan uint
|
|
||||||
currCellBuilder strings.Builder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) pushRow() {
|
|
||||||
t.rows = append(t.rows, &tableRow{
|
|
||||||
cells: make([]*tableCell, 0),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) pushCell() {
|
|
||||||
tc := &tableCell{
|
|
||||||
content: t.currCellBuilder.String(),
|
|
||||||
colspan: t.currColspan,
|
|
||||||
}
|
|
||||||
switch t.currCellMarker {
|
|
||||||
case '|', '^':
|
|
||||||
tc.kind = tableCellDatum
|
|
||||||
case '!':
|
|
||||||
tc.kind = tableCellHeader
|
|
||||||
}
|
|
||||||
// We expect the table to have at least one row ready, so no nil-checking
|
|
||||||
tr := t.rows[len(t.rows)-1]
|
|
||||||
tr.cells = append(tr.cells, tc)
|
|
||||||
t.currCellBuilder = strings.Builder{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) asHtml() (html string) {
|
|
||||||
if t.caption != "" {
|
|
||||||
html += fmt.Sprintf("<caption>%s</caption>", t.caption)
|
|
||||||
}
|
|
||||||
if len(t.rows) > 0 && t.rows[0].looksLikeThead() {
|
|
||||||
html += fmt.Sprintf("<thead>%s</thead>", t.rows[0].asHtml(t.hyphaName))
|
|
||||||
t.rows = t.rows[1:]
|
|
||||||
}
|
|
||||||
html += "\n<tbody>\n"
|
|
||||||
for _, tr := range t.rows {
|
|
||||||
html += tr.asHtml(t.hyphaName)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(`<table>%s</tbody></table>`, html)
|
|
||||||
}
|
|
||||||
|
|
||||||
type tableRow struct {
|
|
||||||
cells []*tableCell
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *tableRow) asHtml(hyphaName string) (html string) {
|
|
||||||
for _, tc := range tr.cells {
|
|
||||||
html += tc.asHtml(hyphaName)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("<tr>%s</tr>\n", html)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Most likely, rows with more than two header cells are theads. I allow one extra datum cell for tables like this:
|
|
||||||
// | ! a ! b
|
|
||||||
// ! c | d | e
|
|
||||||
// ! f | g | h
|
|
||||||
func (tr *tableRow) looksLikeThead() bool {
|
|
||||||
var (
|
|
||||||
headerAmount = 0
|
|
||||||
datumAmount = 0
|
|
||||||
)
|
|
||||||
for _, tc := range tr.cells {
|
|
||||||
switch tc.kind {
|
|
||||||
case tableCellHeader:
|
|
||||||
headerAmount++
|
|
||||||
case tableCellDatum:
|
|
||||||
datumAmount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return headerAmount >= 2 && datumAmount <= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
type tableCell struct {
|
|
||||||
kind tableCellKind
|
|
||||||
colspan uint
|
|
||||||
content string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *tableCell) asHtml(hyphaName string) string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"<%[1]s %[2]s>%[3]s</%[1]s>\n",
|
|
||||||
tc.kind.tagName(),
|
|
||||||
tc.colspanAttribute(),
|
|
||||||
tc.contentAsHtml(hyphaName),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *tableCell) colspanAttribute() string {
|
|
||||||
if tc.colspan <= 1 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(`colspan="%d"`, tc.colspan)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *tableCell) contentAsHtml(hyphaName string) (html string) {
|
|
||||||
for _, line := range strings.Split(tc.content, "\n") {
|
|
||||||
if line = strings.TrimSpace(line); line != "" {
|
|
||||||
if html != "" {
|
|
||||||
html += `<br>`
|
|
||||||
}
|
|
||||||
html += ParagraphToHtml(hyphaName, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return html
|
|
||||||
}
|
|
||||||
|
|
||||||
type tableCellKind int
|
|
||||||
|
|
||||||
const (
|
|
||||||
tableCellUnknown tableCellKind = iota
|
|
||||||
tableCellHeader
|
|
||||||
tableCellDatum
|
|
||||||
)
|
|
||||||
|
|
||||||
func (tck tableCellKind) tagName() string {
|
|
||||||
switch tck {
|
|
||||||
case tableCellHeader:
|
|
||||||
return "th"
|
|
||||||
case tableCellDatum:
|
|
||||||
return "td"
|
|
||||||
default:
|
|
||||||
return "p"
|
|
||||||
}
|
|
||||||
}
|
|
38
markup/testdata/test.myco
vendored
38
markup/testdata/test.myco
vendored
@ -1,38 +0,0 @@
|
|||||||
# 1
|
|
||||||
## 2
|
|
||||||
### 3
|
|
||||||
> quote
|
|
||||||
|
|
||||||
* li 1
|
|
||||||
* li 2
|
|
||||||
text
|
|
||||||
more text
|
|
||||||
=> Pear some link
|
|
||||||
|
|
||||||
* li\n"+
|
|
||||||
```alt text goes here
|
|
||||||
=> preformatted text
|
|
||||||
where markup is not lexed
|
|
||||||
```it ends here"
|
|
||||||
=>linking
|
|
||||||
|
|
||||||
text
|
|
||||||
```
|
|
||||||
()
|
|
||||||
/\
|
|
||||||
```
|
|
||||||
<= Apple : 1..3
|
|
||||||
|
|
||||||
img {
|
|
||||||
hypha1
|
|
||||||
hypha2|
|
|
||||||
hypha3| 60
|
|
||||||
hypha4| { line1
|
|
||||||
line2
|
|
||||||
} this is ignored
|
|
||||||
|
|
||||||
hypha5| {
|
|
||||||
state of minnesota
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Function that returns a function that can strip `prefix` and trim whitespace when called.
|
|
||||||
func remover(prefix string) func(string) string {
|
|
||||||
return func(l string) string {
|
|
||||||
return strings.TrimSpace(strings.TrimPrefix(l, prefix))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove #, ## or ### from beginning of `line`.
|
|
||||||
func removeHeadingOctothorps(line string) string {
|
|
||||||
f := remover("#")
|
|
||||||
return f(f(f(line)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a canonical representation of a hypha `name`.
|
|
||||||
func canonicalName(name string) string {
|
|
||||||
return strings.ToLower(strings.ReplaceAll(strings.TrimSpace(name), " ", "_"))
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const xclError = -9
|
|
||||||
|
|
||||||
// Transclusion is used by markup parser to remember what hyphae shall be transcluded.
|
|
||||||
type Transclusion struct {
|
|
||||||
name string
|
|
||||||
from int // inclusive
|
|
||||||
to int // inclusive
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transclude transcludes `xcl` and returns html representation.
|
|
||||||
func Transclude(xcl Transclusion, recursionLevel int) (html string) {
|
|
||||||
recursionLevel++
|
|
||||||
tmptOk := `<section class="transclusion transclusion_ok">
|
|
||||||
<a class="transclusion__link" href="/page/%s">%s</a>
|
|
||||||
<div class="transclusion__content">%s</div>
|
|
||||||
</section>`
|
|
||||||
tmptFailed := `<section class="transclusion transclusion_failed">
|
|
||||||
<p class="error">Hypha <a class="wikilink_new" href="/page/%s">%s</a> does not exist</p>
|
|
||||||
</section>`
|
|
||||||
if xcl.from == xclError || xcl.to == xclError || xcl.from > xcl.to {
|
|
||||||
return fmt.Sprintf(tmptFailed, xcl.name, xcl.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
rawText, binaryHtml, err := HyphaAccess(xcl.name)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Sprintf(tmptFailed, xcl.name, xcl.name)
|
|
||||||
}
|
|
||||||
md := Doc(xcl.name, rawText)
|
|
||||||
xclText := Parse(md.lex(), xcl.from, xcl.to, recursionLevel)
|
|
||||||
return fmt.Sprintf(tmptOk, xcl.name, xcl.name, binaryHtml+xclText)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Grammar from hypha ‘transclusion’:
|
|
||||||
transclusion_line ::= transclusion_token hypha_name LWS* [":" LWS* range LWS*]
|
|
||||||
transclusion_token ::= "<=" LWS+
|
|
||||||
hypha_name ::= canonical_name | noncanonical_name
|
|
||||||
range ::= id | (from_id two_dots to_id) | (from_id two_dots) | (two_dots to_id)
|
|
||||||
two_dots ::= ".."
|
|
||||||
*/
|
|
||||||
|
|
||||||
func parseTransclusion(line, hyphaName string) (xclusion Transclusion) {
|
|
||||||
line = strings.TrimSpace(remover("<=")(line))
|
|
||||||
if line == "" {
|
|
||||||
return Transclusion{"", xclError, xclError}
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.ContainsRune(line, ':') {
|
|
||||||
parts := strings.SplitN(line, ":", 2)
|
|
||||||
xclusion.name = xclCanonicalName(hyphaName, strings.TrimSpace(parts[0]))
|
|
||||||
selector := strings.TrimSpace(parts[1])
|
|
||||||
xclusion.from, xclusion.to = parseSelector(selector)
|
|
||||||
} else {
|
|
||||||
xclusion.name = xclCanonicalName(hyphaName, strings.TrimSpace(line))
|
|
||||||
}
|
|
||||||
return xclusion
|
|
||||||
}
|
|
||||||
|
|
||||||
func xclCanonicalName(hyphaName, xclName string) string {
|
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(xclName, "./"):
|
|
||||||
return canonicalName(path.Join(hyphaName, strings.TrimPrefix(xclName, "./")))
|
|
||||||
case strings.HasPrefix(xclName, "../"):
|
|
||||||
return canonicalName(path.Join(path.Dir(hyphaName), strings.TrimPrefix(xclName, "../")))
|
|
||||||
default:
|
|
||||||
return canonicalName(xclName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point:
|
|
||||||
// selector ::= id
|
|
||||||
// | from ".."
|
|
||||||
// | from ".." to
|
|
||||||
// | ".." to
|
|
||||||
// If it is not, return (xclError, xclError).
|
|
||||||
func parseSelector(selector string) (from, to int) {
|
|
||||||
if selector == "" {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
if strings.Contains(selector, "..") {
|
|
||||||
parts := strings.Split(selector, "..")
|
|
||||||
|
|
||||||
var (
|
|
||||||
fromStr = strings.TrimSpace(parts[0])
|
|
||||||
from, fromErr = strconv.Atoi(fromStr)
|
|
||||||
toStr = strings.TrimSpace(parts[1])
|
|
||||||
to, toErr = strconv.Atoi(toStr)
|
|
||||||
)
|
|
||||||
if fromStr == "" && toStr == "" {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
if fromErr == nil || toErr == nil {
|
|
||||||
return from, to
|
|
||||||
}
|
|
||||||
} else if id, err := strconv.Atoi(selector); err == nil {
|
|
||||||
return id, id
|
|
||||||
}
|
|
||||||
return xclError, xclError
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
Subproject commit 515634c88a51a616f486dfefc2a7b9e6ca692689
|
|
34
name.go
34
name.go
@ -1,34 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.sr.ht/~adnano/go-gemini"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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+"/"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("HyphaNameFromRq: no matching action passed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 ""
|
|
||||||
}
|
|
@ -4,16 +4,16 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/markup"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/views"
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycomarkup/globals"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
markup.HyphaExists = func(hyphaName string) bool {
|
globals.HyphaExists = func(hyphaName string) bool {
|
||||||
return hyphae.ByName(hyphaName).Exists
|
return hyphae.ByName(hyphaName).Exists
|
||||||
}
|
}
|
||||||
markup.HyphaAccess = func(hyphaName string) (rawText, binaryBlock string, err error) {
|
globals.HyphaAccess = func(hyphaName string) (rawText, binaryBlock string, err error) {
|
||||||
if h := hyphae.ByName(hyphaName); h.Exists {
|
if h := hyphae.ByName(hyphaName); h.Exists {
|
||||||
rawText, err = FetchTextPart(h)
|
rawText, err = FetchTextPart(h)
|
||||||
if h.BinaryPath != "" {
|
if h.BinaryPath != "" {
|
||||||
@ -24,15 +24,9 @@ func init() {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
markup.HyphaIterate = func(λ func(string)) {
|
globals.HyphaIterate = func(λ func(string)) {
|
||||||
for h := range hyphae.YieldExistingHyphae() {
|
for h := range hyphae.YieldExistingHyphae() {
|
||||||
λ(h.Name)
|
λ(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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package shroom
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
@ -13,7 +14,6 @@ import (
|
|||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
"github.com/bouncepaw/mycorrhiza/mimetype"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"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) {
|
func UploadText(h *hyphae.Hypha, data []byte, u *user.User) (hop *history.HistoryOp, errtitle string) {
|
||||||
@ -56,7 +56,7 @@ func UploadBinary(h *hyphae.Hypha, mime string, file multipart.File, u *user.Use
|
|||||||
// uploadHelp is a helper function for UploadText and UploadBinary
|
// 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) {
|
func uploadHelp(h *hyphae.Hypha, hop *history.HistoryOp, ext string, data []byte, u *user.User) (*history.HistoryOp, string) {
|
||||||
var (
|
var (
|
||||||
fullPath = filepath.Join(util.WikiDir, h.Name+ext)
|
fullPath = filepath.Join(cfg.WikiDir, h.Name+ext)
|
||||||
originalFullPath = &h.TextPath
|
originalFullPath = &h.TextPath
|
||||||
)
|
)
|
||||||
if hop.Type == history.TypeEditBinary {
|
if hop.Type == history.TypeEditBinary {
|
||||||
|
@ -4,9 +4,8 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"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.
|
// FetchTextPart tries to read text file of the given hypha. If there is no file, empty string is returned.
|
||||||
@ -24,15 +23,15 @@ func FetchTextPart(h *hyphae.Hypha) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SetHeaderLinks() {
|
func SetHeaderLinks() {
|
||||||
if userLinksHypha := hyphae.ByName(util.HeaderLinksHypha); !userLinksHypha.Exists {
|
if userLinksHypha := hyphae.ByName(cfg.HeaderLinksHypha); !userLinksHypha.Exists {
|
||||||
util.SetDefaultHeaderLinks()
|
cfg.SetDefaultHeaderLinks()
|
||||||
} else {
|
} else {
|
||||||
contents, err := ioutil.ReadFile(userLinksHypha.TextPath)
|
contents, err := ioutil.ReadFile(userLinksHypha.TextPath)
|
||||||
if err != nil || len(contents) == 0 {
|
if err != nil || len(contents) == 0 {
|
||||||
util.SetDefaultHeaderLinks()
|
cfg.SetDefaultHeaderLinks()
|
||||||
} else {
|
} else {
|
||||||
text := string(contents)
|
text := string(contents)
|
||||||
util.ParseHeaderLinks(text, markup.Rocketlink)
|
cfg.ParseHeaderLinks(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,30 +2,31 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/files"
|
"github.com/bouncepaw/mycorrhiza/files"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitUserDatabase checks the configuration for auth methods and loads users
|
// InitUserDatabase checks the configuration for auth methods and loads users
|
||||||
// if necessary. Call it during initialization.
|
// if necessary. Call it during initialization.
|
||||||
func InitUserDatabase() {
|
func InitUserDatabase() {
|
||||||
AuthUsed = util.UseFixedAuth || util.UseRegistration
|
AuthUsed = cfg.UseFixedAuth || cfg.UseRegistration
|
||||||
|
|
||||||
if AuthUsed && (util.FixedCredentialsPath != "" || util.RegistrationCredentialsPath != "") {
|
if AuthUsed && (cfg.FixedAuthCredentialsPath != "" || cfg.RegistrationCredentialsPath != "") {
|
||||||
ReadUsersFromFilesystem()
|
ReadUsersFromFilesystem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadUsersFromFilesystem reads all user information from filesystem and stores it internally.
|
// ReadUsersFromFilesystem reads all user information from filesystem and stores it internally.
|
||||||
func ReadUsersFromFilesystem() {
|
func ReadUsersFromFilesystem() {
|
||||||
if util.UseFixedAuth {
|
if cfg.UseFixedAuth {
|
||||||
rememberUsers(usersFromFixedCredentials())
|
rememberUsers(usersFromFixedCredentials())
|
||||||
}
|
}
|
||||||
if util.UseRegistration {
|
if cfg.UseRegistration {
|
||||||
rememberUsers(usersFromRegistrationCredentials())
|
rememberUsers(usersFromRegistrationCredentials())
|
||||||
}
|
}
|
||||||
readTokensToUsers()
|
readTokensToUsers()
|
||||||
@ -44,6 +45,7 @@ func usersFromFile(path string, source UserSource) (users []*User) {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
|
u.Name = util.CanonicalName(u.Name)
|
||||||
u.Source = source
|
u.Source = source
|
||||||
}
|
}
|
||||||
return users
|
return users
|
||||||
|
@ -2,6 +2,7 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -39,8 +40,8 @@ func Register(username, password string) error {
|
|||||||
username = util.CanonicalName(username)
|
username = util.CanonicalName(username)
|
||||||
log.Println("Attempt to register user", username)
|
log.Println("Attempt to register user", username)
|
||||||
switch {
|
switch {
|
||||||
case CountRegistered() >= util.LimitRegistration && util.LimitRegistration > 0:
|
case CountRegistered() >= cfg.LimitRegistration && cfg.LimitRegistration > 0:
|
||||||
i := strconv.Itoa(util.LimitRegistration)
|
i := strconv.Itoa(cfg.LimitRegistration)
|
||||||
log.Println("Limit reached: " + i)
|
log.Println("Limit reached: " + i)
|
||||||
return errors.New("Reached the limit of registered users: " + i)
|
return errors.New("Reached the limit of registered users: " + i)
|
||||||
case HasUsername(username):
|
case HasUsername(username):
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/go-ini/ini"
|
|
||||||
)
|
|
||||||
|
|
||||||
// See https://mycorrhiza.lesarbr.es/hypha/configuration/fields
|
|
||||||
type Config struct {
|
|
||||||
WikiName string
|
|
||||||
NaviTitleIcon string
|
|
||||||
Hyphae
|
|
||||||
Network
|
|
||||||
Authorization
|
|
||||||
}
|
|
||||||
|
|
||||||
type Hyphae struct {
|
|
||||||
HomeHypha string
|
|
||||||
UserHypha string
|
|
||||||
HeaderLinksHypha string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Network struct {
|
|
||||||
HTTPPort uint64
|
|
||||||
URL string
|
|
||||||
GeminiCertificatePath string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Authorization struct {
|
|
||||||
UseFixedAuth bool
|
|
||||||
FixedAuthCredentialsPath string
|
|
||||||
|
|
||||||
UseRegistration bool
|
|
||||||
RegistrationCredentialsPath string
|
|
||||||
LimitRegistration uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadConfigFile(path string) {
|
|
||||||
cfg := &Config{
|
|
||||||
WikiName: "MycorrhizaWiki",
|
|
||||||
NaviTitleIcon: "🍄",
|
|
||||||
Hyphae: Hyphae{
|
|
||||||
HomeHypha: "home",
|
|
||||||
UserHypha: "u",
|
|
||||||
HeaderLinksHypha: "",
|
|
||||||
},
|
|
||||||
Network: Network{
|
|
||||||
HTTPPort: 1737,
|
|
||||||
URL: "",
|
|
||||||
GeminiCertificatePath: "",
|
|
||||||
},
|
|
||||||
Authorization: Authorization{
|
|
||||||
UseFixedAuth: false,
|
|
||||||
FixedAuthCredentialsPath: "",
|
|
||||||
|
|
||||||
UseRegistration: false,
|
|
||||||
RegistrationCredentialsPath: "",
|
|
||||||
LimitRegistration: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if path != "" {
|
|
||||||
path, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("cannot expand config file path: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Loading config at", path)
|
|
||||||
err = ini.MapTo(cfg, path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map the struct to the global variables
|
|
||||||
SiteName = cfg.WikiName
|
|
||||||
SiteNavIcon = cfg.NaviTitleIcon
|
|
||||||
HomePage = cfg.HomeHypha
|
|
||||||
UserHypha = cfg.UserHypha
|
|
||||||
HeaderLinksHypha = cfg.HeaderLinksHypha
|
|
||||||
ServerPort = strconv.FormatUint(cfg.HTTPPort, 10)
|
|
||||||
URL = cfg.URL
|
|
||||||
GeminiCertPath = cfg.GeminiCertificatePath
|
|
||||||
UseFixedAuth = cfg.UseFixedAuth
|
|
||||||
FixedCredentialsPath = cfg.FixedAuthCredentialsPath
|
|
||||||
UseRegistration = cfg.UseRegistration
|
|
||||||
RegistrationCredentialsPath = cfg.RegistrationCredentialsPath
|
|
||||||
LimitRegistration = int(cfg.LimitRegistration)
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SetDefaultHeaderLinks() {
|
|
||||||
HeaderLinks = []HeaderLink{
|
|
||||||
{"/", SiteName},
|
|
||||||
{"/recent-changes", "Recent changes"},
|
|
||||||
{"/list", "All hyphae"},
|
|
||||||
{"/random", "Random"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rocketlinkλ is markup.Rocketlink. You have to pass it like that to avoid cyclical dependency.
|
|
||||||
func ParseHeaderLinks(text string, rocketlinkλ func(string, string) (string, string, string)) {
|
|
||||||
HeaderLinks = []HeaderLink{}
|
|
||||||
for _, line := range strings.Split(text, "\n") {
|
|
||||||
if strings.HasPrefix(line, "=>") {
|
|
||||||
href, text, _ := rocketlinkλ(line, HeaderLinksHypha)
|
|
||||||
HeaderLinks = append(HeaderLinks, HeaderLink{
|
|
||||||
Href: href,
|
|
||||||
Display: text,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type HeaderLink struct {
|
|
||||||
Href string
|
|
||||||
Display string
|
|
||||||
}
|
|
||||||
|
|
||||||
var HeaderLinks []HeaderLink
|
|
51
util/util.go
51
util/util.go
@ -3,34 +3,19 @@ package util
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: make names match to fields of config file
|
func PrepareRq(rq *http.Request) {
|
||||||
var (
|
log.Println(rq.RequestURI)
|
||||||
SiteName string
|
rq.URL.Path = strings.TrimSuffix(rq.URL.Path, "/")
|
||||||
SiteNavIcon string
|
}
|
||||||
|
|
||||||
HomePage string
|
|
||||||
UserHypha string
|
|
||||||
HeaderLinksHypha string
|
|
||||||
|
|
||||||
ServerPort string
|
|
||||||
URL string
|
|
||||||
GeminiCertPath string
|
|
||||||
|
|
||||||
WikiDir string
|
|
||||||
ConfigFilePath string
|
|
||||||
|
|
||||||
UseFixedAuth bool
|
|
||||||
FixedCredentialsPath string
|
|
||||||
UseRegistration bool
|
|
||||||
RegistrationCredentialsPath string
|
|
||||||
LimitRegistration int
|
|
||||||
)
|
|
||||||
|
|
||||||
// LettersNumbersOnly keeps letters and numbers only in the given string.
|
// LettersNumbersOnly keeps letters and numbers only in the given string.
|
||||||
func LettersNumbersOnly(s string) string {
|
func LettersNumbersOnly(s string) string {
|
||||||
@ -52,8 +37,8 @@ func LettersNumbersOnly(s string) string {
|
|||||||
|
|
||||||
// ShorterPath is used by handlerList to display shorter path to the files. It simply strips WikiDir.
|
// ShorterPath is used by handlerList to display shorter path to the files. It simply strips WikiDir.
|
||||||
func ShorterPath(path string) string {
|
func ShorterPath(path string) string {
|
||||||
if strings.HasPrefix(path, WikiDir) {
|
if strings.HasPrefix(path, cfg.WikiDir) {
|
||||||
tmp := strings.TrimPrefix(path, WikiDir)
|
tmp := strings.TrimPrefix(path, cfg.WikiDir)
|
||||||
if tmp == "" {
|
if tmp == "" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -103,9 +88,9 @@ func CanonicalName(name string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HyphaPattern is a pattern which all hyphae must match.
|
// HyphaPattern is a pattern which all hyphae must match.
|
||||||
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`)
|
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}]+`)
|
||||||
|
|
||||||
var UsernamePattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}/]+`)
|
var UsernamePattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}/]+`)
|
||||||
|
|
||||||
// IsCanonicalName checks if the `name` is canonical.
|
// IsCanonicalName checks if the `name` is canonical.
|
||||||
func IsCanonicalName(name string) bool {
|
func IsCanonicalName(name string) bool {
|
||||||
@ -113,5 +98,17 @@ func IsCanonicalName(name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsPossibleUsername(username string) bool {
|
func IsPossibleUsername(username string) bool {
|
||||||
return UsernamePattern.MatchString(strings.TrimSpace(username))
|
return username != "anon" && UsernamePattern.MatchString(strings.TrimSpace(username))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 CanonicalName(strings.TrimPrefix(p, "/"+action+"/"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Println("HyphaNameFromRq: this request is invalid, fallback to home hypha")
|
||||||
|
return cfg.HomeHypha
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
{% import "net/http" %}
|
{% import "net/http" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
||||||
|
|
||||||
{% func RegisterHTML(rq *http.Request) %}
|
{% func RegisterHTML(rq *http.Request) %}
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<section>
|
<section>
|
||||||
{% if util.UseRegistration %}
|
{% if cfg.UseRegistration %}
|
||||||
<form class="modal" method="post" action="/register?{%s rq.URL.RawQuery %}" id="register-form" enctype="multipart/form-data" autocomplete="off">
|
<form class="modal" method="post" action="/register?{%s rq.URL.RawQuery %}" id="register-form" enctype="multipart/form-data" autocomplete="off">
|
||||||
<fieldset class="modal__fieldset">
|
<fieldset class="modal__fieldset">
|
||||||
<legend class="modal__title">Register to {%s util.SiteName %}</legend>
|
<legend class="modal__title">Register to {%s cfg.WikiName %}</legend>
|
||||||
|
|
||||||
<label for="register-form__username">Username</label>
|
<label for="register-form__username">Username</label>
|
||||||
<br>
|
<br>
|
||||||
@ -20,16 +20,16 @@
|
|||||||
<input type="password" required name="password">
|
<input type="password" required name="password">
|
||||||
<p>The server stores your password in an encrypted form; even administrators cannot read it.</p>
|
<p>The server stores your password in an encrypted form; even administrators cannot read it.</p>
|
||||||
<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>
|
<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">
|
<input class="btn" type="submit" value="Register">
|
||||||
<a class="modal__action modal__cancel" href="/">Cancel</a>
|
<a class="btn btn_weak" href="/{%s rq.URL.RawQuery %}">Cancel</a>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
{% elseif util.UseFixedAuth %}
|
{% elseif cfg.UseFixedAuth %}
|
||||||
<p>Administrators have forbidden registration for this wiki. Administrators can make an account for you by hand; contact them.</p>
|
<p>Administrators have forbidden registration for this wiki. Administrators can make an account for you by hand; contact them.</p>
|
||||||
<p><a href="{%s rq.URL.RawQuery %}">← Go back</a></p>
|
<p><a href="/{%s rq.URL.RawQuery %}">← Go back</a></p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p>
|
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p>
|
||||||
<p><a href="{%s rq.URL.RawQuery %}">← Go back</a></p>
|
<p><a href="/{%s rq.URL.RawQuery %}">← Go back</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
@ -43,8 +43,7 @@
|
|||||||
{% if user.AuthUsed %}
|
{% if user.AuthUsed %}
|
||||||
<form class="modal" method="post" action="/login-data" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
<form class="modal" method="post" action="/login-data" id="login-form" enctype="multipart/form-data" autocomplete="on">
|
||||||
<fieldset class="modal__fieldset">
|
<fieldset class="modal__fieldset">
|
||||||
<legend class="modal__title">Log in to {%s util.SiteName %}</legend>
|
<legend class="modal__title">Log in to {%s cfg.WikiName %}</legend>
|
||||||
<p>Use the data you were given by an administrator.</p>
|
|
||||||
<label for="login-form__username">Username</label>
|
<label for="login-form__username">Username</label>
|
||||||
<br>
|
<br>
|
||||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
||||||
@ -53,8 +52,8 @@
|
|||||||
<br>
|
<br>
|
||||||
<input type="password" required name="password" autocomplete="current-password">
|
<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>
|
<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">
|
<input class="btn" type="submit" value="Log in">
|
||||||
<a class="modal__action modal__cancel" href="/">Cancel</a>
|
<a class="btn btn_weak" href="/">Cancel</a>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -11,7 +11,7 @@ import "net/http"
|
|||||||
import "github.com/bouncepaw/mycorrhiza/user"
|
import "github.com/bouncepaw/mycorrhiza/user"
|
||||||
|
|
||||||
//line views/auth.qtpl:3
|
//line views/auth.qtpl:3
|
||||||
import "github.com/bouncepaw/mycorrhiza/util"
|
import "github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
|
||||||
//line views/auth.qtpl:5
|
//line views/auth.qtpl:5
|
||||||
import (
|
import (
|
||||||
@ -35,7 +35,7 @@ func StreamRegisterHTML(qw422016 *qt422016.Writer, rq *http.Request) {
|
|||||||
<section>
|
<section>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:9
|
//line views/auth.qtpl:9
|
||||||
if util.UseRegistration {
|
if cfg.UseRegistration {
|
||||||
//line views/auth.qtpl:9
|
//line views/auth.qtpl:9
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<form class="modal" method="post" action="/register?`)
|
<form class="modal" method="post" action="/register?`)
|
||||||
@ -46,7 +46,7 @@ func StreamRegisterHTML(qw422016 *qt422016.Writer, rq *http.Request) {
|
|||||||
<fieldset class="modal__fieldset">
|
<fieldset class="modal__fieldset">
|
||||||
<legend class="modal__title">Register to `)
|
<legend class="modal__title">Register to `)
|
||||||
//line views/auth.qtpl:12
|
//line views/auth.qtpl:12
|
||||||
qw422016.E().S(util.SiteName)
|
qw422016.E().S(cfg.WikiName)
|
||||||
//line views/auth.qtpl:12
|
//line views/auth.qtpl:12
|
||||||
qw422016.N().S(`</legend>
|
qw422016.N().S(`</legend>
|
||||||
|
|
||||||
@ -59,17 +59,21 @@ func StreamRegisterHTML(qw422016 *qt422016.Writer, rq *http.Request) {
|
|||||||
<input type="password" required name="password">
|
<input type="password" required name="password">
|
||||||
<p>The server stores your password in an encrypted form; even administrators cannot read it.</p>
|
<p>The server stores your password in an encrypted form; even administrators cannot read it.</p>
|
||||||
<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>
|
<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">
|
<input class="btn" type="submit" value="Register">
|
||||||
<a class="modal__action modal__cancel" href="/">Cancel</a>
|
<a class="btn btn_weak" href="/`)
|
||||||
|
//line views/auth.qtpl:24
|
||||||
|
qw422016.E().S(rq.URL.RawQuery)
|
||||||
|
//line views/auth.qtpl:24
|
||||||
|
qw422016.N().S(`">Cancel</a>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:27
|
//line views/auth.qtpl:27
|
||||||
} else if util.UseFixedAuth {
|
} else if cfg.UseFixedAuth {
|
||||||
//line views/auth.qtpl:27
|
//line views/auth.qtpl:27
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p>Administrators have forbidden registration for this wiki. Administrators can make an account for you by hand; contact them.</p>
|
<p>Administrators have forbidden registration for this wiki. Administrators can make an account for you by hand; contact them.</p>
|
||||||
<p><a href="`)
|
<p><a href="/`)
|
||||||
//line views/auth.qtpl:29
|
//line views/auth.qtpl:29
|
||||||
qw422016.E().S(rq.URL.RawQuery)
|
qw422016.E().S(rq.URL.RawQuery)
|
||||||
//line views/auth.qtpl:29
|
//line views/auth.qtpl:29
|
||||||
@ -80,7 +84,7 @@ func StreamRegisterHTML(qw422016 *qt422016.Writer, rq *http.Request) {
|
|||||||
//line views/auth.qtpl:30
|
//line views/auth.qtpl:30
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p>
|
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p>
|
||||||
<p><a href="`)
|
<p><a href="/`)
|
||||||
//line views/auth.qtpl:32
|
//line views/auth.qtpl:32
|
||||||
qw422016.E().S(rq.URL.RawQuery)
|
qw422016.E().S(rq.URL.RawQuery)
|
||||||
//line views/auth.qtpl:32
|
//line views/auth.qtpl:32
|
||||||
@ -139,10 +143,9 @@ func StreamLoginHTML(qw422016 *qt422016.Writer) {
|
|||||||
<fieldset class="modal__fieldset">
|
<fieldset class="modal__fieldset">
|
||||||
<legend class="modal__title">Log in to `)
|
<legend class="modal__title">Log in to `)
|
||||||
//line views/auth.qtpl:46
|
//line views/auth.qtpl:46
|
||||||
qw422016.E().S(util.SiteName)
|
qw422016.E().S(cfg.WikiName)
|
||||||
//line views/auth.qtpl:46
|
//line views/auth.qtpl:46
|
||||||
qw422016.N().S(`</legend>
|
qw422016.N().S(`</legend>
|
||||||
<p>Use the data you were given by an administrator.</p>
|
|
||||||
<label for="login-form__username">Username</label>
|
<label for="login-form__username">Username</label>
|
||||||
<br>
|
<br>
|
||||||
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
|
||||||
@ -151,182 +154,182 @@ func StreamLoginHTML(qw422016 *qt422016.Writer) {
|
|||||||
<br>
|
<br>
|
||||||
<input type="password" required name="password" autocomplete="current-password">
|
<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>
|
<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">
|
<input class="btn" type="submit" value="Log in">
|
||||||
<a class="modal__action modal__cancel" href="/">Cancel</a>
|
<a class="btn btn_weak" href="/">Cancel</a>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:60
|
//line views/auth.qtpl:59
|
||||||
} else {
|
} else {
|
||||||
//line views/auth.qtpl:60
|
//line views/auth.qtpl:59
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p>
|
<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>
|
<p><a class="modal__cancel" href="/">← Go home</a></p>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:63
|
//line views/auth.qtpl:62
|
||||||
}
|
}
|
||||||
//line views/auth.qtpl:63
|
//line views/auth.qtpl:62
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
func WriteLoginHTML(qq422016 qtio422016.Writer) {
|
func WriteLoginHTML(qq422016 qtio422016.Writer) {
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
StreamLoginHTML(qw422016)
|
StreamLoginHTML(qw422016)
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
func LoginHTML() string {
|
func LoginHTML() string {
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
WriteLoginHTML(qb422016)
|
WriteLoginHTML(qb422016)
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/auth.qtpl:67
|
//line views/auth.qtpl:66
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/auth.qtpl:69
|
//line views/auth.qtpl:68
|
||||||
func StreamLoginErrorHTML(qw422016 *qt422016.Writer, err string) {
|
func StreamLoginErrorHTML(qw422016 *qt422016.Writer, err string) {
|
||||||
//line views/auth.qtpl:69
|
//line views/auth.qtpl:68
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<section>
|
<section>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:73
|
//line views/auth.qtpl:72
|
||||||
switch err {
|
switch err {
|
||||||
//line views/auth.qtpl:74
|
//line views/auth.qtpl:73
|
||||||
case "unknown username":
|
case "unknown username":
|
||||||
//line views/auth.qtpl:74
|
//line views/auth.qtpl:73
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p class="error">Unknown username.</p>
|
<p class="error">Unknown username.</p>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:76
|
//line views/auth.qtpl:75
|
||||||
case "wrong password":
|
case "wrong password":
|
||||||
//line views/auth.qtpl:76
|
//line views/auth.qtpl:75
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p class="error">Wrong password.</p>
|
<p class="error">Wrong password.</p>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:78
|
//line views/auth.qtpl:77
|
||||||
default:
|
default:
|
||||||
//line views/auth.qtpl:78
|
//line views/auth.qtpl:77
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p class="error">`)
|
<p class="error">`)
|
||||||
//line views/auth.qtpl:79
|
//line views/auth.qtpl:78
|
||||||
qw422016.E().S(err)
|
qw422016.E().S(err)
|
||||||
//line views/auth.qtpl:79
|
//line views/auth.qtpl:78
|
||||||
qw422016.N().S(`</p>
|
qw422016.N().S(`</p>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:80
|
//line views/auth.qtpl:79
|
||||||
}
|
}
|
||||||
//line views/auth.qtpl:80
|
//line views/auth.qtpl:79
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p><a href="/login">← Try again</a></p>
|
<p><a href="/login">← Try again</a></p>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
func WriteLoginErrorHTML(qq422016 qtio422016.Writer, err string) {
|
func WriteLoginErrorHTML(qq422016 qtio422016.Writer, err string) {
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
StreamLoginErrorHTML(qw422016, err)
|
StreamLoginErrorHTML(qw422016, err)
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
func LoginErrorHTML(err string) string {
|
func LoginErrorHTML(err string) string {
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
WriteLoginErrorHTML(qb422016, err)
|
WriteLoginErrorHTML(qb422016, err)
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/auth.qtpl:85
|
//line views/auth.qtpl:84
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/auth.qtpl:87
|
//line views/auth.qtpl:86
|
||||||
func StreamLogoutHTML(qw422016 *qt422016.Writer, can bool) {
|
func StreamLogoutHTML(qw422016 *qt422016.Writer, can bool) {
|
||||||
//line views/auth.qtpl:87
|
//line views/auth.qtpl:86
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<section>
|
<section>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:91
|
//line views/auth.qtpl:90
|
||||||
if can {
|
if can {
|
||||||
//line views/auth.qtpl:91
|
//line views/auth.qtpl:90
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<h1>Log out?</h1>
|
<h1>Log out?</h1>
|
||||||
<p><a href="/logout-confirm"><strong>Confirm</strong></a></p>
|
<p><a href="/logout-confirm"><strong>Confirm</strong></a></p>
|
||||||
<p><a href="/">Cancel</a></p>
|
<p><a href="/">Cancel</a></p>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:95
|
//line views/auth.qtpl:94
|
||||||
} else {
|
} else {
|
||||||
//line views/auth.qtpl:95
|
//line views/auth.qtpl:94
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p>You cannot log out because you are not logged in.</p>
|
<p>You cannot log out because you are not logged in.</p>
|
||||||
<p><a href="/login">Login</a></p>
|
<p><a href="/login">Login</a></p>
|
||||||
<p><a href="/login">← Home</a></p>
|
<p><a href="/login">← Home</a></p>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:99
|
//line views/auth.qtpl:98
|
||||||
}
|
}
|
||||||
//line views/auth.qtpl:99
|
//line views/auth.qtpl:98
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
func WriteLogoutHTML(qq422016 qtio422016.Writer, can bool) {
|
func WriteLogoutHTML(qq422016 qtio422016.Writer, can bool) {
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
StreamLogoutHTML(qw422016, can)
|
StreamLogoutHTML(qw422016, can)
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
func LogoutHTML(can bool) string {
|
func LogoutHTML(can bool) string {
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
WriteLogoutHTML(qb422016, can)
|
WriteLogoutHTML(qb422016, can)
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/auth.qtpl:103
|
//line views/auth.qtpl:102
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{% import "net/http" %}
|
{% import "net/http" %}
|
||||||
|
|
||||||
|
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
||||||
@ -44,7 +45,7 @@ if err != nil {
|
|||||||
recent changes
|
recent changes
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<p><img class="icon" width="20" height="20" src="/static/icon/feed">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>
|
<p><img class="icon" width="20" height="20" src="/assets/icon/feed">Subscribe via <a href="/recent-changes-rss">RSS</a>, <a href="/recent-changes-atom">Atom</a> or <a href="/recent-changes-json">JSON feed</a>.</p>
|
||||||
|
|
||||||
{% comment %}
|
{% comment %}
|
||||||
Here I am, willing to add some accessibility using ARIA. Turns out,
|
Here I am, willing to add some accessibility using ARIA. Turns out,
|
||||||
@ -76,7 +77,7 @@ if err != nil {
|
|||||||
<li class="rc-entry__time"><time>{%s rev.TimeString() %}</time></li>
|
<li class="rc-entry__time"><time>{%s rev.TimeString() %}</time></li>
|
||||||
<li class="rc-entry__hash">{%s rev.Hash %}</li>
|
<li class="rc-entry__hash">{%s rev.Hash %}</li>
|
||||||
<li class="rc-entry__links">{%s= rev.HyphaeLinksHTML() %}</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>
|
<li class="rc-entry__msg">{%s rev.Message %} {% if rev.Username != "anon" %}<span class="rc-entry__author">by <a href="/hypha/{%s cfg.UserHypha %}/{%s rev.Username %}" rel="author">{%s rev.Username %}</a></span>{% endif %}</li>
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
{% func HistoryHTML(rq *http.Request, hyphaName, list string) %}
|
{% func HistoryHTML(rq *http.Request, hyphaName, list string) %}
|
||||||
|
@ -8,101 +8,104 @@ package views
|
|||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
//line views/history.qtpl:3
|
//line views/history.qtpl:3
|
||||||
import "github.com/bouncepaw/mycorrhiza/util"
|
import "github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
|
||||||
//line views/history.qtpl:4
|
//line views/history.qtpl:4
|
||||||
import "github.com/bouncepaw/mycorrhiza/user"
|
import "github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
|
||||||
//line views/history.qtpl:5
|
//line views/history.qtpl:5
|
||||||
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
import "github.com/bouncepaw/mycorrhiza/user"
|
||||||
|
|
||||||
//line views/history.qtpl:6
|
//line views/history.qtpl:6
|
||||||
|
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
|
|
||||||
|
//line views/history.qtpl:7
|
||||||
import "github.com/bouncepaw/mycorrhiza/history"
|
import "github.com/bouncepaw/mycorrhiza/history"
|
||||||
|
|
||||||
//line views/history.qtpl:9
|
//line views/history.qtpl:10
|
||||||
import (
|
import (
|
||||||
qtio422016 "io"
|
qtio422016 "io"
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
qt422016 "github.com/valyala/quicktemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/history.qtpl:9
|
//line views/history.qtpl:10
|
||||||
var (
|
var (
|
||||||
_ = qtio422016.Copy
|
_ = qtio422016.Copy
|
||||||
_ = qt422016.AcquireByteBuffer
|
_ = qt422016.AcquireByteBuffer
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/history.qtpl:9
|
//line views/history.qtpl:10
|
||||||
func StreamPrimitiveDiffHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User, hash string) {
|
func StreamPrimitiveDiffHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User, hash string) {
|
||||||
//line views/history.qtpl:9
|
//line views/history.qtpl:10
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:11
|
//line views/history.qtpl:12
|
||||||
text, err := history.PrimitiveDiffAtRevision(h.TextPath, hash)
|
text, err := history.PrimitiveDiffAtRevision(h.TextPath, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
text = err.Error()
|
text = err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:15
|
//line views/history.qtpl:16
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:16
|
//line views/history.qtpl:17
|
||||||
StreamNavHTML(qw422016, rq, h.Name, "history")
|
StreamNavHTML(qw422016, rq, h.Name, "history")
|
||||||
//line views/history.qtpl:16
|
//line views/history.qtpl:17
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<article>
|
<article>
|
||||||
<h1>Diff `)
|
<h1>Diff `)
|
||||||
//line views/history.qtpl:20
|
//line views/history.qtpl:21
|
||||||
qw422016.E().S(util.BeautifulName(h.Name))
|
qw422016.E().S(util.BeautifulName(h.Name))
|
||||||
//line views/history.qtpl:20
|
//line views/history.qtpl:21
|
||||||
qw422016.N().S(` at `)
|
qw422016.N().S(` at `)
|
||||||
//line views/history.qtpl:20
|
//line views/history.qtpl:21
|
||||||
qw422016.E().S(hash)
|
qw422016.E().S(hash)
|
||||||
//line views/history.qtpl:20
|
//line views/history.qtpl:21
|
||||||
qw422016.N().S(`</h1>
|
qw422016.N().S(`</h1>
|
||||||
<pre class="codeblock"><code>`)
|
<pre class="codeblock"><code>`)
|
||||||
//line views/history.qtpl:21
|
//line views/history.qtpl:22
|
||||||
qw422016.E().S(text)
|
qw422016.E().S(text)
|
||||||
//line views/history.qtpl:21
|
//line views/history.qtpl:22
|
||||||
qw422016.N().S(`</code></pre>
|
qw422016.N().S(`</code></pre>
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
func WritePrimitiveDiffHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User, hash string) {
|
func WritePrimitiveDiffHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User, hash string) {
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
StreamPrimitiveDiffHTML(qw422016, rq, h, u, hash)
|
StreamPrimitiveDiffHTML(qw422016, rq, h, u, hash)
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
func PrimitiveDiffHTML(rq *http.Request, h *hyphae.Hypha, u *user.User, hash string) string {
|
func PrimitiveDiffHTML(rq *http.Request, h *hyphae.Hypha, u *user.User, hash string) string {
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
WritePrimitiveDiffHTML(qb422016, rq, h, u, hash)
|
WritePrimitiveDiffHTML(qb422016, rq, h, u, hash)
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/history.qtpl:25
|
//line views/history.qtpl:26
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:27
|
//line views/history.qtpl:28
|
||||||
func StreamRecentChangesHTML(qw422016 *qt422016.Writer, n int) {
|
func StreamRecentChangesHTML(qw422016 *qt422016.Writer, n int) {
|
||||||
//line views/history.qtpl:27
|
//line views/history.qtpl:28
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width recent-changes">
|
<main class="main-width recent-changes">
|
||||||
@ -111,268 +114,268 @@ func StreamRecentChangesHTML(qw422016 *qt422016.Writer, n int) {
|
|||||||
<nav class="recent-changes__count">
|
<nav class="recent-changes__count">
|
||||||
See
|
See
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:34
|
//line views/history.qtpl:35
|
||||||
for _, m := range []int{20, 0, 50, 0, 100} {
|
for _, m := range []int{20, 0, 50, 0, 100} {
|
||||||
//line views/history.qtpl:34
|
//line views/history.qtpl:35
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:35
|
//line views/history.qtpl:36
|
||||||
switch m {
|
switch m {
|
||||||
//line views/history.qtpl:36
|
//line views/history.qtpl:37
|
||||||
case 0:
|
case 0:
|
||||||
//line views/history.qtpl:36
|
//line views/history.qtpl:37
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<span aria-hidden="true">|</span>
|
<span aria-hidden="true">|</span>
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:38
|
//line views/history.qtpl:39
|
||||||
case n:
|
case n:
|
||||||
//line views/history.qtpl:38
|
//line views/history.qtpl:39
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<b>`)
|
<b>`)
|
||||||
//line views/history.qtpl:39
|
//line views/history.qtpl:40
|
||||||
qw422016.N().D(n)
|
qw422016.N().D(n)
|
||||||
//line views/history.qtpl:39
|
//line views/history.qtpl:40
|
||||||
qw422016.N().S(`</b>
|
qw422016.N().S(`</b>
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:40
|
//line views/history.qtpl:41
|
||||||
default:
|
default:
|
||||||
//line views/history.qtpl:40
|
//line views/history.qtpl:41
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<a href="/recent-changes/`)
|
<a href="/recent-changes/`)
|
||||||
//line views/history.qtpl:41
|
//line views/history.qtpl:42
|
||||||
qw422016.N().D(m)
|
qw422016.N().D(m)
|
||||||
//line views/history.qtpl:41
|
//line views/history.qtpl:42
|
||||||
qw422016.N().S(`">`)
|
qw422016.N().S(`">`)
|
||||||
//line views/history.qtpl:41
|
//line views/history.qtpl:42
|
||||||
qw422016.N().D(m)
|
qw422016.N().D(m)
|
||||||
//line views/history.qtpl:41
|
//line views/history.qtpl:42
|
||||||
qw422016.N().S(`</a>
|
qw422016.N().S(`</a>
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:42
|
//line views/history.qtpl:43
|
||||||
}
|
}
|
||||||
//line views/history.qtpl:42
|
//line views/history.qtpl:43
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:43
|
//line views/history.qtpl:44
|
||||||
}
|
}
|
||||||
//line views/history.qtpl:43
|
//line views/history.qtpl:44
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
recent changes
|
recent changes
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<p><img class="icon" width="20" height="20" src="/static/icon/feed">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>
|
<p><img class="icon" width="20" height="20" src="/assets/icon/feed">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:54
|
//line views/history.qtpl:55
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
|
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:57
|
//line views/history.qtpl:58
|
||||||
changes := history.RecentChanges(n)
|
changes := history.RecentChanges(n)
|
||||||
|
|
||||||
//line views/history.qtpl:58
|
//line views/history.qtpl:59
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<section class="recent-changes__list" role="feed">
|
<section class="recent-changes__list" role="feed">
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:60
|
//line views/history.qtpl:61
|
||||||
if len(changes) == 0 {
|
if len(changes) == 0 {
|
||||||
//line views/history.qtpl:60
|
//line views/history.qtpl:61
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p>Could not find any recent changes.</p>
|
<p>Could not find any recent changes.</p>
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:62
|
//line views/history.qtpl:63
|
||||||
} else {
|
} else {
|
||||||
//line views/history.qtpl:62
|
//line views/history.qtpl:63
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:63
|
//line views/history.qtpl:64
|
||||||
for i, entry := range changes {
|
for i, entry := range changes {
|
||||||
//line views/history.qtpl:63
|
//line views/history.qtpl:64
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<ul class="recent-changes__entry rc-entry" role="article"
|
<ul class="recent-changes__entry rc-entry" role="article"
|
||||||
aria-setsize="`)
|
aria-setsize="`)
|
||||||
//line views/history.qtpl:65
|
//line views/history.qtpl:66
|
||||||
qw422016.N().D(n)
|
qw422016.N().D(n)
|
||||||
//line views/history.qtpl:65
|
//line views/history.qtpl:66
|
||||||
qw422016.N().S(`" aria-posinset="`)
|
qw422016.N().S(`" aria-posinset="`)
|
||||||
//line views/history.qtpl:65
|
//line views/history.qtpl:66
|
||||||
qw422016.N().D(i)
|
qw422016.N().D(i)
|
||||||
//line views/history.qtpl:65
|
//line views/history.qtpl:66
|
||||||
qw422016.N().S(`">
|
qw422016.N().S(`">
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:66
|
//line views/history.qtpl:67
|
||||||
qw422016.N().S(recentChangesEntry(entry))
|
qw422016.N().S(recentChangesEntry(entry))
|
||||||
//line views/history.qtpl:66
|
//line views/history.qtpl:67
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</ul>
|
</ul>
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:68
|
//line views/history.qtpl:69
|
||||||
}
|
}
|
||||||
//line views/history.qtpl:68
|
//line views/history.qtpl:69
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:69
|
//line views/history.qtpl:70
|
||||||
}
|
}
|
||||||
//line views/history.qtpl:69
|
//line views/history.qtpl:70
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
func WriteRecentChangesHTML(qq422016 qtio422016.Writer, n int) {
|
func WriteRecentChangesHTML(qq422016 qtio422016.Writer, n int) {
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
StreamRecentChangesHTML(qw422016, n)
|
StreamRecentChangesHTML(qw422016, n)
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
func RecentChangesHTML(n int) string {
|
func RecentChangesHTML(n int) string {
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
WriteRecentChangesHTML(qb422016, n)
|
WriteRecentChangesHTML(qb422016, n)
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/history.qtpl:73
|
//line views/history.qtpl:74
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:75
|
//line views/history.qtpl:76
|
||||||
func streamrecentChangesEntry(qw422016 *qt422016.Writer, rev history.Revision) {
|
func streamrecentChangesEntry(qw422016 *qt422016.Writer, rev history.Revision) {
|
||||||
//line views/history.qtpl:75
|
//line views/history.qtpl:76
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<li class="rc-entry__time"><time>`)
|
<li class="rc-entry__time"><time>`)
|
||||||
//line views/history.qtpl:76
|
//line views/history.qtpl:77
|
||||||
qw422016.E().S(rev.TimeString())
|
qw422016.E().S(rev.TimeString())
|
||||||
//line views/history.qtpl:76
|
//line views/history.qtpl:77
|
||||||
qw422016.N().S(`</time></li>
|
qw422016.N().S(`</time></li>
|
||||||
<li class="rc-entry__hash">`)
|
<li class="rc-entry__hash">`)
|
||||||
//line views/history.qtpl:77
|
//line views/history.qtpl:78
|
||||||
qw422016.E().S(rev.Hash)
|
qw422016.E().S(rev.Hash)
|
||||||
//line views/history.qtpl:77
|
//line views/history.qtpl:78
|
||||||
qw422016.N().S(`</li>
|
qw422016.N().S(`</li>
|
||||||
<li class="rc-entry__links">`)
|
<li class="rc-entry__links">`)
|
||||||
//line views/history.qtpl:78
|
//line views/history.qtpl:79
|
||||||
qw422016.N().S(rev.HyphaeLinksHTML())
|
qw422016.N().S(rev.HyphaeLinksHTML())
|
||||||
//line views/history.qtpl:78
|
//line views/history.qtpl:79
|
||||||
qw422016.N().S(`</li>
|
qw422016.N().S(`</li>
|
||||||
<li class="rc-entry__msg">`)
|
<li class="rc-entry__msg">`)
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
qw422016.E().S(rev.Message)
|
qw422016.E().S(rev.Message)
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
qw422016.N().S(` `)
|
qw422016.N().S(` `)
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
if rev.Username != "anon" {
|
if rev.Username != "anon" {
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
qw422016.N().S(`<span class="rc-entry__author">by <a href="/hypha/`)
|
qw422016.N().S(`<span class="rc-entry__author">by <a href="/hypha/`)
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
qw422016.E().S(util.UserHypha)
|
qw422016.E().S(cfg.UserHypha)
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
qw422016.N().S(`/`)
|
qw422016.N().S(`/`)
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
qw422016.E().S(rev.Username)
|
qw422016.E().S(rev.Username)
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
qw422016.N().S(`" rel="author">`)
|
qw422016.N().S(`" rel="author">`)
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
qw422016.E().S(rev.Username)
|
qw422016.E().S(rev.Username)
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
qw422016.N().S(`</a></span>`)
|
qw422016.N().S(`</a></span>`)
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
}
|
}
|
||||||
//line views/history.qtpl:79
|
//line views/history.qtpl:80
|
||||||
qw422016.N().S(`</li>
|
qw422016.N().S(`</li>
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
func writerecentChangesEntry(qq422016 qtio422016.Writer, rev history.Revision) {
|
func writerecentChangesEntry(qq422016 qtio422016.Writer, rev history.Revision) {
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
streamrecentChangesEntry(qw422016, rev)
|
streamrecentChangesEntry(qw422016, rev)
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
func recentChangesEntry(rev history.Revision) string {
|
func recentChangesEntry(rev history.Revision) string {
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
writerecentChangesEntry(qb422016, rev)
|
writerecentChangesEntry(qb422016, rev)
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/history.qtpl:80
|
//line views/history.qtpl:81
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:82
|
//line views/history.qtpl:83
|
||||||
func StreamHistoryHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, list string) {
|
func StreamHistoryHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, list string) {
|
||||||
//line views/history.qtpl:82
|
//line views/history.qtpl:83
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:83
|
//line views/history.qtpl:84
|
||||||
StreamNavHTML(qw422016, rq, hyphaName, "history")
|
StreamNavHTML(qw422016, rq, hyphaName, "history")
|
||||||
//line views/history.qtpl:83
|
//line views/history.qtpl:84
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<article class="history">
|
<article class="history">
|
||||||
<h1>History of `)
|
<h1>History of `)
|
||||||
//line views/history.qtpl:87
|
//line views/history.qtpl:88
|
||||||
qw422016.E().S(util.BeautifulName(hyphaName))
|
qw422016.E().S(util.BeautifulName(hyphaName))
|
||||||
//line views/history.qtpl:87
|
//line views/history.qtpl:88
|
||||||
qw422016.N().S(`</h1>
|
qw422016.N().S(`</h1>
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:88
|
//line views/history.qtpl:89
|
||||||
qw422016.N().S(list)
|
qw422016.N().S(list)
|
||||||
//line views/history.qtpl:88
|
//line views/history.qtpl:89
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
func WriteHistoryHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, list string) {
|
func WriteHistoryHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, list string) {
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
StreamHistoryHTML(qw422016, rq, hyphaName, list)
|
StreamHistoryHTML(qw422016, rq, hyphaName, list)
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
func HistoryHTML(rq *http.Request, hyphaName, list string) string {
|
func HistoryHTML(rq *http.Request, hyphaName, list string) string {
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
WriteHistoryHTML(qb422016, rq, hyphaName, list)
|
WriteHistoryHTML(qb422016, rq, hyphaName, list)
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/history.qtpl:92
|
//line views/history.qtpl:93
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,47 @@
|
|||||||
{% import "path/filepath" %}
|
{% import "path/filepath" %}
|
||||||
{% import "strings" %}
|
{% import "strings" %}
|
||||||
|
|
||||||
|
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
||||||
|
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||||
|
|
||||||
|
{% func nonExistentHyphaNotice(h *hyphae.Hypha, u *user.User) %}
|
||||||
|
<section class="non-existent-hypha">
|
||||||
|
<h2 class="non-existent-hypha__title">This hypha does not exist</h2>
|
||||||
|
{% if user.AuthUsed && u.Group == "anon" %}
|
||||||
|
<p>You are not authorized to create new hyphae. Here is what you can do:</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/login">Log in to your account, if you have one</a></li>
|
||||||
|
{% if cfg.UseRegistration %}<li><a href="/register">Register a new account</a></li>{% endif %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
<div class="non-existent-hypha__ways">
|
||||||
|
<section class="non-existent-hypha__way">
|
||||||
|
<h3 class="non-existent-hypha__subtitle">📝 Write a text</h3>
|
||||||
|
<p>Write a note, a diary, an article, a story or anything textual using <a href="https://mycorrhiza.lesarbr.es/hypha/mycomarkup">Mycomarkup</a>. Full history of edits to the document will be saved.</p>
|
||||||
|
<p>Make sure to follow this wiki's writing conventions if there are any.</p>
|
||||||
|
<a class="btn stick-to-bottom" href="/edit/{%s h.Name %}">Create</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="non-existent-hypha__way">
|
||||||
|
<h3 class="non-existent-hypha__subtitle">🖼 Upload a media</h3>
|
||||||
|
<p>Upload a picture, a video or an audio. Most common formats can be accessed from the browser, others can be only downloaded afterwards. You can write a description for the media later.</p>
|
||||||
|
<form action="/upload-binary/{%s h.Name %}"
|
||||||
|
method="post" enctype="multipart/form-data"
|
||||||
|
class="upload-binary">
|
||||||
|
<label for="upload-binary__input"></label>
|
||||||
|
<input type="file" id="upload-binary__input" name="binary">
|
||||||
|
|
||||||
|
<input type="submit" class="btn stick-to-bottom" value="Upload">
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</section>
|
||||||
|
{% endfunc %}
|
||||||
|
|
||||||
{% func NaviTitleHTML(h *hyphae.Hypha) %}
|
{% func NaviTitleHTML(h *hyphae.Hypha) %}
|
||||||
{% code
|
{% code
|
||||||
var (
|
var (
|
||||||
@ -12,8 +51,8 @@
|
|||||||
%}
|
%}
|
||||||
<h1 class="navi-title">
|
<h1 class="navi-title">
|
||||||
{% stripspace %}
|
{% stripspace %}
|
||||||
<a href="/hypha/{%s util.HomePage %}">
|
<a href="/hypha/{%s cfg.HomeHypha %}">
|
||||||
{%-s= util.SiteNavIcon -%}
|
{%-s= cfg.NaviTitleIcon -%}
|
||||||
<span aria-hidden="true" class="navi-title__colon">:</span>
|
<span aria-hidden="true" class="navi-title__colon">:</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -10,224 +10,325 @@ import "path/filepath"
|
|||||||
//line views/hypha.qtpl:2
|
//line views/hypha.qtpl:2
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
//line views/hypha.qtpl:3
|
//line views/hypha.qtpl:4
|
||||||
|
import "github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
|
||||||
|
//line views/hypha.qtpl:5
|
||||||
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
|
|
||||||
//line views/hypha.qtpl:4
|
//line views/hypha.qtpl:6
|
||||||
|
import "github.com/bouncepaw/mycorrhiza/user"
|
||||||
|
|
||||||
|
//line views/hypha.qtpl:7
|
||||||
import "github.com/bouncepaw/mycorrhiza/util"
|
import "github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
|
||||||
//line views/hypha.qtpl:6
|
//line views/hypha.qtpl:9
|
||||||
import (
|
import (
|
||||||
qtio422016 "io"
|
qtio422016 "io"
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
qt422016 "github.com/valyala/quicktemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/hypha.qtpl:6
|
//line views/hypha.qtpl:9
|
||||||
var (
|
var (
|
||||||
_ = qtio422016.Copy
|
_ = qtio422016.Copy
|
||||||
_ = qt422016.AcquireByteBuffer
|
_ = qt422016.AcquireByteBuffer
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/hypha.qtpl:6
|
//line views/hypha.qtpl:9
|
||||||
|
func streamnonExistentHyphaNotice(qw422016 *qt422016.Writer, h *hyphae.Hypha, u *user.User) {
|
||||||
|
//line views/hypha.qtpl:9
|
||||||
|
qw422016.N().S(`
|
||||||
|
<section class="non-existent-hypha">
|
||||||
|
<h2 class="non-existent-hypha__title">This hypha does not exist</h2>
|
||||||
|
`)
|
||||||
|
//line views/hypha.qtpl:12
|
||||||
|
if user.AuthUsed && u.Group == "anon" {
|
||||||
|
//line views/hypha.qtpl:12
|
||||||
|
qw422016.N().S(`
|
||||||
|
<p>You are not authorized to create new hyphae. Here is what you can do:</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/login">Log in to your account, if you have one</a></li>
|
||||||
|
`)
|
||||||
|
//line views/hypha.qtpl:16
|
||||||
|
if cfg.UseRegistration {
|
||||||
|
//line views/hypha.qtpl:16
|
||||||
|
qw422016.N().S(`<li><a href="/register">Register a new account</a></li>`)
|
||||||
|
//line views/hypha.qtpl:16
|
||||||
|
}
|
||||||
|
//line views/hypha.qtpl:16
|
||||||
|
qw422016.N().S(`
|
||||||
|
</ul>
|
||||||
|
`)
|
||||||
|
//line views/hypha.qtpl:18
|
||||||
|
} else {
|
||||||
|
//line views/hypha.qtpl:18
|
||||||
|
qw422016.N().S(`
|
||||||
|
|
||||||
|
<div class="non-existent-hypha__ways">
|
||||||
|
<section class="non-existent-hypha__way">
|
||||||
|
<h3 class="non-existent-hypha__subtitle">📝 Write a text</h3>
|
||||||
|
<p>Write a note, a diary, an article, a story or anything textual using <a href="https://mycorrhiza.lesarbr.es/hypha/mycomarkup">Mycomarkup</a>. Full history of edits to the document will be saved.</p>
|
||||||
|
<p>Make sure to follow this wiki's writing conventions if there are any.</p>
|
||||||
|
<a class="btn stick-to-bottom" href="/edit/`)
|
||||||
|
//line views/hypha.qtpl:25
|
||||||
|
qw422016.E().S(h.Name)
|
||||||
|
//line views/hypha.qtpl:25
|
||||||
|
qw422016.N().S(`">Create</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="non-existent-hypha__way">
|
||||||
|
<h3 class="non-existent-hypha__subtitle">🖼 Upload a media</h3>
|
||||||
|
<p>Upload a picture, a video or an audio. Most common formats can be accessed from the browser, others can be only downloaded afterwards. You can write a description for the media later.</p>
|
||||||
|
<form action="/upload-binary/`)
|
||||||
|
//line views/hypha.qtpl:31
|
||||||
|
qw422016.E().S(h.Name)
|
||||||
|
//line views/hypha.qtpl:31
|
||||||
|
qw422016.N().S(`"
|
||||||
|
method="post" enctype="multipart/form-data"
|
||||||
|
class="upload-binary">
|
||||||
|
<label for="upload-binary__input"></label>
|
||||||
|
<input type="file" id="upload-binary__input" name="binary">
|
||||||
|
|
||||||
|
<input type="submit" class="btn stick-to-bottom" value="Upload">
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
//line views/hypha.qtpl:41
|
||||||
|
}
|
||||||
|
//line views/hypha.qtpl:41
|
||||||
|
qw422016.N().S(`
|
||||||
|
</section>
|
||||||
|
`)
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
func writenonExistentHyphaNotice(qq422016 qtio422016.Writer, h *hyphae.Hypha, u *user.User) {
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
streamnonExistentHyphaNotice(qw422016, h, u)
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
func nonExistentHyphaNotice(h *hyphae.Hypha, u *user.User) string {
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
writenonExistentHyphaNotice(qb422016, h, u)
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
return qs422016
|
||||||
|
//line views/hypha.qtpl:43
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/hypha.qtpl:45
|
||||||
func StreamNaviTitleHTML(qw422016 *qt422016.Writer, h *hyphae.Hypha) {
|
func StreamNaviTitleHTML(qw422016 *qt422016.Writer, h *hyphae.Hypha) {
|
||||||
//line views/hypha.qtpl:6
|
//line views/hypha.qtpl:45
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/hypha.qtpl:8
|
//line views/hypha.qtpl:47
|
||||||
var (
|
var (
|
||||||
prevAcc = "/hypha/"
|
prevAcc = "/hypha/"
|
||||||
parts = strings.Split(h.Name, "/")
|
parts = strings.Split(h.Name, "/")
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/hypha.qtpl:12
|
//line views/hypha.qtpl:51
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<h1 class="navi-title">
|
<h1 class="navi-title">
|
||||||
`)
|
`)
|
||||||
//line views/hypha.qtpl:14
|
//line views/hypha.qtpl:53
|
||||||
qw422016.N().S(`<a href="/hypha/`)
|
qw422016.N().S(`<a href="/hypha/`)
|
||||||
//line views/hypha.qtpl:15
|
//line views/hypha.qtpl:54
|
||||||
qw422016.E().S(util.HomePage)
|
qw422016.E().S(cfg.HomeHypha)
|
||||||
//line views/hypha.qtpl:15
|
//line views/hypha.qtpl:54
|
||||||
qw422016.N().S(`">`)
|
qw422016.N().S(`">`)
|
||||||
//line views/hypha.qtpl:16
|
//line views/hypha.qtpl:55
|
||||||
qw422016.N().S(util.SiteNavIcon)
|
qw422016.N().S(cfg.NaviTitleIcon)
|
||||||
//line views/hypha.qtpl:16
|
//line views/hypha.qtpl:55
|
||||||
qw422016.N().S(`<span aria-hidden="true" class="navi-title__colon">:</span></a>`)
|
qw422016.N().S(`<span aria-hidden="true" class="navi-title__colon">:</span></a>`)
|
||||||
//line views/hypha.qtpl:20
|
//line views/hypha.qtpl:59
|
||||||
for i, part := range parts {
|
for i, part := range parts {
|
||||||
//line views/hypha.qtpl:21
|
//line views/hypha.qtpl:60
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
//line views/hypha.qtpl:21
|
//line views/hypha.qtpl:60
|
||||||
qw422016.N().S(`<span aria-hidden="true" class="navi-title__separator">/</span>`)
|
qw422016.N().S(`<span aria-hidden="true" class="navi-title__separator">/</span>`)
|
||||||
//line views/hypha.qtpl:23
|
//line views/hypha.qtpl:62
|
||||||
}
|
}
|
||||||
//line views/hypha.qtpl:23
|
//line views/hypha.qtpl:62
|
||||||
qw422016.N().S(`<a href="`)
|
qw422016.N().S(`<a href="`)
|
||||||
//line views/hypha.qtpl:25
|
//line views/hypha.qtpl:64
|
||||||
qw422016.E().S(prevAcc + part)
|
qw422016.E().S(prevAcc + part)
|
||||||
//line views/hypha.qtpl:25
|
//line views/hypha.qtpl:64
|
||||||
qw422016.N().S(`" rel="`)
|
qw422016.N().S(`" rel="`)
|
||||||
//line views/hypha.qtpl:25
|
//line views/hypha.qtpl:64
|
||||||
if i == len(parts)-1 {
|
if i == len(parts)-1 {
|
||||||
//line views/hypha.qtpl:25
|
//line views/hypha.qtpl:64
|
||||||
qw422016.N().S(`bookmark`)
|
qw422016.N().S(`bookmark`)
|
||||||
//line views/hypha.qtpl:25
|
//line views/hypha.qtpl:64
|
||||||
} else {
|
} else {
|
||||||
//line views/hypha.qtpl:25
|
//line views/hypha.qtpl:64
|
||||||
qw422016.N().S(`up`)
|
qw422016.N().S(`up`)
|
||||||
//line views/hypha.qtpl:25
|
//line views/hypha.qtpl:64
|
||||||
}
|
}
|
||||||
//line views/hypha.qtpl:25
|
//line views/hypha.qtpl:64
|
||||||
qw422016.N().S(`">`)
|
qw422016.N().S(`">`)
|
||||||
//line views/hypha.qtpl:26
|
//line views/hypha.qtpl:65
|
||||||
qw422016.N().S(util.BeautifulName(part))
|
qw422016.N().S(util.BeautifulName(part))
|
||||||
//line views/hypha.qtpl:26
|
//line views/hypha.qtpl:65
|
||||||
qw422016.N().S(`</a>`)
|
qw422016.N().S(`</a>`)
|
||||||
//line views/hypha.qtpl:28
|
//line views/hypha.qtpl:67
|
||||||
prevAcc += part + "/"
|
prevAcc += part + "/"
|
||||||
|
|
||||||
//line views/hypha.qtpl:29
|
//line views/hypha.qtpl:68
|
||||||
}
|
}
|
||||||
//line views/hypha.qtpl:30
|
//line views/hypha.qtpl:69
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</h1>
|
</h1>
|
||||||
`)
|
`)
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
func WriteNaviTitleHTML(qq422016 qtio422016.Writer, h *hyphae.Hypha) {
|
func WriteNaviTitleHTML(qq422016 qtio422016.Writer, h *hyphae.Hypha) {
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
StreamNaviTitleHTML(qw422016, h)
|
StreamNaviTitleHTML(qw422016, h)
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
func NaviTitleHTML(h *hyphae.Hypha) string {
|
func NaviTitleHTML(h *hyphae.Hypha) string {
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
WriteNaviTitleHTML(qb422016, h)
|
WriteNaviTitleHTML(qb422016, h)
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/hypha.qtpl:32
|
//line views/hypha.qtpl:71
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/hypha.qtpl:34
|
//line views/hypha.qtpl:73
|
||||||
func StreamAttachmentHTML(qw422016 *qt422016.Writer, h *hyphae.Hypha) {
|
func StreamAttachmentHTML(qw422016 *qt422016.Writer, h *hyphae.Hypha) {
|
||||||
//line views/hypha.qtpl:34
|
//line views/hypha.qtpl:73
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/hypha.qtpl:35
|
//line views/hypha.qtpl:74
|
||||||
switch filepath.Ext(h.BinaryPath) {
|
switch filepath.Ext(h.BinaryPath) {
|
||||||
//line views/hypha.qtpl:37
|
//line views/hypha.qtpl:76
|
||||||
case ".jpg", ".gif", ".png", ".webp", ".svg", ".ico":
|
case ".jpg", ".gif", ".png", ".webp", ".svg", ".ico":
|
||||||
//line views/hypha.qtpl:37
|
//line views/hypha.qtpl:76
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="binary-container binary-container_with-img">
|
<div class="binary-container binary-container_with-img">
|
||||||
<a href="/binary/`)
|
<a href="/binary/`)
|
||||||
//line views/hypha.qtpl:39
|
//line views/hypha.qtpl:78
|
||||||
qw422016.N().S(h.Name)
|
qw422016.N().S(h.Name)
|
||||||
//line views/hypha.qtpl:39
|
//line views/hypha.qtpl:78
|
||||||
qw422016.N().S(`"><img src="/binary/`)
|
qw422016.N().S(`"><img src="/binary/`)
|
||||||
//line views/hypha.qtpl:39
|
//line views/hypha.qtpl:78
|
||||||
qw422016.N().S(h.Name)
|
qw422016.N().S(h.Name)
|
||||||
//line views/hypha.qtpl:39
|
//line views/hypha.qtpl:78
|
||||||
qw422016.N().S(`"/></a>
|
qw422016.N().S(`"/></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
`)
|
`)
|
||||||
//line views/hypha.qtpl:42
|
//line views/hypha.qtpl:81
|
||||||
case ".ogg", ".webm", ".mp4":
|
case ".ogg", ".webm", ".mp4":
|
||||||
//line views/hypha.qtpl:42
|
//line views/hypha.qtpl:81
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="binary-container binary-container_with-video">
|
<div class="binary-container binary-container_with-video">
|
||||||
<video controls>
|
<video controls>
|
||||||
<source src="/binary/`)
|
<source src="/binary/`)
|
||||||
//line views/hypha.qtpl:45
|
//line views/hypha.qtpl:84
|
||||||
qw422016.N().S(h.Name)
|
qw422016.N().S(h.Name)
|
||||||
//line views/hypha.qtpl:45
|
//line views/hypha.qtpl:84
|
||||||
qw422016.N().S(`"/>
|
qw422016.N().S(`"/>
|
||||||
<p>Your browser does not support video. <a href="/binary/`)
|
<p>Your browser does not support video. <a href="/binary/`)
|
||||||
//line views/hypha.qtpl:46
|
//line views/hypha.qtpl:85
|
||||||
qw422016.N().S(h.Name)
|
qw422016.N().S(h.Name)
|
||||||
//line views/hypha.qtpl:46
|
//line views/hypha.qtpl:85
|
||||||
qw422016.N().S(`">Download video</a></p>
|
qw422016.N().S(`">Download video</a></p>
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
`)
|
`)
|
||||||
//line views/hypha.qtpl:50
|
//line views/hypha.qtpl:89
|
||||||
case ".mp3":
|
case ".mp3":
|
||||||
//line views/hypha.qtpl:50
|
//line views/hypha.qtpl:89
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="binary-container binary-container_with-audio">
|
<div class="binary-container binary-container_with-audio">
|
||||||
<audio controls>
|
<audio controls>
|
||||||
<source src="/binary/`)
|
<source src="/binary/`)
|
||||||
//line views/hypha.qtpl:53
|
//line views/hypha.qtpl:92
|
||||||
qw422016.N().S(h.Name)
|
qw422016.N().S(h.Name)
|
||||||
//line views/hypha.qtpl:53
|
//line views/hypha.qtpl:92
|
||||||
qw422016.N().S(`"/>
|
qw422016.N().S(`"/>
|
||||||
<p>Your browser does not support audio. <a href="/binary/`)
|
<p>Your browser does not support audio. <a href="/binary/`)
|
||||||
//line views/hypha.qtpl:54
|
//line views/hypha.qtpl:93
|
||||||
qw422016.N().S(h.Name)
|
qw422016.N().S(h.Name)
|
||||||
//line views/hypha.qtpl:54
|
//line views/hypha.qtpl:93
|
||||||
qw422016.N().S(`">Download audio</a></p>
|
qw422016.N().S(`">Download audio</a></p>
|
||||||
</audio>
|
</audio>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
`)
|
`)
|
||||||
//line views/hypha.qtpl:58
|
//line views/hypha.qtpl:97
|
||||||
default:
|
default:
|
||||||
//line views/hypha.qtpl:58
|
//line views/hypha.qtpl:97
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="binary-container binary-container_with-nothing">
|
<div class="binary-container binary-container_with-nothing">
|
||||||
<p><a href="/binary/`)
|
<p><a href="/binary/`)
|
||||||
//line views/hypha.qtpl:60
|
//line views/hypha.qtpl:99
|
||||||
qw422016.N().S(h.Name)
|
qw422016.N().S(h.Name)
|
||||||
//line views/hypha.qtpl:60
|
//line views/hypha.qtpl:99
|
||||||
qw422016.N().S(`">Download media</a></p>
|
qw422016.N().S(`">Download media</a></p>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/hypha.qtpl:62
|
//line views/hypha.qtpl:101
|
||||||
}
|
}
|
||||||
//line views/hypha.qtpl:62
|
//line views/hypha.qtpl:101
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
func WriteAttachmentHTML(qq422016 qtio422016.Writer, h *hyphae.Hypha) {
|
func WriteAttachmentHTML(qq422016 qtio422016.Writer, h *hyphae.Hypha) {
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
StreamAttachmentHTML(qw422016, h)
|
StreamAttachmentHTML(qw422016, h)
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
func AttachmentHTML(h *hyphae.Hypha) string {
|
func AttachmentHTML(h *hyphae.Hypha) string {
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
WriteAttachmentHTML(qb422016, h)
|
WriteAttachmentHTML(qb422016, h)
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/hypha.qtpl:63
|
//line views/hypha.qtpl:102
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,8 @@
|
|||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
{% func modalEnd(hyphaName string, shouldFocusOnConfirm bool) %}
|
{% func modalEnd(hyphaName string, shouldFocusOnConfirm bool) %}
|
||||||
<input type="submit" value="Confirm" class="modal__action modal__submit" {% if shouldFocusOnConfirm %}autofocus{% endif %}>
|
<input type="submit" value="Confirm" class="btn" {% if shouldFocusOnConfirm %}autofocus{% endif %}>
|
||||||
<a href="/hypha/{%s hyphaName %}" class="modal__action modal__cancel">Cancel</a>
|
<a href="/hypha/{%s hyphaName %}" class="btn btn_weak">Cancel</a>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
@ -306,7 +306,7 @@ func modalBegin(path, hyphaName, formAttrs, legend string) string {
|
|||||||
func streammodalEnd(qw422016 *qt422016.Writer, hyphaName string, shouldFocusOnConfirm bool) {
|
func streammodalEnd(qw422016 *qt422016.Writer, hyphaName string, shouldFocusOnConfirm bool) {
|
||||||
//line views/modal.qtpl:56
|
//line views/modal.qtpl:56
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<input type="submit" value="Confirm" class="modal__action modal__submit" `)
|
<input type="submit" value="Confirm" class="btn" `)
|
||||||
//line views/modal.qtpl:57
|
//line views/modal.qtpl:57
|
||||||
if shouldFocusOnConfirm {
|
if shouldFocusOnConfirm {
|
||||||
//line views/modal.qtpl:57
|
//line views/modal.qtpl:57
|
||||||
@ -319,7 +319,7 @@ func streammodalEnd(qw422016 *qt422016.Writer, hyphaName string, shouldFocusOnCo
|
|||||||
//line views/modal.qtpl:58
|
//line views/modal.qtpl:58
|
||||||
qw422016.E().S(hyphaName)
|
qw422016.E().S(hyphaName)
|
||||||
//line views/modal.qtpl:58
|
//line views/modal.qtpl:58
|
||||||
qw422016.N().S(`" class="modal__action modal__cancel">Cancel</a>
|
qw422016.N().S(`" class="btn btn_weak">Cancel</a>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
{% import "net/http" %}
|
{% import "net/http" %}
|
||||||
|
|
||||||
|
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||||
|
|
||||||
@ -18,7 +20,7 @@
|
|||||||
{"italic", "wrapItalic()", "<i>//Italic//</i>"},
|
{"italic", "wrapItalic()", "<i>//Italic//</i>"},
|
||||||
{"highlighted", "wrapHighlighted()", "<mark>!!Highlight!!</mark>"},
|
{"highlighted", "wrapHighlighted()", "<mark>!!Highlight!!</mark>"},
|
||||||
{"monospace", "wrapMonospace()", "<code>`Monospace`</code>"},
|
{"monospace", "wrapMonospace()", "<code>`Monospace`</code>"},
|
||||||
{"lifted", "wrapLifted()", "<sup>^Lifted^</sup>"},
|
{"lifted", "wrapLifted()", "<sup>^^Lifted^^</sup>"},
|
||||||
{"lowered", "wrapLowered()", "<sub>,,Lowered,,</sub>"},
|
{"lowered", "wrapLowered()", "<sub>,,Lowered,,</sub>"},
|
||||||
{"strikethrough", "wrapStrikethrough()", "<strike>~~Strikethrough~~</strike>"},
|
{"strikethrough", "wrapStrikethrough()", "<strike>~~Strikethrough~~</strike>"},
|
||||||
{"rocket", "insertRocket()", "=> rocketlink"},
|
{"rocket", "insertRocket()", "=> rocketlink"},
|
||||||
@ -46,6 +48,7 @@
|
|||||||
display string
|
display string
|
||||||
}{
|
}{
|
||||||
{"date", "insertDate()", "Insert current date"},
|
{"date", "insertDate()", "Insert current date"},
|
||||||
|
{"time", "insertTimeUTC()", "Insert current time"},
|
||||||
} %}
|
} %}
|
||||||
<button
|
<button
|
||||||
class="edit-toolbar__btn edit-toolbar__{%s el.class %}"
|
class="edit-toolbar__btn edit-toolbar__{%s el.class %}"
|
||||||
@ -62,7 +65,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
</aside>
|
</aside>
|
||||||
<script src="/static/toolbar.js"></script>
|
<script src="/assets/toolbar.js"></script>
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
{% func EditHTML(rq *http.Request, hyphaName, textAreaFill, warning string) %}
|
{% func EditHTML(rq *http.Request, hyphaName, textAreaFill, warning string) %}
|
||||||
@ -82,6 +85,7 @@
|
|||||||
</main>
|
</main>
|
||||||
{%s= Toolbar(user.FromRequest(rq)) %}
|
{%s= Toolbar(user.FromRequest(rq)) %}
|
||||||
</div>
|
</div>
|
||||||
|
{%= editScripts() %}
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
{% func PreviewHTML(rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) %}
|
{% func PreviewHTML(rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) %}
|
||||||
@ -103,4 +107,11 @@
|
|||||||
</main>
|
</main>
|
||||||
{%s= Toolbar(user.FromRequest(rq)) %}
|
{%s= Toolbar(user.FromRequest(rq)) %}
|
||||||
</div>
|
</div>
|
||||||
|
{%= editScripts() %}
|
||||||
|
{% endfunc %}
|
||||||
|
|
||||||
|
{% func editScripts() %}
|
||||||
|
{% for _, scriptPath := range cfg.EditScripts %}
|
||||||
|
<script src="{%s scriptPath %}"></script>
|
||||||
|
{% endfor %}
|
||||||
{% endfunc %}
|
{% endfunc %}
|
@ -7,34 +7,37 @@ package views
|
|||||||
//line views/mutators.qtpl:1
|
//line views/mutators.qtpl:1
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
//line views/mutators.qtpl:2
|
//line views/mutators.qtpl:3
|
||||||
|
import "github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
|
||||||
|
//line views/mutators.qtpl:4
|
||||||
import "github.com/bouncepaw/mycorrhiza/util"
|
import "github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
|
||||||
//line views/mutators.qtpl:3
|
//line views/mutators.qtpl:5
|
||||||
import "github.com/bouncepaw/mycorrhiza/user"
|
import "github.com/bouncepaw/mycorrhiza/user"
|
||||||
|
|
||||||
//line views/mutators.qtpl:5
|
//line views/mutators.qtpl:7
|
||||||
import (
|
import (
|
||||||
qtio422016 "io"
|
qtio422016 "io"
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
qt422016 "github.com/valyala/quicktemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/mutators.qtpl:5
|
//line views/mutators.qtpl:7
|
||||||
var (
|
var (
|
||||||
_ = qtio422016.Copy
|
_ = qtio422016.Copy
|
||||||
_ = qt422016.AcquireByteBuffer
|
_ = qt422016.AcquireByteBuffer
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/mutators.qtpl:5
|
//line views/mutators.qtpl:7
|
||||||
func StreamToolbar(qw422016 *qt422016.Writer, u *user.User) {
|
func StreamToolbar(qw422016 *qt422016.Writer, u *user.User) {
|
||||||
//line views/mutators.qtpl:5
|
//line views/mutators.qtpl:7
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<aside class="edit-toolbar layout-card">
|
<aside class="edit-toolbar layout-card">
|
||||||
<h2 class="edit-toolbar__title layout-card__title">Markup</h2>
|
<h2 class="edit-toolbar__title layout-card__title">Markup</h2>
|
||||||
<section class="edit-toolbar__buttons">
|
<section class="edit-toolbar__buttons">
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:9
|
//line views/mutators.qtpl:11
|
||||||
for _, el := range []struct {
|
for _, el := range []struct {
|
||||||
class string
|
class string
|
||||||
onclick string
|
onclick string
|
||||||
@ -47,7 +50,7 @@ func StreamToolbar(qw422016 *qt422016.Writer, u *user.User) {
|
|||||||
{"italic", "wrapItalic()", "<i>//Italic//</i>"},
|
{"italic", "wrapItalic()", "<i>//Italic//</i>"},
|
||||||
{"highlighted", "wrapHighlighted()", "<mark>!!Highlight!!</mark>"},
|
{"highlighted", "wrapHighlighted()", "<mark>!!Highlight!!</mark>"},
|
||||||
{"monospace", "wrapMonospace()", "<code>`Monospace`</code>"},
|
{"monospace", "wrapMonospace()", "<code>`Monospace`</code>"},
|
||||||
{"lifted", "wrapLifted()", "<sup>^Lifted^</sup>"},
|
{"lifted", "wrapLifted()", "<sup>^^Lifted^^</sup>"},
|
||||||
{"lowered", "wrapLowered()", "<sub>,,Lowered,,</sub>"},
|
{"lowered", "wrapLowered()", "<sub>,,Lowered,,</sub>"},
|
||||||
{"strikethrough", "wrapStrikethrough()", "<strike>~~Strikethrough~~</strike>"},
|
{"strikethrough", "wrapStrikethrough()", "<strike>~~Strikethrough~~</strike>"},
|
||||||
{"rocket", "insertRocket()", "=> rocketlink"},
|
{"rocket", "insertRocket()", "=> rocketlink"},
|
||||||
@ -59,71 +62,72 @@ func StreamToolbar(qw422016 *qt422016.Writer, u *user.User) {
|
|||||||
{"bulletedlist", "insertBulletedList()", "* bullet list"},
|
{"bulletedlist", "insertBulletedList()", "* bullet list"},
|
||||||
{"numberedlist", "insertNumberedList()", "*. number list"},
|
{"numberedlist", "insertNumberedList()", "*. number list"},
|
||||||
} {
|
} {
|
||||||
//line views/mutators.qtpl:32
|
//line views/mutators.qtpl:34
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<button
|
<button
|
||||||
class="edit-toolbar__btn edit-toolbar__`)
|
class="edit-toolbar__btn edit-toolbar__`)
|
||||||
//line views/mutators.qtpl:34
|
//line views/mutators.qtpl:36
|
||||||
qw422016.E().S(el.class)
|
qw422016.E().S(el.class)
|
||||||
//line views/mutators.qtpl:34
|
//line views/mutators.qtpl:36
|
||||||
qw422016.N().S(`"
|
qw422016.N().S(`"
|
||||||
onclick="`)
|
onclick="`)
|
||||||
//line views/mutators.qtpl:35
|
//line views/mutators.qtpl:37
|
||||||
qw422016.E().S(el.onclick)
|
qw422016.E().S(el.onclick)
|
||||||
//line views/mutators.qtpl:35
|
//line views/mutators.qtpl:37
|
||||||
qw422016.N().S(`">
|
qw422016.N().S(`">
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:36
|
//line views/mutators.qtpl:38
|
||||||
qw422016.N().S(el.display)
|
qw422016.N().S(el.display)
|
||||||
//line views/mutators.qtpl:36
|
//line views/mutators.qtpl:38
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</button>
|
</button>
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:38
|
//line views/mutators.qtpl:40
|
||||||
}
|
}
|
||||||
//line views/mutators.qtpl:38
|
//line views/mutators.qtpl:40
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</section>
|
</section>
|
||||||
<p class="edit-toolbar__ad"><a href="https://mycorrhiza.lesarbr.es/hypha/mycomarkup" target="_blank">Learn more</a> about mycomarkup</p>
|
<p class="edit-toolbar__ad"><a href="https://mycorrhiza.lesarbr.es/hypha/mycomarkup" target="_blank">Learn more</a> about mycomarkup</p>
|
||||||
<h2 class="edit-toolbar__title layout-card__title">Actions</h2>
|
<h2 class="edit-toolbar__title layout-card__title">Actions</h2>
|
||||||
<section class="edit-toolbar__buttons">
|
<section class="edit-toolbar__buttons">
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:43
|
//line views/mutators.qtpl:45
|
||||||
for _, el := range []struct {
|
for _, el := range []struct {
|
||||||
class string
|
class string
|
||||||
onclick string
|
onclick string
|
||||||
display string
|
display string
|
||||||
}{
|
}{
|
||||||
{"date", "insertDate()", "Insert current date"},
|
{"date", "insertDate()", "Insert current date"},
|
||||||
|
{"time", "insertTimeUTC()", "Insert current time"},
|
||||||
} {
|
} {
|
||||||
//line views/mutators.qtpl:49
|
//line views/mutators.qtpl:52
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<button
|
<button
|
||||||
class="edit-toolbar__btn edit-toolbar__`)
|
class="edit-toolbar__btn edit-toolbar__`)
|
||||||
//line views/mutators.qtpl:51
|
//line views/mutators.qtpl:54
|
||||||
qw422016.E().S(el.class)
|
qw422016.E().S(el.class)
|
||||||
//line views/mutators.qtpl:51
|
//line views/mutators.qtpl:54
|
||||||
qw422016.N().S(`"
|
qw422016.N().S(`"
|
||||||
onclick="`)
|
onclick="`)
|
||||||
//line views/mutators.qtpl:52
|
//line views/mutators.qtpl:55
|
||||||
qw422016.E().S(el.onclick)
|
qw422016.E().S(el.onclick)
|
||||||
//line views/mutators.qtpl:52
|
//line views/mutators.qtpl:55
|
||||||
qw422016.N().S(`">
|
qw422016.N().S(`">
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:53
|
//line views/mutators.qtpl:56
|
||||||
qw422016.N().S(el.display)
|
qw422016.N().S(el.display)
|
||||||
//line views/mutators.qtpl:53
|
//line views/mutators.qtpl:56
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</button>
|
</button>
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:55
|
//line views/mutators.qtpl:58
|
||||||
}
|
}
|
||||||
//line views/mutators.qtpl:55
|
//line views/mutators.qtpl:58
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:56
|
//line views/mutators.qtpl:59
|
||||||
if u.Group != "anon" {
|
if u.Group != "anon" {
|
||||||
//line views/mutators.qtpl:56
|
//line views/mutators.qtpl:59
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<button
|
<button
|
||||||
class="edit-toolbar__btn edit-toolbar__user-link"
|
class="edit-toolbar__btn edit-toolbar__user-link"
|
||||||
@ -131,201 +135,260 @@ func StreamToolbar(qw422016 *qt422016.Writer, u *user.User) {
|
|||||||
Link yourself
|
Link yourself
|
||||||
</button>
|
</button>
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:62
|
//line views/mutators.qtpl:65
|
||||||
}
|
}
|
||||||
//line views/mutators.qtpl:62
|
//line views/mutators.qtpl:65
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</section>
|
</section>
|
||||||
</aside>
|
</aside>
|
||||||
<script src="/static/toolbar.js"></script>
|
<script src="/assets/toolbar.js"></script>
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
func WriteToolbar(qq422016 qtio422016.Writer, u *user.User) {
|
func WriteToolbar(qq422016 qtio422016.Writer, u *user.User) {
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
StreamToolbar(qw422016, u)
|
StreamToolbar(qw422016, u)
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
func Toolbar(u *user.User) string {
|
func Toolbar(u *user.User) string {
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
WriteToolbar(qb422016, u)
|
WriteToolbar(qb422016, u)
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/mutators.qtpl:66
|
//line views/mutators.qtpl:69
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/mutators.qtpl:68
|
//line views/mutators.qtpl:71
|
||||||
func StreamEditHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string) {
|
func StreamEditHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string) {
|
||||||
//line views/mutators.qtpl:68
|
//line views/mutators.qtpl:71
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:69
|
//line views/mutators.qtpl:72
|
||||||
qw422016.N().S(NavHTML(rq, hyphaName, "edit"))
|
qw422016.N().S(NavHTML(rq, hyphaName, "edit"))
|
||||||
//line views/mutators.qtpl:69
|
//line views/mutators.qtpl:72
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width edit edit_no-preview">
|
<main class="main-width edit edit_no-preview">
|
||||||
<h1 class="edit__title">Edit `)
|
<h1 class="edit__title">Edit `)
|
||||||
//line views/mutators.qtpl:72
|
//line views/mutators.qtpl:75
|
||||||
qw422016.E().S(util.BeautifulName(hyphaName))
|
qw422016.E().S(util.BeautifulName(hyphaName))
|
||||||
//line views/mutators.qtpl:72
|
//line views/mutators.qtpl:75
|
||||||
qw422016.N().S(`</h1>
|
qw422016.N().S(`</h1>
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:73
|
//line views/mutators.qtpl:76
|
||||||
qw422016.N().S(warning)
|
qw422016.N().S(warning)
|
||||||
//line views/mutators.qtpl:73
|
//line views/mutators.qtpl:76
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<form method="post" class="edit-form"
|
<form method="post" class="edit-form"
|
||||||
action="/upload-text/`)
|
action="/upload-text/`)
|
||||||
//line views/mutators.qtpl:75
|
//line views/mutators.qtpl:78
|
||||||
qw422016.E().S(hyphaName)
|
qw422016.E().S(hyphaName)
|
||||||
//line views/mutators.qtpl:75
|
//line views/mutators.qtpl:78
|
||||||
qw422016.N().S(`">
|
qw422016.N().S(`">
|
||||||
<textarea name="text" class="edit-form__textarea">`)
|
<textarea name="text" class="edit-form__textarea">`)
|
||||||
//line views/mutators.qtpl:76
|
//line views/mutators.qtpl:79
|
||||||
qw422016.E().S(textAreaFill)
|
qw422016.E().S(textAreaFill)
|
||||||
//line views/mutators.qtpl:76
|
//line views/mutators.qtpl:79
|
||||||
qw422016.N().S(`</textarea>
|
qw422016.N().S(`</textarea>
|
||||||
<br/>
|
<br/>
|
||||||
<input type="submit" name="action" value="Save" class="edit-form__save"/>
|
<input type="submit" name="action" value="Save" class="edit-form__save"/>
|
||||||
<input type="submit" name="action" value="Preview" class="edit-form__preview">
|
<input type="submit" name="action" value="Preview" class="edit-form__preview">
|
||||||
<a href="/page/`)
|
<a href="/page/`)
|
||||||
//line views/mutators.qtpl:80
|
//line views/mutators.qtpl:83
|
||||||
qw422016.E().S(hyphaName)
|
qw422016.E().S(hyphaName)
|
||||||
//line views/mutators.qtpl:80
|
//line views/mutators.qtpl:83
|
||||||
qw422016.N().S(`" class="edit-form__cancel">Cancel</a>
|
qw422016.N().S(`" class="edit-form__cancel">Cancel</a>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:83
|
//line views/mutators.qtpl:86
|
||||||
qw422016.N().S(Toolbar(user.FromRequest(rq)))
|
qw422016.N().S(Toolbar(user.FromRequest(rq)))
|
||||||
//line views/mutators.qtpl:83
|
//line views/mutators.qtpl:86
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:85
|
//line views/mutators.qtpl:88
|
||||||
}
|
streameditScripts(qw422016)
|
||||||
|
//line views/mutators.qtpl:88
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
func WriteEditHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string) {
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
StreamEditHTML(qw422016, rq, hyphaName, textAreaFill, warning)
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
}
|
|
||||||
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
func EditHTML(rq *http.Request, hyphaName, textAreaFill, warning string) string {
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
WriteEditHTML(qb422016, rq, hyphaName, textAreaFill, warning)
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
return qs422016
|
|
||||||
//line views/mutators.qtpl:85
|
|
||||||
}
|
|
||||||
|
|
||||||
//line views/mutators.qtpl:87
|
|
||||||
func StreamPreviewHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) {
|
|
||||||
//line views/mutators.qtpl:87
|
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:88
|
//line views/mutators.qtpl:89
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
func WriteEditHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string) {
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
StreamEditHTML(qw422016, rq, hyphaName, textAreaFill, warning)
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
func EditHTML(rq *http.Request, hyphaName, textAreaFill, warning string) string {
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
WriteEditHTML(qb422016, rq, hyphaName, textAreaFill, warning)
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
return qs422016
|
||||||
|
//line views/mutators.qtpl:89
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/mutators.qtpl:91
|
||||||
|
func StreamPreviewHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) {
|
||||||
|
//line views/mutators.qtpl:91
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/mutators.qtpl:92
|
||||||
qw422016.N().S(NavHTML(rq, hyphaName, "edit"))
|
qw422016.N().S(NavHTML(rq, hyphaName, "edit"))
|
||||||
//line views/mutators.qtpl:88
|
//line views/mutators.qtpl:92
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width edit edit_with-preview">
|
<main class="main-width edit edit_with-preview">
|
||||||
<h1>Edit `)
|
<h1>Edit `)
|
||||||
//line views/mutators.qtpl:91
|
//line views/mutators.qtpl:95
|
||||||
qw422016.E().S(util.BeautifulName(hyphaName))
|
qw422016.E().S(util.BeautifulName(hyphaName))
|
||||||
//line views/mutators.qtpl:91
|
//line views/mutators.qtpl:95
|
||||||
qw422016.N().S(` (preview)</h1>
|
qw422016.N().S(` (preview)</h1>
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:92
|
//line views/mutators.qtpl:96
|
||||||
qw422016.N().S(warning)
|
qw422016.N().S(warning)
|
||||||
//line views/mutators.qtpl:92
|
//line views/mutators.qtpl:96
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<form method="post" class="edit-form"
|
<form method="post" class="edit-form"
|
||||||
action="/upload-text/`)
|
action="/upload-text/`)
|
||||||
//line views/mutators.qtpl:94
|
//line views/mutators.qtpl:98
|
||||||
qw422016.E().S(hyphaName)
|
qw422016.E().S(hyphaName)
|
||||||
//line views/mutators.qtpl:94
|
//line views/mutators.qtpl:98
|
||||||
qw422016.N().S(`">
|
qw422016.N().S(`">
|
||||||
<textarea class="edit-form__textarea" name="text">`)
|
<textarea class="edit-form__textarea" name="text">`)
|
||||||
//line views/mutators.qtpl:95
|
//line views/mutators.qtpl:99
|
||||||
qw422016.E().S(textAreaFill)
|
qw422016.E().S(textAreaFill)
|
||||||
//line views/mutators.qtpl:95
|
//line views/mutators.qtpl:99
|
||||||
qw422016.N().S(`</textarea>
|
qw422016.N().S(`</textarea>
|
||||||
<br/>
|
<br/>
|
||||||
<input type="submit" name="action" value="Save" class="edit-form__save"/>
|
<input type="submit" name="action" value="Save" class="edit-form__save"/>
|
||||||
<input type="submit" name="action" value="Preview" class="edit-form__preview">
|
<input type="submit" name="action" value="Preview" class="edit-form__preview">
|
||||||
<a href="/page/`)
|
<a href="/page/`)
|
||||||
//line views/mutators.qtpl:99
|
//line views/mutators.qtpl:103
|
||||||
qw422016.E().S(hyphaName)
|
qw422016.E().S(hyphaName)
|
||||||
//line views/mutators.qtpl:99
|
//line views/mutators.qtpl:103
|
||||||
qw422016.N().S(`" class="edit-form__cancel">Cancel</a>
|
qw422016.N().S(`" class="edit-form__cancel">Cancel</a>
|
||||||
</form>
|
</form>
|
||||||
<p class="warning">Note that the hypha is not saved yet. You can preview the changes ↓</p>
|
<p class="warning">Note that the hypha is not saved yet. You can preview the changes ↓</p>
|
||||||
<article class="edit__preview">`)
|
<article class="edit__preview">`)
|
||||||
//line views/mutators.qtpl:102
|
//line views/mutators.qtpl:106
|
||||||
qw422016.N().S(renderedPage)
|
qw422016.N().S(renderedPage)
|
||||||
//line views/mutators.qtpl:102
|
//line views/mutators.qtpl:106
|
||||||
qw422016.N().S(`</article>
|
qw422016.N().S(`</article>
|
||||||
</main>
|
</main>
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:104
|
//line views/mutators.qtpl:108
|
||||||
qw422016.N().S(Toolbar(user.FromRequest(rq)))
|
qw422016.N().S(Toolbar(user.FromRequest(rq)))
|
||||||
//line views/mutators.qtpl:104
|
//line views/mutators.qtpl:108
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:110
|
||||||
|
streameditScripts(qw422016)
|
||||||
|
//line views/mutators.qtpl:110
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/mutators.qtpl:111
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
func WritePreviewHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) {
|
func WritePreviewHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) {
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
StreamPreviewHTML(qw422016, rq, hyphaName, textAreaFill, warning, renderedPage)
|
StreamPreviewHTML(qw422016, rq, hyphaName, textAreaFill, warning, renderedPage)
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
func PreviewHTML(rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) string {
|
func PreviewHTML(rq *http.Request, hyphaName, textAreaFill, warning string, renderedPage string) string {
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
WritePreviewHTML(qb422016, rq, hyphaName, textAreaFill, warning, renderedPage)
|
WritePreviewHTML(qb422016, rq, hyphaName, textAreaFill, warning, renderedPage)
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/mutators.qtpl:106
|
//line views/mutators.qtpl:111
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/mutators.qtpl:113
|
||||||
|
func streameditScripts(qw422016 *qt422016.Writer) {
|
||||||
|
//line views/mutators.qtpl:113
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/mutators.qtpl:114
|
||||||
|
for _, scriptPath := range cfg.EditScripts {
|
||||||
|
//line views/mutators.qtpl:114
|
||||||
|
qw422016.N().S(`
|
||||||
|
<script src="`)
|
||||||
|
//line views/mutators.qtpl:115
|
||||||
|
qw422016.E().S(scriptPath)
|
||||||
|
//line views/mutators.qtpl:115
|
||||||
|
qw422016.N().S(`"></script>
|
||||||
|
`)
|
||||||
|
//line views/mutators.qtpl:116
|
||||||
|
}
|
||||||
|
//line views/mutators.qtpl:116
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
func writeeditScripts(qq422016 qtio422016.Writer) {
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
streameditScripts(qw422016)
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
func editScripts() string {
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
writeeditScripts(qb422016)
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
|
return qs422016
|
||||||
|
//line views/mutators.qtpl:117
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{% import "net/http" %}
|
{% import "net/http" %}
|
||||||
{% import "strings" %}
|
{% import "strings" %}
|
||||||
|
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||||
|
|
||||||
@ -48,13 +49,18 @@ var navEntries = []navEntry{
|
|||||||
|
|
||||||
{% func UserMenuHTML(u *user.User) %}
|
{% func UserMenuHTML(u *user.User) %}
|
||||||
{% if user.AuthUsed %}
|
{% if user.AuthUsed %}
|
||||||
<li class="header-links__entry header-links__entry_user">
|
<li class="header-links__entry header-links__entry_user">
|
||||||
{% if u.Group == "anon" %}
|
{% if u.Group == "anon" %}
|
||||||
<a href="/login" class="header-links__link">Login</a>
|
<a href="/login" class="header-links__link">Login</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="/hypha/{%s util.UserHypha %}/{%s u.Name %}" class="header-links__link">{%s u.Name %}</a>
|
<a href="/hypha/{%s cfg.UserHypha %}/{%s u.Name %}" class="header-links__link">{%s util.BeautifulName(u.Name) %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if user.AuthUsed && cfg.UseRegistration && u.Group == "anon" %}
|
||||||
|
<li class="header-links__entry header-links__entry_register">
|
||||||
|
<a href="/register" class="header-links__link">Register</a>
|
||||||
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
|
@ -11,27 +11,30 @@ import "net/http"
|
|||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
//line views/nav.qtpl:3
|
//line views/nav.qtpl:3
|
||||||
import "github.com/bouncepaw/mycorrhiza/user"
|
import "github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
|
||||||
//line views/nav.qtpl:4
|
//line views/nav.qtpl:4
|
||||||
|
import "github.com/bouncepaw/mycorrhiza/user"
|
||||||
|
|
||||||
|
//line views/nav.qtpl:5
|
||||||
import "github.com/bouncepaw/mycorrhiza/util"
|
import "github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
|
||||||
// This is the <nav> seen on top of many pages.
|
// This is the <nav> seen on top of many pages.
|
||||||
|
|
||||||
//line views/nav.qtpl:7
|
//line views/nav.qtpl:8
|
||||||
import (
|
import (
|
||||||
qtio422016 "io"
|
qtio422016 "io"
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
qt422016 "github.com/valyala/quicktemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/nav.qtpl:7
|
//line views/nav.qtpl:8
|
||||||
var (
|
var (
|
||||||
_ = qtio422016.Copy
|
_ = qtio422016.Copy
|
||||||
_ = qt422016.AcquireByteBuffer
|
_ = qt422016.AcquireByteBuffer
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/nav.qtpl:8
|
//line views/nav.qtpl:9
|
||||||
type navEntry struct {
|
type navEntry struct {
|
||||||
path string
|
path string
|
||||||
title string
|
title string
|
||||||
@ -48,268 +51,281 @@ var navEntries = []navEntry{
|
|||||||
{"text", "Raw text"},
|
{"text", "Raw text"},
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:24
|
//line views/nav.qtpl:25
|
||||||
func StreamNavHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, navType string, revisionHash ...string) {
|
func StreamNavHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName, navType string, revisionHash ...string) {
|
||||||
//line views/nav.qtpl:24
|
//line views/nav.qtpl:25
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:26
|
//line views/nav.qtpl:27
|
||||||
u := user.FromRequest(rq)
|
u := user.FromRequest(rq)
|
||||||
|
|
||||||
//line views/nav.qtpl:27
|
//line views/nav.qtpl:28
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<nav class="hypha-tabs main-width">
|
<nav class="hypha-tabs main-width">
|
||||||
<ul class="hypha-tabs__flex">
|
<ul class="hypha-tabs__flex">
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:30
|
//line views/nav.qtpl:31
|
||||||
for _, entry := range navEntries {
|
for _, entry := range navEntries {
|
||||||
//line views/nav.qtpl:31
|
//line views/nav.qtpl:32
|
||||||
if navType == "revision" && entry.path == "revision" {
|
if navType == "revision" && entry.path == "revision" {
|
||||||
//line views/nav.qtpl:31
|
//line views/nav.qtpl:32
|
||||||
qw422016.N().S(` <li class="hypha-tabs__tab hypha-tabs__tab_active">
|
qw422016.N().S(` <li class="hypha-tabs__tab hypha-tabs__tab_active">
|
||||||
<span class="hypha-tabs__selection">`)
|
<span class="hypha-tabs__selection">`)
|
||||||
//line views/nav.qtpl:33
|
//line views/nav.qtpl:34
|
||||||
qw422016.E().S(revisionHash[0])
|
qw422016.E().S(revisionHash[0])
|
||||||
//line views/nav.qtpl:33
|
//line views/nav.qtpl:34
|
||||||
qw422016.N().S(`</span>
|
qw422016.N().S(`</span>
|
||||||
</li>
|
</li>
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:35
|
//line views/nav.qtpl:36
|
||||||
} else if navType == entry.path {
|
} else if navType == entry.path {
|
||||||
//line views/nav.qtpl:35
|
//line views/nav.qtpl:36
|
||||||
qw422016.N().S(` <li class="hypha-tabs__tab hypha-tabs__tab_active">
|
qw422016.N().S(` <li class="hypha-tabs__tab hypha-tabs__tab_active">
|
||||||
<span class="hypha-tabs__selection">`)
|
<span class="hypha-tabs__selection">`)
|
||||||
//line views/nav.qtpl:37
|
//line views/nav.qtpl:38
|
||||||
qw422016.E().S(entry.title)
|
qw422016.E().S(entry.title)
|
||||||
//line views/nav.qtpl:37
|
//line views/nav.qtpl:38
|
||||||
qw422016.N().S(`</span>
|
qw422016.N().S(`</span>
|
||||||
</li>
|
</li>
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:39
|
//line views/nav.qtpl:40
|
||||||
} else if entry.path != "revision" && u.CanProceed(entry.path) {
|
} else if entry.path != "revision" && u.CanProceed(entry.path) {
|
||||||
//line views/nav.qtpl:39
|
//line views/nav.qtpl:40
|
||||||
qw422016.N().S(` <li class="hypha-tabs__tab">
|
qw422016.N().S(` <li class="hypha-tabs__tab">
|
||||||
<a class="hypha-tabs__link" href="/`)
|
<a class="hypha-tabs__link" href="/`)
|
||||||
//line views/nav.qtpl:41
|
//line views/nav.qtpl:42
|
||||||
qw422016.E().S(entry.path)
|
qw422016.E().S(entry.path)
|
||||||
//line views/nav.qtpl:41
|
//line views/nav.qtpl:42
|
||||||
qw422016.N().S(`/`)
|
qw422016.N().S(`/`)
|
||||||
//line views/nav.qtpl:41
|
//line views/nav.qtpl:42
|
||||||
qw422016.E().S(hyphaName)
|
qw422016.E().S(hyphaName)
|
||||||
//line views/nav.qtpl:41
|
//line views/nav.qtpl:42
|
||||||
qw422016.N().S(`">`)
|
qw422016.N().S(`">`)
|
||||||
//line views/nav.qtpl:41
|
//line views/nav.qtpl:42
|
||||||
qw422016.E().S(entry.title)
|
qw422016.E().S(entry.title)
|
||||||
//line views/nav.qtpl:41
|
//line views/nav.qtpl:42
|
||||||
qw422016.N().S(`</a>
|
qw422016.N().S(`</a>
|
||||||
</li>
|
</li>
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:43
|
//line views/nav.qtpl:44
|
||||||
}
|
}
|
||||||
//line views/nav.qtpl:44
|
//line views/nav.qtpl:45
|
||||||
}
|
}
|
||||||
//line views/nav.qtpl:44
|
//line views/nav.qtpl:45
|
||||||
qw422016.N().S(` </ul>
|
qw422016.N().S(` </ul>
|
||||||
</nav>
|
</nav>
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
func WriteNavHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, navType string, revisionHash ...string) {
|
func WriteNavHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName, navType string, revisionHash ...string) {
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
StreamNavHTML(qw422016, rq, hyphaName, navType, revisionHash...)
|
StreamNavHTML(qw422016, rq, hyphaName, navType, revisionHash...)
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
func NavHTML(rq *http.Request, hyphaName, navType string, revisionHash ...string) string {
|
func NavHTML(rq *http.Request, hyphaName, navType string, revisionHash ...string) string {
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
WriteNavHTML(qb422016, rq, hyphaName, navType, revisionHash...)
|
WriteNavHTML(qb422016, rq, hyphaName, navType, revisionHash...)
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/nav.qtpl:47
|
//line views/nav.qtpl:48
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:49
|
//line views/nav.qtpl:50
|
||||||
func StreamUserMenuHTML(qw422016 *qt422016.Writer, u *user.User) {
|
func StreamUserMenuHTML(qw422016 *qt422016.Writer, u *user.User) {
|
||||||
//line views/nav.qtpl:49
|
//line views/nav.qtpl:50
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:50
|
//line views/nav.qtpl:51
|
||||||
if user.AuthUsed {
|
if user.AuthUsed {
|
||||||
//line views/nav.qtpl:50
|
//line views/nav.qtpl:51
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<li class="header-links__entry header-links__entry_user">
|
<li class="header-links__entry header-links__entry_user">
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:52
|
//line views/nav.qtpl:53
|
||||||
if u.Group == "anon" {
|
if u.Group == "anon" {
|
||||||
//line views/nav.qtpl:52
|
//line views/nav.qtpl:53
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<a href="/login" class="header-links__link">Login</a>
|
<a href="/login" class="header-links__link">Login</a>
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:54
|
//line views/nav.qtpl:55
|
||||||
} else {
|
} else {
|
||||||
//line views/nav.qtpl:54
|
//line views/nav.qtpl:55
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<a href="/hypha/`)
|
<a href="/hypha/`)
|
||||||
//line views/nav.qtpl:55
|
//line views/nav.qtpl:56
|
||||||
qw422016.E().S(util.UserHypha)
|
qw422016.E().S(cfg.UserHypha)
|
||||||
//line views/nav.qtpl:55
|
//line views/nav.qtpl:56
|
||||||
qw422016.N().S(`/`)
|
qw422016.N().S(`/`)
|
||||||
//line views/nav.qtpl:55
|
//line views/nav.qtpl:56
|
||||||
qw422016.E().S(u.Name)
|
qw422016.E().S(u.Name)
|
||||||
//line views/nav.qtpl:55
|
//line views/nav.qtpl:56
|
||||||
qw422016.N().S(`" class="header-links__link">`)
|
qw422016.N().S(`" class="header-links__link">`)
|
||||||
//line views/nav.qtpl:55
|
//line views/nav.qtpl:56
|
||||||
qw422016.E().S(u.Name)
|
qw422016.E().S(util.BeautifulName(u.Name))
|
||||||
//line views/nav.qtpl:55
|
//line views/nav.qtpl:56
|
||||||
qw422016.N().S(`</a>
|
qw422016.N().S(`</a>
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:56
|
//line views/nav.qtpl:57
|
||||||
}
|
}
|
||||||
//line views/nav.qtpl:56
|
//line views/nav.qtpl:57
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</li>
|
</li>
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:58
|
//line views/nav.qtpl:59
|
||||||
}
|
}
|
||||||
//line views/nav.qtpl:58
|
//line views/nav.qtpl:59
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:60
|
||||||
|
if user.AuthUsed && cfg.UseRegistration && u.Group == "anon" {
|
||||||
|
//line views/nav.qtpl:60
|
||||||
|
qw422016.N().S(`
|
||||||
|
<li class="header-links__entry header-links__entry_register">
|
||||||
|
<a href="/register" class="header-links__link">Register</a>
|
||||||
|
</li>
|
||||||
|
`)
|
||||||
|
//line views/nav.qtpl:64
|
||||||
|
}
|
||||||
|
//line views/nav.qtpl:64
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/nav.qtpl:65
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
func WriteUserMenuHTML(qq422016 qtio422016.Writer, u *user.User) {
|
func WriteUserMenuHTML(qq422016 qtio422016.Writer, u *user.User) {
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
StreamUserMenuHTML(qw422016, u)
|
StreamUserMenuHTML(qw422016, u)
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
func UserMenuHTML(u *user.User) string {
|
func UserMenuHTML(u *user.User) string {
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
WriteUserMenuHTML(qb422016, u)
|
WriteUserMenuHTML(qb422016, u)
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/nav.qtpl:59
|
//line views/nav.qtpl:65
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:61
|
//line views/nav.qtpl:67
|
||||||
func StreamRelativeHyphaeHTML(qw422016 *qt422016.Writer, relatives string) {
|
func StreamRelativeHyphaeHTML(qw422016 *qt422016.Writer, relatives string) {
|
||||||
//line views/nav.qtpl:61
|
//line views/nav.qtpl:67
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<aside class="relative-hyphae layout-card">
|
<aside class="relative-hyphae layout-card">
|
||||||
<h2 class="relative-hyphae__title layout-card__title">Relative hyphae</h2>
|
<h2 class="relative-hyphae__title layout-card__title">Relative hyphae</h2>
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:64
|
//line views/nav.qtpl:70
|
||||||
qw422016.N().S(relatives)
|
qw422016.N().S(relatives)
|
||||||
//line views/nav.qtpl:64
|
//line views/nav.qtpl:70
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</aside>
|
</aside>
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
func WriteRelativeHyphaeHTML(qq422016 qtio422016.Writer, relatives string) {
|
func WriteRelativeHyphaeHTML(qq422016 qtio422016.Writer, relatives string) {
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
StreamRelativeHyphaeHTML(qw422016, relatives)
|
StreamRelativeHyphaeHTML(qw422016, relatives)
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
func RelativeHyphaeHTML(relatives string) string {
|
func RelativeHyphaeHTML(relatives string) string {
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
WriteRelativeHyphaeHTML(qb422016, relatives)
|
WriteRelativeHyphaeHTML(qb422016, relatives)
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/nav.qtpl:66
|
//line views/nav.qtpl:72
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:68
|
//line views/nav.qtpl:74
|
||||||
func StreamSubhyphaeHTML(qw422016 *qt422016.Writer, subhyphae string) {
|
func StreamSubhyphaeHTML(qw422016 *qt422016.Writer, subhyphae string) {
|
||||||
//line views/nav.qtpl:68
|
//line views/nav.qtpl:74
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:69
|
//line views/nav.qtpl:75
|
||||||
if strings.TrimSpace(subhyphae) != "" {
|
if strings.TrimSpace(subhyphae) != "" {
|
||||||
//line views/nav.qtpl:69
|
//line views/nav.qtpl:75
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<section class="subhyphae">
|
<section class="subhyphae">
|
||||||
<h2 class="subhyphae__title">Subhyphae</h2>
|
<h2 class="subhyphae__title">Subhyphae</h2>
|
||||||
<nav class="subhyphae__nav">
|
<nav class="subhyphae__nav">
|
||||||
<ul class="subhyphae__list">
|
<ul class="subhyphae__list">
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:74
|
//line views/nav.qtpl:80
|
||||||
qw422016.N().S(subhyphae)
|
qw422016.N().S(subhyphae)
|
||||||
//line views/nav.qtpl:74
|
//line views/nav.qtpl:80
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</section>
|
</section>
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:78
|
//line views/nav.qtpl:84
|
||||||
}
|
}
|
||||||
//line views/nav.qtpl:78
|
//line views/nav.qtpl:84
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
func WriteSubhyphaeHTML(qq422016 qtio422016.Writer, subhyphae string) {
|
func WriteSubhyphaeHTML(qq422016 qtio422016.Writer, subhyphae string) {
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
StreamSubhyphaeHTML(qw422016, subhyphae)
|
StreamSubhyphaeHTML(qw422016, subhyphae)
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
func SubhyphaeHTML(subhyphae string) string {
|
func SubhyphaeHTML(subhyphae string) string {
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
WriteSubhyphaeHTML(qb422016, subhyphae)
|
WriteSubhyphaeHTML(qb422016, subhyphae)
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/nav.qtpl:79
|
//line views/nav.qtpl:85
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
{% import "path" %}
|
{% import "path" %}
|
||||||
{% import "os" %}
|
{% import "os" %}
|
||||||
|
|
||||||
|
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/mimetype" %}
|
{% import "github.com/bouncepaw/mycorrhiza/mimetype" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/tree" %}
|
{% import "github.com/bouncepaw/mycorrhiza/tree" %}
|
||||||
@ -46,12 +47,14 @@
|
|||||||
{% if u.CanProceed("upload-binary") %}
|
{% if u.CanProceed("upload-binary") %}
|
||||||
<form action="/upload-binary/{%s h.Name %}"
|
<form action="/upload-binary/{%s h.Name %}"
|
||||||
method="post" enctype="multipart/form-data"
|
method="post" enctype="multipart/form-data"
|
||||||
class="modal amnt-menu-block">
|
class="upload-binary modal amnt-menu-block">
|
||||||
<fieldset class="modal__fieldset upload-binary">
|
<fieldset class="modal__fieldset">
|
||||||
<legend class="modal__title modal__title_small">Attach</legend>
|
<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>
|
<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">
|
<label for="upload-binary__input"></label>
|
||||||
<input type="submit" class="modal__action modal__submit">
|
<input type="file" id="upload-binary__input" name="binary">
|
||||||
|
|
||||||
|
<input type="submit" class="btn stick-to-bottom" value="Upload">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -61,7 +64,7 @@
|
|||||||
<fieldset class="modal__fieldset">
|
<fieldset class="modal__fieldset">
|
||||||
<legend class="modal__title modal__title_small">Unattach</legend>
|
<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>
|
<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">
|
<input type="submit" class="btn" value="Unattach">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -75,26 +78,17 @@ If `contents` == "", a helpful message is shown instead.
|
|||||||
{% func HyphaHTML(rq *http.Request, h *hyphae.Hypha, contents string) %}
|
{% func HyphaHTML(rq *http.Request, h *hyphae.Hypha, contents string) %}
|
||||||
{% code
|
{% code
|
||||||
relatives, subhyphae, prevHyphaName, nextHyphaName := tree.Tree(h.Name)
|
relatives, subhyphae, prevHyphaName, nextHyphaName := tree.Tree(h.Name)
|
||||||
|
u := user.FromRequest(rq)
|
||||||
%}
|
%}
|
||||||
{%= NavHTML(rq, h.Name, "page") %}
|
{%= NavHTML(rq, h.Name, "page") %}
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<article>
|
<article>
|
||||||
{%s= NaviTitleHTML(h) %}
|
{%s= NaviTitleHTML(h) %}
|
||||||
{% if contents == "" %}
|
{% if h.Exists %}
|
||||||
<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 %}
|
{%s= contents %}
|
||||||
|
{% else %}
|
||||||
|
{%= nonExistentHyphaNotice(h, u) %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</article>
|
</article>
|
||||||
<section class="prevnext">
|
<section class="prevnext">
|
||||||
@ -109,6 +103,7 @@ If `contents` == "", a helpful message is shown instead.
|
|||||||
</main>
|
</main>
|
||||||
{%= RelativeHyphaeHTML(relatives) %}
|
{%= RelativeHyphaeHTML(relatives) %}
|
||||||
</div>
|
</div>
|
||||||
|
{%= viewScripts() %}
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
{% func RevisionHTML(rq *http.Request, h *hyphae.Hypha, contents, revHash string) %}
|
{% func RevisionHTML(rq *http.Request, h *hyphae.Hypha, contents, revHash string) %}
|
||||||
@ -127,4 +122,11 @@ If `contents` == "", a helpful message is shown instead.
|
|||||||
</main>
|
</main>
|
||||||
{%= RelativeHyphaeHTML(relatives) %}
|
{%= RelativeHyphaeHTML(relatives) %}
|
||||||
</div>
|
</div>
|
||||||
|
{%= viewScripts() %}
|
||||||
|
{% endfunc %}
|
||||||
|
|
||||||
|
{% func viewScripts() %}
|
||||||
|
{% for _, scriptPath := range cfg.ViewScripts %}
|
||||||
|
<script src="{%s scriptPath %}"></script>
|
||||||
|
{% endfor %}
|
||||||
{% endfunc %}
|
{% endfunc %}
|
@ -17,433 +17,476 @@ import "path"
|
|||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
//line views/readers.qtpl:6
|
//line views/readers.qtpl:6
|
||||||
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
import "github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
|
||||||
//line views/readers.qtpl:7
|
//line views/readers.qtpl:7
|
||||||
import "github.com/bouncepaw/mycorrhiza/mimetype"
|
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
|
|
||||||
//line views/readers.qtpl:8
|
//line views/readers.qtpl:8
|
||||||
import "github.com/bouncepaw/mycorrhiza/tree"
|
import "github.com/bouncepaw/mycorrhiza/mimetype"
|
||||||
|
|
||||||
//line views/readers.qtpl:9
|
//line views/readers.qtpl:9
|
||||||
import "github.com/bouncepaw/mycorrhiza/user"
|
import "github.com/bouncepaw/mycorrhiza/tree"
|
||||||
|
|
||||||
//line views/readers.qtpl:10
|
//line views/readers.qtpl:10
|
||||||
|
import "github.com/bouncepaw/mycorrhiza/user"
|
||||||
|
|
||||||
|
//line views/readers.qtpl:11
|
||||||
import "github.com/bouncepaw/mycorrhiza/util"
|
import "github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
|
||||||
//line views/readers.qtpl:12
|
//line views/readers.qtpl:13
|
||||||
import (
|
import (
|
||||||
qtio422016 "io"
|
qtio422016 "io"
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
qt422016 "github.com/valyala/quicktemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/readers.qtpl:12
|
//line views/readers.qtpl:13
|
||||||
var (
|
var (
|
||||||
_ = qtio422016.Copy
|
_ = qtio422016.Copy
|
||||||
_ = qt422016.AcquireByteBuffer
|
_ = qt422016.AcquireByteBuffer
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/readers.qtpl:12
|
//line views/readers.qtpl:13
|
||||||
func StreamAttachmentMenuHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User) {
|
func StreamAttachmentMenuHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User) {
|
||||||
//line views/readers.qtpl:12
|
//line views/readers.qtpl:13
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:13
|
//line views/readers.qtpl:14
|
||||||
StreamNavHTML(qw422016, rq, h.Name, "attachment")
|
StreamNavHTML(qw422016, rq, h.Name, "attachment")
|
||||||
//line views/readers.qtpl:13
|
//line views/readers.qtpl:14
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<h1>Attachment of `)
|
<h1>Attachment of `)
|
||||||
//line views/readers.qtpl:16
|
//line views/readers.qtpl:17
|
||||||
qw422016.E().S(util.BeautifulName(h.Name))
|
qw422016.E().S(util.BeautifulName(h.Name))
|
||||||
//line views/readers.qtpl:16
|
//line views/readers.qtpl:17
|
||||||
qw422016.N().S(`</h1>
|
qw422016.N().S(`</h1>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:17
|
//line views/readers.qtpl:18
|
||||||
if h.BinaryPath == "" {
|
if h.BinaryPath == "" {
|
||||||
//line views/readers.qtpl:17
|
//line views/readers.qtpl:18
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p class="warning">This hypha has no attachment, you can upload it here.</p>
|
<p class="warning">This hypha has no attachment, you can upload it here.</p>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:19
|
//line views/readers.qtpl:20
|
||||||
} else {
|
} else {
|
||||||
//line views/readers.qtpl:19
|
//line views/readers.qtpl:20
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<p class="warning">You can manage the hypha's attachment on this page.</p>
|
<p class="warning">You can manage the hypha's attachment on this page.</p>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:21
|
//line views/readers.qtpl:22
|
||||||
}
|
}
|
||||||
//line views/readers.qtpl:21
|
//line views/readers.qtpl:22
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
|
|
||||||
<section class="amnt-grid">
|
<section class="amnt-grid">
|
||||||
|
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:25
|
//line views/readers.qtpl:26
|
||||||
if h.BinaryPath != "" {
|
if h.BinaryPath != "" {
|
||||||
//line views/readers.qtpl:25
|
//line views/readers.qtpl:26
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:27
|
//line views/readers.qtpl:28
|
||||||
mime := mimetype.FromExtension(path.Ext(h.BinaryPath))
|
mime := mimetype.FromExtension(path.Ext(h.BinaryPath))
|
||||||
fileinfo, err := os.Stat(h.BinaryPath)
|
fileinfo, err := os.Stat(h.BinaryPath)
|
||||||
|
|
||||||
//line views/readers.qtpl:28
|
//line views/readers.qtpl:29
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:29
|
//line views/readers.qtpl:30
|
||||||
if err == nil {
|
if err == nil {
|
||||||
//line views/readers.qtpl:29
|
//line views/readers.qtpl:30
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<fieldset class="amnt-menu-block">
|
<fieldset class="amnt-menu-block">
|
||||||
<legend class="modal__title modal__title_small">Stat</legend>
|
<legend class="modal__title modal__title_small">Stat</legend>
|
||||||
<p class="modal__confirmation-msg"><b>File size:</b> `)
|
<p class="modal__confirmation-msg"><b>File size:</b> `)
|
||||||
//line views/readers.qtpl:32
|
//line views/readers.qtpl:33
|
||||||
qw422016.N().DL(fileinfo.Size())
|
qw422016.N().DL(fileinfo.Size())
|
||||||
//line views/readers.qtpl:32
|
//line views/readers.qtpl:33
|
||||||
qw422016.N().S(` bytes</p>
|
qw422016.N().S(` bytes</p>
|
||||||
<p><b>MIME type:</b> `)
|
<p><b>MIME type:</b> `)
|
||||||
//line views/readers.qtpl:33
|
//line views/readers.qtpl:34
|
||||||
qw422016.E().S(mime)
|
qw422016.E().S(mime)
|
||||||
//line views/readers.qtpl:33
|
//line views/readers.qtpl:34
|
||||||
qw422016.N().S(`</p>
|
qw422016.N().S(`</p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:35
|
//line views/readers.qtpl:36
|
||||||
}
|
}
|
||||||
//line views/readers.qtpl:35
|
//line views/readers.qtpl:36
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
|
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:37
|
//line views/readers.qtpl:38
|
||||||
if strings.HasPrefix(mime, "image/") {
|
if strings.HasPrefix(mime, "image/") {
|
||||||
//line views/readers.qtpl:37
|
//line views/readers.qtpl:38
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<fieldset class="amnt-menu-block">
|
<fieldset class="amnt-menu-block">
|
||||||
<legend class="modal__title modal__title_small">Include</legend>
|
<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>
|
<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 { `)
|
<pre class="codebleck"><code>img { `)
|
||||||
//line views/readers.qtpl:41
|
//line views/readers.qtpl:42
|
||||||
qw422016.E().S(h.Name)
|
qw422016.E().S(h.Name)
|
||||||
//line views/readers.qtpl:41
|
//line views/readers.qtpl:42
|
||||||
qw422016.N().S(` }</code></pre>
|
qw422016.N().S(` }</code></pre>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:43
|
//line views/readers.qtpl:44
|
||||||
}
|
}
|
||||||
//line views/readers.qtpl:43
|
//line views/readers.qtpl:44
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:44
|
//line views/readers.qtpl:45
|
||||||
}
|
}
|
||||||
//line views/readers.qtpl:44
|
//line views/readers.qtpl:45
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
|
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:46
|
//line views/readers.qtpl:47
|
||||||
if u.CanProceed("upload-binary") {
|
if u.CanProceed("upload-binary") {
|
||||||
//line views/readers.qtpl:46
|
//line views/readers.qtpl:47
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<form action="/upload-binary/`)
|
<form action="/upload-binary/`)
|
||||||
//line views/readers.qtpl:47
|
//line views/readers.qtpl:48
|
||||||
qw422016.E().S(h.Name)
|
qw422016.E().S(h.Name)
|
||||||
//line views/readers.qtpl:47
|
//line views/readers.qtpl:48
|
||||||
qw422016.N().S(`"
|
qw422016.N().S(`"
|
||||||
method="post" enctype="multipart/form-data"
|
method="post" enctype="multipart/form-data"
|
||||||
class="modal amnt-menu-block">
|
class="upload-binary modal amnt-menu-block">
|
||||||
<fieldset class="modal__fieldset upload-binary">
|
<fieldset class="modal__fieldset">
|
||||||
<legend class="modal__title modal__title_small">Attach</legend>
|
<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>
|
<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">
|
<label for="upload-binary__input"></label>
|
||||||
<input type="submit" class="modal__action modal__submit">
|
<input type="file" id="upload-binary__input" name="binary">
|
||||||
|
|
||||||
|
<input type="submit" class="btn stick-to-bottom" value="Upload">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:57
|
//line views/readers.qtpl:60
|
||||||
}
|
}
|
||||||
//line views/readers.qtpl:57
|
//line views/readers.qtpl:60
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
|
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:59
|
//line views/readers.qtpl:62
|
||||||
if h.BinaryPath != "" && u.CanProceed("unattach-confirm") {
|
if h.BinaryPath != "" && u.CanProceed("unattach-confirm") {
|
||||||
//line views/readers.qtpl:59
|
//line views/readers.qtpl:62
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<form action="/unattach-confirm/`)
|
<form action="/unattach-confirm/`)
|
||||||
//line views/readers.qtpl:60
|
//line views/readers.qtpl:63
|
||||||
qw422016.E().S(h.Name)
|
qw422016.E().S(h.Name)
|
||||||
//line views/readers.qtpl:60
|
//line views/readers.qtpl:63
|
||||||
qw422016.N().S(`" method="post" class="modal amnt-menu-block">
|
qw422016.N().S(`" method="post" class="modal amnt-menu-block">
|
||||||
<fieldset class="modal__fieldset">
|
<fieldset class="modal__fieldset">
|
||||||
<legend class="modal__title modal__title_small">Unattach</legend>
|
<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>
|
<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">
|
<input type="submit" class="btn" value="Unattach">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:67
|
//line views/readers.qtpl:70
|
||||||
}
|
}
|
||||||
//line views/readers.qtpl:67
|
//line views/readers.qtpl:70
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
func WriteAttachmentMenuHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User) {
|
func WriteAttachmentMenuHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, u *user.User) {
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
StreamAttachmentMenuHTML(qw422016, rq, h, u)
|
StreamAttachmentMenuHTML(qw422016, rq, h, u)
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
func AttachmentMenuHTML(rq *http.Request, h *hyphae.Hypha, u *user.User) string {
|
func AttachmentMenuHTML(rq *http.Request, h *hyphae.Hypha, u *user.User) string {
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
WriteAttachmentMenuHTML(qb422016, rq, h, u)
|
WriteAttachmentMenuHTML(qb422016, rq, h, u)
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/readers.qtpl:72
|
//line views/readers.qtpl:75
|
||||||
}
|
}
|
||||||
|
|
||||||
// If `contents` == "", a helpful message is shown instead.
|
// If `contents` == "", a helpful message is shown instead.
|
||||||
|
|
||||||
//line views/readers.qtpl:75
|
//line views/readers.qtpl:78
|
||||||
func StreamHyphaHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, contents string) {
|
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
|
//line views/readers.qtpl:78
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:79
|
//line views/readers.qtpl:80
|
||||||
|
relatives, subhyphae, prevHyphaName, nextHyphaName := tree.Tree(h.Name)
|
||||||
|
u := user.FromRequest(rq)
|
||||||
|
|
||||||
|
//line views/readers.qtpl:82
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/readers.qtpl:83
|
||||||
StreamNavHTML(qw422016, rq, h.Name, "page")
|
StreamNavHTML(qw422016, rq, h.Name, "page")
|
||||||
//line views/readers.qtpl:79
|
//line views/readers.qtpl:83
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<article>
|
<article>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:83
|
//line views/readers.qtpl:87
|
||||||
qw422016.N().S(NaviTitleHTML(h))
|
qw422016.N().S(NaviTitleHTML(h))
|
||||||
//line views/readers.qtpl:83
|
//line views/readers.qtpl:87
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:84
|
//line views/readers.qtpl:88
|
||||||
if contents == "" {
|
if h.Exists {
|
||||||
//line views/readers.qtpl:84
|
//line views/readers.qtpl:88
|
||||||
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(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:97
|
//line views/readers.qtpl:89
|
||||||
qw422016.N().S(contents)
|
qw422016.N().S(contents)
|
||||||
//line views/readers.qtpl:97
|
//line views/readers.qtpl:89
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:98
|
//line views/readers.qtpl:90
|
||||||
|
} else {
|
||||||
|
//line views/readers.qtpl:90
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/readers.qtpl:91
|
||||||
|
streamnonExistentHyphaNotice(qw422016, h, u)
|
||||||
|
//line views/readers.qtpl:91
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/readers.qtpl:92
|
||||||
}
|
}
|
||||||
//line views/readers.qtpl:98
|
//line views/readers.qtpl:92
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</article>
|
</article>
|
||||||
<section class="prevnext">
|
<section class="prevnext">
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:101
|
//line views/readers.qtpl:95
|
||||||
if prevHyphaName != "" {
|
if prevHyphaName != "" {
|
||||||
//line views/readers.qtpl:101
|
//line views/readers.qtpl:95
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<a class="prevnext__el prevnext__prev" href="/hypha/`)
|
<a class="prevnext__el prevnext__prev" href="/hypha/`)
|
||||||
//line views/readers.qtpl:102
|
//line views/readers.qtpl:96
|
||||||
qw422016.E().S(prevHyphaName)
|
qw422016.E().S(prevHyphaName)
|
||||||
//line views/readers.qtpl:102
|
//line views/readers.qtpl:96
|
||||||
qw422016.N().S(`" rel="prev">← `)
|
qw422016.N().S(`" rel="prev">← `)
|
||||||
//line views/readers.qtpl:102
|
//line views/readers.qtpl:96
|
||||||
qw422016.E().S(util.BeautifulName(path.Base(prevHyphaName)))
|
qw422016.E().S(util.BeautifulName(path.Base(prevHyphaName)))
|
||||||
//line views/readers.qtpl:102
|
//line views/readers.qtpl:96
|
||||||
qw422016.N().S(`</a>
|
qw422016.N().S(`</a>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:103
|
//line views/readers.qtpl:97
|
||||||
}
|
}
|
||||||
//line views/readers.qtpl:103
|
//line views/readers.qtpl:97
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:104
|
//line views/readers.qtpl:98
|
||||||
if nextHyphaName != "" {
|
if nextHyphaName != "" {
|
||||||
//line views/readers.qtpl:104
|
//line views/readers.qtpl:98
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<a class="prevnext__el prevnext__next" href="/hypha/`)
|
<a class="prevnext__el prevnext__next" href="/hypha/`)
|
||||||
//line views/readers.qtpl:105
|
//line views/readers.qtpl:99
|
||||||
qw422016.E().S(nextHyphaName)
|
qw422016.E().S(nextHyphaName)
|
||||||
//line views/readers.qtpl:105
|
//line views/readers.qtpl:99
|
||||||
qw422016.N().S(`" rel="next">`)
|
qw422016.N().S(`" rel="next">`)
|
||||||
//line views/readers.qtpl:105
|
//line views/readers.qtpl:99
|
||||||
qw422016.E().S(util.BeautifulName(path.Base(nextHyphaName)))
|
qw422016.E().S(util.BeautifulName(path.Base(nextHyphaName)))
|
||||||
//line views/readers.qtpl:105
|
//line views/readers.qtpl:99
|
||||||
qw422016.N().S(` →</a>
|
qw422016.N().S(` →</a>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:106
|
//line views/readers.qtpl:100
|
||||||
}
|
}
|
||||||
//line views/readers.qtpl:106
|
//line views/readers.qtpl:100
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</section>
|
</section>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:108
|
//line views/readers.qtpl:102
|
||||||
StreamSubhyphaeHTML(qw422016, subhyphae)
|
StreamSubhyphaeHTML(qw422016, subhyphae)
|
||||||
//line views/readers.qtpl:108
|
//line views/readers.qtpl:102
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</main>
|
</main>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:110
|
//line views/readers.qtpl:104
|
||||||
StreamRelativeHyphaeHTML(qw422016, relatives)
|
StreamRelativeHyphaeHTML(qw422016, relatives)
|
||||||
//line views/readers.qtpl:110
|
//line views/readers.qtpl:104
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:112
|
//line views/readers.qtpl:106
|
||||||
}
|
streamviewScripts(qw422016)
|
||||||
|
//line views/readers.qtpl:106
|
||||||
//line views/readers.qtpl:112
|
|
||||||
func WriteHyphaHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, contents string) {
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
StreamHyphaHTML(qw422016, rq, h, contents)
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
qt422016.ReleaseWriter(qw422016)
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
}
|
|
||||||
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
func HyphaHTML(rq *http.Request, h *hyphae.Hypha, contents string) string {
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
WriteHyphaHTML(qb422016, rq, h, contents)
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
qs422016 := string(qb422016.B)
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
return qs422016
|
|
||||||
//line views/readers.qtpl:112
|
|
||||||
}
|
|
||||||
|
|
||||||
//line views/readers.qtpl:114
|
|
||||||
func StreamRevisionHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, contents, revHash string) {
|
|
||||||
//line views/readers.qtpl:114
|
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:116
|
//line views/readers.qtpl:107
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
func WriteHyphaHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, contents string) {
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
StreamHyphaHTML(qw422016, rq, h, contents)
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
func HyphaHTML(rq *http.Request, h *hyphae.Hypha, contents string) string {
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
WriteHyphaHTML(qb422016, rq, h, contents)
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
return qs422016
|
||||||
|
//line views/readers.qtpl:107
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/readers.qtpl:109
|
||||||
|
func StreamRevisionHTML(qw422016 *qt422016.Writer, rq *http.Request, h *hyphae.Hypha, contents, revHash string) {
|
||||||
|
//line views/readers.qtpl:109
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/readers.qtpl:111
|
||||||
relatives, subhyphae, _, _ := tree.Tree(h.Name)
|
relatives, subhyphae, _, _ := tree.Tree(h.Name)
|
||||||
|
|
||||||
//line views/readers.qtpl:117
|
//line views/readers.qtpl:112
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:118
|
//line views/readers.qtpl:113
|
||||||
StreamNavHTML(qw422016, rq, h.Name, "revision", revHash)
|
StreamNavHTML(qw422016, rq, h.Name, "revision", revHash)
|
||||||
//line views/readers.qtpl:118
|
//line views/readers.qtpl:113
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<article>
|
<article>
|
||||||
<p>Please note that viewing binary parts of hyphae is not supported in history for now.</p>
|
<p>Please note that viewing binary parts of hyphae is not supported in history for now.</p>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:123
|
//line views/readers.qtpl:118
|
||||||
qw422016.N().S(NaviTitleHTML(h))
|
qw422016.N().S(NaviTitleHTML(h))
|
||||||
//line views/readers.qtpl:123
|
//line views/readers.qtpl:118
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:124
|
//line views/readers.qtpl:119
|
||||||
qw422016.N().S(contents)
|
qw422016.N().S(contents)
|
||||||
//line views/readers.qtpl:124
|
//line views/readers.qtpl:119
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</article>
|
</article>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:126
|
//line views/readers.qtpl:121
|
||||||
StreamSubhyphaeHTML(qw422016, subhyphae)
|
StreamSubhyphaeHTML(qw422016, subhyphae)
|
||||||
//line views/readers.qtpl:126
|
//line views/readers.qtpl:121
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</main>
|
</main>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:128
|
//line views/readers.qtpl:123
|
||||||
StreamRelativeHyphaeHTML(qw422016, relatives)
|
StreamRelativeHyphaeHTML(qw422016, relatives)
|
||||||
//line views/readers.qtpl:128
|
//line views/readers.qtpl:123
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:125
|
||||||
|
streamviewScripts(qw422016)
|
||||||
|
//line views/readers.qtpl:125
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/readers.qtpl:126
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
func WriteRevisionHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, contents, revHash string) {
|
func WriteRevisionHTML(qq422016 qtio422016.Writer, rq *http.Request, h *hyphae.Hypha, contents, revHash string) {
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
StreamRevisionHTML(qw422016, rq, h, contents, revHash)
|
StreamRevisionHTML(qw422016, rq, h, contents, revHash)
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
func RevisionHTML(rq *http.Request, h *hyphae.Hypha, contents, revHash string) string {
|
func RevisionHTML(rq *http.Request, h *hyphae.Hypha, contents, revHash string) string {
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
WriteRevisionHTML(qb422016, rq, h, contents, revHash)
|
WriteRevisionHTML(qb422016, rq, h, contents, revHash)
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/readers.qtpl:130
|
//line views/readers.qtpl:126
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/readers.qtpl:128
|
||||||
|
func streamviewScripts(qw422016 *qt422016.Writer) {
|
||||||
|
//line views/readers.qtpl:128
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/readers.qtpl:129
|
||||||
|
for _, scriptPath := range cfg.ViewScripts {
|
||||||
|
//line views/readers.qtpl:129
|
||||||
|
qw422016.N().S(`
|
||||||
|
<script src="`)
|
||||||
|
//line views/readers.qtpl:130
|
||||||
|
qw422016.E().S(scriptPath)
|
||||||
|
//line views/readers.qtpl:130
|
||||||
|
qw422016.N().S(`"></script>
|
||||||
|
`)
|
||||||
|
//line views/readers.qtpl:131
|
||||||
|
}
|
||||||
|
//line views/readers.qtpl:131
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
func writeviewScripts(qq422016 qtio422016.Writer) {
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
streamviewScripts(qw422016)
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
func viewScripts() string {
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
writeviewScripts(qb422016)
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line views/readers.qtpl:132
|
||||||
|
return qs422016
|
||||||
|
//line views/readers.qtpl:132
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{% import "path/filepath" %}
|
{% import "path/filepath" %}
|
||||||
|
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
{% import "github.com/bouncepaw/mycorrhiza/user" %}
|
||||||
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
{% import "github.com/bouncepaw/mycorrhiza/util" %}
|
||||||
@ -9,7 +10,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="stylesheet" type="text/css" href="/static/common.css">
|
<link rel="stylesheet" type="text/css" href="/assets/common.css">
|
||||||
<title>{%s title %}</title>
|
<title>{%s title %}</title>
|
||||||
{% for _, el := range headElements %}{%s= el %}{% endfor %}
|
{% for _, el := range headElements %}{%s= el %}{% endfor %}
|
||||||
</head>
|
</head>
|
||||||
@ -17,7 +18,7 @@
|
|||||||
<header>
|
<header>
|
||||||
<nav class="header-links main-width">
|
<nav class="header-links main-width">
|
||||||
<ul class="header-links__list">
|
<ul class="header-links__list">
|
||||||
{%- for _, link := range util.HeaderLinks -%}
|
{%- for _, link := range cfg.HeaderLinks -%}
|
||||||
<li class="header-links__entry"><a class="header-links__link" href="{%s link.Href %}">{%s link.Display %}</a></li>
|
<li class="header-links__entry"><a class="header-links__link" href="{%s link.Href %}">{%s link.Display %}</a></li>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
{%s= UserMenuHTML(u) %}
|
{%s= UserMenuHTML(u) %}
|
||||||
@ -25,6 +26,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
{%s= body %}
|
{%s= body %}
|
||||||
|
{%= omnipresentScripts() %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
@ -53,19 +55,19 @@ for u := range user.YieldUsers() {
|
|||||||
<section>
|
<section>
|
||||||
<h2>Admins</h2>
|
<h2>Admins</h2>
|
||||||
<ol>{% for _, name := range admins %}
|
<ol>{% for _, name := range admins %}
|
||||||
<li><a href="/page/{%s util.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
<li><a href="/page/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
||||||
{% endfor %}</ol>
|
{% endfor %}</ol>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Moderators</h2>
|
<h2>Moderators</h2>
|
||||||
<ol>{% for _, name := range moderators %}
|
<ol>{% for _, name := range moderators %}
|
||||||
<li><a href="/page/{%s util.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
<li><a href="/page/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
||||||
{% endfor %}</ol>
|
{% endfor %}</ol>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Editors</h2>
|
<h2>Editors</h2>
|
||||||
<ol>{% for _, name := range editors %}
|
<ol>{% for _, name := range editors %}
|
||||||
<li><a href="/page/{%s util.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
<li><a href="/page/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
|
||||||
{% endfor %}</ol>
|
{% endfor %}</ol>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
@ -95,16 +97,16 @@ for u := range user.YieldUsers() {
|
|||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<section>
|
<section>
|
||||||
<h1>About {%s util.SiteName %}</h1>
|
<h1>About {%s cfg.WikiName %}</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> 1.1.0</li>
|
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> 1.2.0 indev</li>
|
||||||
{%- if user.AuthUsed -%}
|
{%- if user.AuthUsed -%}
|
||||||
<li><b>User count:</b> {%d user.Count() %}</li>
|
<li><b>User count:</b> {%d user.Count() %}</li>
|
||||||
<li><b>Home page:</b> <a href="/">{%s util.HomePage %}</a></li>
|
<li><b>Home page:</b> <a href="/">{%s cfg.HomeHypha %}</a></li>
|
||||||
<li><b>Administrators:</b> {%- for i, username := range user.ListUsersWithGroup("admin") -%}
|
<li><b>Administrators:</b> {%- for i, username := range user.ListUsersWithGroup("admin") -%}
|
||||||
{%- if i > 0 -%}<span aria-hidden="true">, </span>
|
{%- if i > 0 -%}<span aria-hidden="true">, </span>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
<a href="/page/{%s util.UserHypha %}/{%s username %}">{%s username %}</a>{%- endfor -%}</li>
|
<a href="/page/{%s cfg.UserHypha %}/{%s username %}">{%s username %}</a>{%- endfor -%}</li>
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
<li>This wiki does not use authorization</li>
|
<li>This wiki does not use authorization</li>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
@ -151,3 +153,9 @@ for u := range user.YieldUsers() {
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
|
{% func omnipresentScripts() %}
|
||||||
|
{% for _, scriptPath := range cfg.OmnipresentScripts %}
|
||||||
|
<script src="{%s scriptPath %}"></script>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfunc %}
|
@ -8,50 +8,53 @@ package views
|
|||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
|
|
||||||
//line views/stuff.qtpl:2
|
//line views/stuff.qtpl:2
|
||||||
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
import "github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
|
||||||
//line views/stuff.qtpl:3
|
//line views/stuff.qtpl:3
|
||||||
import "github.com/bouncepaw/mycorrhiza/user"
|
import "github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
|
|
||||||
//line views/stuff.qtpl:4
|
//line views/stuff.qtpl:4
|
||||||
|
import "github.com/bouncepaw/mycorrhiza/user"
|
||||||
|
|
||||||
|
//line views/stuff.qtpl:5
|
||||||
import "github.com/bouncepaw/mycorrhiza/util"
|
import "github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
|
||||||
//line views/stuff.qtpl:6
|
//line views/stuff.qtpl:7
|
||||||
import (
|
import (
|
||||||
qtio422016 "io"
|
qtio422016 "io"
|
||||||
|
|
||||||
qt422016 "github.com/valyala/quicktemplate"
|
qt422016 "github.com/valyala/quicktemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/stuff.qtpl:6
|
//line views/stuff.qtpl:7
|
||||||
var (
|
var (
|
||||||
_ = qtio422016.Copy
|
_ = qtio422016.Copy
|
||||||
_ = qt422016.AcquireByteBuffer
|
_ = qt422016.AcquireByteBuffer
|
||||||
)
|
)
|
||||||
|
|
||||||
//line views/stuff.qtpl:6
|
//line views/stuff.qtpl:7
|
||||||
func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string, u *user.User, headElements ...string) {
|
func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string, u *user.User, headElements ...string) {
|
||||||
//line views/stuff.qtpl:6
|
//line views/stuff.qtpl:7
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="stylesheet" type="text/css" href="/static/common.css">
|
<link rel="stylesheet" type="text/css" href="/assets/common.css">
|
||||||
<title>`)
|
<title>`)
|
||||||
//line views/stuff.qtpl:13
|
//line views/stuff.qtpl:14
|
||||||
qw422016.E().S(title)
|
qw422016.E().S(title)
|
||||||
//line views/stuff.qtpl:13
|
//line views/stuff.qtpl:14
|
||||||
qw422016.N().S(`</title>
|
qw422016.N().S(`</title>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:14
|
//line views/stuff.qtpl:15
|
||||||
for _, el := range headElements {
|
for _, el := range headElements {
|
||||||
//line views/stuff.qtpl:14
|
//line views/stuff.qtpl:15
|
||||||
qw422016.N().S(el)
|
qw422016.N().S(el)
|
||||||
//line views/stuff.qtpl:14
|
//line views/stuff.qtpl:15
|
||||||
}
|
}
|
||||||
//line views/stuff.qtpl:14
|
//line views/stuff.qtpl:15
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -59,76 +62,81 @@ func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string, u *user.User,
|
|||||||
<nav class="header-links main-width">
|
<nav class="header-links main-width">
|
||||||
<ul class="header-links__list">
|
<ul class="header-links__list">
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:20
|
//line views/stuff.qtpl:21
|
||||||
for _, link := range util.HeaderLinks {
|
for _, link := range cfg.HeaderLinks {
|
||||||
//line views/stuff.qtpl:20
|
//line views/stuff.qtpl:21
|
||||||
qw422016.N().S(` <li class="header-links__entry"><a class="header-links__link" href="`)
|
qw422016.N().S(` <li class="header-links__entry"><a class="header-links__link" href="`)
|
||||||
//line views/stuff.qtpl:21
|
//line views/stuff.qtpl:22
|
||||||
qw422016.E().S(link.Href)
|
qw422016.E().S(link.Href)
|
||||||
//line views/stuff.qtpl:21
|
//line views/stuff.qtpl:22
|
||||||
qw422016.N().S(`">`)
|
qw422016.N().S(`">`)
|
||||||
//line views/stuff.qtpl:21
|
//line views/stuff.qtpl:22
|
||||||
qw422016.E().S(link.Display)
|
qw422016.E().S(link.Display)
|
||||||
//line views/stuff.qtpl:21
|
//line views/stuff.qtpl:22
|
||||||
qw422016.N().S(`</a></li>
|
qw422016.N().S(`</a></li>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:22
|
//line views/stuff.qtpl:23
|
||||||
}
|
}
|
||||||
//line views/stuff.qtpl:22
|
//line views/stuff.qtpl:23
|
||||||
qw422016.N().S(` `)
|
qw422016.N().S(` `)
|
||||||
//line views/stuff.qtpl:23
|
//line views/stuff.qtpl:24
|
||||||
qw422016.N().S(UserMenuHTML(u))
|
qw422016.N().S(UserMenuHTML(u))
|
||||||
//line views/stuff.qtpl:23
|
//line views/stuff.qtpl:24
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:27
|
//line views/stuff.qtpl:28
|
||||||
qw422016.N().S(body)
|
qw422016.N().S(body)
|
||||||
//line views/stuff.qtpl:27
|
//line views/stuff.qtpl:28
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/stuff.qtpl:29
|
||||||
|
streamomnipresentScripts(qw422016)
|
||||||
|
//line views/stuff.qtpl:29
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
func WriteBaseHTML(qq422016 qtio422016.Writer, title, body string, u *user.User, headElements ...string) {
|
func WriteBaseHTML(qq422016 qtio422016.Writer, title, body string, u *user.User, headElements ...string) {
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
StreamBaseHTML(qw422016, title, body, u, headElements...)
|
StreamBaseHTML(qw422016, title, body, u, headElements...)
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
func BaseHTML(title, body string, u *user.User, headElements ...string) string {
|
func BaseHTML(title, body string, u *user.User, headElements ...string) string {
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
WriteBaseHTML(qb422016, title, body, u, headElements...)
|
WriteBaseHTML(qb422016, title, body, u, headElements...)
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/stuff.qtpl:30
|
//line views/stuff.qtpl:32
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:32
|
//line views/stuff.qtpl:34
|
||||||
func StreamUserListHTML(qw422016 *qt422016.Writer) {
|
func StreamUserListHTML(qw422016 *qt422016.Writer) {
|
||||||
//line views/stuff.qtpl:32
|
//line views/stuff.qtpl:34
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width user-list">
|
<main class="main-width user-list">
|
||||||
<h1>List of users</h1>
|
<h1>List of users</h1>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:37
|
//line views/stuff.qtpl:39
|
||||||
var (
|
var (
|
||||||
admins = make([]string, 0)
|
admins = make([]string, 0)
|
||||||
moderators = make([]string, 0)
|
moderators = make([]string, 0)
|
||||||
@ -145,303 +153,303 @@ func StreamUserListHTML(qw422016 *qt422016.Writer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:52
|
//line views/stuff.qtpl:54
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<section>
|
<section>
|
||||||
<h2>Admins</h2>
|
<h2>Admins</h2>
|
||||||
<ol>`)
|
<ol>`)
|
||||||
//line views/stuff.qtpl:55
|
//line views/stuff.qtpl:57
|
||||||
for _, name := range admins {
|
for _, name := range admins {
|
||||||
//line views/stuff.qtpl:55
|
//line views/stuff.qtpl:57
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<li><a href="/page/`)
|
<li><a href="/page/`)
|
||||||
//line views/stuff.qtpl:56
|
//line views/stuff.qtpl:58
|
||||||
qw422016.E().S(util.UserHypha)
|
qw422016.E().S(cfg.UserHypha)
|
||||||
//line views/stuff.qtpl:56
|
//line views/stuff.qtpl:58
|
||||||
qw422016.N().S(`/`)
|
qw422016.N().S(`/`)
|
||||||
//line views/stuff.qtpl:56
|
//line views/stuff.qtpl:58
|
||||||
qw422016.E().S(name)
|
qw422016.E().S(name)
|
||||||
//line views/stuff.qtpl:56
|
//line views/stuff.qtpl:58
|
||||||
qw422016.N().S(`">`)
|
qw422016.N().S(`">`)
|
||||||
//line views/stuff.qtpl:56
|
//line views/stuff.qtpl:58
|
||||||
qw422016.E().S(name)
|
qw422016.E().S(name)
|
||||||
//line views/stuff.qtpl:56
|
//line views/stuff.qtpl:58
|
||||||
qw422016.N().S(`</a></li>
|
qw422016.N().S(`</a></li>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:57
|
//line views/stuff.qtpl:59
|
||||||
}
|
}
|
||||||
//line views/stuff.qtpl:57
|
//line views/stuff.qtpl:59
|
||||||
qw422016.N().S(`</ol>
|
qw422016.N().S(`</ol>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Moderators</h2>
|
<h2>Moderators</h2>
|
||||||
<ol>`)
|
<ol>`)
|
||||||
//line views/stuff.qtpl:61
|
//line views/stuff.qtpl:63
|
||||||
for _, name := range moderators {
|
for _, name := range moderators {
|
||||||
//line views/stuff.qtpl:61
|
//line views/stuff.qtpl:63
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<li><a href="/page/`)
|
<li><a href="/page/`)
|
||||||
//line views/stuff.qtpl:62
|
//line views/stuff.qtpl:64
|
||||||
qw422016.E().S(util.UserHypha)
|
qw422016.E().S(cfg.UserHypha)
|
||||||
//line views/stuff.qtpl:62
|
//line views/stuff.qtpl:64
|
||||||
qw422016.N().S(`/`)
|
qw422016.N().S(`/`)
|
||||||
//line views/stuff.qtpl:62
|
//line views/stuff.qtpl:64
|
||||||
qw422016.E().S(name)
|
qw422016.E().S(name)
|
||||||
//line views/stuff.qtpl:62
|
//line views/stuff.qtpl:64
|
||||||
qw422016.N().S(`">`)
|
qw422016.N().S(`">`)
|
||||||
//line views/stuff.qtpl:62
|
//line views/stuff.qtpl:64
|
||||||
qw422016.E().S(name)
|
qw422016.E().S(name)
|
||||||
//line views/stuff.qtpl:62
|
//line views/stuff.qtpl:64
|
||||||
qw422016.N().S(`</a></li>
|
qw422016.N().S(`</a></li>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:63
|
//line views/stuff.qtpl:65
|
||||||
}
|
}
|
||||||
//line views/stuff.qtpl:63
|
//line views/stuff.qtpl:65
|
||||||
qw422016.N().S(`</ol>
|
qw422016.N().S(`</ol>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Editors</h2>
|
<h2>Editors</h2>
|
||||||
<ol>`)
|
<ol>`)
|
||||||
//line views/stuff.qtpl:67
|
//line views/stuff.qtpl:69
|
||||||
for _, name := range editors {
|
for _, name := range editors {
|
||||||
//line views/stuff.qtpl:67
|
//line views/stuff.qtpl:69
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<li><a href="/page/`)
|
<li><a href="/page/`)
|
||||||
//line views/stuff.qtpl:68
|
//line views/stuff.qtpl:70
|
||||||
qw422016.E().S(util.UserHypha)
|
qw422016.E().S(cfg.UserHypha)
|
||||||
//line views/stuff.qtpl:68
|
//line views/stuff.qtpl:70
|
||||||
qw422016.N().S(`/`)
|
qw422016.N().S(`/`)
|
||||||
//line views/stuff.qtpl:68
|
//line views/stuff.qtpl:70
|
||||||
qw422016.E().S(name)
|
qw422016.E().S(name)
|
||||||
//line views/stuff.qtpl:68
|
//line views/stuff.qtpl:70
|
||||||
qw422016.N().S(`">`)
|
qw422016.N().S(`">`)
|
||||||
//line views/stuff.qtpl:68
|
//line views/stuff.qtpl:70
|
||||||
qw422016.E().S(name)
|
qw422016.E().S(name)
|
||||||
//line views/stuff.qtpl:68
|
//line views/stuff.qtpl:70
|
||||||
qw422016.N().S(`</a></li>
|
qw422016.N().S(`</a></li>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:69
|
//line views/stuff.qtpl:71
|
||||||
}
|
}
|
||||||
//line views/stuff.qtpl:69
|
//line views/stuff.qtpl:71
|
||||||
qw422016.N().S(`</ol>
|
qw422016.N().S(`</ol>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
func WriteUserListHTML(qq422016 qtio422016.Writer) {
|
func WriteUserListHTML(qq422016 qtio422016.Writer) {
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
StreamUserListHTML(qw422016)
|
StreamUserListHTML(qw422016)
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
func UserListHTML() string {
|
func UserListHTML() string {
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
WriteUserListHTML(qb422016)
|
WriteUserListHTML(qb422016)
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/stuff.qtpl:73
|
//line views/stuff.qtpl:75
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:75
|
//line views/stuff.qtpl:77
|
||||||
func StreamHyphaListHTML(qw422016 *qt422016.Writer) {
|
func StreamHyphaListHTML(qw422016 *qt422016.Writer) {
|
||||||
//line views/stuff.qtpl:75
|
//line views/stuff.qtpl:77
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<h1>List of hyphae</h1>
|
<h1>List of hyphae</h1>
|
||||||
<p>This wiki has `)
|
<p>This wiki has `)
|
||||||
//line views/stuff.qtpl:79
|
//line views/stuff.qtpl:81
|
||||||
qw422016.N().D(hyphae.Count())
|
qw422016.N().D(hyphae.Count())
|
||||||
//line views/stuff.qtpl:79
|
//line views/stuff.qtpl:81
|
||||||
qw422016.N().S(` hyphae.</p>
|
qw422016.N().S(` hyphae.</p>
|
||||||
<ul class="hypha-list">
|
<ul class="hypha-list">
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:81
|
//line views/stuff.qtpl:83
|
||||||
for h := range hyphae.YieldExistingHyphae() {
|
for h := range hyphae.YieldExistingHyphae() {
|
||||||
//line views/stuff.qtpl:81
|
//line views/stuff.qtpl:83
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<li class="hypha-list__entry">
|
<li class="hypha-list__entry">
|
||||||
<a class="hypha-list__link" href="/hypha/`)
|
<a class="hypha-list__link" href="/hypha/`)
|
||||||
//line views/stuff.qtpl:83
|
//line views/stuff.qtpl:85
|
||||||
qw422016.E().S(h.Name)
|
qw422016.E().S(h.Name)
|
||||||
//line views/stuff.qtpl:83
|
//line views/stuff.qtpl:85
|
||||||
qw422016.N().S(`">`)
|
qw422016.N().S(`">`)
|
||||||
//line views/stuff.qtpl:83
|
//line views/stuff.qtpl:85
|
||||||
qw422016.E().S(util.BeautifulName(h.Name))
|
qw422016.E().S(util.BeautifulName(h.Name))
|
||||||
//line views/stuff.qtpl:83
|
//line views/stuff.qtpl:85
|
||||||
qw422016.N().S(`</a>
|
qw422016.N().S(`</a>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:84
|
//line views/stuff.qtpl:86
|
||||||
if h.BinaryPath != "" {
|
if h.BinaryPath != "" {
|
||||||
//line views/stuff.qtpl:84
|
//line views/stuff.qtpl:86
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<span class="hypha-list__amnt-type">`)
|
<span class="hypha-list__amnt-type">`)
|
||||||
//line views/stuff.qtpl:85
|
//line views/stuff.qtpl:87
|
||||||
qw422016.E().S(filepath.Ext(h.BinaryPath)[1:])
|
qw422016.E().S(filepath.Ext(h.BinaryPath)[1:])
|
||||||
//line views/stuff.qtpl:85
|
//line views/stuff.qtpl:87
|
||||||
qw422016.N().S(`</span>
|
qw422016.N().S(`</span>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:86
|
//line views/stuff.qtpl:88
|
||||||
}
|
}
|
||||||
//line views/stuff.qtpl:86
|
//line views/stuff.qtpl:88
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</li>
|
</li>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:88
|
//line views/stuff.qtpl:90
|
||||||
}
|
}
|
||||||
//line views/stuff.qtpl:88
|
//line views/stuff.qtpl:90
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
</ul>
|
</ul>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
func WriteHyphaListHTML(qq422016 qtio422016.Writer) {
|
func WriteHyphaListHTML(qq422016 qtio422016.Writer) {
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
StreamHyphaListHTML(qw422016)
|
StreamHyphaListHTML(qw422016)
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
func HyphaListHTML() string {
|
func HyphaListHTML() string {
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
WriteHyphaListHTML(qb422016)
|
WriteHyphaListHTML(qb422016)
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/stuff.qtpl:92
|
//line views/stuff.qtpl:94
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:94
|
//line views/stuff.qtpl:96
|
||||||
func StreamAboutHTML(qw422016 *qt422016.Writer) {
|
func StreamAboutHTML(qw422016 *qt422016.Writer) {
|
||||||
//line views/stuff.qtpl:94
|
//line views/stuff.qtpl:96
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
<section>
|
<section>
|
||||||
<h1>About `)
|
<h1>About `)
|
||||||
//line views/stuff.qtpl:98
|
//line views/stuff.qtpl:100
|
||||||
qw422016.E().S(util.SiteName)
|
qw422016.E().S(cfg.WikiName)
|
||||||
//line views/stuff.qtpl:98
|
//line views/stuff.qtpl:100
|
||||||
qw422016.N().S(`</h1>
|
qw422016.N().S(`</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> 1.1.0</li>
|
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> 1.2.0 indev</li>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:101
|
//line views/stuff.qtpl:103
|
||||||
if user.AuthUsed {
|
if user.AuthUsed {
|
||||||
//line views/stuff.qtpl:101
|
//line views/stuff.qtpl:103
|
||||||
qw422016.N().S(` <li><b>User count:</b> `)
|
qw422016.N().S(` <li><b>User count:</b> `)
|
||||||
//line views/stuff.qtpl:102
|
//line views/stuff.qtpl:104
|
||||||
qw422016.N().D(user.Count())
|
qw422016.N().D(user.Count())
|
||||||
//line views/stuff.qtpl:102
|
//line views/stuff.qtpl:104
|
||||||
qw422016.N().S(`</li>
|
qw422016.N().S(`</li>
|
||||||
<li><b>Home page:</b> <a href="/">`)
|
<li><b>Home page:</b> <a href="/">`)
|
||||||
//line views/stuff.qtpl:103
|
//line views/stuff.qtpl:105
|
||||||
qw422016.E().S(util.HomePage)
|
qw422016.E().S(cfg.HomeHypha)
|
||||||
//line views/stuff.qtpl:103
|
//line views/stuff.qtpl:105
|
||||||
qw422016.N().S(`</a></li>
|
qw422016.N().S(`</a></li>
|
||||||
<li><b>Administrators:</b>`)
|
<li><b>Administrators:</b>`)
|
||||||
//line views/stuff.qtpl:104
|
//line views/stuff.qtpl:106
|
||||||
for i, username := range user.ListUsersWithGroup("admin") {
|
for i, username := range user.ListUsersWithGroup("admin") {
|
||||||
//line views/stuff.qtpl:105
|
//line views/stuff.qtpl:107
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
//line views/stuff.qtpl:105
|
//line views/stuff.qtpl:107
|
||||||
qw422016.N().S(`<span aria-hidden="true">, </span>
|
qw422016.N().S(`<span aria-hidden="true">, </span>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:106
|
//line views/stuff.qtpl:108
|
||||||
}
|
}
|
||||||
//line views/stuff.qtpl:106
|
//line views/stuff.qtpl:108
|
||||||
qw422016.N().S(` <a href="/page/`)
|
qw422016.N().S(` <a href="/page/`)
|
||||||
//line views/stuff.qtpl:107
|
//line views/stuff.qtpl:109
|
||||||
qw422016.E().S(util.UserHypha)
|
qw422016.E().S(cfg.UserHypha)
|
||||||
//line views/stuff.qtpl:107
|
//line views/stuff.qtpl:109
|
||||||
qw422016.N().S(`/`)
|
qw422016.N().S(`/`)
|
||||||
//line views/stuff.qtpl:107
|
//line views/stuff.qtpl:109
|
||||||
qw422016.E().S(username)
|
qw422016.E().S(username)
|
||||||
//line views/stuff.qtpl:107
|
//line views/stuff.qtpl:109
|
||||||
qw422016.N().S(`">`)
|
qw422016.N().S(`">`)
|
||||||
//line views/stuff.qtpl:107
|
//line views/stuff.qtpl:109
|
||||||
qw422016.E().S(username)
|
qw422016.E().S(username)
|
||||||
//line views/stuff.qtpl:107
|
//line views/stuff.qtpl:109
|
||||||
qw422016.N().S(`</a>`)
|
qw422016.N().S(`</a>`)
|
||||||
//line views/stuff.qtpl:107
|
//line views/stuff.qtpl:109
|
||||||
}
|
}
|
||||||
//line views/stuff.qtpl:107
|
//line views/stuff.qtpl:109
|
||||||
qw422016.N().S(`</li>
|
qw422016.N().S(`</li>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:108
|
//line views/stuff.qtpl:110
|
||||||
} else {
|
} else {
|
||||||
//line views/stuff.qtpl:108
|
//line views/stuff.qtpl:110
|
||||||
qw422016.N().S(` <li>This wiki does not use authorization</li>
|
qw422016.N().S(` <li>This wiki does not use authorization</li>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:110
|
//line views/stuff.qtpl:112
|
||||||
}
|
}
|
||||||
//line views/stuff.qtpl:110
|
//line views/stuff.qtpl:112
|
||||||
qw422016.N().S(` </ul>
|
qw422016.N().S(` </ul>
|
||||||
<p>See <a href="/list">/list</a> for information about hyphae on this wiki.</p>
|
<p>See <a href="/list">/list</a> for information about hyphae on this wiki.</p>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
func WriteAboutHTML(qq422016 qtio422016.Writer) {
|
func WriteAboutHTML(qq422016 qtio422016.Writer) {
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
StreamAboutHTML(qw422016)
|
StreamAboutHTML(qw422016)
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
func AboutHTML() string {
|
func AboutHTML() string {
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
WriteAboutHTML(qb422016)
|
WriteAboutHTML(qb422016)
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/stuff.qtpl:116
|
//line views/stuff.qtpl:118
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:118
|
//line views/stuff.qtpl:120
|
||||||
func StreamAdminPanelHTML(qw422016 *qt422016.Writer) {
|
func StreamAdminPanelHTML(qw422016 *qt422016.Writer) {
|
||||||
//line views/stuff.qtpl:118
|
//line views/stuff.qtpl:120
|
||||||
qw422016.N().S(`
|
qw422016.N().S(`
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width">
|
<main class="main-width">
|
||||||
@ -478,31 +486,80 @@ func StreamAdminPanelHTML(qw422016 *qt422016.Writer) {
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
func WriteAdminPanelHTML(qq422016 qtio422016.Writer) {
|
func WriteAdminPanelHTML(qq422016 qtio422016.Writer) {
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
StreamAdminPanelHTML(qw422016)
|
StreamAdminPanelHTML(qw422016)
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
}
|
}
|
||||||
|
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
func AdminPanelHTML() string {
|
func AdminPanelHTML() string {
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
WriteAdminPanelHTML(qb422016)
|
WriteAdminPanelHTML(qb422016)
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
return qs422016
|
return qs422016
|
||||||
//line views/stuff.qtpl:153
|
//line views/stuff.qtpl:155
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/stuff.qtpl:157
|
||||||
|
func streamomnipresentScripts(qw422016 *qt422016.Writer) {
|
||||||
|
//line views/stuff.qtpl:157
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/stuff.qtpl:158
|
||||||
|
for _, scriptPath := range cfg.OmnipresentScripts {
|
||||||
|
//line views/stuff.qtpl:158
|
||||||
|
qw422016.N().S(`
|
||||||
|
<script src="`)
|
||||||
|
//line views/stuff.qtpl:159
|
||||||
|
qw422016.E().S(scriptPath)
|
||||||
|
//line views/stuff.qtpl:159
|
||||||
|
qw422016.N().S(`"></script>
|
||||||
|
`)
|
||||||
|
//line views/stuff.qtpl:160
|
||||||
|
}
|
||||||
|
//line views/stuff.qtpl:160
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
func writeomnipresentScripts(qq422016 qtio422016.Writer) {
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
streamomnipresentScripts(qw422016)
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
}
|
||||||
|
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
func omnipresentScripts() string {
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
writeomnipresentScripts(qb422016)
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
|
return qs422016
|
||||||
|
//line views/stuff.qtpl:161
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
package main
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
"github.com/bouncepaw/mycorrhiza/views"
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is not init(), because user.AuthUsed is not set at init-stage.
|
// initAdmin sets up /admin routes if auth is used. Call it after you have decided if you want to use auth.
|
||||||
func initAdmin() {
|
func initAdmin() {
|
||||||
if user.AuthUsed {
|
if user.AuthUsed {
|
||||||
http.HandleFunc("/admin", handlerAdmin)
|
http.HandleFunc("/admin", handlerAdmin)
|
||||||
@ -18,26 +20,32 @@ func initAdmin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlerAdmin provides the admin panel.
|
||||||
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
if user.CanProceed(rq, "admin") {
|
if user.CanProceed(rq, "admin") {
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write([]byte(base("Admin panel", views.AdminPanelHTML(), user.FromRequest(rq))))
|
_, err := io.WriteString(w, views.BaseHTML("Admin panel", views.AdminPanelHTML(), user.FromRequest(rq)))
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlerAdminShutdown kills the wiki.
|
||||||
func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
if user.CanProceed(rq, "admin/shutdown") && rq.Method == "POST" {
|
if user.CanProceed(rq, "admin/shutdown") && rq.Method == "POST" {
|
||||||
log.Fatal("An admin commanded the wiki to shutdown")
|
log.Fatal("An admin commanded the wiki to shutdown")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlerAdminReindexUsers reinitialises the user system.
|
||||||
func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
if user.CanProceed(rq, "admin") && rq.Method == "POST" {
|
if user.CanProceed(rq, "admin") && rq.Method == "POST" {
|
||||||
user.ReadUsersFromFilesystem()
|
user.ReadUsersFromFilesystem()
|
||||||
http.Redirect(w, rq, "/hypha/"+util.UserHypha, http.StatusSeeOther)
|
http.Redirect(w, rq, "/hypha/"+cfg.UserHypha, http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package main
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -10,23 +11,29 @@ import (
|
|||||||
"github.com/bouncepaw/mycorrhiza/views"
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func initAuth() {
|
||||||
http.HandleFunc("/register", handlerRegister)
|
if !user.AuthUsed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cfg.UseRegistration {
|
||||||
|
http.HandleFunc("/register", handlerRegister)
|
||||||
|
}
|
||||||
http.HandleFunc("/login", handlerLogin)
|
http.HandleFunc("/login", handlerLogin)
|
||||||
http.HandleFunc("/login-data", handlerLoginData)
|
http.HandleFunc("/login-data", handlerLoginData)
|
||||||
http.HandleFunc("/logout", handlerLogout)
|
http.HandleFunc("/logout", handlerLogout)
|
||||||
http.HandleFunc("/logout-confirm", handlerLogoutConfirm)
|
http.HandleFunc("/logout-confirm", handlerLogoutConfirm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlerRegister both displays the register form (GET) and registers users (POST).
|
||||||
func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
if !util.UseRegistration {
|
if !cfg.UseRegistration {
|
||||||
w.WriteHeader(http.StatusForbidden)
|
w.WriteHeader(http.StatusForbidden)
|
||||||
}
|
}
|
||||||
if rq.Method == http.MethodGet {
|
if rq.Method == http.MethodGet {
|
||||||
io.WriteString(
|
io.WriteString(
|
||||||
w,
|
w,
|
||||||
base(
|
views.BaseHTML(
|
||||||
"Register",
|
"Register",
|
||||||
views.RegisterHTML(rq),
|
views.RegisterHTML(rq),
|
||||||
user.FromRequest(rq),
|
user.FromRequest(rq),
|
||||||
@ -47,6 +54,7 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlerLogout shows the logout form.
|
||||||
func handlerLogout(w http.ResponseWriter, rq *http.Request) {
|
func handlerLogout(w http.ResponseWriter, rq *http.Request) {
|
||||||
var (
|
var (
|
||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
@ -60,35 +68,42 @@ func handlerLogout(w http.ResponseWriter, rq *http.Request) {
|
|||||||
log.Println("Unknown user tries to log out")
|
log.Println("Unknown user tries to log out")
|
||||||
w.WriteHeader(http.StatusForbidden)
|
w.WriteHeader(http.StatusForbidden)
|
||||||
}
|
}
|
||||||
w.Write([]byte(base("Logout?", views.LogoutHTML(can), u)))
|
w.Write([]byte(views.BaseHTML("Logout?", views.LogoutHTML(can), u)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlerLogoutConfirm logs the user out.
|
||||||
|
//
|
||||||
|
// TODO: merge into handlerLogout as POST method.
|
||||||
func handlerLogoutConfirm(w http.ResponseWriter, rq *http.Request) {
|
func handlerLogoutConfirm(w http.ResponseWriter, rq *http.Request) {
|
||||||
user.LogoutFromRequest(w, rq)
|
user.LogoutFromRequest(w, rq)
|
||||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerLoginData(w http.ResponseWriter, rq *http.Request) {
|
// handlerLogin shows the login form.
|
||||||
log.Println(rq.URL)
|
|
||||||
var (
|
|
||||||
username = util.CanonicalName(rq.PostFormValue("username"))
|
|
||||||
password = rq.PostFormValue("password")
|
|
||||||
err = user.LoginDataHTTP(w, rq, username, password)
|
|
||||||
)
|
|
||||||
if err != "" {
|
|
||||||
w.Write([]byte(base(err, views.LoginErrorHTML(err), user.EmptyUser())))
|
|
||||||
} else {
|
|
||||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerLogin(w http.ResponseWriter, rq *http.Request) {
|
func handlerLogin(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
if user.AuthUsed {
|
if user.AuthUsed {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
} else {
|
} else {
|
||||||
w.WriteHeader(http.StatusForbidden)
|
w.WriteHeader(http.StatusForbidden)
|
||||||
}
|
}
|
||||||
w.Write([]byte(base("Login", views.LoginHTML(), user.EmptyUser())))
|
w.Write([]byte(views.BaseHTML("Login", views.LoginHTML(), user.EmptyUser())))
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlerLoginData logs the user in.
|
||||||
|
//
|
||||||
|
// TODO: merge into handlerLogin as POST method.
|
||||||
|
func handlerLoginData(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
util.PrepareRq(rq)
|
||||||
|
var (
|
||||||
|
username = util.CanonicalName(rq.PostFormValue("username"))
|
||||||
|
password = rq.PostFormValue("password")
|
||||||
|
err = user.LoginDataHTTP(w, rq, username, password)
|
||||||
|
)
|
||||||
|
if err != "" {
|
||||||
|
w.Write([]byte(views.BaseHTML(err, views.LoginErrorHTML(err), user.EmptyUser())))
|
||||||
|
} else {
|
||||||
|
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/bouncepaw/mycorrhiza/views"
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func initHistory() {
|
||||||
http.HandleFunc("/history/", handlerHistory)
|
http.HandleFunc("/history/", handlerHistory)
|
||||||
http.HandleFunc("/recent-changes/", handlerRecentChanges)
|
http.HandleFunc("/recent-changes/", handlerRecentChanges)
|
||||||
http.HandleFunc("/recent-changes-rss", handlerRecentChangesRSS)
|
http.HandleFunc("/recent-changes-rss", handlerRecentChangesRSS)
|
||||||
@ -21,10 +21,10 @@ func init() {
|
|||||||
http.HandleFunc("/recent-changes-json", handlerRecentChangesJSON)
|
http.HandleFunc("/recent-changes-json", handlerRecentChangesJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerHistory lists all revisions of a hypha
|
// handlerHistory lists all revisions of a hypha.
|
||||||
func handlerHistory(w http.ResponseWriter, rq *http.Request) {
|
func handlerHistory(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
hyphaName := HyphaNameFromRq(rq, "history")
|
hyphaName := util.HyphaNameFromRq(rq, "history")
|
||||||
var list string
|
var list string
|
||||||
|
|
||||||
// History can be found for files that do not exist anymore.
|
// History can be found for files that do not exist anymore.
|
||||||
@ -35,25 +35,26 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) {
|
|||||||
log.Println("Found", len(revs), "revisions for", hyphaName)
|
log.Println("Found", len(revs), "revisions for", hyphaName)
|
||||||
|
|
||||||
util.HTTP200Page(w,
|
util.HTTP200Page(w,
|
||||||
base(hyphaName, views.HistoryHTML(rq, hyphaName, list), user.FromRequest(rq)))
|
views.BaseHTML(hyphaName, views.HistoryHTML(rq, hyphaName, list), user.FromRequest(rq)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recent changes
|
// handlerRecentChanges displays the /recent-changes/ page.
|
||||||
func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
|
func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
noPrefix = strings.TrimPrefix(rq.URL.String(), "/recent-changes/")
|
noPrefix = strings.TrimPrefix(rq.URL.String(), "/recent-changes/")
|
||||||
n, err = strconv.Atoi(noPrefix)
|
n, err = strconv.Atoi(noPrefix)
|
||||||
)
|
)
|
||||||
if err == nil && n < 101 {
|
if err == nil && n < 101 {
|
||||||
util.HTTP200Page(w, base(strconv.Itoa(n)+" recent changes", views.RecentChangesHTML(n), user.FromRequest(rq)))
|
util.HTTP200Page(w, views.BaseHTML(strconv.Itoa(n)+" recent changes", views.RecentChangesHTML(n), user.FromRequest(rq)))
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(w, rq, "/recent-changes/20", http.StatusSeeOther)
|
http.Redirect(w, rq, "/recent-changes/20", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// genericHandlerOfFeeds is a helper function for the web feed handlers.
|
||||||
func genericHandlerOfFeeds(w http.ResponseWriter, rq *http.Request, f func() (string, error), name string) {
|
func genericHandlerOfFeeds(w http.ResponseWriter, rq *http.Request, f func() (string, error), name string) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
if content, err := f(); err != nil {
|
if content, err := f(); err != nil {
|
||||||
w.Header().Set("Content-Type", "text/plain;charset=utf-8")
|
w.Header().Set("Content-Type", "text/plain;charset=utf-8")
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
@ -1,20 +1,21 @@
|
|||||||
package main
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycomarkup"
|
||||||
|
"github.com/bouncepaw/mycomarkup/mycocontext"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/markup"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
"github.com/bouncepaw/mycorrhiza/shroom"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
"github.com/bouncepaw/mycorrhiza/views"
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func initMutators() {
|
||||||
// Those that do not actually mutate anything:
|
// Those that do not actually mutate anything:
|
||||||
http.HandleFunc("/edit/", handlerEdit)
|
http.HandleFunc("/edit/", handlerEdit)
|
||||||
http.HandleFunc("/delete-ask/", handlerDeleteAsk)
|
http.HandleFunc("/delete-ask/", handlerDeleteAsk)
|
||||||
@ -35,14 +36,14 @@ func factoryHandlerAsker(
|
|||||||
succPageTemplate func(*http.Request, string, bool) string,
|
succPageTemplate func(*http.Request, string, bool) string,
|
||||||
) func(http.ResponseWriter, *http.Request) {
|
) func(http.ResponseWriter, *http.Request) {
|
||||||
return func(w http.ResponseWriter, rq *http.Request) {
|
return func(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, actionPath)
|
hyphaName = util.HyphaNameFromRq(rq, actionPath)
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
)
|
)
|
||||||
if err, errtitle := asker(u, h); err != nil {
|
if err, errtitle := asker(u, h); err != nil {
|
||||||
HttpErr(
|
httpErr(
|
||||||
w,
|
w,
|
||||||
http.StatusInternalServerError,
|
http.StatusInternalServerError,
|
||||||
hyphaName,
|
hyphaName,
|
||||||
@ -52,7 +53,7 @@ func factoryHandlerAsker(
|
|||||||
}
|
}
|
||||||
util.HTTP200Page(
|
util.HTTP200Page(
|
||||||
w,
|
w,
|
||||||
base(
|
views.BaseHTML(
|
||||||
fmt.Sprintf(succTitleTemplate, hyphaName),
|
fmt.Sprintf(succTitleTemplate, hyphaName),
|
||||||
succPageTemplate(rq, hyphaName, h.Exists),
|
succPageTemplate(rq, hyphaName, h.Exists),
|
||||||
u))
|
u))
|
||||||
@ -85,14 +86,14 @@ func factoryHandlerConfirmer(
|
|||||||
confirmer func(*hyphae.Hypha, *user.User, *http.Request) (*history.HistoryOp, string),
|
confirmer func(*hyphae.Hypha, *user.User, *http.Request) (*history.HistoryOp, string),
|
||||||
) func(http.ResponseWriter, *http.Request) {
|
) func(http.ResponseWriter, *http.Request) {
|
||||||
return func(w http.ResponseWriter, rq *http.Request) {
|
return func(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, actionPath)
|
hyphaName = util.HyphaNameFromRq(rq, actionPath)
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
)
|
)
|
||||||
if hop, errtitle := confirmer(h, u, rq); hop.HasErrors() {
|
if hop, errtitle := confirmer(h, u, rq); hop.HasErrors() {
|
||||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
httpErr(w, http.StatusInternalServerError, hyphaName,
|
||||||
errtitle,
|
errtitle,
|
||||||
hop.FirstErrorText())
|
hop.FirstErrorText())
|
||||||
return
|
return
|
||||||
@ -129,9 +130,9 @@ var handlerRenameConfirm = factoryHandlerConfirmer(
|
|||||||
|
|
||||||
// handlerEdit shows the edit form. It doesn't edit anything actually.
|
// handlerEdit shows the edit form. It doesn't edit anything actually.
|
||||||
func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "edit")
|
hyphaName = util.HyphaNameFromRq(rq, "edit")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
warning string
|
warning string
|
||||||
textAreaFill string
|
textAreaFill string
|
||||||
@ -139,7 +140,7 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
)
|
)
|
||||||
if err, errtitle := shroom.CanEdit(u, h); err != nil {
|
if err, errtitle := shroom.CanEdit(u, h); err != nil {
|
||||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
httpErr(w, http.StatusInternalServerError, hyphaName,
|
||||||
errtitle,
|
errtitle,
|
||||||
err.Error())
|
err.Error())
|
||||||
return
|
return
|
||||||
@ -148,7 +149,7 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
textAreaFill, err = shroom.FetchTextPart(h)
|
textAreaFill, err = shroom.FetchTextPart(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
httpErr(w, http.StatusInternalServerError, hyphaName,
|
||||||
"Error",
|
"Error",
|
||||||
"Could not fetch text data")
|
"Could not fetch text data")
|
||||||
return
|
return
|
||||||
@ -158,7 +159,7 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
util.HTTP200Page(
|
util.HTTP200Page(
|
||||||
w,
|
w,
|
||||||
base(
|
views.BaseHTML(
|
||||||
"Edit "+hyphaName,
|
"Edit "+hyphaName,
|
||||||
views.EditHTML(rq, hyphaName, textAreaFill, warning),
|
views.EditHTML(rq, hyphaName, textAreaFill, warning),
|
||||||
u))
|
u))
|
||||||
@ -166,9 +167,9 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerUploadText uploads a new text part for the hypha.
|
// handlerUploadText uploads a new text part for the hypha.
|
||||||
func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "upload-text")
|
hyphaName = util.HyphaNameFromRq(rq, "upload-text")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
textData = rq.PostFormValue("text")
|
textData = rq.PostFormValue("text")
|
||||||
action = rq.PostFormValue("action")
|
action = rq.PostFormValue("action")
|
||||||
@ -180,7 +181,7 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
|||||||
if action != "Preview" {
|
if action != "Preview" {
|
||||||
hop, errtitle = shroom.UploadText(h, []byte(textData), u)
|
hop, errtitle = shroom.UploadText(h, []byte(textData), u)
|
||||||
if hop.HasErrors() {
|
if hop.HasErrors() {
|
||||||
HttpErr(w, http.StatusForbidden, hyphaName,
|
httpErr(w, http.StatusForbidden, hyphaName,
|
||||||
errtitle,
|
errtitle,
|
||||||
hop.FirstErrorText())
|
hop.FirstErrorText())
|
||||||
return
|
return
|
||||||
@ -188,16 +189,18 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if action == "Preview" {
|
if action == "Preview" {
|
||||||
|
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, textData)
|
||||||
|
|
||||||
util.HTTP200Page(
|
util.HTTP200Page(
|
||||||
w,
|
w,
|
||||||
base(
|
views.BaseHTML(
|
||||||
"Preview "+hyphaName,
|
"Preview "+hyphaName,
|
||||||
views.PreviewHTML(
|
views.PreviewHTML(
|
||||||
rq,
|
rq,
|
||||||
hyphaName,
|
hyphaName,
|
||||||
textData,
|
textData,
|
||||||
"",
|
"",
|
||||||
markup.Doc(hyphaName, textData).AsHTML()),
|
mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx))),
|
||||||
u))
|
u))
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
|
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
|
||||||
@ -206,21 +209,21 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerUploadBinary uploads a new binary part for the hypha.
|
// handlerUploadBinary uploads a new binary part for the hypha.
|
||||||
func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
rq.ParseMultipartForm(10 << 20) // Set upload limit
|
rq.ParseMultipartForm(10 << 20) // Set upload limit
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "upload-binary")
|
hyphaName = util.HyphaNameFromRq(rq, "upload-binary")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
file, handler, err = rq.FormFile("binary")
|
file, handler, err = rq.FormFile("binary")
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
httpErr(w, http.StatusInternalServerError, hyphaName,
|
||||||
"Error",
|
"Error",
|
||||||
err.Error())
|
err.Error())
|
||||||
}
|
}
|
||||||
if err, errtitle := shroom.CanAttach(u, h); err != nil {
|
if err, errtitle := shroom.CanAttach(u, h); err != nil {
|
||||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
httpErr(w, http.StatusInternalServerError, hyphaName,
|
||||||
errtitle,
|
errtitle,
|
||||||
err.Error())
|
err.Error())
|
||||||
}
|
}
|
||||||
@ -240,7 +243,7 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if hop.HasErrors() {
|
if hop.HasErrors() {
|
||||||
HttpErr(w, http.StatusInternalServerError, hyphaName, errtitle, hop.FirstErrorText())
|
httpErr(w, http.StatusInternalServerError, hyphaName, errtitle, hop.FirstErrorText())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
|
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
|
@ -1,7 +1,8 @@
|
|||||||
package main
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bouncepaw/mycomarkup/mycocontext"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -11,14 +12,15 @@ import (
|
|||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/markup"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
"github.com/bouncepaw/mycorrhiza/mimetype"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
"github.com/bouncepaw/mycorrhiza/views"
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycomarkup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func initReaders() {
|
||||||
http.HandleFunc("/page/", handlerHypha)
|
http.HandleFunc("/page/", handlerHypha)
|
||||||
http.HandleFunc("/hypha/", handlerHypha)
|
http.HandleFunc("/hypha/", handlerHypha)
|
||||||
http.HandleFunc("/text/", handlerText)
|
http.HandleFunc("/text/", handlerText)
|
||||||
@ -29,9 +31,9 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerAttachment(w http.ResponseWriter, rq *http.Request) {
|
func handlerAttachment(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "attachment")
|
hyphaName = util.HyphaNameFromRq(rq, "attachment")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
)
|
)
|
||||||
@ -43,7 +45,7 @@ func handlerAttachment(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/primitive-diff/")
|
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/primitive-diff/")
|
||||||
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
||||||
@ -61,7 +63,7 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerRevision displays a specific revision of text part a page
|
// handlerRevision displays a specific revision of text part a page
|
||||||
func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/rev/")
|
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/rev/")
|
||||||
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
||||||
@ -73,7 +75,8 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
|||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
)
|
)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
contents = markup.Doc(hyphaName, textContents).AsHTML()
|
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, textContents)
|
||||||
|
contents = mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx))
|
||||||
}
|
}
|
||||||
page := views.RevisionHTML(
|
page := views.RevisionHTML(
|
||||||
rq,
|
rq,
|
||||||
@ -83,13 +86,13 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
|||||||
)
|
)
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write([]byte(base(util.BeautifulName(hyphaName), page, u)))
|
_, _ = fmt.Fprint(w, views.BaseHTML(util.BeautifulName(hyphaName), page, u))
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerText serves raw source text of the hypha.
|
// handlerText serves raw source text of the hypha.
|
||||||
func handlerText(w http.ResponseWriter, rq *http.Request) {
|
func handlerText(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
hyphaName := HyphaNameFromRq(rq, "text")
|
hyphaName := util.HyphaNameFromRq(rq, "text")
|
||||||
if h := hyphae.ByName(hyphaName); h.Exists {
|
if h := hyphae.ByName(hyphaName); h.Exists {
|
||||||
log.Println("Serving", h.TextPath)
|
log.Println("Serving", h.TextPath)
|
||||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
@ -99,8 +102,8 @@ func handlerText(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerBinary serves binary part of the hypha.
|
// handlerBinary serves binary part of the hypha.
|
||||||
func handlerBinary(w http.ResponseWriter, rq *http.Request) {
|
func handlerBinary(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
hyphaName := HyphaNameFromRq(rq, "binary")
|
hyphaName := util.HyphaNameFromRq(rq, "binary")
|
||||||
if h := hyphae.ByName(hyphaName); h.Exists {
|
if h := hyphae.ByName(hyphaName); h.Exists {
|
||||||
log.Println("Serving", h.BinaryPath)
|
log.Println("Serving", h.BinaryPath)
|
||||||
w.Header().Set("Content-Type", mimetype.FromExtension(filepath.Ext(h.BinaryPath)))
|
w.Header().Set("Content-Type", mimetype.FromExtension(filepath.Ext(h.BinaryPath)))
|
||||||
@ -110,9 +113,9 @@ func handlerBinary(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.
|
// 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) {
|
func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "page", "hypha")
|
hyphaName = util.HyphaNameFromRq(rq, "page", "hypha")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
contents string
|
contents string
|
||||||
openGraph string
|
openGraph string
|
||||||
@ -122,9 +125,10 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
|||||||
fileContentsT, errT := ioutil.ReadFile(h.TextPath)
|
fileContentsT, errT := ioutil.ReadFile(h.TextPath)
|
||||||
_, errB := os.Stat(h.BinaryPath)
|
_, errB := os.Stat(h.BinaryPath)
|
||||||
if errT == nil {
|
if errT == nil {
|
||||||
md := markup.Doc(hyphaName, string(fileContentsT))
|
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, string(fileContentsT))
|
||||||
contents = md.AsHTML()
|
ast := mycomarkup.BlockTree(ctx)
|
||||||
openGraph = md.OpenGraphHTML()
|
contents = mycomarkup.BlocksToHTML(ctx, ast)
|
||||||
|
openGraph = mycomarkup.OpenGraphHTML(ctx, ast)
|
||||||
}
|
}
|
||||||
if !os.IsNotExist(errB) {
|
if !os.IsNotExist(errB) {
|
||||||
contents = views.AttachmentHTML(h) + contents
|
contents = views.AttachmentHTML(h) + contents
|
92
web/stuff.go
Normal file
92
web/stuff.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
// stuff.go is used for meta stuff about the wiki or all hyphae at once.
|
||||||
|
import (
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/shroom"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/user"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initStuff() {
|
||||||
|
http.HandleFunc("/list/", handlerList)
|
||||||
|
http.HandleFunc("/reindex/", handlerReindex)
|
||||||
|
http.HandleFunc("/update-header-links/", handlerUpdateHeaderLinks)
|
||||||
|
http.HandleFunc("/random/", handlerRandom)
|
||||||
|
http.HandleFunc("/about/", handlerAbout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlerList shows a list of all hyphae in the wiki in random order.
|
||||||
|
func handlerList(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
util.PrepareRq(rq)
|
||||||
|
util.HTTP200Page(w, views.BaseHTML("List of pages", views.HyphaListHTML(), user.FromRequest(rq)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlerReindex reindexes all hyphae by checking the wiki storage directory anew.
|
||||||
|
func handlerReindex(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
util.PrepareRq(rq)
|
||||||
|
if ok := user.CanProceed(rq, "reindex"); !ok {
|
||||||
|
httpErr(w, http.StatusForbidden, cfg.HomeHypha, "Not enough rights", "You must be an admin to reindex hyphae.")
|
||||||
|
log.Println("Rejected", rq.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hyphae.ResetCount()
|
||||||
|
log.Println("Wiki storage directory is", cfg.WikiDir)
|
||||||
|
log.Println("Start indexing hyphae...")
|
||||||
|
hyphae.Index(cfg.WikiDir)
|
||||||
|
log.Println("Indexed", hyphae.Count(), "hyphae")
|
||||||
|
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlerUpdateHeaderLinks updates header links by reading the configured hypha, if there is any, or resorting to default values.
|
||||||
|
//
|
||||||
|
// See https://mycorrhiza.lesarbr.es/hypha/configuration/header
|
||||||
|
func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
util.PrepareRq(rq)
|
||||||
|
if ok := user.CanProceed(rq, "update-header-links"); !ok {
|
||||||
|
httpErr(w, http.StatusForbidden, cfg.HomeHypha, "Not enough rights", "You must be a moderator to update header links.")
|
||||||
|
log.Println("Rejected", rq.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
shroom.SetHeaderLinks()
|
||||||
|
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlerRandom redirects to a random hypha.
|
||||||
|
func handlerRandom(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
util.PrepareRq(rq)
|
||||||
|
var (
|
||||||
|
randomHyphaName string
|
||||||
|
amountOfHyphae = hyphae.Count()
|
||||||
|
)
|
||||||
|
if amountOfHyphae == 0 {
|
||||||
|
httpErr(w, http.StatusNotFound, cfg.HomeHypha, "There are no hyphae",
|
||||||
|
"It is impossible to display a random hypha because the wiki does not contain any hyphae")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := rand.Intn(amountOfHyphae)
|
||||||
|
for h := range hyphae.YieldExistingHyphae() {
|
||||||
|
if i == 0 {
|
||||||
|
randomHyphaName = h.Name
|
||||||
|
}
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
http.Redirect(w, rq, "/hypha/"+randomHyphaName, http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlerAbout shows a summary of wiki's software.
|
||||||
|
func handlerAbout(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err := io.WriteString(w, views.BaseHTML("About "+cfg.WikiName, views.AboutHTML(), user.FromRequest(rq)))
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
130
web/web.go
Normal file
130
web/web.go
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
// Package web contains web handlers and initialization stuff.
|
||||||
|
//
|
||||||
|
// It exports just one function: Init. Call it if you want to have web capabilities.
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/assets"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/user"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
|
w.WriteHeader(status)
|
||||||
|
fmt.Fprint(
|
||||||
|
w,
|
||||||
|
views.BaseHTML(
|
||||||
|
title,
|
||||||
|
fmt.Sprintf(
|
||||||
|
`<main class="main-width"><p>%s. <a href="/page/%s">Go back to the hypha.<a></p></main>`,
|
||||||
|
errMsg,
|
||||||
|
name,
|
||||||
|
),
|
||||||
|
user.EmptyUser(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlerStyle(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
util.PrepareRq(rq)
|
||||||
|
if _, err := os.Stat(cfg.WikiDir + "/assets/common.css"); err == nil {
|
||||||
|
http.ServeFile(w, rq, cfg.WikiDir+"/assets/common.css")
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Type", "text/css;charset=utf-8")
|
||||||
|
w.Write([]byte(assets.DefaultCSS()))
|
||||||
|
}
|
||||||
|
if bytes, err := ioutil.ReadFile(cfg.WikiDir + "/assets/custom.css"); err == nil {
|
||||||
|
w.Write(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlerToolbar(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
util.PrepareRq(rq)
|
||||||
|
w.Header().Set("Content-Type", "text/javascript;charset=utf-8")
|
||||||
|
w.Write([]byte(assets.ToolbarJS()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlerIcon serves the requested icon. All icons are distributed as part of the Mycorrhiza binary.
|
||||||
|
//
|
||||||
|
// See assets/assets/icon/ for icons themselves, see assets/assets.qtpl for their sources.
|
||||||
|
func handlerIcon(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
iconName := strings.TrimPrefix(rq.URL.Path, "/assets/icon/")
|
||||||
|
if iconName == "https" {
|
||||||
|
iconName = "http"
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "image/svg+xml")
|
||||||
|
icon := func() string {
|
||||||
|
switch iconName {
|
||||||
|
case "gemini":
|
||||||
|
return assets.IconGemini()
|
||||||
|
case "mailto":
|
||||||
|
return assets.IconMailto()
|
||||||
|
case "gopher":
|
||||||
|
return assets.IconGopher()
|
||||||
|
case "feed":
|
||||||
|
return assets.IconFeed()
|
||||||
|
default:
|
||||||
|
return assets.IconHTTP()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
_, err := io.WriteString(w, icon)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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(views.BaseHTML("User list", views.UserListHTML(), user.FromRequest(rq))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlerRobotsTxt(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(
|
||||||
|
`User-agent: *
|
||||||
|
Allow: /page/
|
||||||
|
Allow: /recent-changes
|
||||||
|
Disallow: /
|
||||||
|
Crawl-delay: 5`))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
initAdmin()
|
||||||
|
initReaders()
|
||||||
|
initMutators()
|
||||||
|
initAuth()
|
||||||
|
initHistory()
|
||||||
|
initStuff()
|
||||||
|
|
||||||
|
http.HandleFunc("/user-list/", handlerUserList)
|
||||||
|
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(cfg.WikiDir+"/static"))))
|
||||||
|
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
http.ServeFile(w, rq, cfg.WikiDir+"/static/favicon.ico")
|
||||||
|
})
|
||||||
|
http.HandleFunc("/assets/common.css", handlerStyle)
|
||||||
|
http.HandleFunc("/assets/toolbar.js", handlerToolbar)
|
||||||
|
http.HandleFunc("/assets/icon/", handlerIcon)
|
||||||
|
http.HandleFunc("/robots.txt", handlerRobotsTxt)
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
addr, _ := url.Parse("/hypha/" + cfg.HomeHypha) // Let's pray it never fails
|
||||||
|
rq.URL = addr
|
||||||
|
handlerHypha(w, rq)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user