1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2025-10-29 06:37:40 +00:00

Make mycelia somewhat working

This commit is contained in:
Timur Ismagilov
2020-07-04 00:20:56 +05:00
parent 82c8b7ee27
commit 33e7eddfd4
15 changed files with 229 additions and 88 deletions

View File

@@ -18,8 +18,8 @@ const (
HyphaUrl = `/{hypha:` + HyphaPattern + `}` HyphaUrl = `/{hypha:` + HyphaPattern + `}`
RevisionPattern = `[\d]+` RevisionPattern = `[\d]+`
RevQuery = `{rev:` + RevisionPattern + `}` RevQuery = `{rev:` + RevisionPattern + `}`
MyceliumPattern = `:` + HyphaPattern MyceliumPattern = `[^\s\d:/?&\\][^:?&\\/]*`
MyceliumUrl = `/{mycelium:` + MyceliumPattern + `}` MyceliumUrl = `/:{mycelium:` + MyceliumPattern + `}`
) )
var ( var (

View File

@@ -92,5 +92,5 @@ func navitreeEntry(name, class string) string {
return fmt.Sprintf(`<li class="navitree__entry %s"> return fmt.Sprintf(`<li class="navitree__entry %s">
<a class="navitree__link" href="/%s">%s</a> <a class="navitree__link" href="/%s">%s</a>
</li> </li>
`, class, util.DisplayToCanonical(name), filepath.Base(name)) `, class, ":"+util.DisplayToCanonical(name), filepath.Base(name))
} }

View File

@@ -21,7 +21,7 @@ func (h *Hypha) asHtml() (string, error) {
` `
// What about using <figure>? // What about using <figure>?
if h.hasBinaryData() { if h.hasBinaryData() {
ret += fmt.Sprintf(`<img src="/%s?action=binary&rev=%d" class="page__amnt"/>`, util.DisplayToCanonical(rev.FullName), rev.Id) ret += fmt.Sprintf(`<img src="/:%s?action=binary&rev=%d" class="page__amnt"/>`, util.DisplayToCanonical(rev.FullName), rev.Id)
} }
contents, err := ioutil.ReadFile(rev.TextPath) contents, err := ioutil.ReadFile(rev.TextPath)

View File

@@ -12,6 +12,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/bouncepaw/mycorrhiza/mycelium"
"github.com/bouncepaw/mycorrhiza/util" "github.com/bouncepaw/mycorrhiza/util"
) )
@@ -32,7 +33,16 @@ func (h *Hypha) Invalidate(err error) *Hypha {
return h return h
} }
func (s *Storage) Open(name string) *Hypha { func (s *Storage) OpenFromMap(m map[string]string) *Hypha {
name := mycelium.NameWithMyceliumInMap(m)
h := s.open(name)
if rev, ok := m["rev"]; ok {
return h.OnRevision(rev)
}
return h
}
func (s *Storage) open(name string) *Hypha {
name = util.UrlToCanonical(name) name = util.UrlToCanonical(name)
h := &Hypha{ h := &Hypha{
Exists: true, Exists: true,
@@ -110,6 +120,7 @@ func (h *Hypha) ActionRaw(w http.ResponseWriter) *Hypha {
if h.Invalid { if h.Invalid {
return h return h
} }
if h.Exists {
fileContents, err := ioutil.ReadFile(h.actual.TextPath) fileContents, err := ioutil.ReadFile(h.actual.TextPath)
if err != nil { if err != nil {
return h.Invalidate(err) return h.Invalidate(err)
@@ -117,6 +128,10 @@ func (h *Hypha) ActionRaw(w http.ResponseWriter) *Hypha {
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)
} else {
log.Println("Hypha", h.FullName, "has no actual revision")
w.WriteHeader(http.StatusNotFound)
}
return h return h
} }
@@ -126,6 +141,7 @@ func (h *Hypha) ActionBinary(w http.ResponseWriter) *Hypha {
if h.Invalid { if h.Invalid {
return h return h
} }
if h.Exists {
fileContents, err := ioutil.ReadFile(h.actual.BinaryPath) fileContents, err := ioutil.ReadFile(h.actual.BinaryPath)
if err != nil { if err != nil {
return h.Invalidate(err) return h.Invalidate(err)
@@ -133,6 +149,10 @@ func (h *Hypha) ActionBinary(w http.ResponseWriter) *Hypha {
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)
} else {
log.Println("Hypha", h.FullName, "has no actual revision")
w.WriteHeader(http.StatusNotFound)
}
return h return h
} }

View File

@@ -1,56 +0,0 @@
package fs
import (
"io/ioutil"
"log"
"github.com/bouncepaw/mycorrhiza/cfg"
)
var (
MainMycelium string
SystemMycelium string
)
func gatherDirNames(path string) map[string]struct{} {
res := make(map[string]struct{})
nodes, err := ioutil.ReadDir(path)
if err != nil {
log.Fatal(err)
}
for _, node := range nodes {
if node.IsDir() {
res[node.Name()] = struct{}{}
}
}
return res
}
func VerifyMycelia() {
var (
dirs = gatherDirNames(cfg.WikiDir)
mainPresent bool
systemPresent bool
)
for _, mycelium := range cfg.Mycelia {
switch mycelium.Type {
case "main":
mainPresent = true
MainMycelium = mycelium.Names[0]
case "system":
systemPresent = true
SystemMycelium = mycelium.Names[0]
}
// Check if there is a dir corresponding to the mycelium
if _, ok := dirs[mycelium.Names[0]]; !ok {
log.Fatal("No directory found for mycelium " + mycelium.Names[0])
}
}
if !mainPresent {
log.Fatal("No `main` mycelium given in config.json")
}
if !systemPresent {
log.Fatal("No `system` mycelium given in config.json")
}
log.Println("Mycelial dirs are present")
}

View File

@@ -14,7 +14,7 @@ import (
// 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 { func HandlerBase(w http.ResponseWriter, rq *http.Request) *fs.Hypha {
vars := mux.Vars(rq) vars := mux.Vars(rq)
return fs.Hs.Open(vars["hypha"]).OnRevision(RevInMap(vars)) return fs.Hs.OpenFromMap(vars).OnRevision(RevInMap(vars))
} }
func HandlerRaw(w http.ResponseWriter, rq *http.Request) { func HandlerRaw(w http.ResponseWriter, rq *http.Request) {
@@ -41,7 +41,7 @@ func HandlerView(w http.ResponseWriter, rq *http.Request) {
func HandlerEdit(w http.ResponseWriter, rq *http.Request) { func HandlerEdit(w http.ResponseWriter, rq *http.Request) {
vars := mux.Vars(rq) vars := mux.Vars(rq)
h := fs.Hs.Open(vars["hypha"]).OnRevision("0") h := fs.Hs.OpenFromMap(vars).OnRevision("0")
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(render.HyphaEdit(h)) w.Write(render.HyphaEdit(h))
@@ -51,7 +51,7 @@ func HandlerUpdate(w http.ResponseWriter, rq *http.Request) {
vars := mux.Vars(rq) vars := mux.Vars(rq)
log.Println("Attempt to update hypha", vars["hypha"]) log.Println("Attempt to update hypha", vars["hypha"])
h := fs.Hs. h := fs.Hs.
Open(vars["hypha"]). OpenFromMap(vars).
CreateDirIfNeeded(). CreateDirIfNeeded().
AddRevisionFromHttpData(rq). AddRevisionFromHttpData(rq).
WriteTextFileFromHttpData(rq). WriteTextFileFromHttpData(rq).

View File

@@ -10,6 +10,7 @@ import (
"github.com/bouncepaw/mycorrhiza/cfg" "github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/fs" "github.com/bouncepaw/mycorrhiza/fs"
"github.com/bouncepaw/mycorrhiza/mycelium"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
@@ -53,7 +54,7 @@ func main() {
log.Println("Welcome to MycorrhizaWiki α") log.Println("Welcome to MycorrhizaWiki α")
cfg.InitConfig(wikiDir) cfg.InitConfig(wikiDir)
log.Println("Indexing hyphae...") log.Println("Indexing hyphae...")
fs.VerifyMycelia() mycelium.Init()
fs.InitStorage() fs.InitStorage()
// Start server code. See handlers.go for handlers' implementations. // Start server code. See handlers.go for handlers' implementations.
@@ -78,6 +79,7 @@ func main() {
r.Queries("action", "update").Path(cfg.HyphaUrl). r.Queries("action", "update").Path(cfg.HyphaUrl).
Methods("POST").HandlerFunc(HandlerUpdate) Methods("POST").HandlerFunc(HandlerUpdate)
r.HandleFunc(cfg.MyceliumUrl+cfg.HyphaUrl, HandlerView)
r.HandleFunc(cfg.HyphaUrl, HandlerView) r.HandleFunc(cfg.HyphaUrl, HandlerView)
// Debug page that renders all hyphae. // Debug page that renders all hyphae.

107
mycelium/mycelium.go Normal file
View File

@@ -0,0 +1,107 @@
package mycelium
import (
"io/ioutil"
"log"
"strings"
"github.com/bouncepaw/mycorrhiza/cfg"
)
var (
MainMycelium string
SystemMycelium string
)
func gatherDirNames(path string) map[string]struct{} {
res := make(map[string]struct{})
nodes, err := ioutil.ReadDir(path)
if err != nil {
log.Fatal(err)
}
for _, node := range nodes {
if node.IsDir() {
res[node.Name()] = struct{}{}
}
}
return res
}
// Add values to the set. If a value is already there, return false.
func addInUniqueSet(set map[string]struct{}, names []string) bool {
ok := true
for _, name := range names {
if _, present := set[name]; present {
ok = false
}
set[name] = struct{}{}
}
return ok
}
func Init() {
var (
// Used to check if there are no duplicates
foundNames = make(map[string]struct{})
dirs = gatherDirNames(cfg.WikiDir)
mainPresent bool
systemPresent bool
)
for _, mycelium := range cfg.Mycelia {
switch mycelium.Type {
case "main":
mainPresent = true
MainMycelium = mycelium.Names[0]
case "system":
systemPresent = true
SystemMycelium = mycelium.Names[0]
}
// Check if there is a dir corresponding to the mycelium
if _, ok := dirs[mycelium.Names[0]]; !ok {
log.Fatal("No directory found for mycelium " + mycelium.Names[0])
}
// Confirm uniqueness of names
if ok := addInUniqueSet(foundNames, mycelium.Names); !ok {
log.Fatal("At least one name was used more than once for mycelia")
}
}
if !mainPresent {
log.Fatal("No `main` mycelium given in config.json")
}
if !systemPresent {
log.Fatal("No `system` mycelium given in config.json")
}
log.Println("Mycelial dirs are present")
}
func NameWithMyceliumInMap(m map[string]string) (res string) {
var (
hyphaName, okH = m["hypha"]
mycelName, okM = m["mycelium"]
)
log.Println(m)
if !okH {
// It will result in an error when trying to open a hypha with such name
return ":::"
}
if okM {
res = canonicalMycelium(mycelName)
} else {
res = MainMycelium
}
return res + "/" + hyphaName
}
func canonicalMycelium(name string) string {
log.Println("Determining canonical mycelial name for", name)
name = strings.ToLower(name)
for _, mycel := range cfg.Mycelia {
for _, mycelName := range mycel.Names {
if mycelName == name {
return mycel.Names[0]
}
}
}
// This is a nonexistent mycelium. Return a name that will trigger an error
return ":error:"
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/bouncepaw/mycorrhiza/cfg" "github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/fs" "github.com/bouncepaw/mycorrhiza/fs"
"github.com/bouncepaw/mycorrhiza/mycelium"
) )
// HyphaEdit renders hypha editor. // HyphaEdit renders hypha editor.
@@ -85,7 +86,12 @@ type Layout struct {
} }
func layout(name string) *Layout { func layout(name string) *Layout {
h := fs.Hs.Open(path.Join(fs.SystemMycelium, "theme", cfg.Theme, name+".html")).OnRevision("0") lytName := path.Join("theme", cfg.Theme, name+".html")
h := fs.Hs.OpenFromMap(map[string]string{
"mycelium": mycelium.SystemMycelium,
"hypha": lytName,
"rev": "0",
})
if h.Invalid { if h.Invalid {
return &Layout{nil, nil, true, h.Err} return &Layout{nil, nil, true, h.Err}
} }

View File

@@ -5,12 +5,28 @@ import (
"unicode" "unicode"
) )
func addColonPerhaps(name string) string {
if strings.HasPrefix(name, ":") {
return name
}
return ":" + name
}
func removeColonPerhaps(name string) string {
if strings.HasPrefix(name, ":") {
return name[1:]
}
return name
}
func UrlToCanonical(name string) string { func UrlToCanonical(name string) string {
return strings.ToLower(strings.ReplaceAll(name, " ", "_")) return removeColonPerhaps(
strings.ToLower(strings.ReplaceAll(name, " ", "_")))
} }
func DisplayToCanonical(name string) string { func DisplayToCanonical(name string) string {
return strings.ToLower(strings.ReplaceAll(name, " ", "_")) return removeColonPerhaps(
strings.ToLower(strings.ReplaceAll(name, " ", "_")))
} }
func CanonicalToDisplay(name string) (res string) { func CanonicalToDisplay(name string) (res string) {
@@ -29,5 +45,5 @@ func CanonicalToDisplay(name string) (res string) {
} }
res += string(ch) res += string(ch)
} }
return res return addColonPerhaps(res)
} }

11
wiki/sys/theme/2.markdown Normal file
View File

@@ -0,0 +1,11 @@
# Themes
In MycorrhizaWiki, themes are used to specify what is shown to a user, the front-end. So, a theme consists of:
- **HTML templates.** Data is inserted into them.
- **CSS.**
- **JS.**
Your HTML template structure must be identical to the one. You can include any CSS and JS in your themes.
See [default-light](:sys/theme/default-light) theme for more information. This is the only theme that is guaranteed to work.

11
wiki/sys/theme/3.markdown Normal file
View File

@@ -0,0 +1,11 @@
# Themes
In MycorrhizaWiki, themes are used to specify what is shown to a user, the front-end. So, a theme consists of:
- **HTML templates.** Data is inserted into them.
- **CSS.**
- **JS.**
Your HTML template structure must be identical to the one. You can include any CSS and JS in your themes.
See [default-light](/:sys/theme/default-light) theme for more information. This is the only theme that is guaranteed to work.

View File

@@ -1,7 +1,7 @@
<html> <html>
<head> <head>
<title>{{ .Title }}</title> <title>{{ .Title }}</title>
<link rel="stylesheet" href="/sys/theme/default-light/main.css?action=raw"> <link rel="stylesheet" href="/:sys/theme/default-light/main.css?action=raw">
</head> </head>
<body> <body>
<header class="header"> <header class="header">
@@ -20,6 +20,6 @@
<footer> <footer>
<p>This website runs <a href='https://github.com/bouncepaw/mycorrhiza'>MycorrhizaWiki</a></p> <p>This website runs <a href='https://github.com/bouncepaw/mycorrhiza'>MycorrhizaWiki</a></p>
</footer> </footer>
<script src="/sys/theme/default-light/main.js?action=raw"></script> <script src="/:sys/theme/default-light/main.js?action=raw"></script>
</body> </body>
</html> </html>

View File

@@ -15,10 +15,10 @@ h1, h2, h3, h4, h5, h6 { margin: 0.5em 0 0.25em; }
code { background-color: #f4f4f4; } code { background-color: #f4f4f4; }
.page { line-height: 1.666; max-width: 40rem; hyphens: auto; } .page { line-height: 1.666; max-width: 40rem; hyphens: auto; }
.page img { max-width:100%; } .page img { max-width:100%; }
.page pre { white-space: break-spaces; } .page pre { white-space: break-spaces; background-color: #f4f4f4; }
.page__title { font-size: 2rem; margin: 0; } .page__title { font-size: 2rem; margin: 0; }
footer { padding: 1rem 0; font-size: .8rem; bottom: 0; position: absolute; } footer { padding: 1rem 0; font-size: .8rem; }
footer a, footer a:visited { color: black; } footer a, footer a:visited { color: black; }
/* Sidebar section */ /* Sidebar section */
.sidebar { padding: 1rem 0; background: #f4f4f4; } .sidebar { padding: 1rem 0; background: #f4f4f4; }

View File

@@ -14,8 +14,32 @@
"binary_mime": "", "binary_mime": "",
"text_name": "1.markdown", "text_name": "1.markdown",
"binary_name": "" "binary_name": ""
}
}, },
"Invalid": false, "2": {
"Err": null "tags": [
""
],
"name": "Theme",
"comment": "Update Sys/Theme",
"author": "",
"time": 1593802668,
"text_mime": "text/markdown",
"binary_mime": "",
"text_name": "2.markdown",
"binary_name": ""
},
"3": {
"tags": [
""
],
"name": "Theme",
"comment": "Update Sys/Theme",
"author": "",
"time": 1593802769,
"text_mime": "text/markdown",
"binary_mime": "",
"text_name": "3.markdown",
"binary_name": ""
}
}
} }