github.com/mssola/todo@v0.0.0-20181029153210-d25348dc3f48/app/topics.go (about) 1 // Copyright (C) 2014-2017 Miquel Sabaté Solà <mikisabate@gmail.com> 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, v. 2.0. If a copy of the MPL was not distributed with this 5 // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 package app 8 9 import ( 10 "log" 11 "net/http" 12 "time" 13 14 "github.com/docker/distribution/uuid" 15 "github.com/gorilla/mux" 16 "github.com/microcosm-cc/bluemonday" 17 "github.com/mssola/todo/lib" 18 "github.com/russross/blackfriday" 19 ) 20 21 // Topic is my way to divide different "contexts" inside my To Do list. 22 // Moreover this type also has the "Markdown" attribute. This attribute does 23 // not match any column from the database, but it comes handy in the API layer. 24 type Topic struct { 25 ID string `json:"id"` 26 Name string `json:"name"` 27 Contents string `json:"contents"` 28 CreatedAt time.Time `db:"created_at" json:"created_at"` 29 Markdown string `db:"-" json:"markdown"` 30 } 31 32 // RenderMarkdown generates the Markdown code for the current contents of this 33 // topic. 34 func (t *Topic) RenderMarkdown() { 35 unsafe := blackfriday.MarkdownCommon([]byte(t.Contents)) 36 t.Markdown = string(bluemonday.UGCPolicy().SanitizeBytes(unsafe)) 37 } 38 39 // TopicData is the data that will be assed to the renderShow function in order 40 // to render the main page. 41 type TopicData struct { 42 lib.ViewData 43 44 Current *Topic 45 46 Topics []Topic 47 } 48 49 // Given a name, try to create a new topic. 50 func createTopic(name string) (*Topic, error) { 51 id := uuid.Generate().String() 52 t := &Topic{ID: id, Name: name, CreatedAt: time.Now()} 53 return t, Db.Insert(t) 54 } 55 56 // Sends the main page with the given topic rendered in it as 57 // the current one. 58 func renderShow(res http.ResponseWriter, topic *Topic, print bool) { 59 var topics []Topic 60 _, err := Db.Select(&topics, "select * from topics order by name") 61 if err != nil { 62 log.Printf("Select went wrong: %v", err) 63 } 64 topic.RenderMarkdown() 65 66 // And render the page. 67 o := &TopicData{ 68 Current: topic, 69 Topics: topics, 70 } 71 o.JS = "topics" 72 o.Print = print 73 lib.Render(res, "topics/show", o) 74 } 75 76 // TopicsIndex responds to: GET /topics 77 func TopicsIndex(res http.ResponseWriter, req *http.Request) { 78 if lib.JSONEncoding(req) { 79 TopicsIndexJSON(res, req) 80 return 81 } 82 83 var err error 84 var t Topic 85 86 if id := lib.GetCookie(req, "topic"); id != "" && id != nil { 87 err = Db.SelectOne(&t, "select * from topics where id=$1", id) 88 } else { 89 err = Db.SelectOne(&t, "select * from topics order by name limit 1") 90 } 91 if err != nil { 92 log.Printf("Could not select topics: %v", err) 93 } 94 renderShow(res, &t, false) 95 } 96 97 // TopicsCreate responds to: POST /topics 98 func TopicsCreate(res http.ResponseWriter, req *http.Request) { 99 if lib.JSONEncoding(req) { 100 TopicsCreateJSON(res, req) 101 return 102 } 103 104 if t, err := createTopic(req.FormValue("name")); err != nil { 105 http.Redirect(res, req, "/topics", http.StatusFound) 106 } else { 107 http.Redirect(res, req, "/topics/"+t.ID, http.StatusFound) 108 } 109 } 110 111 // TopicsShow responds to: GET /topics/:id 112 func TopicsShow(res http.ResponseWriter, req *http.Request) { 113 if lib.JSONEncoding(req) { 114 TopicsShowJSON(res, req) 115 return 116 } 117 118 var t Topic 119 120 p := mux.Vars(req) 121 err := Db.SelectOne(&t, "select * from topics where id=$1", p["id"]) 122 if err != nil { 123 log.Printf("Could not select topic: %v", err) 124 } 125 if t.ID != "" { 126 lib.SetCookie(res, req, "topic", t.ID) 127 } 128 print := req.URL.Query().Get("print") == "1" 129 renderShow(res, &t, print) 130 } 131 132 // TopicsUpdate responds to: PUT/PATCH /posts/:id 133 func TopicsUpdate(res http.ResponseWriter, req *http.Request) { 134 var err error 135 p := mux.Vars(req) 136 137 // We can either rename, or change the contents, but not both things at the 138 // same time. 139 name := req.FormValue("name") 140 if name != "" { 141 _, err = Db.Exec("update topics set name=$1 where id=$2", name, p["id"]) 142 } else { 143 cts := req.FormValue("contents") 144 _, err = Db.Exec("update topics set contents=$1 where id=$2", cts, p["id"]) 145 } 146 if err != nil { 147 log.Printf("Could not update topic: %v", err) 148 } 149 http.Redirect(res, req, "/topics", http.StatusFound) 150 } 151 152 // TopicsDestroy responds to: DELETE /posts/:id 153 func TopicsDestroy(res http.ResponseWriter, req *http.Request) { 154 p := mux.Vars(req) 155 _, err := Db.Exec("delete from topics where id=$1", p["id"]) 156 if err != nil { 157 log.Printf("Could not perform delete of topic: %v", err) 158 } 159 http.Redirect(res, req, "/topics", http.StatusFound) 160 }