1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-05 02:29:54 +00:00

Merge pull request #143 from bouncepaw/pkg-revolution

Pkg revolution
This commit is contained in:
Timur Ismagilov 2022-04-09 15:14:16 +03:00 committed by GitHub
commit 4713c1e569
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 2078 additions and 2523 deletions

View File

@ -9,8 +9,8 @@ import (
"github.com/bouncepaw/mycorrhiza/util"
)
// YieldHyphaBacklinks gets backlinks for the desired hypha, sorts and yields them one by one.
func YieldHyphaBacklinks(hyphaName string) <-chan string {
// yieldHyphaBacklinks gets backlinks for the desired hypha, sorts and yields them one by one.
func yieldHyphaBacklinks(hyphaName string) <-chan string {
hyphaName = util.CanonicalName(hyphaName)
out := make(chan string)
sorted := hyphae.PathographicSort(out)
@ -147,7 +147,7 @@ type backlinkIndexRenaming struct {
links []string
}
// Apply changes backlink index respective to the operation data
// apply changes backlink index respective to the operation data
func (op backlinkIndexRenaming) apply() {
for _, link := range op.links {
if lSet, exists := backlinkIndex[link]; exists {

View File

@ -1,10 +1,11 @@
package backlinks
import (
"github.com/bouncepaw/mycomarkup/v3"
"github.com/bouncepaw/mycomarkup/v3/links"
"github.com/bouncepaw/mycomarkup/v3/mycocontext"
"github.com/bouncepaw/mycomarkup/v3/tools"
"github.com/bouncepaw/mycomarkup/v4"
"github.com/bouncepaw/mycomarkup/v4/links"
"github.com/bouncepaw/mycomarkup/v4/mycocontext"
"github.com/bouncepaw/mycomarkup/v4/options"
"github.com/bouncepaw/mycomarkup/v4/tools"
"github.com/bouncepaw/mycorrhiza/hyphae"
)
@ -34,7 +35,7 @@ func extractHyphaLinks(h hyphae.Hypha) []string {
// extractHyphaLinksFromContent extracts local hypha links from the provided text.
func extractHyphaLinksFromContent(hyphaName string, contents string) []string {
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, contents)
ctx, _ := mycocontext.ContextFromStringInput(contents, options.Options{HyphaName: hyphaName}.FillTheRest())
linkVisitor, getLinks := tools.LinkVisitor(ctx)
// Ignore the result of BlockTree because we call it for linkVisitor.
_ = mycomarkup.BlockTree(ctx, linkVisitor)

View File

@ -0,0 +1,17 @@
{{define "backlinks to text"}}Backlinks to {{.}}{{end}}
{{define "title"}}{{template "backlinks to text" .HyphaName}}{{end}}
{{define "body"}}
<div class="layout">
<main class="main-width backlinks">
<h1>{{block "backlinks to link" .HyphaName}}Backlinks to <a href="/hypha/{{.}}">{{beautifulName .}}</a>{{end}}</h1>
<p>{{block "description" .}}Hyphae which have a link to this hypha, embed it as an image or transclude it are listed below.{{end}}</p>
<ul class="backlinks__list">
{{range .Backlinks}}
<li class="backlinks__entry">
<a class="backlinks__link wikilink" href="/hypha/{{.}}">{{beautifulName .}}</a>
</li>
{{end}}
</ul>
</main>
</div>
{{end}}

62
backlinks/web.go Normal file
View File

@ -0,0 +1,62 @@
package backlinks
import (
"embed"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/gorilla/mux"
"log"
"net/http"
"text/template"
)
func InitHandlers(rtr *mux.Router) {
rtr.PathPrefix("/backlinks/").HandlerFunc(handlerBacklinks)
chain = viewutil.
En(viewutil.CopyEnWith(fs, "view_backlinks.html")).
Ru(template.Must(viewutil.CopyRuWith(fs, "view_backlinks.html").Parse(ruTranslation)))
}
// handlerBacklinks lists all backlinks to a hypha.
func handlerBacklinks(w http.ResponseWriter, rq *http.Request) {
var (
hyphaName = util.HyphaNameFromRq(rq, "backlinks")
backlinks []string
)
for b := range yieldHyphaBacklinks(hyphaName) {
backlinks = append(backlinks, b)
}
viewBacklinks(viewutil.MetaFrom(w, rq), hyphaName, backlinks)
}
var (
//go:embed *.html
fs embed.FS
ruTranslation = `
{{define "backlinks to text"}}Обратные ссылки на {{.}}{{end}}
{{define "backlinks to link"}}Обратные ссылки на <a href="/hypha/{{.}}">{{beautifulName .}}</a>{{end}}
{{define "description"}}Ниже перечислены гифы, на которых есть ссылка на эту гифу, трансклюзия этой гифы или эта гифа вставлена как изображение.{{end}}
`
chain viewutil.Chain
)
type backlinksData struct {
viewutil.BaseData
HyphaName string
Backlinks []string
}
func viewBacklinks(meta viewutil.Meta, hyphaName string, backlinks []string) {
if err := chain.Get(meta).ExecuteTemplate(meta.W, "page", backlinksData{
BaseData: viewutil.BaseData{
Meta: meta,
HeaderLinks: cfg.HeaderLinks,
CommonScripts: cfg.CommonScripts,
},
HyphaName: hyphaName,
Backlinks: backlinks,
}); err != nil {
log.Println(err)
}
}

View File

@ -1,4 +1,4 @@
// Package categories provides category management. All operations in this package are mutexed.
// Package categories provides category management.
//
// As per the long pondering, this is how categories (cats for short)
// work in Mycorrhiza:
@ -12,12 +12,19 @@
// cat operations are not mentioned on the recent changes page.
// - For cat A, if there are 0 hyphae in the cat, cat A does not
// exist. If there are 1 or more hyphae in the cat, cat A exists.
//
// List of things to do with categories later:
//
// - Forbid / in cat names.
// - Rename categories.
// - Delete categories.
// - Bind hyphae.
package categories
import "sync"
// List returns names of all categories.
func List() (categoryList []string) {
// listOfCategories returns names of all categories.
func listOfCategories() (categoryList []string) {
mutex.RLock()
for cat, _ := range categoryToHyphae {
categoryList = append(categoryList, cat)
@ -26,8 +33,8 @@ func List() (categoryList []string) {
return categoryList
}
// WithHypha returns what categories have the given hypha. The hypha name must be canonical.
func WithHypha(hyphaName string) (categoryList []string) {
// categoriesWithHypha returns what categories have the given hypha. The hypha name must be canonical.
func categoriesWithHypha(hyphaName string) (categoryList []string) {
mutex.RLock()
defer mutex.RUnlock()
if node, ok := hyphaToCategories[hyphaName]; ok {
@ -37,8 +44,8 @@ func WithHypha(hyphaName string) (categoryList []string) {
}
}
// Contents returns what hyphae are in the category. If the returned slice is empty, the category does not exist, and vice versa. The category name must be canonical.
func Contents(catName string) (hyphaList []string) {
// hyphaeInCategory returns what hyphae are in the category. If the returned slice is empty, the category does not exist, and vice versa. The category name must be canonical.
func hyphaeInCategory(catName string) (hyphaList []string) {
mutex.RLock()
defer mutex.RUnlock()
if node, ok := categoryToHyphae[catName]; ok {
@ -50,8 +57,8 @@ func Contents(catName string) (hyphaList []string) {
var mutex sync.RWMutex
// AddHyphaToCategory adds the hypha to the category and updates the records on the disk. If the hypha is already in the category, nothing happens. Pass canonical names.
func AddHyphaToCategory(hyphaName, catName string) {
// addHyphaToCategory adds the hypha to the category and updates the records on the disk. If the hypha is already in the category, nothing happens. Pass canonical names.
func addHyphaToCategory(hyphaName, catName string) {
mutex.Lock()
if node, ok := hyphaToCategories[hyphaName]; ok {
node.storeCategory(catName)
@ -68,8 +75,8 @@ func AddHyphaToCategory(hyphaName, catName string) {
go saveToDisk()
}
// RemoveHyphaFromCategory removes the hypha from the category and updates the records on the disk. If the hypha is not in the category, nothing happens. Pass canonical names.
func RemoveHyphaFromCategory(hyphaName, catName string) {
// removeHyphaFromCategory removes the hypha from the category and updates the records on the disk. If the hypha is not in the category, nothing happens. Pass canonical names.
func removeHyphaFromCategory(hyphaName, catName string) {
mutex.Lock()
if node, ok := hyphaToCategories[hyphaName]; ok {
node.removeCategory(catName)

View File

@ -12,8 +12,8 @@ import (
var categoryToHyphae = map[string]*categoryNode{}
var hyphaToCategories = map[string]*hyphaNode{}
// InitCategories initializes the category system. Call it after the Structure is initialized. This function might terminate the program in case of a bad mood or filesystem faults.
func InitCategories() {
// Init initializes the category system. Call it after the Structure is initialized. This function might terminate the program in case of a bad mood or filesystem faults.
func Init() {
var (
record, err = readCategoriesFromDisk()
)

View File

@ -1,10 +1,9 @@
package web
package categories
import (
"github.com/bouncepaw/mycorrhiza/hyphae/categories"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/gorilla/mux"
"io"
"log"
@ -12,16 +11,18 @@ import (
"strings"
)
func initCategories(r *mux.Router) {
// InitHandlers initializes HTTP handlers for the given router. Call somewhere in package web.
func InitHandlers(r *mux.Router) {
r.PathPrefix("/add-to-category").HandlerFunc(handlerAddToCategory).Methods("POST")
r.PathPrefix("/remove-from-category").HandlerFunc(handlerRemoveFromCategory).Methods("POST")
r.PathPrefix("/category/").HandlerFunc(handlerCategory).Methods("GET")
r.PathPrefix("/category").HandlerFunc(handlerListCategory).Methods("GET")
prepareViews()
}
func handlerListCategory(w http.ResponseWriter, rq *http.Request) {
log.Println("Viewing list of categories")
views.CategoryList(views.MetaFrom(w, rq))
categoryList(viewutil.MetaFrom(w, rq))
}
func handlerCategory(w http.ResponseWriter, rq *http.Request) {
@ -32,7 +33,7 @@ func handlerCategory(w http.ResponseWriter, rq *http.Request) {
return
}
log.Println("Viewing category", catName)
views.CategoryPage(views.MetaFrom(w, rq), catName)
categoryPage(viewutil.MetaFrom(w, rq), catName)
}
func handlerRemoveFromCategory(w http.ResponseWriter, rq *http.Request) {
@ -51,7 +52,8 @@ func handlerRemoveFromCategory(w http.ResponseWriter, rq *http.Request) {
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
return
}
categories.RemoveHyphaFromCategory(hyphaName, catName)
log.Println(user.FromRequest(rq).Name, "removed", hyphaName, "from", catName)
removeHyphaFromCategory(hyphaName, catName)
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
}
@ -71,6 +73,7 @@ func handlerAddToCategory(w http.ResponseWriter, rq *http.Request) {
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
return
}
categories.AddHyphaToCategory(hyphaName, catName)
log.Println(user.FromRequest(rq).Name, "added", hyphaName, "to", catName)
addHyphaToCategory(hyphaName, catName)
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
}

35
categories/view_card.html Normal file
View File

@ -0,0 +1,35 @@
{{define "category card"}}
{{$hyphaName := .HyphaName}}
{{$givenPermission := .GivenPermissionToModify}}
<aside class="layout-card categories-card">
<h2 class="layout-card__title">{{block `categories` .}}Categories{{end}}</h2>
<ul class="categories-card__entries">
{{range .Categories}}
<li class="categories-card__entry">
<a class="categories-card__link" href="/category/{{.}}">{{beautifulName .}}</a>
<form method="POST" action="/remove-from-category" class="categories-card__remove-form">
<input type="hidden" name="cat" value="{{.}}">
<input type="hidden" name="hypha" value="{{$hyphaName}}">
<input type="hidden" name="redirect-to" value="/hypha/{{$hyphaName}}">
{{if $givenPermission}}
<input type="submit" value="x" class="btn categories-card__btn"
title="{{block `remove from category title` .}}Remove the hypha from this category{{end}}">
{{end}}
</form>
</li>
{{end}}
{{if .GivenPermissionToModify}}
<li class="categories-card__entry categories-card__add-to-cat">
<form method="POST" action="/add-to-category" class="categories-card__add-form">
<input type="text" name="cat" id="_cat-name"
placeholder="{{block `placeholder` .}}Category name...{{end}}">
<input type="hidden" name="hypha" value="{{$hyphaName}}">
<input type="hidden" name="redirect-to" value="/hypha/{{$hyphaName}}">
<input type="submit" class="btn categories-card__btn" value="+"
title="{{block `add to category title` .}}Add the hypha to this category{{end}}">
</form>
</li>
{{end}}
</ul>
</aside>
{{end}}

18
categories/view_list.html Normal file
View File

@ -0,0 +1,18 @@
{{define "category list"}}Category list{{end}}
{{define "title"}}{{template "category list"}}{{end}}
{{define "body"}}
<main class="main-width category-list">
<h1>{{template "title"}}</h1>
{{if len .Categories}}
<ul class="category-list__entries">
{{range .Categories}}
<li class="category-list__entry">
<a class="category-list__link" href="/category/{{.}}">{{beautifulName .}}</a>
</li>
{{end}}
</ul>
{{else}}
<p>{{block `no categories` .}}This wiki has no categories.{{end}}</p>
{{end}}
</main>
{{end}}

29
categories/view_page.html Normal file
View File

@ -0,0 +1,29 @@
{{define "category x"}}Category {{. | beautifulName}}{{end}}
{{define "title"}}{{template "category x" .CatName}}{{end}}
{{define "body"}}
{{$catName := .CatName}}
<main class="main-width category">
<h1>{{block "cat" .}}Category{{end}} <i>{{beautifulName $catName}}</i></h1>
{{if len .Hyphae | not}}
<p>{{block "empty cat" .}}This category is empty{{end}}</p>
{{end}}
<ul class="category__entries">
{{range .Hyphae}}
<li class="category__entry">
<a class="category__link" href="/hypha/{{.}}">{{beautifulName .}}</a>
</li>
{{end}}
{{if .GivenPermissionToModify}}
<li class="category__entry category__add-to-cat">
<form method="POST" action="/add-to-category" class="category__add-form">
<input type="text" name="hypha" id="_hypha-name"
placeholder="{{block `hypha name` .}}Hypha name{{end}}">
<input type="hidden" name="cat" value="{{$catName}}">
<input type="hidden" name="redirect-to" value="/category/{{$catName}}">
<input type="submit" class="btn" value="{{block `add hypha` .}}Add to the category{{end}}">
</form>
</li>
{{end}}
</ul>
</main>
{{end}}

104
categories/views.go Normal file
View File

@ -0,0 +1,104 @@
package categories
import (
"embed"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/viewutil"
"log"
"strings"
"text/template" // TODO: Fight
)
const ruTranslation = `
{{define "empty cat"}}Эта категория пуста.{{end}}
{{define "add hypha"}}Добавить в категорию{{end}}
{{define "cat"}}Категория{{end}}
{{define "hypha name"}}Имя гифы{{end}}
{{define "categories"}}Категории{{end}}
{{define "placeholder"}}Имя категории...{{end}}
{{define "remove from category title"}}Убрать гифу из этой категории{{end}}
{{define "add to category title"}}Добавить гифу в эту категорию{{end}}
{{define "category list"}}Список категорий{{end}}
{{define "no categories"}}В этой вики нет категорий.{{end}}
{{define "category x"}}Категория {{. | beautifulName}}{{end}}
`
var (
//go:embed *.html
fs embed.FS
viewListChain, viewPageChain, viewCardChain viewutil.Chain
)
func prepareViews() {
m := template.Must
viewCardChain = viewutil.
En(viewutil.CopyEnWith(fs, "view_card.html")).
Ru(m(viewutil.CopyRuWith(fs, "view_card.html").Parse(ruTranslation)))
viewListChain = viewutil.
En(viewutil.CopyEnWith(fs, "view_list.html")).
Ru(m(viewutil.CopyRuWith(fs, "view_list.html").Parse(ruTranslation)))
viewPageChain = viewutil.
En(viewutil.CopyEnWith(fs, "view_page.html")).
Ru(m(viewutil.CopyRuWith(fs, "view_page.html").Parse(ruTranslation)))
}
type cardData struct {
HyphaName string
Categories []string
GivenPermissionToModify bool
}
// CategoryCard is the little sidebar that is shown nearby the hypha view.
func CategoryCard(meta viewutil.Meta, hyphaName string) string {
var buf strings.Builder
err := viewCardChain.Get(meta).ExecuteTemplate(&buf, "category card", cardData{
hyphaName,
categoriesWithHypha(hyphaName),
meta.U.CanProceed("add-to-category"),
})
if err != nil {
log.Println(err)
}
return buf.String()
}
type pageData struct {
viewutil.BaseData
CatName string
Hyphae []string
GivenPermissionToModify bool
}
func categoryPage(meta viewutil.Meta, catName string) {
if err := viewPageChain.Get(meta).ExecuteTemplate(meta.W, "page", pageData{
BaseData: viewutil.BaseData{
Meta: meta,
HeaderLinks: cfg.HeaderLinks,
CommonScripts: cfg.CommonScripts,
},
CatName: catName,
Hyphae: hyphaeInCategory(catName),
GivenPermissionToModify: meta.U.CanProceed("add-to-category"),
}); err != nil {
log.Println(err)
}
}
type listData struct {
viewutil.BaseData
Categories []string
}
func categoryList(meta viewutil.Meta) {
if err := viewListChain.Get(meta).ExecuteTemplate(meta.W, "page", listData{
BaseData: viewutil.BaseData{
Meta: meta,
HeaderLinks: cfg.HeaderLinks,
CommonScripts: cfg.CommonScripts,
},
Categories: listOfCategories(),
}); err != nil {
log.Println(err)
}
}

View File

@ -193,7 +193,7 @@ func ReadConfigFile(path string) error {
TelegramEnabled = (TelegramBotToken != "") && (TelegramBotName != "")
// This URL makes much more sense. If no URL is set or the protocol is forgotten, assume HTTP.
if (URL == "") || (strings.Index(URL, ":") == -1) {
if (URL == "") || !strings.Contains(URL, ":") {
URL = "http://" + ListenAddr
}

View File

@ -1,10 +1,10 @@
package cfg
// See https://mycorrhiza.wiki/hypha/configuration/header
import (
"github.com/bouncepaw/mycomarkup/v3"
"github.com/bouncepaw/mycomarkup/v3/blocks"
"github.com/bouncepaw/mycomarkup/v3/mycocontext"
"github.com/bouncepaw/mycomarkup/v4"
"github.com/bouncepaw/mycomarkup/v4/blocks"
"github.com/bouncepaw/mycomarkup/v4/mycocontext"
"github.com/bouncepaw/mycomarkup/v4/options"
)
// 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.
@ -24,7 +24,7 @@ func SetDefaultHeaderLinks() {
// ParseHeaderLinks extracts all rocketlinks from the given text and saves them as header links.
func ParseHeaderLinks(text string) {
HeaderLinks = []HeaderLink{}
ctx, _ := mycocontext.ContextFromStringInput("", text)
ctx, _ := mycocontext.ContextFromStringInput(text, options.Options{}.FillTheRest())
// We call for side-effects
_ = mycomarkup.BlockTree(ctx, func(block blocks.Block) {
switch launchpad := block.(type) {

5
go.mod
View File

@ -3,7 +3,7 @@ module github.com/bouncepaw/mycorrhiza
go 1.18
require (
github.com/bouncepaw/mycomarkup/v3 v3.6.3
github.com/bouncepaw/mycomarkup/v4 v4.0.0
github.com/go-ini/ini v1.63.2
github.com/gorilla/feeds v1.1.1
github.com/gorilla/mux v1.8.0
@ -22,7 +22,8 @@ require (
// Use this trick to test local Mycomarkup changes, replace the path with yours,
// but do not commit the change to the path:
// replace github.com/bouncepaw/mycomarkup/v3 v3.6.3 => "/Users/bouncepaw/GolandProjects/mycomarkup"
// replace github.com/bouncepaw/mycomarkup/v4 v4.0.0 => "/Users/bouncepaw/GolandProjects/mycomarkup"
// Use this utility every time Mycomarkup gets a major update:
// https://github.com/marwan-at-work/mod

6
go.sum
View File

@ -1,7 +1,9 @@
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bouncepaw/mycomarkup/v3 v3.6.3 h1:FQzzCxrHAEFBjPKFF/7R9gamyeU/8Cn+cFZEgngYtjE=
github.com/bouncepaw/mycomarkup/v3 v3.6.3/go.mod h1:BpiGUVsYCgRZCDxF0iIdc08LJokm/Ab36S/Hif0J6D0=
github.com/bouncepaw/mycomarkup/v4 v3.6.2 h1:5zqb12aOw19xg8/0QIvgoA8oEW2doSdWqCbXltPEaPQ=
github.com/bouncepaw/mycomarkup/v4 v3.6.2/go.mod h1:BpiGUVsYCgRZCDxF0iIdc08LJokm/Ab36S/Hif0J6D0=
github.com/bouncepaw/mycomarkup/v4 v4.0.0 h1:qokseZ+otcFuQ5vARdvxKqjlEZFMvsjFJ7YpJ4sUr8c=
github.com/bouncepaw/mycomarkup/v4 v4.0.0/go.mod h1:y0b8U6Xfnh3KfNUpG3QuAXRJwqFPPpmS2kYvLzaf688=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ini/ini v1.63.2 h1:kwN3umicd2HF3Tgvap4um1ZG52/WyKT9GGdPx0CJk6Y=

View File

@ -15,6 +15,7 @@ On big screens, the top bar is spread onto two lines.
** All hyphae
** Random
** Help
** Categories
On small screens, the authorization section and the most-used-links section are hidden behind a menu. Click the button to see them. If your browser does not support JavaScript, they are always shown.
@ -40,9 +41,11 @@ Reload the wiki.
----
Edit the hypha. You can put any markup there. Rocket links will be used for generating the top bar:
Edit the hypha. You can put any markup there. Only rocket links will be used for generating the top bar:
```myco
This paragraph is unused.
=> /recent-changes | Recent changes
=> Highlights
=> Philosophy | Our views on life

View File

@ -5,7 +5,7 @@ import (
"embed"
)
//go:embed en en.myco
//go:embed en en.myco *.html
var fs embed.FS
// Get determines what help text you need and returns it. The path is a substring from URL, it follows this form:

50
help/view_help.html Normal file
View File

@ -0,0 +1,50 @@
{{define "title"}}Help{{end}}
{{define "body"}}
<div class="layout">
<main class="main-width help">
<article>
{{if .ContentsHTML}}
{{.ContentsHTML}}
{{else}}
<h1>{{block "entry not found" .}}Entry not found{{end}}</h1>
<p>{{block "entry not found invitation" .}}If you want to write this entry by yourself, consider <a class="wikilink wikilink_external wikilink_https" href="https://github.com/bouncepaw/mycorrhiza">contributing</a> it directly.{{end}}</p>
{{end}}
</article>
</main>
<aside class="help-topics layout-card">
<h2 class="layout-card__title">{{block "topics" .}}Help topics{{end}}</h2>
<ul class="help-topics__list">
<li><a href="/help/en">{{block "main" .}}Main{{end}}</a></li>
<li><a href="/help/en/hypha">{{block "hypha" .}}Hypha{{end}}</a>
<ul>
<a href="/help/en/media">{{block "media" .}}Media{{end}}</a>
</ul>
</li>
<li><a href="/help/en/mycomarkup">{{block "mycomarkup" .}}Mycomarkup{{end}}</a></li>
<li><a href="/help/en/category">{{block "category" .}}Categories{{end}}</a></li>
<li>{{block "interface" .}}Interface{{end}}
<ul>
<li><a href="/help/en/prevnext">{{block "prevnext" .}}Previous/next{{end}}</a></li>
<li><a href="/help/en/top_bar">{{block "top_bar" .}}Top bar{{end}}</a></li>
<li><a href="/help/en/sibling_hyphae_section">{{block "sibling_hyphae" .}}Sibling hyphae{{end}}</a></li>
</ul>
</li>
<li>{{block "special pages" .}}Special pages{{end}}
<ul>
<li><a href="/help/en/recent_changes">{{block "recent_changes" .}}Recent changes{{end}}</a></li>
<li><a href="/help/en/feeds">{{block "feeds" .}}Feeds{{end}}</a></li>
</ul>
</li>
<li>{{block "configuration" .}}Configuration (for administrators){{end}}
<ul>
<li><a href="/help/en/config_file">{{block "config_file" .}}Configuration file{{end}}</a></li>
<li><a href="/help/en/lock">{{block "lock" .}}Lock{{end}}</a></li>
<li><a href="/help/en/whitelist">{{block "whitelist" .}}Whitelist{{end}}</a></li>
<li><a href="/help/en/telegram">{{block "telegram" .}}Telegram authentication{{end}}</a></li>
</ul>
</li>
</ul>
</aside>
</div>
{{end}}

111
help/web.go Normal file
View File

@ -0,0 +1,111 @@
package help
// stuff.go is used for meta stuff about the wiki or all hyphae at once.
import (
"github.com/bouncepaw/mycomarkup/v4"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/shroom"
"github.com/bouncepaw/mycorrhiza/viewutil"
"io"
"log"
"net/http"
"strings"
"text/template"
"github.com/gorilla/mux"
"github.com/bouncepaw/mycomarkup/v4/mycocontext"
)
var (
chain viewutil.Chain
ruTranslation = `
{{define "title"}}Справка{{end}}
{{define "entry not found"}}Статья не найдена{{end}}
{{define "entry not found invitation"}}Если вы хотите написать эту статью сами, то будем рады вашим правкам <a class="wikilink wikilink_external wikilink_https" href="https://github.com/bouncepaw/mycorrhiza">в репозитории Миокризы</a>.{{end}}
{{define "topics"}}Темы справки{{end}}
{{define "main"}}Введение{{end}}
{{define "hypha"}}Гифа{{end}}
{{define "media"}}Медиа{{end}}
{{define "mycomarkup"}}Микоразметка{{end}}
{{define "category"}}Категории{{end}}
{{define "interface"}}Интерфейс{{end}}
{{define "prevnext"}}Пред/след{{end}}
{{define "top_bar"}}Верхняя панель{{end}}
{{define "sibling_hyphae"}}Гифы-сиблинги{{end}}
{{define "special pages"}}Специальные страницы{{end}}
{{define "recent_changes"}}Недавние изменения{{end}}
{{define "feeds"}}Ленты{{end}}
{{define "configuration"}}Конфигурация (для администраторов){{end}}
{{define "config_file"}}Файл конфигурации{{end}}
{{define "lock"}}Замок{{end}}
{{define "whitelist"}}Белый список{{end}}
{{define "telegram"}}Вход через Телеграм{{end}}
`
)
func InitHandlers(r *mux.Router) {
r.PathPrefix("/help").HandlerFunc(handlerHelp)
chain = viewutil.
En(viewutil.CopyEnWith(fs, "view_help.html")).
Ru(template.Must(viewutil.CopyRuWith(fs, "view_help.html").Parse(ruTranslation)))
}
// handlerHelp gets the appropriate documentation or tells you where you (personally) have failed.
func handlerHelp(w http.ResponseWriter, rq *http.Request) {
// See the history of this file to resurrect the old algorithm that supported multiple languages
var (
meta = viewutil.MetaFrom(w, rq)
articlePath = strings.TrimPrefix(strings.TrimPrefix(rq.URL.Path, "/help/"), "/help")
lang = "en"
)
if articlePath == "" {
articlePath = "en"
}
if !strings.HasPrefix(articlePath, "en") {
w.WriteHeader(http.StatusNotFound)
_, _ = io.WriteString(w, "404 Not found")
return
}
content, err := Get(articlePath)
if err != nil && strings.HasPrefix(err.Error(), "open") {
w.WriteHeader(http.StatusNotFound)
viewHelp(meta, lang, "")
return
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
viewHelp(meta, lang, err.Error())
return
}
// TODO: change for the function that uses byte array when there is such function in mycomarkup.
ctx, _ := mycocontext.ContextFromStringInput(string(content), shroom.MarkupOptions(articlePath))
ast := mycomarkup.BlockTree(ctx)
result := mycomarkup.BlocksToHTML(ctx, ast)
w.WriteHeader(http.StatusOK)
viewHelp(meta, lang, result)
}
type helpData struct {
viewutil.BaseData
ContentsHTML string
Lang string
}
func viewHelp(meta viewutil.Meta, lang, contentsHTML string) {
if err := chain.Get(meta).ExecuteTemplate(meta.W, "page", helpData{
BaseData: viewutil.BaseData{
Meta: meta,
HeaderLinks: cfg.HeaderLinks,
CommonScripts: cfg.CommonScripts,
},
ContentsHTML: contentsHTML,
Lang: lang,
}); err != nil {
log.Println(err)
}
}

View File

@ -175,7 +175,7 @@ func (rev *Revision) hyphaeAffected() (hyphae []string) {
filesAffected = rev.filesAffected()
)
for _, filename := range filesAffected {
if strings.IndexRune(filename, '.') >= 0 {
if strings.ContainsRune(filename, '.') {
dotPos := strings.LastIndexByte(filename, '.')
hyphaName := string([]byte(filename)[0:dotPos]) // is it safe?
if isNewName(hyphaName) {

View File

@ -3,20 +3,6 @@
"register": "Register",
"title_search": "Search by title",
"admin_panel": "Admin panel",
"search_results_title": "Search: {{.query}}",
"search_results_query": "Search results for {{.query}}",
"search_results_desc": "Every hypha name has been compared with the query. Hyphae that have matched the query are listed below.",
"backlinks_title": "Backlinks to {{.hypha_name}}",
"backlinks_heading": "Backlinks to {{.hypha_link}}",
"backlinks_desc": "Hyphae which have a link to this hypha, embed it as an image or transclude it are listed below.",
"list_title": "List of pages",
"list_heading": "List of hyphae",
"list_desc": "This wiki has {{.n}} %s.",
"list_desc+one": "hypha",
"list_desc+other": "hyphae",
"edit_link": "Edit text",
"logout_link": "Log out",

View File

@ -118,7 +118,7 @@ func (t Localizer) GetWithLocale(locale, key string, replacements ...*Replacemen
// If the str doesn't have any substitutions, no need to
// template.Execute.
if strings.Index(str, "}}") == -1 {
if !strings.Contains(str, "}}") {
return str
}
@ -145,7 +145,7 @@ func (t Localizer) GetPlural(key string, n int, replacements ...*Replacements) s
// As in the original, we skip templating if have nothing to replace
// (however, it's strange case for plurals)
if strings.Index(str, "}}") == -1 {
if !strings.Contains(str, "}}") {
return str
}
@ -164,7 +164,7 @@ func (t Localizer) GetPlural64(key string, n int64, replacements ...*Replacement
// As in the original, we skip templating if have nothing to replace
// (however, it's strange case for plurals)
if strings.Index(str, "}}") == -1 {
if !strings.Contains(str, "}}") {
return str
}

View File

@ -3,21 +3,10 @@
"register": "Регистрация",
"title_search": "Поиск по названию",
"admin_panel": "Администрирование",
"search_results_title": "Поиск: {{.query}}",
"search_results_query": "Результаты поиска для «{{.query}}»",
"search_results_desc": "Название каждой из существующих гиф сопоставлено с запросом. Подходящие гифы приведены ниже.",
"backlinks_title": "Обратные ссылки на {{.hypha_name}}",
"backlinks_heading": "Обратные ссылки на {{.hypha_link}}",
"backlinks_desc": "Ниже перечислены гифы, на которых есть ссылка на эту гифу, трансклюзия этой гифы или эта гифа вставлена как изображение.",
"list_title": "Список страниц",
"list_heading": "Список гиф",
"list_desc": "В этой вики {{.n}} %s.",
"list_desc+one": "гифа",
"list_desc+few": "гифы",
"list_desc+many": "гиф",
"edit_link": "Редактировать",
"logout_link": "Выйти",

View File

@ -5,13 +5,13 @@
package main
import (
"github.com/bouncepaw/mycorrhiza/hyphae/categories"
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/categories"
"github.com/bouncepaw/mycorrhiza/migration"
"github.com/bouncepaw/mycorrhiza/viewutil"
"log"
"os"
"github.com/bouncepaw/mycorrhiza/hyphae/backlinks"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/history"
@ -41,6 +41,7 @@ func main() {
log.Println("Using Git storage at", files.HyphaeDir())
// Init the subsystems:
viewutil.Init()
hyphae.Index(files.HyphaeDir())
backlinks.IndexBacklinks()
go backlinks.RunBacklinksConveyor()
@ -49,7 +50,7 @@ func main() {
history.InitGitRepo()
migration.MigrateRocketsMaybe()
shroom.SetHeaderLinks()
categories.InitCategories()
categories.Init()
// Static files:
static.InitFS(files.StaticFiles())

View File

@ -4,7 +4,7 @@
package migration
import (
"github.com/bouncepaw/mycomarkup/v3/tools"
"github.com/bouncepaw/mycomarkup/v4/tools"
"io"
"io/ioutil"
"log"

159
misc/handlers.go Normal file
View File

@ -0,0 +1,159 @@
// Package misc provides miscellaneous informative views.
package misc
import (
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/shroom"
"github.com/bouncepaw/mycorrhiza/static"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/gorilla/mux"
"io"
"log"
"math/rand"
"mime"
"net/http"
)
func InitHandlers(rtr *mux.Router) {
rtr.HandleFunc("/robots.txt", handlerRobotsTxt)
rtr.HandleFunc("/static/style.css", handlerStyle)
rtr.PathPrefix("/static/").
Handler(http.StripPrefix("/static/", http.FileServer(http.FS(static.FS))))
rtr.HandleFunc("/list", handlerList)
rtr.HandleFunc("/reindex", handlerReindex)
rtr.HandleFunc("/update-header-links", handlerUpdateHeaderLinks)
rtr.HandleFunc("/random", handlerRandom)
rtr.HandleFunc("/about", handlerAbout)
rtr.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) {
http.Redirect(w, rq, "/static/favicon.ico", http.StatusSeeOther)
})
rtr.HandleFunc("/title-search/", handlerTitleSearch)
initViews()
}
// handlerList shows a list of all hyphae in the wiki in random order.
func handlerList(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
viewList(viewutil.MetaFrom(w, 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 {
var lc = l18n.FromRequest(rq)
viewutil.HttpErr(viewutil.MetaFrom(w, rq), http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.reindex_no_rights"))
log.Println("Rejected", rq.URL)
return
}
hyphae.ResetCount()
log.Println("Reindexing hyphae in", files.HyphaeDir())
hyphae.Index(files.HyphaeDir())
backlinks.IndexBacklinks()
http.Redirect(w, rq, "/", http.StatusSeeOther)
}
// handlerUpdateHeaderLinks updates header links by reading the configured hypha, if there is any, or resorting to default values.
func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if ok := user.CanProceed(rq, "update-header-links"); !ok {
var lc = l18n.FromRequest(rq)
viewutil.HttpErr(viewutil.MetaFrom(w, rq), http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.header_no_rights"))
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 {
var lc = l18n.FromRequest(rq)
viewutil.HttpErr(viewutil.MetaFrom(w, rq), http.StatusNotFound, cfg.HomeHypha, lc.Get("ui.random_no_hyphae_tip"))
return
}
i := rand.Intn(amountOfHyphae)
for h := range hyphae.YieldExistingHyphae() {
if i == 0 {
randomHyphaName = h.CanonicalName()
}
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)
var (
lc = l18n.FromRequest(rq)
title = lc.Get("ui.about_title", &l18n.Replacements{"name": cfg.WikiName})
)
_, err := io.WriteString(w, views.Base(
viewutil.MetaFrom(w, rq),
title,
views.AboutHTML(lc),
))
if err != nil {
log.Println(err)
}
}
var stylesheets = []string{"default.css", "custom.css"}
func handlerStyle(w http.ResponseWriter, rq *http.Request) {
w.Header().Set("Content-Type", mime.TypeByExtension(".css"))
for _, name := range stylesheets {
file, err := static.FS.Open(name)
if err != nil {
continue
}
_, err = io.Copy(w, file)
if err != nil {
log.Println(err)
}
_ = file.Close()
}
}
func handlerRobotsTxt(w http.ResponseWriter, rq *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
file, err := static.FS.Open("robots.txt")
if err != nil {
return
}
_, err = io.Copy(w, file)
if err != nil {
log.Println()
}
_ = file.Close()
}
func handlerTitleSearch(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
_ = rq.ParseForm()
var (
query = rq.FormValue("q")
results []string
)
for hyphaName := range shroom.YieldHyphaNamesContainingString(query) {
results = append(results, hyphaName)
}
w.WriteHeader(http.StatusOK)
viewTitleSearch(viewutil.MetaFrom(w, rq), query, results)
}

21
misc/view_list.html Normal file
View File

@ -0,0 +1,21 @@
{{define "list of hyphae"}}List of hyphae{{end}}
{{define "title"}}{{template "list of hyphae"}}{{end}}
{{define "body"}}
<div class="layout">
<main class="main-width">
<h1>{{template "list of hyphae"}}</h1>
<ol class="hypha-list">
{{range .Entries}}
<li class="hypha-list__entry">
<a class="hypha-list__link" href="/hypha/{{.Name}}">
{{beautifulName .Name}}
</a>
{{if .Ext}}
<span class="hypha-list__amnt-type">{{.Ext}}</span>
{{end}}
</li>
{{end}}
</ol>
</main>
</div>
{{end}}

View File

@ -0,0 +1,16 @@
{{define "search:"}}Search: {{.}}{{end}}
{{define "title"}}{{template "search:" .Query}}{{end}}
{{define "body"}}
<div class="layout"><main class="main-width title-search">
<h1>{{block "search results for" .Query}}Search results for {{.}}{{end}}</h1>
<p>{{block "search desc" .}}Every hypha name has been compared with the query. Hyphae that have matched the query are listed below.{{end}}</p>
<ul class="title-search__results">
{{range .Results}}
<li class="title-search__entry">
<a class="title-search__link wikilink" href="/hypha/{%s hyphaName %}">{{beautifulName .}}</a>
</li>
{{end}}
</ul>
</main>
</div>
{{end}}

95
misc/views.go Normal file
View File

@ -0,0 +1,95 @@
package misc
import (
"embed"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/viewutil"
"log"
"path/filepath"
"text/template"
)
var (
//go:embed *html
fs embed.FS
chainList, chainTitleSearch viewutil.Chain
ruTranslation = `
{{define "list of hyphae"}}Список гиф{{end}}
{{define "search:"}}Поиск:{{end}}
{{define "search results for"}}Результаты поиска для «{{.}}»{{end}}
{{define "search desc"}}Название каждой из существующих гиф сопоставлено с запросом. Подходящие гифы приведены ниже.{{end}}
`
)
func initViews() {
m := template.Must
chainList = viewutil.
En(viewutil.CopyEnWith(fs, "view_list.html")).
Ru(m(viewutil.CopyRuWith(fs, "view_list.html").Parse(ruTranslation)))
chainTitleSearch = viewutil.
En(viewutil.CopyEnWith(fs, "view_title_search.html")).
Ru(m(viewutil.CopyRuWith(fs, "view_title_search.html").Parse(ruTranslation)))
}
type listDatum struct {
Name string
Ext string
}
type listData struct {
viewutil.BaseData
Entries []listDatum
}
func viewList(meta viewutil.Meta) {
// TODO: make this more effective, there are too many loops and vars
var (
hyphaNames = make(chan string)
sortedHypha = hyphae.PathographicSort(hyphaNames)
data []listDatum
)
for hypha := range hyphae.YieldExistingHyphae() {
hyphaNames <- hypha.CanonicalName()
}
close(hyphaNames)
for hyphaName := range sortedHypha {
switch h := hyphae.ByName(hyphaName).(type) {
case *hyphae.TextualHypha:
data = append(data, listDatum{h.CanonicalName(), ""})
case *hyphae.MediaHypha:
data = append(data, listDatum{h.CanonicalName(), filepath.Ext(h.MediaFilePath())[1:]})
}
}
if err := chainList.Get(meta).ExecuteTemplate(meta.W, "page", listData{
BaseData: viewutil.BaseData{
Meta: meta,
HeaderLinks: cfg.HeaderLinks,
CommonScripts: cfg.CommonScripts,
},
Entries: data,
}); err != nil {
log.Println(err)
}
}
type titleSearchData struct {
viewutil.BaseData
Query string
Results []string
}
func viewTitleSearch(meta viewutil.Meta, query string, results []string) {
if err := chainTitleSearch.Get(meta).ExecuteTemplate(meta.W, "page", titleSearchData{
BaseData: viewutil.BaseData{
Meta: meta,
HeaderLinks: cfg.HeaderLinks,
CommonScripts: cfg.CommonScripts,
},
Query: query,
Results: results,
}); err != nil {
log.Println(err)
}
}

View File

@ -2,8 +2,7 @@ package shroom
import (
"fmt"
"github.com/bouncepaw/mycorrhiza/hyphae/backlinks"
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/user"

View File

@ -7,9 +7,6 @@ import (
"github.com/bouncepaw/mycorrhiza/user"
)
func rejectDeleteLog(h hyphae.Hypha, u *user.User, errmsg string) {
log.Printf("Reject delete %s by @%s: %s\n", h.CanonicalName(), u.Name, errmsg)
}
func rejectRenameLog(h hyphae.Hypha, u *user.User, errmsg string) {
log.Printf("Reject rename %s by @%s: %s\n", h.CanonicalName(), u.Name, errmsg)
}

View File

@ -0,0 +1,46 @@
package shroom
import (
"errors"
"github.com/bouncepaw/mycomarkup/v4/options"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/views"
)
func MarkupOptions(hyphaName string) options.Options {
return fillMycomarkupOptions(options.Options{
HyphaName: hyphaName,
WebSiteURL: cfg.URL,
TransclusionSupported: true,
})
}
func fillMycomarkupOptions(opts options.Options) options.Options {
opts.HyphaExists = func(hyphaName string) bool {
switch hyphae.ByName(hyphaName).(type) {
case *hyphae.EmptyHypha:
return false
default:
return true
}
}
opts.HyphaHTMLData = func(hyphaName string) (rawText, binaryBlock string, err error) {
switch h := hyphae.ByName(hyphaName).(type) {
case *hyphae.EmptyHypha:
err = errors.New("Hypha " + hyphaName + " does not exist")
case *hyphae.TextualHypha:
rawText, err = FetchTextFile(h)
case *hyphae.MediaHypha:
rawText, err = FetchTextFile(h)
binaryBlock = views.MediaRaw(h)
}
return
}
opts.IterateHyphaNamesWith = func(λ func(string)) {
for h := range hyphae.YieldExistingHyphae() {
λ(h.CanonicalName())
}
}
return opts.FillTheRest()
}

View File

@ -3,7 +3,7 @@ package shroom
import (
"errors"
"fmt"
"github.com/bouncepaw/mycorrhiza/hyphae/backlinks"
"github.com/bouncepaw/mycorrhiza/backlinks"
"regexp"
"github.com/bouncepaw/mycorrhiza/history"

View File

@ -7,7 +7,7 @@ import (
"github.com/bouncepaw/mycorrhiza/util"
)
// YieldHyphaNamesContainingString picks hyphae with have a string in their title, sorts and iterates over them.
// YieldHyphaNamesContainingString picks hyphae with have a string in their title, sorts and iterates over them in alphabetical order.
func YieldHyphaNamesContainingString(query string) <-chan string {
query = util.CanonicalName(strings.TrimSpace(query))
out := make(chan string)

View File

@ -2,41 +2,3 @@
//
// Some of them are wrappers around functions provided by package hyphae. They manage history for you.
package shroom
import (
"errors"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/views"
"github.com/bouncepaw/mycomarkup/v3/globals"
)
func init() {
// TODO: clean this complete and utter mess
globals.HyphaExists = func(hyphaName string) bool {
switch hyphae.ByName(hyphaName).(type) {
case *hyphae.EmptyHypha:
return false
default:
return true
}
}
globals.HyphaAccess = func(hyphaName string) (rawText, binaryBlock string, err error) {
switch h := hyphae.ByName(hyphaName).(type) {
case *hyphae.EmptyHypha:
err = errors.New("Hypha " + hyphaName + " does not exist")
case *hyphae.TextualHypha:
rawText, err = FetchTextFile(h)
case *hyphae.MediaHypha:
rawText, err = FetchTextFile(h)
binaryBlock = views.MediaRaw(h)
}
return
}
globals.HyphaIterate = func(λ func(string)) {
for h := range hyphae.YieldExistingHyphae() {
λ(h.CanonicalName())
}
}
}

View File

@ -4,10 +4,10 @@ import (
"bytes"
"errors"
"fmt"
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/hyphae/backlinks"
"github.com/bouncepaw/mycorrhiza/mimetype"
"github.com/bouncepaw/mycorrhiza/user"
"io"
@ -97,7 +97,7 @@ func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User) e
}
// TODO: that []byte(...) part should be removed
if bytes.Compare(data, []byte(oldText)) == 0 {
if bytes.Equal(data, []byte(oldText)) {
// No changes! Just like cancel button
hop.Abort()
return nil
@ -118,7 +118,7 @@ func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User) e
}
// TODO: that []byte(...) part should be removed
if bytes.Compare(data, []byte(oldText)) == 0 {
if bytes.Equal(data, []byte(oldText)) {
// No changes! Just like cancel button
hop.Abort()
return nil

View File

@ -8,7 +8,7 @@ import (
"net/http"
"strings"
"github.com/bouncepaw/mycomarkup/v3/util"
"github.com/bouncepaw/mycomarkup/v4/util"
"github.com/bouncepaw/mycorrhiza/cfg"
)

View File

@ -47,7 +47,7 @@ const aboutTemplateString = `<div class="layout">
<li><b>{{ get .L.Version }}</b> 1.9.0</li>
{{ if .Cfg.UseAuth }}
<li><b>{{ get .L.UserCount }}</b> {{ .UserCount }}</li>
<li><b>{{ get .L.HomePage }}</b> <a href="/">{{ .Cfg.HomeHypha }}</a></li>
<li><b>{{ get .L.HomeHypha }}</b> <a href="/">{{ .Cfg.HomeHypha }}</a></li>
<li><b>{{ get .L.Admins }}</b> {{$cfg := .Cfg}}{{ range $i, $username := .Admins }}
{{ if gt $i 0 }}<span aria-hidden="true">, </span>{{ end }}
<a href="/hypha/{{ $cfg.UserHypha }}/{{ $username }}">{{ $username }}</a>
@ -71,7 +71,7 @@ var aboutData = struct {
"Title": e().en("About %s").ru("О %s"),
"Version": e().en("<a href=\"https://mycorrhiza.wiki\">Mycorrhiza Wiki</a> version:").ru("Версия <a href=\"https://mycorrhiza.wiki\">Микоризы</a>:"),
"UserCount": e().en("User count:").ru("Число пользователей:"),
"HomePage": e().en("Home page:").ru("Домашняя гифа:"),
"HomeHypha": e().en("Home hypha:").ru("Домашняя гифа:"),
"Admins": e().en("Administrators:").ru("Администраторы:"),
"NoAuth": e().en("This wiki does not use authorization").ru("На этой вики не используется авторизация"),
"AboutHyphae": e().en("See <a href=\"/list\">/list</a> for information about hyphae on this wiki.").ru("См. <a href=\"/list\">/list</a>, чтобы узнать о гифах в этой вики."),

View File

@ -2,6 +2,7 @@ package views
import (
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/viewutil"
"html/template"
"io"
"log"
@ -25,7 +26,7 @@ var (
adminTemplatesRu *template.Template
)
func localizedAdminTemplates(meta Meta) *template.Template {
func localizedAdminTemplates(meta viewutil.Meta) *template.Template {
if meta.Lc.Locale == "ru" {
return adminTemplatesRu
}
@ -59,16 +60,18 @@ func init() {
template.Must(adminTemplatesEn.Clone()).Parse(adminTranslationRu))
}
func AdminPanel(meta Meta) {
func AdminPanel(meta viewutil.Meta) {
var buf strings.Builder
err := localizedAdminTemplates(meta).ExecuteTemplate(&buf, "panel", nil)
if err != nil {
log.Println(err)
}
_, err = io.WriteString(meta.W, Base(
meta,
templateAsString(localizedAdminTemplates(meta), "panel title"),
buf.String(),
meta.Lc,
meta.U,
))
if err != nil {
log.Println(err)
}
}

View File

@ -1,6 +1,8 @@
{% import "net/http" %}
{% import "sort" %}
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% import "github.com/bouncepaw/mycorrhiza/user" %}
{% func Register(rq *http.Request) %}
{% code
@ -148,3 +150,63 @@ Telegram auth widget was requested by Yogurt. As you can see, we don't offer use
</body>
</html>
{% endfunc %}
{% code
var userListL10n = map[string]l10nEntry{
"heading": en("List of users").ru("Список пользователей"),
"administrators": en("Administrators").ru("Администраторы"),
"moderators": en("Moderators").ru("Модераторы"),
"editors": en("Editors").ru("Редакторы"),
}
%}
{% func UserList(lc *l18n.Localizer) %}
<div class="layout">
<main class="main-width user-list">
{% code
var get = func(key string) string {
return userListL10n[key].get(lc.Locale)
}
var (
admins = make([]string, 0)
moderators = make([]string, 0)
editors = make([]string, 0)
)
for u := range user.YieldUsers() {
switch u.Group {
// What if we place the users into sorted slices?
case "admin":
admins = append(admins, u.Name)
case "moderator":
moderators = append(moderators, u.Name)
case "editor", "trusted":
editors = append(editors, u.Name)
}
}
sort.Strings(admins)
sort.Strings(moderators)
sort.Strings(editors)
%}
<h1>{%s get("heading") %}</h1>
<section>
<h2>{%s get("administrators") %}</h2>
<ol>{% for _, name := range admins %}
<li><a href="/hypha/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
{% endfor %}</ol>
</section>
<section>
<h2>{%s get("moderators") %}</h2>
<ol>{% for _, name := range moderators %}
<li><a href="/hypha/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
{% endfor %}</ol>
</section>
<section>
<h2>{%s get("editors") %}</h2>
<ol>{% for _, name := range editors %}
<li><a href="/hypha/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
{% endfor %}</ol>
</section>
</main>
</div>
{% endfunc %}

View File

@ -8,517 +8,523 @@ package views
import "net/http"
//line views/auth.qtpl:2
import "github.com/bouncepaw/mycorrhiza/cfg"
import "sort"
//line views/auth.qtpl:3
import "github.com/bouncepaw/mycorrhiza/cfg"
//line views/auth.qtpl:4
import "github.com/bouncepaw/mycorrhiza/l18n"
//line views/auth.qtpl:5
import "github.com/bouncepaw/mycorrhiza/user"
//line views/auth.qtpl:7
import (
qtio422016 "io"
qt422016 "github.com/valyala/quicktemplate"
)
//line views/auth.qtpl:5
//line views/auth.qtpl:7
var (
_ = qtio422016.Copy
_ = qt422016.AcquireByteBuffer
)
//line views/auth.qtpl:5
//line views/auth.qtpl:7
func StreamRegister(qw422016 *qt422016.Writer, rq *http.Request) {
//line views/auth.qtpl:5
//line views/auth.qtpl:7
qw422016.N().S(`
`)
//line views/auth.qtpl:7
//line views/auth.qtpl:9
lc := l18n.FromRequest(rq)
//line views/auth.qtpl:8
//line views/auth.qtpl:10
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<section>
`)
//line views/auth.qtpl:12
//line views/auth.qtpl:14
if cfg.AllowRegistration {
//line views/auth.qtpl:12
//line views/auth.qtpl:14
qw422016.N().S(`
<form class="modal" method="post" action="/register?`)
//line views/auth.qtpl:13
//line views/auth.qtpl:15
qw422016.E().S(rq.URL.RawQuery)
//line views/auth.qtpl:13
//line views/auth.qtpl:15
qw422016.N().S(`" id="register-form" enctype="multipart/form-data" autocomplete="off">
<fieldset class="modal__fieldset">
<legend class="modal__title">`)
//line views/auth.qtpl:15
//line views/auth.qtpl:17
qw422016.E().S(lc.Get("auth.register_header", &l18n.Replacements{"name": cfg.WikiName}))
//line views/auth.qtpl:15
//line views/auth.qtpl:17
qw422016.N().S(`</legend>
<label for="register-form__username">`)
//line views/auth.qtpl:17
//line views/auth.qtpl:19
qw422016.E().S(lc.Get("auth.username"))
//line views/auth.qtpl:17
//line views/auth.qtpl:19
qw422016.N().S(`</label>
<br>
<input type="text" required autofocus id="login-form__username" name="username">
<br>
<label for="login-form__password">`)
//line views/auth.qtpl:21
//line views/auth.qtpl:23
qw422016.E().S(lc.Get("auth.password"))
//line views/auth.qtpl:21
//line views/auth.qtpl:23
qw422016.N().S(`</label>
<br>
<input type="password" required name="password">
<p>`)
//line views/auth.qtpl:24
//line views/auth.qtpl:26
qw422016.E().S(lc.Get("auth.password_tip"))
//line views/auth.qtpl:24
//line views/auth.qtpl:26
qw422016.N().S(`</p>
<p>`)
//line views/auth.qtpl:25
//line views/auth.qtpl:27
qw422016.E().S(lc.Get("auth.cookie_tip"))
//line views/auth.qtpl:25
//line views/auth.qtpl:27
qw422016.N().S(`</p>
<button class="btn" type="submit" value="Register">`)
//line views/auth.qtpl:26
//line views/auth.qtpl:28
qw422016.E().S(lc.Get("auth.register_button"))
//line views/auth.qtpl:26
//line views/auth.qtpl:28
qw422016.N().S(`</button>
<a class="btn btn_weak" href="/`)
//line views/auth.qtpl:27
//line views/auth.qtpl:29
qw422016.E().S(rq.URL.RawQuery)
//line views/auth.qtpl:27
//line views/auth.qtpl:29
qw422016.N().S(`">`)
//line views/auth.qtpl:27
//line views/auth.qtpl:29
qw422016.E().S(lc.Get("ui.cancel"))
//line views/auth.qtpl:27
//line views/auth.qtpl:29
qw422016.N().S(`</a>
</fieldset>
</form>
`)
//line views/auth.qtpl:30
//line views/auth.qtpl:32
streamtelegramWidget(qw422016, lc)
//line views/auth.qtpl:30
//line views/auth.qtpl:32
qw422016.N().S(`
`)
//line views/auth.qtpl:31
//line views/auth.qtpl:33
} else if cfg.UseAuth {
//line views/auth.qtpl:31
//line views/auth.qtpl:33
qw422016.N().S(`
<p>`)
//line views/auth.qtpl:32
//line views/auth.qtpl:34
qw422016.E().S(lc.Get("auth.noregister"))
//line views/auth.qtpl:32
//line views/auth.qtpl:34
qw422016.N().S(`</p>
<p><a href="/`)
//line views/auth.qtpl:33
//line views/auth.qtpl:35
qw422016.E().S(rq.URL.RawQuery)
//line views/auth.qtpl:33
//line views/auth.qtpl:35
qw422016.N().S(`">← `)
//line views/auth.qtpl:33
//line views/auth.qtpl:35
qw422016.E().S(lc.Get("auth.go_back"))
//line views/auth.qtpl:33
//line views/auth.qtpl:35
qw422016.N().S(`</a></p>
`)
//line views/auth.qtpl:34
//line views/auth.qtpl:36
} else {
//line views/auth.qtpl:34
//line views/auth.qtpl:36
qw422016.N().S(`
<p>`)
//line views/auth.qtpl:35
//line views/auth.qtpl:37
qw422016.E().S(lc.Get("auth.noauth"))
//line views/auth.qtpl:35
//line views/auth.qtpl:37
qw422016.N().S(`</p>
<p><a href="/`)
//line views/auth.qtpl:36
//line views/auth.qtpl:38
qw422016.E().S(rq.URL.RawQuery)
//line views/auth.qtpl:36
//line views/auth.qtpl:38
qw422016.N().S(`">← `)
//line views/auth.qtpl:36
//line views/auth.qtpl:38
qw422016.E().S(lc.Get("auth.go_back"))
//line views/auth.qtpl:36
//line views/auth.qtpl:38
qw422016.N().S(`</a></p>
`)
//line views/auth.qtpl:37
//line views/auth.qtpl:39
}
//line views/auth.qtpl:37
//line views/auth.qtpl:39
qw422016.N().S(`
</section>
</main>
</div>
`)
//line views/auth.qtpl:41
//line views/auth.qtpl:43
}
//line views/auth.qtpl:41
//line views/auth.qtpl:43
func WriteRegister(qq422016 qtio422016.Writer, rq *http.Request) {
//line views/auth.qtpl:41
//line views/auth.qtpl:43
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:41
//line views/auth.qtpl:43
StreamRegister(qw422016, rq)
//line views/auth.qtpl:41
//line views/auth.qtpl:43
qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:41
//line views/auth.qtpl:43
}
//line views/auth.qtpl:41
//line views/auth.qtpl:43
func Register(rq *http.Request) string {
//line views/auth.qtpl:41
//line views/auth.qtpl:43
qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:41
//line views/auth.qtpl:43
WriteRegister(qb422016, rq)
//line views/auth.qtpl:41
//line views/auth.qtpl:43
qs422016 := string(qb422016.B)
//line views/auth.qtpl:41
//line views/auth.qtpl:43
qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:41
//line views/auth.qtpl:43
return qs422016
//line views/auth.qtpl:41
//line views/auth.qtpl:43
}
//line views/auth.qtpl:43
//line views/auth.qtpl:45
func StreamLogin(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
//line views/auth.qtpl:43
//line views/auth.qtpl:45
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<section>
`)
//line views/auth.qtpl:47
//line views/auth.qtpl:49
if cfg.UseAuth {
//line views/auth.qtpl:47
//line views/auth.qtpl:49
qw422016.N().S(`
<form class="modal" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
<fieldset class="modal__fieldset">
<legend class="modal__title">`)
//line views/auth.qtpl:50
//line views/auth.qtpl:52
qw422016.E().S(lc.Get("auth.login_header", &l18n.Replacements{"name": cfg.WikiName}))
//line views/auth.qtpl:50
//line views/auth.qtpl:52
qw422016.N().S(`</legend>
<label for="login-form__username">`)
//line views/auth.qtpl:51
//line views/auth.qtpl:53
qw422016.E().S(lc.Get("auth.username"))
//line views/auth.qtpl:51
//line views/auth.qtpl:53
qw422016.N().S(`</label>
<br>
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
<br>
<label for="login-form__password">`)
//line views/auth.qtpl:55
//line views/auth.qtpl:57
qw422016.E().S(lc.Get("auth.password"))
//line views/auth.qtpl:55
//line views/auth.qtpl:57
qw422016.N().S(`</label>
<br>
<input type="password" required name="password" autocomplete="current-password">
<p>`)
//line views/auth.qtpl:58
//line views/auth.qtpl:60
qw422016.E().S(lc.Get("auth.cookie_tip"))
//line views/auth.qtpl:58
//line views/auth.qtpl:60
qw422016.N().S(`</p>
<button class="btn" type="submit" value="Log in">`)
//line views/auth.qtpl:59
//line views/auth.qtpl:61
qw422016.E().S(lc.Get("auth.login_button"))
//line views/auth.qtpl:59
//line views/auth.qtpl:61
qw422016.N().S(`</button>
<a class="btn btn_weak" href="/">`)
//line views/auth.qtpl:60
//line views/auth.qtpl:62
qw422016.E().S(lc.Get("ui.cancel"))
//line views/auth.qtpl:60
//line views/auth.qtpl:62
qw422016.N().S(`</a>
</fieldset>
</form>
`)
//line views/auth.qtpl:63
//line views/auth.qtpl:65
streamtelegramWidget(qw422016, lc)
//line views/auth.qtpl:63
//line views/auth.qtpl:65
qw422016.N().S(`
`)
//line views/auth.qtpl:64
//line views/auth.qtpl:66
} else {
//line views/auth.qtpl:64
//line views/auth.qtpl:66
qw422016.N().S(`
<p>`)
//line views/auth.qtpl:65
//line views/auth.qtpl:67
qw422016.E().S(lc.Get("auth.noauth"))
//line views/auth.qtpl:65
//line views/auth.qtpl:67
qw422016.N().S(`</p>
<p><a class="btn btn_weak" href="/"> `)
//line views/auth.qtpl:66
//line views/auth.qtpl:68
qw422016.E().S(lc.Get("auth.go_home"))
//line views/auth.qtpl:66
//line views/auth.qtpl:68
qw422016.N().S(`</a></p>
`)
//line views/auth.qtpl:67
//line views/auth.qtpl:69
}
//line views/auth.qtpl:67
//line views/auth.qtpl:69
qw422016.N().S(`
</section>
</main>
</div>
`)
//line views/auth.qtpl:71
//line views/auth.qtpl:73
}
//line views/auth.qtpl:71
//line views/auth.qtpl:73
func WriteLogin(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
//line views/auth.qtpl:71
//line views/auth.qtpl:73
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:71
//line views/auth.qtpl:73
StreamLogin(qw422016, lc)
//line views/auth.qtpl:71
//line views/auth.qtpl:73
qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:71
//line views/auth.qtpl:73
}
//line views/auth.qtpl:71
//line views/auth.qtpl:73
func Login(lc *l18n.Localizer) string {
//line views/auth.qtpl:71
//line views/auth.qtpl:73
qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:71
//line views/auth.qtpl:73
WriteLogin(qb422016, lc)
//line views/auth.qtpl:71
//line views/auth.qtpl:73
qs422016 := string(qb422016.B)
//line views/auth.qtpl:71
//line views/auth.qtpl:73
qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:71
//line views/auth.qtpl:73
return qs422016
//line views/auth.qtpl:71
//line views/auth.qtpl:73
}
// Telegram auth widget was requested by Yogurt. As you can see, we don't offer user administrators control over it. Of course we don't.
//line views/auth.qtpl:74
//line views/auth.qtpl:76
func streamtelegramWidget(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
//line views/auth.qtpl:74
//line views/auth.qtpl:76
qw422016.N().S(`
`)
//line views/auth.qtpl:75
//line views/auth.qtpl:77
if cfg.TelegramEnabled {
//line views/auth.qtpl:75
//line views/auth.qtpl:77
qw422016.N().S(`
<p class="telegram-notice">`)
//line views/auth.qtpl:76
//line views/auth.qtpl:78
qw422016.E().S(lc.Get("auth.telegram_tip"))
//line views/auth.qtpl:76
//line views/auth.qtpl:78
qw422016.N().S(`</p>
<script async src="https://telegram.org/js/telegram-widget.js?15" data-telegram-login="`)
//line views/auth.qtpl:77
//line views/auth.qtpl:79
qw422016.E().S(cfg.TelegramBotName)
//line views/auth.qtpl:77
//line views/auth.qtpl:79
qw422016.N().S(`" data-size="medium" data-userpic="false" data-auth-url="`)
//line views/auth.qtpl:77
//line views/auth.qtpl:79
qw422016.E().S(cfg.URL)
//line views/auth.qtpl:77
//line views/auth.qtpl:79
qw422016.N().S(`/telegram-login"></script>
`)
//line views/auth.qtpl:78
//line views/auth.qtpl:80
}
//line views/auth.qtpl:78
//line views/auth.qtpl:80
qw422016.N().S(`
`)
//line views/auth.qtpl:79
//line views/auth.qtpl:81
}
//line views/auth.qtpl:79
//line views/auth.qtpl:81
func writetelegramWidget(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
//line views/auth.qtpl:79
//line views/auth.qtpl:81
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:79
//line views/auth.qtpl:81
streamtelegramWidget(qw422016, lc)
//line views/auth.qtpl:79
//line views/auth.qtpl:81
qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:79
//line views/auth.qtpl:81
}
//line views/auth.qtpl:79
//line views/auth.qtpl:81
func telegramWidget(lc *l18n.Localizer) string {
//line views/auth.qtpl:79
//line views/auth.qtpl:81
qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:79
//line views/auth.qtpl:81
writetelegramWidget(qb422016, lc)
//line views/auth.qtpl:79
//line views/auth.qtpl:81
qs422016 := string(qb422016.B)
//line views/auth.qtpl:79
//line views/auth.qtpl:81
qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:79
//line views/auth.qtpl:81
return qs422016
//line views/auth.qtpl:79
//line views/auth.qtpl:81
}
//line views/auth.qtpl:81
//line views/auth.qtpl:83
func StreamLoginError(qw422016 *qt422016.Writer, err string, lc *l18n.Localizer) {
//line views/auth.qtpl:81
//line views/auth.qtpl:83
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<section>
`)
//line views/auth.qtpl:85
//line views/auth.qtpl:87
switch err {
//line views/auth.qtpl:86
//line views/auth.qtpl:88
case "unknown username":
//line views/auth.qtpl:86
//line views/auth.qtpl:88
qw422016.N().S(`
<p class="error">`)
//line views/auth.qtpl:87
//line views/auth.qtpl:89
qw422016.E().S(lc.Get("auth.error_username"))
//line views/auth.qtpl:87
//line views/auth.qtpl:89
qw422016.N().S(`</p>
`)
//line views/auth.qtpl:88
//line views/auth.qtpl:90
case "wrong password":
//line views/auth.qtpl:88
//line views/auth.qtpl:90
qw422016.N().S(`
<p class="error">`)
//line views/auth.qtpl:89
//line views/auth.qtpl:91
qw422016.E().S(lc.Get("auth.error_password"))
//line views/auth.qtpl:89
//line views/auth.qtpl:91
qw422016.N().S(`</p>
`)
//line views/auth.qtpl:90
//line views/auth.qtpl:92
default:
//line views/auth.qtpl:90
//line views/auth.qtpl:92
qw422016.N().S(`
<p class="error">`)
//line views/auth.qtpl:91
//line views/auth.qtpl:93
qw422016.E().S(err)
//line views/auth.qtpl:91
//line views/auth.qtpl:93
qw422016.N().S(`</p>
`)
//line views/auth.qtpl:92
//line views/auth.qtpl:94
}
//line views/auth.qtpl:92
//line views/auth.qtpl:94
qw422016.N().S(`
<p><a href="/login"> `)
//line views/auth.qtpl:93
//line views/auth.qtpl:95
qw422016.E().S(lc.Get("auth.try_again"))
//line views/auth.qtpl:93
//line views/auth.qtpl:95
qw422016.N().S(`</a></p>
</section>
</main>
</div>
`)
//line views/auth.qtpl:97
//line views/auth.qtpl:99
}
//line views/auth.qtpl:97
//line views/auth.qtpl:99
func WriteLoginError(qq422016 qtio422016.Writer, err string, lc *l18n.Localizer) {
//line views/auth.qtpl:97
//line views/auth.qtpl:99
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:97
//line views/auth.qtpl:99
StreamLoginError(qw422016, err, lc)
//line views/auth.qtpl:97
//line views/auth.qtpl:99
qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:97
//line views/auth.qtpl:99
}
//line views/auth.qtpl:97
//line views/auth.qtpl:99
func LoginError(err string, lc *l18n.Localizer) string {
//line views/auth.qtpl:97
//line views/auth.qtpl:99
qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:97
//line views/auth.qtpl:99
WriteLoginError(qb422016, err, lc)
//line views/auth.qtpl:97
//line views/auth.qtpl:99
qs422016 := string(qb422016.B)
//line views/auth.qtpl:97
//line views/auth.qtpl:99
qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:97
//line views/auth.qtpl:99
return qs422016
//line views/auth.qtpl:97
//line views/auth.qtpl:99
}
//line views/auth.qtpl:99
//line views/auth.qtpl:101
func StreamLogout(qw422016 *qt422016.Writer, can bool, lc *l18n.Localizer) {
//line views/auth.qtpl:99
//line views/auth.qtpl:101
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<section>
`)
//line views/auth.qtpl:103
//line views/auth.qtpl:105
if can {
//line views/auth.qtpl:103
//line views/auth.qtpl:105
qw422016.N().S(`
<h1>`)
//line views/auth.qtpl:104
//line views/auth.qtpl:106
qw422016.E().S(lc.Get("auth.logout_header"))
//line views/auth.qtpl:104
//line views/auth.qtpl:106
qw422016.N().S(`</h1>
<form method="POST" action="/logout">
<input class="btn btn_accent" type="submit" value="`)
//line views/auth.qtpl:106
//line views/auth.qtpl:108
qw422016.E().S(lc.Get("auth.logout_button"))
//line views/auth.qtpl:106
//line views/auth.qtpl:108
qw422016.N().S(`"/>
<a class="btn btn_weak" href="/">`)
//line views/auth.qtpl:107
//line views/auth.qtpl:109
qw422016.E().S(lc.Get("auth.go_home"))
//line views/auth.qtpl:107
//line views/auth.qtpl:109
qw422016.N().S(`</a>
</form>
`)
//line views/auth.qtpl:109
//line views/auth.qtpl:111
} else {
//line views/auth.qtpl:109
//line views/auth.qtpl:111
qw422016.N().S(`
<p>`)
//line views/auth.qtpl:110
//line views/auth.qtpl:112
qw422016.E().S(lc.Get("auth.logout_anon"))
//line views/auth.qtpl:110
//line views/auth.qtpl:112
qw422016.N().S(`</p>
<p><a href="/login">`)
//line views/auth.qtpl:111
//line views/auth.qtpl:113
qw422016.E().S(lc.Get("auth.login_title"))
//line views/auth.qtpl:111
//line views/auth.qtpl:113
qw422016.N().S(`</a></p>
<p><a href="/"> `)
//line views/auth.qtpl:112
//line views/auth.qtpl:114
qw422016.E().S(lc.Get("auth.go_home"))
//line views/auth.qtpl:112
//line views/auth.qtpl:114
qw422016.N().S(`</a></p>
`)
//line views/auth.qtpl:113
//line views/auth.qtpl:115
}
//line views/auth.qtpl:113
//line views/auth.qtpl:115
qw422016.N().S(`
</section>
</main>
</div>
`)
//line views/auth.qtpl:117
//line views/auth.qtpl:119
}
//line views/auth.qtpl:117
//line views/auth.qtpl:119
func WriteLogout(qq422016 qtio422016.Writer, can bool, lc *l18n.Localizer) {
//line views/auth.qtpl:117
//line views/auth.qtpl:119
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:117
//line views/auth.qtpl:119
StreamLogout(qw422016, can, lc)
//line views/auth.qtpl:117
//line views/auth.qtpl:119
qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:117
//line views/auth.qtpl:119
}
//line views/auth.qtpl:117
//line views/auth.qtpl:119
func Logout(can bool, lc *l18n.Localizer) string {
//line views/auth.qtpl:117
//line views/auth.qtpl:119
qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:117
//line views/auth.qtpl:119
WriteLogout(qb422016, can, lc)
//line views/auth.qtpl:117
//line views/auth.qtpl:119
qs422016 := string(qb422016.B)
//line views/auth.qtpl:117
//line views/auth.qtpl:119
qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:117
//line views/auth.qtpl:119
return qs422016
//line views/auth.qtpl:117
//line views/auth.qtpl:119
}
//line views/auth.qtpl:119
//line views/auth.qtpl:121
func StreamLock(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
//line views/auth.qtpl:119
//line views/auth.qtpl:121
qw422016.N().S(`
<!doctype html>
<html>
@ -526,9 +532,9 @@ func StreamLock(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>🔒 `)
//line views/auth.qtpl:125
//line views/auth.qtpl:127
qw422016.E().S(lc.Get("auth.lock_title"))
//line views/auth.qtpl:125
//line views/auth.qtpl:127
qw422016.N().S(`</title>
<link rel="shortcut icon" href="/static/favicon.ico">
<link rel="stylesheet" href="/static/style.css">
@ -538,68 +544,237 @@ func StreamLock(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
<section class="locked-notice__message">
<p class="locked-notice__lock">🔒</p>
<h1 class="locked-notice__title">`)
//line views/auth.qtpl:133
//line views/auth.qtpl:135
qw422016.E().S(lc.Get("auth.lock_title"))
//line views/auth.qtpl:133
//line views/auth.qtpl:135
qw422016.N().S(`</h1>
<form class="locked-notice__login-form" method="post" action="/login" id="login-form" enctype="multipart/form-data" autocomplete="on">
<label for="login-form__username">`)
//line views/auth.qtpl:135
//line views/auth.qtpl:137
qw422016.E().S(lc.Get("auth.username"))
//line views/auth.qtpl:135
//line views/auth.qtpl:137
qw422016.N().S(`</label>
<br>
<input type="text" required autofocus id="login-form__username" name="username" autocomplete="username">
<br>
<label for="login-form__password">`)
//line views/auth.qtpl:139
//line views/auth.qtpl:141
qw422016.E().S(lc.Get("auth.password"))
//line views/auth.qtpl:139
//line views/auth.qtpl:141
qw422016.N().S(`</label>
<br>
<input type="password" required name="password" autocomplete="current-password">
<br>
<button class="btn" type="submit" value="Log in">`)
//line views/auth.qtpl:143
//line views/auth.qtpl:145
qw422016.E().S(lc.Get("auth.login_button"))
//line views/auth.qtpl:143
//line views/auth.qtpl:145
qw422016.N().S(`</button>
</form>
`)
//line views/auth.qtpl:145
//line views/auth.qtpl:147
streamtelegramWidget(qw422016, lc)
//line views/auth.qtpl:145
//line views/auth.qtpl:147
qw422016.N().S(`
</section>
</main>
</body>
</html>
`)
//line views/auth.qtpl:150
//line views/auth.qtpl:152
}
//line views/auth.qtpl:150
//line views/auth.qtpl:152
func WriteLock(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
//line views/auth.qtpl:150
//line views/auth.qtpl:152
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:150
//line views/auth.qtpl:152
StreamLock(qw422016, lc)
//line views/auth.qtpl:150
//line views/auth.qtpl:152
qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:150
//line views/auth.qtpl:152
}
//line views/auth.qtpl:150
//line views/auth.qtpl:152
func Lock(lc *l18n.Localizer) string {
//line views/auth.qtpl:150
//line views/auth.qtpl:152
qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:150
//line views/auth.qtpl:152
WriteLock(qb422016, lc)
//line views/auth.qtpl:150
//line views/auth.qtpl:152
qs422016 := string(qb422016.B)
//line views/auth.qtpl:150
//line views/auth.qtpl:152
qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:150
//line views/auth.qtpl:152
return qs422016
//line views/auth.qtpl:150
//line views/auth.qtpl:152
}
//line views/auth.qtpl:155
var userListL10n = map[string]l10nEntry{
"heading": en("List of users").ru("Список пользователей"),
"administrators": en("Administrators").ru("Администраторы"),
"moderators": en("Moderators").ru("Модераторы"),
"editors": en("Editors").ru("Редакторы"),
}
//line views/auth.qtpl:163
func StreamUserList(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
//line views/auth.qtpl:163
qw422016.N().S(`
<div class="layout">
<main class="main-width user-list">
`)
//line views/auth.qtpl:167
var get = func(key string) string {
return userListL10n[key].get(lc.Locale)
}
var (
admins = make([]string, 0)
moderators = make([]string, 0)
editors = make([]string, 0)
)
for u := range user.YieldUsers() {
switch u.Group {
// What if we place the users into sorted slices?
case "admin":
admins = append(admins, u.Name)
case "moderator":
moderators = append(moderators, u.Name)
case "editor", "trusted":
editors = append(editors, u.Name)
}
}
sort.Strings(admins)
sort.Strings(moderators)
sort.Strings(editors)
//line views/auth.qtpl:190
qw422016.N().S(`
<h1>`)
//line views/auth.qtpl:191
qw422016.E().S(get("heading"))
//line views/auth.qtpl:191
qw422016.N().S(`</h1>
<section>
<h2>`)
//line views/auth.qtpl:193
qw422016.E().S(get("administrators"))
//line views/auth.qtpl:193
qw422016.N().S(`</h2>
<ol>`)
//line views/auth.qtpl:194
for _, name := range admins {
//line views/auth.qtpl:194
qw422016.N().S(`
<li><a href="/hypha/`)
//line views/auth.qtpl:195
qw422016.E().S(cfg.UserHypha)
//line views/auth.qtpl:195
qw422016.N().S(`/`)
//line views/auth.qtpl:195
qw422016.E().S(name)
//line views/auth.qtpl:195
qw422016.N().S(`">`)
//line views/auth.qtpl:195
qw422016.E().S(name)
//line views/auth.qtpl:195
qw422016.N().S(`</a></li>
`)
//line views/auth.qtpl:196
}
//line views/auth.qtpl:196
qw422016.N().S(`</ol>
</section>
<section>
<h2>`)
//line views/auth.qtpl:199
qw422016.E().S(get("moderators"))
//line views/auth.qtpl:199
qw422016.N().S(`</h2>
<ol>`)
//line views/auth.qtpl:200
for _, name := range moderators {
//line views/auth.qtpl:200
qw422016.N().S(`
<li><a href="/hypha/`)
//line views/auth.qtpl:201
qw422016.E().S(cfg.UserHypha)
//line views/auth.qtpl:201
qw422016.N().S(`/`)
//line views/auth.qtpl:201
qw422016.E().S(name)
//line views/auth.qtpl:201
qw422016.N().S(`">`)
//line views/auth.qtpl:201
qw422016.E().S(name)
//line views/auth.qtpl:201
qw422016.N().S(`</a></li>
`)
//line views/auth.qtpl:202
}
//line views/auth.qtpl:202
qw422016.N().S(`</ol>
</section>
<section>
<h2>`)
//line views/auth.qtpl:205
qw422016.E().S(get("editors"))
//line views/auth.qtpl:205
qw422016.N().S(`</h2>
<ol>`)
//line views/auth.qtpl:206
for _, name := range editors {
//line views/auth.qtpl:206
qw422016.N().S(`
<li><a href="/hypha/`)
//line views/auth.qtpl:207
qw422016.E().S(cfg.UserHypha)
//line views/auth.qtpl:207
qw422016.N().S(`/`)
//line views/auth.qtpl:207
qw422016.E().S(name)
//line views/auth.qtpl:207
qw422016.N().S(`">`)
//line views/auth.qtpl:207
qw422016.E().S(name)
//line views/auth.qtpl:207
qw422016.N().S(`</a></li>
`)
//line views/auth.qtpl:208
}
//line views/auth.qtpl:208
qw422016.N().S(`</ol>
</section>
</main>
</div>
`)
//line views/auth.qtpl:212
}
//line views/auth.qtpl:212
func WriteUserList(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
//line views/auth.qtpl:212
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:212
StreamUserList(qw422016, lc)
//line views/auth.qtpl:212
qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:212
}
//line views/auth.qtpl:212
func UserList(lc *l18n.Localizer) string {
//line views/auth.qtpl:212
qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:212
WriteUserList(qb422016, lc)
//line views/auth.qtpl:212
qs422016 := string(qb422016.B)
//line views/auth.qtpl:212
qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:212
return qs422016
//line views/auth.qtpl:212
}

View File

@ -2,28 +2,11 @@ package views
import (
"embed"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/user"
"io"
"net/http"
"github.com/bouncepaw/mycorrhiza/viewutil"
)
// Meta is a bundle of common stuffs used by views, templates.
type Meta struct {
Lc *l18n.Localizer
U *user.User
W io.Writer
PageTitle string
}
// MetaFrom makes a Meta from the given data. You are meant to further modify it.
func MetaFrom(w http.ResponseWriter, rq *http.Request) Meta {
return Meta{
Lc: l18n.FromRequest(rq),
U: user.FromRequest(rq),
W: w,
}
}
//go:embed *.html
var fs embed.FS
var (
//go:embed *.html
fs embed.FS
Base = viewutil.Base
)

View File

@ -1,125 +0,0 @@
package views
import (
"github.com/bouncepaw/mycorrhiza/hyphae/categories"
"github.com/bouncepaw/mycorrhiza/util"
"html/template"
"io"
"log"
"strings"
)
const categoriesRu = `
{{define "empty cat"}}Эта категория пуста.{{end}}
{{define "add hypha"}}Добавить в категорию{{end}}
{{define "cat"}}Категория{{end}}
{{define "hypha name"}}Имя гифы{{end}}
{{define "categories"}}Категории{{end}}
{{define "placeholder"}}Имя категории...{{end}}
{{define "remove from category title"}}Убрать гифу из этой категории{{end}}
{{define "add to category title"}}Добавить гифу в эту категорию{{end}}
{{define "category list heading"}}Список категорий{{end}}
{{define "no categories"}}В этой вики нет категорий.{{end}}
{{define "category x"}}Категория {{. | beautifulName}}{{end}}
`
var (
categoryTemplatesEn *template.Template
categoryTemplatesRu *template.Template
)
func init() {
categoryTemplatesEn = template.Must(template.
New("category").
Funcs(
template.FuncMap{
"beautifulName": util.BeautifulName,
}).
ParseFS(fs, "categories.html"))
categoryTemplatesRu = template.Must(template.Must(categoryTemplatesEn.Clone()).Parse(categoriesRu))
}
func localizedCatTemplates(meta Meta) *template.Template {
if meta.Lc.Locale == "ru" {
return categoryTemplatesRu
}
return categoryTemplatesEn
}
func localizedCatTemplateAsString(meta Meta, name string, datum ...interface{}) string {
var buf strings.Builder
var err error
if len(datum) == 1 {
err = localizedCatTemplates(meta).ExecuteTemplate(&buf, name, datum[0])
} else {
err = localizedCatTemplates(meta).ExecuteTemplate(&buf, name, nil)
}
if err != nil {
log.Println(err)
return ""
}
return buf.String()
}
func categoryCard(meta Meta, hyphaName string) string {
var buf strings.Builder
err := localizedCatTemplates(meta).ExecuteTemplate(&buf, "category card", struct {
HyphaName string
Categories []string
GivenPermissionToModify bool
}{
hyphaName,
categories.WithHypha(hyphaName),
meta.U.CanProceed("add-to-category"),
})
if err != nil {
log.Println(err)
}
return buf.String()
}
func CategoryPage(meta Meta, catName string) {
var buf strings.Builder
err := localizedCatTemplates(meta).ExecuteTemplate(&buf, "category page", struct {
CatName string
Hyphae []string
GivenPermissionToModify bool
}{
catName,
categories.Contents(catName),
meta.U.CanProceed("add-to-category"),
})
if err != nil {
log.Println(err)
}
_, err = io.WriteString(meta.W, Base(
localizedCatTemplateAsString(meta, "category x", catName),
buf.String(),
meta.Lc,
meta.U,
))
if err != nil {
log.Println(err)
}
}
func CategoryList(meta Meta) {
var buf strings.Builder
err := localizedCatTemplates(meta).ExecuteTemplate(&buf, "category list", struct {
Categories []string
}{
categories.List(),
})
if err != nil {
log.Println(err)
}
_, err = io.WriteString(meta.W, Base(
localizedCatTemplateAsString(meta, "category list heading"),
buf.String(),
meta.Lc,
meta.U,
))
if err != nil {
log.Println(err)
}
}

View File

@ -1,82 +0,0 @@
{{define "category x"}}Category {{. | beautifulName}}{{end}}
{{define "category card"}}
{{$hyphaName := .HyphaName}}
{{$givenPermission := .GivenPermissionToModify}}
<aside class="layout-card categories-card">
<h2 class="layout-card__title">{{block `categories` .}}Categories{{end}}</h2>
<ul class="categories-card__entries">
{{range .Categories}}
<li class="categories-card__entry">
<a class="categories-card__link" href="/category/{{.}}">{{beautifulName .}}</a>
<form method="POST" action="/remove-from-category" class="categories-card__remove-form">
<input type="hidden" name="cat" value="{{.}}">
<input type="hidden" name="hypha" value="{{$hyphaName}}">
<input type="hidden" name="redirect-to" value="/hypha/{{$hyphaName}}">
{{if $givenPermission}}
<input type="submit" value="x" class="btn categories-card__btn"
title="{{block `remove from category title` .}}Remove the hypha from this category{{end}}">
{{end}}
</form>
</li>
{{end}}
{{if .GivenPermissionToModify}}
<li class="categories-card__entry categories-card__add-to-cat">
<form method="POST" action="/add-to-category" class="categories-card__add-form">
<input type="text" name="cat" id="_cat-name"
placeholder="{{block `placeholder` .}}Category name...{{end}}">
<input type="hidden" name="hypha" value="{{$hyphaName}}">
<input type="hidden" name="redirect-to" value="/hypha/{{$hyphaName}}">
<input type="submit" class="btn categories-card__btn" value="+"
title="{{block `add to category title` .}}Add the hypha to this category{{end}}">
</form>
</li>
{{end}}
</ul>
</aside>
{{end}}
{{define "category page"}}
{{$catName := .CatName}}
<main class="main-width category">
<h1>{{block "cat" .}}Category{{end}} <i>{{beautifulName $catName}}</i></h1>
{{if len .Hyphae | not}}
<p>{{block "empty cat" .}}This category is empty{{end}}</p>
{{end}}
<ul class="category__entries">
{{range .Hyphae}}
<li class="category__entry">
<a class="category__link" href="/hypha/{{.}}">{{beautifulName .}}</a>
</li>
{{end}}
{{if .GivenPermissionToModify}}
<li class="category__entry category__add-to-cat">
<form method="POST" action="/add-to-category" class="category__add-form">
<input type="text" name="hypha" id="_hypha-name"
placeholder="{{block `hypha name` .}}Hypha name{{end}}">
<input type="hidden" name="cat" value="{{$catName}}">
<input type="hidden" name="redirect-to" value="/category/{{$catName}}">
<input type="submit" class="btn" value="{{block `add hypha` .}}Add to the category{{end}}">
</form>
</li>
{{end}}
</ul>
</main>
{{end}}
{{define "category list"}}
<main class="main-width category-list">
<h1>{{block `category list heading` .}}Category list{{end}}</h1>
{{if len .Categories}}
<ul class="category-list__entries">
{{range .Categories}}
<li class="category-list__entry">
<a class="category-list__link" href="/category/{{.}}">{{beautifulName .}}</a>
</li>
{{end}}
</ul>
{{else}}
<p>{{block `no categories` .}}This wiki has no categories.{{end}}</p>
{{end}}
</main>
{{end}}

View File

@ -1,101 +0,0 @@
package views
import (
"fmt"
"github.com/bouncepaw/mycorrhiza/l18n"
"log"
"strings"
"text/template"
)
var helpTopicsL10n = map[string][]string{
"topics": {"Help topics", "Темы справки"},
"main": {"Main", "Введение"},
"hypha": {"Hypha", "Гифа"},
"media": {"Media", "Медиа"},
"mycomarkup": {"Mycomarkup", "Микоразметка"},
"interface": {"Interface", "Интерфейс"},
"prevnext": {"Previous/next", "Назад/далее"}, // пред след?
"top_bar": {"Top bar", "Верхняя панель"},
"sibling_hyphae": {"Sibling hyphae", "Гифы-сиблинги"},
"special_pages": {"Special pages", "Специальные страницы"},
"recent_changes": {"Recent changes", "Недавние изменения"}, // так ли? В медиавики свежие правки
"feeds": {"Feeds", "Ленты"},
"configuration": {"Configuration (for administrators)", "Конфигурация (для администраторов)"},
"config_file": {"Configuration file", "Файл конфигурации"},
"lock": {"Lock", "Блокировка"}, // Не Замок ли?
"whitelist": {"Whitelist", "Белый список"},
"telegram": {"Telegram authentication", "Вход через Телеграм"},
"category": {"Categories", "Категории"},
}
const helpTopicTemplate = `<aside class="help-topics layout-card">
<h2 class="layout-card__title">{{l "topics"}}</h2>
<ul class="help-topics__list">
<li>{{l "main" | a ""}}</li>
<li>{{l "hypha" | a "/hypha"}}
<ul>
{{l "media" | a "/media"}}
</ul>
</li>
<li>{{l "mycomarkup" | a "/mycomarkup"}}</li>
<li>{{l "category" | a "/category"}}</li>
<li>{{l "interface"}}
<ul>
<li>{{l "prevnext" | a "/prevnext"}}</li>
<li>{{l "top_bar" | a "/top_bar"}}</li>
<li>{{l "sibling_hyphae" | a "/sibling_hyphae_section"}}</li>
</ul>
</li>
<li>{{l "special_pages"}}
<ul>
<li>{{l "recent_changes" | a "/recent_changes"}}</li>
<li>{{l "feeds" | a "/feeds"}}</li>
</ul>
</li>
<li>{{l "configuration"}}
<ul>
<li>{{l "config_file" | a "/config_file"}}</li>
<li>{{l "lock" | a "/lock"}}</li>
<li>{{l "whitelist" | a "/whitelist"}}</li>
<li>{{l "telegram" | a "/telegram"}}</li>
</ul>
</li>
</ul>
</aside>`
// helpTopicsLinkWrapper wraps in <a>
func helpTopicsLinkWrapper(lang string) func(string, string) string {
return func(path, contents string) string {
return fmt.Sprintf(`<a href="/help/%s%s">%s</a>`, lang, path, contents)
}
}
func helpTopicsLocalizedTopic(lang string) func(string) string {
pos := 0
if lang == "ru" {
pos = 1
}
return func(topic string) string {
return helpTopicsL10n[topic][pos]
}
}
func helpTopics(lang string, lc *l18n.Localizer) string {
temp, err := template.
New("help topics").
Funcs(template.FuncMap{
"a": helpTopicsLinkWrapper(lang),
"l": helpTopicsLocalizedTopic(lc.Locale),
}).
Parse(helpTopicTemplate)
if err != nil {
log.Println(err)
return ""
}
// TODO: one day, it should write to a better place
var out strings.Builder
_ = temp.Execute(&out, nil) // Shall not fail!
return out.String()
}

View File

@ -1,9 +1,10 @@
{% import "strings" %}
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/hyphae/backlinks" %}
{% import "github.com/bouncepaw/mycorrhiza/backlinks" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% import "github.com/bouncepaw/mycorrhiza/user" %}
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
{% import "github.com/bouncepaw/mycorrhiza/viewutil" %}
{% func hyphaInfoEntry(h hyphae.Hypha, u *user.User, action, displayText string) %}
{% if u.CanProceed(action) %}
@ -13,7 +14,7 @@
{% endif %}
{% endfunc %}
{% func hyphaInfo(meta Meta, h hyphae.Hypha) %}
{% func hyphaInfo(meta viewutil.Meta, h hyphae.Hypha) %}
{% code
u := meta.U
lc := meta.Lc
@ -52,3 +53,9 @@
</section>
{% endif %}
{% endfunc %}
{% func commonScripts() %}
{% for _, scriptPath := range cfg.CommonScripts %}
<script src="{%s scriptPath %}"></script>
{% endfor %}
{% endfunc %}

View File

@ -11,7 +11,7 @@ import "strings"
import "github.com/bouncepaw/mycorrhiza/cfg"
//line views/nav.qtpl:3
import "github.com/bouncepaw/mycorrhiza/hyphae/backlinks"
import "github.com/bouncepaw/mycorrhiza/backlinks"
//line views/nav.qtpl:4
import "github.com/bouncepaw/mycorrhiza/l18n"
@ -22,270 +22,322 @@ import "github.com/bouncepaw/mycorrhiza/user"
//line views/nav.qtpl:6
import "github.com/bouncepaw/mycorrhiza/hyphae"
//line views/nav.qtpl:8
//line views/nav.qtpl:7
import "github.com/bouncepaw/mycorrhiza/viewutil"
//line views/nav.qtpl:9
import (
qtio422016 "io"
qt422016 "github.com/valyala/quicktemplate"
)
//line views/nav.qtpl:8
//line views/nav.qtpl:9
var (
_ = qtio422016.Copy
_ = qt422016.AcquireByteBuffer
)
//line views/nav.qtpl:8
//line views/nav.qtpl:9
func streamhyphaInfoEntry(qw422016 *qt422016.Writer, h hyphae.Hypha, u *user.User, action, displayText string) {
//line views/nav.qtpl:8
//line views/nav.qtpl:9
qw422016.N().S(`
`)
//line views/nav.qtpl:9
//line views/nav.qtpl:10
if u.CanProceed(action) {
//line views/nav.qtpl:9
//line views/nav.qtpl:10
qw422016.N().S(`
<li class="hypha-info__entry hypha-info__entry_`)
//line views/nav.qtpl:10
//line views/nav.qtpl:11
qw422016.E().S(action)
//line views/nav.qtpl:10
//line views/nav.qtpl:11
qw422016.N().S(`">
<a class="hypha-info__link" href="/`)
//line views/nav.qtpl:11
//line views/nav.qtpl:12
qw422016.E().S(action)
//line views/nav.qtpl:11
//line views/nav.qtpl:12
qw422016.N().S(`/`)
//line views/nav.qtpl:11
//line views/nav.qtpl:12
qw422016.E().S(h.CanonicalName())
//line views/nav.qtpl:11
//line views/nav.qtpl:12
qw422016.N().S(`">`)
//line views/nav.qtpl:11
//line views/nav.qtpl:12
qw422016.E().S(displayText)
//line views/nav.qtpl:11
//line views/nav.qtpl:12
qw422016.N().S(`</a>
</li>
`)
//line views/nav.qtpl:13
//line views/nav.qtpl:14
}
//line views/nav.qtpl:13
//line views/nav.qtpl:14
qw422016.N().S(`
`)
//line views/nav.qtpl:14
//line views/nav.qtpl:15
}
//line views/nav.qtpl:14
//line views/nav.qtpl:15
func writehyphaInfoEntry(qq422016 qtio422016.Writer, h hyphae.Hypha, u *user.User, action, displayText string) {
//line views/nav.qtpl:14
//line views/nav.qtpl:15
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/nav.qtpl:14
//line views/nav.qtpl:15
streamhyphaInfoEntry(qw422016, h, u, action, displayText)
//line views/nav.qtpl:14
//line views/nav.qtpl:15
qt422016.ReleaseWriter(qw422016)
//line views/nav.qtpl:14
//line views/nav.qtpl:15
}
//line views/nav.qtpl:14
//line views/nav.qtpl:15
func hyphaInfoEntry(h hyphae.Hypha, u *user.User, action, displayText string) string {
//line views/nav.qtpl:14
//line views/nav.qtpl:15
qb422016 := qt422016.AcquireByteBuffer()
//line views/nav.qtpl:14
//line views/nav.qtpl:15
writehyphaInfoEntry(qb422016, h, u, action, displayText)
//line views/nav.qtpl:14
//line views/nav.qtpl:15
qs422016 := string(qb422016.B)
//line views/nav.qtpl:14
//line views/nav.qtpl:15
qt422016.ReleaseByteBuffer(qb422016)
//line views/nav.qtpl:14
//line views/nav.qtpl:15
return qs422016
//line views/nav.qtpl:14
//line views/nav.qtpl:15
}
//line views/nav.qtpl:16
func streamhyphaInfo(qw422016 *qt422016.Writer, meta Meta, h hyphae.Hypha) {
//line views/nav.qtpl:16
//line views/nav.qtpl:17
func streamhyphaInfo(qw422016 *qt422016.Writer, meta viewutil.Meta, h hyphae.Hypha) {
//line views/nav.qtpl:17
qw422016.N().S(`
`)
//line views/nav.qtpl:18
//line views/nav.qtpl:19
u := meta.U
lc := meta.Lc
backs := backlinks.BacklinksCount(h)
//line views/nav.qtpl:21
//line views/nav.qtpl:22
qw422016.N().S(`
<nav class="hypha-info">
<ul class="hypha-info__list">
`)
//line views/nav.qtpl:24
//line views/nav.qtpl:25
streamhyphaInfoEntry(qw422016, h, u, "history", lc.Get("ui.history_link"))
//line views/nav.qtpl:24
//line views/nav.qtpl:25
qw422016.N().S(`
`)
//line views/nav.qtpl:25
//line views/nav.qtpl:26
streamhyphaInfoEntry(qw422016, h, u, "rename", lc.Get("ui.rename_link"))
//line views/nav.qtpl:25
//line views/nav.qtpl:26
qw422016.N().S(`
`)
//line views/nav.qtpl:26
//line views/nav.qtpl:27
streamhyphaInfoEntry(qw422016, h, u, "delete", lc.Get("ui.delete_link"))
//line views/nav.qtpl:26
//line views/nav.qtpl:27
qw422016.N().S(`
`)
//line views/nav.qtpl:27
//line views/nav.qtpl:28
streamhyphaInfoEntry(qw422016, h, u, "text", lc.Get("ui.text_link"))
//line views/nav.qtpl:27
//line views/nav.qtpl:28
qw422016.N().S(`
`)
//line views/nav.qtpl:28
//line views/nav.qtpl:29
streamhyphaInfoEntry(qw422016, h, u, "media", lc.Get("ui.media_link"))
//line views/nav.qtpl:28
//line views/nav.qtpl:29
qw422016.N().S(`
`)
//line views/nav.qtpl:29
//line views/nav.qtpl:30
streamhyphaInfoEntry(qw422016, h, u, "backlinks", lc.GetPlural("ui.backlinks_link", backs))
//line views/nav.qtpl:29
//line views/nav.qtpl:30
qw422016.N().S(`
</ul>
</nav>
`)
//line views/nav.qtpl:32
//line views/nav.qtpl:33
}
//line views/nav.qtpl:32
func writehyphaInfo(qq422016 qtio422016.Writer, meta Meta, h hyphae.Hypha) {
//line views/nav.qtpl:32
//line views/nav.qtpl:33
func writehyphaInfo(qq422016 qtio422016.Writer, meta viewutil.Meta, h hyphae.Hypha) {
//line views/nav.qtpl:33
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/nav.qtpl:32
//line views/nav.qtpl:33
streamhyphaInfo(qw422016, meta, h)
//line views/nav.qtpl:32
//line views/nav.qtpl:33
qt422016.ReleaseWriter(qw422016)
//line views/nav.qtpl:32
//line views/nav.qtpl:33
}
//line views/nav.qtpl:32
func hyphaInfo(meta Meta, h hyphae.Hypha) string {
//line views/nav.qtpl:32
//line views/nav.qtpl:33
func hyphaInfo(meta viewutil.Meta, h hyphae.Hypha) string {
//line views/nav.qtpl:33
qb422016 := qt422016.AcquireByteBuffer()
//line views/nav.qtpl:32
//line views/nav.qtpl:33
writehyphaInfo(qb422016, meta, h)
//line views/nav.qtpl:32
//line views/nav.qtpl:33
qs422016 := string(qb422016.B)
//line views/nav.qtpl:32
//line views/nav.qtpl:33
qt422016.ReleaseByteBuffer(qb422016)
//line views/nav.qtpl:32
//line views/nav.qtpl:33
return qs422016
//line views/nav.qtpl:32
//line views/nav.qtpl:33
}
//line views/nav.qtpl:34
//line views/nav.qtpl:35
func streamsiblingHyphae(qw422016 *qt422016.Writer, siblings string, lc *l18n.Localizer) {
//line views/nav.qtpl:34
//line views/nav.qtpl:35
qw422016.N().S(`
`)
//line views/nav.qtpl:35
//line views/nav.qtpl:36
if cfg.UseSiblingHyphaeSidebar {
//line views/nav.qtpl:35
//line views/nav.qtpl:36
qw422016.N().S(`
<aside class="sibling-hyphae layout-card">
<h2 class="sibling-hyphae__title layout-card__title">`)
//line views/nav.qtpl:37
//line views/nav.qtpl:38
qw422016.E().S(lc.Get("ui.sibling_hyphae"))
//line views/nav.qtpl:37
//line views/nav.qtpl:38
qw422016.N().S(`</h2>
`)
//line views/nav.qtpl:38
//line views/nav.qtpl:39
qw422016.N().S(siblings)
//line views/nav.qtpl:38
//line views/nav.qtpl:39
qw422016.N().S(`
</aside>
`)
//line views/nav.qtpl:40
//line views/nav.qtpl:41
}
//line views/nav.qtpl:40
//line views/nav.qtpl:41
qw422016.N().S(`
`)
//line views/nav.qtpl:41
//line views/nav.qtpl:42
}
//line views/nav.qtpl:41
//line views/nav.qtpl:42
func writesiblingHyphae(qq422016 qtio422016.Writer, siblings string, lc *l18n.Localizer) {
//line views/nav.qtpl:41
//line views/nav.qtpl:42
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/nav.qtpl:41
//line views/nav.qtpl:42
streamsiblingHyphae(qw422016, siblings, lc)
//line views/nav.qtpl:41
//line views/nav.qtpl:42
qt422016.ReleaseWriter(qw422016)
//line views/nav.qtpl:41
//line views/nav.qtpl:42
}
//line views/nav.qtpl:41
//line views/nav.qtpl:42
func siblingHyphae(siblings string, lc *l18n.Localizer) string {
//line views/nav.qtpl:41
//line views/nav.qtpl:42
qb422016 := qt422016.AcquireByteBuffer()
//line views/nav.qtpl:41
//line views/nav.qtpl:42
writesiblingHyphae(qb422016, siblings, lc)
//line views/nav.qtpl:41
//line views/nav.qtpl:42
qs422016 := string(qb422016.B)
//line views/nav.qtpl:41
//line views/nav.qtpl:42
qt422016.ReleaseByteBuffer(qb422016)
//line views/nav.qtpl:41
//line views/nav.qtpl:42
return qs422016
//line views/nav.qtpl:41
//line views/nav.qtpl:42
}
//line views/nav.qtpl:43
//line views/nav.qtpl:44
func StreamSubhyphae(qw422016 *qt422016.Writer, subhyphae string, lc *l18n.Localizer) {
//line views/nav.qtpl:43
//line views/nav.qtpl:44
qw422016.N().S(`
`)
//line views/nav.qtpl:44
//line views/nav.qtpl:45
if strings.TrimSpace(subhyphae) != "" {
//line views/nav.qtpl:44
//line views/nav.qtpl:45
qw422016.N().S(`
<section class="subhyphae">
<h2 class="subhyphae__title">`)
//line views/nav.qtpl:46
//line views/nav.qtpl:47
qw422016.E().S(lc.Get("ui.subhyphae"))
//line views/nav.qtpl:46
//line views/nav.qtpl:47
qw422016.N().S(`</h2>
<nav class="subhyphae__nav">
<ul class="subhyphae__list">
`)
//line views/nav.qtpl:49
//line views/nav.qtpl:50
qw422016.N().S(subhyphae)
//line views/nav.qtpl:49
//line views/nav.qtpl:50
qw422016.N().S(`
</ul>
</nav>
</section>
`)
//line views/nav.qtpl:53
//line views/nav.qtpl:54
}
//line views/nav.qtpl:53
//line views/nav.qtpl:54
qw422016.N().S(`
`)
//line views/nav.qtpl:54
//line views/nav.qtpl:55
}
//line views/nav.qtpl:54
//line views/nav.qtpl:55
func WriteSubhyphae(qq422016 qtio422016.Writer, subhyphae string, lc *l18n.Localizer) {
//line views/nav.qtpl:54
//line views/nav.qtpl:55
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/nav.qtpl:54
//line views/nav.qtpl:55
StreamSubhyphae(qw422016, subhyphae, lc)
//line views/nav.qtpl:54
//line views/nav.qtpl:55
qt422016.ReleaseWriter(qw422016)
//line views/nav.qtpl:54
//line views/nav.qtpl:55
}
//line views/nav.qtpl:54
//line views/nav.qtpl:55
func Subhyphae(subhyphae string, lc *l18n.Localizer) string {
//line views/nav.qtpl:54
//line views/nav.qtpl:55
qb422016 := qt422016.AcquireByteBuffer()
//line views/nav.qtpl:54
//line views/nav.qtpl:55
WriteSubhyphae(qb422016, subhyphae, lc)
//line views/nav.qtpl:54
//line views/nav.qtpl:55
qs422016 := string(qb422016.B)
//line views/nav.qtpl:54
//line views/nav.qtpl:55
qt422016.ReleaseByteBuffer(qb422016)
//line views/nav.qtpl:54
//line views/nav.qtpl:55
return qs422016
//line views/nav.qtpl:54
//line views/nav.qtpl:55
}
//line views/nav.qtpl:57
func streamcommonScripts(qw422016 *qt422016.Writer) {
//line views/nav.qtpl:57
qw422016.N().S(`
`)
//line views/nav.qtpl:58
for _, scriptPath := range cfg.CommonScripts {
//line views/nav.qtpl:58
qw422016.N().S(`
<script src="`)
//line views/nav.qtpl:59
qw422016.E().S(scriptPath)
//line views/nav.qtpl:59
qw422016.N().S(`"></script>
`)
//line views/nav.qtpl:60
}
//line views/nav.qtpl:60
qw422016.N().S(`
`)
//line views/nav.qtpl:61
}
//line views/nav.qtpl:61
func writecommonScripts(qq422016 qtio422016.Writer) {
//line views/nav.qtpl:61
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/nav.qtpl:61
streamcommonScripts(qw422016)
//line views/nav.qtpl:61
qt422016.ReleaseWriter(qw422016)
//line views/nav.qtpl:61
}
//line views/nav.qtpl:61
func commonScripts() string {
//line views/nav.qtpl:61
qb422016 := qt422016.AcquireByteBuffer()
//line views/nav.qtpl:61
writecommonScripts(qb422016)
//line views/nav.qtpl:61
qs422016 := string(qb422016.B)
//line views/nav.qtpl:61
qt422016.ReleaseByteBuffer(qb422016)
//line views/nav.qtpl:61
return qs422016
//line views/nav.qtpl:61
}

View File

@ -5,11 +5,13 @@
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
{% import "github.com/bouncepaw/mycorrhiza/categories" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% import "github.com/bouncepaw/mycorrhiza/mimetype" %}
{% import "github.com/bouncepaw/mycorrhiza/tree" %}
{% import "github.com/bouncepaw/mycorrhiza/user" %}
{% import "github.com/bouncepaw/mycorrhiza/util" %}
{% import "github.com/bouncepaw/mycorrhiza/viewutil" %}
{% func MediaMenu(rq *http.Request, h hyphae.Hypha, u *user.User) %}
{% code
@ -85,7 +87,7 @@
If `contents` == "", a helpful message is shown instead.
If you rename .prevnext, change the docs too.
{% func Hypha(meta Meta, h hyphae.Hypha, contents string) %}
{% func Hypha(meta viewutil.Meta, h hyphae.Hypha, contents string) %}
{% code
siblings, subhyphae, prevHyphaName, nextHyphaName := tree.Tree(h.CanonicalName())
lc := meta.Lc
@ -131,7 +133,7 @@ If you rename .prevnext, change the docs too.
{%= hyphaInfo(meta, h) %}
</section>
</main>
{%s= categoryCard(meta, h.CanonicalName()) %}
{%s= categories.CategoryCard(meta, h.CanonicalName()) %}
{%= siblingHyphae(siblings, meta.Lc) %}
</div>
{%= viewScripts() %}

View File

@ -23,613 +23,619 @@ import "github.com/bouncepaw/mycorrhiza/cfg"
import "github.com/bouncepaw/mycorrhiza/hyphae"
//line views/readers.qtpl:8
import "github.com/bouncepaw/mycorrhiza/l18n"
import "github.com/bouncepaw/mycorrhiza/categories"
//line views/readers.qtpl:9
import "github.com/bouncepaw/mycorrhiza/mimetype"
import "github.com/bouncepaw/mycorrhiza/l18n"
//line views/readers.qtpl:10
import "github.com/bouncepaw/mycorrhiza/tree"
import "github.com/bouncepaw/mycorrhiza/mimetype"
//line views/readers.qtpl:11
import "github.com/bouncepaw/mycorrhiza/user"
import "github.com/bouncepaw/mycorrhiza/tree"
//line views/readers.qtpl:12
import "github.com/bouncepaw/mycorrhiza/user"
//line views/readers.qtpl:13
import "github.com/bouncepaw/mycorrhiza/util"
//line views/readers.qtpl:14
import "github.com/bouncepaw/mycorrhiza/viewutil"
//line views/readers.qtpl:16
import (
qtio422016 "io"
qt422016 "github.com/valyala/quicktemplate"
)
//line views/readers.qtpl:14
//line views/readers.qtpl:16
var (
_ = qtio422016.Copy
_ = qt422016.AcquireByteBuffer
)
//line views/readers.qtpl:14
//line views/readers.qtpl:16
func StreamMediaMenu(qw422016 *qt422016.Writer, rq *http.Request, h hyphae.Hypha, u *user.User) {
//line views/readers.qtpl:14
//line views/readers.qtpl:16
qw422016.N().S(`
`)
//line views/readers.qtpl:16
//line views/readers.qtpl:18
lc := l18n.FromRequest(rq)
//line views/readers.qtpl:17
//line views/readers.qtpl:19
qw422016.N().S(`
<div class="layout">
<main class="main-width media-tab">
<h1>`)
//line views/readers.qtpl:20
//line views/readers.qtpl:22
qw422016.N().S(lc.Get("ui.media_title", &l18n.Replacements{"name": beautifulLink(h.CanonicalName())}))
//line views/readers.qtpl:20
//line views/readers.qtpl:22
qw422016.N().S(`</h1>
`)
//line views/readers.qtpl:21
//line views/readers.qtpl:23
switch h.(type) {
//line views/readers.qtpl:22
//line views/readers.qtpl:24
case *hyphae.MediaHypha:
//line views/readers.qtpl:22
//line views/readers.qtpl:24
qw422016.N().S(`
<p class="explanation">`)
//line views/readers.qtpl:23
//line views/readers.qtpl:25
qw422016.E().S(lc.Get("ui.media_tip"))
//line views/readers.qtpl:23
//line views/readers.qtpl:25
qw422016.N().S(` <a href="/help/en/media" class="shy-link">`)
//line views/readers.qtpl:23
//line views/readers.qtpl:25
qw422016.E().S(lc.Get("ui.media_what_is"))
//line views/readers.qtpl:23
//line views/readers.qtpl:25
qw422016.N().S(`</a></p>
`)
//line views/readers.qtpl:24
//line views/readers.qtpl:26
default:
//line views/readers.qtpl:24
//line views/readers.qtpl:26
qw422016.N().S(`
<p class="explanation">`)
//line views/readers.qtpl:25
//line views/readers.qtpl:27
qw422016.E().S(lc.Get("ui.media_empty"))
//line views/readers.qtpl:25
//line views/readers.qtpl:27
qw422016.N().S(` <a href="/help/en/media" class="shy-link">`)
//line views/readers.qtpl:25
//line views/readers.qtpl:27
qw422016.E().S(lc.Get("ui.media_what_is"))
//line views/readers.qtpl:25
//line views/readers.qtpl:27
qw422016.N().S(`</a></p>
`)
//line views/readers.qtpl:26
//line views/readers.qtpl:28
}
//line views/readers.qtpl:26
//line views/readers.qtpl:28
qw422016.N().S(`
<section class="amnt-grid">
`)
//line views/readers.qtpl:29
//line views/readers.qtpl:31
switch h := h.(type) {
//line views/readers.qtpl:30
//line views/readers.qtpl:32
case *hyphae.MediaHypha:
//line views/readers.qtpl:30
//line views/readers.qtpl:32
qw422016.N().S(`
`)
//line views/readers.qtpl:32
//line views/readers.qtpl:34
mime := mimetype.FromExtension(path.Ext(h.MediaFilePath()))
fileinfo, err := os.Stat(h.MediaFilePath())
//line views/readers.qtpl:33
//line views/readers.qtpl:35
qw422016.N().S(`
`)
//line views/readers.qtpl:34
//line views/readers.qtpl:36
if err == nil {
//line views/readers.qtpl:34
//line views/readers.qtpl:36
qw422016.N().S(`
<fieldset class="amnt-menu-block">
<legend class="modal__title modal__title_small">`)
//line views/readers.qtpl:36
//line views/readers.qtpl:38
qw422016.E().S(lc.Get("ui.media_stat"))
//line views/readers.qtpl:36
//line views/readers.qtpl:38
qw422016.N().S(`</legend>
<p class="modal__confirmation-msg"><b>`)
//line views/readers.qtpl:37
//line views/readers.qtpl:39
qw422016.E().S(lc.Get("ui.media_stat_size"))
//line views/readers.qtpl:37
//line views/readers.qtpl:39
qw422016.N().S(`</b> `)
//line views/readers.qtpl:37
//line views/readers.qtpl:39
qw422016.E().S(lc.GetPlural64("ui.media_size_value", fileinfo.Size()))
//line views/readers.qtpl:37
//line views/readers.qtpl:39
qw422016.N().S(`</p>
<p><b>`)
//line views/readers.qtpl:38
//line views/readers.qtpl:40
qw422016.E().S(lc.Get("ui.media_stat_mime"))
//line views/readers.qtpl:38
//line views/readers.qtpl:40
qw422016.N().S(`</b> `)
//line views/readers.qtpl:38
//line views/readers.qtpl:40
qw422016.E().S(mime)
//line views/readers.qtpl:38
//line views/readers.qtpl:40
qw422016.N().S(`</p>
</fieldset>
`)
//line views/readers.qtpl:40
//line views/readers.qtpl:42
}
//line views/readers.qtpl:40
//line views/readers.qtpl:42
qw422016.N().S(`
`)
//line views/readers.qtpl:42
//line views/readers.qtpl:44
if strings.HasPrefix(mime, "image/") {
//line views/readers.qtpl:42
//line views/readers.qtpl:44
qw422016.N().S(`
<fieldset class="amnt-menu-block">
<legend class="modal__title modal__title_small">`)
//line views/readers.qtpl:44
//line views/readers.qtpl:46
qw422016.E().S(lc.Get("ui.media_include"))
//line views/readers.qtpl:44
//line views/readers.qtpl:46
qw422016.N().S(`</legend>
<p class="modal__confirmation-msg">`)
//line views/readers.qtpl:45
//line views/readers.qtpl:47
qw422016.E().S(lc.Get("ui.media_include_tip"))
//line views/readers.qtpl:45
//line views/readers.qtpl:47
qw422016.N().S(`</p>
<pre class="codeblock"><code>img { `)
//line views/readers.qtpl:46
//line views/readers.qtpl:48
qw422016.E().S(h.CanonicalName())
//line views/readers.qtpl:46
//line views/readers.qtpl:48
qw422016.N().S(` }</code></pre>
</fieldset>
`)
//line views/readers.qtpl:48
//line views/readers.qtpl:50
}
//line views/readers.qtpl:48
//line views/readers.qtpl:50
qw422016.N().S(`
`)
//line views/readers.qtpl:49
//line views/readers.qtpl:51
}
//line views/readers.qtpl:49
//line views/readers.qtpl:51
qw422016.N().S(`
`)
//line views/readers.qtpl:51
//line views/readers.qtpl:53
if u.CanProceed("upload-binary") {
//line views/readers.qtpl:51
//line views/readers.qtpl:53
qw422016.N().S(`
<form action="/upload-binary/`)
//line views/readers.qtpl:52
//line views/readers.qtpl:54
qw422016.E().S(h.CanonicalName())
//line views/readers.qtpl:52
//line views/readers.qtpl:54
qw422016.N().S(`"
method="post" enctype="multipart/form-data"
class="upload-binary modal amnt-menu-block">
<fieldset class="modal__fieldset">
<legend class="modal__title modal__title_small">`)
//line views/readers.qtpl:56
//line views/readers.qtpl:58
qw422016.E().S(lc.Get("ui.media_new"))
//line views/readers.qtpl:56
//line views/readers.qtpl:58
qw422016.N().S(`</legend>
<p class="modal__confirmation-msg">`)
//line views/readers.qtpl:57
//line views/readers.qtpl:59
qw422016.E().S(lc.Get("ui.media_new_tip"))
//line views/readers.qtpl:57
//line views/readers.qtpl:59
qw422016.N().S(`</p>
<label for="upload-binary__input"></label>
<input type="file" id="upload-binary__input" name="binary">
<button type="submit" class="btn stick-to-bottom" value="Upload">`)
//line views/readers.qtpl:61
//line views/readers.qtpl:63
qw422016.E().S(lc.Get("ui.media_upload"))
//line views/readers.qtpl:61
//line views/readers.qtpl:63
qw422016.N().S(`</button>
</fieldset>
</form>
`)
//line views/readers.qtpl:64
//line views/readers.qtpl:66
}
//line views/readers.qtpl:64
//line views/readers.qtpl:66
qw422016.N().S(`
`)
//line views/readers.qtpl:67
//line views/readers.qtpl:69
switch h := h.(type) {
//line views/readers.qtpl:68
//line views/readers.qtpl:70
case *hyphae.MediaHypha:
//line views/readers.qtpl:68
//line views/readers.qtpl:70
qw422016.N().S(`
`)
//line views/readers.qtpl:69
//line views/readers.qtpl:71
if u.CanProceed("remove-media") {
//line views/readers.qtpl:69
//line views/readers.qtpl:71
qw422016.N().S(`
<form action="/remove-media/`)
//line views/readers.qtpl:70
//line views/readers.qtpl:72
qw422016.E().S(h.CanonicalName())
//line views/readers.qtpl:70
//line views/readers.qtpl:72
qw422016.N().S(`" method="post" class="modal amnt-menu-block" method="POST">
<fieldset class="modal__fieldset">
<legend class="modal__title modal__title_small">`)
//line views/readers.qtpl:72
//line views/readers.qtpl:74
qw422016.E().S(lc.Get("ui.media_remove"))
//line views/readers.qtpl:72
//line views/readers.qtpl:74
qw422016.N().S(`</legend>
<p class="modal__confirmation-msg">`)
//line views/readers.qtpl:73
//line views/readers.qtpl:75
qw422016.E().S(lc.Get("ui.media_remove_tip"))
//line views/readers.qtpl:73
//line views/readers.qtpl:75
qw422016.N().S(`</p>
<button type="submit" class="btn" value="Remove media">`)
//line views/readers.qtpl:74
//line views/readers.qtpl:76
qw422016.E().S(lc.Get("ui.media_remove_button"))
//line views/readers.qtpl:74
//line views/readers.qtpl:76
qw422016.N().S(`</button>
</fieldset>
</form>
`)
//line views/readers.qtpl:77
//line views/readers.qtpl:79
}
//line views/readers.qtpl:77
//line views/readers.qtpl:79
qw422016.N().S(`
`)
//line views/readers.qtpl:78
//line views/readers.qtpl:80
}
//line views/readers.qtpl:78
//line views/readers.qtpl:80
qw422016.N().S(`
</section>
</main>
</div>
`)
//line views/readers.qtpl:83
//line views/readers.qtpl:85
}
//line views/readers.qtpl:83
//line views/readers.qtpl:85
func WriteMediaMenu(qq422016 qtio422016.Writer, rq *http.Request, h hyphae.Hypha, u *user.User) {
//line views/readers.qtpl:83
//line views/readers.qtpl:85
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/readers.qtpl:83
//line views/readers.qtpl:85
StreamMediaMenu(qw422016, rq, h, u)
//line views/readers.qtpl:83
//line views/readers.qtpl:85
qt422016.ReleaseWriter(qw422016)
//line views/readers.qtpl:83
//line views/readers.qtpl:85
}
//line views/readers.qtpl:83
//line views/readers.qtpl:85
func MediaMenu(rq *http.Request, h hyphae.Hypha, u *user.User) string {
//line views/readers.qtpl:83
//line views/readers.qtpl:85
qb422016 := qt422016.AcquireByteBuffer()
//line views/readers.qtpl:83
//line views/readers.qtpl:85
WriteMediaMenu(qb422016, rq, h, u)
//line views/readers.qtpl:83
//line views/readers.qtpl:85
qs422016 := string(qb422016.B)
//line views/readers.qtpl:83
//line views/readers.qtpl:85
qt422016.ReleaseByteBuffer(qb422016)
//line views/readers.qtpl:83
//line views/readers.qtpl:85
return qs422016
//line views/readers.qtpl:83
//line views/readers.qtpl:85
}
// If `contents` == "", a helpful message is shown instead.
//
// If you rename .prevnext, change the docs too.
//line views/readers.qtpl:88
func StreamHypha(qw422016 *qt422016.Writer, meta Meta, h hyphae.Hypha, contents string) {
//line views/readers.qtpl:88
//line views/readers.qtpl:90
func StreamHypha(qw422016 *qt422016.Writer, meta viewutil.Meta, h hyphae.Hypha, contents string) {
//line views/readers.qtpl:90
qw422016.N().S(`
`)
//line views/readers.qtpl:90
//line views/readers.qtpl:92
siblings, subhyphae, prevHyphaName, nextHyphaName := tree.Tree(h.CanonicalName())
lc := meta.Lc
//line views/readers.qtpl:92
//line views/readers.qtpl:94
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<section id="hypha">
`)
//line views/readers.qtpl:96
//line views/readers.qtpl:98
if meta.U.CanProceed("edit") {
//line views/readers.qtpl:96
//line views/readers.qtpl:98
qw422016.N().S(`
<div class="btn btn_navititle">
<a class="btn__link_navititle" href="/edit/`)
//line views/readers.qtpl:98
//line views/readers.qtpl:100
qw422016.E().S(h.CanonicalName())
//line views/readers.qtpl:98
//line views/readers.qtpl:100
qw422016.N().S(`">`)
//line views/readers.qtpl:98
//line views/readers.qtpl:100
qw422016.E().S(lc.Get("ui.edit_link"))
//line views/readers.qtpl:98
//line views/readers.qtpl:100
qw422016.N().S(`</a>
</div>
`)
//line views/readers.qtpl:100
//line views/readers.qtpl:102
}
//line views/readers.qtpl:100
//line views/readers.qtpl:102
qw422016.N().S(`
`)
//line views/readers.qtpl:102
//line views/readers.qtpl:104
if cfg.UseAuth && util.IsProfileName(h.CanonicalName()) && meta.U.Name == strings.TrimPrefix(h.CanonicalName(), cfg.UserHypha+"/") {
//line views/readers.qtpl:102
//line views/readers.qtpl:104
qw422016.N().S(`
<div class="btn btn_navititle">
<a class="btn__link_navititle" href="/logout">`)
//line views/readers.qtpl:104
//line views/readers.qtpl:106
qw422016.E().S(lc.Get("ui.logout_link"))
//line views/readers.qtpl:104
//line views/readers.qtpl:106
qw422016.N().S(`</a>
</div>
`)
//line views/readers.qtpl:106
//line views/readers.qtpl:108
if meta.U.Group == "admin" {
//line views/readers.qtpl:106
//line views/readers.qtpl:108
qw422016.N().S(`
<div class="btn btn_navititle">
<a class="btn__link_navititle" href="/admin">`)
//line views/readers.qtpl:108
//line views/readers.qtpl:110
qw422016.E().S(lc.Get("ui.admin_panel"))
//line views/readers.qtpl:108
//line views/readers.qtpl:110
qw422016.N().S(`<a>
</div>
`)
//line views/readers.qtpl:110
//line views/readers.qtpl:112
}
//line views/readers.qtpl:110
//line views/readers.qtpl:112
qw422016.N().S(`
`)
//line views/readers.qtpl:111
//line views/readers.qtpl:113
}
//line views/readers.qtpl:111
//line views/readers.qtpl:113
qw422016.N().S(`
`)
//line views/readers.qtpl:113
//line views/readers.qtpl:115
qw422016.N().S(NaviTitle(h))
//line views/readers.qtpl:113
//line views/readers.qtpl:115
qw422016.N().S(`
`)
//line views/readers.qtpl:114
//line views/readers.qtpl:116
switch h.(type) {
//line views/readers.qtpl:115
//line views/readers.qtpl:117
case *hyphae.EmptyHypha:
//line views/readers.qtpl:115
//line views/readers.qtpl:117
qw422016.N().S(`
`)
//line views/readers.qtpl:116
//line views/readers.qtpl:118
streamnonExistentHyphaNotice(qw422016, h, meta.U, meta.Lc)
//line views/readers.qtpl:116
//line views/readers.qtpl:118
qw422016.N().S(`
`)
//line views/readers.qtpl:117
//line views/readers.qtpl:119
default:
//line views/readers.qtpl:117
//line views/readers.qtpl:119
qw422016.N().S(`
`)
//line views/readers.qtpl:118
//line views/readers.qtpl:120
qw422016.N().S(contents)
//line views/readers.qtpl:118
//line views/readers.qtpl:120
qw422016.N().S(`
`)
//line views/readers.qtpl:119
//line views/readers.qtpl:121
}
//line views/readers.qtpl:119
//line views/readers.qtpl:121
qw422016.N().S(`
</section>
<section class="prevnext">
`)
//line views/readers.qtpl:122
//line views/readers.qtpl:124
if prevHyphaName != "" {
//line views/readers.qtpl:122
//line views/readers.qtpl:124
qw422016.N().S(`
<a class="prevnext__el prevnext__prev" href="/hypha/`)
//line views/readers.qtpl:123
//line views/readers.qtpl:125
qw422016.E().S(prevHyphaName)
//line views/readers.qtpl:123
//line views/readers.qtpl:125
qw422016.N().S(`" rel="prev">← `)
//line views/readers.qtpl:123
//line views/readers.qtpl:125
qw422016.E().S(util.BeautifulName(path.Base(prevHyphaName)))
//line views/readers.qtpl:123
//line views/readers.qtpl:125
qw422016.N().S(`</a>
`)
//line views/readers.qtpl:124
//line views/readers.qtpl:126
}
//line views/readers.qtpl:124
//line views/readers.qtpl:126
qw422016.N().S(`
`)
//line views/readers.qtpl:125
//line views/readers.qtpl:127
if nextHyphaName != "" {
//line views/readers.qtpl:125
//line views/readers.qtpl:127
qw422016.N().S(`
<a class="prevnext__el prevnext__next" href="/hypha/`)
//line views/readers.qtpl:126
//line views/readers.qtpl:128
qw422016.E().S(nextHyphaName)
//line views/readers.qtpl:126
//line views/readers.qtpl:128
qw422016.N().S(`" rel="next">`)
//line views/readers.qtpl:126
//line views/readers.qtpl:128
qw422016.E().S(util.BeautifulName(path.Base(nextHyphaName)))
//line views/readers.qtpl:126
//line views/readers.qtpl:128
qw422016.N().S(` </a>
`)
//line views/readers.qtpl:127
//line views/readers.qtpl:129
}
//line views/readers.qtpl:127
//line views/readers.qtpl:129
qw422016.N().S(`
</section>
`)
//line views/readers.qtpl:129
//line views/readers.qtpl:131
StreamSubhyphae(qw422016, subhyphae, meta.Lc)
//line views/readers.qtpl:129
//line views/readers.qtpl:131
qw422016.N().S(`
<section id="hypha-bottom">
`)
//line views/readers.qtpl:131
//line views/readers.qtpl:133
streamhyphaInfo(qw422016, meta, h)
//line views/readers.qtpl:131
//line views/readers.qtpl:133
qw422016.N().S(`
</section>
</main>
`)
//line views/readers.qtpl:134
qw422016.N().S(categoryCard(meta, h.CanonicalName()))
//line views/readers.qtpl:134
//line views/readers.qtpl:136
qw422016.N().S(categories.CategoryCard(meta, h.CanonicalName()))
//line views/readers.qtpl:136
qw422016.N().S(`
`)
//line views/readers.qtpl:135
//line views/readers.qtpl:137
streamsiblingHyphae(qw422016, siblings, meta.Lc)
//line views/readers.qtpl:135
//line views/readers.qtpl:137
qw422016.N().S(`
</div>
`)
//line views/readers.qtpl:137
//line views/readers.qtpl:139
streamviewScripts(qw422016)
//line views/readers.qtpl:137
//line views/readers.qtpl:139
qw422016.N().S(`
`)
//line views/readers.qtpl:138
//line views/readers.qtpl:140
}
//line views/readers.qtpl:138
func WriteHypha(qq422016 qtio422016.Writer, meta Meta, h hyphae.Hypha, contents string) {
//line views/readers.qtpl:138
//line views/readers.qtpl:140
func WriteHypha(qq422016 qtio422016.Writer, meta viewutil.Meta, h hyphae.Hypha, contents string) {
//line views/readers.qtpl:140
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/readers.qtpl:138
//line views/readers.qtpl:140
StreamHypha(qw422016, meta, h, contents)
//line views/readers.qtpl:138
//line views/readers.qtpl:140
qt422016.ReleaseWriter(qw422016)
//line views/readers.qtpl:138
//line views/readers.qtpl:140
}
//line views/readers.qtpl:138
func Hypha(meta Meta, h hyphae.Hypha, contents string) string {
//line views/readers.qtpl:138
//line views/readers.qtpl:140
func Hypha(meta viewutil.Meta, h hyphae.Hypha, contents string) string {
//line views/readers.qtpl:140
qb422016 := qt422016.AcquireByteBuffer()
//line views/readers.qtpl:138
//line views/readers.qtpl:140
WriteHypha(qb422016, meta, h, contents)
//line views/readers.qtpl:138
//line views/readers.qtpl:140
qs422016 := string(qb422016.B)
//line views/readers.qtpl:138
//line views/readers.qtpl:140
qt422016.ReleaseByteBuffer(qb422016)
//line views/readers.qtpl:138
//line views/readers.qtpl:140
return qs422016
//line views/readers.qtpl:138
//line views/readers.qtpl:140
}
//line views/readers.qtpl:140
//line views/readers.qtpl:142
func StreamRevision(qw422016 *qt422016.Writer, rq *http.Request, lc *l18n.Localizer, h hyphae.Hypha, contents, revHash string) {
//line views/readers.qtpl:140
//line views/readers.qtpl:142
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<section>
<p>`)
//line views/readers.qtpl:144
//line views/readers.qtpl:146
qw422016.E().S(lc.Get("ui.revision_warning"))
//line views/readers.qtpl:144
//line views/readers.qtpl:146
qw422016.N().S(` <a href="/rev-text/`)
//line views/readers.qtpl:144
//line views/readers.qtpl:146
qw422016.E().S(revHash)
//line views/readers.qtpl:144
//line views/readers.qtpl:146
qw422016.N().S(`/`)
//line views/readers.qtpl:144
//line views/readers.qtpl:146
qw422016.E().S(h.CanonicalName())
//line views/readers.qtpl:144
//line views/readers.qtpl:146
qw422016.N().S(`">`)
//line views/readers.qtpl:144
//line views/readers.qtpl:146
qw422016.E().S(lc.Get("ui.revision_link"))
//line views/readers.qtpl:144
//line views/readers.qtpl:146
qw422016.N().S(`</a></p>
`)
//line views/readers.qtpl:145
//line views/readers.qtpl:147
qw422016.N().S(NaviTitle(h))
//line views/readers.qtpl:145
//line views/readers.qtpl:147
qw422016.N().S(`
`)
//line views/readers.qtpl:146
//line views/readers.qtpl:148
qw422016.N().S(contents)
//line views/readers.qtpl:146
//line views/readers.qtpl:148
qw422016.N().S(`
</section>
</main>
</div>
`)
//line views/readers.qtpl:150
//line views/readers.qtpl:152
streamviewScripts(qw422016)
//line views/readers.qtpl:150
//line views/readers.qtpl:152
qw422016.N().S(`
`)
//line views/readers.qtpl:151
//line views/readers.qtpl:153
}
//line views/readers.qtpl:151
//line views/readers.qtpl:153
func WriteRevision(qq422016 qtio422016.Writer, rq *http.Request, lc *l18n.Localizer, h hyphae.Hypha, contents, revHash string) {
//line views/readers.qtpl:151
//line views/readers.qtpl:153
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/readers.qtpl:151
//line views/readers.qtpl:153
StreamRevision(qw422016, rq, lc, h, contents, revHash)
//line views/readers.qtpl:151
//line views/readers.qtpl:153
qt422016.ReleaseWriter(qw422016)
//line views/readers.qtpl:151
//line views/readers.qtpl:153
}
//line views/readers.qtpl:151
//line views/readers.qtpl:153
func Revision(rq *http.Request, lc *l18n.Localizer, h hyphae.Hypha, contents, revHash string) string {
//line views/readers.qtpl:151
//line views/readers.qtpl:153
qb422016 := qt422016.AcquireByteBuffer()
//line views/readers.qtpl:151
//line views/readers.qtpl:153
WriteRevision(qb422016, rq, lc, h, contents, revHash)
//line views/readers.qtpl:151
//line views/readers.qtpl:153
qs422016 := string(qb422016.B)
//line views/readers.qtpl:151
//line views/readers.qtpl:153
qt422016.ReleaseByteBuffer(qb422016)
//line views/readers.qtpl:151
//line views/readers.qtpl:153
return qs422016
//line views/readers.qtpl:151
//line views/readers.qtpl:153
}
//line views/readers.qtpl:153
//line views/readers.qtpl:155
func streamviewScripts(qw422016 *qt422016.Writer) {
//line views/readers.qtpl:153
//line views/readers.qtpl:155
qw422016.N().S(`
`)
//line views/readers.qtpl:154
//line views/readers.qtpl:156
for _, scriptPath := range cfg.ViewScripts {
//line views/readers.qtpl:154
//line views/readers.qtpl:156
qw422016.N().S(`
<script src="`)
//line views/readers.qtpl:155
//line views/readers.qtpl:157
qw422016.E().S(scriptPath)
//line views/readers.qtpl:155
//line views/readers.qtpl:157
qw422016.N().S(`"></script>
`)
//line views/readers.qtpl:156
//line views/readers.qtpl:158
}
//line views/readers.qtpl:156
//line views/readers.qtpl:158
qw422016.N().S(`
`)
//line views/readers.qtpl:157
//line views/readers.qtpl:159
}
//line views/readers.qtpl:157
//line views/readers.qtpl:159
func writeviewScripts(qq422016 qtio422016.Writer) {
//line views/readers.qtpl:157
//line views/readers.qtpl:159
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/readers.qtpl:157
//line views/readers.qtpl:159
streamviewScripts(qw422016)
//line views/readers.qtpl:157
//line views/readers.qtpl:159
qt422016.ReleaseWriter(qw422016)
//line views/readers.qtpl:157
//line views/readers.qtpl:159
}
//line views/readers.qtpl:157
//line views/readers.qtpl:159
func viewScripts() string {
//line views/readers.qtpl:157
//line views/readers.qtpl:159
qb422016 := qt422016.AcquireByteBuffer()
//line views/readers.qtpl:157
//line views/readers.qtpl:159
writeviewScripts(qb422016)
//line views/readers.qtpl:157
//line views/readers.qtpl:159
qs422016 := string(qb422016.B)
//line views/readers.qtpl:157
//line views/readers.qtpl:159
qt422016.ReleaseByteBuffer(qb422016)
//line views/readers.qtpl:157
//line views/readers.qtpl:159
return qs422016
//line views/readers.qtpl:157
//line views/readers.qtpl:159
}

View File

@ -1,180 +0,0 @@
{% import "fmt" %}
{% import "path/filepath" %}
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
{% import "github.com/bouncepaw/mycorrhiza/user" %}
{% import "github.com/bouncepaw/mycorrhiza/util" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% func Base(title, body string, lc *l18n.Localizer, u *user.User, headElements ...string) %}
<!doctype html>
<html lang="{%s lc.Locale %}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{%s title %}</title>
<link rel="shortcut icon" href="/static/favicon.ico">
<link rel="stylesheet" href="/static/style.css">
<script src="/static/shortcuts.js"></script>
{% for _, el := range headElements %}{%s= el %}{% endfor %}
</head>
<body>
<header>
<nav class="main-width top-bar">
<ul class="top-bar__wrapper">
<li class="top-bar__section top-bar__section_home">
<div class="top-bar__home-link-wrapper">
<a class="top-bar__home-link" href="/">{%s cfg.WikiName %}</a>
</div>
</li>
<li class="top-bar__section top-bar__section_search">
<form class="top-bar__search" method="GET" action="/title-search">
<input type="text" name="q" placeholder="{%s lc.Get("ui.title_search") %}" class="top-bar__search-bar">
</form>
</li>
<li class="top-bar__section top-bar__section_auth">
{% if cfg.UseAuth %}
<ul class="top-bar__auth auth-links">
<li class="auth-links__box auth-links__user-box">
{% if u.Group == "anon" %}
<a href="/login" class="auth-links__link auth-links__login-link">{%s lc.Get("ui.login") %}</a>
{% else %}
<a href="/hypha/{%s cfg.UserHypha %}/{%s u.Name %}" class="auth-links__link auth-links__user-link">{%s util.BeautifulName(u.Name) %}</a>
{% endif %}
</li>
{% if cfg.AllowRegistration && u.Group == "anon" %}
<li class="auth-links__box auth-links__register-box">
<a href="/register" class="auth-links__link auth-links__register-link">{%s lc.Get("ui.register") %}</a>
</li>
{% endif %}
</ul>
{% endif %}
</li>
<li class="top-bar__section top-bar__section_highlights">
<ul class="top-bar__highlights">
{%- for _, link := range cfg.HeaderLinks -%}
{% if link.Href != "/" %}
<li class="top-bar__highlight">
<a class="top-bar__highlight-link" href="{%s link.Href %}">{%s link.Display %}</a>
</li>
{% endif %}
{%- endfor -%}
</ul>
</li>
</ul>
</nav>
</header>
{%s= body %}
<template id="dialog-template">
<div class="dialog-backdrop"></div>
<div class="dialog" tabindex="0">
<div class="dialog__header">
<h1 class="dialog__title"></h1>
<button class="dialog__close-button" aria-label="{%s lc.Get("ui.close_dialog") %}"></button>
</div>
<div class="dialog__content"></div>
</div>
</template>
{%= commonScripts() %}
<script src="/static/view.js"></script>
</body>
</html>
{% endfunc %}
{% func TitleSearch(query string, generator func(string) <-chan string, lc *l18n.Localizer) %}
<div class="layout">
<main class="main-width title-search">
<h1>{%s lc.Get("ui.search_results_query", &l18n.Replacements{"query": query})%}</h1>
<p>{%s lc.Get("ui.search_results_desc")%}</p>
<ul class="title-search__results">
{% for hyphaName := range generator(query) %}
<li class="title-search__entry">
<a class="title-search__link wikilink" href="/hypha/{%s hyphaName %}">{%s util.BeautifulName(hyphaName) %}</a>
</li>
{% endfor %}
</main>
</div>
{% endfunc %}
{% func Backlinks(hyphaName string, generator func(string) <-chan string, lc *l18n.Localizer) %}
<div class="layout">
<main class="main-width backlinks">
<h1>{%s= lc.Get(
"ui.backlinks_heading",
&l18n.Replacements{
"hypha_link": fmt.Sprintf(
`<a href="/hypha/%s">%s</a>`,
hyphaName,
util.BeautifulName(hyphaName),
),
},
)%}</h1>
<p>{%s lc.Get("ui.backlinks_desc")%}</p>
<ul class="backlinks__list">
{% for hyphaName := range generator(hyphaName) %}
<li class="backlinks__entry">
<a class="backlinks__link wikilink" href="/hypha/{%s hyphaName %}">{%s util.BeautifulName(hyphaName) %}</a>
</li>
{% endfor %}
</ul>
</main>
</div>
{% endfunc %}
{% func Help(content, lang string, lc *l18n.Localizer) %}
<div class="layout">
<main class="main-width help">
<article>
{%s= content %}
</article>
</main>
{%s= helpTopics(lang, lc) %}
</div>
{% endfunc %}
{% func HelpEmptyError(lc *l18n.Localizer) %}
<h1>{%s lc.Get("help.empty_error_title") %}</h1>
<p>{%s lc.Get("help.empty_error_line_1") %}</p>
<p>{%s lc.Get("help.empty_error_line_2a") %} <a class="wikilink wikilink_external wikilink_https" href="https://github.com/bouncepaw/mycorrhiza">{%s lc.Get("help.empty_error_link") %}</a> {%s lc.Get("help.empty_error_line_2b") %}</p>
{% endfunc %}
{% func HyphaList(lc *l18n.Localizer) %}
<div class="layout">
<main class="main-width">
<h1>{%s lc.Get("ui.list_heading") %}</h1>
<p>{%s lc.GetPlural("ui.list_desc", hyphae.Count()) %}</p>
<ul class="hypha-list">
{% code
hyphaNames := make(chan string)
sortedHypha := hyphae.PathographicSort(hyphaNames)
for hypha := range hyphae.YieldExistingHyphae() {
hyphaNames <- hypha.CanonicalName()
}
close(hyphaNames)
%}
{% for hyphaName := range sortedHypha %}
{% code h := hyphae.ByName(hyphaName) %}
<li class="hypha-list__entry">
<a class="hypha-list__link" href="/hypha/{%s h.CanonicalName() %}">
{%s util.BeautifulName(h.CanonicalName()) %}
</a>
{% switch h := h.(type) %}
{% case *hyphae.MediaHypha %}
<span class="hypha-list__amnt-type">
{%s filepath.Ext(h.MediaFilePath())[1:] %}
</span>
{% endswitch %}
</li>
{% endfor %}
</ul>
</main>
</div>
{% endfunc %}
{% func commonScripts() %}
{% for _, scriptPath := range cfg.CommonScripts %}
<script src="{%s scriptPath %}"></script>
{% endfor %}
{% endfunc %}

View File

@ -1,667 +0,0 @@
// Code generated by qtc from "stuff.qtpl". DO NOT EDIT.
// See https://github.com/valyala/quicktemplate for details.
//line views/stuff.qtpl:1
package views
//line views/stuff.qtpl:1
import "fmt"
//line views/stuff.qtpl:2
import "path/filepath"
//line views/stuff.qtpl:4
import "github.com/bouncepaw/mycorrhiza/cfg"
//line views/stuff.qtpl:5
import "github.com/bouncepaw/mycorrhiza/hyphae"
//line views/stuff.qtpl:6
import "github.com/bouncepaw/mycorrhiza/user"
//line views/stuff.qtpl:7
import "github.com/bouncepaw/mycorrhiza/util"
//line views/stuff.qtpl:8
import "github.com/bouncepaw/mycorrhiza/l18n"
//line views/stuff.qtpl:10
import (
qtio422016 "io"
qt422016 "github.com/valyala/quicktemplate"
)
//line views/stuff.qtpl:10
var (
_ = qtio422016.Copy
_ = qt422016.AcquireByteBuffer
)
//line views/stuff.qtpl:10
func StreamBase(qw422016 *qt422016.Writer, title, body string, lc *l18n.Localizer, u *user.User, headElements ...string) {
//line views/stuff.qtpl:10
qw422016.N().S(`
<!doctype html>
<html lang="`)
//line views/stuff.qtpl:12
qw422016.E().S(lc.Locale)
//line views/stuff.qtpl:12
qw422016.N().S(`">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>`)
//line views/stuff.qtpl:16
qw422016.E().S(title)
//line views/stuff.qtpl:16
qw422016.N().S(`</title>
<link rel="shortcut icon" href="/static/favicon.ico">
<link rel="stylesheet" href="/static/style.css">
<script src="/static/shortcuts.js"></script>
`)
//line views/stuff.qtpl:20
for _, el := range headElements {
//line views/stuff.qtpl:20
qw422016.N().S(el)
//line views/stuff.qtpl:20
}
//line views/stuff.qtpl:20
qw422016.N().S(`
</head>
<body>
<header>
<nav class="main-width top-bar">
<ul class="top-bar__wrapper">
<li class="top-bar__section top-bar__section_home">
<div class="top-bar__home-link-wrapper">
<a class="top-bar__home-link" href="/">`)
//line views/stuff.qtpl:28
qw422016.E().S(cfg.WikiName)
//line views/stuff.qtpl:28
qw422016.N().S(`</a>
</div>
</li>
<li class="top-bar__section top-bar__section_search">
<form class="top-bar__search" method="GET" action="/title-search">
<input type="text" name="q" placeholder="`)
//line views/stuff.qtpl:33
qw422016.E().S(lc.Get("ui.title_search"))
//line views/stuff.qtpl:33
qw422016.N().S(`" class="top-bar__search-bar">
</form>
</li>
<li class="top-bar__section top-bar__section_auth">
`)
//line views/stuff.qtpl:37
if cfg.UseAuth {
//line views/stuff.qtpl:37
qw422016.N().S(`
<ul class="top-bar__auth auth-links">
<li class="auth-links__box auth-links__user-box">
`)
//line views/stuff.qtpl:40
if u.Group == "anon" {
//line views/stuff.qtpl:40
qw422016.N().S(`
<a href="/login" class="auth-links__link auth-links__login-link">`)
//line views/stuff.qtpl:41
qw422016.E().S(lc.Get("ui.login"))
//line views/stuff.qtpl:41
qw422016.N().S(`</a>
`)
//line views/stuff.qtpl:42
} else {
//line views/stuff.qtpl:42
qw422016.N().S(`
<a href="/hypha/`)
//line views/stuff.qtpl:43
qw422016.E().S(cfg.UserHypha)
//line views/stuff.qtpl:43
qw422016.N().S(`/`)
//line views/stuff.qtpl:43
qw422016.E().S(u.Name)
//line views/stuff.qtpl:43
qw422016.N().S(`" class="auth-links__link auth-links__user-link">`)
//line views/stuff.qtpl:43
qw422016.E().S(util.BeautifulName(u.Name))
//line views/stuff.qtpl:43
qw422016.N().S(`</a>
`)
//line views/stuff.qtpl:44
}
//line views/stuff.qtpl:44
qw422016.N().S(`
</li>
`)
//line views/stuff.qtpl:46
if cfg.AllowRegistration && u.Group == "anon" {
//line views/stuff.qtpl:46
qw422016.N().S(`
<li class="auth-links__box auth-links__register-box">
<a href="/register" class="auth-links__link auth-links__register-link">`)
//line views/stuff.qtpl:48
qw422016.E().S(lc.Get("ui.register"))
//line views/stuff.qtpl:48
qw422016.N().S(`</a>
</li>
`)
//line views/stuff.qtpl:50
}
//line views/stuff.qtpl:50
qw422016.N().S(`
</ul>
`)
//line views/stuff.qtpl:52
}
//line views/stuff.qtpl:52
qw422016.N().S(`
</li>
<li class="top-bar__section top-bar__section_highlights">
<ul class="top-bar__highlights">
`)
//line views/stuff.qtpl:56
for _, link := range cfg.HeaderLinks {
//line views/stuff.qtpl:56
qw422016.N().S(` `)
//line views/stuff.qtpl:57
if link.Href != "/" {
//line views/stuff.qtpl:57
qw422016.N().S(`
<li class="top-bar__highlight">
<a class="top-bar__highlight-link" href="`)
//line views/stuff.qtpl:59
qw422016.E().S(link.Href)
//line views/stuff.qtpl:59
qw422016.N().S(`">`)
//line views/stuff.qtpl:59
qw422016.E().S(link.Display)
//line views/stuff.qtpl:59
qw422016.N().S(`</a>
</li>
`)
//line views/stuff.qtpl:61
}
//line views/stuff.qtpl:61
qw422016.N().S(`
`)
//line views/stuff.qtpl:62
}
//line views/stuff.qtpl:62
qw422016.N().S(` </ul>
</li>
</ul>
</nav>
</header>
`)
//line views/stuff.qtpl:68
qw422016.N().S(body)
//line views/stuff.qtpl:68
qw422016.N().S(`
<template id="dialog-template">
<div class="dialog-backdrop"></div>
<div class="dialog" tabindex="0">
<div class="dialog__header">
<h1 class="dialog__title"></h1>
<button class="dialog__close-button" aria-label="`)
//line views/stuff.qtpl:74
qw422016.E().S(lc.Get("ui.close_dialog"))
//line views/stuff.qtpl:74
qw422016.N().S(`"></button>
</div>
<div class="dialog__content"></div>
</div>
</template>
`)
//line views/stuff.qtpl:80
streamcommonScripts(qw422016)
//line views/stuff.qtpl:80
qw422016.N().S(`
<script src="/static/view.js"></script>
</body>
</html>
`)
//line views/stuff.qtpl:84
}
//line views/stuff.qtpl:84
func WriteBase(qq422016 qtio422016.Writer, title, body string, lc *l18n.Localizer, u *user.User, headElements ...string) {
//line views/stuff.qtpl:84
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:84
StreamBase(qw422016, title, body, lc, u, headElements...)
//line views/stuff.qtpl:84
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:84
}
//line views/stuff.qtpl:84
func Base(title, body string, lc *l18n.Localizer, u *user.User, headElements ...string) string {
//line views/stuff.qtpl:84
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:84
WriteBase(qb422016, title, body, lc, u, headElements...)
//line views/stuff.qtpl:84
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:84
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:84
return qs422016
//line views/stuff.qtpl:84
}
//line views/stuff.qtpl:86
func StreamTitleSearch(qw422016 *qt422016.Writer, query string, generator func(string) <-chan string, lc *l18n.Localizer) {
//line views/stuff.qtpl:86
qw422016.N().S(`
<div class="layout">
<main class="main-width title-search">
<h1>`)
//line views/stuff.qtpl:89
qw422016.E().S(lc.Get("ui.search_results_query", &l18n.Replacements{"query": query}))
//line views/stuff.qtpl:89
qw422016.N().S(`</h1>
<p>`)
//line views/stuff.qtpl:90
qw422016.E().S(lc.Get("ui.search_results_desc"))
//line views/stuff.qtpl:90
qw422016.N().S(`</p>
<ul class="title-search__results">
`)
//line views/stuff.qtpl:92
for hyphaName := range generator(query) {
//line views/stuff.qtpl:92
qw422016.N().S(`
<li class="title-search__entry">
<a class="title-search__link wikilink" href="/hypha/`)
//line views/stuff.qtpl:94
qw422016.E().S(hyphaName)
//line views/stuff.qtpl:94
qw422016.N().S(`">`)
//line views/stuff.qtpl:94
qw422016.E().S(util.BeautifulName(hyphaName))
//line views/stuff.qtpl:94
qw422016.N().S(`</a>
</li>
`)
//line views/stuff.qtpl:96
}
//line views/stuff.qtpl:96
qw422016.N().S(`
</main>
</div>
`)
//line views/stuff.qtpl:99
}
//line views/stuff.qtpl:99
func WriteTitleSearch(qq422016 qtio422016.Writer, query string, generator func(string) <-chan string, lc *l18n.Localizer) {
//line views/stuff.qtpl:99
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:99
StreamTitleSearch(qw422016, query, generator, lc)
//line views/stuff.qtpl:99
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:99
}
//line views/stuff.qtpl:99
func TitleSearch(query string, generator func(string) <-chan string, lc *l18n.Localizer) string {
//line views/stuff.qtpl:99
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:99
WriteTitleSearch(qb422016, query, generator, lc)
//line views/stuff.qtpl:99
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:99
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:99
return qs422016
//line views/stuff.qtpl:99
}
//line views/stuff.qtpl:101
func StreamBacklinks(qw422016 *qt422016.Writer, hyphaName string, generator func(string) <-chan string, lc *l18n.Localizer) {
//line views/stuff.qtpl:101
qw422016.N().S(`
<div class="layout">
<main class="main-width backlinks">
<h1>`)
//line views/stuff.qtpl:104
qw422016.N().S(lc.Get(
"ui.backlinks_heading",
&l18n.Replacements{
"hypha_link": fmt.Sprintf(
`<a href="/hypha/%s">%s</a>`,
hyphaName,
util.BeautifulName(hyphaName),
),
},
))
//line views/stuff.qtpl:113
qw422016.N().S(`</h1>
<p>`)
//line views/stuff.qtpl:114
qw422016.E().S(lc.Get("ui.backlinks_desc"))
//line views/stuff.qtpl:114
qw422016.N().S(`</p>
<ul class="backlinks__list">
`)
//line views/stuff.qtpl:116
for hyphaName := range generator(hyphaName) {
//line views/stuff.qtpl:116
qw422016.N().S(`
<li class="backlinks__entry">
<a class="backlinks__link wikilink" href="/hypha/`)
//line views/stuff.qtpl:118
qw422016.E().S(hyphaName)
//line views/stuff.qtpl:118
qw422016.N().S(`">`)
//line views/stuff.qtpl:118
qw422016.E().S(util.BeautifulName(hyphaName))
//line views/stuff.qtpl:118
qw422016.N().S(`</a>
</li>
`)
//line views/stuff.qtpl:120
}
//line views/stuff.qtpl:120
qw422016.N().S(`
</ul>
</main>
</div>
`)
//line views/stuff.qtpl:124
}
//line views/stuff.qtpl:124
func WriteBacklinks(qq422016 qtio422016.Writer, hyphaName string, generator func(string) <-chan string, lc *l18n.Localizer) {
//line views/stuff.qtpl:124
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:124
StreamBacklinks(qw422016, hyphaName, generator, lc)
//line views/stuff.qtpl:124
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:124
}
//line views/stuff.qtpl:124
func Backlinks(hyphaName string, generator func(string) <-chan string, lc *l18n.Localizer) string {
//line views/stuff.qtpl:124
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:124
WriteBacklinks(qb422016, hyphaName, generator, lc)
//line views/stuff.qtpl:124
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:124
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:124
return qs422016
//line views/stuff.qtpl:124
}
//line views/stuff.qtpl:126
func StreamHelp(qw422016 *qt422016.Writer, content, lang string, lc *l18n.Localizer) {
//line views/stuff.qtpl:126
qw422016.N().S(`
<div class="layout">
<main class="main-width help">
<article>
`)
//line views/stuff.qtpl:130
qw422016.N().S(content)
//line views/stuff.qtpl:130
qw422016.N().S(`
</article>
</main>
`)
//line views/stuff.qtpl:133
qw422016.N().S(helpTopics(lang, lc))
//line views/stuff.qtpl:133
qw422016.N().S(`
</div>
`)
//line views/stuff.qtpl:135
}
//line views/stuff.qtpl:135
func WriteHelp(qq422016 qtio422016.Writer, content, lang string, lc *l18n.Localizer) {
//line views/stuff.qtpl:135
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:135
StreamHelp(qw422016, content, lang, lc)
//line views/stuff.qtpl:135
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:135
}
//line views/stuff.qtpl:135
func Help(content, lang string, lc *l18n.Localizer) string {
//line views/stuff.qtpl:135
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:135
WriteHelp(qb422016, content, lang, lc)
//line views/stuff.qtpl:135
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:135
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:135
return qs422016
//line views/stuff.qtpl:135
}
//line views/stuff.qtpl:137
func StreamHelpEmptyError(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
//line views/stuff.qtpl:137
qw422016.N().S(`
<h1>`)
//line views/stuff.qtpl:138
qw422016.E().S(lc.Get("help.empty_error_title"))
//line views/stuff.qtpl:138
qw422016.N().S(`</h1>
<p>`)
//line views/stuff.qtpl:139
qw422016.E().S(lc.Get("help.empty_error_line_1"))
//line views/stuff.qtpl:139
qw422016.N().S(`</p>
<p>`)
//line views/stuff.qtpl:140
qw422016.E().S(lc.Get("help.empty_error_line_2a"))
//line views/stuff.qtpl:140
qw422016.N().S(` <a class="wikilink wikilink_external wikilink_https" href="https://github.com/bouncepaw/mycorrhiza">`)
//line views/stuff.qtpl:140
qw422016.E().S(lc.Get("help.empty_error_link"))
//line views/stuff.qtpl:140
qw422016.N().S(`</a> `)
//line views/stuff.qtpl:140
qw422016.E().S(lc.Get("help.empty_error_line_2b"))
//line views/stuff.qtpl:140
qw422016.N().S(`</p>
`)
//line views/stuff.qtpl:141
}
//line views/stuff.qtpl:141
func WriteHelpEmptyError(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
//line views/stuff.qtpl:141
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:141
StreamHelpEmptyError(qw422016, lc)
//line views/stuff.qtpl:141
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:141
}
//line views/stuff.qtpl:141
func HelpEmptyError(lc *l18n.Localizer) string {
//line views/stuff.qtpl:141
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:141
WriteHelpEmptyError(qb422016, lc)
//line views/stuff.qtpl:141
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:141
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:141
return qs422016
//line views/stuff.qtpl:141
}
//line views/stuff.qtpl:143
func StreamHyphaList(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
//line views/stuff.qtpl:143
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<h1>`)
//line views/stuff.qtpl:146
qw422016.E().S(lc.Get("ui.list_heading"))
//line views/stuff.qtpl:146
qw422016.N().S(`</h1>
<p>`)
//line views/stuff.qtpl:147
qw422016.E().S(lc.GetPlural("ui.list_desc", hyphae.Count()))
//line views/stuff.qtpl:147
qw422016.N().S(`</p>
<ul class="hypha-list">
`)
//line views/stuff.qtpl:150
hyphaNames := make(chan string)
sortedHypha := hyphae.PathographicSort(hyphaNames)
for hypha := range hyphae.YieldExistingHyphae() {
hyphaNames <- hypha.CanonicalName()
}
close(hyphaNames)
//line views/stuff.qtpl:156
qw422016.N().S(`
`)
//line views/stuff.qtpl:157
for hyphaName := range sortedHypha {
//line views/stuff.qtpl:157
qw422016.N().S(`
`)
//line views/stuff.qtpl:158
h := hyphae.ByName(hyphaName)
//line views/stuff.qtpl:158
qw422016.N().S(`
<li class="hypha-list__entry">
<a class="hypha-list__link" href="/hypha/`)
//line views/stuff.qtpl:160
qw422016.E().S(h.CanonicalName())
//line views/stuff.qtpl:160
qw422016.N().S(`">
`)
//line views/stuff.qtpl:161
qw422016.E().S(util.BeautifulName(h.CanonicalName()))
//line views/stuff.qtpl:161
qw422016.N().S(`
</a>
`)
//line views/stuff.qtpl:163
switch h := h.(type) {
//line views/stuff.qtpl:164
case *hyphae.MediaHypha:
//line views/stuff.qtpl:164
qw422016.N().S(`
<span class="hypha-list__amnt-type">
`)
//line views/stuff.qtpl:166
qw422016.E().S(filepath.Ext(h.MediaFilePath())[1:])
//line views/stuff.qtpl:166
qw422016.N().S(`
</span>
`)
//line views/stuff.qtpl:168
}
//line views/stuff.qtpl:168
qw422016.N().S(`
</li>
`)
//line views/stuff.qtpl:170
}
//line views/stuff.qtpl:170
qw422016.N().S(`
</ul>
</main>
</div>
`)
//line views/stuff.qtpl:174
}
//line views/stuff.qtpl:174
func WriteHyphaList(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
//line views/stuff.qtpl:174
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:174
StreamHyphaList(qw422016, lc)
//line views/stuff.qtpl:174
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:174
}
//line views/stuff.qtpl:174
func HyphaList(lc *l18n.Localizer) string {
//line views/stuff.qtpl:174
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:174
WriteHyphaList(qb422016, lc)
//line views/stuff.qtpl:174
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:174
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:174
return qs422016
//line views/stuff.qtpl:174
}
//line views/stuff.qtpl:176
func streamcommonScripts(qw422016 *qt422016.Writer) {
//line views/stuff.qtpl:176
qw422016.N().S(`
`)
//line views/stuff.qtpl:177
for _, scriptPath := range cfg.CommonScripts {
//line views/stuff.qtpl:177
qw422016.N().S(`
<script src="`)
//line views/stuff.qtpl:178
qw422016.E().S(scriptPath)
//line views/stuff.qtpl:178
qw422016.N().S(`"></script>
`)
//line views/stuff.qtpl:179
}
//line views/stuff.qtpl:179
qw422016.N().S(`
`)
//line views/stuff.qtpl:180
}
//line views/stuff.qtpl:180
func writecommonScripts(qq422016 qtio422016.Writer) {
//line views/stuff.qtpl:180
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/stuff.qtpl:180
streamcommonScripts(qw422016)
//line views/stuff.qtpl:180
qt422016.ReleaseWriter(qw422016)
//line views/stuff.qtpl:180
}
//line views/stuff.qtpl:180
func commonScripts() string {
//line views/stuff.qtpl:180
qb422016 := qt422016.AcquireByteBuffer()
//line views/stuff.qtpl:180
writecommonScripts(qb422016)
//line views/stuff.qtpl:180
qs422016 := string(qb422016.B)
//line views/stuff.qtpl:180
qt422016.ReleaseByteBuffer(qb422016)
//line views/stuff.qtpl:180
return qs422016
//line views/stuff.qtpl:180
}

View File

@ -1,64 +0,0 @@
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% import "github.com/bouncepaw/mycorrhiza/user" %}
{% import "sort" %}
{% code
var hyphaListL10n = map[string]l10nEntry{
"heading": en("List of users").ru("Список пользователей"),
"administrators": en("Administrators").ru("Администраторы"),
"moderators": en("Moderators").ru("Модераторы"),
"editors": en("Editors").ru("Редакторы"),
}
%}
{% func UserList(lc *l18n.Localizer) %}
<div class="layout">
<main class="main-width user-list">
{% code
var get = func(key string) string {
return hyphaListL10n[key].get(lc.Locale)
}
var (
admins = make([]string, 0)
moderators = make([]string, 0)
editors = make([]string, 0)
)
for u := range user.YieldUsers() {
switch u.Group {
// What if we place the users into sorted slices?
case "admin":
admins = append(admins, u.Name)
case "moderator":
moderators = append(moderators, u.Name)
case "editor", "trusted":
editors = append(editors, u.Name)
}
}
sort.Strings(admins)
sort.Strings(moderators)
sort.Strings(editors)
%}
<h1>{%s get("heading") %}</h1>
<section>
<h2>{%s get("administrators") %}</h2>
<ol>{% for _, name := range admins %}
<li><a href="/hypha/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
{% endfor %}</ol>
</section>
<section>
<h2>{%s get("moderators") %}</h2>
<ol>{% for _, name := range moderators %}
<li><a href="/hypha/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
{% endfor %}</ol>
</section>
<section>
<h2>{%s get("editors") %}</h2>
<ol>{% for _, name := range editors %}
<li><a href="/hypha/{%s cfg.UserHypha %}/{%s name %}">{%s name %}</a></li>
{% endfor %}</ol>
</section>
</main>
</div>
{% endfunc %}

View File

@ -1,199 +0,0 @@
// Code generated by qtc from "user_list.qtpl". DO NOT EDIT.
// See https://github.com/valyala/quicktemplate for details.
//line views/user_list.qtpl:1
package views
//line views/user_list.qtpl:1
import "github.com/bouncepaw/mycorrhiza/cfg"
//line views/user_list.qtpl:2
import "github.com/bouncepaw/mycorrhiza/l18n"
//line views/user_list.qtpl:3
import "github.com/bouncepaw/mycorrhiza/user"
//line views/user_list.qtpl:4
import "sort"
//line views/user_list.qtpl:6
import (
qtio422016 "io"
qt422016 "github.com/valyala/quicktemplate"
)
//line views/user_list.qtpl:6
var (
_ = qtio422016.Copy
_ = qt422016.AcquireByteBuffer
)
//line views/user_list.qtpl:7
var hyphaListL10n = map[string]l10nEntry{
"heading": en("List of users").ru("Список пользователей"),
"administrators": en("Administrators").ru("Администраторы"),
"moderators": en("Moderators").ru("Модераторы"),
"editors": en("Editors").ru("Редакторы"),
}
//line views/user_list.qtpl:15
func StreamUserList(qw422016 *qt422016.Writer, lc *l18n.Localizer) {
//line views/user_list.qtpl:15
qw422016.N().S(`
<div class="layout">
<main class="main-width user-list">
`)
//line views/user_list.qtpl:19
var get = func(key string) string {
return hyphaListL10n[key].get(lc.Locale)
}
var (
admins = make([]string, 0)
moderators = make([]string, 0)
editors = make([]string, 0)
)
for u := range user.YieldUsers() {
switch u.Group {
// What if we place the users into sorted slices?
case "admin":
admins = append(admins, u.Name)
case "moderator":
moderators = append(moderators, u.Name)
case "editor", "trusted":
editors = append(editors, u.Name)
}
}
sort.Strings(admins)
sort.Strings(moderators)
sort.Strings(editors)
//line views/user_list.qtpl:42
qw422016.N().S(`
<h1>`)
//line views/user_list.qtpl:43
qw422016.E().S(get("heading"))
//line views/user_list.qtpl:43
qw422016.N().S(`</h1>
<section>
<h2>`)
//line views/user_list.qtpl:45
qw422016.E().S(get("administrators"))
//line views/user_list.qtpl:45
qw422016.N().S(`</h2>
<ol>`)
//line views/user_list.qtpl:46
for _, name := range admins {
//line views/user_list.qtpl:46
qw422016.N().S(`
<li><a href="/hypha/`)
//line views/user_list.qtpl:47
qw422016.E().S(cfg.UserHypha)
//line views/user_list.qtpl:47
qw422016.N().S(`/`)
//line views/user_list.qtpl:47
qw422016.E().S(name)
//line views/user_list.qtpl:47
qw422016.N().S(`">`)
//line views/user_list.qtpl:47
qw422016.E().S(name)
//line views/user_list.qtpl:47
qw422016.N().S(`</a></li>
`)
//line views/user_list.qtpl:48
}
//line views/user_list.qtpl:48
qw422016.N().S(`</ol>
</section>
<section>
<h2>`)
//line views/user_list.qtpl:51
qw422016.E().S(get("moderators"))
//line views/user_list.qtpl:51
qw422016.N().S(`</h2>
<ol>`)
//line views/user_list.qtpl:52
for _, name := range moderators {
//line views/user_list.qtpl:52
qw422016.N().S(`
<li><a href="/hypha/`)
//line views/user_list.qtpl:53
qw422016.E().S(cfg.UserHypha)
//line views/user_list.qtpl:53
qw422016.N().S(`/`)
//line views/user_list.qtpl:53
qw422016.E().S(name)
//line views/user_list.qtpl:53
qw422016.N().S(`">`)
//line views/user_list.qtpl:53
qw422016.E().S(name)
//line views/user_list.qtpl:53
qw422016.N().S(`</a></li>
`)
//line views/user_list.qtpl:54
}
//line views/user_list.qtpl:54
qw422016.N().S(`</ol>
</section>
<section>
<h2>`)
//line views/user_list.qtpl:57
qw422016.E().S(get("editors"))
//line views/user_list.qtpl:57
qw422016.N().S(`</h2>
<ol>`)
//line views/user_list.qtpl:58
for _, name := range editors {
//line views/user_list.qtpl:58
qw422016.N().S(`
<li><a href="/hypha/`)
//line views/user_list.qtpl:59
qw422016.E().S(cfg.UserHypha)
//line views/user_list.qtpl:59
qw422016.N().S(`/`)
//line views/user_list.qtpl:59
qw422016.E().S(name)
//line views/user_list.qtpl:59
qw422016.N().S(`">`)
//line views/user_list.qtpl:59
qw422016.E().S(name)
//line views/user_list.qtpl:59
qw422016.N().S(`</a></li>
`)
//line views/user_list.qtpl:60
}
//line views/user_list.qtpl:60
qw422016.N().S(`</ol>
</section>
</main>
</div>
`)
//line views/user_list.qtpl:64
}
//line views/user_list.qtpl:64
func WriteUserList(qq422016 qtio422016.Writer, lc *l18n.Localizer) {
//line views/user_list.qtpl:64
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/user_list.qtpl:64
StreamUserList(qw422016, lc)
//line views/user_list.qtpl:64
qt422016.ReleaseWriter(qw422016)
//line views/user_list.qtpl:64
}
//line views/user_list.qtpl:64
func UserList(lc *l18n.Localizer) string {
//line views/user_list.qtpl:64
qb422016 := qt422016.AcquireByteBuffer()
//line views/user_list.qtpl:64
WriteUserList(qb422016, lc)
//line views/user_list.qtpl:64
qs422016 := string(qb422016.B)
//line views/user_list.qtpl:64
qt422016.ReleaseByteBuffer(qb422016)
//line views/user_list.qtpl:64
return qs422016
//line views/user_list.qtpl:64
}

82
viewutil/base.html Normal file
View File

@ -0,0 +1,82 @@
{{define "page"}}
<!doctype html>
<html lang="{{.Meta.Locale}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{block "title" .}}{{end}}</title>
<link rel="shortcut icon" href="/static/favicon.ico">
<link rel="stylesheet" href="/static/style.css">
<script src="/static/shortcuts.js"></script>
{{range .HeadElements}}{{.}}{{end}}
</head>
<body>
<header>
<nav class="main-width top-bar">
<ul class="top-bar__wrapper">
<li class="top-bar__section top-bar__section_home">
<div class="top-bar__home-link-wrapper">
<a class="top-bar__home-link" href="/">{{block "wiki name" .}}{{end}}</a>
</div>
</li>
<li class="top-bar__section top-bar__section_search">
<form class="top-bar__search" method="GET" action="/title-search">
<input type="text" name="q" class="top-bar__search-bar"
placeholder="{{block `search by title` .}}Search by title{{end}}">
</form>
</li>
<li class="top-bar__section top-bar__section_auth">
{{block "auth" .}}
<ul class="top-bar__auth auth-links">
<li class="auth-links__box auth-links__user-box">
{{if .Meta.U.Group | eq "anon" }}
<a href="/login" class="auth-links__link auth-links__login-link">
{{block "login" .}}Login{{end}}
</a>
{{else}}
<a href="/hypha/{%s cfg.UserHypha %}/{%s u.Name %}" class="auth-links__link auth-links__user-link">
{{beautifulName .Meta.U.Name}}
</a>
{{end}}
</li>
{{block "registration" .}}
{{if .Meta.U.Group | eq "anon"}}
<li class="auth-links__box auth-links__register-box">
<a href="/register" class="auth-links__link auth-links__register-link">
{{block "register" .}}Register{{end}}
</a>
</li>
{{end}}
{{end}}
</ul>
{{end}}
</li>
<li class="top-bar__section top-bar__section_highlights">
<ul class="top-bar__highlights">
{{range .HeaderLinks}}
<li class="top-bar__highlight">
<a class="top-bar__highlight-link" href="{{.Href}}">{{.Display}}</a>
</li>
{{end}}
</ul>
</li>
</ul>
</nav>
</header>
{{block "body" .}}{{end}}
<template id="dialog-template">
<div class="dialog-backdrop"></div>
<div class="dialog" tabindex="0">
<div class="dialog__header">
<h1 class="dialog__title"></h1>
<button class="dialog__close-button" aria-label="{{block `close this dialog` .}}{{end}}"></button>
</div>
<div class="dialog__content"></div>
</div>
</template>
{{range .CommonScripts}}{{.}}{{end}}
<script src="/static/view.js"></script>
</body>
</html>
{{end}}

33
viewutil/chain.go Normal file
View File

@ -0,0 +1,33 @@
package viewutil
import "text/template"
// Chain represents a chain of different language versions of the same template.
type Chain struct {
en *template.Template
ru *template.Template
}
// En returns a new Chain. This is the only constructor of the type, so every view is forced to have an English representation.
func En(en *template.Template) Chain {
return Chain{
en: en,
}
}
// Ru adds a Russian translation to the Chain.
func (c Chain) Ru(ru *template.Template) Chain {
c.ru = ru
return c
}
// Get returns an appropriate language representation for the given locale in meta.
func (c Chain) Get(meta Meta) *template.Template {
switch meta.Locale() {
case "en":
return c.en
case "ru":
return c.ru
}
panic("unknown language " + meta.Locale())
}

27
viewutil/err.go Normal file
View File

@ -0,0 +1,27 @@
package viewutil
import (
"fmt"
"mime"
"net/http"
)
// HttpErr is used by many handlers to signal errors in a compact way.
// TODO: get rid of this abomination
func HttpErr(meta Meta, status int, name, errMsg string) {
meta.W.(http.ResponseWriter).Header().Set("Content-Type", mime.TypeByExtension(".html"))
meta.W.(http.ResponseWriter).WriteHeader(status)
fmt.Fprint(
meta.W,
Base(
meta,
"Error",
fmt.Sprintf(
`<main class="main-width"><p>%s. <a href="/hypha/%s">%s<a></p></main>`,
errMsg,
name,
meta.Lc.Get("ui.error_go_back"),
),
),
)
}

28
viewutil/meta.go Normal file
View File

@ -0,0 +1,28 @@
package viewutil
import (
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/user"
"io"
"net/http"
)
// Meta is a bundle of common stuffs used by views, templates.
type Meta struct {
Lc *l18n.Localizer
U *user.User
W io.Writer
}
// MetaFrom makes a Meta from the given data. You are meant to further modify it.
func MetaFrom(w http.ResponseWriter, rq *http.Request) Meta {
return Meta{
Lc: l18n.FromRequest(rq),
U: user.FromRequest(rq),
W: w,
}
}
func (m Meta) Locale() string {
return m.Lc.Locale
}

96
viewutil/viewutil.go Normal file
View File

@ -0,0 +1,96 @@
// Package viewutil provides utilities and common templates for views across all packages.
package viewutil
import (
"embed"
"fmt"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/util"
"io/fs"
"log"
"strings"
"text/template" // TODO: save the world
)
var (
//go:embed *.html
fsys embed.FS
BaseEn *template.Template
BaseRu *template.Template
m = template.Must
)
const ruText = `
{{define "search by title"}}Поиск по названию{{end}}
{{define "close this dialog"}}Закрыть этот диалог{{end}}
{{define "login"}}Войти{{end}}
{{define "Register"}}Регистрация{{end}}
`
func Init() {
dataText := fmt.Sprintf(`
{{define "wiki name"}}%s{{end}}
`, cfg.WikiName)
BaseEn = m(m(template.New("").
Funcs(template.FuncMap{
"beautifulName": util.BeautifulName,
}).ParseFS(fsys, "base.html")).
Parse(dataText))
if !cfg.UseAuth {
m(BaseEn.Parse(`{{define "auth"}}{{end}}`))
}
if !cfg.AllowRegistration {
m(BaseEn.Parse(`{{define "registration"}}{{end}}`))
}
BaseRu = m(m(BaseEn.Clone()).Parse(ruText))
}
// TODO: get rid of this
func localizedBaseWithWeirdBody(meta Meta) *template.Template {
t := func() *template.Template {
if meta.Locale() == "ru" {
return BaseRu
}
return BaseEn
}()
return m(m(t.Clone()).Parse(`
{{define "body"}}{{.Body}}{{end}}
{{define "title"}}{{.Title}}{{end}}
`))
}
type BaseData struct {
Meta Meta
HeadElements []string
HeaderLinks []cfg.HeaderLink
CommonScripts []string
Title string // TODO: remove
Body string // TODO: remove
}
// Base is a temporary wrapper around BaseEn and BaseRu, meant to facilitate the migration from qtpl.
func Base(meta Meta, title, body string, headElements ...string) string {
var w strings.Builder
meta.W = &w
t := localizedBaseWithWeirdBody(meta)
err := t.ExecuteTemplate(&w, "page", BaseData{
Meta: meta,
Title: title,
HeadElements: headElements,
HeaderLinks: cfg.HeaderLinks,
CommonScripts: cfg.CommonScripts,
Body: body,
})
if err != nil {
log.Println(err)
}
return w.String()
}
func CopyEnWith(fsys fs.FS, f string) *template.Template {
return m(m(BaseEn.Clone()).ParseFS(fsys, f))
}
func CopyRuWith(fsys fs.FS, f string) *template.Template {
return m(m(BaseRu.Clone()).ParseFS(fsys, f))
}

View File

@ -2,6 +2,7 @@ package web
import (
"fmt"
"github.com/bouncepaw/mycorrhiza/viewutil"
"io"
"log"
"mime"
@ -35,7 +36,7 @@ func initAdmin(r *mux.Router) {
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.WriteHeader(http.StatusOK)
views.AdminPanel(views.MetaFrom(w, rq))
views.AdminPanel(viewutil.MetaFrom(w, rq))
}
// handlerAdminShutdown kills the wiki.
@ -70,7 +71,7 @@ func handlerAdminUsers(w http.ResponseWriter, rq *http.Request) {
var lc = l18n.FromRequest(rq)
html := views.AdminUsersPanel(userList, lc)
html = views.Base(lc.Get("admin.users_title"), html, lc, user.FromRequest(rq))
html = views.Base(viewutil.MetaFrom(w, rq), lc.Get("admin.users_title"), html)
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
io.WriteString(w, html)
@ -109,7 +110,7 @@ func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
var lc = l18n.FromRequest(rq)
html := views.AdminUserEdit(u, f, lc)
html = views.Base(fmt.Sprintf(lc.Get("admin.user_title"), u.Name), html, lc, user.FromRequest(rq))
html = views.Base(viewutil.MetaFrom(w, rq), fmt.Sprintf(lc.Get("admin.user_title"), u.Name), html)
if f.HasError() {
w.WriteHeader(http.StatusBadRequest)
@ -139,7 +140,7 @@ func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) {
var lc = l18n.FromRequest(rq)
html := views.AdminUserDelete(u, util.NewFormData(), lc)
html = views.Base(fmt.Sprintf(lc.Get("admin.user_title"), u.Name), html, l18n.FromRequest(rq), user.FromRequest(rq))
html = views.Base(viewutil.MetaFrom(w, rq), fmt.Sprintf(lc.Get("admin.user_title"), u.Name), html)
if f.HasError() {
w.WriteHeader(http.StatusBadRequest)
@ -153,7 +154,7 @@ func handlerAdminUserNew(w http.ResponseWriter, rq *http.Request) {
if rq.Method == http.MethodGet {
// New user form
html := views.AdminUserNew(util.NewFormData(), lc)
html = views.Base(lc.Get("admin.newuser_title"), html, lc, user.FromRequest(rq))
html = views.Base(viewutil.MetaFrom(w, rq), lc.Get("admin.newuser_title"), html)
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
io.WriteString(w, html)
@ -165,7 +166,7 @@ func handlerAdminUserNew(w http.ResponseWriter, rq *http.Request) {
if err != nil {
html := views.AdminUserNew(f.WithError(err), lc)
html = views.Base(lc.Get("admin.newuser_title"), html, lc, user.FromRequest(rq))
html = views.Base(viewutil.MetaFrom(w, rq), lc.Get("admin.newuser_title"), html)
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))

View File

@ -3,6 +3,7 @@ package web
import (
"errors"
"fmt"
"github.com/bouncepaw/mycorrhiza/viewutil"
"io"
"log"
"mime"
@ -19,6 +20,7 @@ import (
)
func initAuth(r *mux.Router) {
r.HandleFunc("/user-list", handlerUserList)
r.HandleFunc("/lock", handlerLock)
// The check below saves a lot of extra checks and lines of codes in other places in this file.
if !cfg.UseAuth {
@ -34,6 +36,13 @@ func initAuth(r *mux.Router) {
r.HandleFunc("/logout", handlerLogout)
}
func handlerUserList(w http.ResponseWriter, rq *http.Request) {
lc := l18n.FromRequest(rq)
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
w.WriteHeader(http.StatusOK)
w.Write([]byte(views.Base(viewutil.MetaFrom(w, rq), lc.Get("ui.users_title"), views.UserList(lc))))
}
func handlerLock(w http.ResponseWriter, rq *http.Request) {
_, _ = io.WriteString(w, views.Lock(l18n.FromRequest(rq)))
}
@ -46,10 +55,9 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
_, _ = io.WriteString(
w,
views.Base(
viewutil.MetaFrom(w, rq),
lc.Get("auth.register_title"),
views.Register(rq),
lc,
user.FromRequest(rq),
),
)
} else if rq.Method == http.MethodPost {
@ -65,14 +73,13 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
_, _ = io.WriteString(
w,
views.Base(
viewutil.MetaFrom(w, rq),
lc.Get("auth.register_title"),
fmt.Sprintf(
`<main class="main-width"><p>%s</p><p><a href="/register">%s<a></p></main>`,
err.Error(),
lc.Get("auth.try_again"),
),
lc,
user.FromRequest(rq),
),
)
} else {
@ -101,7 +108,7 @@ func handlerLogout(w http.ResponseWriter, rq *http.Request) {
}
_, _ = io.WriteString(
w,
views.Base(lc.Get("auth.logout_title"), views.Logout(can, lc), lc, u),
views.Base(viewutil.MetaFrom(w, rq), lc.Get("auth.logout_title"), views.Logout(can, lc)),
)
} else if rq.Method == http.MethodPost {
user.LogoutFromRequest(w, rq)
@ -118,10 +125,9 @@ func handlerLogin(w http.ResponseWriter, rq *http.Request) {
_, _ = io.WriteString(
w,
views.Base(
viewutil.MetaFrom(w, rq),
lc.Get("auth.login_title"),
views.Login(lc),
lc,
user.EmptyUser(),
),
)
} else if rq.Method == http.MethodPost {
@ -133,7 +139,7 @@ func handlerLogin(w http.ResponseWriter, rq *http.Request) {
if err != "" {
w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.WriteHeader(http.StatusInternalServerError)
_, _ = io.WriteString(w, views.Base(err, views.LoginError(err, lc), lc, user.EmptyUser()))
_, _ = io.WriteString(w, views.Base(viewutil.MetaFrom(w, rq), err, views.LoginError(err, lc)))
return
}
http.Redirect(w, rq, "/", http.StatusSeeOther)
@ -172,6 +178,7 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
_, _ = io.WriteString(
w,
views.Base(
viewutil.MetaFrom(w, rq),
lc.Get("ui.error"),
fmt.Sprintf(
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
@ -179,8 +186,6 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
err.Error(),
lc.Get("auth.go_login"),
),
lc,
user.FromRequest(rq),
),
)
return
@ -193,6 +198,7 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
_, _ = io.WriteString(
w,
views.Base(
viewutil.MetaFrom(w, rq),
"Error",
fmt.Sprintf(
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
@ -200,8 +206,6 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
err.Error(),
lc.Get("auth.go_login"),
),
lc,
user.FromRequest(rq),
),
)
return

View File

@ -1,30 +0,0 @@
package web
import (
"github.com/bouncepaw/mycorrhiza/hyphae/backlinks"
"net/http"
"github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
)
func initBacklinks(r *mux.Router) {
r.PathPrefix("/backlinks/").HandlerFunc(handlerBacklinks)
}
// handlerBacklinks lists all backlinks to a hypha.
func handlerBacklinks(w http.ResponseWriter, rq *http.Request) {
var (
hyphaName = util.HyphaNameFromRq(rq, "backlinks")
lc = l18n.FromRequest(rq)
)
util.HTTP200Page(w, views.Base(
lc.Get("ui.backlinks_title", &l18n.Replacements{"query": util.BeautifulName(hyphaName)}),
views.Backlinks(hyphaName, backlinks.YieldHyphaBacklinks, lc),
lc,
user.FromRequest(rq)))
}

View File

@ -2,6 +2,7 @@ package web
import (
"fmt"
"github.com/bouncepaw/mycorrhiza/viewutil"
"log"
"net/http"
"strconv"
@ -10,7 +11,6 @@ import (
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
)
@ -42,10 +42,10 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) {
var lc = l18n.FromRequest(rq)
util.HTTP200Page(w, views.Base(
viewutil.MetaFrom(w, rq),
fmt.Sprintf(lc.Get("ui.history_title"), util.BeautifulName(hyphaName)),
views.History(rq, hyphaName, list, lc),
lc,
user.FromRequest(rq)))
))
}
// handlerRecentChanges displays the /recent-changes/ page.
@ -57,10 +57,10 @@ func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
}
var lc = l18n.FromRequest(rq)
util.HTTP200Page(w, views.Base(
viewutil.MetaFrom(w, rq),
lc.GetPlural("ui.recent_title", n),
views.RecentChanges(n, lc),
lc,
user.FromRequest(rq)))
))
}
// genericHandlerOfFeeds is a helper function for the web feed handlers.

View File

@ -2,11 +2,12 @@ package web
import (
"fmt"
"github.com/bouncepaw/mycomarkup/v4"
"github.com/bouncepaw/mycorrhiza/viewutil"
"log"
"net/http"
"github.com/bouncepaw/mycomarkup/v3"
"github.com/bouncepaw/mycomarkup/v3/mycocontext"
"github.com/bouncepaw/mycomarkup/v4/mycocontext"
"github.com/gorilla/mux"
@ -32,31 +33,31 @@ func initMutators(r *mux.Router) {
func handlerRemoveMedia(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
var (
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
h = hyphae.ByName(util.HyphaNameFromRq(rq, "delete"))
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
h = hyphae.ByName(util.HyphaNameFromRq(rq, "delete"))
meta = viewutil.MetaFrom(w, rq)
)
if !u.CanProceed("remove-media") {
httpErr(w, lc, http.StatusForbidden, h.CanonicalName(), "no rights")
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "no rights")
return
}
if rq.Method == "GET" {
util.HTTP200Page(
w,
views.Base(
meta,
fmt.Sprintf(lc.Get("ui.ask_remove_media"), util.BeautifulName(h.CanonicalName())),
views.RemoveMediaAsk(rq, h.CanonicalName()),
lc,
u))
views.RemoveMediaAsk(rq, h.CanonicalName())))
return
}
switch h := h.(type) {
case *hyphae.EmptyHypha, *hyphae.TextualHypha:
httpErr(w, lc, http.StatusForbidden, h.CanonicalName(), "no media to remove")
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "no media to remove")
return
case *hyphae.MediaHypha:
if err := shroom.RemoveMedia(u, h); err != nil {
httpErr(w, lc, http.StatusInternalServerError, h.CanonicalName(), err.Error())
viewutil.HttpErr(meta, http.StatusInternalServerError, h.CanonicalName(), err.Error())
return
}
}
@ -65,14 +66,15 @@ func handlerRemoveMedia(w http.ResponseWriter, rq *http.Request) {
func handlerDelete(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
var (
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
h = hyphae.ByName(util.HyphaNameFromRq(rq, "delete"))
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
h = hyphae.ByName(util.HyphaNameFromRq(rq, "delete"))
meta = viewutil.MetaFrom(w, rq)
)
if !u.CanProceed("delete") {
log.Printf("%s has no rights to delete %s\n", u.Name, h.CanonicalName())
httpErr(w, lc, http.StatusForbidden, h.CanonicalName(), "No rights")
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "No rights")
return
}
@ -80,7 +82,7 @@ func handlerDelete(w http.ResponseWriter, rq *http.Request) {
case *hyphae.EmptyHypha:
log.Printf("%s tries to delete empty hypha %s\n", u.Name, h.CanonicalName())
// TODO: localize
httpErr(w, lc, http.StatusForbidden, h.CanonicalName(), "Cannot delete an empty hypha")
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "Cannot delete an empty hypha")
return
}
@ -88,16 +90,15 @@ func handlerDelete(w http.ResponseWriter, rq *http.Request) {
util.HTTP200Page(
w,
views.Base(
meta,
fmt.Sprintf(lc.Get("ui.ask_delete"), util.BeautifulName(h.CanonicalName())),
views.DeleteAsk(rq, h.CanonicalName()),
lc,
u))
views.DeleteAsk(rq, h.CanonicalName())))
return
}
if err := shroom.Delete(u, h.(hyphae.ExistingHypha)); err != nil {
log.Println(err)
httpErr(w, lc, http.StatusInternalServerError, h.CanonicalName(), err.Error())
viewutil.HttpErr(meta, http.StatusInternalServerError, h.CanonicalName(), err.Error())
return
}
http.Redirect(w, rq, "/hypha/"+h.CanonicalName(), http.StatusSeeOther)
@ -106,21 +107,22 @@ func handlerDelete(w http.ResponseWriter, rq *http.Request) {
func handlerRename(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
var (
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
h = hyphae.ByName(util.HyphaNameFromRq(rq, "rename"))
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
h = hyphae.ByName(util.HyphaNameFromRq(rq, "rename"))
meta = viewutil.MetaFrom(w, rq)
)
switch h.(type) {
case *hyphae.EmptyHypha:
log.Printf("%s tries to rename empty hypha %s", u.Name, h.CanonicalName())
httpErr(w, lc, http.StatusForbidden, h.CanonicalName(), "Cannot rename an empty hypha") // TODO: localize
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "Cannot rename an empty hypha") // TODO: localize
return
}
if !u.CanProceed("rename") {
log.Printf("%s has no rights to rename %s\n", u.Name, h.CanonicalName())
httpErr(w, lc, http.StatusForbidden, h.CanonicalName(), "No rights")
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "No rights")
return
}
@ -134,16 +136,15 @@ func handlerRename(w http.ResponseWriter, rq *http.Request) {
util.HTTP200Page(
w,
views.Base(
meta,
fmt.Sprintf(lc.Get("ui.ask_rename"), util.BeautifulName(oldHypha.CanonicalName())),
views.RenameAsk(rq, oldHypha.CanonicalName()),
lc,
u))
views.RenameAsk(rq, oldHypha.CanonicalName())))
return
}
if err := shroom.Rename(oldHypha, newName, recursive, u); err != nil {
log.Printf("%s tries to rename %s: %s", u.Name, oldHypha.CanonicalName(), err.Error())
httpErr(w, lc, http.StatusForbidden, oldHypha.CanonicalName(), lc.Get(err.Error())) // TODO: localize
viewutil.HttpErr(meta, http.StatusForbidden, oldHypha.CanonicalName(), lc.Get(err.Error())) // TODO: localize
return
}
http.Redirect(w, rq, "/hypha/"+newName, http.StatusSeeOther)
@ -160,10 +161,10 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
err error
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
meta = viewutil.MetaFrom(w, rq)
)
if err := shroom.CanEdit(u, h, lc); err != nil {
httpErr(w, lc, http.StatusInternalServerError, hyphaName,
err.Error())
viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
return
}
switch h.(type) {
@ -173,18 +174,16 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
textAreaFill, err = shroom.FetchTextFile(h)
if err != nil {
log.Println(err)
httpErr(w, lc, http.StatusInternalServerError, hyphaName,
lc.Get("ui.error_text_fetch"))
viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, lc.Get("ui.error_text_fetch"))
return
}
}
util.HTTP200Page(
w,
views.Base(
meta,
fmt.Sprintf(lc.Get("edit.title"), util.BeautifulName(hyphaName)),
views.Editor(rq, hyphaName, textAreaFill, warning),
lc,
u))
views.Editor(rq, hyphaName, textAreaFill, warning)))
}
// handlerUploadText uploads a new text part for the hypha.
@ -198,21 +197,23 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
message = rq.PostFormValue("message")
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
meta = viewutil.MetaFrom(w, rq)
)
if action != "Preview" {
if err := shroom.UploadText(h, []byte(textData), message, u); err != nil {
httpErr(w, lc, http.StatusForbidden, hyphaName, err.Error())
viewutil.HttpErr(meta, http.StatusForbidden, hyphaName, err.Error())
return
}
}
if action == "Preview" {
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, textData)
ctx, _ := mycocontext.ContextFromStringInput(textData, shroom.MarkupOptions(hyphaName))
util.HTTP200Page(
w,
views.Base(
meta,
fmt.Sprintf(lc.Get("edit.preview_title"), util.BeautifulName(hyphaName)),
views.Preview(
rq,
@ -220,9 +221,7 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
textData,
message,
"",
mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx))),
lc,
u))
mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx)))))
} else {
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
}
@ -238,14 +237,13 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
file, handler, err = rq.FormFile("binary")
meta = viewutil.MetaFrom(w, rq)
)
if err != nil {
httpErr(w, lc, http.StatusInternalServerError, hyphaName,
err.Error())
viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
}
if err := shroom.CanAttach(u, h, lc); err != nil {
httpErr(w, lc, http.StatusInternalServerError, hyphaName,
err.Error())
viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
}
// If file is not passed:
@ -263,7 +261,7 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
)
if err := shroom.UploadBinary(h, mime, file, u); err != nil {
httpErr(w, lc, http.StatusInternalServerError, hyphaName, err.Error())
viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
return
}
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)

View File

@ -3,6 +3,9 @@ package web
import (
"encoding/hex"
"fmt"
"github.com/bouncepaw/mycomarkup/v4"
"github.com/bouncepaw/mycorrhiza/shroom"
"github.com/bouncepaw/mycorrhiza/viewutil"
"io"
"log"
"net/http"
@ -12,7 +15,6 @@ import (
"github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/l18n"
@ -21,9 +23,8 @@ import (
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
"github.com/bouncepaw/mycomarkup/v3"
"github.com/bouncepaw/mycomarkup/v3/mycocontext"
"github.com/bouncepaw/mycomarkup/v3/tools"
"github.com/bouncepaw/mycomarkup/v4/mycocontext"
"github.com/bouncepaw/mycomarkup/v4/tools"
)
func initReaders(r *mux.Router) {
@ -47,10 +48,9 @@ func handlerMedia(w http.ResponseWriter, rq *http.Request) {
)
util.HTTP200Page(w,
views.Base(
viewutil.MetaFrom(w, rq),
lc.Get("ui.media_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName)}),
views.MediaMenu(rq, h, u),
lc,
u))
views.MediaMenu(rq, h, u)))
}
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
@ -73,7 +73,7 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
hyphaName = util.CanonicalName(slug)
h = hyphae.ByName(hyphaName)
user = user.FromRequest(rq)
locale = l18n.FromRequest(rq)
lc = l18n.FromRequest(rq)
)
switch h := h.(type) {
case *hyphae.EmptyHypha:
@ -81,8 +81,9 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
io.WriteString(w, "404 not found")
case hyphae.ExistingHypha:
util.HTTP200Page(w, views.Base(
locale.Get("ui.diff_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName), "rev": revHash}),
views.PrimitiveDiff(rq, h, user, revHash), locale, user))
viewutil.MetaFrom(w, rq),
lc.Get("ui.diff_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName), "rev": revHash}),
views.PrimitiveDiff(rq, h, user, revHash)))
}
}
@ -133,14 +134,13 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
hyphaName = util.CanonicalName(shorterURL[firstSlashIndex+1:])
h = hyphae.ByName(hyphaName)
contents = fmt.Sprintf(`<p>%s</p>`, lc.Get("ui.revision_no_text"))
u = user.FromRequest(rq)
)
switch h := h.(type) {
case hyphae.ExistingHypha:
var textContents, err = history.FileAtRevision(h.TextFilePath(), revHash)
if err == nil {
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, textContents)
ctx, _ := mycocontext.ContextFromStringInput(textContents, shroom.MarkupOptions(hyphaName))
contents = mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx))
}
}
@ -156,10 +156,9 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
_, _ = fmt.Fprint(
w,
views.Base(
viewutil.MetaFrom(w, rq),
lc.Get("ui.revision_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName), "rev": revHash}),
page,
lc,
u,
),
)
}
@ -200,7 +199,6 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
h = hyphae.ByName(hyphaName)
contents string
openGraph string
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
)
@ -208,16 +206,14 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
case *hyphae.EmptyHypha:
util.HTTP404Page(w,
views.Base(
viewutil.MetaFrom(w, rq),
util.BeautifulName(hyphaName),
views.Hypha(views.MetaFrom(w, rq), h, contents),
lc,
u,
views.Hypha(viewutil.MetaFrom(w, rq), h, contents),
openGraph))
case hyphae.ExistingHypha:
fileContentsT, errT := os.ReadFile(h.TextFilePath())
if errT == nil {
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, string(fileContentsT))
ctx = mycocontext.WithWebSiteURL(ctx, cfg.URL)
ctx, _ := mycocontext.ContextFromStringInput(string(fileContentsT), shroom.MarkupOptions(hyphaName))
getOpenGraph, descVisitor, imgVisitor := tools.OpenGraphVisitors(ctx)
ast := mycomarkup.BlockTree(ctx, descVisitor, imgVisitor)
contents = mycomarkup.BlocksToHTML(ctx, ast)
@ -230,10 +226,9 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
util.HTTP200Page(w,
views.Base(
viewutil.MetaFrom(w, rq),
util.BeautifulName(hyphaName),
views.Hypha(views.MetaFrom(w, rq), h, contents),
lc,
u,
views.Hypha(viewutil.MetaFrom(w, rq), h, contents),
openGraph))
}
}

View File

@ -1,38 +0,0 @@
package web
import (
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/shroom"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
)
func initSearch(r *mux.Router) {
r.HandleFunc("/title-search/", handlerTitleSearch)
}
func handlerTitleSearch(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
_ = rq.ParseForm()
var (
query = rq.FormValue("q")
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
)
w.WriteHeader(http.StatusOK)
_, _ = io.WriteString(
w,
views.Base(
lc.Get("ui.title_search_title", &l18n.Replacements{"query": query}),
views.TitleSearch(query, shroom.YieldHyphaNamesContainingString, lc),
lc,
u,
),
)
}

View File

@ -1,167 +0,0 @@
package web
// stuff.go is used for meta stuff about the wiki or all hyphae at once.
import (
"github.com/bouncepaw/mycorrhiza/hyphae/backlinks"
"io"
"log"
"math/rand"
"net/http"
"strings"
"github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/help"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/shroom"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
"github.com/bouncepaw/mycomarkup/v3"
"github.com/bouncepaw/mycomarkup/v3/mycocontext"
)
func initStuff(r *mux.Router) {
r.PathPrefix("/help").HandlerFunc(handlerHelp)
r.HandleFunc("/list", handlerList)
r.HandleFunc("/reindex", handlerReindex)
r.HandleFunc("/update-header-links", handlerUpdateHeaderLinks)
r.HandleFunc("/random", handlerRandom)
r.HandleFunc("/about", handlerAbout)
r.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) {
http.Redirect(w, rq, "/static/favicon.ico", http.StatusSeeOther)
})
}
// handlerHelp gets the appropriate documentation or tells you where you (personally) have failed.
func handlerHelp(w http.ResponseWriter, rq *http.Request) {
lc := l18n.FromRequest(rq)
articlePath := strings.TrimPrefix(strings.TrimPrefix(rq.URL.Path, "/help/"), "/help")
// See the history of this file to resurrect the old algorithm that supported multiple languages
lang := "en"
if articlePath == "" {
articlePath = "en"
}
if !strings.HasPrefix(articlePath, "en") {
w.WriteHeader(http.StatusNotFound)
_, _ = io.WriteString(w, "404 Not found")
return
}
content, err := help.Get(articlePath)
if err != nil && strings.HasPrefix(err.Error(), "open") {
w.WriteHeader(http.StatusNotFound)
_, _ = io.WriteString(
w,
views.Base(lc.Get("help.entry_not_found"),
views.Help(views.HelpEmptyError(lc), lang, lc),
lc,
user.FromRequest(rq)),
)
return
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = io.WriteString(
w,
views.Base(err.Error(),
views.Help(err.Error(), lang, lc),
lc,
user.FromRequest(rq)),
)
return
}
// TODO: change for the function that uses byte array when there is such function in mycomarkup.
ctx, _ := mycocontext.ContextFromStringInput(articlePath, string(content))
ast := mycomarkup.BlockTree(ctx)
result := mycomarkup.BlocksToHTML(ctx, ast)
w.WriteHeader(http.StatusOK)
_, _ = io.WriteString(
w,
views.Base(lc.Get("help.title"),
views.Help(result, lang, lc),
lc,
user.FromRequest(rq)),
)
}
// handlerList shows a list of all hyphae in the wiki in random order.
func handlerList(w http.ResponseWriter, rq *http.Request) {
u := user.FromRequest(rq)
var lc = l18n.FromRequest(rq)
util.PrepareRq(rq)
util.HTTP200Page(w, views.Base(lc.Get("ui.list_title"), views.HyphaList(lc), lc, u))
}
// 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 {
var lc = l18n.FromRequest(rq)
httpErr(w, lc, http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.reindex_no_rights"))
log.Println("Rejected", rq.URL)
return
}
hyphae.ResetCount()
log.Println("Reindexing hyphae in", files.HyphaeDir())
hyphae.Index(files.HyphaeDir())
backlinks.IndexBacklinks()
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.wiki/hypha/configuration/header
func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if ok := user.CanProceed(rq, "update-header-links"); !ok {
var lc = l18n.FromRequest(rq)
httpErr(w, lc, http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.header_no_rights"))
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 {
var lc = l18n.FromRequest(rq)
httpErr(w, lc, http.StatusNotFound, cfg.HomeHypha, lc.Get("ui.random_no_hyphae_tip"))
return
}
i := rand.Intn(amountOfHyphae)
for h := range hyphae.YieldExistingHyphae() {
if i == 0 {
randomHyphaName = h.CanonicalName()
}
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)
var (
lc = l18n.FromRequest(rq)
title = lc.Get("ui.about_title", &l18n.Replacements{"name": cfg.WikiName})
)
_, err := io.WriteString(w, views.Base(title, views.AboutHTML(lc), lc, user.FromRequest(rq)))
if err != nil {
log.Println(err)
}
}

View File

@ -2,74 +2,21 @@
package web
import (
"fmt"
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/categories"
"github.com/bouncepaw/mycorrhiza/help"
"github.com/bouncepaw/mycorrhiza/misc"
"io"
"mime"
"net/http"
"net/url"
"github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/static"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/views"
)
var stylesheets = []string{"default.css", "custom.css"}
// httpErr is used by many handlers to signal errors in a compact way.
func httpErr(w http.ResponseWriter, lc *l18n.Localizer, status int, name, errMsg string) {
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
w.WriteHeader(status)
fmt.Fprint(
w,
views.Base(
"Error",
fmt.Sprintf(
`<main class="main-width"><p>%s. <a href="/hypha/%s">%s<a></p></main>`,
errMsg,
name,
lc.Get("ui.error_go_back"),
),
lc,
user.EmptyUser(),
),
)
}
func handlerStyle(w http.ResponseWriter, rq *http.Request) {
w.Header().Set("Content-Type", mime.TypeByExtension(".css"))
for _, name := range stylesheets {
file, err := static.FS.Open(name)
if err != nil {
continue
}
io.Copy(w, file)
file.Close()
}
}
func handlerUserList(w http.ResponseWriter, rq *http.Request) {
lc := l18n.FromRequest(rq)
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
w.WriteHeader(http.StatusOK)
w.Write([]byte(views.Base(lc.Get("ui.users_title"), views.UserList(lc), lc, user.FromRequest(rq))))
}
func handlerRobotsTxt(w http.ResponseWriter, rq *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
file, err := static.FS.Open("robots.txt")
if err != nil {
return
}
io.Copy(w, file)
file.Close()
}
// Handler initializes and returns the HTTP router based on the configuration.
func Handler() http.Handler {
router := mux.NewRouter()
@ -86,10 +33,6 @@ func Handler() http.Handler {
// Public routes. They're always accessible regardless of the user status.
initAuth(router)
router.HandleFunc("/robots.txt", handlerRobotsTxt)
router.HandleFunc("/static/style.css", handlerStyle)
router.PathPrefix("/static/").
Handler(http.StripPrefix("/static/", http.FileServer(http.FS(static.FS))))
// Wiki routes. They may be locked or restricted.
wikiRouter := router.PathPrefix("").Subrouter()
@ -105,10 +48,10 @@ func Handler() http.Handler {
initReaders(wikiRouter)
initMutators(wikiRouter)
initHistory(wikiRouter)
initStuff(wikiRouter)
initSearch(wikiRouter)
initBacklinks(wikiRouter)
initCategories(wikiRouter)
help.InitHandlers(wikiRouter)
backlinks.InitHandlers(wikiRouter)
categories.InitHandlers(wikiRouter)
misc.InitHandlers(wikiRouter)
// Admin routes.
if cfg.UseAuth {
@ -117,9 +60,6 @@ func Handler() http.Handler {
initAdmin(adminRouter)
}
// Miscellaneous
wikiRouter.HandleFunc("/user-list", handlerUserList)
// Index page
wikiRouter.HandleFunc("/", func(w http.ResponseWriter, rq *http.Request) {
// Let's pray it never fails