From 6992312c466238ed39a57bbd7f1b5724e26c6fd7 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Thu, 6 Aug 2020 01:19:14 +0500 Subject: [PATCH 01/15] Implement tree generation --- README.md | 11 +++++- http_readers.go | 12 +++++-- main.go | 7 ++++ tree/tree.go | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 tree/tree.go diff --git a/README.md b/README.md index 64dc545..c25b673 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,16 @@ # mycorrhiza wiki A wiki engine inspired by fungi. Not production-ready. -Current version: 0.7 (or more?) +This branch is devoted to version 0.8. +* [ ] Tree generation + * [x] Basic generation + * [ ] Generation that takes non-existent hyphae into account¹ +* [ ] History + * [ ] Saving all changes to git + * [ ] Make it possible to see any previous version + * [ ] A nice UI for that + +¹ Current algorithm won't detect `a/b/c` as a child of `a` if `a/b` does not exist. ## Current features * Edit pages through html forms diff --git a/http_readers.go b/http_readers.go index f00673d..fab83ee 100644 --- a/http_readers.go +++ b/http_readers.go @@ -8,6 +8,7 @@ import ( "os" "github.com/bouncepaw/mycorrhiza/gemtext" + "github.com/bouncepaw/mycorrhiza/tree" ) func init() { @@ -70,7 +71,7 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) { %[2]s %[3]s -
+
@@ -78,8 +79,15 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) {
+
+ -`, hyphaName, naviTitle(hyphaName), contents) +`, hyphaName, + naviTitle(hyphaName), + contents, + tree.TreeAsHtml(hyphaName, IterateHyphaNamesWith)) w.Header().Set("Content-Type", "text/html;charset=utf-8") w.WriteHeader(http.StatusOK) w.Write([]byte(base(hyphaName, form))) diff --git a/main.go b/main.go index 7d08e0b..e6b1ede 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,13 @@ var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%]+`) // HyphaStorage is a mapping between canonical hypha names and their meta information. var HyphaStorage = make(map[string]*HyphaData) +// IterateHyphaNamesWith is a closure to be passed to subpackages to let them iterate all hypha names read-only. +func IterateHyphaNamesWith(f func(string)) { + for hyphaName, _ := range HyphaStorage { + f(hyphaName) + } +} + // HttpErr is used by many handlers to signal errors in a compact way. func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) { log.Println(errMsg, "for", name) diff --git a/tree/tree.go b/tree/tree.go new file mode 100644 index 0000000..d7e8c45 --- /dev/null +++ b/tree/tree.go @@ -0,0 +1,89 @@ +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 { + for _, siblingName := range t.siblings { + html += navitreeEntry(siblingName, "navitree__sibling") + } + html += navitreeEntry(t.name, "navitree__pagename") + } else { + html += navitreeEntry(t.name, "navitree__name") + } + + for _, subtree := range t.descendants { + html += subtree.asHtml() + } + + return `` +} + +// 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(`
  • + %s +
  • +`, class, name, beautifulName(name)) +} From 37ae7d6bc1d50027011416ea815790da200fd926 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Sun, 9 Aug 2020 01:10:28 +0500 Subject: [PATCH 02/15] Test git capabilities --- go.mod | 1 + go.sum | 59 +++++++++++++++++++++++++ history/history.go | 106 +++++++++++++++++++++++++++++++++++++++++++++ http_mutators.go | 3 ++ main.go | 29 +++++++++++++ metarrhiza | 2 +- 6 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 history/history.go diff --git a/go.mod b/go.mod index d4ae430..52eea2b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/bouncepaw/mycorrhiza go 1.14 require ( + github.com/go-git/go-git/v5 v5.1.0 golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6 // indirect ) diff --git a/go.sum b/go.sum index 12c582a..594e00c 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,51 @@ +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git v1.0.0 h1:YcN9iDGDoXuIw0vHls6rINwV416HYa0EB2X+RBsyYp4= +github.com/go-git/go-git v4.7.0+incompatible h1:+W9rgGY4DOKKdX2x6HxSR7HNeTxqiKrOvKnuittYVdA= +github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk= +github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= +github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -8,13 +53,20 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB 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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= 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-20190221075227-b4e8571b14e0/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-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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= @@ -23,3 +75,10 @@ golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc 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= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/history/history.go b/history/history.go new file mode 100644 index 0000000..f905dd6 --- /dev/null +++ b/history/history.go @@ -0,0 +1,106 @@ +package history + +import ( + "fmt" + "log" + "time" + + "github.com/go-git/go-git/v5" + // "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" +) + +/* +// 10 should be enough +const ShortHashLength = 10 + +type EditType int + +const ( + TypeRename EditType = iota + TypeDelete + TypeEditText + TypeEditBinary +) + +type Revision struct { + ShortHash [ShortHashLength]byte + Type EditType +}*/ + +var WikiRepo *git.Repository +var Worktree *git.Worktree + +func Start(wikiDir string) { + ry, err := git.PlainOpen(wikiDir) + if err != nil { + log.Fatal(err) + } + WikiRepo = ry + Worktree, err = WikiRepo.Worktree() + if err != nil { + log.Fatal(err) + } + log.Println("Wiki repository found") +} + +func Stage(path string) error { + var err error + _, err = Worktree.Add(path) + if err != nil { + log.Println(err) + } + return err +} + +func CommitTest() { + opts := &git.CommitOptions{ + All: false, + Author: &object.Signature{ + Name: "wikimind", + Email: "wikimind@thiswiki", + When: time.Now(), + }, + } + err := opts.Validate(WikiRepo) + if err != nil { + log.Fatal(err) + } + _, err = Worktree.Commit("This is a test commit", opts) + if err != nil { + log.Fatal(err) + } + log.Println("Made a test commit") +} + +func StatusTable() (html string) { + status, err := Worktree.Status() + if err != nil { + log.Fatal(err) + } + for path, stat := range status { + html += fmt.Sprintf(` + + %s + %v + `, path, stat) + } + return `` + html + `
    ` +} + +func CommitsTable() (html string) { + commitIter, err := WikiRepo.CommitObjects() + if err != nil { + log.Fatal(err) + } + err = commitIter.ForEach(func(commit *object.Commit) error { + html += fmt.Sprintf(` + + %v + %v + %s + `, commit.Hash, commit.Author, commit.Message) + return nil + }) + return `` + html + `
    ` +} diff --git a/http_mutators.go b/http_mutators.go index ca8cdd1..d67976a 100644 --- a/http_mutators.go +++ b/http_mutators.go @@ -7,6 +7,8 @@ import ( "net/http" "os" "path/filepath" + + "github.com/bouncepaw/mycorrhiza/history" ) func init() { @@ -91,6 +93,7 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) { hyphaData.textType = TextGemini hyphaData.textPath = fullPath } + history.Stage(shorterPath(fullPath)) http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther) } diff --git a/main.go b/main.go index e6b1ede..84d4943 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,8 @@ import ( "path/filepath" "regexp" "strings" + + "github.com/bouncepaw/mycorrhiza/history" ) // WikiDir is a rooted path to the wiki storage directory. @@ -111,6 +113,28 @@ func handlerReindex(w http.ResponseWriter, rq *http.Request) { log.Println("Indexed", len(HyphaStorage), "hyphae") } +func handlerListCommits(w http.ResponseWriter, rq *http.Request) { + log.Println(rq.URL) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(history.CommitsTable())) +} + +func handlerStatus(w http.ResponseWriter, rq *http.Request) { + log.Println(rq.URL) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(history.StatusTable())) +} + +func handlerCommitTest(w http.ResponseWriter, rq *http.Request) { + log.Println(rq.URL) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + w.WriteHeader(http.StatusOK) + history.CommitTest() + w.Write([]byte("if you are here, a commit has been done")) +} + func main() { log.Println("Running MycorrhizaWiki β") @@ -124,11 +148,16 @@ func main() { Index(WikiDir) log.Println("Indexed", len(HyphaStorage), "hyphae") + history.Start(WikiDir) + http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(WikiDir+"/static")))) // See http_readers.go for /page/, /text/, /binary/. // See http_mutators.go for /upload-binary/, /upload-text/, /edit/. http.HandleFunc("/list", handlerList) http.HandleFunc("/reindex", handlerReindex) + http.HandleFunc("/git/list", handlerListCommits) + http.HandleFunc("/git/status", handlerStatus) + http.HandleFunc("/git/commit", handlerCommitTest) http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) { http.ServeFile(w, rq, WikiDir+"/static/favicon.ico") }) diff --git a/metarrhiza b/metarrhiza index a22fcac..b0157a1 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit a22fcac89f10ad1e1db77d765788dfd8966cbb36 +Subproject commit b0157a17f535e99e485c8d3e85f66995754badcf From b52419152a9a5e8e44d141645a0bcbbe221b0331 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Mon, 10 Aug 2020 00:33:47 +0500 Subject: [PATCH 03/15] Start creating history operations methods --- history/history.go | 87 ++++++++++++++++++++++++++++++++++------------ http_mutators.go | 5 ++- main.go | 13 +++---- util/util.go | 19 ++++++++++ 4 files changed, 90 insertions(+), 34 deletions(-) create mode 100644 util/util.go diff --git a/history/history.go b/history/history.go index f905dd6..dbe3010 100644 --- a/history/history.go +++ b/history/history.go @@ -5,6 +5,7 @@ import ( "log" "time" + "github.com/bouncepaw/mycorrhiza/util" "github.com/go-git/go-git/v5" // "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" @@ -44,32 +45,74 @@ func Start(wikiDir string) { log.Println("Wiki repository found") } -func Stage(path string) error { - var err error - _, err = Worktree.Add(path) - if err != nil { - log.Println(err) +type HistoryOp struct { + Errs []error + userMsg string + signature *object.Signature + isDone bool +} + +// WithFiles stages all passed `paths`. Paths can be rooted or not. +func (hop *HistoryOp) WithFiles(paths ...string) *HistoryOp { + for _, path := range paths { + if _, err := Worktree.Add(util.ShorterPath(path)); err != nil { + log.Println(err) + hop.Errs = append(hop.Errs, err) + } } - return err + return hop +} + +// WithUserMsg sets what user message will be used for the future commit. If it == "", a default one be used. If user messages are not supported for this one type of history operation, this user message will be dropped. If user messages exceeds one line, it is stripped down. +func (hop *HistoryOp) WithUserMsg(userMsg string) *HistoryOp { + // Isn't it too imperative? + var firstLine string + for _, ch := range userMsg { + if ch == '\r' || ch == '\n' { + break + } + firstLine += string(ch) + } + hop.userMsg = userMsg + 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.signature = &object.Signature{ + Name: username, + // A fake email, why not + Email: username + "@mycorrhiza", + When: time.Now(), + } + return hop +} + +// Apply applies history operation by doing the commit. You can't apply the same operation more than once. +func (hop *HistoryOp) Apply() *HistoryOp { + if !hop.isDone { + opts := &git.CommitOptions{ + All: false, + Author: hop.signature, + } + err := opts.Validate(WikiRepo) + if err != nil { + hop.Errs = append(hop.Errs, err) + } + // TODO: work on this section: + _, err = Worktree.Commit(hop.userMsg, opts) + if err != nil { + hop.Errs = append(hop.Errs, err) + } + } + return hop } func CommitTest() { - opts := &git.CommitOptions{ - All: false, - Author: &object.Signature{ - Name: "wikimind", - Email: "wikimind@thiswiki", - When: time.Now(), - }, - } - err := opts.Validate(WikiRepo) - if err != nil { - log.Fatal(err) - } - _, err = Worktree.Commit("This is a test commit", opts) - if err != nil { - log.Fatal(err) - } + (&HistoryOp{}). + WithUserMsg("This is a test commit"). + WithSignature("wikimind"). + Apply() log.Println("Made a test commit") } diff --git a/http_mutators.go b/http_mutators.go index d67976a..71de895 100644 --- a/http_mutators.go +++ b/http_mutators.go @@ -7,8 +7,7 @@ import ( "net/http" "os" "path/filepath" - - "github.com/bouncepaw/mycorrhiza/history" + // "github.com/bouncepaw/mycorrhiza/history" ) func init() { @@ -93,7 +92,7 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) { hyphaData.textType = TextGemini hyphaData.textPath = fullPath } - history.Stage(shorterPath(fullPath)) + // TODO: make history changes http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther) } diff --git a/main.go b/main.go index 84d4943..55c32a6 100644 --- a/main.go +++ b/main.go @@ -7,9 +7,9 @@ import ( "os" "path/filepath" "regexp" - "strings" "github.com/bouncepaw/mycorrhiza/history" + "github.com/bouncepaw/mycorrhiza/util" ) // WikiDir is a rooted path to the wiki storage directory. @@ -38,14 +38,8 @@ func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) { 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:] -} +// shorterPath is used by handlerList to display shorter path to the files. It simply strips WikiDir. It was moved to util package, this is an alias. TODO: demolish. +var shorterPath = util.ShorterPath // Show all hyphae func handlerList(w http.ResponseWriter, rq *http.Request) { @@ -140,6 +134,7 @@ func main() { var err error WikiDir, err = filepath.Abs(os.Args[1]) + util.WikiDir = WikiDir if err != nil { log.Fatal(err) } diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..f45ef9a --- /dev/null +++ b/util/util.go @@ -0,0 +1,19 @@ +package util + +import ( + "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 +} From c03f8be5cdb9fa0996031f3cffa94f62de7d9158 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Tue, 11 Aug 2020 00:58:02 +0500 Subject: [PATCH 04/15] Make commits on text edits, commits on binary edits WIP --- history/history.go | 39 ++++++++++++++++++--------------------- http_mutators.go | 18 +++++++++++++++--- main.go | 9 --------- metarrhiza | 2 +- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/history/history.go b/history/history.go index dbe3010..3d9d081 100644 --- a/history/history.go +++ b/history/history.go @@ -11,24 +11,14 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" ) -/* -// 10 should be enough -const ShortHashLength = 10 - -type EditType int +type OpType int const ( - TypeRename EditType = iota - TypeDelete + TypeNone OpType = iota TypeEditText TypeEditBinary ) -type Revision struct { - ShortHash [ShortHashLength]byte - Type EditType -}*/ - var WikiRepo *git.Repository var Worktree *git.Worktree @@ -47,11 +37,20 @@ func Start(wikiDir string) { type HistoryOp struct { Errs []error + opType OpType userMsg string signature *object.Signature isDone bool } +func Operation(opType OpType) *HistoryOp { + hop := &HistoryOp{ + Errs: []error{}, + opType: opType, + } + return hop +} + // WithFiles stages all passed `paths`. Paths can be rooted or not. func (hop *HistoryOp) WithFiles(paths ...string) *HistoryOp { for _, path := range paths { @@ -63,8 +62,8 @@ func (hop *HistoryOp) WithFiles(paths ...string) *HistoryOp { return hop } -// WithUserMsg sets what user message will be used for the future commit. If it == "", a default one be used. If user messages are not supported for this one type of history operation, this user message will be dropped. If user messages exceeds one line, it is stripped down. -func (hop *HistoryOp) WithUserMsg(userMsg string) *HistoryOp { +// 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 { // Isn't it too imperative? var firstLine string for _, ch := range userMsg { @@ -73,7 +72,7 @@ func (hop *HistoryOp) WithUserMsg(userMsg string) *HistoryOp { } firstLine += string(ch) } - hop.userMsg = userMsg + hop.userMsg = firstLine return hop } @@ -108,12 +107,10 @@ func (hop *HistoryOp) Apply() *HistoryOp { return hop } -func CommitTest() { - (&HistoryOp{}). - WithUserMsg("This is a test commit"). - WithSignature("wikimind"). - Apply() - log.Println("Made a test commit") +func Rename(from, to string) error { + log.Println(util.ShorterPath(from), util.ShorterPath(to)) + _, err := Worktree.Move(util.ShorterPath(from), util.ShorterPath(to)) + return err } func StatusTable() (html string) { diff --git a/http_mutators.go b/http_mutators.go index 71de895..c943da3 100644 --- a/http_mutators.go +++ b/http_mutators.go @@ -7,7 +7,8 @@ import ( "net/http" "os" "path/filepath" - // "github.com/bouncepaw/mycorrhiza/history" + + "github.com/bouncepaw/mycorrhiza/history" ) func init() { @@ -92,7 +93,11 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) { hyphaData.textType = TextGemini hyphaData.textPath = fullPath } - // TODO: make history changes + history.Operation(history.TypeEditText). + WithFiles(fullPath). + WithMsg(fmt.Sprintf("Edit ‘%s’", hyphaName)). + WithSignature("anon"). + Apply() http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther) } @@ -141,13 +146,20 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) { } } else { if hyphaData.binaryPath != fullPath { - if err := os.Remove(hyphaData.binaryPath); err != nil { + if err := history.Rename(hyphaData.binaryPath, fullPath); err != nil { log.Println(err) + } else { + log.Println("Moved", hyphaData.binaryPath, "to", fullPath) } } hyphaData.binaryPath = fullPath hyphaData.binaryType = mimeType } log.Println("Written", len(data), "of binary data for", hyphaName, "to path", fullPath) + history.Operation(history.TypeEditText). + WithFiles(fullPath). + WithMsg(fmt.Sprintf("Upload binary part for ‘%s’ with type ‘%s’", hyphaName, mimeType.Mime())). + WithSignature("anon"). + Apply() http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther) } diff --git a/main.go b/main.go index 55c32a6..cab791b 100644 --- a/main.go +++ b/main.go @@ -121,14 +121,6 @@ func handlerStatus(w http.ResponseWriter, rq *http.Request) { w.Write([]byte(history.StatusTable())) } -func handlerCommitTest(w http.ResponseWriter, rq *http.Request) { - log.Println(rq.URL) - w.Header().Set("Content-Type", "text/html;charset=utf-8") - w.WriteHeader(http.StatusOK) - history.CommitTest() - w.Write([]byte("if you are here, a commit has been done")) -} - func main() { log.Println("Running MycorrhizaWiki β") @@ -152,7 +144,6 @@ func main() { http.HandleFunc("/reindex", handlerReindex) http.HandleFunc("/git/list", handlerListCommits) http.HandleFunc("/git/status", handlerStatus) - http.HandleFunc("/git/commit", handlerCommitTest) http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) { http.ServeFile(w, rq, WikiDir+"/static/favicon.ico") }) diff --git a/metarrhiza b/metarrhiza index b0157a1..30287be 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit b0157a17f535e99e485c8d3e85f66995754badcf +Subproject commit 30287be4496638e342878e2adf50e0a74ce71a5f From 60fcbdd30dd421a604bd0cbdf70c4ed5363622d8 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Wed, 19 Aug 2020 23:54:23 +0500 Subject: [PATCH 05/15] WIP history page --- data | 1 + go.mod | 6 +-- go.sum | 28 ++----------- history/history.go | 4 +- history/sh.go | 100 +++++++++++++++++++++++++++++++++++++++++++++ http_mutators.go | 12 +++--- http_readers.go | 44 ++++++++++++++++++++ main.go | 5 ++- metarrhiza | 2 +- tree/tree.go | 9 ++-- 10 files changed, 169 insertions(+), 42 deletions(-) create mode 160000 data create mode 100644 history/sh.go diff --git a/data b/data new file mode 160000 index 0000000..b2e7803 --- /dev/null +++ b/data @@ -0,0 +1 @@ +Subproject commit b2e78031973e4a6844d943333e823c0714e7a673 diff --git a/go.mod b/go.mod index 52eea2b..a66370d 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,4 @@ module github.com/bouncepaw/mycorrhiza go 1.14 -require ( - github.com/go-git/go-git/v5 v5.1.0 - golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect - golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6 // indirect -) +require github.com/go-git/go-git/v5 v5.1.0 diff --git a/go.sum b/go.sum index 594e00c..66994b3 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,6 @@ github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git v1.0.0 h1:YcN9iDGDoXuIw0vHls6rINwV416HYa0EB2X+RBsyYp4= -github.com/go-git/go-git v4.7.0+incompatible h1:+W9rgGY4DOKKdX2x6HxSR7HNeTxqiKrOvKnuittYVdA= github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk= github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM= @@ -40,41 +38,21 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/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-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= -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-20190221075227-b4e8571b14e0/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-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -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= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/history/history.go b/history/history.go index 3d9d081..5a54ac0 100644 --- a/history/history.go +++ b/history/history.go @@ -3,6 +3,7 @@ package history import ( "fmt" "log" + "os" "time" "github.com/bouncepaw/mycorrhiza/util" @@ -107,9 +108,10 @@ func (hop *HistoryOp) Apply() *HistoryOp { return hop } +// Rename renames from `from` to `to`. NB. It uses os.Rename internally rather than git.Move because git.Move works wrong for some reason. func Rename(from, to string) error { log.Println(util.ShorterPath(from), util.ShorterPath(to)) - _, err := Worktree.Move(util.ShorterPath(from), util.ShorterPath(to)) + err := os.Rename(from, to) return err } diff --git a/history/sh.go b/history/sh.go new file mode 100644 index 0000000..34af389 --- /dev/null +++ b/history/sh.go @@ -0,0 +1,100 @@ +package history + +import ( + "bytes" + "fmt" + "log" + "os/exec" + "regexp" + "strconv" + "strings" + "time" +) + +type Revision struct { + Hash string + Username string + Time time.Time + Message string +} + +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) { + cmd := exec.Command(gitpath, args...) + cmd.Stdout = &out + cmd.Run() + if err != nil { + log.Println("gitsh:", err) + } + return out, err +} + +func Revisions(filepath string) ([]Revision, error) { + if filepath == "" { + return []Revision{}, nil + } + 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\"", + "--", filepath, + ) + revs []Revision + ) + if err == nil { + for _, line := range strings.Split(out.String(), "\n") { + if rev := parseRevisionLine(line); rev != nil { + revs = append(revs, *rev) + } + } + } + return revs, err +} + +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 +} + +// 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 { + var ( + results = revisionLinePattern.FindStringSubmatch(line) + rev = Revision{ + Hash: results[1], + Username: results[2], + Time: *unixTimestampAsTime(results[3]), + Message: results[4], + } + ) + return &rev +} + +func (rev *Revision) AsHtmlTableRow() string { + return fmt.Sprintf(` + + %s + %s + + %s +`, rev.Hash, rev.Username, rev.Time.String(), rev.Message) +} diff --git a/http_mutators.go b/http_mutators.go index c943da3..7012839 100644 --- a/http_mutators.go +++ b/http_mutators.go @@ -134,11 +134,6 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) { if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil { 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 { HyphaStorage[hyphaName] = &HyphaData{ binaryPath: fullPath, @@ -155,9 +150,14 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) { hyphaData.binaryPath = fullPath 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) history.Operation(history.TypeEditText). - WithFiles(fullPath). + WithFiles(fullPath, hyphaData.binaryPath). WithMsg(fmt.Sprintf("Upload binary part for ‘%s’ with type ‘%s’", hyphaName, mimeType.Mime())). WithSignature("anon"). Apply() diff --git a/http_readers.go b/http_readers.go index fab83ee..889d83d 100644 --- a/http_readers.go +++ b/http_readers.go @@ -8,6 +8,7 @@ import ( "os" "github.com/bouncepaw/mycorrhiza/gemtext" + "github.com/bouncepaw/mycorrhiza/history" "github.com/bouncepaw/mycorrhiza/tree" ) @@ -15,6 +16,49 @@ func init() { http.HandleFunc("/page/", handlerPage) http.HandleFunc("/text/", handlerText) http.HandleFunc("/binary/", handlerBinary) + http.HandleFunc("/history/", handlerHistory) +} + +// 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 data, ok := HyphaStorage[hyphaName]; ok { + revsT, err := history.Revisions(data.textPath) + if err == nil { + for _, rev := range revsT { + tbody += rev.AsHtmlTableRow() + } + } + revsB, err := history.Revisions(data.binaryPath) + if err == nil { + for _, rev := range revsB { + tbody += rev.AsHtmlTableRow() + } + } + log.Println(revsT, revsB) + } + + table := fmt.Sprintf(` +
    + + + + + + + + + + + %s + +
    HashUsernameTimeMessage
    +
    `, tbody) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(base(hyphaName, table))) } // handlerText serves raw source text of the hypha. diff --git a/main.go b/main.go index cab791b..39663cc 100644 --- a/main.go +++ b/main.go @@ -130,6 +130,9 @@ func main() { if err != nil { log.Fatal(err) } + if err := os.Chdir(WikiDir); err != nil { + log.Fatal(err) + } log.Println("Wiki storage directory is", WikiDir) log.Println("Start indexing hyphae...") Index(WikiDir) @@ -138,7 +141,7 @@ func main() { history.Start(WikiDir) 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/. http.HandleFunc("/list", handlerList) http.HandleFunc("/reindex", handlerReindex) diff --git a/metarrhiza b/metarrhiza index 30287be..2f58d4e 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit 30287be4496638e342878e2adf50e0a74ce71a5f +Subproject commit 2f58d4e9663806496d7edfdeb24098abe986f454 diff --git a/tree/tree.go b/tree/tree.go index d7e8c45..1655664 100644 --- a/tree/tree.go +++ b/tree/tree.go @@ -60,9 +60,6 @@ func (t *tree) fill() { // It applies itself recursively on the tree's children. func (t *tree) asHtml() (html string) { if t.root { - for _, siblingName := range t.siblings { - html += navitreeEntry(siblingName, "navitree__sibling") - } html += navitreeEntry(t.name, "navitree__pagename") } else { html += navitreeEntry(t.name, "navitree__name") @@ -72,6 +69,12 @@ func (t *tree) asHtml() (html string) { html += subtree.asHtml() } + if t.root { + for _, siblingName := range t.siblings { + html += navitreeEntry(siblingName, "navitree__sibling") + } + } + return `` } From 813bbcf954d94aa92ebc36689cf05ca443014a15 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Thu, 20 Aug 2020 22:20:13 +0500 Subject: [PATCH 06/15] Route /rev/ to see any text part at a specified revision --- history/sh.go | 11 ++++++++--- http_readers.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/history/sh.go b/history/sh.go index 34af389..7bee54b 100644 --- a/history/sh.go +++ b/history/sh.go @@ -89,12 +89,17 @@ func parseRevisionLine(line string) *Revision { return &rev } -func (rev *Revision) AsHtmlTableRow() string { +func (rev *Revision) AsHtmlTableRow(hyphaName string) string { return fmt.Sprintf(` - %s + %s %s %s -`, rev.Hash, rev.Username, rev.Time.String(), rev.Message) +`, rev.Hash, hyphaName, rev.Hash, rev.Username, rev.Time.String(), rev.Message) +} + +func FileAtRevision(filepath, hash string) (string, error) { + out, err := gitsh("show", hash+":"+filepath) + return out.String(), err } diff --git a/http_readers.go b/http_readers.go index 889d83d..61cca09 100644 --- a/http_readers.go +++ b/http_readers.go @@ -6,6 +6,8 @@ import ( "log" "net/http" "os" + "path" + "strings" "github.com/bouncepaw/mycorrhiza/gemtext" "github.com/bouncepaw/mycorrhiza/history" @@ -17,6 +19,48 @@ func init() { http.HandleFunc("/text/", handlerText) 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(`

    This hypha had no text at this revision.

    `) + textPath = hyphaName + "&.gmi" + textContents, err = history.FileAtRevision(textPath, revHash) + ) + log.Println(revHash, hyphaName, textPath, textContents, err) + if err == nil { + contents = gemtext.ToHtml(hyphaName, textContents) + } + form := fmt.Sprintf(` +
    + +
    + %[2]s + %[3]s +
    +
    + +
    +`, hyphaName, + naviTitle(hyphaName), + contents, + tree.TreeAsHtml(hyphaName, IterateHyphaNamesWith)) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(base(hyphaName, form))) } // handlerHistory lists all revisions of a hypha @@ -28,13 +72,13 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) { revsT, err := history.Revisions(data.textPath) if err == nil { for _, rev := range revsT { - tbody += rev.AsHtmlTableRow() + tbody += rev.AsHtmlTableRow(hyphaName) } } revsB, err := history.Revisions(data.binaryPath) if err == nil { for _, rev := range revsB { - tbody += rev.AsHtmlTableRow() + tbody += rev.AsHtmlTableRow(hyphaName) } } log.Println(revsT, revsB) From e004f653bd9bc2033a89c613ddcf87c46b435fa8 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Thu, 27 Aug 2020 21:27:57 +0500 Subject: [PATCH 07/15] Make commits using git executable --- data | 1 - history/common.go | 58 +++++++++++++++++++++++++++++++++++++++++ history/history.go | 65 +++++++++++++++++++--------------------------- history/sh.go | 44 ------------------------------- metarrhiza | 2 +- 5 files changed, 86 insertions(+), 84 deletions(-) delete mode 160000 data create mode 100644 history/common.go diff --git a/data b/data deleted file mode 160000 index b2e7803..0000000 --- a/data +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b2e78031973e4a6844d943333e823c0714e7a673 diff --git a/history/common.go b/history/common.go new file mode 100644 index 0000000..d4424c7 --- /dev/null +++ b/history/common.go @@ -0,0 +1,58 @@ +package history + +import ( + "bytes" + "fmt" + "log" + "os/exec" + "strconv" + "time" + + "github.com/bouncepaw/mycorrhiza/util" +) + +// 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 +} diff --git a/history/history.go b/history/history.go index 5a54ac0..dc32e1a 100644 --- a/history/history.go +++ b/history/history.go @@ -3,12 +3,9 @@ package history import ( "fmt" "log" - "os" - "time" "github.com/bouncepaw/mycorrhiza/util" "github.com/go-git/go-git/v5" - // "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" ) @@ -33,6 +30,8 @@ func Start(wikiDir string) { if err != nil { log.Fatal(err) } + gitsh("config", "user.name", "wikimind") + gitsh("config", "user.email", "wikimind@mycorrhiza") log.Println("Wiki repository found") } @@ -41,7 +40,8 @@ type HistoryOp struct { opType OpType userMsg string signature *object.Signature - isDone bool + name string + email string } func Operation(opType OpType) *HistoryOp { @@ -52,66 +52,55 @@ func Operation(opType OpType) *HistoryOp { return hop } -// WithFiles stages all passed `paths`. Paths can be rooted or not. -func (hop *HistoryOp) WithFiles(paths ...string) *HistoryOp { - for _, path := range paths { - if _, err := Worktree.Add(util.ShorterPath(path)); err != nil { - log.Println(err) - hop.Errs = append(hop.Errs, err) - } +func (hop *HistoryOp) gitop(args ...string) *HistoryOp { + out, err := gitsh(args...) + fmt.Println("out:", out.String()) + if err != nil { + 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) + } + return hop.gitop(append([]string{"add"}, paths...)...) +} + // 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 { - // Isn't it too imperative? - var firstLine string for _, ch := range userMsg { if ch == '\r' || ch == '\n' { break } - firstLine += string(ch) + hop.userMsg += string(ch) } - hop.userMsg = firstLine 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.signature = &object.Signature{ - Name: username, - // A fake email, why not - Email: username + "@mycorrhiza", - When: time.Now(), - } + hop.name = username + hop.email = username + "@mycorrhiza" // A fake email, why not return hop } -// Apply applies history operation by doing the commit. You can't apply the same operation more than once. +// Apply applies history operation by doing the commit. func (hop *HistoryOp) Apply() *HistoryOp { - if !hop.isDone { - opts := &git.CommitOptions{ - All: false, - Author: hop.signature, - } - err := opts.Validate(WikiRepo) - if err != nil { - hop.Errs = append(hop.Errs, err) - } - // TODO: work on this section: - _, err = Worktree.Commit(hop.userMsg, opts) - if err != nil { - hop.Errs = append(hop.Errs, err) - } - } + hop.gitop( + "commit", + "--author='"+hop.name+" <"+hop.email+">'", + "--message="+hop.userMsg, + ) return hop } // Rename renames from `from` to `to`. NB. It uses os.Rename internally rather than git.Move because git.Move works wrong for some reason. func Rename(from, to string) error { log.Println(util.ShorterPath(from), util.ShorterPath(to)) - err := os.Rename(from, to) + _, err := gitsh("mv", from, to) return err } diff --git a/history/sh.go b/history/sh.go index 7bee54b..4f4438d 100644 --- a/history/sh.go +++ b/history/sh.go @@ -1,46 +1,11 @@ package history import ( - "bytes" "fmt" - "log" - "os/exec" "regexp" - "strconv" "strings" - "time" ) -type Revision struct { - Hash string - Username string - Time time.Time - Message string -} - -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) { - cmd := exec.Command(gitpath, args...) - cmd.Stdout = &out - cmd.Run() - if err != nil { - log.Println("gitsh:", err) - } - return out, err -} - func Revisions(filepath string) ([]Revision, error) { if filepath == "" { return []Revision{}, nil @@ -64,15 +29,6 @@ func Revisions(filepath string) ([]Revision, error) { return revs, err } -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 -} - // 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(.*)\"") diff --git a/metarrhiza b/metarrhiza index 2f58d4e..e0e48d3 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit 2f58d4e9663806496d7edfdeb24098abe986f454 +Subproject commit e0e48d39b81968be9c5b00cac6b66cd68c1db3ce From 28dee1087c5e008ed0486990e556797df7f8cac2 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Sat, 29 Aug 2020 00:10:46 +0500 Subject: [PATCH 08/15] Some changes --- README.md | 21 +++++++-------------- history/sh.go | 7 ++----- http_readers.go | 17 ++++++----------- metarrhiza | 2 +- 4 files changed, 16 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index c25b673..8e9f51b 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,22 @@ -# mycorrhiza wiki -A wiki engine inspired by fungi. Not production-ready. - -This branch is devoted to version 0.8. -* [ ] Tree generation - * [x] Basic generation - * [ ] Generation that takes non-existent hyphae into account¹ -* [ ] History - * [ ] Saving all changes to git - * [ ] Make it possible to see any previous version - * [ ] A nice UI for that - -¹ Current algorithm won't detect `a/b/c` as a child of `a` if `a/b` does not exist. +# MycorrhizaWiki +A wiki engine inspired by fungi. ## Current features * Edit pages through html forms * Responsive design * Works in text browsers -* Pages (called hyphae) can be in gemtext. +* Pages (called hyphae) are in gemtext * Everything is stored as simple files, no database required +* Page trees +* Changes are saved to git +* History page ## Future features * Tags * Authorization * History view * Granular user rights +* A better page tree algorithm ## Installation I guess you can just clone this repo and run `make` to play around with the default wiki. diff --git a/history/sh.go b/history/sh.go index 4f4438d..e7216f9 100644 --- a/history/sh.go +++ b/history/sh.go @@ -6,16 +6,13 @@ import ( "strings" ) -func Revisions(filepath string) ([]Revision, error) { - if filepath == "" { - return []Revision{}, nil - } +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\"", - "--", filepath, + "--", hyphaName+"&.*", ) revs []Revision ) diff --git a/http_readers.go b/http_readers.go index 61cca09..8c02a89 100644 --- a/http_readers.go +++ b/http_readers.go @@ -41,11 +41,12 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
    +

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

    %[2]s %[3]s
    @@ -68,20 +69,14 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) { log.Println(rq.URL) hyphaName := HyphaNameFromRq(rq, "history") var tbody string - if data, ok := HyphaStorage[hyphaName]; ok { - revsT, err := history.Revisions(data.textPath) + if _, ok := HyphaStorage[hyphaName]; ok { + revs, err := history.Revisions(hyphaName) if err == nil { - for _, rev := range revsT { + for _, rev := range revs { tbody += rev.AsHtmlTableRow(hyphaName) } } - revsB, err := history.Revisions(data.binaryPath) - if err == nil { - for _, rev := range revsB { - tbody += rev.AsHtmlTableRow(hyphaName) - } - } - log.Println(revsT, revsB) + log.Println(revs) } table := fmt.Sprintf(` diff --git a/metarrhiza b/metarrhiza index e0e48d3..41b2e0b 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit e0e48d39b81968be9c5b00cac6b66cd68c1db3ce +Subproject commit 41b2e0bda18d1c4188363790fcd6289f29b296c3 From 7d0ad92669a0fbe41545312762bea89bdb8a5d0d Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Sat, 29 Aug 2020 00:17:32 +0500 Subject: [PATCH 09/15] Fix gemtext list --- gemtext/lexer.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gemtext/lexer.go b/gemtext/lexer.go index 507808b..5fd1c77 100644 --- a/gemtext/lexer.go +++ b/gemtext/lexer.go @@ -23,11 +23,6 @@ type GemLexerState struct { buf string } -// GeminiToHtml converts gemtext `content` of hypha `name` to html string. -func GeminiToHtml(name, content string) string { - return "TODO: do" -} - type Line struct { id int // 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) { var state = GemLexerState{name: name} - for _, line := range strings.Split(content, "\n") { + for _, line := range append(strings.Split(content, "\n"), "") { geminiLineToAST(line, &state, &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`. 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 state.where == "list" { + state.where = "" + addLine(state.buf + "") + } return } startsWith := func(token string) bool { 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. switch state.where { From f33d7598acef8fa898c541a725d8cf534aece65c Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Sat, 29 Aug 2020 00:43:36 +0500 Subject: [PATCH 10/15] A better navigation --- http_readers.go | 23 +++++++++++++++++------ metarrhiza | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/http_readers.go b/http_readers.go index 8c02a89..ec87d26 100644 --- a/http_readers.go +++ b/http_readers.go @@ -33,7 +33,6 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) { textPath = hyphaName + "&.gmi" textContents, err = history.FileAtRevision(textPath, revHash) ) - log.Println(revHash, hyphaName, textPath, textContents, err) if err == nil { contents = gemtext.ToHtml(hyphaName, textContents) } @@ -41,8 +40,11 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
    @@ -58,7 +60,8 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) { `, hyphaName, naviTitle(hyphaName), contents, - tree.TreeAsHtml(hyphaName, IterateHyphaNamesWith)) + tree.TreeAsHtml(hyphaName, IterateHyphaNamesWith), + revHash) w.Header().Set("Content-Type", "text/html;charset=utf-8") w.WriteHeader(http.StatusOK) w.Write([]byte(base(hyphaName, form))) @@ -81,6 +84,14 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) { table := fmt.Sprintf(`
    + @@ -91,10 +102,10 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) { - %s + %[2]s
    -
    `, tbody) +
    `, hyphaName, tbody) w.Header().Set("Content-Type", "text/html;charset=utf-8") w.WriteHeader(http.StatusOK) w.Write([]byte(base(hyphaName, table))) @@ -144,9 +155,9 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) {
    diff --git a/metarrhiza b/metarrhiza index 41b2e0b..2a36350 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit 41b2e0bda18d1c4188363790fcd6289f29b296c3 +Subproject commit 2a36350d4248610d7e09119d03a602414252abe4 From aeee45104478d7fe8f26cd0325111b9ef29d78f0 Mon Sep 17 00:00:00 2001 From: bouncepaw Date: Sat, 29 Aug 2020 22:54:57 +0500 Subject: [PATCH 11/15] Do not depend on go-git anymore, clean history code a little --- go.mod | 2 - go.sum | 62 ------------- history/common.go | 58 ------------ history/history.go | 142 +++++++++--------------------- history/{sh.go => information.go} | 33 +++---- history/operations.go | 84 ++++++++++++++++++ http_mutators.go | 4 +- http_readers.go | 2 +- main.go | 16 ---- 9 files changed, 145 insertions(+), 258 deletions(-) delete mode 100644 history/common.go rename history/{sh.go => information.go} (60%) create mode 100644 history/operations.go diff --git a/go.mod b/go.mod index a66370d..84004ef 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,3 @@ module github.com/bouncepaw/mycorrhiza go 1.14 - -require github.com/go-git/go-git/v5 v5.1.0 diff --git a/go.sum b/go.sum index 66994b3..e69de29 100644 --- a/go.sum +++ b/go.sum @@ -1,62 +0,0 @@ -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= -github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= -github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= -github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk= -github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= -github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= -github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/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-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/history/common.go b/history/common.go deleted file mode 100644 index d4424c7..0000000 --- a/history/common.go +++ /dev/null @@ -1,58 +0,0 @@ -package history - -import ( - "bytes" - "fmt" - "log" - "os/exec" - "strconv" - "time" - - "github.com/bouncepaw/mycorrhiza/util" -) - -// 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 -} diff --git a/history/history.go b/history/history.go index dc32e1a..787b358 100644 --- a/history/history.go +++ b/history/history.go @@ -1,137 +1,77 @@ package history import ( + "bytes" "fmt" "log" + "os/exec" + "strconv" + "time" "github.com/bouncepaw/mycorrhiza/util" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/object" ) -type OpType int - -const ( - TypeNone OpType = iota - TypeEditText - TypeEditBinary -) - -var WikiRepo *git.Repository -var Worktree *git.Worktree - +// Start initializes git credentials. func Start(wikiDir string) { - ry, err := git.PlainOpen(wikiDir) + _, err := gitsh("config", "user.name", "wikimind") if err != nil { log.Fatal(err) } - WikiRepo = ry - Worktree, err = WikiRepo.Worktree() + _, err = gitsh("config", "user.email", "wikimind@mycorrhiza") if err != nil { log.Fatal(err) } - gitsh("config", "user.name", "wikimind") - gitsh("config", "user.email", "wikimind@mycorrhiza") - log.Println("Wiki repository found") } -type HistoryOp struct { - Errs []error - opType OpType - userMsg string - signature *object.Signature - name string - email string +// 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 } -func Operation(opType OpType) *HistoryOp { - hop := &HistoryOp{ - Errs: []error{}, - opType: opType, - } - return hop -} +// Path to git executable. Set at init() +var gitpath string -func (hop *HistoryOp) gitop(args ...string) *HistoryOp { - out, err := gitsh(args...) - fmt.Println("out:", out.String()) +func init() { + path, err := exec.LookPath("git") if err != nil { - hop.Errs = append(hop.Errs, err) + log.Fatal("Cound not find the git executable. Check your $PATH.") + } else { + log.Println("Git path is", path) } - return hop + gitpath = path + } -// 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) +// 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 hop.gitop(append([]string{"add"}, paths...)...) + return *bytes.NewBuffer(b), err } -// 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) +// 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 } - return hop + tm := time.Unix(i, 0) + return &tm } -// 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 -} - -// 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 -} - -// Rename renames from `from` to `to`. NB. It uses os.Rename internally rather than git.Move because git.Move works wrong for some reason. +// 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 } - -func StatusTable() (html string) { - status, err := Worktree.Status() - if err != nil { - log.Fatal(err) - } - for path, stat := range status { - html += fmt.Sprintf(` - - %s - %v - `, path, stat) - } - return `` + html + `
    ` -} - -func CommitsTable() (html string) { - commitIter, err := WikiRepo.CommitObjects() - if err != nil { - log.Fatal(err) - } - err = commitIter.ForEach(func(commit *object.Commit) error { - html += fmt.Sprintf(` - - %v - %v - %s - `, commit.Hash, commit.Author, commit.Message) - return nil - }) - return `` + html + `
    ` -} diff --git a/history/sh.go b/history/information.go similarity index 60% rename from history/sh.go rename to history/information.go index e7216f9..eff1e78 100644 --- a/history/sh.go +++ b/history/information.go @@ -1,11 +1,15 @@ +// 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( @@ -18,9 +22,7 @@ func Revisions(hyphaName string) ([]Revision, error) { ) if err == nil { for _, line := range strings.Split(out.String(), "\n") { - if rev := parseRevisionLine(line); rev != nil { - revs = append(revs, *rev) - } + revs = append(revs, parseRevisionLine(line)) } } return revs, err @@ -29,29 +31,28 @@ func Revisions(hyphaName string) ([]Revision, error) { // 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 { - var ( - results = revisionLinePattern.FindStringSubmatch(line) - rev = Revision{ - Hash: results[1], - Username: results[2], - Time: *unixTimestampAsTime(results[3]), - Message: results[4], - } - ) - return &rev +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(` + %s %s - %s -`, rev.Hash, hyphaName, rev.Hash, rev.Username, rev.Time.String(), rev.Message) +`, rev.Time.Format(time.RFC822), rev.Hash, hyphaName, rev.Hash, rev.Username, 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 diff --git a/history/operations.go b/history/operations.go new file mode 100644 index 0000000..3567d78 --- /dev/null +++ b/history/operations.go @@ -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 +} diff --git a/http_mutators.go b/http_mutators.go index 7012839..2db520a 100644 --- a/http_mutators.go +++ b/http_mutators.go @@ -93,12 +93,12 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) { hyphaData.textType = TextGemini hyphaData.textPath = fullPath } + http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther) history.Operation(history.TypeEditText). WithFiles(fullPath). WithMsg(fmt.Sprintf("Edit ‘%s’", hyphaName)). WithSignature("anon"). Apply() - http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther) } // handlerUploadBinary uploads a new binary part for the hypha. @@ -156,10 +156,10 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) { return } log.Println("Written", len(data), "of binary data for", hyphaName, "to path", fullPath) + 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() - http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther) } diff --git a/http_readers.go b/http_readers.go index ec87d26..34a7146 100644 --- a/http_readers.go +++ b/http_readers.go @@ -95,9 +95,9 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) { + - diff --git a/main.go b/main.go index 39663cc..04acaad 100644 --- a/main.go +++ b/main.go @@ -107,20 +107,6 @@ func handlerReindex(w http.ResponseWriter, rq *http.Request) { log.Println("Indexed", len(HyphaStorage), "hyphae") } -func handlerListCommits(w http.ResponseWriter, rq *http.Request) { - log.Println(rq.URL) - w.Header().Set("Content-Type", "text/html;charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write([]byte(history.CommitsTable())) -} - -func handlerStatus(w http.ResponseWriter, rq *http.Request) { - log.Println(rq.URL) - w.Header().Set("Content-Type", "text/html;charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write([]byte(history.StatusTable())) -} - func main() { log.Println("Running MycorrhizaWiki β") @@ -145,8 +131,6 @@ func main() { // See http_mutators.go for /upload-binary/, /upload-text/, /edit/. http.HandleFunc("/list", handlerList) http.HandleFunc("/reindex", handlerReindex) - http.HandleFunc("/git/list", handlerListCommits) - http.HandleFunc("/git/status", handlerStatus) http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) { http.ServeFile(w, rq, WikiDir+"/static/favicon.ico") }) From 41551fd63b1afee2114c27ba85dc135e8bfcc010 Mon Sep 17 00:00:00 2001 From: bouncepaw Date: Mon, 31 Aug 2020 22:52:26 +0500 Subject: [PATCH 12/15] Migrate to quicktemplates --- Makefile | 1 + go.mod | 2 + go.sum | 15 ++ history/information.go | 3 +- http_mutators.go | 21 +-- http_readers.go | 96 ++--------- main.go | 51 ++---- metarrhiza | 2 +- templates/http_mutators.qtpl | 14 ++ templates/http_mutators.qtpl.go | 83 +++++++++ templates/http_readers.qtpl | 81 +++++++++ templates/http_readers.qtpl.go | 286 ++++++++++++++++++++++++++++++++ templates/http_stuff.qtpl | 33 ++++ templates/http_stuff.qtpl.go | 126 ++++++++++++++ util/util.go | 8 + 15 files changed, 676 insertions(+), 146 deletions(-) create mode 100644 templates/http_mutators.qtpl create mode 100644 templates/http_mutators.qtpl.go create mode 100644 templates/http_readers.qtpl create mode 100644 templates/http_readers.qtpl.go create mode 100644 templates/http_stuff.qtpl create mode 100644 templates/http_stuff.qtpl.go diff --git a/Makefile b/Makefile index 49dc584..dd9d604 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ run: build ./mycorrhiza metarrhiza build: + go generate go build . test: diff --git a/go.mod b/go.mod index 84004ef..17f2ff6 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/bouncepaw/mycorrhiza go 1.14 + +require github.com/valyala/quicktemplate v1.6.2 diff --git a/go.sum b/go.sum index e69de29..344ceb3 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,15 @@ +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/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-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= diff --git a/history/information.go b/history/information.go index eff1e78..ff6e654 100644 --- a/history/information.go +++ b/history/information.go @@ -48,8 +48,7 @@ func (rev *Revision) AsHtmlTableRow(hyphaName string) string { - -`, rev.Time.Format(time.RFC822), rev.Hash, hyphaName, rev.Hash, rev.Username, rev.Message) +`, rev.Time.Format(time.RFC822), rev.Hash, hyphaName, rev.Hash, rev.Message) } // See how the file with `filepath` looked at commit with `hash`. diff --git a/http_mutators.go b/http_mutators.go index 2db520a..e3cd397 100644 --- a/http_mutators.go +++ b/http_mutators.go @@ -9,6 +9,8 @@ import ( "path/filepath" "github.com/bouncepaw/mycorrhiza/history" + "github.com/bouncepaw/mycorrhiza/templates" + "github.com/bouncepaw/mycorrhiza/util" ) func init() { @@ -38,24 +40,7 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) { } else { warning = `

    You are creating a new hypha.

    ` } - form := fmt.Sprintf(` -
    -

    Edit %[1]s

    - %[3]s -
    - -
    - - Cancel - -
    -`, hyphaName, textAreaFill, warning) - - w.Header().Set("Content-Type", "text/html;charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write([]byte(base( - "Edit "+hyphaName, form))) + util.HTTP200Page(w, base("Edit"+hyphaName, templates.EditHTML(hyphaName, textAreaFill, warning))) } // handlerUploadText uploads a new text part for the hypha. diff --git a/http_readers.go b/http_readers.go index 34a7146..f288210 100644 --- a/http_readers.go +++ b/http_readers.go @@ -11,7 +11,9 @@ import ( "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() { @@ -36,35 +38,16 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) { if err == nil { contents = gemtext.ToHtml(hyphaName, textContents) } - form := fmt.Sprintf(` -
    - -
    -

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

    - %[2]s - %[3]s -
    -
    - -
    -`, hyphaName, + page := templates.RevisionHTML( + hyphaName, naviTitle(hyphaName), contents, tree.TreeAsHtml(hyphaName, IterateHyphaNamesWith), - revHash) + revHash, + ) w.Header().Set("Content-Type", "text/html;charset=utf-8") w.WriteHeader(http.StatusOK) - w.Write([]byte(base(hyphaName, form))) + w.Write([]byte(base(hyphaName, page))) } // handlerHistory lists all revisions of a hypha @@ -82,33 +65,8 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) { log.Println(revs) } - table := fmt.Sprintf(` -
    - -
    Time Hash UsernameTime Message
    %s %s%s
    - - - - - - - - - - %[2]s - -
    TimeHashUsernameMessage
    -
    `, hyphaName, tbody) - w.Header().Set("Content-Type", "text/html;charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write([]byte(base(hyphaName, table))) + util.HTTP200Page(w, + base(hyphaName, templates.HistoryHTML(hyphaName, tbody))) } // handlerText serves raw source text of the hypha. @@ -138,8 +96,8 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) { log.Println(rq.URL) var ( hyphaName = HyphaNameFromRq(rq, "page") - contents = fmt.Sprintf(`

    This hypha has no text. Why not create it?

    `, hyphaName) data, hyphaExists = HyphaStorage[hyphaName] + contents string ) if hyphaExists { fileContentsT, errT := ioutil.ReadFile(data.textPath) @@ -151,38 +109,8 @@ func handlerPage(w http.ResponseWriter, rq *http.Request) { contents = binaryHtmlBlock(hyphaName, data) + contents } } - form := fmt.Sprintf(` -
    - -
    - %[2]s - %[3]s -
    -
    -
    - -
    - - -
    -
    - -
    -`, hyphaName, + util.HTTP200Page(w, base(hyphaName, templates.PageHTML(hyphaName, naviTitle(hyphaName), contents, - tree.TreeAsHtml(hyphaName, IterateHyphaNamesWith)) - w.Header().Set("Content-Type", "text/html;charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write([]byte(base(hyphaName, form))) + tree.TreeAsHtml(hyphaName, IterateHyphaNamesWith)))) } diff --git a/main.go b/main.go index 04acaad..cf567eb 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,5 @@ +//go:generate go get -u github.com/valyala/quicktemplate/qtc +//go:generate qtc -dir=templates package main import ( @@ -9,6 +11,7 @@ import ( "regexp" "github.com/bouncepaw/mycorrhiza/history" + "github.com/bouncepaw/mycorrhiza/templates" "github.com/bouncepaw/mycorrhiza/util" ) @@ -38,29 +41,13 @@ func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) { errMsg, name))) } -// shorterPath is used by handlerList to display shorter path to the files. It simply strips WikiDir. It was moved to util package, this is an alias. TODO: demolish. -var shorterPath = util.ShorterPath - // Show all hyphae func handlerList(w http.ResponseWriter, rq *http.Request) { log.Println(rq.URL) - w.Header().Set("Content-Type", "text/html;charset=utf-8") - w.WriteHeader(http.StatusOK) - buf := ` -

    List of pages

    - - - - - - - - - - - ` + var tbody string + // It should be moved to templates too, right? for name, data := range HyphaStorage { - buf += fmt.Sprintf(` + tbody += fmt.Sprintf(` @@ -69,33 +56,15 @@ func handlerList(w http.ResponseWriter, rq *http.Request) { `, name, name, - shorterPath(data.textPath), data.textType, - shorterPath(data.binaryPath), data.binaryType, + util.ShorterPath(data.textPath), data.textType, + util.ShorterPath(data.binaryPath), data.binaryType, ) } - buf += ` - -
    NameText pathText typeBinary pathBinary type
    %s %s%d
    -` - w.Write([]byte(base("List of pages", buf))) + util.HTTP200Page(w, base("List of pages", templates.PageListHTML(tbody))) } // This part is present in all html documents. -func base(title, body string) string { - return fmt.Sprintf(` - - - - - - %s - - - %s - - -`, title, body) -} +var base = templates.BaseHTML // Reindex all hyphae by checking the wiki storage directory anew. func handlerReindex(w http.ResponseWriter, rq *http.Request) { diff --git a/metarrhiza b/metarrhiza index 2a36350..ce6ffcd 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit 2a36350d4248610d7e09119d03a602414252abe4 +Subproject commit ce6ffcdb83be6d5d5d15f0a8d240db5430889d45 diff --git a/templates/http_mutators.qtpl b/templates/http_mutators.qtpl new file mode 100644 index 0000000..72f40a1 --- /dev/null +++ b/templates/http_mutators.qtpl @@ -0,0 +1,14 @@ + +{% func EditHTML(hyphaName, textAreaFill, warning string) %} +
    +

    Edit {%s hyphaName %}

    + {%s= warning %} +
    + +
    + + Cancel +
    +
    +{% endfunc %} diff --git a/templates/http_mutators.qtpl.go b/templates/http_mutators.qtpl.go new file mode 100644 index 0000000..0c880ea --- /dev/null +++ b/templates/http_mutators.qtpl.go @@ -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(` +
    +

    Edit `) +//line templates/http_mutators.qtpl:4 + qw422016.E().S(hyphaName) +//line templates/http_mutators.qtpl:4 + qw422016.N().S(`

    + `) +//line templates/http_mutators.qtpl:5 + qw422016.N().S(warning) +//line templates/http_mutators.qtpl:5 + qw422016.N().S(` +
    + +
    + + Cancel +
    +
    +`) +//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 +} diff --git a/templates/http_readers.qtpl b/templates/http_readers.qtpl new file mode 100644 index 0000000..0c3b4ea --- /dev/null +++ b/templates/http_readers.qtpl @@ -0,0 +1,81 @@ +{% func HistoryHTML(hyphaName, tbody string) %} +
    + + + + + + + + + + + {%s= tbody %} + +
    TimeHashMessage
    +
    +{% endfunc %} + +{% func RevisionHTML(hyphaName, naviTitle, contents, tree, revHash string) %} +
    + +
    +

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

    + {%s= naviTitle %} + {%s= contents %} +
    +
    + +
    +{% endfunc %} + +If `contents` == "", a helpful message is shown instead. +{% func PageHTML(hyphaName, naviTitle, contents, tree string) %} +
    + +
    + {%s= naviTitle %} + {% if contents == "" %} +

    This hypha has no text. Why not create it?

    + {% else %} + {%s= contents %} + {% endif %} +
    +
    +
    + +
    + + +
    +
    + +
    +{% endfunc %} diff --git a/templates/http_readers.qtpl.go b/templates/http_readers.qtpl.go new file mode 100644 index 0000000..97d4a0b --- /dev/null +++ b/templates/http_readers.qtpl.go @@ -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(` +
    + + + + + + + + + + + `) +//line templates/http_readers.qtpl:20 + qw422016.N().S(tbody) +//line templates/http_readers.qtpl:20 + qw422016.N().S(` + +
    TimeHashMessage
    +
    +`) +//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(` +
    + +
    +

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

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

    This hypha has no text. Why not create it?

    + `) +//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(` +
    +
    +
    + +
    + + +
    +
    + +
    +`) +//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 +} diff --git a/templates/http_stuff.qtpl b/templates/http_stuff.qtpl new file mode 100644 index 0000000..63a3a4e --- /dev/null +++ b/templates/http_stuff.qtpl @@ -0,0 +1,33 @@ +{% func BaseHTML(title, body string) %} + + + + + + {%s title %} + + + {%s= body %} + + +{% endfunc %} + +{% func PageListHTML(tbody string) %} +
    +

    List of pages

    + + + + + + + + + + + + {%s= tbody %} + +
    NameText pathText typeBinary pathBinary type
    +
    +{% endfunc %} diff --git a/templates/http_stuff.qtpl.go b/templates/http_stuff.qtpl.go new file mode 100644 index 0000000..89d322c --- /dev/null +++ b/templates/http_stuff.qtpl.go @@ -0,0 +1,126 @@ +// 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(` + + + + + + `) +//line templates/http_stuff.qtpl:7 + qw422016.E().S(title) +//line templates/http_stuff.qtpl:7 + qw422016.N().S(` + + + `) +//line templates/http_stuff.qtpl:10 + qw422016.N().S(body) +//line templates/http_stuff.qtpl:10 + qw422016.N().S(` + + +`) +//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 StreamPageListHTML(qw422016 *qt422016.Writer, tbody string) { +//line templates/http_stuff.qtpl:15 + qw422016.N().S(` +
    +

    List of pages

    + + + + + + + + + + + + `) +//line templates/http_stuff.qtpl:29 + qw422016.N().S(tbody) +//line templates/http_stuff.qtpl:29 + qw422016.N().S(` + +
    NameText pathText typeBinary pathBinary type
    +
    +`) +//line templates/http_stuff.qtpl:33 +} + +//line templates/http_stuff.qtpl:33 +func WritePageListHTML(qq422016 qtio422016.Writer, tbody string) { +//line templates/http_stuff.qtpl:33 + qw422016 := qt422016.AcquireWriter(qq422016) +//line templates/http_stuff.qtpl:33 + StreamPageListHTML(qw422016, tbody) +//line templates/http_stuff.qtpl:33 + qt422016.ReleaseWriter(qw422016) +//line templates/http_stuff.qtpl:33 +} + +//line templates/http_stuff.qtpl:33 +func PageListHTML(tbody string) string { +//line templates/http_stuff.qtpl:33 + qb422016 := qt422016.AcquireByteBuffer() +//line templates/http_stuff.qtpl:33 + WritePageListHTML(qb422016, tbody) +//line templates/http_stuff.qtpl:33 + qs422016 := string(qb422016.B) +//line templates/http_stuff.qtpl:33 + qt422016.ReleaseByteBuffer(qb422016) +//line templates/http_stuff.qtpl:33 + return qs422016 +//line templates/http_stuff.qtpl:33 +} diff --git a/util/util.go b/util/util.go index f45ef9a..ee77e07 100644 --- a/util/util.go +++ b/util/util.go @@ -1,6 +1,7 @@ package util import ( + "net/http" "strings" ) @@ -17,3 +18,10 @@ func ShorterPath(path string) string { } 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)) +} From bcceb1200980351e2d9e326f1b78541042c908b2 Mon Sep 17 00:00:00 2001 From: bouncepaw Date: Mon, 31 Aug 2020 23:16:22 +0500 Subject: [PATCH 13/15] Make a better /list page --- main.go | 23 ++----- templates/http_stuff.qtpl | 23 +++++-- templates/http_stuff.qtpl.go | 124 +++++++++++++++++++++++++++-------- 3 files changed, 119 insertions(+), 51 deletions(-) diff --git a/main.go b/main.go index cf567eb..dfa1089 100644 --- a/main.go +++ b/main.go @@ -44,23 +44,14 @@ func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) { // Show all hyphae func handlerList(w http.ResponseWriter, rq *http.Request) { log.Println(rq.URL) - var tbody string - // It should be moved to templates too, right? - for name, data := range HyphaStorage { - tbody += fmt.Sprintf(` - - %s - %s - %d - %s - %d - `, - name, name, - util.ShorterPath(data.textPath), data.textType, - util.ShorterPath(data.binaryPath), data.binaryType, - ) + var ( + tbody string + pageCount = len(HyphaStorage) + ) + for hyphaName, data := range HyphaStorage { + tbody += templates.HyphaListRowHTML(hyphaName, data.binaryType.Mime(), data.binaryPath != "") } - util.HTTP200Page(w, base("List of pages", templates.PageListHTML(tbody))) + util.HTTP200Page(w, base("List of pages", templates.HyphaListHTML(tbody, pageCount))) } // This part is present in all html documents. diff --git a/templates/http_stuff.qtpl b/templates/http_stuff.qtpl index 63a3a4e..9bacf51 100644 --- a/templates/http_stuff.qtpl +++ b/templates/http_stuff.qtpl @@ -12,17 +12,15 @@ {% endfunc %} -{% func PageListHTML(tbody string) %} +{% func HyphaListHTML(tbody string, pageCount int) %}
    -

    List of pages

    +

    List of hyphae

    +

    This wiki has {%d pageCount %} hyphae.

    - - - - - + + @@ -31,3 +29,14 @@
    NameText pathText typeBinary pathBinary typeFull nameBinary part type
    {% endfunc %} + +{% func HyphaListRowHTML(hyphaName, binaryMime string, binaryPresent bool) %} + + {%s hyphaName %} + {% if binaryPresent %} + {%s binaryMime %} + {% else %} + + {% endif %} + +{% endfunc %} diff --git a/templates/http_stuff.qtpl.go b/templates/http_stuff.qtpl.go index 89d322c..8fa0096 100644 --- a/templates/http_stuff.qtpl.go +++ b/templates/http_stuff.qtpl.go @@ -71,56 +71,124 @@ func BaseHTML(title, body string) string { } //line templates/http_stuff.qtpl:15 -func StreamPageListHTML(qw422016 *qt422016.Writer, tbody string) { +func StreamHyphaListHTML(qw422016 *qt422016.Writer, tbody string, pageCount int) { //line templates/http_stuff.qtpl:15 qw422016.N().S(`
    -

    List of pages

    +

    List of hyphae

    +

    This wiki has `) +//line templates/http_stuff.qtpl:18 + qw422016.N().D(pageCount) +//line templates/http_stuff.qtpl:18 + qw422016.N().S(` hyphae.

    - - - - - + + `) -//line templates/http_stuff.qtpl:29 +//line templates/http_stuff.qtpl:27 qw422016.N().S(tbody) -//line templates/http_stuff.qtpl:29 +//line templates/http_stuff.qtpl:27 qw422016.N().S(`
    NameText pathText typeBinary pathBinary typeFull nameBinary part type
    `) -//line templates/http_stuff.qtpl:33 +//line templates/http_stuff.qtpl:31 } -//line templates/http_stuff.qtpl:33 -func WritePageListHTML(qq422016 qtio422016.Writer, tbody string) { -//line templates/http_stuff.qtpl:33 +//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:33 - StreamPageListHTML(qw422016, tbody) -//line templates/http_stuff.qtpl:33 +//line templates/http_stuff.qtpl:31 + StreamHyphaListHTML(qw422016, tbody, pageCount) +//line templates/http_stuff.qtpl:31 qt422016.ReleaseWriter(qw422016) -//line templates/http_stuff.qtpl:33 +//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 PageListHTML(tbody string) string { -//line templates/http_stuff.qtpl:33 - qb422016 := qt422016.AcquireByteBuffer() -//line templates/http_stuff.qtpl:33 - WritePageListHTML(qb422016, tbody) -//line templates/http_stuff.qtpl:33 - qs422016 := string(qb422016.B) -//line templates/http_stuff.qtpl:33 - qt422016.ReleaseByteBuffer(qb422016) -//line templates/http_stuff.qtpl:33 - return qs422016 +func StreamHyphaListRowHTML(qw422016 *qt422016.Writer, hyphaName, binaryMime string, binaryPresent bool) { //line templates/http_stuff.qtpl:33 + qw422016.N().S(` + + `) +//line templates/http_stuff.qtpl:35 + qw422016.E().S(hyphaName) +//line templates/http_stuff.qtpl:35 + qw422016.N().S(` + `) +//line templates/http_stuff.qtpl:36 + if binaryPresent { +//line templates/http_stuff.qtpl:36 + qw422016.N().S(` + `) +//line templates/http_stuff.qtpl:37 + qw422016.E().S(binaryMime) +//line templates/http_stuff.qtpl:37 + qw422016.N().S(` + `) +//line templates/http_stuff.qtpl:38 + } else { +//line templates/http_stuff.qtpl:38 + qw422016.N().S(` + + `) +//line templates/http_stuff.qtpl:40 + } +//line templates/http_stuff.qtpl:40 + qw422016.N().S(` + +`) +//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 } From 756b97f64428b859a05665f3a550f874b9062091 Mon Sep 17 00:00:00 2001 From: bouncepaw Date: Mon, 31 Aug 2020 23:35:36 +0500 Subject: [PATCH 14/15] Add random page --- main.go | 17 +++++++++++++++++ metarrhiza | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index dfa1089..cb9588f 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ package main import ( "fmt" "log" + "math/rand" "net/http" "os" "path/filepath" @@ -67,6 +68,21 @@ func handlerReindex(w http.ResponseWriter, rq *http.Request) { 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() { log.Println("Running MycorrhizaWiki β") @@ -91,6 +107,7 @@ func main() { // See http_mutators.go for /upload-binary/, /upload-text/, /edit/. http.HandleFunc("/list", handlerList) http.HandleFunc("/reindex", handlerReindex) + http.HandleFunc("/random", handlerRandom) http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) { http.ServeFile(w, rq, WikiDir+"/static/favicon.ico") }) diff --git a/metarrhiza b/metarrhiza index ce6ffcd..bdaaab6 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit ce6ffcdb83be6d5d5d15f0a8d240db5430889d45 +Subproject commit bdaaab62574023487610d608d1e9f2f351707a7f From 0e6c22080b08d542ed1e77fb89e81969ad742184 Mon Sep 17 00:00:00 2001 From: bouncepaw Date: Tue, 1 Sep 2020 21:52:40 +0500 Subject: [PATCH 15/15] A better README.md for 0.8 release --- README.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8e9f51b..a733e95 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,35 @@ -# MycorrhizaWiki -A wiki engine inspired by fungi. +# 🍄 MycorrhizaWiki 0.8 +A wiki engine. + +## 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 * Edit pages through html forms * Responsive design * Works in text browsers -* Pages (called hyphae) are in gemtext +* Wiki pages (called hyphae) are in gemtext * 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 -* Tags +## Contributing +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 -* History view -* Granular user rights -* A better page tree algorithm - -## Installation -I guess you can just clone this repo and run `make` to play around with the default wiki. - +* Better history viewing +* Recent changes page +* More markups