Create Page is done
This commit is contained in:
@@ -78,7 +78,6 @@ func listFiltered(category model.PageCategory) wrouter.FunctionHandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// story.aiterp.net/Ruins_of_Rakhana
|
||||
func init() {
|
||||
ListController.Function("/", listIndex)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
+8
-7
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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"><</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 }}
|
||||
@@ -3,8 +3,8 @@
|
||||
<h1>Login</h1>
|
||||
<form action="/user/login", method="POST">
|
||||
<p class="red">{{$.Error}}</p>
|
||||
<input placeholder="Username" name="username" type="text" />
|
||||
<input placeholder="Password" name="password" type="password" />
|
||||
<input placeholder="Username" name="username" type="text" value="{{$.UserName}}" {{if ne $.UserName ""}}autofocus{{end}} />
|
||||
<input placeholder="Password" name="password" type="password" {{if $.UserName}}autofocus{{end}} />
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</article>
|
||||
@@ -12,10 +12,12 @@
|
||||
|
||||
{{ define "menu" }}
|
||||
<a href="/"><h1>Aite RP</h1></a>
|
||||
|
||||
<li><a href="/"><div class="mg-icon"><</div><div class="mg-label">Back</div></a></li>
|
||||
|
||||
<ul>
|
||||
<li><a href="/"><div class="mg-icon"><</div><div class="mg-label">Back</div></a></li>
|
||||
</ul>
|
||||
{{ end }}
|
||||
|
||||
{{ define "head" }}
|
||||
|
||||
<link rel="stylesheet" href="/ui/css/form.css" />
|
||||
{{ end }}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user