Further work on note ownership and sharing
This commit is contained in:
parent
bb775ec55f
commit
4fda818e6e
4 changed files with 125 additions and 34 deletions
|
|
@ -24,10 +24,30 @@
|
|||
href="/static/css/tiny-mde.min.css"
|
||||
/>
|
||||
<script type="text/javascript">
|
||||
|
||||
|
||||
var tinyMDE = new TinyMDE.Editor({ textarea: "noteBodyInput" });
|
||||
var commandBar = new TinyMDE.CommandBar({
|
||||
element: "toolbar",
|
||||
editor: tinyMDE,
|
||||
commands: [
|
||||
'bold', 'italic', 'strikethrough',
|
||||
'|',
|
||||
'code',
|
||||
'|',
|
||||
'h1', 'h2',
|
||||
'|',
|
||||
'ul', 'ol', {name: "checklist", title: "Check List", action: makeChecklist},
|
||||
'|',
|
||||
'blockquote', 'hr',
|
||||
'|',
|
||||
'insertLink', 'insertImage'
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
function makeChecklist(editor) {
|
||||
editor.wrapSelection("* [ ] ", "")
|
||||
};
|
||||
</script>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,26 @@
|
|||
<a class="btn btn-primary" href="{{.urlEdit}}">Edit</a>
|
||||
<a class="btn btn-danger" href="{{.urlDelete}}">Delete</a>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<h3>Ownership</h3>
|
||||
{{if .note.Viewers}}
|
||||
<p>This note is owned by <em>{{.note.Owner}}</em> and is further visible to</p>
|
||||
<ul>
|
||||
{{range .viewers}}
|
||||
<li>{{.}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<p>This note is owned by <em>{{.note.Owner}}</em>.</p>
|
||||
{{end}}
|
||||
|
||||
<form action="{{.urlShare}}" method="POST">
|
||||
<div class="mb-3">
|
||||
<input type="text" class="form-control" id="viewerInput" name="viewer" aria-described-by="viewerHelp" />
|
||||
<div id="viewerHelp" class="form-text">Share with other user</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">Share</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let checkBoxes = document.querySelectorAll('input[type=checkbox]')
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ type Note struct {
|
|||
BodyRendered template.HTML
|
||||
Body []byte
|
||||
Owner string
|
||||
Viewers []string
|
||||
Viewers map[string]struct{}
|
||||
Uid string
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ func Init() error {
|
|||
for _, f := range files {
|
||||
if !f.IsDir() {
|
||||
uid := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
|
||||
note, err := LoadNote(uid)
|
||||
note, err := loadNote(uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -82,10 +82,10 @@ func Init() error {
|
|||
|
||||
log.Printf("Found note %s (title '%s', owner %s)", uid, title, note.Owner)
|
||||
|
||||
Notes.notes[note.Owner] = append(Notes.notes[note.Owner], note)
|
||||
Notes.Add(note, note.Owner)
|
||||
|
||||
for _, viewer := range note.Viewers {
|
||||
Notes.notes[viewer] = append(Notes.notes[viewer], note)
|
||||
for viewer := range note.Viewers {
|
||||
Notes.Add(note, viewer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -109,8 +109,8 @@ func (ns *NoteStore) GetOne(owner string, uid string) (*Note, bool) {
|
|||
return nil, false
|
||||
}
|
||||
|
||||
func (ns *NoteStore) Add(note *Note) {
|
||||
ns.notes[note.Owner] = append(ns.notes[note.Owner], note)
|
||||
func (ns *NoteStore) Add(note *Note, user string) {
|
||||
ns.notes[user] = append(ns.notes[user], note)
|
||||
}
|
||||
|
||||
func fmtPath(path string) string {
|
||||
|
|
@ -118,19 +118,39 @@ func fmtPath(path string) string {
|
|||
}
|
||||
|
||||
func (n *Note) marshalFrontmatter() ([]byte, error) {
|
||||
viewers := make([]string, 0, len(n.Viewers))
|
||||
|
||||
for viewer := range n.Viewers {
|
||||
viewers = append(viewers, viewer)
|
||||
}
|
||||
frontmatter := make(map[string]interface{})
|
||||
frontmatter["owner"] = n.Owner
|
||||
frontmatter["viewers"] = n.Viewers
|
||||
frontmatter["viewers"] = viewers
|
||||
frontmatter["title"] = n.Title
|
||||
|
||||
marshaled, err := yaml.Marshal(&frontmatter)
|
||||
return marshaled, err
|
||||
}
|
||||
|
||||
func (n *Note) ViewersAsList() []string {
|
||||
keys := make([]string, 0, len(n.Viewers))
|
||||
for key := range n.Viewers {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func NewNoteNoSave(title string, owner string) *Note {
|
||||
note := &Note{Title: title, Owner: owner}
|
||||
note.Viewers = make(map[string]struct{})
|
||||
return note
|
||||
}
|
||||
|
||||
func NewNote(title string, owner string) *Note {
|
||||
note := &Note{Title: title, Owner: owner, Uid: rand.Text()}
|
||||
note := NewNoteNoSave(title, owner)
|
||||
note.Uid = rand.Text()
|
||||
note.Save()
|
||||
Notes.Add(note)
|
||||
Notes.Add(note, note.Owner)
|
||||
return note
|
||||
}
|
||||
|
||||
|
|
@ -161,8 +181,8 @@ func (n *Note) Render() {
|
|||
n.BodyRendered = template.HTML(buf.String())
|
||||
}
|
||||
|
||||
// LoadNote from the disk. The path is derived from the title
|
||||
func LoadNote(uid string) (*Note, error) {
|
||||
// loadNote from the disk. The path is derived from the uid
|
||||
func loadNote(uid string) (*Note, error) {
|
||||
filename := filepath.Join(conf.Conf.NotesDir, fmtPath(uid))
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
|
|
@ -213,7 +233,9 @@ func LoadNote(uid string) (*Note, error) {
|
|||
return nil, errors.New("invalid note, missing 'title' in frontmatter")
|
||||
}
|
||||
|
||||
note := &Note{Title: title, Body: body, Owner: owner, Uid: uid}
|
||||
note := NewNoteNoSave(title, owner)
|
||||
note.Uid = uid
|
||||
note.Body = body
|
||||
|
||||
viewers := metaData["viewers"].([]interface{})
|
||||
|
||||
|
|
@ -222,13 +244,22 @@ func LoadNote(uid string) (*Note, error) {
|
|||
if !ok {
|
||||
return nil, errors.New("invalid note, non string type in 'viewers' in frontmatter")
|
||||
}
|
||||
note.Viewers = append(note.Viewers, v)
|
||||
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
note.AddViewer(v)
|
||||
}
|
||||
log.Printf("Note %s shared with %v", note.Title, note.Viewers)
|
||||
|
||||
return note, nil
|
||||
}
|
||||
|
||||
func (n *Note) AddViewer(viewer string) {
|
||||
n.Viewers[viewer] = struct{}{}
|
||||
}
|
||||
|
||||
func DeleteNote(uid string) error {
|
||||
filename := filepath.Join(conf.Conf.NotesDir, fmtPath(uid))
|
||||
return os.Remove(filename)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ func GetRoutes(prefix string) *http.ServeMux {
|
|||
"view": {Path: "/{note}/", Protocol: "GET", Handler: view},
|
||||
"delete": {Path: "/{note}/delete/", Protocol: "GET", Handler: delete},
|
||||
"edit": {Path: "/{note}/edit/", Protocol: "GET", Handler: edit},
|
||||
"share": {Path: "/{note}/share/", Protocol: "POST", Handler: share},
|
||||
"save": {Path: "/{note}/edit/save/", Protocol: "POST", Handler: save},
|
||||
"togglebox": {Path: "/{note}/togglebox/", Protocol: "POST", Handler: togglebox},
|
||||
},
|
||||
|
|
@ -35,21 +36,25 @@ func GetRoutes(prefix string) *http.ServeMux {
|
|||
}
|
||||
|
||||
func view(w http.ResponseWriter, r *http.Request) {
|
||||
// user := r.Context().Value(middleware.ContextKey("user")).(string)
|
||||
user := r.Context().Value(middleware.ContextKey("user")).(string)
|
||||
|
||||
title := r.PathValue("note")
|
||||
note, err := notes.LoadNote(title)
|
||||
urlEdit := myurls.Reverse("edit", urls.Repl{"note": title})
|
||||
urlNew := myurls.Reverse("new", urls.Repl{})
|
||||
urlDelete := myurls.Reverse("delete", urls.Repl{"note": title})
|
||||
if err != nil {
|
||||
http.Redirect(w, r, urlEdit, http.StatusFound)
|
||||
uid := r.PathValue("note")
|
||||
note, ok := notes.Notes.GetOne(user, uid)
|
||||
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
viewers := note.ViewersAsList()
|
||||
|
||||
context := templ.Ctx{"note": note, "urlEdit": urlEdit, "urlDelete": urlDelete, "urlNew": urlNew}
|
||||
urlEdit := myurls.Reverse("edit", urls.Repl{"note": uid})
|
||||
urlNew := myurls.Reverse("new", urls.Repl{})
|
||||
urlDelete := myurls.Reverse("delete", urls.Repl{"note": uid})
|
||||
urlShare := myurls.Reverse("share", urls.Repl{"note": uid})
|
||||
|
||||
context := templ.Ctx{"note": note, "urlEdit": urlEdit, "urlDelete": urlDelete, "urlNew": urlNew, "urlShare": urlShare, "viewers": viewers}
|
||||
note.Render()
|
||||
err = templ.RenderTemplate(w, r, "view.tmpl.html", context)
|
||||
err := templ.RenderTemplate(w, r, "view.tmpl.html", context)
|
||||
if err != nil {
|
||||
log.Print(err.Error())
|
||||
http.Error(w, "Couldn't load template", http.StatusInternalServerError)
|
||||
|
|
@ -61,15 +66,14 @@ func edit(w http.ResponseWriter, r *http.Request) {
|
|||
user := r.Context().Value(middleware.ContextKey("user")).(string)
|
||||
|
||||
uid := r.PathValue("note")
|
||||
note, err := notes.LoadNote(uid)
|
||||
if err != nil {
|
||||
note = ¬es.Note{Title: "", Uid: uid}
|
||||
note, ok := notes.Notes.GetOne(user, uid)
|
||||
if !ok {
|
||||
note = notes.NewNote("", user)
|
||||
}
|
||||
|
||||
urlSave := myurls.Reverse("save", urls.Repl{"note": uid})
|
||||
context := templ.Ctx{"note": note, "urlSave": urlSave, "text": string(note.Body)}
|
||||
err = templ.RenderTemplate(w, r, "edit.tmpl.html", context)
|
||||
err := templ.RenderTemplate(w, r, "edit.tmpl.html", context)
|
||||
if err != nil {
|
||||
log.Print(err.Error())
|
||||
http.Error(w, "Couldn't load template", http.StatusInternalServerError)
|
||||
|
|
@ -126,19 +130,35 @@ func save(w http.ResponseWriter, r *http.Request) {
|
|||
http.Redirect(w, r, myurls.Reverse("view", urls.Repl{"note": note.Uid}), http.StatusFound)
|
||||
}
|
||||
|
||||
func togglebox(w http.ResponseWriter, r *http.Request) {
|
||||
// user := r.Context().Value(middleware.ContextKey("user")).(string)
|
||||
func share(w http.ResponseWriter, r *http.Request) {
|
||||
user := r.Context().Value(middleware.ContextKey("user")).(string)
|
||||
uid := r.PathValue("note")
|
||||
note, ok := notes.Notes.GetOne(user, uid)
|
||||
|
||||
title := r.PathValue("note")
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
viewer := r.FormValue("viewer")
|
||||
note.AddViewer(viewer)
|
||||
note.Save()
|
||||
|
||||
http.Redirect(w, r, myurls.Reverse("view", urls.Repl{"note": note.Uid}), http.StatusFound)
|
||||
}
|
||||
|
||||
func togglebox(w http.ResponseWriter, r *http.Request) {
|
||||
user := r.Context().Value(middleware.ContextKey("user")).(string)
|
||||
|
||||
uid := r.PathValue("note")
|
||||
nthBox, err := strconv.Atoi(r.FormValue("box"))
|
||||
if err != nil {
|
||||
log.Fatal("You fucked up boy")
|
||||
return
|
||||
}
|
||||
|
||||
note, err := notes.LoadNote(title)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, myurls.Reverse("view", urls.Repl{"note": title}), http.StatusFound)
|
||||
note, ok := notes.Notes.GetOne(user, uid)
|
||||
if !ok {
|
||||
http.Redirect(w, r, myurls.Reverse("view", urls.Repl{"note": uid}), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue