Changed tags to allow same-name taggs with different categories, added tag list
This commit is contained in:
@@ -52,7 +52,7 @@ func listFiltered(category model.PageCategory) wrouter.FunctionHandlerFunc {
|
|||||||
vm := viewmodel.PageList{}
|
vm := viewmodel.PageList{}
|
||||||
|
|
||||||
if tagName != "" {
|
if tagName != "" {
|
||||||
tag, err := model.FindTag("name", tagName)
|
tag, err := model.FindTag("name", tagName, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Text(w, 404, err.Error())
|
response.Text(w, 404, err.Error())
|
||||||
return true
|
return true
|
||||||
@@ -79,11 +79,53 @@ func listFiltered(category model.PageCategory) wrouter.FunctionHandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listTagged(tagType string) 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 == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := model.FindTag("name", tagName, tagType)
|
||||||
|
if err != nil {
|
||||||
|
response.Text(w, 404, err.Error())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.ActiveTag = *tag
|
||||||
|
vm.Headers, err = model.ListHeadersByTag("", tag)
|
||||||
|
|
||||||
|
vm.Categories = model.PageCategories
|
||||||
|
vm.Setup(user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
response.Text(w, 500, err.Error())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
view.Render(w, "index", 200, vm)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ListController.Function("/", listIndex)
|
ListController.Function("/", listIndex)
|
||||||
|
|
||||||
ListController.Function("/", listFiltered(model.PageCategory{Key: ""}))
|
|
||||||
for _, category := range model.PageCategories {
|
for _, category := range model.PageCategories {
|
||||||
ListController.Function(fmt.Sprintf("/%s/", strings.ToLower(category.Plural)), listFiltered(category))
|
ListController.Function(fmt.Sprintf("/%s/", strings.ToLower(category.Plural)), listFiltered(category))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, tagType := range model.TagTypes {
|
||||||
|
ListController.Function(fmt.Sprintf("/%s/", strings.ToLower(tagType)), listTagged(tagType))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@@ -10,6 +10,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("/page", &controllers.PageController)
|
||||||
|
router.Mount("/tags", &controllers.TagController)
|
||||||
router.Mount("/", &controllers.ListController)
|
router.Mount("/", &controllers.ListController)
|
||||||
|
|
||||||
router.Static("/ui/", server.Main.Config.Directories.UI)
|
router.Static("/ui/", server.Main.Config.Directories.UI)
|
||||||
|
|||||||
+37
-6
@@ -129,8 +129,9 @@ func (tag Tag) Hook() string {
|
|||||||
return strings.Replace(tag.Name, " ", "_", -1)
|
return strings.Replace(tag.Name, " ", "_", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindTag finds a tag by ID
|
// FindTag finds a tag by ID. Leave tagtype blank to ignore it. Both key and
|
||||||
func FindTag(key string, id string) (*Tag, error) {
|
// id are checked against their respective lists to prevent SQL injection attacks.
|
||||||
|
func FindTag(key string, id string, tagType string) (*Tag, error) {
|
||||||
db := server.Main.DB
|
db := server.Main.DB
|
||||||
|
|
||||||
// Make damn sure that – should this take user data as key – it
|
// Make damn sure that – should this take user data as key – it
|
||||||
@@ -139,7 +140,24 @@ func FindTag(key string, id string) (*Tag, error) {
|
|||||||
return nil, errors.New("invalid key")
|
return nil, errors.New("invalid key")
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := db.Query("SELECT id,type,name FROM `tag` WHERE "+key+"=? AND disabled=false", id)
|
query := "SELECT id,type,name FROM `tag` WHERE " + key + "=? AND disabled=false"
|
||||||
|
if tagType != "" {
|
||||||
|
// Prevent more shenanigans
|
||||||
|
found := false
|
||||||
|
for _, existing := range TagTypes {
|
||||||
|
if existing == tagType {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return nil, errors.New("invalid tag type")
|
||||||
|
}
|
||||||
|
|
||||||
|
query += fmt.Sprintf(` AND type="%s"`, tagType)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := db.Query(query, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -156,7 +174,7 @@ func FindTag(key string, id string) (*Tag, error) {
|
|||||||
// EnsureTag finds or creates a tag.
|
// EnsureTag finds or creates a tag.
|
||||||
func EnsureTag(tagType, tagName string) (*Tag, error) {
|
func EnsureTag(tagType, tagName string) (*Tag, error) {
|
||||||
// Try to find it first
|
// Try to find it first
|
||||||
tag, err := FindTag("name", tagName)
|
tag, err := FindTag("name", tagName, tagType)
|
||||||
if err != nil && err.Error() != "not found" { // You saw nothing
|
if err != nil && err.Error() != "not found" { // You saw nothing
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -179,7 +197,15 @@ func EnsureTag(tagType, tagName string) (*Tag, error) {
|
|||||||
func ListTags() ([]Tag, error) {
|
func ListTags() ([]Tag, error) {
|
||||||
db := server.Main.DB
|
db := server.Main.DB
|
||||||
|
|
||||||
rows, err := db.Query("SELECT id,type,name FROM `tag` WHERE disabled=false")
|
query := `
|
||||||
|
SELECT id,type,name,count(pt.page_id) FROM tag
|
||||||
|
LEFT JOIN page_tag AS pt ON (pt.tag_id = tag.id)
|
||||||
|
WHERE disabled=false
|
||||||
|
GROUP BY pt.tag_id
|
||||||
|
ORDER BY type,name
|
||||||
|
`
|
||||||
|
|
||||||
|
rows, err := db.Query(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -187,10 +213,15 @@ func ListTags() ([]Tag, error) {
|
|||||||
|
|
||||||
results := make([]Tag, 0, 64)
|
results := make([]Tag, 0, 64)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
count := 0
|
||||||
tag := Tag{}
|
tag := Tag{}
|
||||||
rows.Scan(&tag.ID, &tag.Type, &tag.Name)
|
rows.Scan(&tag.ID, &tag.Type, &tag.Name, &count)
|
||||||
|
|
||||||
|
// Let's pretend unused tags don't exist. >_>
|
||||||
|
if count > 0 {
|
||||||
results = append(results, tag)
|
results = append(results, tag)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,4 +83,5 @@ func init() {
|
|||||||
Register("page/edit", "base/default")
|
Register("page/edit", "base/default")
|
||||||
Register("page/view", "base/default")
|
Register("page/view", "base/default")
|
||||||
Register("page/delete", "base/default")
|
Register("page/delete", "base/default")
|
||||||
|
Register("tags/list", "base/default")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<link rel="stylesheet" href="/ui/css/magic.css" />
|
<link rel="stylesheet" href="/ui/css/magic.css" />
|
||||||
<link rel="stylesheet" href="/ui/css/theme.css" />
|
<link rel="stylesheet" href="/ui/css/theme.css" />
|
||||||
|
|
||||||
<link rel="stylesheet" media="screen" href="/ui/fonts/SeanSans.css" type="text/css"/>
|
<link rel="stylesheet" media="screen" href="/ui/fonts/source-sans-pro/source-sans-pro.css" type="text/css"/>
|
||||||
|
|
||||||
<script type="text/javascript" src="/ui/js/background.js"></script>
|
<script type="text/javascript" src="/ui/js/background.js"></script>
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{{ range .Categories }}
|
{{ range .Categories }}
|
||||||
<li class="{{ if eq .Key $.ActiveCategory.Key }}selected{{end}}"><a href="/{{.URLRoot}}/"><div class="mg-icon">{{.Icon}}</div><div class="mg-label">{{.Plural}}</div></a></li>
|
<li class="{{ if eq .Key $.ActiveCategory.Key }}selected{{end}}"><a href="/{{.Plural}}/"><div class="mg-icon">{{.Icon}}</div><div class="mg-label">{{.Plural}}</div></a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@@ -40,6 +40,10 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="/tags/"><div class="mg-icon">T</div><div class="mg-label">Tags</div></a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
{{ if $.User.LoggedIn }}
|
{{ if $.User.LoggedIn }}
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/page/create"><div class="mg-icon">+</div><div class="mg-label">Create</div></a></li>
|
<li><a href="/page/create"><div class="mg-icon">+</div><div class="mg-label">Create</div></a></li>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ define "menu" }}
|
{{ define "menu" }}
|
||||||
<a href="/page/{{$.Page.ID}}"><h1>Page</h1></a>
|
<a href="/"><h1>Page</h1></a>
|
||||||
{{ if $.Page.Dated}}
|
{{ if $.Page.Dated}}
|
||||||
<div class="page-property">{{$.Page.FictionalDate | formatDate}}</div>
|
<div class="page-property">{{$.Page.FictionalDate | formatDate}}</div>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{{ range $.Page.Tags }}
|
{{ range $.Page.Tags }}
|
||||||
<li><a href="/{{.Hook}}"><div class="mg-icon">{{.Icon}}</div><div class="mg-label {{.CSSCLass}}">{{.Name}}</div></a></li>
|
<li><a href="/{{.Type}}/{{.Hook}}"><div class="mg-icon">{{.Icon}}</div><div class="mg-label {{.CSSCLass}}">{{.Name}}</div></a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{{ define "content" }}
|
||||||
|
<article>
|
||||||
|
<h1>All Tags</h1>
|
||||||
|
|
||||||
|
<div class="tag-list">
|
||||||
|
{{ range $tagHeader, $tags := .Map }}
|
||||||
|
<div class="tl-item">
|
||||||
|
<h2>{{ $tagHeader }}</h2>
|
||||||
|
<ul>
|
||||||
|
{{ range $tags }}
|
||||||
|
<li><a class="ttype-{{.Type | tolower }}" href="/{{.Type}}/{{.Name}}">{{ .Name }}</a></li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "menu" }}
|
||||||
|
<a href="/"><h1>Tags</h1></a>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="/"><div class="mg-icon"><</div><div class="mg-label">Back</div></a></li>
|
||||||
|
</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 }}
|
||||||
|
|
||||||
|
{{ define "head" }}
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
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))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user