Migrate cat views

This commit is contained in:
Timur Ismagilov 2024-07-28 13:15:12 +03:00
parent bd155622af
commit ec57251f58
9 changed files with 122 additions and 124 deletions

View File

@ -23,8 +23,8 @@ package categories
import "sync" import "sync"
// listOfCategories returns unsorted names of all categories. // ListOfCategories returns unsorted names of all categories.
func listOfCategories() (categoryList []string) { func ListOfCategories() (categoryList []string) {
mutex.RLock() mutex.RLock()
for cat, _ := range categoryToHyphae { for cat, _ := range categoryToHyphae {
categoryList = append(categoryList, cat) categoryList = append(categoryList, cat)
@ -44,8 +44,8 @@ func CategoriesWithHypha(hyphaName string) (categoryList []string) {
} }
} }
// hyphaeInCategory returns what hyphae are in the category. If the returned slice is empty, the category does not exist, and vice versa. The category name must be canonical. // HyphaeInCategory returns what hyphae are in the category. If the returned slice is empty, the category does not exist, and vice versa. The category name must be canonical.
func hyphaeInCategory(catName string) (hyphaList []string) { func HyphaeInCategory(catName string) (hyphaList []string) {
mutex.RLock() mutex.RLock()
defer mutex.RUnlock() defer mutex.RUnlock()
if node, ok := categoryToHyphae[catName]; ok { if node, ok := categoryToHyphae[catName]; ok {
@ -75,8 +75,8 @@ func AddHyphaToCategory(hyphaName, catName string) {
go saveToDisk() go saveToDisk()
} }
// removeHyphaFromCategory removes the hypha from the category and updates the records on the disk. If the hypha is not in the category, nothing happens. Pass canonical names. // RemoveHyphaFromCategory removes the hypha from the category and updates the records on the disk. If the hypha is not in the category, nothing happens. Pass canonical names.
func removeHyphaFromCategory(hyphaName, catName string) { func RemoveHyphaFromCategory(hyphaName, catName string) {
mutex.Lock() mutex.Lock()
if node, ok := hyphaToCategories[hyphaName]; ok { if node, ok := hyphaToCategories[hyphaName]; ok {
node.removeCategory(catName) node.removeCategory(catName)

View File

@ -1,37 +0,0 @@
{{define "edit category x"}}Edit category {{beautifulName .}}{{end}}
{{define "title"}}{{template "edit category x" .CatName}}{{end}}
{{define "body"}}
<main class="main-width category">
<h1>{{block "edit category heading" .CatName}}Edit category <a href="/category/{{.}}">{{beautifulName .}}</a>{{end}}</h1>
{{if len .Hyphae | not}}
<p>{{block "empty cat" .}}This category is empty{{end}}</p>
{{end}}
{{if .GivenPermissionToModify}}
<h2>{{block "add to category title" .}}Add a hypha to the category{{end}}</h2>
<form method="POST" action="/add-to-category" class="add-to-category">
<input type="text" name="hypha" id="_hypha-name"
placeholder="{{block `hypha name` .}}Hypha name{{end}}">
<input type="hidden" name="cat" value="{{.CatName}}">
<input type="hidden" name="redirect-to" value="/category/{{.CatName}}">
<input type="submit" class="btn" value="{{block `add` .}}Add{{end}}">
</form>
{{if len .Hyphae}}
<h2>{{block "remove hyphae" .}}Remove hyphae from the category{{end}}</h2>
<form method="POST" action="/remove-from-category" class="multi-remove-from-category">
<ol>
{{range .Hyphae}}
<li>
<input type="checkbox" name="_{{.}}" id="_{{.}}">
<label for="_{{.}}"><a href="/hypha/{{.}}">{{beautifulName .}}</a></label>
</li>
{{end}}
</ol>
<input type="hidden" name="cat" value="{{.CatName}}">
<input type="hidden" name="redirect-to" value="/edit-category/{{.CatName}}">
<input type="submit" class="btn" value="{{block `remove` .}}Remove{{end}}">
</form>
{{end}}{{end}}
</main>
{{end}}

View File

@ -1,45 +1,29 @@
package categories package categories
import ( import (
"embed"
"sort"
"github.com/bouncepaw/mycorrhiza/web/viewutil" "github.com/bouncepaw/mycorrhiza/web/viewutil"
) )
const ruTranslation = ` const ruTranslation = `
{{define "empty cat"}}Эта категория пуста.{{end}} {{define "empty cat"}}{{end}}
{{define "cat"}}Категория{{end}} {{define "cat"}}{{end}}
{{define "hypha name"}}Название гифы{{end}} {{define "hypha name"}}{{end}}
{{define "categories"}}Категории{{end}} {{define "categories"}}Категории{{end}}
{{define "placeholder"}}Название категории...{{end}} {{define "placeholder"}}Название категории...{{end}}
{{define "remove from category title"}}Убрать гифу из этой категории{{end}} {{define "remove from category title"}}Убрать гифу из этой категории{{end}}
{{define "add to category title"}}Добавить гифу в эту категорию{{end}} {{define "add to category title"}}{{end}}
{{define "category list"}}Список категорий{{end}} {{define "category list"}}{{end}}
{{define "no categories"}}В этой вики нет категорий.{{end}} {{define "no categories"}}{{end}}
{{define "category x"}}Категория {{. | beautifulName}}{{end}} {{define "category x"}}{{end}}
{{define "edit category x"}}Редактирование категории {{beautifulName .}}{{end}} {{define "edit category x"}}{{end}}
{{define "edit category heading"}}Редактирование категории <a href="/category/{{.}}">{{beautifulName .}}</a>{{end}} {{define "edit category heading"}}{{end}}
{{define "add"}}Добавить{{end}} {{define "add"}}{{end}}
{{define "remove hyphae"}}Убрать гифы из этой категории{{end}} {{define "remove hyphae"}}{{end}}
{{define "remove"}}Убрать{{end}} {{define "remove"}}{{end}}
{{define "edit"}}Редактировать{{end}} {{define "edit"}}{{end}}
` `
var (
//go:embed *.html
fs embed.FS
viewListChain, viewPageChain, viewCardChain, viewEditChain viewutil.Chain
)
func prepareViews() {
viewCardChain = viewutil.CopyEnRuWith(fs, "view_card.html", ruTranslation)
viewListChain = viewutil.CopyEnRuWith(fs, "view_list.html", ruTranslation)
viewPageChain = viewutil.CopyEnRuWith(fs, "view_page.html", ruTranslation)
viewEditChain = viewutil.CopyEnRuWith(fs, "view_edit.html", ruTranslation)
}
type catData struct { type catData struct {
*viewutil.BaseData *viewutil.BaseData
CatName string CatName string
@ -47,26 +31,8 @@ type catData struct {
GivenPermissionToModify bool GivenPermissionToModify bool
} }
func categoryEdit(meta viewutil.Meta, catName string) {
viewutil.ExecutePage(meta, viewEditChain, catData{
BaseData: &viewutil.BaseData{
Addr: "/edit-category/" + catName,
},
CatName: catName,
Hyphae: hyphaeInCategory(catName),
GivenPermissionToModify: meta.U.CanProceed("add-to-category"),
})
}
func categoryPage(meta viewutil.Meta, catName string) { func categoryPage(meta viewutil.Meta, catName string) {
viewutil.ExecutePage(meta, viewPageChain, catData{
BaseData: &viewutil.BaseData{
Addr: "/category/" + catName,
},
CatName: catName,
Hyphae: hyphaeInCategory(catName),
GivenPermissionToModify: meta.U.CanProceed("add-to-category"),
})
} }
type listData struct { type listData struct {
@ -75,12 +41,5 @@ type listData struct {
} }
func categoryList(meta viewutil.Meta) { func categoryList(meta viewutil.Meta) {
cats := listOfCategories()
sort.Strings(cats)
viewutil.ExecutePage(meta, viewListChain, listData{
BaseData: &viewutil.BaseData{
Addr: "/category",
},
Categories: cats,
})
} }

View File

@ -1,41 +1,46 @@
package categories package web
import ( import (
"github.com/bouncepaw/mycorrhiza/categories"
"io" "io"
"log" "log"
"log/slog"
"net/http" "net/http"
"sort"
"strings" "strings"
"github.com/bouncepaw/mycorrhiza/internal/user" "github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/util" "github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/web/viewutil" "github.com/bouncepaw/mycorrhiza/web/viewutil"
"github.com/gorilla/mux"
) )
// InitHandlers initializes HTTP handlers for the given router. Call somewhere in package web.
func InitHandlers(r *mux.Router) {
r.PathPrefix("/add-to-category").HandlerFunc(handlerAddToCategory).Methods("POST")
r.PathPrefix("/remove-from-category").HandlerFunc(handlerRemoveFromCategory).Methods("POST")
r.PathPrefix("/category/").HandlerFunc(handlerCategory).Methods("GET")
r.PathPrefix("/edit-category/").HandlerFunc(handlerEditCategory).Methods("GET")
r.PathPrefix("/category").HandlerFunc(handlerListCategory).Methods("GET")
prepareViews()
}
func handlerEditCategory(w http.ResponseWriter, rq *http.Request) { func handlerEditCategory(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq) util.PrepareRq(rq)
meta := viewutil.MetaFrom(w, rq)
catName := util.CanonicalName(strings.TrimPrefix(strings.TrimPrefix(rq.URL.Path, "/edit-category"), "/")) catName := util.CanonicalName(strings.TrimPrefix(strings.TrimPrefix(rq.URL.Path, "/edit-category"), "/"))
if catName == "" { if catName == "" {
http.Error(w, "No category name", http.StatusBadRequest) viewutil.HandlerNotFound(w, rq)
return return
} }
log.Println("Editing category", catName)
categoryEdit(viewutil.MetaFrom(w, rq), catName) slog.Info("Editing category", "name", catName)
_ = pageCatEdit.RenderTo(meta, map[string]any{
"Addr": "/edit-category/" + catName,
"CatName": catName,
"Hyphae": categories.HyphaeInCategory(catName),
"GivenPermissionToModify": meta.U.CanProceed("add-to-category"),
})
} }
func handlerListCategory(w http.ResponseWriter, rq *http.Request) { func handlerListCategory(w http.ResponseWriter, rq *http.Request) {
log.Println("Viewing list of categories") slog.Info("Viewing list of categories")
categoryList(viewutil.MetaFrom(w, rq)) cats := categories.ListOfCategories()
sort.Strings(cats)
_ = pageCatList.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{
"Addr": "/category",
"Categories": cats,
})
} }
func handlerCategory(w http.ResponseWriter, rq *http.Request) { func handlerCategory(w http.ResponseWriter, rq *http.Request) {
@ -45,8 +50,15 @@ func handlerCategory(w http.ResponseWriter, rq *http.Request) {
handlerListCategory(w, rq) handlerListCategory(w, rq)
return return
} }
log.Println("Viewing category", catName)
categoryPage(viewutil.MetaFrom(w, rq), catName) meta := viewutil.MetaFrom(w, rq)
slog.Info("Viewing category", "name", catName)
_ = pageCatPage.RenderTo(meta, map[string]any{
"Addr": "/category/" + catName,
"CatName": catName,
"Hyphae": categories.HyphaeInCategory(catName),
"GivenPermissionToModify": meta.U.CanProceed("add-to-category"),
})
} }
// A request for removal of hyphae can either remove one hypha (used in the card on /hypha) or many hyphae (used in /edit-category). Both approaches are handled by /remove-from-category. This function finds all passed hyphae. // A request for removal of hyphae can either remove one hypha (used in the card on /hypha) or many hyphae (used in /edit-category). Both approaches are handled by /remove-from-category. This function finds all passed hyphae.
@ -94,7 +106,7 @@ func handlerRemoveFromCategory(w http.ResponseWriter, rq *http.Request) {
} }
for _, hyphaName := range hyphaNames { for _, hyphaName := range hyphaNames {
// TODO: Make it more effective. // TODO: Make it more effective.
removeHyphaFromCategory(hyphaName, catName) categories.RemoveHyphaFromCategory(hyphaName, catName)
} }
log.Printf("%s removed %q from category %s\n", u.Name, hyphaNames, catName) log.Printf("%s removed %q from category %s\n", u.Name, hyphaNames, catName)
http.Redirect(w, rq, redirectTo, http.StatusSeeOther) http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
@ -117,6 +129,6 @@ func handlerAddToCategory(w http.ResponseWriter, rq *http.Request) {
return return
} }
log.Println(user.FromRequest(rq).Name, "added", hyphaName, "to", catName) log.Println(user.FromRequest(rq).Name, "added", hyphaName, "to", catName)
AddHyphaToCategory(hyphaName, catName) categories.AddHyphaToCategory(hyphaName, catName)
http.Redirect(w, rq, redirectTo, http.StatusSeeOther) http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
} }

View File

@ -12,7 +12,8 @@ var fs embed.FS
var pageOrphans, pageBacklinks, pageUserList, pageChangePassword *newtmpl.Page var pageOrphans, pageBacklinks, pageUserList, pageChangePassword *newtmpl.Page
var pageHyphaDelete, pageHyphaEdit, pageHyphaEmpty, pageHypha *newtmpl.Page var pageHyphaDelete, pageHyphaEdit, pageHyphaEmpty, pageHypha *newtmpl.Page
var pageRevision, pageMedia *newtmpl.Page var pageRevision, pageMedia *newtmpl.Page
var pageAuthLock, pageAuthLogin, pageAuthLogout, pageAuthRegister, pageAuthTelegram *newtmpl.Page var pageAuthLock, pageAuthLogin, pageAuthLogout, pageAuthRegister *newtmpl.Page
var pageCatPage, pageCatList, pageCatEdit *newtmpl.Page
var panelChain, listChain, newUserChain, editUserChain, deleteUserChain viewutil.Chain var panelChain, listChain, newUserChain, editUserChain, deleteUserChain viewutil.Chain
@ -62,7 +63,7 @@ func initPages() {
"describe your changes": `Опишите ваши правки`, "describe your changes": `Опишите ваши правки`,
"save": `Сохранить`, "save": `Сохранить`,
"preview": `Предпросмотр`, "preview": `Предпросмотр`,
"previewing hypha": `Предпросмотр «{{beautifulName .}}»`, "previewing hypha": `Предпросмотр {{beautifulName .}}`,
"preview tip": `Заметьте, эта гифа ещё не сохранена. Вот её предпросмотр:`, "preview tip": `Заметьте, эта гифа ещё не сохранена. Вот её предпросмотр:`,
"markup": `Разметка`, "markup": `Разметка`,
@ -176,4 +177,26 @@ func initPages() {
"register on x": "Регистрация на {{.}}", "register on x": "Регистрация на {{.}}",
}, "views/auth-telegram.html", "views/auth-register.html") }, "views/auth-telegram.html", "views/auth-register.html")
pageCatPage = newtmpl.NewPage(fs, map[string]string{
"category x": "Категория {{. | beautifulName}}",
"edit": "Редактировать",
"cat": "Категория",
"empty cat": "Эта категория пуста.",
}, "views/cat-page.html")
pageCatEdit = newtmpl.NewPage(fs, map[string]string{
"edit category x": "Редактирование категории {{beautifulName .}}",
"edit category heading": "Редактирование категории <a href=\"/category/{{.}}\">{{beautifulName .}}</a>",
"empty cat": "Эта категория пуста.",
"add to category title": "Добавить гифу в эту категорию",
"hypha name": "Название гифы",
"add": "Добавить",
"remove hyphae": "Убрать гифы из этой категории",
"remove": "Убрать",
}, "views/cat-edit.html")
pageCatList = newtmpl.NewPage(fs, map[string]string{
"category list": "Список категорий",
"no categories": "В этой вики нет категорий.",
}, "views/cat-list.html")
} }

