miki

Miki: personal wiki with the fmd markup format
git clone https://wtenison.com/repo/miki.git
Log | Files | Refs | README | LICENSE

commit 89870243554569fb99fa0cdf59ed8d923214217a
parent 921013e4a47a87afebb7a9af7e85a88d5fbfa056
Author: weetat <wtenison@protonmail.com>
Date:   Thu, 27 Feb 2025 23:51:01 +0000

Add preview page for rendering editor input

The edit form posts to /preview, which renders the submitted fmd. The
interpreter also drops any word containing a <script> tag.

Diffstat:
Msrc/fmd/fmd.go | 13+++++++++++++
Msrc/fmd/interpreter.go | 4++++
Msrc/network/routes.go | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mweb/edit/index.html | 5++++-
Mweb/edit/styles.css | 16++++++++++++++--
Aweb/preview/index.html | 32++++++++++++++++++++++++++++++++
6 files changed, 138 insertions(+), 20 deletions(-)

diff --git a/src/fmd/fmd.go b/src/fmd/fmd.go @@ -7,6 +7,19 @@ import ( "path/filepath" ) +func ReadFMDStreamtoHTMLString(inputStreamFMD []byte) string { + + _l := MakeLexer(inputStreamFMD) + + _tokens := _l.Exec() + + _p := MakeParser(_tokens) + + _ast := _p.Exec() + + return interpret(_ast) +} + func WriteFMDToHTMLFile(inputFilePath string, outputFilePath string) { _fileName := filepath.Base(inputFilePath) if filepath.Ext(_fileName) != ".fmd" { diff --git a/src/fmd/interpreter.go b/src/fmd/interpreter.go @@ -55,6 +55,10 @@ func interpret(n node) string { case nodeSpace: return n.value case nodeLiteral: + if strings.Contains(n.value, "<script>") || strings.Contains(n.value, "</script>") { + return "" + } + return n.value } return _res.String() diff --git a/src/network/routes.go b/src/network/routes.go @@ -1,6 +1,7 @@ package network import ( + "bytes" "log" "net/http" "net/url" @@ -11,6 +12,8 @@ import ( "miki/src/fmd" ) +// TODO: There is a lot of duplicate functionality here. Simplify/consolidate. + var mux *http.ServeMux func makeServerRoutes() { @@ -26,6 +29,7 @@ func makeServerRoutes() { mux.HandleFunc("/home", handlePageHome) mux.HandleFunc("/subject", handlePageSubject) mux.HandleFunc("/edit", handlePageEdit) + mux.HandleFunc("/preview", handlePagePreview) mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { @@ -42,7 +46,7 @@ func handlePageHome(w http.ResponseWriter, r *http.Request) { // Collect HTML template files from directory. _rPkg, _err := mfs.MakeDirectoryPkg(filepath.Join(mfs.Dirs["web"], r.URL.String()), mfs.SEARCH_DEPTH_ZERO, "*.html") if _err != nil { - log.Printf("_ERROR:network:handlePage(\"/subject\"):%s", _err) + log.Printf("_ERROR:network:handlePage(\"/home\"):%s", _err) http.NotFound(w, r) return } @@ -65,12 +69,12 @@ func handlePageHome(w http.ResponseWriter, r *http.Request) { // Render template. if _err = _tmpl.ExecuteTemplate(w, "index.html", _templateDataContainer); _err != nil { - log.Printf("_ERROR:network:handlePageDefault(\"/\"):%s", _err) - http.Error(w, "_ERROR:network:handlePageDefault(\"/\"):template.ExecuteTemplate", http.StatusInternalServerError) + log.Printf("_ERROR:network:handlePage(\"/home\"):%s", _err) + http.Error(w, "_ERROR:network:handlePage(\"/home\"):template.ExecuteTemplate", http.StatusInternalServerError) return } - log.Printf("DONE\thandlePageDefault:%s", r.URL) + log.Printf("DONE\thandlePage:%s", r.URL) } func handlePageSubject(w http.ResponseWriter, r *http.Request) { @@ -122,12 +126,12 @@ func handlePageSubject(w http.ResponseWriter, r *http.Request) { // Render template. if _err = _tmpl.ExecuteTemplate(w, "index.html", _templateDataContainer); _err != nil { - log.Printf("_ERROR:network:handlePageDefault(\"/\"):%s", _err) - http.Error(w, "_ERROR:network:handlePageDefault(\"/\"):template.ExecuteTemplate", http.StatusInternalServerError) + log.Printf("_ERROR:network:handlePage(\"/subject\"):%s", _err) + http.Error(w, "_ERROR:network:handlePage(\"/subject\"):template.ExecuteTemplate", http.StatusInternalServerError) return } - log.Printf("DONE\thandlePageDefault:%s", r.URL) + log.Printf("DONE\thandlePage:%s", r.URL) } func handlePageEdit(w http.ResponseWriter, r *http.Request) { @@ -135,7 +139,7 @@ func handlePageEdit(w http.ResponseWriter, r *http.Request) { // Collect HTML template files from directory. _rPkg, _err := mfs.MakeDirectoryPkg(filepath.Join(mfs.Dirs["web"], r.URL.Path), mfs.SEARCH_DEPTH_ZERO, "*.html") if _err != nil { - log.Printf("_ERROR:network:handlePageDefault(\"/\"):%s", _err) + log.Printf("_ERROR:network:handlePage(\"/edit\"):%s", _err) http.NotFound(w, r) return } @@ -146,15 +150,15 @@ func handlePageEdit(w http.ResponseWriter, r *http.Request) { // Get file name from URL _urlContent, _err := url.Parse(r.URL.String()) if _err != nil { - log.Printf("_ERROR:network:handlePage(\"/subject\"):%s", _err) - http.Error(w, "_ERROR:network:handlePage(\"/subject\"):url.Parse", http.StatusInternalServerError) + log.Printf("_ERROR:network:handle(\"/edit\"):%s", _err) + http.Error(w, "_ERROR:network:handlePage(\"/edit\"):url.Parse", http.StatusInternalServerError) } // Parse URL for query parameters. _urlQuerySubject, _err := url.ParseQuery(_urlContent.RawQuery) if _err != nil { - log.Printf("_ERROR:network:handlePage(\"/subject\"):%s", _err) - http.Error(w, "_ERROR:network:handlePage(\"/subject\"):url.ParseQuery", http.StatusInternalServerError) + log.Printf("_ERROR:network:handlePage(\"/edit\"):%s", _err) + http.Error(w, "_ERROR:network:handlePage(\"/edit\"):url.ParseQuery", http.StatusInternalServerError) } _fileName := _urlQuerySubject.Get("s") @@ -170,17 +174,67 @@ func handlePageEdit(w http.ResponseWriter, r *http.Request) { // Create template from package. _tmpl, _err := template.ParseFiles(_rPkg.DependencyFiles...) if _err != nil { - log.Printf("_ERROR:network:handlePageDefault(\"/\"):%s", _err) - http.Error(w, "_ERROR:network:handlePageDefault(\"/\"):template.ParseFiles", http.StatusInternalServerError) + log.Printf("_ERROR:network:handlePage(\"/edit\"):%s", _err) + http.Error(w, "_ERROR:network:handlePage(\"/edit\"):template.ParseFiles", http.StatusInternalServerError) + return + } + + // Render template. + if _err = _tmpl.ExecuteTemplate(w, "index.html", _templateDataContainer); _err != nil { + log.Printf("_ERROR:network:handlePage(\"/edit\"):%s", _err) + http.Error(w, "_ERROR:network:handlePage(\"/edit\"):template.ExecuteTemplate", http.StatusInternalServerError) return } + log.Printf("DONE\thandlePage:%s", r.URL) +} + +func handlePagePreview(w http.ResponseWriter, r *http.Request) { + + // Collect HTML template files from directory. + _rPkg, _err := mfs.MakeDirectoryPkg(filepath.Join(mfs.Dirs["web"], r.URL.Path), mfs.SEARCH_DEPTH_ZERO, "*.html") + if _err != nil { + log.Printf("_ERROR:network:handlePage(\"/preview\"):%s", _err) + http.NotFound(w, r) + return + } + + // Add 'global' file dependencies. + _rPkg.DependencyFiles = append(_rPkg.DependencyFiles, mfs.WebGlobalPkg.DependencyFiles...) + + // Create template from package. + _tmpl, _err := template.ParseFiles(_rPkg.DependencyFiles...) + if _err != nil { + log.Printf("_ERROR:network:handlePage(\"/preview\"):%s", _err) + http.Error(w, "_ERROR:network:handlePage(\"/preview\"):template.ParseFiles", http.StatusInternalServerError) + return + } + + // Get FMD content from form post. + // if _err = r.ParseForm(); _err != nil { + // log.Printf("_ERROR:network:handlePage(\"/preview\"):%s", _err) + // http.Error(w, "_ERROR:network:handlePage(\"/preview\"):r.ParseForm", http.StatusInternalServerError) + // return + // } + + _inputFMDStream := bytes.NewBufferString(r.FormValue("editor")) + + // Assign rendered FMD file to buffer. + var _templateDataContainer struct { + FileText string + } + _templateDataContainer.FileText = fmd.ReadFMDStreamtoHTMLString(_inputFMDStream.Bytes()) + // _templateDataContainer.FileText = r.FormValue("editor") + if _templateDataContainer.FileText == "" { + _templateDataContainer.FileText = "No File Found" + } + // Render template. if _err = _tmpl.ExecuteTemplate(w, "index.html", _templateDataContainer); _err != nil { - log.Printf("_ERROR:network:handlePageDefault(\"/\"):%s", _err) - http.Error(w, "_ERROR:network:handlePageDefault(\"/\"):template.ExecuteTemplate", http.StatusInternalServerError) + log.Printf("_ERROR:network:handlePage(\"/preview\"):%s", _err) + http.Error(w, "_ERROR:network:handlePage(\"/preview\"):template.ExecuteTemplate", http.StatusInternalServerError) return } - log.Printf("DONE\thandlePageDefault:%s", r.URL) + log.Printf("DONE\thandlePage:%s", r.URL) } diff --git a/web/edit/index.html b/web/edit/index.html @@ -20,7 +20,10 @@ <section id="main" class="__container"> - <textarea name="editor" id="0">{{.FileText}}</textarea> + <form class="mform" action="/preview" method="post" target="_blank"> + <button type="submit">PREVIEW HTML OUTPUT</button> + <textarea name="editor" id="0">{{.FileText}}</textarea> + </form> </section> diff --git a/web/edit/styles.css b/web/edit/styles.css @@ -1,5 +1,17 @@ @layer entity { - textarea { - height: 75vh; + .mform { + display: grid; + + > button { + width: fit-content; + padding: var(--miki_padding--half); + margin: var(--miki_margin); + } + + > textarea { + height: 76vh; + padding: var(--miki_padding); + } } + } \ No newline at end of file diff --git a/web/preview/index.html b/web/preview/index.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> + +<head> + + <title>Miki/Preview</title> + <meta name="viewport" charset="utf-8" content="width=device-width, initial-scale=1.0" /> + {{template "meta"}} + +</head> + +<body> + + {{template "header" "Preview"}} + + <section class="_content"> + + <section id="left"></section> + + <section id="subject_"> + + {{.FileText}} + + </section> + + <section id="right"></section> + + </section> +</body> + +</html> +\ No newline at end of file