Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "http" |
| 5 | "io/ioutil" |
| 6 | "os" |
| 7 | "regexp" |
Andrew Gerrand | b67b72d | 2011-08-18 10:38:08 +1000 | [diff] [blame] | 8 | "template" |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 9 | ) |
| 10 | |
Andrew Gerrand | add4e16 | 2011-01-26 14:56:52 +1000 | [diff] [blame] | 11 | type Page struct { |
| 12 | Title string |
| 13 | Body []byte |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 14 | } |
| 15 | |
Andrew Gerrand | add4e16 | 2011-01-26 14:56:52 +1000 | [diff] [blame] | 16 | func (p *Page) save() os.Error { |
| 17 | filename := p.Title + ".txt" |
| 18 | return ioutil.WriteFile(filename, p.Body, 0600) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 19 | } |
| 20 | |
Andrew Gerrand | add4e16 | 2011-01-26 14:56:52 +1000 | [diff] [blame] | 21 | func loadPage(title string) (*Page, os.Error) { |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 22 | filename := title + ".txt" |
| 23 | body, err := ioutil.ReadFile(filename) |
| 24 | if err != nil { |
| 25 | return nil, err |
| 26 | } |
Andrew Gerrand | add4e16 | 2011-01-26 14:56:52 +1000 | [diff] [blame] | 27 | return &Page{Title: title, Body: body}, nil |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 28 | } |
| 29 | |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 30 | func viewHandler(w http.ResponseWriter, r *http.Request) { |
| 31 | title, err := getTitle(w, r) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 32 | if err != nil { |
| 33 | return |
| 34 | } |
| 35 | p, err := loadPage(title) |
| 36 | if err != nil { |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 37 | http.Redirect(w, r, "/edit/"+title, http.StatusFound) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 38 | return |
| 39 | } |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 40 | renderTemplate(w, "view", p) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 41 | } |
| 42 | |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 43 | func editHandler(w http.ResponseWriter, r *http.Request) { |
| 44 | title, err := getTitle(w, r) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 45 | if err != nil { |
| 46 | return |
| 47 | } |
| 48 | p, err := loadPage(title) |
| 49 | if err != nil { |
Andrew Gerrand | add4e16 | 2011-01-26 14:56:52 +1000 | [diff] [blame] | 50 | p = &Page{Title: title} |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 51 | } |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 52 | renderTemplate(w, "edit", p) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 53 | } |
| 54 | |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 55 | func saveHandler(w http.ResponseWriter, r *http.Request) { |
| 56 | title, err := getTitle(w, r) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 57 | if err != nil { |
| 58 | return |
| 59 | } |
| 60 | body := r.FormValue("body") |
Andrew Gerrand | add4e16 | 2011-01-26 14:56:52 +1000 | [diff] [blame] | 61 | p := &Page{Title: title, Body: []byte(body)} |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 62 | err = p.save() |
| 63 | if err != nil { |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 64 | http.Error(w, err.String(), http.StatusInternalServerError) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 65 | return |
| 66 | } |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 67 | http.Redirect(w, r, "/view/"+title, http.StatusFound) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 68 | } |
| 69 | |
Andrew Gerrand | add4e16 | 2011-01-26 14:56:52 +1000 | [diff] [blame] | 70 | func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { |
Andrew Gerrand | b67b72d | 2011-08-18 10:38:08 +1000 | [diff] [blame] | 71 | t, err := template.ParseFile(tmpl+".html") |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 72 | if err != nil { |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 73 | http.Error(w, err.String(), http.StatusInternalServerError) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 74 | return |
| 75 | } |
Andrew Gerrand | 4896b17 | 2011-03-09 12:59:13 +1100 | [diff] [blame] | 76 | err = t.Execute(w, p) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 77 | if err != nil { |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 78 | http.Error(w, err.String(), http.StatusInternalServerError) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 79 | } |
| 80 | } |
| 81 | |
| 82 | const lenPath = len("/view/") |
| 83 | |
| 84 | var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$") |
| 85 | |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 86 | func getTitle(w http.ResponseWriter, r *http.Request) (title string, err os.Error) { |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 87 | title = r.URL.Path[lenPath:] |
| 88 | if !titleValidator.MatchString(title) { |
Stephen Ma | a2332a3 | 2010-09-30 13:19:33 +1000 | [diff] [blame] | 89 | http.NotFound(w, r) |
Andrew Gerrand | 78d9a60 | 2010-04-28 12:36:39 +1000 | [diff] [blame] | 90 | err = os.NewError("Invalid Page Title") |
| 91 | } |
| 92 | return |
| 93 | } |
| 94 | |
| 95 | func main() { |
| 96 | http.HandleFunc("/view/", viewHandler) |
| 97 | http.HandleFunc("/edit/", editHandler) |
| 98 | http.HandleFunc("/save/", saveHandler) |
| 99 | http.ListenAndServe(":8080", nil) |
| 100 | } |