mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-01-18 22:52:50 +00:00
Final touches. Refactoring is done?
This commit is contained in:
parent
d36f86f715
commit
5f79930539
81
fs/data.go
Normal file
81
fs/data.go
Normal file
@ -0,0 +1,81 @@
|
||||
// This file contains methods for Hypha that calculate data about the hypha based on known information.
|
||||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||
)
|
||||
|
||||
func (h *Hypha) MetaJsonPath() string {
|
||||
return filepath.Join(h.Path(), "meta.json")
|
||||
}
|
||||
|
||||
func (h *Hypha) Path() string {
|
||||
return filepath.Join(cfg.WikiDir, h.FullName)
|
||||
}
|
||||
|
||||
func (h *Hypha) TextPath() string {
|
||||
return h.actual.TextPath
|
||||
}
|
||||
|
||||
func (h *Hypha) parentName() string {
|
||||
return filepath.Dir(h.FullName)
|
||||
}
|
||||
|
||||
// hasBinaryData returns true if the revision has any binary data associated.
|
||||
// During initialisation, it is guaranteed that r.BinaryMime is set to "" if the revision has no binary data. (is it?)
|
||||
func (h *Hypha) hasBinaryData() bool {
|
||||
return h.actual.BinaryMime != ""
|
||||
}
|
||||
|
||||
func (h *Hypha) TagsJoined() string {
|
||||
if h.Exists {
|
||||
return strings.Join(h.actual.Tags, ", ")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *Hypha) TextMime() string {
|
||||
if h.Exists {
|
||||
return h.actual.TextMime
|
||||
}
|
||||
return "text/markdown"
|
||||
}
|
||||
|
||||
func (h *Hypha) mimeTypeForActionRaw() string {
|
||||
// If text mime type is text/html, it is not good as it will be rendered.
|
||||
if h.actual.TextMime == "text/html" {
|
||||
return "text/plain"
|
||||
}
|
||||
return h.actual.TextMime
|
||||
}
|
||||
|
||||
// NewestId finds the largest id among all revisions.
|
||||
func (h *Hypha) NewestId() string {
|
||||
var largest int
|
||||
for k, _ := range h.Revisions {
|
||||
id, _ := strconv.Atoi(k)
|
||||
if id > largest {
|
||||
largest = id
|
||||
}
|
||||
}
|
||||
return strconv.Itoa(largest)
|
||||
}
|
||||
|
||||
func (h *Hypha) TextContent() string {
|
||||
if h.Exists {
|
||||
contents, err := ioutil.ReadFile(h.TextPath())
|
||||
if err != nil {
|
||||
log.Println("Could not read", h.FullName)
|
||||
return "Error: could not hypha text content file. It is recommended to cancel editing. Please contact the wiki admin. If you are the admin, see the logs."
|
||||
}
|
||||
return string(contents)
|
||||
}
|
||||
return fmt.Sprintf(cfg.DescribeHyphaHerePattern, h.FullName)
|
||||
}
|
113
fs/hypha.go
113
fs/hypha.go
@ -3,7 +3,6 @@ package fs
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
@ -33,44 +32,6 @@ func (h *Hypha) Invalidate(err error) *Hypha {
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *Hypha) MetaJsonPath() string {
|
||||
return filepath.Join(h.Path(), "meta.json")
|
||||
}
|
||||
|
||||
func (h *Hypha) Path() string {
|
||||
return filepath.Join(cfg.WikiDir, h.FullName)
|
||||
}
|
||||
|
||||
func (h *Hypha) TextPath() string {
|
||||
return h.actual.TextPath
|
||||
}
|
||||
|
||||
func (h *Hypha) TagsJoined() string {
|
||||
if h.Exists {
|
||||
return strings.Join(h.actual.Tags, ", ")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *Hypha) TextMime() string {
|
||||
if h.Exists {
|
||||
return h.actual.TextMime
|
||||
}
|
||||
return "text/markdown"
|
||||
}
|
||||
|
||||
func (h *Hypha) TextContent() string {
|
||||
if h.Exists {
|
||||
contents, err := ioutil.ReadFile(h.TextPath())
|
||||
if err != nil {
|
||||
log.Println("Could not read", h.FullName)
|
||||
return "Error: could not hypha text content file. It is recommended to cancel editing. Please contact the wiki admin. If you are the admin, see the logs."
|
||||
}
|
||||
return string(contents)
|
||||
}
|
||||
return fmt.Sprintf(cfg.DescribeHyphaHerePattern, h.FullName)
|
||||
}
|
||||
|
||||
func (s *Storage) Open(name string) *Hypha {
|
||||
h := &Hypha{
|
||||
Exists: true,
|
||||
@ -107,14 +68,6 @@ func (s *Storage) Open(name string) *Hypha {
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *Hypha) parentName() string {
|
||||
return filepath.Dir(h.FullName)
|
||||
}
|
||||
|
||||
func (h *Hypha) metaJsonPath() string {
|
||||
return filepath.Join(cfg.WikiDir, h.FullName, "meta.json")
|
||||
}
|
||||
|
||||
// OnRevision tries to change to a revision specified by `id`.
|
||||
func (h *Hypha) OnRevision(id string) *Hypha {
|
||||
if h.Invalid || !h.Exists {
|
||||
@ -133,18 +86,6 @@ func (h *Hypha) OnRevision(id string) *Hypha {
|
||||
return h
|
||||
}
|
||||
|
||||
// NewestId finds the largest id among all revisions.
|
||||
func (h *Hypha) NewestId() string {
|
||||
var largest int
|
||||
for k, _ := range h.Revisions {
|
||||
id, _ := strconv.Atoi(k)
|
||||
if id > largest {
|
||||
largest = id
|
||||
}
|
||||
}
|
||||
return strconv.Itoa(largest)
|
||||
}
|
||||
|
||||
func (h *Hypha) PlainLog(s string) {
|
||||
if h.Exists {
|
||||
log.Println(h.FullName, h.actual.Id, s)
|
||||
@ -153,74 +94,74 @@ func (h *Hypha) PlainLog(s string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hypha) mimeTypeForActionRaw() string {
|
||||
// If text mime type is text/html, it is not good as it will be rendered.
|
||||
if h.actual.TextMime == "text/html" {
|
||||
return "text/plain"
|
||||
func (h *Hypha) LogSuccMaybe(succMsg string) *Hypha {
|
||||
if h.Invalid {
|
||||
h.PlainLog(h.Err.Error())
|
||||
} else {
|
||||
h.PlainLog(succMsg)
|
||||
}
|
||||
return h.actual.TextMime
|
||||
}
|
||||
|
||||
// hasBinaryData returns true if the revision has any binary data associated.
|
||||
// During initialisation, it is guaranteed that r.BinaryMime is set to "" if the revision has no binary data. (is it?)
|
||||
func (h *Hypha) hasBinaryData() bool {
|
||||
return h.actual.BinaryMime != ""
|
||||
return h
|
||||
}
|
||||
|
||||
// ActionRaw is used with `?action=raw`.
|
||||
// It writes text content of the revision without any parsing or rendering.
|
||||
func (h *Hypha) ActionRaw(w http.ResponseWriter) {
|
||||
func (h *Hypha) ActionRaw(w http.ResponseWriter) *Hypha {
|
||||
if h.Invalid {
|
||||
return h
|
||||
}
|
||||
fileContents, err := ioutil.ReadFile(h.actual.TextPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
return h.Invalidate(err)
|
||||
}
|
||||
w.Header().Set("Content-Type", h.mimeTypeForActionRaw())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(fileContents)
|
||||
h.PlainLog("Serving raw text")
|
||||
return h
|
||||
}
|
||||
|
||||
// ActionBinary is used with `?action=binary`.
|
||||
// It writes contents of binary content file.
|
||||
func (h *Hypha) ActionBinary(w http.ResponseWriter) {
|
||||
func (h *Hypha) ActionBinary(w http.ResponseWriter) *Hypha {
|
||||
if h.Invalid {
|
||||
return h
|
||||
}
|
||||
fileContents, err := ioutil.ReadFile(h.actual.BinaryPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
return h.Invalidate(err)
|
||||
}
|
||||
w.Header().Set("Content-Type", h.actual.BinaryMime)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(fileContents)
|
||||
h.PlainLog("Serving raw text")
|
||||
return h
|
||||
}
|
||||
|
||||
// ActionZen is used with `?action=zen`.
|
||||
// It renders the hypha but without any layout or styles. Pure. Zen.
|
||||
func (h *Hypha) ActionZen(w http.ResponseWriter) {
|
||||
func (h *Hypha) ActionZen(w http.ResponseWriter) *Hypha {
|
||||
if h.Invalid {
|
||||
return h
|
||||
}
|
||||
html, err := h.asHtml()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
return h.Invalidate(err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(html))
|
||||
h.PlainLog("Rendering zen")
|
||||
return h
|
||||
}
|
||||
|
||||
// ActionView is used with `?action=view` or no action at all.
|
||||
// It renders the page, the layout and everything else.
|
||||
func (h *Hypha) ActionView(w http.ResponseWriter, renderExists, renderNotExists func(string, string) string) {
|
||||
func (h *Hypha) ActionView(w http.ResponseWriter, renderExists, renderNotExists func(string, string) string) *Hypha {
|
||||
var html string
|
||||
var err error
|
||||
if h.Exists {
|
||||
html, err = h.asHtml()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
return h.Invalidate(err)
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||
@ -230,7 +171,7 @@ func (h *Hypha) ActionView(w http.ResponseWriter, renderExists, renderNotExists
|
||||
} else {
|
||||
w.Write([]byte(renderNotExists(h.FullName, "")))
|
||||
}
|
||||
h.PlainLog("Rendering hypha view")
|
||||
return h
|
||||
}
|
||||
|
||||
// CreateDirIfNeeded creates directory where the hypha must reside if needed.
|
||||
|
49
handlers.go
49
handlers.go
@ -2,13 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
// "io/ioutil"
|
||||
// "log"
|
||||
"net/http"
|
||||
// "path/filepath"
|
||||
// "strconv"
|
||||
// "strings"
|
||||
// "time"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/fs"
|
||||
"github.com/bouncepaw/mycorrhiza/render"
|
||||
@ -18,40 +12,31 @@ import (
|
||||
// There are handlers below. See main() for their usage.
|
||||
|
||||
// Boilerplate code present in many handlers. Good to have it.
|
||||
func HandlerBase(w http.ResponseWriter, rq *http.Request) (*fs.Hypha, bool) {
|
||||
func HandlerBase(w http.ResponseWriter, rq *http.Request) *fs.Hypha {
|
||||
vars := mux.Vars(rq)
|
||||
h := fs.Hs.Open(vars["hypha"]).OnRevision(RevInMap(vars))
|
||||
if h.Invalid {
|
||||
log.Println(h.Err)
|
||||
return h, false
|
||||
}
|
||||
return h, true
|
||||
return fs.Hs.Open(vars["hypha"]).OnRevision(RevInMap(vars))
|
||||
}
|
||||
|
||||
func HandlerRaw(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println("?action=raw")
|
||||
if h, ok := HandlerBase(w, rq); ok {
|
||||
h.ActionRaw(w)
|
||||
}
|
||||
HandlerBase(w, rq).ActionRaw(w).LogSuccMaybe("Serving raw text")
|
||||
}
|
||||
|
||||
func HandlerBinary(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println("?action=binary")
|
||||
if h, ok := HandlerBase(w, rq); ok {
|
||||
h.ActionBinary(w)
|
||||
}
|
||||
HandlerBase(w, rq).ActionBinary(w).LogSuccMaybe("Serving binary data")
|
||||
}
|
||||
|
||||
func HandlerZen(w http.ResponseWriter, rq *http.Request) {
|
||||
if h, ok := HandlerBase(w, rq); ok {
|
||||
h.ActionZen(w)
|
||||
}
|
||||
log.Println("?action=zen")
|
||||
HandlerBase(w, rq).ActionZen(w).LogSuccMaybe("Rendering zen")
|
||||
}
|
||||
|
||||
func HandlerView(w http.ResponseWriter, rq *http.Request) {
|
||||
if h, ok := HandlerBase(w, rq); ok {
|
||||
h.ActionView(w, render.HyphaPage, render.Hypha404)
|
||||
}
|
||||
log.Println("?action=view")
|
||||
HandlerBase(w, rq).
|
||||
ActionView(w, render.HyphaPage, render.Hypha404).
|
||||
LogSuccMaybe("Rendering hypha view")
|
||||
}
|
||||
|
||||
func HandlerEdit(w http.ResponseWriter, rq *http.Request) {
|
||||
@ -72,14 +57,12 @@ func HandlerUpdate(w http.ResponseWriter, rq *http.Request) {
|
||||
WriteTextFileFromHttpData(rq).
|
||||
WriteBinaryFileFromHttpData(rq).
|
||||
SaveJson().
|
||||
Store()
|
||||
Store().
|
||||
LogSuccMaybe("Saved changes")
|
||||
|
||||
if h.Invalid {
|
||||
log.Println(h.Err)
|
||||
return
|
||||
if !h.Invalid {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.Write([]byte(render.HyphaUpdateOk(h)))
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.Write([]byte(render.HyphaUpdateOk(h)))
|
||||
}
|
||||
|
4
w/m/Fruit/3.markdown
Normal file
4
w/m/Fruit/3.markdown
Normal file
@ -0,0 +1,4 @@
|
||||
According to real *scientists*, fruit is a type of fetus. Most of them are tasty and cool, though some of them are really sour and depressing. Be careful when choosing fruit. Best ones are:
|
||||
|
||||
* [Apple](Apple)
|
||||
* [Pear](Pear)
|
@ -1,22 +1,47 @@
|
||||
{
|
||||
"revisions":{
|
||||
"1":{
|
||||
"name": "Fruit",
|
||||
"time": 1591635559,
|
||||
"author": "bouncepaw",
|
||||
"comment": "create Fruit",
|
||||
"tags": ["fetus", "tasty"],
|
||||
"text_mime": "text/markdown",
|
||||
"text_name": "1.md"
|
||||
},
|
||||
"2":{
|
||||
"name": "Fruit",
|
||||
"time": 1591636222,
|
||||
"author": "fungimaster",
|
||||
"comment": "update Fruit",
|
||||
"tags": ["fetus", "tasty"],
|
||||
"text_mime": "text/markdown",
|
||||
"text_name": "2.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
"views": 0,
|
||||
"deleted": false,
|
||||
"revisions": {
|
||||
"1": {
|
||||
"tags": [
|
||||
"fetus",
|
||||
"tasty"
|
||||
],
|
||||
"name": "Fruit",
|
||||
"comment": "create Fruit",
|
||||
"author": "bouncepaw",
|
||||
"time": 1591635559,
|
||||
"text_mime": "text/markdown",
|
||||
"binary_mime": "",
|
||||
"text_name": "1.md",
|
||||
"binary_name": ""
|
||||
},
|
||||
"2": {
|
||||
"tags": [
|
||||
"fetus",
|
||||
"tasty"
|
||||
],
|
||||
"name": "Fruit",
|
||||
"comment": "update Fruit",
|
||||
"author": "fungimaster",
|
||||
"time": 1591636222,
|
||||
"text_mime": "text/markdown",
|
||||
"binary_mime": "",
|
||||
"text_name": "2.md",
|
||||
"binary_name": ""
|
||||
},
|
||||
"3": {
|
||||
"tags": null,
|
||||
"name": "Fruit",
|
||||
"comment": "Update Fruit",
|
||||
"author": "",
|
||||
"time": 1593279957,
|
||||
"text_mime": "text/markdown",
|
||||
"binary_mime": "",
|
||||
"text_name": "3.markdown",
|
||||
"binary_name": ""
|
||||
}
|
||||
},
|
||||
"Invalid": false,
|
||||
"Err": null
|
||||
}
|
Loading…
Reference in New Issue
Block a user