1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2025-04-14 23:03:17 +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 + `}`
RevisionPattern = `[\d]+`
RevQuery = `{rev:` + RevisionPattern + `}`
MyceliumPattern = `:` + HyphaPattern
MyceliumUrl = `/{mycelium:` + MyceliumPattern + `}`
MyceliumPattern = `[^\s\d:/?&\\][^:?&\\/]*`
MyceliumUrl = `/:{mycelium:` + MyceliumPattern + `}`
)
var (

View File

@ -92,5 +92,5 @@ func navitreeEntry(name, class string) string {
return fmt.Sprintf(`<li class="navitree__entry %s">
<a class="navitree__link" href="/%s">%s</a>
</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>?
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)

View File

@ -12,6 +12,7 @@ import (
"strings"
"time"
"github.com/bouncepaw/mycorrhiza/mycelium"
"github.com/bouncepaw/mycorrhiza/util"
)
@ -32,7 +33,16 @@ func (h *Hypha) Invalidate(err error) *Hypha {
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)
h := &Hypha{
Exists: true,
@ -110,13 +120,18 @@ func (h *Hypha) ActionRaw(w http.ResponseWriter) *Hypha {
if h.Invalid {
return h
}
fileContents, err := ioutil.ReadFile(h.actual.TextPath)
if err != nil {
return h.Invalidate(err)
if h.Exists {
fileContents, err := ioutil.ReadFile(h.actual.TextPath)
if err != nil {
return h.Invalidate(err)
}
w.Header().Set("Content-Type", h.mimeTypeForActionRaw())
w.WriteHeader(http.StatusOK)
w.Write(fileContents)
} else {
log.Println("Hypha", h.FullName, "has no actual revision")
w.WriteHeader(http.StatusNotFound)
}
w.Header().Set("Content-Type", h.mimeTypeForActionRaw())
w.WriteHeader(http.StatusOK)
w.Write(fileContents)
return h
}
@ -126,13 +141,18 @@ func (h *Hypha) ActionBinary(w http.ResponseWriter) *Hypha {
if h.Invalid {
return h
}
fileContents, err := ioutil.ReadFile(h.actual.BinaryPath)
if err != nil {
return h.Invalidate(err)
if h.Exists {
fileContents, err := ioutil.ReadFile(h.actual.BinaryPath)
if err != nil {
return h.Invalidate(err)
}
w.Header().Set("Content-Type", h.actual.BinaryMime)
w.WriteHeader(http.StatusOK)
w.Write(fileContents)
} else {
log.Println("Hypha", h.FullName, "has no actual revision")
w.WriteHeader(http.StatusNotFound)
}
w.Header().Set("Content-Type", h.actual.BinaryMime)
w.WriteHeader(http.StatusOK)
w.Write(fileContents)
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.
func HandlerBase(w http.ResponseWriter, rq *http.Request) *fs.Hypha {
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) {
@ -41,7 +41,7 @@ func HandlerView(w http.ResponseWriter, rq *http.Request) {
func HandlerEdit(w http.ResponseWriter, rq *http.Request) {
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.WriteHeader(http.StatusOK)
w.Write(render.HyphaEdit(h))
@ -51,7 +51,7 @@ func HandlerUpdate(w http.ResponseWriter, rq *http.Request) {
vars := mux.Vars(rq)
log.Println("Attempt to update hypha", vars["hypha"])
h := fs.Hs.
Open(vars["hypha"]).
OpenFromMap(vars).
CreateDirIfNeeded().
AddRevisionFromHttpData(rq).
WriteTextFileFromHttpData(rq).

View File

@ -10,6 +10,7 @@ import (
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/fs"
"github.com/bouncepaw/mycorrhiza/mycelium"
"github.com/gorilla/mux"
)
@ -53,7 +54,7 @@ func main() {
log.Println("Welcome to MycorrhizaWiki α")
cfg.InitConfig(wikiDir)
log.Println("Indexing hyphae...")
fs.VerifyMycelia()
mycelium.Init()
fs.InitStorage()
// Start server code. See handlers.go for handlers' implementations.
@ -78,6 +79,7 @@ func main() {
r.Queries("action", "update").Path(cfg.HyphaUrl).
Methods("POST").HandlerFunc(HandlerUpdate)
r.HandleFunc(cfg.MyceliumUrl+cfg.HyphaUrl, HandlerView)
r.HandleFunc(cfg.HyphaUrl, HandlerView)
// 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/fs"
"github.com/bouncepaw/mycorrhiza/mycelium"
)
// HyphaEdit renders hypha editor.
@ -85,7 +86,12 @@ type Layout struct {
}
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 {
return &Layout{nil, nil, true, h.Err}
}

View File

@ -5,12 +5,28 @@ import (
"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 {
return strings.ToLower(strings.ReplaceAll(name, " ", "_"))
return removeColonPerhaps(
strings.ToLower(strings.ReplaceAll(name, " ", "_")))
}
func DisplayToCanonical(name string) string {
return strings.ToLower(strings.ReplaceAll(name, " ", "_"))
return removeColonPerhaps(
strings.ToLower(strings.ReplaceAll(name, " ", "_")))
}
func CanonicalToDisplay(name string) (res string) {
@ -29,5 +45,5 @@ func CanonicalToDisplay(name string) (res string) {
}
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>
<head>
<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>
<body>
<header class="header">
@ -20,6 +20,6 @@
<footer>
<p>This website runs <a href='https://github.com/bouncepaw/mycorrhiza'>MycorrhizaWiki</a></p>
</footer>
<script src="/sys/theme/default-light/main.js?action=raw"></script>
<script src="/:sys/theme/default-light/main.js?action=raw"></script>
</body>
</html>

View File

@ -15,10 +15,10 @@ h1, h2, h3, h4, h5, h6 { margin: 0.5em 0 0.25em; }
code { background-color: #f4f4f4; }
.page { line-height: 1.666; max-width: 40rem; hyphens: auto; }
.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; }
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; }
/* Sidebar section */
.sidebar { padding: 1rem 0; background: #f4f4f4; }

View File

@ -14,8 +14,32 @@
"binary_mime": "",
"text_name": "1.markdown",
"binary_name": ""
},
"2": {
"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": ""
}
},
"Invalid": false,
"Err": null
}
}