Create Page is done

This commit is contained in:
2017-10-07 16:37:14 +02:00
parent 98c813bff5
commit d15e4e967e
10 changed files with 227 additions and 14 deletions
-1
View File
@@ -78,7 +78,6 @@ func listFiltered(category model.PageCategory) wrouter.FunctionHandlerFunc {
} }
} }
// story.aiterp.net/Ruins_of_Rakhana
func init() { func init() {
ListController.Function("/", listIndex) ListController.Function("/", listIndex)
+120
View File
@@ -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)
}
+4 -1
View File
@@ -3,6 +3,7 @@ package formparser
import ( import (
"errors" "errors"
"fmt" "fmt"
"log"
"time" "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 { func Select(value string, target *string, allowedValues []string, optional bool) error {
if value == "" { if value == "" {
if !optional { if !optional {
return errors.New("not a valid option") return errors.New("no option selected")
} }
return nil return nil
} }
log.Println(value, allowedValues)
for _, allowedValue := range allowedValues { for _, allowedValue := range allowedValues {
if value == allowedValue { if value == allowedValue {
*target = value *target = value
+1
View File
@@ -9,6 +9,7 @@ func main() {
auth.Register(&controllers.WikiAthenticator{}) auth.Register(&controllers.WikiAthenticator{})
router.Mount("/user", &controllers.UserController) router.Mount("/user", &controllers.UserController)
router.Mount("/page", &controllers.PageController)
router.Mount("/", &controllers.ListController) router.Mount("/", &controllers.ListController)
router.Static("/ui/", server.Main.Config.Directories.UI) router.Static("/ui/", server.Main.Config.Directories.UI)
+8 -7
View File
@@ -9,6 +9,7 @@ type PageCategory struct {
Key string Key string
Plural string Plural string
Icon string Icon string
Info string
} }
// URLRoot is the "folder" used for searching within the category // 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 // 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 // or the database, but for now I think this list is pretty fixed
var PageCategories = []PageCategory{ var PageCategories = []PageCategory{
{"OoC", "OoC", "ooc"}, {"OoC", "OoC", "ooc", "OoC content is for announcements, scheduling, general information, or anything that is not in-universe"},
{"Info", "Info", "info"}, {"Info", "Info", "info", "Information gained during and between RP sessions"},
{"News", "News", "news"}, {"News", "News", "news", "News stories that might be pertinent to ongoing plots"},
{"Item", "Items", "item"}, {"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"}, {"Document", "Documents", "document", "Data files, shadow broker dossiers, and other data that is not inside an item"},
{"Background", "Background", "background"}, {"Background", "Background", "background", "Rumors, suspicious persons, or inter-RP occurences that may be noticed"},
{"Story", "Stories", "story"}, {"Story", "Stories", "story", "Background stories and inter-RP character intearactions"},
} }
var pageCategories []string var pageCategories []string
+1
View File
@@ -79,4 +79,5 @@ func Run(w io.Writer, name string, viewModel interface{}) error {
func init() { func init() {
Register("index", "base/default") Register("index", "base/default")
Register("login", "base/default") Register("login", "base/default")
Register("create", "base/default")
} }
+50
View File
@@ -0,0 +1,50 @@
{{ define "content" }}
<article>
<h1>Create</h1>
<form action="/page/create", method="POST">
<p class="red">{{$.Error}}</p>
<input class="big" placeholder="Page Name" name="name" type="text" value="{{$.Page.Name}}" autofocus />
<textarea class="tall" placeholder="Content" name="source">{{$.Page.Source}}</textarea>
<input placeholder="IC Date" name="fictionalDate" type="text" value="{{if $.Page.FictionalDate.IsZero}}{{else}}{{$.Page.FictionalDate}}{{end}}" autofocus />
<div class="group">
{{ range $.Categories }}
<div class="radio-wrapper">
<input name="category" type="radio" name="category" value="{{.Key}}" {{if eq $.Page.Category .Key}}checked{{end}}><b> {{.Key}}</b>: {{.Info}}</input>
</div>
{{ end }}
</div>
<textarea name="tags" placeholder="Tags, e.g. 'Location: Miner's Respite'. One per line, topmost is primary tag"></textarea>
<div class="group">
<div class="radio-wrapper">
<input type="checkbox" name="dated" value="true" {{if $.Page.Dated}}checked{{end}}><b> Dated</b>: The IC date is shown on the page list. It will still be used for sorting if this option is disabled.</input>
</div>
<div class="radio-wrapper">
<input type="checkbox" name="unlisted" value="true" {{if $.Page.Unlisted}}checked{{end}}><b> Unlisted</b>: This page will not show up on page lists, but anyone with a link can view it.</input>
</div>
<div class="radio-wrapper">
<input type="checkbox" name="specific" value="true" {{if $.Page.Specific}}checked{{end}}><b> Specific</b>: This page will only show up on page lists when one of its tags is searched for.</input>
</div>
</div>
<!-- Future option -->
<input type="hidden" name="type" value="Markdown" />
<input type="hidden" name="published" value="True" />
<button type="submit">Submit</button>
</form>
</article>
{{ end }}
{{ define "menu" }}
<a href="/"><h1>Aite RP</h1></a>
<ul>
<li><a href="/"><div class="mg-icon">&lt;</div><div class="mg-label">Back</div></a></li>
</ul>
{{ end }}
{{ define "head" }}
<link rel="stylesheet" href="/ui/css/form.css" />
<script type="text/javascript" src="/ui/js/form-page.js"></script>
{{ end }}
+7 -5
View File
@@ -3,8 +3,8 @@
<h1>Login</h1> <h1>Login</h1>
<form action="/user/login", method="POST"> <form action="/user/login", method="POST">
<p class="red">{{$.Error}}</p> <p class="red">{{$.Error}}</p>
<input placeholder="Username" name="username" type="text" /> <input placeholder="Username" name="username" type="text" value="{{$.UserName}}" {{if ne $.UserName ""}}autofocus{{end}} />
<input placeholder="Password" name="password" type="password" /> <input placeholder="Password" name="password" type="password" {{if $.UserName}}autofocus{{end}} />
<button type="submit">Submit</button> <button type="submit">Submit</button>
</form> </form>
</article> </article>
@@ -12,10 +12,12 @@
{{ define "menu" }} {{ define "menu" }}
<a href="/"><h1>Aite RP</h1></a> <a href="/"><h1>Aite RP</h1></a>
<li><a href="/"><div class="mg-icon">&lt;</div><div class="mg-label">Back</div></a></li> <ul>
<li><a href="/"><div class="mg-icon">&lt;</div><div class="mg-label">Back</div></a></li>
</ul>
{{ end }} {{ end }}
{{ define "head" }} {{ define "head" }}
<link rel="stylesheet" href="/ui/css/form.css" />
{{ end }} {{ end }}
+21
View File
@@ -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()
}
+15
View File
@@ -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)
}