mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-10-30 11:46:16 +00:00
commit
79868cebf8
1
Makefile
1
Makefile
@ -2,6 +2,7 @@ run: build
|
|||||||
./mycorrhiza metarrhiza
|
./mycorrhiza metarrhiza
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
go generate
|
||||||
go build .
|
go build .
|
||||||
|
|
||||||
test:
|
test:
|
||||||
|
38
README.md
38
README.md
@ -1,21 +1,35 @@
|
|||||||
# mycorrhiza wiki
|
# 🍄 MycorrhizaWiki 0.8
|
||||||
A wiki engine inspired by fungi. Not production-ready.
|
A wiki engine.
|
||||||
|
|
||||||
Current version: 0.7 (or more?)
|
## Installation
|
||||||
|
```sh
|
||||||
|
git clone --recurse-submodules https://github.com/bouncepaw/mycorrhiza
|
||||||
|
cd mycorrhiza
|
||||||
|
make
|
||||||
|
# That make will:
|
||||||
|
# * run the default wiki. You can edit it right away.
|
||||||
|
# * create an executable called `mycorrhiza`. Run it with path to your wiki.
|
||||||
|
```
|
||||||
|
|
||||||
## Current features
|
## Current features
|
||||||
* Edit pages through html forms
|
* Edit pages through html forms
|
||||||
* Responsive design
|
* Responsive design
|
||||||
* Works in text browsers
|
* Works in text browsers
|
||||||
* Pages (called hyphae) can be in gemtext.
|
* Wiki pages (called hyphae) are in gemtext
|
||||||
* Everything is stored as simple files, no database required
|
* Everything is stored as simple files, no database required
|
||||||
|
* Page trees
|
||||||
|
* Changes are saved to git
|
||||||
|
* List of hyphae page
|
||||||
|
* History page
|
||||||
|
* Random page
|
||||||
|
* Light on resources: I run a home wiki on this engine 24/7 at an [Orange π Lite](http://www.orangepi.org/orangepilite/).
|
||||||
|
|
||||||
## Future features
|
## Contributing
|
||||||
* Tags
|
Help is always needed. We have a [tg chat](https://t.me/mycorrhizadev) where some development is coordinated. Feel free to open an issue or contact me.
|
||||||
|
|
||||||
|
## Future plans
|
||||||
|
* Tagging system
|
||||||
* Authorization
|
* Authorization
|
||||||
* History view
|
* Better history viewing
|
||||||
* Granular user rights
|
* Recent changes page
|
||||||
|
* More markups
|
||||||
## Installation
|
|
||||||
I guess you can just clone this repo and run `make` to play around with the default wiki.
|
|
||||||
|
|
||||||
|
@ -23,11 +23,6 @@ type GemLexerState struct {
|
|||||||
buf string
|
buf string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GeminiToHtml converts gemtext `content` of hypha `name` to html string.
|
|
||||||
func GeminiToHtml(name, content string) string {
|
|
||||||
return "TODO: do"
|
|
||||||
}
|
|
||||||
|
|
||||||
type Line struct {
|
type Line struct {
|
||||||
id int
|
id int
|
||||||
// interface{} may be bad. What I need is a sum of string and Transclusion
|
// interface{} may be bad. What I need is a sum of string and Transclusion
|
||||||
@ -78,7 +73,7 @@ func wikilink(src string, state *GemLexerState) (href, text, class string) {
|
|||||||
func lex(name, content string) (ast []Line) {
|
func lex(name, content string) (ast []Line) {
|
||||||
var state = GemLexerState{name: name}
|
var state = GemLexerState{name: name}
|
||||||
|
|
||||||
for _, line := range strings.Split(content, "\n") {
|
for _, line := range append(strings.Split(content, "\n"), "") {
|
||||||
geminiLineToAST(line, &state, &ast)
|
geminiLineToAST(line, &state, &ast)
|
||||||
}
|
}
|
||||||
return ast
|
return ast
|
||||||
@ -86,16 +81,21 @@ func lex(name, content string) (ast []Line) {
|
|||||||
|
|
||||||
// Lex `line` in gemtext and save it to `ast` using `state`.
|
// Lex `line` in gemtext and save it to `ast` using `state`.
|
||||||
func geminiLineToAST(line string, state *GemLexerState, ast *[]Line) {
|
func geminiLineToAST(line string, state *GemLexerState, ast *[]Line) {
|
||||||
|
addLine := func(text interface{}) {
|
||||||
|
*ast = append(*ast, Line{id: state.id, contents: text})
|
||||||
|
}
|
||||||
|
|
||||||
if "" == strings.TrimSpace(line) {
|
if "" == strings.TrimSpace(line) {
|
||||||
|
if state.where == "list" {
|
||||||
|
state.where = ""
|
||||||
|
addLine(state.buf + "</ul>")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
startsWith := func(token string) bool {
|
startsWith := func(token string) bool {
|
||||||
return strings.HasPrefix(line, token)
|
return strings.HasPrefix(line, token)
|
||||||
}
|
}
|
||||||
addLine := func(text interface{}) {
|
|
||||||
*ast = append(*ast, Line{id: state.id, contents: text})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Beware! Usage of goto. Some may say it is considered evil but in this case it helped to make a better-structured code.
|
// Beware! Usage of goto. Some may say it is considered evil but in this case it helped to make a better-structured code.
|
||||||
switch state.where {
|
switch state.where {
|
||||||
|
5
go.mod
5
go.mod
@ -2,7 +2,4 @@ module github.com/bouncepaw/mycorrhiza
|
|||||||
|
|
||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
require (
|
require github.com/valyala/quicktemplate v1.6.2
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
|
|
||||||
golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6 // indirect
|
|
||||||
)
|
|
||||||
|
32
go.sum
32
go.sum
@ -1,25 +1,15 @@
|
|||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||||
|
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
|
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasthttp v1.15.1/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA=
|
||||||
|
github.com/valyala/quicktemplate v1.6.2 h1:k0vgK7zlmFzqAoIBIOrhrfmZ6JoTGJlLRPLbkPGr2/M=
|
||||||
|
github.com/valyala/quicktemplate v1.6.2/go.mod h1:mtEJpQtUiBV0SHhMX6RtiJtqxncgrfmjcUy5T68X8TM=
|
||||||
|
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
|
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6 h1:qKpj8TpV+LEhel7H/fR788J+KvhWZ3o3V6N2fU/iuLU=
|
|
||||||
golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
|
77
history/history.go
Normal file
77
history/history.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package history
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start initializes git credentials.
|
||||||
|
func Start(wikiDir string) {
|
||||||
|
_, err := gitsh("config", "user.name", "wikimind")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = gitsh("config", "user.email", "wikimind@mycorrhiza")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revision represents a revision, duh. Hash is usually short. Username is extracted from email.
|
||||||
|
type Revision struct {
|
||||||
|
Hash string
|
||||||
|
Username string
|
||||||
|
Time time.Time
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path to git executable. Set at init()
|
||||||
|
var gitpath string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
path, err := exec.LookPath("git")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Cound not find the git executable. Check your $PATH.")
|
||||||
|
} else {
|
||||||
|
log.Println("Git path is", path)
|
||||||
|
}
|
||||||
|
gitpath = path
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// I pronounce it as [gɪt͡ʃ].
|
||||||
|
func gitsh(args ...string) (out bytes.Buffer, err error) {
|
||||||
|
fmt.Printf("$ %v\n", args)
|
||||||
|
cmd := exec.Command(gitpath, args...)
|
||||||
|
|
||||||
|
cmd.Dir = util.WikiDir
|
||||||
|
|
||||||
|
b, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("gitsh:", err)
|
||||||
|
}
|
||||||
|
return *bytes.NewBuffer(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a UNIX timestamp as string into a time. If nil is returned, it means that the timestamp could not be converted.
|
||||||
|
func unixTimestampAsTime(ts string) *time.Time {
|
||||||
|
i, err := strconv.ParseInt(ts, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tm := time.Unix(i, 0)
|
||||||
|
return &tm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename renames from `from` to `to` using `git mv`.
|
||||||
|
func Rename(from, to string) error {
|
||||||
|
log.Println(util.ShorterPath(from), util.ShorterPath(to))
|
||||||
|
_, err := gitsh("mv", from, to)
|
||||||
|
return err
|
||||||
|
}
|
58
history/information.go
Normal file
58
history/information.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// information.go
|
||||||
|
// Things related to gathering existing information.
|
||||||
|
package history
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Revisions returns slice of revisions for the given hypha name.
|
||||||
|
func Revisions(hyphaName string) ([]Revision, error) {
|
||||||
|
var (
|
||||||
|
out, err = gitsh(
|
||||||
|
"log", "--oneline", "--no-merges",
|
||||||
|
// Hash, Commiter email, Commiter time, Commit msg separated by tab
|
||||||
|
"--pretty=format:\"%h\t%ce\t%ct\t%s\"",
|
||||||
|
"--", hyphaName+"&.*",
|
||||||
|
)
|
||||||
|
revs []Revision
|
||||||
|
)
|
||||||
|
if err == nil {
|
||||||
|
for _, line := range strings.Split(out.String(), "\n") {
|
||||||
|
revs = append(revs, parseRevisionLine(line))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return revs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This regex is wrapped in "". For some reason, these quotes appear at some time and we have to get rid of them.
|
||||||
|
var revisionLinePattern = regexp.MustCompile("\"(.*)\t(.*)@.*\t(.*)\t(.*)\"")
|
||||||
|
|
||||||
|
func parseRevisionLine(line string) Revision {
|
||||||
|
results := revisionLinePattern.FindStringSubmatch(line)
|
||||||
|
return Revision{
|
||||||
|
Hash: results[1],
|
||||||
|
Username: results[2],
|
||||||
|
Time: *unixTimestampAsTime(results[3]),
|
||||||
|
Message: results[4],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represent revision as a table row.
|
||||||
|
func (rev *Revision) AsHtmlTableRow(hyphaName string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
<tr>
|
||||||
|
<td><time>%s</time></td>
|
||||||
|
<td><a href="/rev/%s/%s">%s</a></td>
|
||||||
|
<td>%s</td>
|
||||||
|
</tr>`, rev.Time.Format(time.RFC822), rev.Hash, hyphaName, rev.Hash, rev.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See how the file with `filepath` looked at commit with `hash`.
|
||||||
|
func FileAtRevision(filepath, hash string) (string, error) {
|
||||||
|
out, err := gitsh("show", hash+":"+filepath)
|
||||||
|
return out.String(), err
|
||||||
|
}
|
84
history/operations.go
Normal file
84
history/operations.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// history/operations.go
|
||||||
|
// Things related to writing history.
|
||||||
|
package history
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OpType is the type a history operation has. Callers shall set appropriate optypes when creating history operations.
|
||||||
|
type OpType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeNone OpType = iota
|
||||||
|
TypeEditText
|
||||||
|
TypeEditBinary
|
||||||
|
)
|
||||||
|
|
||||||
|
// HistoryOp is an object representing a history operation.
|
||||||
|
type HistoryOp struct {
|
||||||
|
// All errors are appended here.
|
||||||
|
Errs []error
|
||||||
|
opType OpType
|
||||||
|
userMsg string
|
||||||
|
name string
|
||||||
|
email string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operation is a constructor of a history operation.
|
||||||
|
func Operation(opType OpType) *HistoryOp {
|
||||||
|
hop := &HistoryOp{
|
||||||
|
Errs: []error{},
|
||||||
|
opType: opType,
|
||||||
|
}
|
||||||
|
return hop
|
||||||
|
}
|
||||||
|
|
||||||
|
// git operation maker helper
|
||||||
|
func (hop *HistoryOp) gitop(args ...string) *HistoryOp {
|
||||||
|
out, err := gitsh(args...)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("out:", out.String())
|
||||||
|
hop.Errs = append(hop.Errs, err)
|
||||||
|
}
|
||||||
|
return hop
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFiles stages all passed `paths`. Paths can be rooted or not.
|
||||||
|
func (hop *HistoryOp) WithFiles(paths ...string) *HistoryOp {
|
||||||
|
for i, path := range paths {
|
||||||
|
paths[i] = util.ShorterPath(path)
|
||||||
|
}
|
||||||
|
// 1 git operation is more effective than n operations.
|
||||||
|
return hop.gitop(append([]string{"add"}, paths...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply applies history operation by doing the commit.
|
||||||
|
func (hop *HistoryOp) Apply() *HistoryOp {
|
||||||
|
hop.gitop(
|
||||||
|
"commit",
|
||||||
|
"--author='"+hop.name+" <"+hop.email+">'",
|
||||||
|
"--message="+hop.userMsg,
|
||||||
|
)
|
||||||
|
return hop
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMsg sets what message will be used for the future commit. If user message exceeds one line, it is stripped down.
|
||||||
|
func (hop *HistoryOp) WithMsg(userMsg string) *HistoryOp {
|
||||||
|
for _, ch := range userMsg {
|
||||||
|
if ch == '\r' || ch == '\n' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
hop.userMsg += string(ch)
|
||||||
|
}
|
||||||
|
return hop
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSignature sets a signature for the future commit. You need to pass a username only, the rest is upon us (including email and time).
|
||||||
|
func (hop *HistoryOp) WithSignature(username string) *HistoryOp {
|
||||||
|
hop.name = username
|
||||||
|
hop.email = username + "@mycorrhiza" // A fake email, why not
|
||||||
|
return hop
|
||||||
|
}
|
@ -7,6 +7,10 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/templates"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -36,24 +40,7 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
warning = `<p>You are creating a new hypha.</p>`
|
warning = `<p>You are creating a new hypha.</p>`
|
||||||
}
|
}
|
||||||
form := fmt.Sprintf(`
|
util.HTTP200Page(w, base("Edit"+hyphaName, templates.EditHTML(hyphaName, textAreaFill, warning)))
|
||||||
<main>
|
|
||||||
<h1>Edit %[1]s</h1>
|
|
||||||
%[3]s
|
|
||||||
<form method="post" class="upload-text-form"
|
|
||||||
action="/upload-text/%[1]s">
|
|
||||||
<textarea name="text">%[2]s</textarea>
|
|
||||||
<br/>
|
|
||||||
<input type="submit"/>
|
|
||||||
<a href="/page/%[1]s">Cancel</a>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
||||||
`, hyphaName, textAreaFill, warning)
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte(base(
|
|
||||||
"Edit "+hyphaName, form)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerUploadText uploads a new text part for the hypha.
|
// handlerUploadText uploads a new text part for the hypha.
|
||||||
@ -92,6 +79,11 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
|||||||
hyphaData.textPath = fullPath
|
hyphaData.textPath = fullPath
|
||||||
}
|
}
|
||||||
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
||||||
|
history.Operation(history.TypeEditText).
|
||||||
|
WithFiles(fullPath).
|
||||||
|
WithMsg(fmt.Sprintf("Edit ‘%s’", hyphaName)).
|
||||||
|
WithSignature("anon").
|
||||||
|
Apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerUploadBinary uploads a new binary part for the hypha.
|
// handlerUploadBinary uploads a new binary part for the hypha.
|
||||||
@ -127,11 +119,6 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
|||||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
|
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
if err = ioutil.WriteFile(fullPath, data, 0644); err != nil {
|
|
||||||
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error",
|
|
||||||
"Could not save passed data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !isOld {
|
if !isOld {
|
||||||
HyphaStorage[hyphaName] = &HyphaData{
|
HyphaStorage[hyphaName] = &HyphaData{
|
||||||
binaryPath: fullPath,
|
binaryPath: fullPath,
|
||||||
@ -139,13 +126,25 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if hyphaData.binaryPath != fullPath {
|
if hyphaData.binaryPath != fullPath {
|
||||||
if err := os.Remove(hyphaData.binaryPath); err != nil {
|
if err := history.Rename(hyphaData.binaryPath, fullPath); err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
} else {
|
||||||
|
log.Println("Moved", hyphaData.binaryPath, "to", fullPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hyphaData.binaryPath = fullPath
|
hyphaData.binaryPath = fullPath
|
||||||
hyphaData.binaryType = mimeType
|
hyphaData.binaryType = mimeType
|
||||||
}
|
}
|
||||||
|
if err = ioutil.WriteFile(fullPath, data, 0644); err != nil {
|
||||||
|
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error",
|
||||||
|
"Could not save passed data")
|
||||||
|
return
|
||||||
|
}
|
||||||
log.Println("Written", len(data), "of binary data for", hyphaName, "to path", fullPath)
|
log.Println("Written", len(data), "of binary data for", hyphaName, "to path", fullPath)
|
||||||
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
||||||
|
history.Operation(history.TypeEditText).
|
||||||
|
WithFiles(fullPath, hyphaData.binaryPath).
|
||||||
|
WithMsg(fmt.Sprintf("Upload binary part for ‘%s’ with type ‘%s’", hyphaName, mimeType.Mime())).
|
||||||
|
WithSignature("anon").
|
||||||
|
Apply()
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,67 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/gemtext"
|
"github.com/bouncepaw/mycorrhiza/gemtext"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/templates"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/tree"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
http.HandleFunc("/page/", handlerPage)
|
http.HandleFunc("/page/", handlerPage)
|
||||||
http.HandleFunc("/text/", handlerText)
|
http.HandleFunc("/text/", handlerText)
|
||||||
http.HandleFunc("/binary/", handlerBinary)
|
http.HandleFunc("/binary/", handlerBinary)
|
||||||
|
http.HandleFunc("/history/", handlerHistory)
|
||||||
|
http.HandleFunc("/rev/", handlerRevision)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlerRevision displays a specific revision of text part a page
|
||||||
|
func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
log.Println(rq.URL)
|
||||||
|
var (
|
||||||
|
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/rev/")
|
||||||
|
revHash = path.Dir(shorterUrl)
|
||||||
|
hyphaName = CanonicalName(strings.TrimPrefix(shorterUrl, revHash+"/"))
|
||||||
|
contents = fmt.Sprintf(`<p>This hypha had no text at this revision.</p>`)
|
||||||
|
textPath = hyphaName + "&.gmi"
|
||||||
|
textContents, err = history.FileAtRevision(textPath, revHash)
|
||||||
|
)
|
||||||
|
if err == nil {
|
||||||
|
contents = gemtext.ToHtml(hyphaName, textContents)
|
||||||
|
}
|
||||||
|
page := templates.RevisionHTML(
|
||||||
|
hyphaName,
|
||||||
|
naviTitle(hyphaName),
|
||||||
|
contents,
|
||||||
|
tree.TreeAsHtml(hyphaName, IterateHyphaNamesWith),
|
||||||
|
revHash,
|
||||||
|
)
|
||||||
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(base(hyphaName, page)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlerHistory lists all revisions of a hypha
|
||||||
|
func handlerHistory(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
log.Println(rq.URL)
|
||||||
|
hyphaName := HyphaNameFromRq(rq, "history")
|
||||||
|
var tbody string
|
||||||
|
if _, ok := HyphaStorage[hyphaName]; ok {
|
||||||
|
revs, err := history.Revisions(hyphaName)
|
||||||
|
if err == nil {
|
||||||
|
for _, rev := range revs {
|
||||||
|
tbody += rev.AsHtmlTableRow(hyphaName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Println(revs)
|
||||||
|
}
|
||||||
|
|
||||||
|
util.HTTP200Page(w,
|
||||||
|
base(hyphaName, templates.HistoryHTML(hyphaName, tbody)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlerText serves raw source text of the hypha.
|
// handlerText serves raw source text of the hypha.
|
||||||
@ -43,8 +96,8 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) {
|
|||||||
log.Println(rq.URL)
|
log.Println(rq.URL)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "page")
|
hyphaName = HyphaNameFromRq(rq, "page")
|
||||||
contents = fmt.Sprintf(`<p>This hypha has no text. Why not <a href="/edit/%s">create it</a>?</p>`, hyphaName)
|
|
||||||
data, hyphaExists = HyphaStorage[hyphaName]
|
data, hyphaExists = HyphaStorage[hyphaName]
|
||||||
|
contents string
|
||||||
)
|
)
|
||||||
if hyphaExists {
|
if hyphaExists {
|
||||||
fileContentsT, errT := ioutil.ReadFile(data.textPath)
|
fileContentsT, errT := ioutil.ReadFile(data.textPath)
|
||||||
@ -56,31 +109,8 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) {
|
|||||||
contents = binaryHtmlBlock(hyphaName, data) + contents
|
contents = binaryHtmlBlock(hyphaName, data) + contents
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
form := fmt.Sprintf(`
|
util.HTTP200Page(w, base(hyphaName, templates.PageHTML(hyphaName,
|
||||||
<main>
|
naviTitle(hyphaName),
|
||||||
<nav>
|
contents,
|
||||||
<ul>
|
tree.TreeAsHtml(hyphaName, IterateHyphaNamesWith))))
|
||||||
<li><a href="/edit/%[1]s">Edit</a></li>
|
|
||||||
<li><a href="/text/%[1]s">Raw text</a></li>
|
|
||||||
<li><a href="/binary/%[1]s">Binary part</a></li>
|
|
||||||
<li><a href="/history/%[1]s">History</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<article>
|
|
||||||
%[2]s
|
|
||||||
%[3]s
|
|
||||||
</article>
|
|
||||||
<hr>
|
|
||||||
<form action="/upload-binary/%[1]s"
|
|
||||||
method="post" enctype="multipart/form-data">
|
|
||||||
<label for="upload-binary__input">Upload new binary part</label>
|
|
||||||
<br>
|
|
||||||
<input type="file" id="upload-binary__input" name="binary"/>
|
|
||||||
<input type="submit"/>
|
|
||||||
</form>
|
|
||||||
</main>
|
|
||||||
`, hyphaName, naviTitle(hyphaName), contents)
|
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte(base(hyphaName, form)))
|
|
||||||
}
|
}
|
||||||
|
104
main.go
104
main.go
@ -1,13 +1,19 @@
|
|||||||
|
//go:generate go get -u github.com/valyala/quicktemplate/qtc
|
||||||
|
//go:generate qtc -dir=templates
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/templates"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WikiDir is a rooted path to the wiki storage directory.
|
// WikiDir is a rooted path to the wiki storage directory.
|
||||||
@ -19,6 +25,13 @@ var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%]+`)
|
|||||||
// HyphaStorage is a mapping between canonical hypha names and their meta information.
|
// HyphaStorage is a mapping between canonical hypha names and their meta information.
|
||||||
var HyphaStorage = make(map[string]*HyphaData)
|
var HyphaStorage = make(map[string]*HyphaData)
|
||||||
|
|
||||||
|
// IterateHyphaNamesWith is a closure to be passed to subpackages to let them iterate all hypha names read-only.
|
||||||
|
func IterateHyphaNamesWith(f func(string)) {
|
||||||
|
for hyphaName, _ := range HyphaStorage {
|
||||||
|
f(hyphaName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HttpErr is used by many handlers to signal errors in a compact way.
|
// HttpErr is used by many handlers to signal errors in a compact way.
|
||||||
func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) {
|
func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) {
|
||||||
log.Println(errMsg, "for", name)
|
log.Println(errMsg, "for", name)
|
||||||
@ -29,70 +42,21 @@ func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) {
|
|||||||
errMsg, name)))
|
errMsg, name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// shorterPath is used by handlerList to display shorter path to the files. It simply strips WikiDir.
|
|
||||||
func shorterPath(fullPath string) string {
|
|
||||||
tmp := strings.TrimPrefix(fullPath, WikiDir)
|
|
||||||
if tmp == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return tmp[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show all hyphae
|
// Show all hyphae
|
||||||
func handlerList(w http.ResponseWriter, rq *http.Request) {
|
func handlerList(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
log.Println(rq.URL)
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
var (
|
||||||
w.WriteHeader(http.StatusOK)
|
tbody string
|
||||||
buf := `
|
pageCount = len(HyphaStorage)
|
||||||
<h1>List of pages</h1>
|
)
|
||||||
<table>
|
for hyphaName, data := range HyphaStorage {
|
||||||
<thead>
|
tbody += templates.HyphaListRowHTML(hyphaName, data.binaryType.Mime(), data.binaryPath != "")
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Text path</th>
|
|
||||||
<th>Text type</th>
|
|
||||||
<th>Binary path</th>
|
|
||||||
<th>Binary type</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>`
|
|
||||||
for name, data := range HyphaStorage {
|
|
||||||
buf += fmt.Sprintf(`
|
|
||||||
<tr>
|
|
||||||
<td><a href="/page/%s">%s</a></td>
|
|
||||||
<td>%s</td>
|
|
||||||
<td>%d</td>
|
|
||||||
<td>%s</td>
|
|
||||||
<td>%d</td>
|
|
||||||
</tr>`,
|
|
||||||
name, name,
|
|
||||||
shorterPath(data.textPath), data.textType,
|
|
||||||
shorterPath(data.binaryPath), data.binaryType,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
buf += `
|
util.HTTP200Page(w, base("List of pages", templates.HyphaListHTML(tbody, pageCount)))
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
`
|
|
||||||
w.Write([]byte(base("List of pages", buf)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This part is present in all html documents.
|
// This part is present in all html documents.
|
||||||
func base(title, body string) string {
|
var base = templates.BaseHTML
|
||||||
return fmt.Sprintf(`
|
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="stylesheet" type="text/css" href="/static/common.css">
|
|
||||||
<title>%s</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
%s
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`, title, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reindex all hyphae by checking the wiki storage directory anew.
|
// Reindex all hyphae by checking the wiki storage directory anew.
|
||||||
func handlerReindex(w http.ResponseWriter, rq *http.Request) {
|
func handlerReindex(w http.ResponseWriter, rq *http.Request) {
|
||||||
@ -104,24 +68,46 @@ func handlerReindex(w http.ResponseWriter, rq *http.Request) {
|
|||||||
log.Println("Indexed", len(HyphaStorage), "hyphae")
|
log.Println("Indexed", len(HyphaStorage), "hyphae")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Redirect to a random hypha.
|
||||||
|
func handlerRandom(w http.ResponseWriter, rq *http.Request) {
|
||||||
|
log.Println(rq.URL)
|
||||||
|
var randomHyphaName string
|
||||||
|
i := rand.Intn(len(HyphaStorage))
|
||||||
|
for hyphaName := range HyphaStorage {
|
||||||
|
if i == 0 {
|
||||||
|
randomHyphaName = hyphaName
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
http.Redirect(w, rq, "/page/"+randomHyphaName, http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Println("Running MycorrhizaWiki β")
|
log.Println("Running MycorrhizaWiki β")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
WikiDir, err = filepath.Abs(os.Args[1])
|
WikiDir, err = filepath.Abs(os.Args[1])
|
||||||
|
util.WikiDir = WikiDir
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if err := os.Chdir(WikiDir); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
log.Println("Wiki storage directory is", WikiDir)
|
log.Println("Wiki storage directory is", WikiDir)
|
||||||
log.Println("Start indexing hyphae...")
|
log.Println("Start indexing hyphae...")
|
||||||
Index(WikiDir)
|
Index(WikiDir)
|
||||||
log.Println("Indexed", len(HyphaStorage), "hyphae")
|
log.Println("Indexed", len(HyphaStorage), "hyphae")
|
||||||
|
|
||||||
|
history.Start(WikiDir)
|
||||||
|
|
||||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(WikiDir+"/static"))))
|
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(WikiDir+"/static"))))
|
||||||
// See http_readers.go for /page/, /text/, /binary/.
|
// See http_readers.go for /page/, /text/, /binary/, /history/.
|
||||||
// See http_mutators.go for /upload-binary/, /upload-text/, /edit/.
|
// See http_mutators.go for /upload-binary/, /upload-text/, /edit/.
|
||||||
http.HandleFunc("/list", handlerList)
|
http.HandleFunc("/list", handlerList)
|
||||||
http.HandleFunc("/reindex", handlerReindex)
|
http.HandleFunc("/reindex", handlerReindex)
|
||||||
|
http.HandleFunc("/random", handlerRandom)
|
||||||
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) {
|
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) {
|
||||||
http.ServeFile(w, rq, WikiDir+"/static/favicon.ico")
|
http.ServeFile(w, rq, WikiDir+"/static/favicon.ico")
|
||||||
})
|
})
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit a22fcac89f10ad1e1db77d765788dfd8966cbb36
|
Subproject commit bdaaab62574023487610d608d1e9f2f351707a7f
|
14
templates/http_mutators.qtpl
Normal file
14
templates/http_mutators.qtpl
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
{% func EditHTML(hyphaName, textAreaFill, warning string) %}
|
||||||
|
<main>
|
||||||
|
<h1>Edit {%s hyphaName %}</h1>
|
||||||
|
{%s= warning %}
|
||||||
|
<form method="post" class="upload-text-form"
|
||||||
|
action="/upload-text/{%s hyphaName %}">
|
||||||
|
<textarea name="text">{%s textAreaFill %}</textarea>
|
||||||
|
<br/>
|
||||||
|
<input type="submit"/>
|
||||||
|
<a href="/page/{%s hyphaName %}">Cancel</a>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
{% endfunc %}
|
83
templates/http_mutators.qtpl.go
Normal file
83
templates/http_mutators.qtpl.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// Code generated by qtc from "http_mutators.qtpl". DO NOT EDIT.
|
||||||
|
// See https://github.com/valyala/quicktemplate for details.
|
||||||
|
|
||||||
|
//line templates/http_mutators.qtpl:2
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//line templates/http_mutators.qtpl:2
|
||||||
|
import (
|
||||||
|
qtio422016 "io"
|
||||||
|
|
||||||
|
qt422016 "github.com/valyala/quicktemplate"
|
||||||
|
)
|
||||||
|
|
||||||
|
//line templates/http_mutators.qtpl:2
|
||||||
|
var (
|
||||||
|
_ = qtio422016.Copy
|
||||||
|
_ = qt422016.AcquireByteBuffer
|
||||||
|
)
|
||||||
|
|
||||||
|
//line templates/http_mutators.qtpl:2
|
||||||
|
func StreamEditHTML(qw422016 *qt422016.Writer, hyphaName, textAreaFill, warning string) {
|
||||||
|
//line templates/http_mutators.qtpl:2
|
||||||
|
qw422016.N().S(`
|
||||||
|
<main>
|
||||||
|
<h1>Edit `)
|
||||||
|
//line templates/http_mutators.qtpl:4
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_mutators.qtpl:4
|
||||||
|
qw422016.N().S(`</h1>
|
||||||
|
`)
|
||||||
|
//line templates/http_mutators.qtpl:5
|
||||||
|
qw422016.N().S(warning)
|
||||||
|
//line templates/http_mutators.qtpl:5
|
||||||
|
qw422016.N().S(`
|
||||||
|
<form method="post" class="upload-text-form"
|
||||||
|
action="/upload-text/`)
|
||||||
|
//line templates/http_mutators.qtpl:7
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_mutators.qtpl:7
|
||||||
|
qw422016.N().S(`">
|
||||||
|
<textarea name="text">`)
|
||||||
|
//line templates/http_mutators.qtpl:8
|
||||||
|
qw422016.E().S(textAreaFill)
|
||||||
|
//line templates/http_mutators.qtpl:8
|
||||||
|
qw422016.N().S(`</textarea>
|
||||||
|
<br/>
|
||||||
|
<input type="submit"/>
|
||||||
|
<a href="/page/`)
|
||||||
|
//line templates/http_mutators.qtpl:11
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_mutators.qtpl:11
|
||||||
|
qw422016.N().S(`">Cancel</a>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
`)
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
func WriteEditHTML(qq422016 qtio422016.Writer, hyphaName, textAreaFill, warning string) {
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
StreamEditHTML(qw422016, hyphaName, textAreaFill, warning)
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
func EditHTML(hyphaName, textAreaFill, warning string) string {
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
WriteEditHTML(qb422016, hyphaName, textAreaFill, warning)
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
return qs422016
|
||||||
|
//line templates/http_mutators.qtpl:14
|
||||||
|
}
|
81
templates/http_readers.qtpl
Normal file
81
templates/http_readers.qtpl
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{% func HistoryHTML(hyphaName, tbody string) %}
|
||||||
|
<main>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/page/{%s hyphaName %}">Hypha</a></li>
|
||||||
|
<li><a href="/edit/{%s hyphaName %}">Edit</a></li>
|
||||||
|
<li><a href="/text/{%s hyphaName %}">Raw text</a></li>
|
||||||
|
<li><b>History</b></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Hash</th>
|
||||||
|
<th>Message</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{%s= tbody %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
{% endfunc %}
|
||||||
|
|
||||||
|
{% func RevisionHTML(hyphaName, naviTitle, contents, tree, revHash string) %}
|
||||||
|
<main>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/page/{%s hyphaName %}">Hypha</a></li>
|
||||||
|
<li><a href="/edit/{%s hyphaName %}">Edit</a></li>
|
||||||
|
<li><a href="/text/{%s hyphaName %}">Raw text</a></li>
|
||||||
|
<li><a href="/history/{%s hyphaName %}">History</a></li>
|
||||||
|
<li><b>{%s revHash %}</b></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<article>
|
||||||
|
<p>Please note that viewing binary parts of hyphae is not supported in history for now.</p>
|
||||||
|
{%s= naviTitle %}
|
||||||
|
{%s= contents %}
|
||||||
|
</article>
|
||||||
|
<hr/>
|
||||||
|
<aside>
|
||||||
|
{%s= tree %}
|
||||||
|
</aside>
|
||||||
|
</main>
|
||||||
|
{% endfunc %}
|
||||||
|
|
||||||
|
If `contents` == "", a helpful message is shown instead.
|
||||||
|
{% func PageHTML(hyphaName, naviTitle, contents, tree string) %}
|
||||||
|
<main>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><b>Hypha</b></li>
|
||||||
|
<li><a href="/edit/{%s hyphaName %}">Edit</a></li>
|
||||||
|
<li><a href="/text/{%s hyphaName %}">Raw text</a></li>
|
||||||
|
<li><a href="/history/{%s hyphaName %}">History</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<article>
|
||||||
|
{%s= naviTitle %}
|
||||||
|
{% if contents == "" %}
|
||||||
|
<p>This hypha has no text. Why not <a href="/edit/{%s hyphaName %}">create it</a>?</p>
|
||||||
|
{% else %}
|
||||||
|
{%s= contents %}
|
||||||
|
{% endif %}
|
||||||
|
</article>
|
||||||
|
<hr/>
|
||||||
|
<form action="/upload-binary/{%s hyphaName %}"
|
||||||
|
method="post" enctype="multipart/form-data">
|
||||||
|
<label for="upload-binary__input">Upload new binary part</label>
|
||||||
|
<br>
|
||||||
|
<input type="file" id="upload-binary__input" name="binary"/>
|
||||||
|
<input type="submit"/>
|
||||||
|
</form>
|
||||||
|
<hr/>
|
||||||
|
<aside>
|
||||||
|
{%s= tree %}
|
||||||
|
</aside>
|
||||||
|
</main>
|
||||||
|
{% endfunc %}
|
286
templates/http_readers.qtpl.go
Normal file
286
templates/http_readers.qtpl.go
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
// Code generated by qtc from "http_readers.qtpl". DO NOT EDIT.
|
||||||
|
// See https://github.com/valyala/quicktemplate for details.
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:1
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:1
|
||||||
|
import (
|
||||||
|
qtio422016 "io"
|
||||||
|
|
||||||
|
qt422016 "github.com/valyala/quicktemplate"
|
||||||
|
)
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:1
|
||||||
|
var (
|
||||||
|
_ = qtio422016.Copy
|
||||||
|
_ = qt422016.AcquireByteBuffer
|
||||||
|
)
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:1
|
||||||
|
func StreamHistoryHTML(qw422016 *qt422016.Writer, hyphaName, tbody string) {
|
||||||
|
//line templates/http_readers.qtpl:1
|
||||||
|
qw422016.N().S(`
|
||||||
|
<main>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/page/`)
|
||||||
|
//line templates/http_readers.qtpl:5
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:5
|
||||||
|
qw422016.N().S(`">Hypha</a></li>
|
||||||
|
<li><a href="/edit/`)
|
||||||
|
//line templates/http_readers.qtpl:6
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:6
|
||||||
|
qw422016.N().S(`">Edit</a></li>
|
||||||
|
<li><a href="/text/`)
|
||||||
|
//line templates/http_readers.qtpl:7
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:7
|
||||||
|
qw422016.N().S(`">Raw text</a></li>
|
||||||
|
<li><b>History</b></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Hash</th>
|
||||||
|
<th>Message</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:20
|
||||||
|
qw422016.N().S(tbody)
|
||||||
|
//line templates/http_readers.qtpl:20
|
||||||
|
qw422016.N().S(`
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
func WriteHistoryHTML(qq422016 qtio422016.Writer, hyphaName, tbody string) {
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
StreamHistoryHTML(qw422016, hyphaName, tbody)
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
func HistoryHTML(hyphaName, tbody string) string {
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
WriteHistoryHTML(qb422016, hyphaName, tbody)
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
return qs422016
|
||||||
|
//line templates/http_readers.qtpl:24
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:26
|
||||||
|
func StreamRevisionHTML(qw422016 *qt422016.Writer, hyphaName, naviTitle, contents, tree, revHash string) {
|
||||||
|
//line templates/http_readers.qtpl:26
|
||||||
|
qw422016.N().S(`
|
||||||
|
<main>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/page/`)
|
||||||
|
//line templates/http_readers.qtpl:30
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:30
|
||||||
|
qw422016.N().S(`">Hypha</a></li>
|
||||||
|
<li><a href="/edit/`)
|
||||||
|
//line templates/http_readers.qtpl:31
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:31
|
||||||
|
qw422016.N().S(`">Edit</a></li>
|
||||||
|
<li><a href="/text/`)
|
||||||
|
//line templates/http_readers.qtpl:32
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:32
|
||||||
|
qw422016.N().S(`">Raw text</a></li>
|
||||||
|
<li><a href="/history/`)
|
||||||
|
//line templates/http_readers.qtpl:33
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:33
|
||||||
|
qw422016.N().S(`">History</a></li>
|
||||||
|
<li><b>`)
|
||||||
|
//line templates/http_readers.qtpl:34
|
||||||
|
qw422016.E().S(revHash)
|
||||||
|
//line templates/http_readers.qtpl:34
|
||||||
|
qw422016.N().S(`</b></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<article>
|
||||||
|
<p>Please note that viewing binary parts of hyphae is not supported in history for now.</p>
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:39
|
||||||
|
qw422016.N().S(naviTitle)
|
||||||
|
//line templates/http_readers.qtpl:39
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:40
|
||||||
|
qw422016.N().S(contents)
|
||||||
|
//line templates/http_readers.qtpl:40
|
||||||
|
qw422016.N().S(`
|
||||||
|
</article>
|
||||||
|
<hr/>
|
||||||
|
<aside>
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:44
|
||||||
|
qw422016.N().S(tree)
|
||||||
|
//line templates/http_readers.qtpl:44
|
||||||
|
qw422016.N().S(`
|
||||||
|
</aside>
|
||||||
|
</main>
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
func WriteRevisionHTML(qq422016 qtio422016.Writer, hyphaName, naviTitle, contents, tree, revHash string) {
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
StreamRevisionHTML(qw422016, hyphaName, naviTitle, contents, tree, revHash)
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
func RevisionHTML(hyphaName, naviTitle, contents, tree, revHash string) string {
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
WriteRevisionHTML(qb422016, hyphaName, naviTitle, contents, tree, revHash)
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
return qs422016
|
||||||
|
//line templates/http_readers.qtpl:47
|
||||||
|
}
|
||||||
|
|
||||||
|
// If `contents` == "", a helpful message is shown instead.
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:50
|
||||||
|
func StreamPageHTML(qw422016 *qt422016.Writer, hyphaName, naviTitle, contents, tree string) {
|
||||||
|
//line templates/http_readers.qtpl:50
|
||||||
|
qw422016.N().S(`
|
||||||
|
<main>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><b>Hypha</b></li>
|
||||||
|
<li><a href="/edit/`)
|
||||||
|
//line templates/http_readers.qtpl:55
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:55
|
||||||
|
qw422016.N().S(`">Edit</a></li>
|
||||||
|
<li><a href="/text/`)
|
||||||
|
//line templates/http_readers.qtpl:56
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:56
|
||||||
|
qw422016.N().S(`">Raw text</a></li>
|
||||||
|
<li><a href="/history/`)
|
||||||
|
//line templates/http_readers.qtpl:57
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:57
|
||||||
|
qw422016.N().S(`">History</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<article>
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:61
|
||||||
|
qw422016.N().S(naviTitle)
|
||||||
|
//line templates/http_readers.qtpl:61
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:62
|
||||||
|
if contents == "" {
|
||||||
|
//line templates/http_readers.qtpl:62
|
||||||
|
qw422016.N().S(`
|
||||||
|
<p>This hypha has no text. Why not <a href="/edit/`)
|
||||||
|
//line templates/http_readers.qtpl:63
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:63
|
||||||
|
qw422016.N().S(`">create it</a>?</p>
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:64
|
||||||
|
} else {
|
||||||
|
//line templates/http_readers.qtpl:64
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:65
|
||||||
|
qw422016.N().S(contents)
|
||||||
|
//line templates/http_readers.qtpl:65
|
||||||
|
qw422016.N().S(`
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:66
|
||||||
|
}
|
||||||
|
//line templates/http_readers.qtpl:66
|
||||||
|
qw422016.N().S(`
|
||||||
|
</article>
|
||||||
|
<hr/>
|
||||||
|
<form action="/upload-binary/`)
|
||||||
|
//line templates/http_readers.qtpl:69
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_readers.qtpl:69
|
||||||
|
qw422016.N().S(`"
|
||||||
|
method="post" enctype="multipart/form-data">
|
||||||
|
<label for="upload-binary__input">Upload new binary part</label>
|
||||||
|
<br>
|
||||||
|
<input type="file" id="upload-binary__input" name="binary"/>
|
||||||
|
<input type="submit"/>
|
||||||
|
</form>
|
||||||
|
<hr/>
|
||||||
|
<aside>
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:78
|
||||||
|
qw422016.N().S(tree)
|
||||||
|
//line templates/http_readers.qtpl:78
|
||||||
|
qw422016.N().S(`
|
||||||
|
</aside>
|
||||||
|
</main>
|
||||||
|
`)
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
func WritePageHTML(qq422016 qtio422016.Writer, hyphaName, naviTitle, contents, tree string) {
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
StreamPageHTML(qw422016, hyphaName, naviTitle, contents, tree)
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
func PageHTML(hyphaName, naviTitle, contents, tree string) string {
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
WritePageHTML(qb422016, hyphaName, naviTitle, contents, tree)
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
return qs422016
|
||||||
|
//line templates/http_readers.qtpl:81
|
||||||
|
}
|
42
templates/http_stuff.qtpl
Normal file
42
templates/http_stuff.qtpl
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{% func BaseHTML(title, body string) %}
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/common.css">
|
||||||
|
<title>{%s title %}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{%s= body %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{% endfunc %}
|
||||||
|
|
||||||
|
{% func HyphaListHTML(tbody string, pageCount int) %}
|
||||||
|
<main>
|
||||||
|
<h1>List of hyphae</h1>
|
||||||
|
<p>This wiki has {%d pageCount %} hyphae.</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Full name</th>
|
||||||
|
<th>Binary part type</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{%s= tbody %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
{% endfunc %}
|
||||||
|
|
||||||
|
{% func HyphaListRowHTML(hyphaName, binaryMime string, binaryPresent bool) %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="/page/{%s hyphaName %}">{%s hyphaName %}</a></td>
|
||||||
|
{% if binaryPresent %}
|
||||||
|
<td>{%s binaryMime %}</td>
|
||||||
|
{% else %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endfunc %}
|
194
templates/http_stuff.qtpl.go
Normal file
194
templates/http_stuff.qtpl.go
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
// Code generated by qtc from "http_stuff.qtpl". DO NOT EDIT.
|
||||||
|
// See https://github.com/valyala/quicktemplate for details.
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:1
|
||||||
|
package templates
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:1
|
||||||
|
import (
|
||||||
|
qtio422016 "io"
|
||||||
|
|
||||||
|
qt422016 "github.com/valyala/quicktemplate"
|
||||||
|
)
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:1
|
||||||
|
var (
|
||||||
|
_ = qtio422016.Copy
|
||||||
|
_ = qt422016.AcquireByteBuffer
|
||||||
|
)
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:1
|
||||||
|
func StreamBaseHTML(qw422016 *qt422016.Writer, title, body string) {
|
||||||
|
//line templates/http_stuff.qtpl:1
|
||||||
|
qw422016.N().S(`
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/common.css">
|
||||||
|
<title>`)
|
||||||
|
//line templates/http_stuff.qtpl:7
|
||||||
|
qw422016.E().S(title)
|
||||||
|
//line templates/http_stuff.qtpl:7
|
||||||
|
qw422016.N().S(`</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
`)
|
||||||
|
//line templates/http_stuff.qtpl:10
|
||||||
|
qw422016.N().S(body)
|
||||||
|
//line templates/http_stuff.qtpl:10
|
||||||
|
qw422016.N().S(`
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
func WriteBaseHTML(qq422016 qtio422016.Writer, title, body string) {
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
StreamBaseHTML(qw422016, title, body)
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
func BaseHTML(title, body string) string {
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
WriteBaseHTML(qb422016, title, body)
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
return qs422016
|
||||||
|
//line templates/http_stuff.qtpl:13
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:15
|
||||||
|
func StreamHyphaListHTML(qw422016 *qt422016.Writer, tbody string, pageCount int) {
|
||||||
|
//line templates/http_stuff.qtpl:15
|
||||||
|
qw422016.N().S(`
|
||||||
|
<main>
|
||||||
|
<h1>List of hyphae</h1>
|
||||||
|
<p>This wiki has `)
|
||||||
|
//line templates/http_stuff.qtpl:18
|
||||||
|
qw422016.N().D(pageCount)
|
||||||
|
//line templates/http_stuff.qtpl:18
|
||||||
|
qw422016.N().S(` hyphae.</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Full name</th>
|
||||||
|
<th>Binary part type</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
`)
|
||||||
|
//line templates/http_stuff.qtpl:27
|
||||||
|
qw422016.N().S(tbody)
|
||||||
|
//line templates/http_stuff.qtpl:27
|
||||||
|
qw422016.N().S(`
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
`)
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
func WriteHyphaListHTML(qq422016 qtio422016.Writer, tbody string, pageCount int) {
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
StreamHyphaListHTML(qw422016, tbody, pageCount)
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
func HyphaListHTML(tbody string, pageCount int) string {
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
WriteHyphaListHTML(qb422016, tbody, pageCount)
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
return qs422016
|
||||||
|
//line templates/http_stuff.qtpl:31
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:33
|
||||||
|
func StreamHyphaListRowHTML(qw422016 *qt422016.Writer, hyphaName, binaryMime string, binaryPresent bool) {
|
||||||
|
//line templates/http_stuff.qtpl:33
|
||||||
|
qw422016.N().S(`
|
||||||
|
<tr>
|
||||||
|
<td><a href="/page/`)
|
||||||
|
//line templates/http_stuff.qtpl:35
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_stuff.qtpl:35
|
||||||
|
qw422016.N().S(`">`)
|
||||||
|
//line templates/http_stuff.qtpl:35
|
||||||
|
qw422016.E().S(hyphaName)
|
||||||
|
//line templates/http_stuff.qtpl:35
|
||||||
|
qw422016.N().S(`</a></td>
|
||||||
|
`)
|
||||||
|
//line templates/http_stuff.qtpl:36
|
||||||
|
if binaryPresent {
|
||||||
|
//line templates/http_stuff.qtpl:36
|
||||||
|
qw422016.N().S(`
|
||||||
|
<td>`)
|
||||||
|
//line templates/http_stuff.qtpl:37
|
||||||
|
qw422016.E().S(binaryMime)
|
||||||
|
//line templates/http_stuff.qtpl:37
|
||||||
|
qw422016.N().S(`</td>
|
||||||
|
`)
|
||||||
|
//line templates/http_stuff.qtpl:38
|
||||||
|
} else {
|
||||||
|
//line templates/http_stuff.qtpl:38
|
||||||
|
qw422016.N().S(`
|
||||||
|
<td></td>
|
||||||
|
`)
|
||||||
|
//line templates/http_stuff.qtpl:40
|
||||||
|
}
|
||||||
|
//line templates/http_stuff.qtpl:40
|
||||||
|
qw422016.N().S(`
|
||||||
|
</tr>
|
||||||
|
`)
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
func WriteHyphaListRowHTML(qq422016 qtio422016.Writer, hyphaName, binaryMime string, binaryPresent bool) {
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
StreamHyphaListRowHTML(qw422016, hyphaName, binaryMime, binaryPresent)
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
}
|
||||||
|
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
func HyphaListRowHTML(hyphaName, binaryMime string, binaryPresent bool) string {
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
WriteHyphaListRowHTML(qb422016, hyphaName, binaryMime, binaryPresent)
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
return qs422016
|
||||||
|
//line templates/http_stuff.qtpl:42
|
||||||
|
}
|
92
tree/tree.go
Normal file
92
tree/tree.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package tree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// If Name == "", the tree is empty.
|
||||||
|
type tree struct {
|
||||||
|
name string
|
||||||
|
siblings []string
|
||||||
|
descendants []*tree
|
||||||
|
root bool
|
||||||
|
hyphaIterator func(func(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TreeAsHtml generates a tree for `hyphaName`. `hyphaStorage` has this type because package `tree` has no access to `HyphaData` data type. One day it shall have it, I guess.
|
||||||
|
func TreeAsHtml(hyphaName string, hyphaIterator func(func(string))) string {
|
||||||
|
t := &tree{name: hyphaName, root: true, hyphaIterator: hyphaIterator}
|
||||||
|
t.fill()
|
||||||
|
return t.asHtml()
|
||||||
|
}
|
||||||
|
|
||||||
|
// subtree adds a descendant tree to `t` and returns that tree.
|
||||||
|
func (t *tree) fork(descendantName string) *tree {
|
||||||
|
subt := &tree{
|
||||||
|
name: descendantName,
|
||||||
|
root: false,
|
||||||
|
hyphaIterator: t.hyphaIterator,
|
||||||
|
}
|
||||||
|
t.descendants = append(t.descendants, subt)
|
||||||
|
return subt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares names and does something with them, may generate a subtree.
|
||||||
|
func (t *tree) compareNamesAndAppend(name2 string) {
|
||||||
|
switch {
|
||||||
|
case t.name == name2:
|
||||||
|
case t.root && path.Dir(t.name) == path.Dir(name2):
|
||||||
|
t.siblings = append(t.siblings, name2)
|
||||||
|
case t.name == path.Dir(name2):
|
||||||
|
t.fork(name2).fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fills t.siblings and t.descendants, sorts them and does the same to the descendants.
|
||||||
|
func (t *tree) fill() {
|
||||||
|
t.hyphaIterator(func(hyphaName string) {
|
||||||
|
t.compareNamesAndAppend(hyphaName)
|
||||||
|
})
|
||||||
|
sort.Strings(t.siblings)
|
||||||
|
sort.Slice(t.descendants, func(i, j int) bool {
|
||||||
|
return t.descendants[i].name < t.descendants[j].name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// asHtml returns HTML representation of a tree.
|
||||||
|
// It applies itself recursively on the tree's children.
|
||||||
|
func (t *tree) asHtml() (html string) {
|
||||||
|
if t.root {
|
||||||
|
html += navitreeEntry(t.name, "navitree__pagename")
|
||||||
|
} else {
|
||||||
|
html += navitreeEntry(t.name, "navitree__name")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, subtree := range t.descendants {
|
||||||
|
html += subtree.asHtml()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.root {
|
||||||
|
for _, siblingName := range t.siblings {
|
||||||
|
html += navitreeEntry(siblingName, "navitree__sibling")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<ul class="navitree__node">` + html + `</ul>`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip hypha name from all ancestor names, replace _ with spaces, title case
|
||||||
|
func beautifulName(uglyName string) string {
|
||||||
|
return strings.Title(strings.ReplaceAll(path.Base(uglyName), "_", " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// navitreeEntry is a small utility function that makes generating html easier.
|
||||||
|
func navitreeEntry(name, class string) string {
|
||||||
|
return fmt.Sprintf(`<li class="navitree__entry %s">
|
||||||
|
<a class="navitree__link" href="/page/%s">%s</a>
|
||||||
|
</li>
|
||||||
|
`, class, name, beautifulName(name))
|
||||||
|
}
|
27
util/util.go
Normal file
27
util/util.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var WikiDir string
|
||||||
|
|
||||||
|
// ShorterPath is used by handlerList to display shorter path to the files. It simply strips WikiDir.
|
||||||
|
func ShorterPath(path string) string {
|
||||||
|
if strings.HasPrefix(path, WikiDir) {
|
||||||
|
tmp := strings.TrimPrefix(path, WikiDir)
|
||||||
|
if tmp == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return tmp[1:]
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP200Page wraps some frequently used things for successful 200 responses.
|
||||||
|
func HTTP200Page(w http.ResponseWriter, page string) {
|
||||||
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(page))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user