37
web/views/cat-edit.html Normal file
View File

@ -0,0 +1,37 @@
{{define "edit category x"}}Edit category {{beautifulName .}}{{end}}
{{define "title"}}{{template "edit category x" .CatName}}{{end}}
{{define "body"}}
<main class="main-width category">
<h1>{{block "edit category heading" .CatName}}Edit category <a href="/category/{{.}}">{{beautifulName .}}</a>{{end}}</h1>
{{if len .Hyphae | not}}
<p>{{block "empty cat" .}}This category is empty{{end}}</p>
{{end}}
{{if .GivenPermissionToModify}}
<h2>{{block "add to category title" .}}Add a hypha to the category{{end}}</h2>
<form method="POST" action="/add-to-category" class="add-to-category">
<input type="text" name="hypha" id="_hypha-name"
placeholder="{{block `hypha name` .}}Hypha name{{end}}">
<input type="hidden" name="cat" value="{{.CatName}}">
<input type="hidden" name="redirect-to" value="/category/{{.CatName}}">
<input type="submit" class="btn" value="{{block `add` .}}Add{{end}}">
</form>
{{if len .Hyphae}}
<h2>{{block "remove hyphae" .}}Remove hyphae from the category{{end}}</h2>
<form method="POST" action="/remove-from-category" class="multi-remove-from-category">
<ol>
{{range .Hyphae}}
<li>
<input type="checkbox" name="_{{.}}" id="_{{.}}">
<label for="_{{.}}"><a href="/hypha/{{.}}">{{beautifulName .}}</a></label>
</li>
{{end}}
</ol>
<input type="hidden" name="cat" value="{{.CatName}}">
<input type="hidden" name="redirect-to" value="/edit-category/{{.CatName}}">
<input type="submit" class="btn" value="{{block `remove` .}}Remove{{end}}">
</form>
{{end}}{{end}}
</main>
{{end}}

