diff --git a/controllers/listcontroller.go b/controllers/listcontroller.go index 2ef1fe8..3f54061 100644 --- a/controllers/listcontroller.go +++ b/controllers/listcontroller.go @@ -78,7 +78,6 @@ func listFiltered(category model.PageCategory) wrouter.FunctionHandlerFunc { } } -// story.aiterp.net/Ruins_of_Rakhana func init() { ListController.Function("/", listIndex) diff --git a/controllers/pagecontroller.go b/controllers/pagecontroller.go new file mode 100644 index 0000000..a5285c7 --- /dev/null +++ b/controllers/pagecontroller.go @@ -0,0 +1,120 @@ +package controllers + +import ( + "net/http" + "strings" + "time" + + "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 + } + } + } + + view.Render(w, "create", 200, pc) + + return true +} + +func pageView(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 + } + + return true +} + +func init() { + PageController.Function("/create", pageCreate) + PageController.Function("/", pageView) +} diff --git a/formparser/parsers.go b/formparser/parsers.go index 9a79f0c..b684229 100644 --- a/formparser/parsers.go +++ b/formparser/parsers.go @@ -3,6 +3,7 @@ package formparser import ( "errors" "fmt" + "log" "time" ) @@ -24,12 +25,14 @@ func String(value string, target *string, min, max int) error { func Select(value string, target *string, allowedValues []string, optional bool) error { if value == "" { if !optional { - return errors.New("not a valid option") + return errors.New("no option selected") } return nil } + log.Println(value, allowedValues) + for _, allowedValue := range allowedValues { if value == allowedValue { *target = value diff --git a/main.go b/main.go index f309436..bee08d7 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ func main() { auth.Register(&controllers.WikiAthenticator{}) router.Mount("/user", &controllers.UserController) + router.Mount("/page", &controllers.PageController) router.Mount("/", &controllers.ListController) router.Static("/ui/", server.Main.Config.Directories.UI) diff --git a/model/category.go b/model/category.go index 459e996..ba9cc5b 100644 --- a/model/category.go +++ b/model/category.go @@ -9,6 +9,7 @@ type PageCategory struct { Key string Plural string Icon string + Info string } // URLRoot is the "folder" used for searching within the category @@ -20,13 +21,13 @@ func (category *PageCategory) URLRoot() string { // a limited selection of categories. I may move it to a configuration // or the database, but for now I think this list is pretty fixed var PageCategories = []PageCategory{ - {"OoC", "OoC", "ooc"}, - {"Info", "Info", "info"}, - {"News", "News", "news"}, - {"Item", "Items", "item"}, - {"Document", "Documents", "document"}, - {"Background", "Background", "background"}, - {"Story", "Stories", "story"}, + {"OoC", "OoC", "ooc", "OoC content is for announcements, scheduling, general information, or anything that is not in-universe"}, + {"Info", "Info", "info", "Information gained during and between RP sessions"}, + {"News", "News", "news", "News stories that might be pertinent to ongoing plots"}, + {"Item", "Items", "item", "Items relevant to plots, that is more than just a document saved on a character's own omni-tool"}, + {"Document", "Documents", "document", "Data files, shadow broker dossiers, and other data that is not inside an item"}, + {"Background", "Background", "background", "Rumors, suspicious persons, or inter-RP occurences that may be noticed"}, + {"Story", "Stories", "story", "Background stories and inter-RP character intearactions"}, } var pageCategories []string diff --git a/view/renderer.go b/view/renderer.go index cadd975..21185eb 100644 --- a/view/renderer.go +++ b/view/renderer.go @@ -79,4 +79,5 @@ func Run(w io.Writer, name string, viewModel interface{}) error { func init() { Register("index", "base/default") Register("login", "base/default") + Register("create", "base/default") } diff --git a/view/templates/create.tmpl b/view/templates/create.tmpl new file mode 100644 index 0000000..6b8440c --- /dev/null +++ b/view/templates/create.tmpl @@ -0,0 +1,50 @@ +{{ define "content" }} +
+

Create

+
+

{{$.Error}}

+ + + +
+ {{ range $.Categories }} +
+ {{.Key}}: {{.Info}} +
+ {{ end }} +
+ +
+
+ Dated: The IC date is shown on the page list. It will still be used for sorting if this option is disabled. +
+
+ Unlisted: This page will not show up on page lists, but anyone with a link can view it. +
+
+ Specific: This page will only show up on page lists when one of its tags is searched for. +
+
+ + + + + + + +
+
+{{ end }} + +{{ define "menu" }} +

Aite RP

+ + +{{ end }} + +{{ define "head" }} + + +{{ end }} \ No newline at end of file diff --git a/view/templates/login.tmpl b/view/templates/login.tmpl index 8162347..ecdfe3c 100644 --- a/view/templates/login.tmpl +++ b/view/templates/login.tmpl @@ -3,8 +3,8 @@

Login

{{$.Error}}

- - + +
@@ -12,10 +12,12 @@ {{ define "menu" }}

Aite RP

- -
  • <
    Back
  • + + {{ end }} {{ define "head" }} - + {{ end }} \ No newline at end of file diff --git a/viewmodel/pageform.go b/viewmodel/pageform.go new file mode 100644 index 0000000..c84ac79 --- /dev/null +++ b/viewmodel/pageform.go @@ -0,0 +1,21 @@ +package viewmodel + +import ( + "git.aiterp.net/AiteRP/aitestory/model" + "git.aiterp.net/gisle/wrouter/auth" +) + +type PageForm struct { + Base + Page model.Page + Categories []model.PageCategory + Operation string + Error string + TagInput string +} + +func (pf *PageForm) Setup(user *auth.User) { + pf.setupBase(user, "Create") + pf.Categories = model.PageCategories + pf.Page.Defaults() +} diff --git a/viewmodel/pageview.go b/viewmodel/pageview.go new file mode 100644 index 0000000..643fa41 --- /dev/null +++ b/viewmodel/pageview.go @@ -0,0 +1,15 @@ +package viewmodel + +import ( + "git.aiterp.net/AiteRP/aitestory/model" + "git.aiterp.net/gisle/wrouter/auth" +) + +type PageView struct { + Base + Page model.Page +} + +func (pv *PageView) Setup(user *auth.User) { + pv.setupBase(user, pv.Page.Name) +}