Completed template system, added login/logout
This commit is contained in:
+1
-1
@@ -16,7 +16,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"wiki": {
|
"wiki": {
|
||||||
"url": "https://wiki.aiterp.net",
|
"url": "https://wiki.aiterp.net/w/api.php",
|
||||||
"username": "AiteBot@AiteStory",
|
"username": "AiteBot@AiteStory",
|
||||||
"password": ""
|
"password": ""
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.aiterp.net/gisle/wrouter/response"
|
||||||
|
|
||||||
|
"git.aiterp.net/AiteRP/aitestory/model"
|
||||||
"git.aiterp.net/AiteRP/aitestory/view"
|
"git.aiterp.net/AiteRP/aitestory/view"
|
||||||
"git.aiterp.net/AiteRP/aitestory/viewmodel"
|
"git.aiterp.net/AiteRP/aitestory/viewmodel"
|
||||||
|
|
||||||
@@ -15,18 +19,71 @@ import (
|
|||||||
var ListController = wrouter.Router{}
|
var ListController = wrouter.Router{}
|
||||||
|
|
||||||
func listIndex(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool {
|
func listIndex(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool {
|
||||||
if req.Method != "GET" || strings.LastIndex(req.URL.Path, "/") >= len(path) {
|
var err error
|
||||||
|
if req.Method != "GET" || len(req.URL.Path) > len(path) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
vm := viewmodel.PageList{}
|
vm := viewmodel.PageList{}
|
||||||
|
vm.Headers, err = model.ListHeaders()
|
||||||
|
vm.Categories = model.PageCategories
|
||||||
vm.Setup(user)
|
vm.Setup(user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
response.Text(w, 500, err.Error())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
view.Render(w, "index", 200, vm)
|
view.Render(w, "index", 200, vm)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listFiltered(category model.PageCategory) wrouter.FunctionHandlerFunc {
|
||||||
|
return func(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
|
||||||
|
}
|
||||||
|
|
||||||
|
tagName := strings.Replace(req.URL.Path[len(path):], "_", " ", -1)
|
||||||
|
|
||||||
|
vm := viewmodel.PageList{}
|
||||||
|
|
||||||
|
if tagName != "" {
|
||||||
|
tag, err := model.FindTag("name", tagName)
|
||||||
|
if err != nil {
|
||||||
|
response.Text(w, 404, err.Error())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.Headers, err = model.ListHeadersByTag(category.Key, tag)
|
||||||
|
} else {
|
||||||
|
vm.Headers, err = model.ListHeadersByCategory(category.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.Categories = model.PageCategories
|
||||||
|
vm.ActiveCategory = category
|
||||||
|
vm.Setup(user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
response.Text(w, 500, err.Error())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
view.Render(w, "index", 200, vm)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// story.aiterp.net/Ruins_of_Rakhana
|
||||||
func init() {
|
func init() {
|
||||||
ListController.Function("/", listIndex)
|
ListController.Function("/", listIndex)
|
||||||
|
|
||||||
|
ListController.Function("/", listFiltered(model.PageCategory{Key: ""}))
|
||||||
|
for _, category := range model.PageCategories {
|
||||||
|
ListController.Function(fmt.Sprintf("/%s/", strings.ToLower(category.Plural)), listFiltered(category))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.aiterp.net/AiteRP/aitestory/view"
|
||||||
|
"git.aiterp.net/AiteRP/aitestory/viewmodel"
|
||||||
|
"git.aiterp.net/gisle/wrouter"
|
||||||
|
"git.aiterp.net/gisle/wrouter/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserController serves and handles the login form
|
||||||
|
var UserController = wrouter.Router{}
|
||||||
|
|
||||||
|
func userLogin(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
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method == "GET" && user != nil {
|
||||||
|
http.Redirect(w, req, "/", 302)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
ul := viewmodel.UserLogin{}
|
||||||
|
|
||||||
|
if req.Method == "POST" {
|
||||||
|
req.ParseForm()
|
||||||
|
|
||||||
|
wa := WikiAthenticator{}
|
||||||
|
newUser, err := wa.Login(req.Form.Get("username"), req.Form.Get("password"))
|
||||||
|
if err == nil {
|
||||||
|
sess := auth.OpenSession(newUser)
|
||||||
|
|
||||||
|
http.SetCookie(w, &http.Cookie{Name: auth.SessionCookieName, Value: sess.ID, Expires: sess.Time.Add(auth.SessionMaxTime), Path: "/", HttpOnly: true})
|
||||||
|
http.Redirect(w, req, "/", 302)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ul.UserName = req.Form.Get("username")
|
||||||
|
ul.Error = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.Setup(user)
|
||||||
|
view.Render(w, "login", 200, ul)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func userLogout(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool {
|
||||||
|
//var err error
|
||||||
|
|
||||||
|
if user != nil {
|
||||||
|
auth.CloseSession(user.Session.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, req, "/", 302)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
UserController.Function("/login", userLogin)
|
||||||
|
UserController.Function("/logout", userLogout)
|
||||||
|
}
|
||||||
@@ -15,14 +15,17 @@ import (
|
|||||||
// to log in
|
// to log in
|
||||||
type WikiAthenticator struct{}
|
type WikiAthenticator struct{}
|
||||||
|
|
||||||
|
// ID is for a potential multi-login future
|
||||||
func (wikiAuth *WikiAthenticator) ID() string {
|
func (wikiAuth *WikiAthenticator) ID() string {
|
||||||
return "wiki"
|
return "wiki"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name is for a potential multi-login future
|
||||||
func (wikiAuth *WikiAthenticator) Name() string {
|
func (wikiAuth *WikiAthenticator) Name() string {
|
||||||
return "Wiki"
|
return "Wiki"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find finds a user that has logged in at least once
|
||||||
func (wikiAuth *WikiAthenticator) Find(username string) *auth.User {
|
func (wikiAuth *WikiAthenticator) Find(username string) *auth.User {
|
||||||
db := server.Main.DB
|
db := server.Main.DB
|
||||||
|
|
||||||
@@ -38,7 +41,7 @@ func (wikiAuth *WikiAthenticator) Find(username string) *auth.User {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
user := auth.NewUser(wikiAuth, "", "member", nil)
|
user := auth.NewUser(wikiAuth, "", "member", make(map[string]string, 4))
|
||||||
role := "member"
|
role := "member"
|
||||||
rows.Scan(&user.ID, &role)
|
rows.Scan(&user.ID, &role)
|
||||||
user.Data["role"] = role
|
user.Data["role"] = role
|
||||||
@@ -63,20 +66,19 @@ func (wikiAuth *WikiAthenticator) Login(username, password string) (*auth.User,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look up the user
|
// Look up the user
|
||||||
rows, err := db.Query("SELECT id,role FROM `user` WHERE id=?", client.BasicAuthUser)
|
rows, err := db.Query("SELECT id,role FROM `user` WHERE id=?", username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Login failed %v", err)
|
return nil, fmt.Errorf("Login failed %v", err)
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If none was found, just create a new record with the role of member
|
// If none was found, just create a new record with the role of member
|
||||||
if !rows.Next() {
|
if !rows.Next() {
|
||||||
_, err = db.Exec("INSERT INTO `user` (id, role) VALUES (?, 'member')", client.BasicAuthUser)
|
_, err = db.Exec("INSERT INTO `user` (id, role) VALUES (?, 'member')", username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Login failed %v", err)
|
return nil, fmt.Errorf("Login failed %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return auth.NewUser(wikiAuth, client.BasicAuthUser, "member", nil), nil
|
return auth.NewUser(wikiAuth, username, "member", nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user was found, read it in
|
// If the user was found, read it in
|
||||||
@@ -91,6 +93,7 @@ func (wikiAuth *WikiAthenticator) Login(username, password string) (*auth.User,
|
|||||||
return auth.NewUser(wikiAuth, userid, "member", nil), nil
|
return auth.NewUser(wikiAuth, userid, "member", nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register just tells the user that they can't.
|
||||||
func (wikiAuth *WikiAthenticator) Register(username, password string, data map[string]string) (*auth.User, error) {
|
func (wikiAuth *WikiAthenticator) Register(username, password string, data map[string]string) (*auth.User, error) {
|
||||||
return nil, errors.New("Registration not allowed")
|
return nil, errors.New("Registration not allowed")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ func main() {
|
|||||||
router := &server.Main.Router
|
router := &server.Main.Router
|
||||||
|
|
||||||
auth.Register(&controllers.WikiAthenticator{})
|
auth.Register(&controllers.WikiAthenticator{})
|
||||||
|
router.Mount("/user", &controllers.UserController)
|
||||||
router.Mount("/", &controllers.ListController)
|
router.Mount("/", &controllers.ListController)
|
||||||
|
|
||||||
router.Static("/ui/", server.Main.Config.Server.UI)
|
router.Static("/ui/", server.Main.Config.Server.UI)
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PageCategory represents a page category
|
||||||
|
type PageCategory struct {
|
||||||
|
Key string
|
||||||
|
Plural string
|
||||||
|
Icon string
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLRoot is the "folder" used for searching within the category
|
||||||
|
func (category *PageCategory) URLRoot() string {
|
||||||
|
return strings.ToLower(category.Plural)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageCategories are used by the view model and page to enforce
|
||||||
|
// 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"},
|
||||||
|
}
|
||||||
|
var pageCategories []string
|
||||||
|
|
||||||
|
// init setups pageCategories
|
||||||
|
func init() {
|
||||||
|
pageCategories := make([]string, len(PageCategories))
|
||||||
|
for i, category := range PageCategories {
|
||||||
|
pageCategories[i] = category.Key
|
||||||
|
}
|
||||||
|
}
|
||||||
+92
-2
@@ -56,9 +56,42 @@ func ListHeaders() ([]Header, error) {
|
|||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListHeadersByTag lists all headers that has the tag
|
// ListHeadersByCategory grabs all the pages in the given category
|
||||||
func ListHeadersByTag(tag *Tag) ([]Header, error) {
|
func ListHeadersByCategory(category string) ([]Header, error) {
|
||||||
const query = `
|
const query = `
|
||||||
|
SELECT page.id,page.name,author,category,fictional_date,publish_date,edit_date,dated,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.category = ?;
|
||||||
|
`
|
||||||
|
|
||||||
|
db := server.Main.DB
|
||||||
|
|
||||||
|
rows, err := db.Query(query, category)
|
||||||
|
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) {
|
||||||
|
const query1 = `
|
||||||
SELECT page.id,page.name,page.author,page.category,page.fictional_date,page.publish_date,page.edit_date,page.dated,tag.id,tag.type,tag.name
|
SELECT page.id,page.name,page.author,page.category,page.fictional_date,page.publish_date,page.edit_date,page.dated,tag.id,tag.type,tag.name
|
||||||
FROM page_tag
|
FROM page_tag
|
||||||
RIGHT JOIN page ON page.id = page_tag.page_id
|
RIGHT JOIN page ON page.id = page_tag.page_id
|
||||||
@@ -66,6 +99,14 @@ func ListHeadersByTag(tag *Tag) ([]Header, error) {
|
|||||||
LEFT JOIN (tag AS tag) ON (tag.id = pt2.tag_id)
|
LEFT JOIN (tag AS tag) ON (tag.id = pt2.tag_id)
|
||||||
WHERE page_tag.tag_id=?
|
WHERE page_tag.tag_id=?
|
||||||
`
|
`
|
||||||
|
const query2 = `
|
||||||
|
SELECT page.id,page.name,page.author,page.category,page.fictional_date,page.publish_date,page.edit_date,page.dated,tag.id,tag.type,tag.name
|
||||||
|
FROM page_tag
|
||||||
|
RIGHT JOIN page ON page.id = page_tag.page_id
|
||||||
|
LEFT JOIN (page_tag AS pt2) ON (page.id = pt2.page_id AND pt2.primary = true)
|
||||||
|
LEFT JOIN (tag AS tag) ON (tag.id = pt2.tag_id)
|
||||||
|
WHERE page_tag.tag_id=? AND page.category=?
|
||||||
|
`
|
||||||
|
|
||||||
if tag == nil {
|
if tag == nil {
|
||||||
return nil, errors.New("no tag")
|
return nil, errors.New("no tag")
|
||||||
@@ -73,6 +114,11 @@ func ListHeadersByTag(tag *Tag) ([]Header, error) {
|
|||||||
|
|
||||||
db := server.Main.DB
|
db := server.Main.DB
|
||||||
|
|
||||||
|
query := query1
|
||||||
|
if category != "" {
|
||||||
|
query = query2
|
||||||
|
}
|
||||||
|
|
||||||
rows, err := db.Query(query, tag.ID)
|
rows, err := db.Query(query, tag.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -93,6 +139,50 @@ func ListHeadersByTag(tag *Tag) ([]Header, error) {
|
|||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListHeadersByTags searches for the first tag, then filters the result based on the
|
||||||
|
// others. There is room for improvement in this function, but I'll have to judge whether
|
||||||
|
// there will be a need for that.
|
||||||
|
func ListHeadersByTags(category string, tags []Tag) ([]Header, error) {
|
||||||
|
if len(tags) == 0 {
|
||||||
|
return nil, errors.New("no tags")
|
||||||
|
}
|
||||||
|
|
||||||
|
headers, err := ListHeadersByTag(category, &tags[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(headers) == 0 {
|
||||||
|
return headers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags[1:] {
|
||||||
|
headers2, err := ListHeadersByTag(category, &tag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make([]Header, 0, len(headers))
|
||||||
|
for _, header := range headers {
|
||||||
|
found := false
|
||||||
|
for _, header2 := range headers2 {
|
||||||
|
if header.ID == header2.ID {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
results = append(results, header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = results
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers, nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseHeader(header *Header, rows *sql.Rows) error {
|
func parseHeader(header *Header, rows *sql.Rows) error {
|
||||||
var tagID, tagName, tagType string
|
var tagID, tagName, tagType string
|
||||||
var fictionalDate, publishDate, editDate string
|
var fictionalDate, publishDate, editDate string
|
||||||
|
|||||||
+2
-14
@@ -14,18 +14,6 @@ import (
|
|||||||
"github.com/russross/blackfriday"
|
"github.com/russross/blackfriday"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PageCategories are used by the view model and page to enforce
|
|
||||||
// a limited selection of categories. I may move it to a configuration
|
|
||||||
var PageCategories = []string{
|
|
||||||
"OoC",
|
|
||||||
"Story",
|
|
||||||
"Background",
|
|
||||||
"Document",
|
|
||||||
"News",
|
|
||||||
"Item",
|
|
||||||
"Info",
|
|
||||||
}
|
|
||||||
|
|
||||||
// PageTypes describes how the source is rendered. For now it's only markdown,
|
// PageTypes describes how the source is rendered. For now it's only markdown,
|
||||||
// but who knows what the future holds.
|
// but who knows what the future holds.
|
||||||
var PageTypes = []string{
|
var PageTypes = []string{
|
||||||
@@ -62,7 +50,7 @@ type Page struct {
|
|||||||
|
|
||||||
// Defaults fills in the default details for a page, suited for populating a form
|
// Defaults fills in the default details for a page, suited for populating a form
|
||||||
func (page *Page) Defaults() {
|
func (page *Page) Defaults() {
|
||||||
page.Category = PageCategories[0]
|
page.Category = PageCategories[0].Key
|
||||||
|
|
||||||
page.Dated = true
|
page.Dated = true
|
||||||
page.Published = true
|
page.Published = true
|
||||||
@@ -220,7 +208,7 @@ func (page *Page) ParseForm(form url.Values) []error {
|
|||||||
errors := make([]error, 0, 4)
|
errors := make([]error, 0, 4)
|
||||||
page.cachedOutput = ""
|
page.cachedOutput = ""
|
||||||
|
|
||||||
err := formparser.Select(form.Get("category"), &page.Category, PageCategories, page.Category != "")
|
err := formparser.Select(form.Get("category"), &page.Category, pageCategories, page.Category != "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = append(errors, fmt.Errorf("Category: %s", err))
|
errors = append(errors, fmt.Errorf("Category: %s", err))
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -131,7 +131,7 @@ func TestPage(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ListHeadersByTag", func(t *testing.T) {
|
t.Run("ListHeadersByTag", func(t *testing.T) {
|
||||||
headers, err := ListHeadersByTag(testPageTags[1])
|
headers, err := ListHeadersByTag("", testPageTags[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ func TestPage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make a fake tag and make sure that doesn't return stuff
|
// Make a fake tag and make sure that doesn't return stuff
|
||||||
headers, err = ListHeadersByTag(&Tag{ID: generate.ID()})
|
headers, err = ListHeadersByTag("", &Tag{ID: generate.ID()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.aiterp.net/AiteRP/aitestory/server"
|
"git.aiterp.net/AiteRP/aitestory/server"
|
||||||
"git.aiterp.net/gisle/wrouter/generate"
|
"git.aiterp.net/gisle/wrouter/generate"
|
||||||
@@ -108,6 +109,12 @@ func (tag *Tag) Validate() error {
|
|||||||
return nil
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
// FindTag finds a tag by ID
|
// FindTag finds a tag by ID
|
||||||
func FindTag(key string, id string) (*Tag, error) {
|
func FindTag(key string, id string) (*Tag, error) {
|
||||||
db := server.Main.DB
|
db := server.Main.DB
|
||||||
|
|||||||
+23
-1
@@ -96,7 +96,29 @@ func TestTag(t *testing.T) {
|
|||||||
t.Run("FindByID", func(t *testing.T) {
|
t.Run("FindByID", func(t *testing.T) {
|
||||||
tag, err := FindTag("id", id)
|
tag, err := FindTag("id", id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log("Failed to get tags:", err)
|
t.Log("Failed to find tag:", err)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if tag == nil {
|
||||||
|
t.Log("No tag found")
|
||||||
|
t.Fail()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Tag found: %+v", tag)
|
||||||
|
|
||||||
|
if tag.Name != name || tag.ID != id {
|
||||||
|
t.Error("Incorrect tag")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
testTag = tag
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("FindByName", func(t *testing.T) {
|
||||||
|
tag, err := FindTag("name", name)
|
||||||
|
if err != nil {
|
||||||
|
t.Log("Failed to find tag:", err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
if tag == nil {
|
if tag == nil {
|
||||||
|
|||||||
+17
-3
@@ -15,15 +15,22 @@ import (
|
|||||||
var wd, _ = os.Getwd()
|
var wd, _ = os.Getwd()
|
||||||
var rootPath = path.Join(wd, "./view/templates/")
|
var rootPath = path.Join(wd, "./view/templates/")
|
||||||
var cache = make(map[string]*template.Template)
|
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
|
// Register registers a template and compiles it for rendering. This should be done
|
||||||
// in the beginning since an error will terminate the server
|
// in the beginning since an error will terminate the server
|
||||||
func Register(name string, base string) {
|
func Register(name string, base string, fragments ...string) {
|
||||||
tmpl, err := template.New(name).ParseFiles(path.Join(rootPath, name+".tmpl"), path.Join(rootPath, base+".tmpl"))
|
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).ParseFiles(args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to register %s: %s", name, err)
|
log.Fatalf("Failed to register %s: %s", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
argsCache[name] = args
|
||||||
cache[name] = tmpl
|
cache[name] = tmpl
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +44,14 @@ func Render(w http.ResponseWriter, name string, status int, viewModel interface{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.New(name).ParseFiles(argsCache[name]...)
|
||||||
|
if err != nil {
|
||||||
|
response.Text(w, 500, "Failed to run template "+name+": "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
err := tmpl.ExecuteTemplate(w, "base", viewModel)
|
err = tmpl.ExecuteTemplate(w, "base", viewModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Template error:", err.Error())
|
log.Println("Template error:", err.Error())
|
||||||
}
|
}
|
||||||
@@ -56,4 +69,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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<script type="text/javascript" src="/ui/js/background.js"></script>
|
<script type="text/javascript" src="/ui/js/background.js"></script>
|
||||||
|
|
||||||
{{ template "head" }}
|
{{ block "head" . }}{{end}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -28,14 +28,11 @@
|
|||||||
|
|
||||||
<div id="content-wrapper">
|
<div id="content-wrapper">
|
||||||
<main>
|
<main>
|
||||||
{{ template "content" }}
|
{{ block "content" . }}{{end}}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<nav class="main-menu">
|
<nav class="main-menu">
|
||||||
{{ template "menu" }}
|
{{ block "menu" . }}{{end}}
|
||||||
<ul>
|
|
||||||
<li><a><div class="mg-icon">A</div><div class="mg-label">Login</div></a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,11 +1,33 @@
|
|||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<article>
|
<article>
|
||||||
<h1>Hello, World</h1>
|
|
||||||
</article>
|
</article>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ define "menu" }}
|
{{ define "menu" }}
|
||||||
<h1>Aite RP</h1>
|
<a href="/"><h1>Aite RP</h1></a>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{{ range .Categories }}
|
||||||
|
<li class="{{ if eq .Key $.ActiveCategory.Key }}selected{{end}}"><a href="/{{.URLRoot}}/{{$.ActiveTag.Hook}}"><div class="mg-icon">Π</div><div class="mg-label">{{.Plural}}</div></a></li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{ if $.User.LoggedIn }}
|
||||||
|
<ul>
|
||||||
|
<li><a href="/page/create"><div class="mg-icon">+</div><div class="mg-label">Create</div></a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="/user/logout"><div class="mg-icon">A</div><div class="mg-label">Logout</div></a></li>
|
||||||
|
</ul>
|
||||||
|
{{ else }}
|
||||||
|
<ul>
|
||||||
|
<li><a href="/user/login"><div class="mg-icon">A</div><div class="mg-label">Login</div></a></li>
|
||||||
|
</ul>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ define "head" }}
|
{{ define "head" }}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{{ define "content" }}
|
||||||
|
<article>
|
||||||
|
<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" />
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ 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>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "head" }}
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
+11
-9
@@ -2,7 +2,7 @@ package viewmodel
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"log"
|
||||||
|
|
||||||
"git.aiterp.net/AiteRP/aitestory/server"
|
"git.aiterp.net/AiteRP/aitestory/server"
|
||||||
"git.aiterp.net/gisle/wrouter/auth"
|
"git.aiterp.net/gisle/wrouter/auth"
|
||||||
@@ -10,20 +10,22 @@ import (
|
|||||||
|
|
||||||
// Base is the basic information used to render the page
|
// Base is the basic information used to render the page
|
||||||
type Base struct {
|
type Base struct {
|
||||||
UserID string
|
User struct {
|
||||||
UserName string
|
Name string
|
||||||
UserRole string
|
Role string
|
||||||
UserLoggedIn bool
|
LoggedIn bool
|
||||||
|
}
|
||||||
ViewTitle string
|
ViewTitle string
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitBase initializes the base of the viewmodel
|
// 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) {
|
||||||
if user != nil {
|
if user != nil {
|
||||||
base.UserID = user.ID
|
log.Printf("%+v", user)
|
||||||
base.UserName = strings.SplitN(user.ID, ":", 2)[1]
|
|
||||||
base.UserRole = user.Data["role"]
|
base.User.Name = user.ID
|
||||||
base.UserLoggedIn = true
|
base.User.Role = user.Data["role"]
|
||||||
|
base.User.LoggedIn = true
|
||||||
}
|
}
|
||||||
|
|
||||||
base.ViewTitle = fmt.Sprintf("%s - %s", viewTitle, server.Main.Config.View.Title)
|
base.ViewTitle = fmt.Sprintf("%s - %s", viewTitle, server.Main.Config.View.Title)
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ import (
|
|||||||
type PageList struct {
|
type PageList struct {
|
||||||
Base
|
Base
|
||||||
Headers []model.Header
|
Headers []model.Header
|
||||||
Category string
|
ActiveCategory model.PageCategory
|
||||||
ActiveTags []model.Tag
|
Categories []model.PageCategory
|
||||||
|
ActiveTag model.Tag
|
||||||
FavoriteTags []model.Tag
|
FavoriteTags []model.Tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package viewmodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.aiterp.net/gisle/wrouter/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserLogin is a view model for rendering the user login form
|
||||||
|
type UserLogin struct {
|
||||||
|
Base
|
||||||
|
Error string
|
||||||
|
UserName string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user