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