View File

@ -16,7 +16,6 @@ import (
"net/url" "net/url"
"strings" "strings"
"github.com/bouncepaw/mycorrhiza/categories"
"github.com/bouncepaw/mycorrhiza/help" "github.com/bouncepaw/mycorrhiza/help"
"github.com/bouncepaw/mycorrhiza/history/histweb" "github.com/bouncepaw/mycorrhiza/history/histweb"
"github.com/bouncepaw/mycorrhiza/hypview" "github.com/bouncepaw/mycorrhiza/hypview"
@ -73,12 +72,17 @@ func Handler() http.Handler {
initReaders(r) initReaders(r)
initMutators(r) initMutators(r)
help.InitHandlers(r) help.InitHandlers(r)
categories.InitHandlers(r)
misc.InitHandlers(r) misc.InitHandlers(r)
hypview.Init() hypview.Init()
histweb.InitHandlers(r) histweb.InitHandlers(r)
interwiki.InitHandlers(r) interwiki.InitHandlers(r)
r.PathPrefix("/add-to-category").HandlerFunc(handlerAddToCategory).Methods("POST")
r.PathPrefix("/remove-from-category").HandlerFunc(handlerRemoveFromCategory).Methods("POST")
r.PathPrefix("/category/").HandlerFunc(handlerCategory).Methods("GET")
r.PathPrefix("/edit-category/").HandlerFunc(handlerEditCategory).Methods("GET")
r.PathPrefix("/category").HandlerFunc(handlerListCategory).Methods("GET")
// Admin routes // Admin routes
if cfg.UseAuth { if cfg.UseAuth {
adminRouter := r.PathPrefix("/admin").Subrouter() adminRouter := r.PathPrefix("/admin").Subrouter()