mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-01-31 19:39:09 +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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -33,44 +32,6 @@ func (h *Hypha) Invalidate(err error) *Hypha {
|
|||||||
return h
|
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 {
|
func (s *Storage) Open(name string) *Hypha {
|
||||||
h := &Hypha{
|
h := &Hypha{
|
||||||
Exists: true,
|
Exists: true,
|
||||||
@ -107,14 +68,6 @@ func (s *Storage) Open(name string) *Hypha {
|
|||||||
return h
|
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`.
|
// OnRevision tries to change to a revision specified by `id`.
|
||||||
func (h *Hypha) OnRevision(id string) *Hypha {
|
func (h *Hypha) OnRevision(id string) *Hypha {
|
||||||
if h.Invalid || !h.Exists {
|
if h.Invalid || !h.Exists {
|
||||||
@ -133,18 +86,6 @@ func (h *Hypha) OnRevision(id string) *Hypha {
|
|||||||
return h
|
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) {
|
func (h *Hypha) PlainLog(s string) {
|
||||||
if h.Exists {
|
if h.Exists {
|
||||||
log.Println(h.FullName, h.actual.Id, s)
|
log.Println(h.FullName, h.actual.Id, s)
|
||||||
@ -153,74 +94,74 @@ func (h *Hypha) PlainLog(s string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hypha) mimeTypeForActionRaw() string {
|
func (h *Hypha) LogSuccMaybe(succMsg string) *Hypha {
|
||||||
// If text mime type is text/html, it is not good as it will be rendered.
|
if h.Invalid {
|
||||||
if h.actual.TextMime == "text/html" {
|
h.PlainLog(h.Err.Error())
|
||||||
return "text/plain"
|
} else {
|
||||||
|
h.PlainLog(succMsg)
|
||||||
}
|
}
|
||||||
return h.actual.TextMime
|
return h
|
||||||
}
|
|
||||||
|
|
||||||
// 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 != ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionRaw is used with `?action=raw`.
|
// ActionRaw is used with `?action=raw`.
|
||||||
// It writes text content of the revision without any parsing or rendering.
|
// 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)
|
fileContents, err := ioutil.ReadFile(h.actual.TextPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return h.Invalidate(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", h.mimeTypeForActionRaw())
|
w.Header().Set("Content-Type", h.mimeTypeForActionRaw())
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write(fileContents)
|
w.Write(fileContents)
|
||||||
h.PlainLog("Serving raw text")
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionBinary is used with `?action=binary`.
|
// ActionBinary is used with `?action=binary`.
|
||||||
// It writes contents of binary content file.
|
// 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)
|
fileContents, err := ioutil.ReadFile(h.actual.BinaryPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return h.Invalidate(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", h.actual.BinaryMime)
|
w.Header().Set("Content-Type", h.actual.BinaryMime)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write(fileContents)
|
w.Write(fileContents)
|
||||||
h.PlainLog("Serving raw text")
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionZen is used with `?action=zen`.
|
// ActionZen is used with `?action=zen`.
|
||||||
// It renders the hypha but without any layout or styles. Pure. 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()
|
html, err := h.asHtml()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return h.Invalidate(err)
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write([]byte(html))
|
w.Write([]byte(html))
|
||||||
h.PlainLog("Rendering zen")
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionView is used with `?action=view` or no action at all.
|
// ActionView is used with `?action=view` or no action at all.
|
||||||
// It renders the page, the layout and everything else.
|
// 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 html string
|
||||||
var err error
|
var err error
|
||||||
if h.Exists {
|
if h.Exists {
|
||||||
html, err = h.asHtml()
|
html, err = h.asHtml()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return h.Invalidate(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
@ -230,7 +171,7 @@ func (h *Hypha) ActionView(w http.ResponseWriter, renderExists, renderNotExists
|
|||||||
} else {
|
} else {
|
||||||
w.Write([]byte(renderNotExists(h.FullName, "")))
|
w.Write([]byte(renderNotExists(h.FullName, "")))
|
||||||
}
|
}
|
||||||
h.PlainLog("Rendering hypha view")
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateDirIfNeeded creates directory where the hypha must reside if needed.
|
// CreateDirIfNeeded creates directory where the hypha must reside if needed.
|
||||||
|
45
handlers.go
45
handlers.go
@ -2,13 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
// "io/ioutil"
|
|
||||||
// "log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
// "path/filepath"
|
|
||||||
// "strconv"
|
|
||||||
// "strings"
|
|
||||||
// "time"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/fs"
|
"github.com/bouncepaw/mycorrhiza/fs"
|
||||||
"github.com/bouncepaw/mycorrhiza/render"
|
"github.com/bouncepaw/mycorrhiza/render"
|
||||||
@ -18,40 +12,31 @@ import (
|
|||||||
// There are handlers below. See main() for their usage.
|
// There are handlers below. See main() for their usage.
|
||||||
|
|
||||||
// Boilerplate code present in many handlers. Good to have it.
|
// 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)
|
vars := mux.Vars(rq)
|
||||||
h := fs.Hs.Open(vars["hypha"]).OnRevision(RevInMap(vars))
|
return fs.Hs.Open(vars["hypha"]).OnRevision(RevInMap(vars))
|
||||||
if h.Invalid {
|
|
||||||
log.Println(h.Err)
|
|
||||||
return h, false
|
|
||||||
}
|
|
||||||
return h, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlerRaw(w http.ResponseWriter, rq *http.Request) {
|
func HandlerRaw(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println("?action=raw")
|
log.Println("?action=raw")
|
||||||
if h, ok := HandlerBase(w, rq); ok {
|
HandlerBase(w, rq).ActionRaw(w).LogSuccMaybe("Serving raw text")
|
||||||
h.ActionRaw(w)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlerBinary(w http.ResponseWriter, rq *http.Request) {
|
func HandlerBinary(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println("?action=binary")
|
log.Println("?action=binary")
|
||||||
if h, ok := HandlerBase(w, rq); ok {
|
HandlerBase(w, rq).ActionBinary(w).LogSuccMaybe("Serving binary data")
|
||||||
h.ActionBinary(w)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlerZen(w http.ResponseWriter, rq *http.Request) {
|
func HandlerZen(w http.ResponseWriter, rq *http.Request) {
|
||||||
if h, ok := HandlerBase(w, rq); ok {
|
log.Println("?action=zen")
|
||||||
h.ActionZen(w)
|
HandlerBase(w, rq).ActionZen(w).LogSuccMaybe("Rendering zen")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlerView(w http.ResponseWriter, rq *http.Request) {
|
func HandlerView(w http.ResponseWriter, rq *http.Request) {
|
||||||
if h, ok := HandlerBase(w, rq); ok {
|
log.Println("?action=view")
|
||||||
h.ActionView(w, render.HyphaPage, render.Hypha404)
|
HandlerBase(w, rq).
|
||||||
}
|
ActionView(w, render.HyphaPage, render.Hypha404).
|
||||||
|
LogSuccMaybe("Rendering hypha view")
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlerEdit(w http.ResponseWriter, rq *http.Request) {
|
func HandlerEdit(w http.ResponseWriter, rq *http.Request) {
|
||||||
@ -72,14 +57,12 @@ func HandlerUpdate(w http.ResponseWriter, rq *http.Request) {
|
|||||||
WriteTextFileFromHttpData(rq).
|
WriteTextFileFromHttpData(rq).
|
||||||
WriteBinaryFileFromHttpData(rq).
|
WriteBinaryFileFromHttpData(rq).
|
||||||
SaveJson().
|
SaveJson().
|
||||||
Store()
|
Store().
|
||||||
|
LogSuccMaybe("Saved changes")
|
||||||
if h.Invalid {
|
|
||||||
log.Println(h.Err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if !h.Invalid {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
w.Write([]byte(render.HyphaUpdateOk(h)))
|
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 @@
|
|||||||
{
|
{
|
||||||
|
"views": 0,
|
||||||
|
"deleted": false,
|
||||||
"revisions": {
|
"revisions": {
|
||||||
"1": {
|
"1": {
|
||||||
|
"tags": [
|
||||||
|
"fetus",
|
||||||
|
"tasty"
|
||||||
|
],
|
||||||
"name": "Fruit",
|
"name": "Fruit",
|
||||||
"time": 1591635559,
|
|
||||||
"author": "bouncepaw",
|
|
||||||
"comment": "create Fruit",
|
"comment": "create Fruit",
|
||||||
"tags": ["fetus", "tasty"],
|
"author": "bouncepaw",
|
||||||
|
"time": 1591635559,
|
||||||
"text_mime": "text/markdown",
|
"text_mime": "text/markdown",
|
||||||
"text_name": "1.md"
|
"binary_mime": "",
|
||||||
|
"text_name": "1.md",
|
||||||
|
"binary_name": ""
|
||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
|
"tags": [
|
||||||
|
"fetus",
|
||||||
|
"tasty"
|
||||||
|
],
|
||||||
"name": "Fruit",
|
"name": "Fruit",
|
||||||
"time": 1591636222,
|
|
||||||
"author": "fungimaster",
|
|
||||||
"comment": "update Fruit",
|
"comment": "update Fruit",
|
||||||
"tags": ["fetus", "tasty"],
|
"author": "fungimaster",
|
||||||
|
"time": 1591636222,
|
||||||
"text_mime": "text/markdown",
|
"text_mime": "text/markdown",
|
||||||
"text_name": "2.md"
|
"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