-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwiki.go
159 lines (125 loc) · 4.37 KB
/
wiki.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package main
import (
"html/template"
"io/ioutil"
"log"
"net/http"
"regexp"
// "fmt"
)
// pre-parse all templates (to help with caching)
var templates = template.Must(template.ParseFiles("tmpl/edit.html", "tmpl/view.html", "tmpl/front.html"))
// regular expression to validate page paths
var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)(/?)$")
func main() {
// p1 := &Page{Title: "TestPage", Body: []byte("This is a sample page.")}
// p1.save()
// p2, _ := loadPage("TestPage")
// fmt.Println(string(p2.Body))
// handler bindings for
http.HandleFunc("/view/", makeHandler(viewHandler))
http.HandleFunc("/edit/", makeHandler(editHandler))
http.HandleFunc("/save/", makeHandler(saveHandler))
// handler to show /view/frontpage for /
http.HandleFunc("/", frontPageHandler)
// serve directory of static resources
// http.Handle("/", http.FileServer(http.Dir("css/")))
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
// run server
log.Fatal(http.ListenAndServe(":8080", nil))
}
// struct to store a web page
type Page struct {
Title string
Body []byte // page body if []byte instead of string because that's what the IO libraries we use would require
Pages []string
}
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
err := templates.ExecuteTemplate(w, tmpl+".html", p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
// save body of a Page to a text file <pagetitle>.txt
func (p *Page) save() error {
filename := "./data/" + p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
// construct page filename for a gives page, load from file, and return pointer to Page literal
func loadPage(title string) (*Page, error) {
filename := "./data/" + title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}
// wrapper taking a function literal of xHandler(resp, req, title) type and returns a function of type http.HandlerFunc
func makeHandler(fn func (http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// extract page title from request and call the provided handler 'fn'
m := validPath.FindStringSubmatch(r.URL.Path)
if m == nil {
http.NotFound(w, r)
return
}
fn(w, r, m[2])
}
}
// ----------------- handler functions for views, edits and saves -----------------
// show a frontpage with a list of already created wiki pages
func frontPageHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/view/FrontPage/", http.StatusFound)
}
// view a webpage specified as http://server/view/webpage
func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
title = r.URL.Path[len("/view/"):]
if title == "FrontPage/" || title == "FrontPage" {
// fmt.Fprintf(w, "Welcome to the %s!", title)
// return
pageTitle := "FrontPage"
// get list of pages created
pages, err := ioutil.ReadDir("./data")
if err != nil {
log.Fatal(err)
}
pagesList := []string{}
if len(pages) <= 0 {
// provide link to create first page
} else {
for _, page := range pages {
pageName := page.Name()
pagesList = append(pagesList, pageName[:len(pageName) - len(".txt")])
}
}
p := &Page{Title: pageTitle, Pages: pagesList}
renderTemplate(w, "front", p)
return
}
p, err := loadPage(title)
// requested nonexistent page - redirect to edit page to create
if err != nil {
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
return
}
renderTemplate(w, "view", p)
}
// edithandler loads a given page (or creates struct if it doesn't exist) in editable form
func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
// fmt.Fprintf(w, "<h1>Editing %s</h1>" + "<form action=\"/save/%s\" method=\"POST\">" + "<textarea name=\"body\">%s</textarea><br>" + "<input type=\"submit\" value=\"Save\">" + "</form>", p.Title, p.Title, p.Body)
renderTemplate(w, "edit", p)
}
func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body")
p := &Page{Title: title, Body: []byte(body)}
err := p.save()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/view/"+title, http.StatusFound)
}