package controllers import ( "fmt" "net/http" "strings" "time" "git.aiterp.net/gisle/wrouter/response" "git.aiterp.net/AiteRP/aitestory/model" "git.aiterp.net/AiteRP/aitestory/viewmodel" "git.aiterp.net/AiteRP/aitestory/view" "git.aiterp.net/gisle/wrouter" "git.aiterp.net/gisle/wrouter/auth" ) // PageController serves page creation, viewing, editing and deleting through the website. var PageController = wrouter.Router{} func pageCreate(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool { if (req.Method != "GET" && req.Method != "POST") || len(req.URL.Path) > len(path) { return false } pc := viewmodel.PageForm{} pc.Setup(user) pc.Operation = "Create" // Make sure the user is logged in if user == nil { // It's completely safe to eject logged-out users if they're not submitting if req.Method == "GET" { http.Redirect(w, req, "/user/login?form=/page/create", 302) return true } // Logged in users would probably want a chance to log in on another form, though. pc.Error = "You are not logged in." } // Respect the banhammer's authority if user != nil && user.Level == "restricted" { pc.Error = "Your user account is not permitted to do this" } // Handle submissions if nothing has complained yet if req.Method == "POST" && pc.Error == "" { // Validate form req.ParseForm() errs := pc.Page.ParseForm(req.Form) if len(errs) > 0 { pc.Error = "Validation failed: " + errs[0].Error() } // Parse the tags textbox pc.TagInput = req.Form.Get("tags") tagLines := strings.Split(pc.TagInput, "\n") for _, line := range tagLines { var tagType, tagName string // Skip empty lines, and allow some accidental letters if len(line) < 2 { continue } // Parse tokens tokens := strings.SplitN(line, ":", 2) if len(tokens) == 2 { tagType = strings.Trim(tokens[0], "  \t\r") tagName = strings.Trim(tokens[1], "  \t\r") } else { tagType = "*" // Permit untyped tags if it exists. tagName = strings.Trim(tokens[0], "  \t\r") } // Grab the tag tag, err := model.EnsureTag(tagType, tagName) if err != nil { pc.Error = "Check your tags: " + err.Error() break } // Take a copy of it pc.Page.Tags = append(pc.Page.Tags, *tag) } // If everything worked out, fill in the last bits and send it off if len(errs) == 0 && pc.Error == "" { pc.Page.Author = user.FullID() pc.Page.PublishDate = time.Now() pc.Page.EditDate = pc.Page.PublishDate.Add(-time.Hour) err := pc.Page.Insert() if err != nil { pc.Error = "Insert failed: " + err.Error() } else { // Let us see what you have wrought upon the world http.Redirect(w, req, "/page/"+pc.Page.ID, 302) return true } } } else { // Losing input makes the user sad, let's see to it // that it doesn't happen. req.ParseForm() pc.Page.ParseForm(req.Form) pc.TagInput = req.Form.Get("tags") } view.Render(w, "page/create", 200, pc) return true } func pageView(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool { if req.Method != "GET" || strings.LastIndex(req.URL.Path, "/") > len(path) { return false } page, err := model.FindPage(req.URL.Path[len(path):]) if err != nil { response.Text(w, 500, err.Error()) return true } else if page == nil { return false } pv := viewmodel.PageView{} pv.Page = page pv.Setup(user) view.Render(w, "page/view", 200, pv) return true } func pageDelete(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool { if (req.Method != "GET" && req.Method != "POST") || strings.LastIndex(req.URL.Path, "/") > len(path) { return false } page, err := model.FindPage(req.URL.Path[len(path):]) if err != nil { response.Text(w, 500, err.Error()) return true } else if page == nil { return false } pv := viewmodel.PageView{} pv.Page = page pv.Setup(user) if user == nil || user.FullID() != page.Author { view.Render(w, "message/error-access", http.StatusForbidden, path) return true } if req.Method == "POST" { req.ParseForm() // Catch sneaky shenanigans if req.Form.Get("aft") != user.Session.ID { view.Render(w, "message/error-forgery", http.StatusForbidden, path) return true } // Thy will be done err := page.Delete() if err != nil { // It wasn't done D: view.Render(w, "message/error-internal", http.StatusInternalServerError, err) return true } // It has been done view.Render(w, "message/page-deleted", 200, pv) return true } view.Render(w, "page/delete", 200, pv) return true } func pageEdit(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool { if (req.Method != "GET" && req.Method != "POST") || strings.LastIndex(req.URL.Path, "/") > len(path) { return false } page, err := model.FindPage(req.URL.Path[len(path):]) if err != nil { response.Text(w, 500, err.Error()) return true } else if page == nil { return false } pf := viewmodel.PageForm{} pf.Setup(user) pf.Operation = "Edit" pf.Page = *page if user == nil { pf.Error = "You are not logged in" } if user != nil && user.FullID() != page.Author { pf.Error = "Permission denied" } if req.Method == "POST" { req.ParseForm() errs := pf.Page.ParseForm(req.Form) if len(errs) > 0 { pf.Error = "Validation failed: " + errs[0].Error() } if len(errs) == 0 && pf.Error == "" { pf.TagInput = req.Form.Get("tags") pf.Page.Tags = make([]model.Tag, 0, strings.Count(pf.TagInput, "\n")) tagLines := strings.Split(pf.TagInput, "\n") for _, line := range tagLines { var tagType, tagName string // Skip empty lines, and allow some accidental letters if len(line) < 2 { continue } // Parse tokens tokens := strings.SplitN(line, ":", 2) if len(tokens) == 2 { tagType = strings.Trim(tokens[0], "  \t\r") tagName = strings.Trim(tokens[1], "  \t\r") } else { tagType = "*" // Permit untyped tags if it exists. tagName = strings.Trim(tokens[0], "  \t\r") } // Grab the tag tag, err := model.EnsureTag(tagType, tagName) if err != nil { pf.Error = "Check your tags: " + err.Error() break } // Take a copy of it pf.Page.Tags = append(pf.Page.Tags, *tag) } if pf.Error == "" { pf.Page.EditDate = time.Now() err := pf.Page.Update() if err != nil { pf.Error = "Edit failed: " + err.Error() } else { http.Redirect(w, req, "/page/"+pf.Page.ID, 302) return true } } } } else { for _, tag := range page.Tags { line := fmt.Sprintf("%s: %s\n", tag.Type, tag.Name) if page.PrimaryTag().ID == tag.ID { pf.TagInput = line + pf.TagInput } else { pf.TagInput += line } } } view.Render(w, "page/edit", 200, pf) return true } func init() { PageController.Function("/create", pageCreate) PageController.Function("/edit/", pageEdit) PageController.Function("/delete/", pageDelete) PageController.Function("/", pageView) }