diff --git a/internal/conf/templates/base.tmpl.html b/internal/conf/templates/base.tmpl.html index ee9e756..67efee4 100644 --- a/internal/conf/templates/base.tmpl.html +++ b/internal/conf/templates/base.tmpl.html @@ -1,3 +1,15 @@ +{{define "navLinks"}} +{{end}} +{{define "navExtra"}} +
+
+ +
+
+ +
+
+{{end}} {{define "base"}} @@ -23,7 +35,9 @@ + {{template "navLinks" .}} + {{template "navExtra" .}}
diff --git a/internal/conf/templates/edit.tmpl.html b/internal/conf/templates/edit.tmpl.html index d6ea5b8..a84b5d1 100644 --- a/internal/conf/templates/edit.tmpl.html +++ b/internal/conf/templates/edit.tmpl.html @@ -1,4 +1,5 @@ -{{define "title"}}Edit {{.note.Title}}{{end}} +{{define "title"}}Edit: {{.note.Title}}{{end}} +{{define "navExtra"}}{{end}} {{define "main"}}
diff --git a/internal/conf/templates/list.tmpl.html b/internal/conf/templates/list.tmpl.html index 1c4bb44..6b39588 100644 --- a/internal/conf/templates/list.tmpl.html +++ b/internal/conf/templates/list.tmpl.html @@ -1,10 +1,10 @@ {{define "title"}}All Notes{{end}} {{define "main"}}
- {{range $title := .titles}} + {{range $note := .notes}} - {{$title}} + href="{{$note.Url}}/"> + {{$note.Title}} Edit {{end}} diff --git a/internal/notes/notes.go b/internal/notes/notes.go index c75216a..5791ce1 100644 --- a/internal/notes/notes.go +++ b/internal/notes/notes.go @@ -4,6 +4,7 @@ package notes import ( "bytes" + "encoding/base64" "fmt" "html/template" "log" @@ -29,7 +30,7 @@ func fmtPath(path string) string { // Save a note to a path derived from the title func (n *Note) Save() error { - filename := filepath.Join(conf.Conf.NotesDir, fmtPath(n.Title)) + filename := filepath.Join(conf.Conf.NotesDir, fmtPath(n.EncodedTitle())) return os.WriteFile(filename, n.Body, 0600) } @@ -50,12 +51,13 @@ func (n *Note) Render() { } // Load a note from the disk. The path is derived from the title -func LoadNote(title string) (*Note, error) { - filename := filepath.Join(conf.Conf.NotesDir, fmtPath(title)) +func LoadNote(encodedTitle string) (*Note, error) { + filename := filepath.Join(conf.Conf.NotesDir, fmtPath(encodedTitle)) body, err := os.ReadFile(filename) if err != nil { return nil, err } + title := DecodeTitle(encodedTitle) return &Note{Title: title, Body: body}, nil } @@ -63,3 +65,16 @@ func DeleteNote(title string) error { filename := filepath.Join(conf.Conf.NotesDir, fmtPath(title)) return os.Remove(filename) } + +func (n *Note) EncodedTitle() string { + return base64.StdEncoding.EncodeToString([]byte(n.Title)) +} + +func DecodeTitle(encodedTitle string) string { + title, err := base64.StdEncoding.DecodeString(encodedTitle) + if err != nil { + log.Printf("Couldn't decode base64 string '%s': %s", encodedTitle, err) + } + + return string(title) +} diff --git a/internal/notes/views/views.go b/internal/notes/views/views.go index 71d849f..3979605 100644 --- a/internal/notes/views/views.go +++ b/internal/notes/views/views.go @@ -19,12 +19,13 @@ func GetRoutes(prefix string) *http.ServeMux { myurls = urls.URLs{ Prefix: prefix, URLs: map[string]urls.URL{ + "list": {Path: "/", Protocol: "GET", Handler: list}, + "new": {Path: "/new", Protocol: "GET", Handler: new}, "view_": {Path: "/{note}", Protocol: "GET", Handler: view}, "view": {Path: "/{note}/", Protocol: "GET", Handler: view}, "delete": {Path: "/{note}/delete/", Protocol: "GET", Handler: delete}, "edit": {Path: "/{note}/edit/", Protocol: "GET", Handler: edit}, "save": {Path: "/{note}/edit/save/", Protocol: "POST", Handler: save}, - "list": {Path: "/", Protocol: "GET", Handler: list}, }, } return myurls.GetRouter() @@ -51,13 +52,14 @@ func view(w http.ResponseWriter, r *http.Request) { } func edit(w http.ResponseWriter, r *http.Request) { - title := r.PathValue("note") - note, err := notes.LoadNote(title) + encodedTitle := r.PathValue("note") + note, err := notes.LoadNote(encodedTitle) if err != nil { + title := notes.DecodeTitle(encodedTitle) note = ¬es.Note{Title: title} } - urlSave := myurls.Reverse("save", urls.Repl{"note": title}) + urlSave := myurls.Reverse("save", urls.Repl{"note": encodedTitle}) context := templ.Ctx{"note": note, "urlSave": urlSave, "text": string(note.Body)} err = templ.RenderTemplate(w, "edit.tmpl.html", context) if err != nil { @@ -67,17 +69,25 @@ func edit(w http.ResponseWriter, r *http.Request) { } } +func new(w http.ResponseWriter, r *http.Request) { + title := r.FormValue("title") + note := ¬es.Note{Title: title} + + urlEdit := myurls.Reverse("edit", urls.Repl{"note": note.EncodedTitle()}) + http.Redirect(w, r, urlEdit, http.StatusFound) +} + func delete(w http.ResponseWriter, r *http.Request) { - title := r.PathValue("note") - err := notes.DeleteNote(title) + encodedTitle := r.PathValue("note") + err := notes.DeleteNote(encodedTitle) if err != nil { log.Print(err.Error()) http.Error(w, "Couldn't delete note", http.StatusInternalServerError) return } - urlDelete := myurls.Reverse("list", urls.Repl{}) - http.Redirect(w, r, urlDelete, http.StatusFound) + urlList := myurls.Reverse("list", urls.Repl{}) + http.Redirect(w, r, urlList, http.StatusFound) } func save(w http.ResponseWriter, r *http.Request) { @@ -88,11 +98,16 @@ func save(w http.ResponseWriter, r *http.Request) { note := ¬es.Note{Title: title, Body: []byte(body)} note.Save() - if oldTitle != title { + if oldTitle != note.EncodedTitle() { notes.DeleteNote(oldTitle) } - http.Redirect(w, r, myurls.Reverse("view", urls.Repl{"note": title}), http.StatusFound) + http.Redirect(w, r, myurls.Reverse("view", urls.Repl{"note": note.EncodedTitle()}), http.StatusFound) +} + +type titleAndUrl struct { + Title string + Url string } func list(w http.ResponseWriter, r *http.Request) { @@ -103,16 +118,26 @@ func list(w http.ResponseWriter, r *http.Request) { return } - titles := make([]string, 0) + titlesAndUrls := make([]titleAndUrl, 0) for _, f := range files { if !f.IsDir() { - title := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name())) - titles = append(titles, title) + encodedTitle := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name())) + title := notes.DecodeTitle(encodedTitle) + + log.Printf("Found note %s (title '%s')", encodedTitle, title) + + titlesAndUrls = append( + titlesAndUrls, + titleAndUrl{Title: title, Url: myurls.Reverse("view", urls.Repl{"note": encodedTitle})}, + ) + log.Print(titlesAndUrls) } } - err = templ.RenderTemplate(w, "list.tmpl.html", templ.Ctx{"titles": titles}) + urlNew := myurls.Reverse("new", urls.Repl{}) + + err = templ.RenderTemplate(w, "list.tmpl.html", templ.Ctx{"notes": titlesAndUrls, "urlNew": urlNew}) if err != nil { log.Print(err.Error()) http.Error(w, "Internal Server Error", http.StatusInternalServerError) diff --git a/internal/templ/templ.go b/internal/templ/templ.go index e8ef51d..1897e9d 100644 --- a/internal/templ/templ.go +++ b/internal/templ/templ.go @@ -11,6 +11,7 @@ import ( type Ctx map[string]any func RenderTemplate(w http.ResponseWriter, tmpl string, context any) error { + var err error files := []string{ filepath.Join("templates", "base.tmpl.html"), filepath.Join("templates", tmpl), @@ -25,6 +26,6 @@ func RenderTemplate(w http.ResponseWriter, tmpl string, context any) error { } t, err := template.ParseFS(conf.Templates, files...) - t.ExecuteTemplate(w, "base", context) + err = t.ExecuteTemplate(w, "base", context) return err }