fix style and implement basic static file tree browsing
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAmKi5uUACgkQO3+8IhRO Y5jyNw/9F9IHZky8Ky3zfAffhY0Bq+y9Tt3zq+ox7ckYFetHwalyK2PV/w5074JG FSfso8pIwFH/tWY2zxcatXN4T9qWcaPukkcUTvthbfH4o1XE+HRj/lQ6PnxiWAId UUMLQ3I2Wp1hR78cwu9AzzS2EXlOpvEs5alDPXMshRKxgPIigYa4E8kLwE9BhGUT jzCQwIY2tNm8rg3/grNZVm3F41O5T/EkP7pzTuBJj0Uu453klbcVoLlOdP1TOJpA LCX/hqo2PtPhh2LAJfKJDpXijGCn5Oos2z1o+gDWXH1XJdyup4A5Z7lV2NME3FS+ cUIBFUkVT2fJXHqq1XQiDw2w1w70feZU3Q9Ms8PdGqjNp7eoFD63avBy9zIHMXmY BpgimGUMMYS7z+dVgwzkk8k4ZsFEncwMA29CQ29G/gWaWWTHVExlz3YtIqyEWBbT auRgolaThTiLjpZV/9gxP0QLr6SNkBYSirwFw58Dku3l8idwiApghsIH7zmJ3sxn +NEnz89zGuq3LZbDG+hDvYue6VDlfpOgEhsp+2gVoq17hehyj3byfChiDV6d7tXw 08P+6YNI8qRj1kBAfNjeFGbipZZg/lpX31qcEPVQqopS5Gm/ouS5i8VTSGfFLOzS nSw9YsIj19shaGAgCRQP4wtxbvHglPuzzXDexxY5sGxad5Ls2Ys= =UIgG -----END PGP SIGNATURE-----
11 files changed,
193 insertions(+),
24 deletions(-)
M
archetype/config.go
→
archetype/config.go
@@ -9,11 +9,13 @@ "strings"
) type Config struct { - Adapter Adapter // adapter for this instance - Root string // root of the site data - StaticRoot string // root of static files for StaticFileManager - AssetRoot string // root of Nirvash dist files (CSS, images) - Plugins map[string]interface{} + Adapter Adapter // adapter for this instance + Root string // root of the site data + StaticRoot string // root of static files for StaticFileManager + StaticShowHidden bool // whether to show hidden files in the StaticFileManager + StaticShowHtml bool // whether to show html files in the StaticFileManager + AssetRoot string // root of Nirvash dist files (CSS, images) + Plugins map[string]interface{} } func GetConfigLocation() string {
M
archetype/eureka.go
→
archetype/eureka.go
@@ -25,7 +25,7 @@ }
self.Root = cfg.Root self.Config = make(map[ConfigOption]string) - // TODO: read config.h and build self.Config + err = self.readCfg() if err != nil { panic("config.h is malformed!")
A
archetype/fileManager.go
@@ -0,0 +1,88 @@
+package archetype + +import ( + "io/ioutil" + "path/filepath" + "strings" +) + +type SimpleFileManager struct { + Root string + ShowHtml bool + ShowHidden bool +} + +type FileListing struct { + Error string + Root string + Up string + SubDirs []string + Files []string +} + +type FileManager interface { + Init(cfg *Config) error + // ListTree() FileListing + ListSubTree(root string) FileListing + // AddFile(path string, file multipart.FileHeader) error + // MkDir(path string) error + // Remove(path string) error + // Rename(old, new string) error +} + +func (self *SimpleFileManager) Init(cfg *Config) error { + self.Root = cfg.StaticRoot + self.ShowHtml = cfg.StaticShowHtml + self.ShowHidden = cfg.StaticShowHidden + return nil +} + +func (self *SimpleFileManager) ListSubTree(root string) FileListing { + list := FileListing{} + + if strings.Contains(root, "../") || strings.Contains(root, "..\\") { + list.Error = "You cannot escape!" + return list + } + + fullPath := filepath.Join(self.Root, root) + + files, err := ioutil.ReadDir(fullPath) + + if err != nil { + list.Error = err.Error() + return list + } + + list.Root = root + if !strings.HasSuffix(list.Root, "/") { + list.Root += "/" + } + if !strings.HasPrefix(list.Root, "/") { + list.Root = "/" + list.Root + } + + levels := strings.Split(root, "/") + if list.Root != "/" { + list.Up = "/" + } + if len(levels) >= 2 { + list.Up = "/" + strings.Join(levels[:len(levels)-1], "/") + } + + for _, file := range files { + if !self.ShowHidden && strings.HasPrefix(file.Name(), ".") { + continue + } + if file.IsDir() { + list.SubDirs = append(list.SubDirs, file.Name()) + } else { + if !self.ShowHtml && strings.HasSuffix(file.Name(), ".html") { + continue + } + list.Files = append(list.Files, file.Name()) + } + } + + return list +}
D
archetype/staticFileManager.go
@@ -1,16 +0,0 @@
-package archetype - -type StaticFileManager struct { - Root string - ShowHtml bool - ShowHidden bool -} - -type FileManager interface { - Init(cfg Config) error - ListTree() []string - ListSubTree(root string) []string - AddFile(path string, file interface{}) error - MkDir(path string) error - Remove(path string) error -}
M
lfo/middleware.go
→
lfo/middleware.go
@@ -16,6 +16,15 @@
return http.HandlerFunc(handlerFunc) } +func WithFileManager(next http.Handler, fileManager core.FileManager) http.Handler { + handlerFunc := func(w http.ResponseWriter, req *http.Request) { + *req = *req.WithContext(context.WithValue(req.Context(), "file-manager", fileManager)) + next.ServeHTTP(w, req) + } + + return http.HandlerFunc(handlerFunc) +} + func EnsurePageData(next http.Handler, adapter core.Adapter) http.Handler { handlerFunc := func(w http.ResponseWriter, req *http.Request) { pageTitle := req.FormValue("title")
M
nirvash.go
→
nirvash.go
@@ -27,11 +27,15 @@ }
cfg.Adapter.Init(cfg) + fileManager := &core.SimpleFileManager{} + fileManager.Init(cfg) + pathConcat := filepath.Join rtr := &router.Router{ StaticPaths: map[string]string{ - "/static": filepath.Join(cfg.AssetRoot, "static"), + "/static/": filepath.Join(cfg.AssetRoot, "static"), + "/files/": cfg.StaticRoot, }, }@@ -200,6 +204,20 @@ udb,
"/login"), udb, "/")) + + rtr.Get( + `/static-mgr/(?P<Slug>.*)`, + Fortify( + Protected( + WithFileManager( + renderer.Template( + pathConcat(templateRoot, "file_list.html"), + pathConcat(templateRoot, "header.html"), + pathConcat(templateRoot, "footer.html")), + fileManager), + http.MethodGet, + udb, + "/login"))) http.ListenAndServe(":8080", rtr) }
A
static/delete.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.0" + width="100" + height="100" + id="svg8978"> + <defs + id="defs8980" /> + <g + id="layer1"> + <path + d="M 6.3895625,6.4195626 C 93.580437,93.610437 93.580437,93.610437 93.580437,93.610437" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:18.05195999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path8986" /> + <path + d="M 6.3894001,93.6106 C 93.830213,6.4194003 93.830213,6.4194003 93.830213,6.4194003" + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:17.80202103;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path8988" /> + </g> +</svg>
A
static/move.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500"> +<path stroke="#000" stroke-width="55" fill="none" +stroke-linecap="round" stroke-linejoin="round" +d="m249,30a220,220 0 1,0 2,0zm-10,75 140,145-140,145M110,250H350"/> +</svg>
M
static/style.css
→
static/style.css
@@ -291,4 +291,9 @@ }
form input[readonly] { border: none; +} + +.edit-error { + display: block; + border-bottom: solid 2px crimson; }
A
templates/file_list.html
@@ -0,0 +1,34 @@
+{{ $slug := ((.Context).Value "params").Slug }} +{{ $fileList := ((.Context).Value "file-manager").ListSubTree $slug }} + +{{ template "header" .}} + + +{{ if ($fileList).Error }} +<h2>File Listing Error</h2> + +<span class="adapter-error">{{($fileList).Error}}</span> +{{ else }} +<h2>Files: {{($fileList).Root}}</h2> + +<div class="new-page-button-wrapper"> + <a class="new-page-button" href="/upload{{($fileList).Root}}">Upload File</a> +</div> + +<div class="page-list"> + <ul> + {{ if ($fileList).Up }} + <li><a href="/static-mgr{{$fileList.Up}}">..</a></li> + {{ end }} + {{ range $dir := ($fileList).SubDirs }} + <li><a href="/static-mgr{{($fileList).Root}}{{$dir}}">{{$dir}}/</a></li> + {{ end }} + {{ range $file := ($fileList).Files }} + <li><a href="/files{{($fileList).Root}}{{$file}}">{{$file}}</a></li> + {{ end }} + </ul> +</div> + +{{ end }} + +{{ template "footer" .}}
M
templates/header.html
→
templates/header.html
@@ -13,7 +13,7 @@ <header><h1>Nirvash CMS</h1></header>
<nav> <ul> <li><a href="/">Pages</a></li> - <li><a href="/static-mgr">Static Files</a></li> + <li><a href="/static-mgr/">Static Files</a></li> <li><a href="/build">Build</a></li> <li><a href="/config">Configuration</a></li> <li><a href="/logout">Logout</a></li>