mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-12-12 05:20:26 +00:00
Implement saving changes
This commit is contained in:
parent
2879096258
commit
17c97ebe9a
122
handlers.go
122
handlers.go
@ -2,8 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Boilerplate code present in many handlers. Good to have it.
|
// Boilerplate code present in many handlers. Good to have it.
|
||||||
@ -62,7 +67,118 @@ func HandlerRename(w http.ResponseWriter, r *http.Request) {
|
|||||||
log.Println("Attempt to access an unimplemented thing")
|
log.Println("Attempt to access an unimplemented thing")
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlerUpdate(w http.ResponseWriter, r *http.Request) {
|
func makeTagsSlice(responseTagsString string) (ret []string) {
|
||||||
w.WriteHeader(http.StatusNotImplemented)
|
// `responseTagsString` is string like "foo,, bar,kek". Whitespace around commas is insignificant. Expected output: []string{"foo", "bar", "kek"}
|
||||||
log.Println("Attempt to access an unimplemented thing")
|
for _, tag := range strings.Split(responseTagsString, ",") {
|
||||||
|
if trimmed := strings.TrimSpace(tag); "" == trimmed {
|
||||||
|
ret = append(ret, trimmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an existing hypha it exists in `hyphae` or create a new one. If it `isNew`, you'll have to insert it to `hyphae` yourself.
|
||||||
|
func getHypha(name string) (*Hypha, bool) {
|
||||||
|
log.Println("Accessing hypha", name)
|
||||||
|
if h, ok := hyphae[name]; ok {
|
||||||
|
log.Println("Got hypha", name)
|
||||||
|
return h, false
|
||||||
|
}
|
||||||
|
log.Println("Create hypha", name)
|
||||||
|
h := &Hypha{
|
||||||
|
FullName: name,
|
||||||
|
Path: filepath.Join(rootWikiDir, name),
|
||||||
|
Revisions: make(map[string]*Revision),
|
||||||
|
parentName: filepath.Dir(name),
|
||||||
|
}
|
||||||
|
return h, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new revison for hypha `h`. All data is fetched from `r`, except for BinaryMime and BinaryPath which require additional processing. You'll have te insert the revision to `h` yourself.
|
||||||
|
func revisionFromHttpData(h *Hypha, r *http.Request) *Revision {
|
||||||
|
idStr := strconv.Itoa(h.NewestRevisionInt() + 1)
|
||||||
|
log.Println(idStr)
|
||||||
|
rev := &Revision{
|
||||||
|
Id: h.NewestRevisionInt() + 1,
|
||||||
|
FullName: h.FullName,
|
||||||
|
Tags: makeTagsSlice(r.PostFormValue("tags")),
|
||||||
|
Comment: r.PostFormValue("comment"),
|
||||||
|
Author: r.PostFormValue("author"),
|
||||||
|
Time: int(time.Now().Unix()),
|
||||||
|
TextMime: r.PostFormValue("text_mime"),
|
||||||
|
TextPath: filepath.Join(h.Path, idStr+".txt"),
|
||||||
|
// Left: BinaryMime, BinaryPath
|
||||||
|
}
|
||||||
|
return rev
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTextFileFromHttpData(rev *Revision, r *http.Request) error {
|
||||||
|
data := []byte(r.PostFormValue("text"))
|
||||||
|
err := ioutil.WriteFile(rev.TextPath, data, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write", len(data), "bytes to", rev.TextPath)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeBinaryFileFromHttpData(h *Hypha, oldRev Revision, newRev *Revision, r *http.Request) error {
|
||||||
|
// 10 MB file size limit
|
||||||
|
r.ParseMultipartForm(10 << 20)
|
||||||
|
// Read file
|
||||||
|
file, handler, err := r.FormFile("binary")
|
||||||
|
if file != nil {
|
||||||
|
defer file.Close()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Println("No binary data passed for", newRev.FullName)
|
||||||
|
newRev.BinaryMime = oldRev.BinaryMime
|
||||||
|
newRev.BinaryPath = oldRev.BinaryPath
|
||||||
|
log.Println("Set previous revision's binary data")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newRev.BinaryMime = handler.Header.Get("Content-Type")
|
||||||
|
newRev.BinaryPath = filepath.Join(h.Path, newRev.IdAsStr()+".bin")
|
||||||
|
data, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println("Got", len(data), "of binary data for", newRev.FullName)
|
||||||
|
err = ioutil.WriteFile(newRev.BinaryPath, data, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to write", len(data), "bytes to", newRev.TextPath)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println("Written", len(data), "of binary data for", newRev.FullName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandlerUpdate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
log.Println("Attempt to update hypha", mux.Vars(r)["hypha"])
|
||||||
|
h, isNew := getHypha(vars["hypha"])
|
||||||
|
oldRev := h.GetNewestRevision()
|
||||||
|
newRev := revisionFromHttpData(h, r)
|
||||||
|
|
||||||
|
if isNew {
|
||||||
|
h.CreateDir()
|
||||||
|
}
|
||||||
|
err := writeTextFileFromHttpData(newRev, r)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = writeBinaryFileFromHttpData(h, oldRev, newRev, r)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Revisions[newRev.IdAsStr()] = newRev
|
||||||
|
h.SaveJson()
|
||||||
|
|
||||||
|
log.Println("Current hyphae storage is", hyphae)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte("Saved successfully"))
|
||||||
}
|
}
|
||||||
|
47
hypha.go
47
hypha.go
@ -1,20 +1,24 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Hypha struct {
|
type Hypha struct {
|
||||||
FullName string
|
FullName string `json:"-"`
|
||||||
Path string
|
Path string `json:"-"`
|
||||||
ViewCount int `json:"views"`
|
ViewCount int `json:"views"`
|
||||||
Deleted bool `json:"deleted"`
|
Deleted bool `json:"deleted"`
|
||||||
Revisions map[string]*Revision `json:"revisions"`
|
Revisions map[string]*Revision `json:"revisions"`
|
||||||
ChildrenNames []string
|
ChildrenNames []string `json:"-"`
|
||||||
parentName string
|
parentName string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +43,15 @@ func (h *Hypha) Name() string {
|
|||||||
return h.FullName
|
return h.FullName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Hypha) GetNewestRevision() Revision {
|
||||||
|
return *h.Revisions[h.NewestRevision()]
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Hypha) NewestRevision() string {
|
func (h *Hypha) NewestRevision() string {
|
||||||
|
return strconv.Itoa(h.NewestRevisionInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hypha) NewestRevisionInt() int {
|
||||||
var largest int
|
var largest int
|
||||||
for k, _ := range h.Revisions {
|
for k, _ := range h.Revisions {
|
||||||
rev, _ := strconv.Atoi(k)
|
rev, _ := strconv.Atoi(k)
|
||||||
@ -47,16 +59,38 @@ func (h *Hypha) NewestRevision() string {
|
|||||||
largest = rev
|
largest = rev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return strconv.Itoa(largest)
|
return largest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hypha) MetaJsonPath() string {
|
||||||
|
return filepath.Join(h.Path, "meta.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hypha) CreateDir() error {
|
||||||
|
return os.MkdirAll(h.Path, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hypha) ParentName() string {
|
func (h *Hypha) ParentName() string {
|
||||||
return h.parentName
|
return h.parentName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Hypha) SaveJson() {
|
||||||
|
data, err := json.Marshal(h)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to create JSON of hypha.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(h.MetaJsonPath(), data, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to save JSON of hypha.", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Saved JSON data of", h.FullName)
|
||||||
|
}
|
||||||
|
|
||||||
func ActionEdit(hyphaName string, w http.ResponseWriter) {
|
func ActionEdit(hyphaName string, w http.ResponseWriter) {
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
var initContents, initTextMime, initBinaryMime, initTags string
|
var initContents, initTextMime, initTags string
|
||||||
hypha, ok := hyphae[hyphaName]
|
hypha, ok := hyphae[hyphaName]
|
||||||
if !ok {
|
if !ok {
|
||||||
initContents = "Describe " + hyphaName + "here."
|
initContents = "Describe " + hyphaName + "here."
|
||||||
@ -71,10 +105,9 @@ func ActionEdit(hyphaName string, w http.ResponseWriter) {
|
|||||||
}
|
}
|
||||||
initContents = string(contents)
|
initContents = string(contents)
|
||||||
initTextMime = newestRev.TextMime
|
initTextMime = newestRev.TextMime
|
||||||
initBinaryMime = newestRev.BinaryMime
|
|
||||||
initTags = strings.Join(newestRev.Tags, ",")
|
initTags = strings.Join(newestRev.Tags, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write([]byte(EditHyphaPage(hyphaName, initTextMime, initBinaryMime, initContents, initTags)))
|
w.Write([]byte(EditHyphaPage(hyphaName, initTextMime, initContents, initTags)))
|
||||||
}
|
}
|
||||||
|
4
main.go
4
main.go
@ -101,7 +101,9 @@ func main() {
|
|||||||
r.Queries("action", "rename", "to", hyphaPattern).Path(hyphaUrl).
|
r.Queries("action", "rename", "to", hyphaPattern).Path(hyphaUrl).
|
||||||
HandlerFunc(HandlerRename)
|
HandlerFunc(HandlerRename)
|
||||||
|
|
||||||
r.Queries("action", "update").Path(hyphaUrl).
|
r.Queries(
|
||||||
|
"action", "update",
|
||||||
|
).Path(hyphaUrl).Methods("POST").
|
||||||
HandlerFunc(HandlerUpdate)
|
HandlerFunc(HandlerUpdate)
|
||||||
|
|
||||||
r.HandleFunc(hyphaUrl, HandlerView)
|
r.HandleFunc(hyphaUrl, HandlerView)
|
||||||
|
17
render.go
17
render.go
@ -24,12 +24,15 @@ func Layout(f map[string]string) string {
|
|||||||
return fmt.Sprintf(template, f["title"], f["head"], f["header"], f["main"], f["sidebar"], FooterText, f["bodyBottom"])
|
return fmt.Sprintf(template, f["title"], f["head"], f["header"], f["main"], f["sidebar"], FooterText, f["bodyBottom"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func EditHyphaPage(name, text_mime, binary_mime, content, tags string) string {
|
func EditHyphaPage(name, text_mime, content, tags string) string {
|
||||||
template := `
|
template := `
|
||||||
<div class="naviwrapper">
|
<div class="naviwrapper">
|
||||||
<form class="naviwrapper__edit edit-box">
|
<form class="naviwrapper__edit edit-box"
|
||||||
|
method="POST"
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
action="?action=update">
|
||||||
<div class="naviwrapper__buttons">
|
<div class="naviwrapper__buttons">
|
||||||
<input type="submit" name="action" value="update"/>
|
<input type="submit" value="update"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="edit-box__left">
|
<div class="edit-box__left">
|
||||||
@ -48,17 +51,13 @@ func EditHyphaPage(name, text_mime, binary_mime, content, tags string) string {
|
|||||||
<p>Good types are <code>text/markdown</code> and <code>text/plain</code></p>
|
<p>Good types are <code>text/markdown</code> and <code>text/plain</code></p>
|
||||||
<input type="text" name="text_mime" value="%s"/>
|
<input type="text" name="text_mime" value="%s"/>
|
||||||
|
|
||||||
<h4>Media MIME-type</h4>
|
|
||||||
<p>For now, only image formats are supported. Choose any, but <code>image/jpeg</code> and <code>image/png</code> are recommended</p>
|
|
||||||
<input type="text" name="binary_mime" value="%s"/>
|
|
||||||
|
|
||||||
<h4>Revision comment</h4>
|
<h4>Revision comment</h4>
|
||||||
<p>Please make your comment helpful</p>
|
<p>Please make your comment helpful</p>
|
||||||
<input type="text" name="comment" value="%s"/>
|
<input type="text" name="comment" value="%s"/>
|
||||||
|
|
||||||
<h4>Edit tags</h4>
|
<h4>Edit tags</h4>
|
||||||
<p>Tags are separated by commas, whitespace is ignored</p>
|
<p>Tags are separated by commas, whitespace is ignored</p>
|
||||||
<input type="text" name="comment" value="%s"/>
|
<input type="text" name="tags" value="%s"/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -67,7 +66,7 @@ func EditHyphaPage(name, text_mime, binary_mime, content, tags string) string {
|
|||||||
"title": fmt.Sprintf(TitleTemplate, "Edit "+name),
|
"title": fmt.Sprintf(TitleTemplate, "Edit "+name),
|
||||||
"head": DefaultStyles,
|
"head": DefaultStyles,
|
||||||
"header": `<h1 class="header__edit-title">Edit ` + name + `</h1>`,
|
"header": `<h1 class="header__edit-title">Edit ` + name + `</h1>`,
|
||||||
"main": fmt.Sprintf(template, content, text_mime, binary_mime, "Update "+name, tags),
|
"main": fmt.Sprintf(template, content, text_mime, "Update "+name, tags),
|
||||||
"sidebar": "",
|
"sidebar": "",
|
||||||
"footer": FooterText,
|
"footer": FooterText,
|
||||||
}
|
}
|
||||||
|
14
revision.go
14
revision.go
@ -6,11 +6,13 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// In different places, revision variable is called `r`. But when there is an http.Request as well, the revision becomes `rev`. TODO: name them consistently.
|
||||||
type Revision struct {
|
type Revision struct {
|
||||||
Id int
|
Id int `json:"-"`
|
||||||
FullName string
|
FullName string `json:"-"`
|
||||||
Tags []string `json:"tags"`
|
Tags []string `json:"tags"`
|
||||||
ShortName string `json:"name"`
|
ShortName string `json:"name"`
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
@ -18,8 +20,12 @@ type Revision struct {
|
|||||||
Time int `json:"time"`
|
Time int `json:"time"`
|
||||||
TextMime string `json:"text_mime"`
|
TextMime string `json:"text_mime"`
|
||||||
BinaryMime string `json:"binary_mime"`
|
BinaryMime string `json:"binary_mime"`
|
||||||
TextPath string
|
TextPath string `json:"-"`
|
||||||
BinaryPath string
|
BinaryPath string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Revision) IdAsStr() string {
|
||||||
|
return strconv.Itoa(r.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// During initialisation, it is guaranteed that r.BinaryMime is set to "" if the revision has no binary data.
|
// During initialisation, it is guaranteed that r.BinaryMime is set to "" if the revision has no binary data.
|
||||||
|
BIN
w/m/Fruit/Apple/2.bin
Normal file
BIN
w/m/Fruit/Apple/2.bin
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
3
w/m/Fruit/Apple/2.txt
Normal file
3
w/m/Fruit/Apple/2.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Красное яблоко. Для съёмки использовалась белая бумага позади и над яблоком, и фотовспышка SB-600 в 1/4 мощности.
|
||||||
|
|
||||||
|
Source: https://commons.wikimedia.org/wiki/File:Red_Apple.jpg
|
6
w/m/Fruit/Apple/3.txt
Normal file
6
w/m/Fruit/Apple/3.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Mycorrhiza is pure happiness
|
||||||
|
|
||||||
|
Красное яблоко. Для съёмки использовалась белая бумага позади и над яблоком, и фотовспышка SB-600 в 1/4 мощности.
|
||||||
|
|
||||||
|
Source: https://commons.wikimedia.org/wiki/File:Red_Apple.jpg
|
||||||
|
|
@ -1,13 +1 @@
|
|||||||
{
|
{"views":0,"deleted":false,"revisions":{"1":{"tags":["img"],"name":"Apple","comment":"add apple pic hehehe","author":"bouncepaw","time":1591639464,"text_mime":"text/plain","binary_mime":"image/jpeg"},"2":{"tags":null,"name":"","comment":"Update Fruit/Apple","author":"","time":1592570366,"text_mime":"text/plain","binary_mime":"image/jpeg"},"3":{"tags":null,"name":"","comment":"Test fs dumping","author":"","time":1592570926,"text_mime":"text/plain","binary_mime":"image/jpeg"}}}
|
||||||
"revisions":{
|
|
||||||
"1":{
|
|
||||||
"name": "Apple",
|
|
||||||
"time": 1591639464,
|
|
||||||
"author": "bouncepaw",
|
|
||||||
"comment": "add apple pic hehehe",
|
|
||||||
"tags": ["img"],
|
|
||||||
"text_mime": "text/plain",
|
|
||||||
"binary_mime": "image/jpeg"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user