From fb9e43ba12626c886cf2014ff4d39be4a97a80b8 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Sun, 29 Oct 2017 17:41:40 +0100 Subject: [PATCH] Massive overhaul, fixing menus and changing template engine. Added /month/ and /author/ filters --- controllers/listcontroller.go | 94 +++++++++++++++++++++++-- controllers/pagecontroller.go | 44 ++++++++---- controllers/tagcontroller.go | 34 --------- main.go | 19 +++-- model/category.go | 11 +-- model/header.go | 74 ++++++++++++++++++- model/page.go | 7 +- model/tag.go | 21 ++++-- view/amber/base/master.amber | 44 ++++++++++++ view/amber/base/mixins.amber | 30 ++++++++ view/amber/create.amber | 63 +++++++++++++++++ view/amber/index.amber | 25 +++++++ view/amber/menu.amber | 25 +++++++ view/amber/message.amber | 12 ++++ view/amber/page/delete.amber | 30 ++++++++ view/amber/page/edit.amber | 64 +++++++++++++++++ view/amber/page/menu.amber | 29 ++++++++ view/amber/page/source.amber | 14 ++++ view/amber/page/view.amber | 13 ++++ view/amber/tags.amber | 17 +++++ view/amber/user/login.amber | 26 +++++++ view/amber/user/menu.amber | 4 ++ view/renderer.go | 79 +++++++++++---------- view/templates/base/default.tmpl | 41 ----------- view/templates/index.tmpl | 66 ----------------- view/templates/page/create.tmpl | 44 ------------ view/templates/page/delete.tmpl | 37 ---------- view/templates/page/edit.tmpl | 44 ------------ view/templates/page/view.tmpl | 44 ------------ view/templates/tags/list.tmpl | 48 ------------- view/templates/user/login.tmpl | 24 ------- viewmodel/base.go | 9 ++- viewmodel/indexbase.go | 16 +++++ viewmodel/{pagelist.go => indexlist.go} | 12 ++-- viewmodel/indextags.go | 49 +++++++++++++ viewmodel/message.go | 19 +++++ viewmodel/pagebase.go | 9 +++ viewmodel/pagecreate.go | 26 +++++++ viewmodel/pagedelete.go | 19 +++++ viewmodel/{pageform.go => pageedit.go} | 14 ++-- viewmodel/pagesource.go | 20 ++++++ viewmodel/pageview.go | 11 ++- viewmodel/taglist.go | 46 ------------ viewmodel/userlogin.go | 2 +- 44 files changed, 861 insertions(+), 518 deletions(-) delete mode 100644 controllers/tagcontroller.go create mode 100644 view/amber/base/master.amber create mode 100644 view/amber/base/mixins.amber create mode 100644 view/amber/create.amber create mode 100644 view/amber/index.amber create mode 100644 view/amber/menu.amber create mode 100644 view/amber/message.amber create mode 100644 view/amber/page/delete.amber create mode 100644 view/amber/page/edit.amber create mode 100644 view/amber/page/menu.amber create mode 100644 view/amber/page/source.amber create mode 100644 view/amber/page/view.amber create mode 100644 view/amber/tags.amber create mode 100644 view/amber/user/login.amber create mode 100644 view/amber/user/menu.amber delete mode 100644 view/templates/base/default.tmpl delete mode 100644 view/templates/index.tmpl delete mode 100644 view/templates/page/create.tmpl delete mode 100644 view/templates/page/delete.tmpl delete mode 100644 view/templates/page/edit.tmpl delete mode 100644 view/templates/page/view.tmpl delete mode 100644 view/templates/tags/list.tmpl delete mode 100644 view/templates/user/login.tmpl create mode 100644 viewmodel/indexbase.go rename viewmodel/{pagelist.go => indexlist.go} (58%) create mode 100644 viewmodel/indextags.go create mode 100644 viewmodel/message.go create mode 100644 viewmodel/pagebase.go create mode 100644 viewmodel/pagecreate.go create mode 100644 viewmodel/pagedelete.go rename viewmodel/{pageform.go => pageedit.go} (50%) create mode 100644 viewmodel/pagesource.go delete mode 100644 viewmodel/taglist.go diff --git a/controllers/listcontroller.go b/controllers/listcontroller.go index ad59367..447f4c9 100644 --- a/controllers/listcontroller.go +++ b/controllers/listcontroller.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "strings" + "time" "git.aiterp.net/gisle/wrouter/response" @@ -24,10 +25,10 @@ func listIndex(path string, w http.ResponseWriter, req *http.Request, user *auth return false } - vm := viewmodel.PageList{} + vm := viewmodel.IndexList{} vm.Headers, err = model.ListHeaders() vm.Categories = model.PageCategories - vm.Setup(user) + vm.Setup(user, req.URL.Path, "Index") if err != nil { response.Text(w, 500, err.Error()) @@ -49,7 +50,7 @@ func listFiltered(category model.PageCategory) wrouter.FunctionHandlerFunc { tagName := strings.Replace(req.URL.Path[len(path):], "_", " ", -1) - vm := viewmodel.PageList{} + vm := viewmodel.IndexList{} if tagName != "" { tag, err := model.FindTag("name", tagName, "") @@ -66,7 +67,7 @@ func listFiltered(category model.PageCategory) wrouter.FunctionHandlerFunc { vm.Categories = model.PageCategories vm.ActiveCategory = category - vm.Setup(user) + vm.Setup(user, path, category.Plural) if err != nil { response.Text(w, 500, err.Error()) @@ -89,7 +90,7 @@ func listTagged(tagType string) wrouter.FunctionHandlerFunc { tagName := strings.Replace(req.URL.Path[len(path):], "_", " ", -1) - vm := viewmodel.PageList{} + vm := viewmodel.IndexList{} if tagName == "" { return false @@ -105,7 +106,7 @@ func listTagged(tagType string) wrouter.FunctionHandlerFunc { vm.Headers, err = model.ListHeadersByTag("", tag) vm.Categories = model.PageCategories - vm.Setup(user) + vm.Setup(user, tag.Path(), tag.Name) if err != nil { response.Text(w, 500, err.Error()) @@ -118,11 +119,90 @@ func listTagged(tagType string) wrouter.FunctionHandlerFunc { } } +func tagList(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool { + //var err error + if (req.Method != "GET" && req.Method != "POST") || len(req.URL.Path) > len(path) { + return false + } + + tl := viewmodel.IndexTags{} + tl.Tags, _ = model.ListTags() + tl.Setup(user, path) + + view.Render(w, "tags", 200, tl) + + return true +} + +func listAuthor(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool { + var err error + + if req.Method != "GET" || strings.LastIndex(req.URL.Path, "/") >= len(path) { + return false + } + + authorName := strings.Replace(req.URL.Path[len(path):], "_", " ", -1) + + vm := viewmodel.IndexList{} + vm.ActiveAuthor = authorName + + vm.Headers, err = model.ListHeadersByAuthor(authorName) + if err != nil { + response.Text(w, 500, err.Error()) + return true + } + + if len(vm.Headers) > 0 { + authorName = vm.Headers[0].Author + } + + vm.Categories = model.PageCategories + vm.Setup(user, "/author/"+authorName, authorName) + + view.Render(w, "index", 200, vm) + + return true +} + +func listDate(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool { + var err error + + if req.Method != "GET" || strings.LastIndex(req.URL.Path, "/") >= len(path) { + return false + } + + date, err := time.Parse("2006-01", req.URL.Path[len(path):]) + if err != nil { + response.Text(w, 400, "Invalid date") + return true + } + + vm := viewmodel.IndexList{} + vm.ActiveDate = date.Format("January 2006") + vm.ActiveDatePath = "/month/" + date.Format("2006-01") + + vm.Headers, err = model.ListHeadersByMonth(date.Year(), int(date.Month())) + if err != nil { + response.Text(w, 500, err.Error()) + return true + } + + vm.Categories = model.PageCategories + vm.Setup(user, "/month/"+date.Format("2006-01"), vm.ActiveDate) + + view.Render(w, "index", 200, vm) + + return true +} + func init() { ListController.Function("/", listIndex) + ListController.Function("/tags/", tagList) + ListController.Function("/author/", listAuthor) + ListController.Function("/month/", listDate) for _, category := range model.PageCategories { - ListController.Function(fmt.Sprintf("/%s/", strings.ToLower(category.Plural)), listFiltered(category)) + ListController.Function(category.Path, listFiltered(category)) } for _, tagType := range model.TagTypes { diff --git a/controllers/pagecontroller.go b/controllers/pagecontroller.go index efb5c6f..78d08f6 100644 --- a/controllers/pagecontroller.go +++ b/controllers/pagecontroller.go @@ -25,9 +25,8 @@ func pageCreate(path string, w http.ResponseWriter, req *http.Request, user *aut return false } - pc := viewmodel.PageForm{} + pc := viewmodel.PageCreate{} pc.Setup(user) - pc.Operation = "Create" // Make sure the user is logged in if user == nil { @@ -110,7 +109,7 @@ func pageCreate(path string, w http.ResponseWriter, req *http.Request, user *aut pc.TagInput = req.Form.Get("tags") } - view.Render(w, "page/create", 200, pc) + view.Render(w, "create", 200, pc) return true } @@ -137,6 +136,28 @@ func pageView(path string, w http.ResponseWriter, req *http.Request, user *auth. return true } +func pageSource(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.PageSource{} + pv.Page = page + pv.Setup(user) + + view.Render(w, "page/source", 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 @@ -150,12 +171,12 @@ func pageDelete(path string, w http.ResponseWriter, req *http.Request, user *aut return false } - pv := viewmodel.PageView{} + pv := viewmodel.PageDelete{} pv.Page = page pv.Setup(user) if user == nil || user.FullID() != page.Author { - view.Render(w, "message/error-access", http.StatusForbidden, path) + view.Render(w, "message", http.StatusForbidden, viewmodel.NewMessage(user, "Access Denied", true, "You are not allowed to delete this page.")) return true } @@ -164,7 +185,7 @@ func pageDelete(path string, w http.ResponseWriter, req *http.Request, user *aut // Catch sneaky shenanigans if req.Form.Get("aft") != user.Session.ID { - view.Render(w, "message/error-forgery", http.StatusForbidden, path) + view.Render(w, "message", http.StatusForbidden, viewmodel.NewMessage(user, "Forgery Detected", true, "The server thinks something nasty is afoot. Try again if it is your page, and prod me on IRC if it still doesn't work.")) return true } @@ -172,12 +193,12 @@ func pageDelete(path string, w http.ResponseWriter, req *http.Request, user *aut err := page.Delete() if err != nil { // It wasn't done D: - view.Render(w, "message/error-internal", http.StatusInternalServerError, err) + view.Render(w, "message", http.StatusInternalServerError, viewmodel.NewMessage(user, "Internal Server Error", true, err.Error())) return true } // It has been done - view.Render(w, "message/page-deleted", 200, pv) + view.Render(w, "message", 200, viewmodel.NewMessage(user, "Deletion Successful", false, "The page has been deleted.")) return true } @@ -198,12 +219,10 @@ func pageEdit(path string, w http.ResponseWriter, req *http.Request, user *auth. return false } - pf := viewmodel.PageForm{} + pf := viewmodel.PageEdit{} + pf.Page = page pf.Setup(user) - pf.Operation = "Edit" - pf.Page = *page - if user == nil { pf.Error = "You are not logged in" } @@ -285,5 +304,6 @@ func init() { PageController.Function("/create", pageCreate) PageController.Function("/edit/", pageEdit) PageController.Function("/delete/", pageDelete) + PageController.Function("/source/", pageSource) PageController.Function("/", pageView) } diff --git a/controllers/tagcontroller.go b/controllers/tagcontroller.go deleted file mode 100644 index 3c68132..0000000 --- a/controllers/tagcontroller.go +++ /dev/null @@ -1,34 +0,0 @@ -package controllers - -import ( - "net/http" - - "git.aiterp.net/AiteRP/aitestory/view" - "git.aiterp.net/AiteRP/aitestory/viewmodel" - - "git.aiterp.net/AiteRP/aitestory/model" - "git.aiterp.net/gisle/wrouter" - "git.aiterp.net/gisle/wrouter/auth" -) - -// TagController serves and handles the tag managment pages -var TagController = wrouter.Router{} - -func tagList(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool { - //var err error - if (req.Method != "GET" && req.Method != "POST") || len(req.URL.Path) > len(path) { - return false - } - - tl := viewmodel.TagList{} - tl.Tags, _ = model.ListTags() - tl.Setup(user) - - view.Render(w, "tags/list", 200, tl) - - return true -} - -func init() { - TagController.Function("/", tagList) -} diff --git a/main.go b/main.go index 597d0f7..dfcaf2a 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,14 @@ package main -import "git.aiterp.net/AiteRP/aitestory/server" -import "git.aiterp.net/AiteRP/aitestory/controllers" -import "git.aiterp.net/gisle/wrouter/auth" +import ( + "net/http" + + "git.aiterp.net/AiteRP/aitestory/controllers" + "git.aiterp.net/AiteRP/aitestory/server" + "git.aiterp.net/AiteRP/aitestory/view" + "git.aiterp.net/AiteRP/aitestory/viewmodel" + "git.aiterp.net/gisle/wrouter/auth" +) func main() { router := &server.Main.Router @@ -10,10 +16,15 @@ func main() { auth.Register(&controllers.WikiAthenticator{}) router.Mount("/user", &controllers.UserController) router.Mount("/page", &controllers.PageController) - router.Mount("/tags", &controllers.TagController) router.Mount("/", &controllers.ListController) router.Static("/ui/", server.Main.Config.Directories.UI) + router.Function("/", func(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool { + view.Render(w, "message", http.StatusNotFound, viewmodel.NewMessage(user, "404: "+req.URL.Path, true, "The server don't know what to do with that address.")) + + return true + }) + server.Main.Start() } diff --git a/model/category.go b/model/category.go index 5e108a4..639dd3d 100644 --- a/model/category.go +++ b/model/category.go @@ -9,6 +9,7 @@ type PageCategory struct { Key string Plural string Icon string + Path string Info string } @@ -22,12 +23,12 @@ func (category *PageCategory) URLRoot() string { // or the database, but for now I think this list is pretty fixed var PageCategories = []PageCategory{ //{"OoC", "OoC", "O", "OoC content is for announcements, scheduling, general information, or anything that is not in-universe"}, - {"Info", "Info", "i", "Information relevant to RP that might be something looked for in IRC logs"}, - {"News", "News", "N", "News stories that might be pertinent to ongoing plots"}, + {"Info", "Info", "i", "/info/", "Information relevant to RP that might be something looked for in IRC logs"}, + {"News", "News", "N", "/news/", "News stories that might be pertinent to ongoing plots"}, //{"Item", "Items", "I", "Items relevant to plots, that is more than just a document saved on a character's own omni-tool"}, - {"Document", "Documents", "D", "Data files, shadow broker dossiers, and other data that is not inside an item"}, - {"Background", "Background", "B", "Rumors, suspicious persons, or inter-RP occurences that may be noticed"}, - {"Story", "Stories", "S", "Background stories and inter-RP character intearactions"}, + {"Document", "Documents", "D", "/documents/", "Data files, shadow broker dossiers, and other data that is not inside an item"}, + {"Background", "Background", "B", "/background/", "Rumors, suspicious persons, or inter-RP occurences that may be noticed"}, + {"Story", "Stories", "S", "/stories/", "Background stories and inter-RP character intearactions"}, } var pageCategories []string diff --git a/model/header.go b/model/header.go index 26c8697..c9bec56 100644 --- a/model/header.go +++ b/model/header.go @@ -3,6 +3,7 @@ package model import ( "database/sql" "errors" + "fmt" "time" "git.aiterp.net/AiteRP/aitestory/server" @@ -34,7 +35,12 @@ func (header *Header) CategoryInfo() PageCategory { } } - return PageCategory{"Unknown", "Unknown", "?", ""} + return PageCategory{"Unknown", "Unknown", "?", "/", ""} +} + +// Path returns the path for viewing the page +func (header *Header) Path() string { + return fmt.Sprintf("/page/%s", header.ID) } // ListHeaders grabs all the general pages from @@ -104,6 +110,72 @@ func ListHeadersByCategory(category string) ([]Header, error) { return results, nil } +// ListHeadersByAuthor grabs all the pages by the given author +func ListHeadersByAuthor(author string) ([]Header, error) { + const query = ` + SELECT page.id,page.name,author,category,fictional_date,publish_date,edit_date,tag.id,tag.type,tag.name + FROM page + LEFT JOIN page_tag ON (page.id = page_tag.page_id AND page_tag.primary = true) + LEFT JOIN tag ON (tag.id = page_tag.tag_id) + WHERE page.specific=false AND page.published=true AND page.unlisted=false AND page.author = ? + ORDER BY page.publish_date DESC + ` + + db := server.Main.DB + + rows, err := db.Query(query, author) + if err != nil { + return nil, err + } + defer rows.Close() + + results := make([]Header, 0, 64) + header := Header{} + for rows.Next() { + err := parseHeader(&header, rows) + if err != nil { + return nil, err + } + + results = append(results, header) + } + + return results, nil +} + +// ListHeadersByMonth grabs all the pages within the given month +func ListHeadersByMonth(year int, month int) ([]Header, error) { + const query = ` + SELECT page.id,page.name,author,category,fictional_date,publish_date,edit_date,tag.id,tag.type,tag.name + FROM page + LEFT JOIN page_tag ON (page.id = page_tag.page_id AND page_tag.primary = true) + LEFT JOIN tag ON (tag.id = page_tag.tag_id) + WHERE page.specific=false AND page.published=true AND page.unlisted=false AND YEAR(fictional_date) = ? AND MONTH(fictional_date) = ? + ORDER BY page.publish_date DESC + ` + + db := server.Main.DB + + rows, err := db.Query(query, year, month) + if err != nil { + return nil, err + } + defer rows.Close() + + results := make([]Header, 0, 64) + header := Header{} + for rows.Next() { + err := parseHeader(&header, rows) + if err != nil { + return nil, err + } + + results = append(results, header) + } + + return results, nil +} + // ListHeadersByTag lists all headers that has the tag. Leave the category empty // to not filter by it func ListHeadersByTag(category string, tag *Tag) ([]Header, error) { diff --git a/model/page.go b/model/page.go index a170b68..099c850 100644 --- a/model/page.go +++ b/model/page.go @@ -139,7 +139,7 @@ func (page *Page) Update() error { } // Stop now if the tages haven't changed - if len(page.prevTags) == len(page.Tags) { + if len(page.Tags) > 0 && len(page.prevTags) == len(page.Tags) { change := false for i, tag := range page.prevTags { @@ -265,6 +265,11 @@ func (page *Page) ParseForm(form url.Values) []error { return errors } +// OpPath gets a path associated with a specific operation on this page. +func (page *Page) OpPath(op string) string { + return fmt.Sprintf("/page/%s/%s", op, page.ID) +} + // Standardize page ID generation func (page *Page) generateID() { page.ID = generate.FriendlyID(16) diff --git a/model/tag.go b/model/tag.go index 0d78245..18dabaf 100644 --- a/model/tag.go +++ b/model/tag.go @@ -18,6 +18,15 @@ var TagTypes = []string{ "Series", } +// TagTitles are the titles to use when listing tags +var TagTitles = map[string]string{ + "Location": "Locations", + "Character": "Characters", + "Event": "Plots & Events", + "Organization": "Organizations", + "Series": "Series", +} + // Tag describes a tag type Tag struct { ID string @@ -121,10 +130,14 @@ func (tag *Tag) Validate() error { return nil } -// Hook returns the url friendly name, which is pretty much just -// adding underscores to it. -func (tag Tag) Hook() string { - return strings.Replace(tag.Name, " ", "_", -1) +// Path returns a path used for the tag's list page +func (tag Tag) Path() string { + return fmt.Sprintf("/%s/%s", strings.ToLower(tag.Type), strings.Replace(tag.Name, " ", "_", -1)) +} + +// CSSClass gets the tag's css class, which determines its color. +func (tag Tag) CSSClass() string { + return fmt.Sprintf("ttype-%s", strings.ToLower(tag.Type)) } // FindTag finds a tag by ID. Leave tagtype blank to ignore it. Both key and diff --git a/view/amber/base/master.amber b/view/amber/base/master.amber new file mode 100644 index 0000000..1edd5aa --- /dev/null +++ b/view/amber/base/master.amber @@ -0,0 +1,44 @@ +doctype 5 + +import mixins + +html + head + block meta + meta[name="description"][content="This is a great website"] + meta[charset="UTF-8"] + meta[name="viewport"][content="width=device-width, initial-scale=1.0, maximum-scale=2.0, user-scalable=no"] + meta[name="theme-color"][content="#112233"] + meta[http-equiv="Content-Type"][content="text/html; charset=utf-8"] + + block css + link[rel="stylesheet"][href="/ui/css/base.css"] + link[rel="stylesheet"][href="/ui/css/magic.css"] + link[rel="stylesheet"][href="/ui/css/theme.css"] + link[rel="stylesheet"][media="screen"][href="/ui/fonts/source-sans-pro/source-sans-pro.css"][type="text/css"] + + block js + script[type="text/javascript"][src="/ui/js/background.js"] + + title #{ViewTitle} + body + if ViewBackground != "" + img[id="main-background"][src="/ui/img/bg.png"] + else + img[id="main-background"][src="/ui/img/bg.png"] + + div.content-wrapper + nav + block menu + if User.LoggedIn + ul + +menuitem("/user/logout", "U", "Logout") + else + ul + +menuitem("/user/login", "U", "Login") + + if ViewPath != "/" + +menuitem("/", "<", "Back") + + main + block main \ No newline at end of file diff --git a/view/amber/base/mixins.amber b/view/amber/base/mixins.amber new file mode 100644 index 0000000..b19512b --- /dev/null +++ b/view/amber/base/mixins.amber @@ -0,0 +1,30 @@ +$root = $ + +mixin menuitem($path, $icon, $text) + li + .selected ? $root.ViewPath == $path + a[href=$path] + div.mg-icon #{$icon} + div.mg-label #{$text} + +mixin tagmenuitem($tag) + li + [class=$tag.CSSClass] + a[href=$tag.Path] + div.mg-icon #{$tag.Icon} + div.mg-label #{$tag.Name} + + + +mixin formoption($name, $selected, $label, $info) + div.radio-wrapper + $id = printf("checkbox-%s", $name) + + if $selected + input[id=$id][type="checkbox"][name=$name][value="true"][checked] + else + input[id=$id][type="checkbox"][name=$name][value="true"] + + label[for="checkbox-unlisted"] + b #{$label}: + spamn #{$info} \ No newline at end of file diff --git a/view/amber/create.amber b/view/amber/create.amber new file mode 100644 index 0000000..66e38cb --- /dev/null +++ b/view/amber/create.amber @@ -0,0 +1,63 @@ +extends base/master + +block append css + link[rel="stylesheet"][href="/ui/css/form.css"] + +block append js + script[type="text/javascript"][src="/ui/js/form-page.js"] + +block menu + import menu + +block main + $page = $.Page + + article + h1 Create + form[action="/page/create"][method="POST"] + p.danger #{$.Error} + + input.big + [placeholder="Page Name"] + [value=$page.Name] + [type="text"] + [name="name"] + textarea.tall + [placeholder="Content"] + [name="source"] + | #{$page.Source} + input + [placeholder="IC Date (e.g. 'Oct 27, 2185')"] + [name="fictionalDate"] + [type="text"] + [value=$page.FictionalDate] ? !$page.FictionalDate.IsZero + + h2 Category + div.group[title="Category"] + each $category in $.Categories + div.radio-wrapper + $radioid = printf("radio-%s", $category.Key) + if $category.Key == $page.Category + input[id=$radioid][name="category"][type="radio"][name="category"][value=$category.Key][checked] + else + input[id=$radioid][name="category"][type="radio"][name="category"][value=$category.Key] + label[for=$radioid] + b #{$category.Key}: + span #{$category.Info} + + h2 Tags + textarea + [name="tags"] + [placeholder="Location: Miner's Respite\nOrganization: Redrock Agency\nCharacter: Renala T'Iavay\nEvent: Skipping Work\nSeries: Just Another Tuesday"] + | #{$.TagInput} + + h2 Options + div.group + +formoption("unlisted", $page.Unlisted, "Unlisted", "The page will not show up in any page list. Meant for omni-tool messages, quick copypastes and so on.") + +formoption("indexed", $page.Unlisted, "Indexable", "Third-party search engines (that play by the rules) are permitted to crawl this page.") + + // Future options (maybe) + input[type="hidden"][name="type"][value="Markdown"] + input[type="hidden"][name="published"][value="True"] + + button[type="submit"] Create diff --git a/view/amber/index.amber b/view/amber/index.amber new file mode 100644 index 0000000..1db3afc --- /dev/null +++ b/view/amber/index.amber @@ -0,0 +1,25 @@ +extends base/master + +block menu + import menu + +block main + article + table.page-list + tbody + each $header in Headers + tr + td.pl-icon #{$header.CategoryInfo.Icon} + td.pl-content + div.plc-title + a[href=$header.Path] #{$header.Name} + div.plc-meta + div.plcm-date #{formatDate($header.PublishDate)} + if $header.Dated + div.plcm-date #{formatDate($header.FictionalDate)} + if $header.PrimaryTag + div.plcm-tag[class=$header.PrimaryTag.CSSCLass] #{$header.PrimaryTag.Name} + div.plcm-author #{formatUserID($header.Author)} + tr.spacer + td + diff --git a/view/amber/menu.amber b/view/amber/menu.amber new file mode 100644 index 0000000..85b25db --- /dev/null +++ b/view/amber/menu.amber @@ -0,0 +1,25 @@ +import base/mixins + +$authorPath = printf("/author/%s", ActiveAuthor) + +a[href="/"] + h1 Aite RP + +ul + if User.LoggedIn + +menuitem("/page/create", "+", "Create") + +ul + each $category in Categories + +menuitem($category.Path, $category.Icon, $category.Plural) + +ul + if ActiveTag.ID + +menuitem(ActiveTag.Path, ActiveTag.Icon, ActiveTag.Name) + if ActiveDate + +menuitem(ActiveDatePath, "D", ActiveDate) + if ActiveAuthor + +menuitem($authorPath, "A", formatUserID(ActiveAuthor)) + +ul + +menuitem("/tags/", "T", "Tags") \ No newline at end of file diff --git a/view/amber/message.amber b/view/amber/message.amber new file mode 100644 index 0000000..12e7307 --- /dev/null +++ b/view/amber/message.amber @@ -0,0 +1,12 @@ +extends base/master + +block menu + a[href="/"] + h1 Aite RP + +block main + article + h1 + .danger ? $.Danger + | #{$.Title} + p #{$.Text} \ No newline at end of file diff --git a/view/amber/page/delete.amber b/view/amber/page/delete.amber new file mode 100644 index 0000000..bfdb052 --- /dev/null +++ b/view/amber/page/delete.amber @@ -0,0 +1,30 @@ +extends ../base/master + +block append css + link[rel="stylesheet"][href="/ui/css/form.css"] + +block append js + script[type="text/javascript"][src="/ui/js/form-page.js"] + +block menu + import menu + +block main + $page = $.Page + + article + h1.danger Delete + form[action=$page.OpPath("delete")][method="POST"] + p.danger This is irreversible, so make sure you are deleting the right page. + + ul + li ID: #{$.Page.ID} + li Name: #{$.Page.Name} + li Category: #{$.Page.Category} + li Published: #{formatDateLong($.Page.PublishDate)} + ul + + input[type="hidden"][name="aft"][value=$.User.SessionID] + + + button[type="submit"] Delete diff --git a/view/amber/page/edit.amber b/view/amber/page/edit.amber new file mode 100644 index 0000000..ca5837b --- /dev/null +++ b/view/amber/page/edit.amber @@ -0,0 +1,64 @@ +extends ../base/master + + +block append css + link[rel="stylesheet"][href="/ui/css/form.css"] + +block append js + script[type="text/javascript"][src="/ui/js/form-page.js"] + +block menu + import menu + +block main + $page = $.Page + + article + h1 Create + form[action=$page.OpPath("edit")][method="POST"] + p.danger #{$.Error} + + input.big + [placeholder="Page Name"] + [value=$page.Name] + [type="text"] + [name="name"] + textarea.tall + [placeholder="Content"] + [name="source"] + | #{$page.Source} + input + [placeholder="IC Date (e.g. 'Oct 27, 2185')"] + [name="fictionalDate"] + [type="text"] + [value=$page.FictionalDate] ? !$page.FictionalDate.IsZero + + h2 Category + div.group[title="Category"] + each $category in $.Categories + div.radio-wrapper + $radioid = printf("radio-%s", $category.Key) + if $category.Key == $page.Category + input[id=$radioid][name="category"][type="radio"][name="category"][value=$category.Key][checked] + else + input[id=$radioid][name="category"][type="radio"][name="category"][value=$category.Key] + label[for=$radioid] + b #{$category.Key}: + span #{$category.Info} + + h2 Tags + textarea + [name="tags"] + [placeholder="Location: Miner's Respite\nOrganization: Redrock Agency\nCharacter: Renala T'Iavay\nEvent: Skipping Work\nSeries: Just Another Tuesday"] + | #{$.TagInput} + + h2 Options + div.group + +formoption("unlisted", $page.Unlisted, "Unlisted", "The page will not show up in any page list. Meant for omni-tool messages, quick copypastes and so on.") + +formoption("indexed", $page.Indexed, "Indexable", "Third-party search engines (that play by the rules) are permitted to crawl this page.") + + // Future options (maybe) + input[type="hidden"][name="type"][value="Markdown"] + input[type="hidden"][name="published"][value="True"] + + button[type="submit"] Edit diff --git a/view/amber/page/menu.amber b/view/amber/page/menu.amber new file mode 100644 index 0000000..2efde11 --- /dev/null +++ b/view/amber/page/menu.amber @@ -0,0 +1,29 @@ +import ../base/mixins + +$pagePath = printf("/page/%s", $.Page.ID) +$pageEditPath = printf("/page/edit/%s", $.Page.ID) +$pageDeletePath = printf("/page/delete/%s", $.Page.ID) +$pageSourcePath = printf("/page/source/%s", $.Page.ID) +$datePath = printf("/month/%s", $.Page.FictionalDate.UTC.Format("2006-01")) +$authorPath = printf("/author/%s", $.Page.Author) + +a[href="/"] + h1 Page + +if !$.HideMenu + ul + +menuitem($pagePath, "V", "View") + if $.Page.Author == $.User.ID + +menuitem($pageEditPath, "E", "Edit") + +menuitem($pageDeletePath, "X", "Delete") + else + +menuitem($pageSourcePath, "#", "Source") + + ul + each $tag in $.Page.Tags + +tagmenuitem($tag) + + ul + if $.Page.Dated + +menuitem($datePath, "D", formatDate($.Page.FictionalDate)) + +menuitem($authorPath, "A", formatUserID($.Page.Author)) diff --git a/view/amber/page/source.amber b/view/amber/page/source.amber new file mode 100644 index 0000000..058d78f --- /dev/null +++ b/view/amber/page/source.amber @@ -0,0 +1,14 @@ +extends ../base/master + +block append meta + if !$.Page.Indexed + meta[name="robots"][content="noindex"] + meta[name="googlebot"][content="noindex"] + +block menu + import menu + +block main + article + code.block + #{$.Page.Source} \ No newline at end of file diff --git a/view/amber/page/view.amber b/view/amber/page/view.amber new file mode 100644 index 0000000..e45dee9 --- /dev/null +++ b/view/amber/page/view.amber @@ -0,0 +1,13 @@ +extends ../base/master + +block append meta + if !$.Page.Indexed + meta[name="robots"][content="noindex"] + meta[name="googlebot"][content="noindex"] + +block menu + import menu + +block main + article.narrow + #{$.Page.Content} \ No newline at end of file diff --git a/view/amber/tags.amber b/view/amber/tags.amber new file mode 100644 index 0000000..aa13821 --- /dev/null +++ b/view/amber/tags.amber @@ -0,0 +1,17 @@ +extends base/master + +block menu + import menu + +block main + article + h1 All Tags + div.tag-list + each $key, $tags in $.Map + if $tags + div.tl-item + h2 #{$key} + ul + each $tag in $tags + li + a[class=$tag.CSSClass][href=$tag.Path] #{$tag.Name} \ No newline at end of file diff --git a/view/amber/user/login.amber b/view/amber/user/login.amber new file mode 100644 index 0000000..1f9dd1b --- /dev/null +++ b/view/amber/user/login.amber @@ -0,0 +1,26 @@ +extends ../base/master + +block append css + link[rel="stylesheet"][href="/ui/css/form.css"] + +block menu + import menu + +block main + article + h1 Login + form[action="/user/login"][method="POST"] + p Use your wiki.aiterp.net account. + p.danger #{$.Error} + input + [placeholder="Username"] + [name="username"] + [type="text"] + [value=$.UserName] + [autofocus] ? $.UserName == "" + input + [placeholder="Password"] + [name="password"] + [type="password"] + [autofocus] ? $.UserName != "" + button[type="submit"] Login \ No newline at end of file diff --git a/view/amber/user/menu.amber b/view/amber/user/menu.amber new file mode 100644 index 0000000..c596f06 --- /dev/null +++ b/view/amber/user/menu.amber @@ -0,0 +1,4 @@ +import ../base/mixins + +a[href="/"] + h1 User \ No newline at end of file diff --git a/view/renderer.go b/view/renderer.go index a0f14cd..bf56b9a 100644 --- a/view/renderer.go +++ b/view/renderer.go @@ -2,12 +2,14 @@ package view import ( "errors" + "fmt" "html/template" "io" "log" "net/http" "os" - "path" + + "github.com/eknkc/amber" "git.aiterp.net/AiteRP/aitestory/server" "git.aiterp.net/gisle/wrouter/response" @@ -17,52 +19,33 @@ var wd, _ = os.Getwd() var cache = make(map[string]*template.Template) var argsCache = make(map[string][]string) -// Register registers a template and compiles it for rendering. This should be done -// in the beginning since an error will terminate the server -func Register(name string, base string, fragments ...string) { - rootPath := server.Main.Config.Directories.Templates - - for i, fragment := range fragments { - fragments[i] = path.Join(rootPath, fragment+".tmpl") - } - - args := append([]string{path.Join(rootPath, name+".tmpl"), path.Join(rootPath, base+".tmpl")}, fragments...) - tmpl, err := template.New(name).Funcs(funcMap).ParseFiles(args...) - if err != nil { - log.Fatalf("Failed to register %s: %s", name, err) - } - - argsCache[name] = args - cache[name] = tmpl -} +var defaultOptions = amber.Options{PrettyPrint: true} +var defaultDirOptions = amber.DirOptions{Ext: ".amber", Recursive: true} // Render renders a template with the name it was registered with, and with the // view model. The view model is expected to be the correct model from the viewmodel // package func Render(w http.ResponseWriter, name string, status int, viewModel interface{}) { - var tmpl *template.Template var err error if server.Main.Config.Server.Debug { - tmpl, err = template.New(name).Funcs(funcMap).ParseFiles(argsCache[name]...) + cache, err = amber.CompileDir(server.Main.Config.Directories.Templates, defaultDirOptions, defaultOptions) if err != nil { - response.Text(w, 500, "Failed to run template "+name+": "+err.Error()) - return + log.Println(err) } - } else { - var ok bool + } - tmpl, ok = cache[name] - if !ok { - response.Text(w, 500, "Template not found: "+name) - return - } + tmpl, ok := cache[name] + if !ok { + response.Text(w, 500, "Template not found: "+name) + return } w.WriteHeader(status) - err = tmpl.ExecuteTemplate(w, "base", viewModel) + err = tmpl.Execute(w, viewModel) if err != nil { log.Println("Template error:", err.Error()) + response.Text(w, 500, "Template error: "+err.Error()) } } @@ -77,11 +60,31 @@ func Run(w io.Writer, name string, viewModel interface{}) error { } func init() { - Register("index", "base/default") - Register("user/login", "base/default") - Register("page/create", "base/default") - Register("page/edit", "base/default") - Register("page/view", "base/default") - Register("page/delete", "base/default") - Register("tags/list", "base/default") + var err error + + for key, value := range funcMap { + if _, exists := amber.FuncMap[key]; exists { + log.Printf("Amber already provides a function by the name: %s", key) + } + + amber.FuncMap[key] = value + } + + cache, err = amber.CompileDir(server.Main.Config.Directories.Templates, defaultDirOptions, defaultOptions) + if err != nil { + if server.Main.Config.Server.Debug { + log.Println(err) + return + } + + log.Fatalln(err) + } + + if server.Main.Config.Server.Debug { + fmt.Printf("Template directory: %s\n", server.Main.Config.Directories.Templates) + + for key := range cache { + fmt.Printf("Template loaded: %s\n", key) + } + } } diff --git a/view/templates/base/default.tmpl b/view/templates/base/default.tmpl deleted file mode 100644 index 10027cb..0000000 --- a/view/templates/base/default.tmpl +++ /dev/null @@ -1,41 +0,0 @@ -{{define "base"}} - - - - - - {{ .ViewTitle }} - - - - - - - - - - - - - - - - - {{ block "head" . }}{{end}} - - - - - -
-
- {{ block "content" . }}{{end}} -
- - -
- - -{{end}} \ No newline at end of file diff --git a/view/templates/index.tmpl b/view/templates/index.tmpl deleted file mode 100644 index bc544b4..0000000 --- a/view/templates/index.tmpl +++ /dev/null @@ -1,66 +0,0 @@ -{{ define "content" }} -
- - {{ range .Headers }} - - - - - - - {{ end }} -
{{.CategoryInfo.Icon}} - -
-
{{.PublishDate | formatDate}}
- {{ if .Dated }} -
{{.FictionalDate | formatDate}}
- {{ end }} - {{ if .PrimaryTag }} -
{{.PrimaryTag.Name}}
- {{ end }} -
{{.Author | formatUserID}}
-
-
-
-{{ end }} - -{{ define "menu" }} -

Aite RP

- - - - {{ if $.ActiveTag.ID }} - - {{ end }} - - - - {{ if $.User.LoggedIn }} - - - - {{ else }} - - {{ end }} - - -{{ end }} - -{{ define "head" }} - -{{ end }} \ No newline at end of file diff --git a/view/templates/page/create.tmpl b/view/templates/page/create.tmpl deleted file mode 100644 index 02dc8da..0000000 --- a/view/templates/page/create.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -{{ define "content" }} -
-

{{$.Operation}}

-
-

{{$.Error}}

- - - -
- {{ range $.Categories }} -
- {{.Key}}: {{.Info}} -
- {{ end }} -
- -
-
- Unlisted: This page will not show up on page lists, but anyone with a link can view it. -
-
- - - - - - - -
-
-{{ end }} - -{{ define "menu" }} -

Aite RP

- - -{{ end }} - -{{ define "head" }} - - -{{ end }} \ No newline at end of file diff --git a/view/templates/page/delete.tmpl b/view/templates/page/delete.tmpl deleted file mode 100644 index e28e8d2..0000000 --- a/view/templates/page/delete.tmpl +++ /dev/null @@ -1,37 +0,0 @@ -{{ define "content" }} -
-

Delete Page

-
-

- This is an irreversible action, so make sure that this is the correct page! -

-
    -
  • ID: {{$.Page.ID}}
  • -
  • Name: {{$.Page.Name}}
  • -
  • Category: {{$.Page.Category}}
  • -
  • Published: {{$.Page.PublishDate | formatDateLong}}
  • -
- - - -
-{{ end }} - -{{ define "menu" }} -

Page

- - {{ if eq $.User.ID $.Page.Author}} - - {{ end }} - - -{{ end }} - -{{ define "head" }} - -{{ end }} \ No newline at end of file diff --git a/view/templates/page/edit.tmpl b/view/templates/page/edit.tmpl deleted file mode 100644 index 3c9bebe..0000000 --- a/view/templates/page/edit.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -{{ define "content" }} -
-

{{$.Operation}}

- -

{{$.Error}}

- - - -
- {{ range $.Categories }} -
- {{.Key}}: {{.Info}} -
- {{ end }} -
- -
-
- Unlisted: This page will not show up on page lists, but anyone with a link can view it. -
-
- - - - - - - - -
-{{ end }} - -{{ define "menu" }} -

Aite RP

- - -{{ end }} - -{{ define "head" }} - - -{{ end }} \ No newline at end of file diff --git a/view/templates/page/view.tmpl b/view/templates/page/view.tmpl deleted file mode 100644 index e43b0b6..0000000 --- a/view/templates/page/view.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -{{ define "content" }} -
- {{$.Page.Content}} -
-{{ end }} - -{{ define "menu" }} -

Page

- {{ if $.Page.Dated}} -
{{$.Page.FictionalDate | formatDate}}
- {{ else }} -
{{$.Page.PublishDate | formatDate}}
- {{ end }} - - - - {{ if eq $.User.ID $.Page.Author}} - - {{ end }} - - - - {{ if $.User.LoggedIn }} - - {{ else }} - - {{ end }} -{{ end }} - -{{ define "head" }} -{{ end }} \ No newline at end of file diff --git a/view/templates/tags/list.tmpl b/view/templates/tags/list.tmpl deleted file mode 100644 index 4a2ba07..0000000 --- a/view/templates/tags/list.tmpl +++ /dev/null @@ -1,48 +0,0 @@ -{{ define "content" }} -
-

All Tags

- -
- {{ range $tagHeader, $tags := .Map }} -
-

{{ $tagHeader }}

- -
- {{ end }} -
- - -
-{{ end }} - -{{ define "menu" }} -

Tags

- - - - {{ if $.User.LoggedIn }} - - - - {{ else }} - - {{ end }} - - -{{ end }} - -{{ define "head" }} - -{{ end }} \ No newline at end of file diff --git a/view/templates/user/login.tmpl b/view/templates/user/login.tmpl deleted file mode 100644 index 37b7aaa..0000000 --- a/view/templates/user/login.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -{{ define "content" }} -
-

Login

-
-

Use your wiki.aiterp.net account.

-

{{$.Error}}

- - - -
-
-{{ end }} - -{{ define "menu" }} -

Aite RP

- - -{{ end }} - -{{ define "head" }} - -{{ end }} \ No newline at end of file diff --git a/viewmodel/base.go b/viewmodel/base.go index 3e9ff94..ae10e45 100644 --- a/viewmodel/base.go +++ b/viewmodel/base.go @@ -16,11 +16,14 @@ type Base struct { LoggedIn bool SessionID string } - ViewTitle string + ViewBackground string + ViewPath string + ViewTitle string + Message string } // InitBase initializes the base of the viewmodel -func (base *Base) setupBase(user *auth.User, viewTitle string) { +func (base *Base) setupBase(user *auth.User, viewTitle string, viewPath string) { if user != nil { base.User.ID = user.FullID() base.User.Name = user.ID @@ -29,5 +32,7 @@ func (base *Base) setupBase(user *auth.User, viewTitle string) { base.User.SessionID = user.Session.ID } + base.ViewPath = viewPath + base.ViewTitle = fmt.Sprintf("%s - %s", viewTitle, server.Main.Config.View.Title) } diff --git a/viewmodel/indexbase.go b/viewmodel/indexbase.go new file mode 100644 index 0000000..21aa92b --- /dev/null +++ b/viewmodel/indexbase.go @@ -0,0 +1,16 @@ +package viewmodel + +import "git.aiterp.net/AiteRP/aitestory/model" + +// IndexBase is used for the index menu. +type IndexBase struct { + Categories []model.PageCategory + ActiveTag model.Tag + ActiveAuthor string + ActiveDate string + ActiveDatePath string +} + +func (im *IndexBase) setupMenu() { + im.Categories = model.PageCategories +} diff --git a/viewmodel/pagelist.go b/viewmodel/indexlist.go similarity index 58% rename from viewmodel/pagelist.go rename to viewmodel/indexlist.go index a622d2b..d18a1eb 100644 --- a/viewmodel/pagelist.go +++ b/viewmodel/indexlist.go @@ -5,18 +5,18 @@ import ( "git.aiterp.net/gisle/wrouter/auth" ) -// PageList is a view model for rendering the front page -type PageList struct { +// IndexList is a view model for rendering the front page +type IndexList struct { Base + IndexBase Headers []model.Header ActiveCategory model.PageCategory - Categories []model.PageCategory - ActiveTag model.Tag FavoriteTags []model.Tag } // Setup sets up the page model and the base, and should // be run after the details have been filled in. -func (pl *PageList) Setup(user *auth.User) { - pl.setupBase(user, "Index") +func (il *IndexList) Setup(user *auth.User, viewPath string, viewTitle string) { + il.setupMenu() + il.setupBase(user, viewTitle, viewPath) } diff --git a/viewmodel/indextags.go b/viewmodel/indextags.go new file mode 100644 index 0000000..e65851d --- /dev/null +++ b/viewmodel/indextags.go @@ -0,0 +1,49 @@ +package viewmodel + +import ( + "fmt" + + "git.aiterp.net/AiteRP/aitestory/model" + "git.aiterp.net/gisle/wrouter/auth" +) + +// IndexTags is a view model for rendering the tag lists. +type IndexTags struct { + Base + IndexBase + + Type string + Tags []model.Tag + TagCategories []string + + tagMap map[string][]model.Tag +} + +// Map lazy-loads the map of tags, which is only +// used when listing all of them. +func (it IndexTags) Map() map[string][]model.Tag { + // Set up the map + it.tagMap = make(map[string][]model.Tag, len(model.TagTypes)) + for _, tagType := range model.TagTypes { + it.tagMap[tagType] = make([]model.Tag, 0, 64) + } + + // Organize + for _, tag := range it.Tags { + it.tagMap[tag.Type] = append(it.tagMap[tag.Type], tag) + } + + return it.tagMap +} + +// Setup sets up the page model and the base, and should +// be run after the details have been filled in. +func (it *IndexTags) Setup(user *auth.User, viewPath string) { + title := it.Type + if it.Type != "" { + title = "All" + } + + it.setupMenu() + it.setupBase(user, fmt.Sprintf("%s Tags", title), viewPath) +} diff --git a/viewmodel/message.go b/viewmodel/message.go new file mode 100644 index 0000000..368ff2e --- /dev/null +++ b/viewmodel/message.go @@ -0,0 +1,19 @@ +package viewmodel + +import "git.aiterp.net/gisle/wrouter/auth" + +// Message is a simple viewmodel for the message pages +type Message struct { + Base + Title string + Text string + Danger bool +} + +// NewMessage creates a message model +func NewMessage(user *auth.User, title string, danger bool, text string) *Message { + msg := &Message{Title: title, Text: text, Danger: danger} + msg.setupBase(user, title, "") + + return msg +} diff --git a/viewmodel/pagebase.go b/viewmodel/pagebase.go new file mode 100644 index 0000000..3c5d37c --- /dev/null +++ b/viewmodel/pagebase.go @@ -0,0 +1,9 @@ +package viewmodel + +import "git.aiterp.net/AiteRP/aitestory/model" + +// PageBase describes the basic parts of a page. +type PageBase struct { + Page *model.Page + HideMenu bool +} diff --git a/viewmodel/pagecreate.go b/viewmodel/pagecreate.go new file mode 100644 index 0000000..e435bcf --- /dev/null +++ b/viewmodel/pagecreate.go @@ -0,0 +1,26 @@ +package viewmodel + +import ( + "git.aiterp.net/AiteRP/aitestory/model" + "git.aiterp.net/gisle/wrouter/auth" +) + +// PageCreate is the view model for the page create form. +type PageCreate struct { + Base + IndexBase + + Page model.Page + Error string + TagInput string +} + +// Setup sets the view model up. +func (pf *PageCreate) Setup(user *auth.User) { + pf.setupMenu() + pf.setupBase(user, "Create", "/page/create") + + pf.Categories = model.PageCategories + + pf.Page.Defaults() +} diff --git a/viewmodel/pagedelete.go b/viewmodel/pagedelete.go new file mode 100644 index 0000000..625687d --- /dev/null +++ b/viewmodel/pagedelete.go @@ -0,0 +1,19 @@ +package viewmodel + +import ( + "fmt" + + "git.aiterp.net/gisle/wrouter/auth" +) + +// PageDelete is the view model used to view the page's delete form. +type PageDelete struct { + Base + PageBase +} + +// Setup sets up the page view model. It should be calles last +func (pv *PageDelete) Setup(user *auth.User) { + pv.HideMenu = false + pv.setupBase(user, pv.Page.Name, fmt.Sprintf("/page/delete/%s", pv.Page.ID)) +} diff --git a/viewmodel/pageform.go b/viewmodel/pageedit.go similarity index 50% rename from viewmodel/pageform.go rename to viewmodel/pageedit.go index c84ac79..7022a00 100644 --- a/viewmodel/pageform.go +++ b/viewmodel/pageedit.go @@ -5,17 +5,19 @@ import ( "git.aiterp.net/gisle/wrouter/auth" ) -type PageForm struct { +// PageEdit is the view model for the page edit form. +type PageEdit struct { Base - Page model.Page + PageBase + Categories []model.PageCategory - Operation string Error string TagInput string } -func (pf *PageForm) Setup(user *auth.User) { - pf.setupBase(user, "Create") +// Setup sets the view model up. +func (pf *PageEdit) Setup(user *auth.User) { + pf.setupBase(user, "Edit", "/page/edit/"+pf.Page.ID) + pf.Categories = model.PageCategories - pf.Page.Defaults() } diff --git a/viewmodel/pagesource.go b/viewmodel/pagesource.go new file mode 100644 index 0000000..9ba7a8d --- /dev/null +++ b/viewmodel/pagesource.go @@ -0,0 +1,20 @@ +package viewmodel + +import ( + "fmt" + + "git.aiterp.net/gisle/wrouter/auth" +) + +// PageSource is the view model used to view the page's source. +// It's got no extra data on its own. +type PageSource struct { + Base + PageBase +} + +// Setup sets up the page view model. It should be calles last +func (pv *PageSource) Setup(user *auth.User) { + pv.HideMenu = false + pv.setupBase(user, pv.Page.Name, fmt.Sprintf("/page/source/%s", pv.Page.ID)) +} diff --git a/viewmodel/pageview.go b/viewmodel/pageview.go index e3f28e1..0fc4ea3 100644 --- a/viewmodel/pageview.go +++ b/viewmodel/pageview.go @@ -1,15 +1,20 @@ package viewmodel import ( - "git.aiterp.net/AiteRP/aitestory/model" + "fmt" + "git.aiterp.net/gisle/wrouter/auth" ) +// PageView is the view model used to view the page. It's got no extra +// data on its own. type PageView struct { Base - Page *model.Page + PageBase } +// Setup sets up the page view model. It should be calles last func (pv *PageView) Setup(user *auth.User) { - pv.setupBase(user, pv.Page.Name) + pv.HideMenu = false + pv.setupBase(user, pv.Page.Name, fmt.Sprintf("/page/%s", pv.Page.ID)) } diff --git a/viewmodel/taglist.go b/viewmodel/taglist.go deleted file mode 100644 index c02a515..0000000 --- a/viewmodel/taglist.go +++ /dev/null @@ -1,46 +0,0 @@ -package viewmodel - -import ( - "fmt" - - "git.aiterp.net/AiteRP/aitestory/model" - "git.aiterp.net/gisle/wrouter/auth" -) - -// TagList is a view model for rendering the tag lists. -type TagList struct { - Base - Type string - Tags []model.Tag - TagCategories []string - - tagMap map[string][]model.Tag -} - -// Map lazy-loads the map of tags, which is only -// used when listing all of them. -func (tl TagList) Map() map[string][]model.Tag { - // Set up the map - tl.tagMap = make(map[string][]model.Tag, len(model.TagTypes)) - for _, tagType := range model.TagTypes { - tl.tagMap[tagType] = make([]model.Tag, 0, 64) - } - - // Organize - for _, tag := range tl.Tags { - tl.tagMap[tag.Type] = append(tl.tagMap[tag.Type], tag) - } - - return tl.tagMap -} - -// Setup sets up the page model and the base, and should -// be run after the details have been filled in. -func (tl *TagList) Setup(user *auth.User) { - title := tl.Type - if tl.Type != "" { - title = "All" - } - - tl.setupBase(user, fmt.Sprintf("%s Tags", title)) -} diff --git a/viewmodel/userlogin.go b/viewmodel/userlogin.go index 9701847..32eeb03 100644 --- a/viewmodel/userlogin.go +++ b/viewmodel/userlogin.go @@ -15,5 +15,5 @@ type UserLogin struct { // Setup sets up the page model and the base, and should // be run after the details have been filled in. func (ul *UserLogin) Setup(user *auth.User) { - ul.setupBase(user, "Login") + ul.setupBase(user, "Login", "/user/login") }