jordi fita mas c0f532df4e Add the pages section
For now, this is almost identical to the campsite types, but this
section is for purely informational pages that have no other relation
to the database than “belongs to the same company”.

Part of #33.
2023-08-08 20:09:57 +02:00

184 lines
4.6 KiB
Go

/*
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
* SPDX-License-Identifier: AGPL-3.0-only
*/
package page
import (
"context"
"net/http"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/form"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
"dev.tandem.ws/tandem/camper/pkg/locale"
"dev.tandem.ws/tandem/camper/pkg/template"
"dev.tandem.ws/tandem/camper/pkg/uuid"
)
type AdminHandler struct {
}
func NewAdminHandler() *AdminHandler {
return &AdminHandler{}
}
func (h *AdminHandler) Handler(user *auth.User, company *auth.Company, conn *database.Conn) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var head string
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
switch head {
case "new":
switch r.Method {
case http.MethodGet:
f := newPageForm()
f.MustRender(w, r, user, company)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
case "":
switch r.Method {
case http.MethodGet:
servePageIndex(w, r, user, company, conn)
case http.MethodPost:
f := newPageForm()
f.Handle(w, r, user, company, func(ctx context.Context) {
conn.MustExec(ctx, "select add_page($1, $2, $3)", company.ID, f.Title, f.Content)
})
default:
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPost)
}
default:
if !uuid.Valid(head) {
http.NotFound(w, r)
return
}
f := newPageForm()
if err := f.FillFromDatabase(r.Context(), conn, head); err != nil {
if database.ErrorIsNotFound(err) {
http.NotFound(w, r)
return
}
panic(err)
}
switch r.Method {
case http.MethodGet:
f.MustRender(w, r, user, company)
case http.MethodPut:
f.Handle(w, r, user, company, func(ctx context.Context) {
conn.MustExec(ctx, "select edit_page($1, $2, $3)", f.Slug, f.Title, f.Content)
})
default:
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
}
}
})
}
func servePageIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
pages, err := collectPageEntries(r.Context(), company, conn)
if err != nil {
panic(err)
}
page := &pageIndex{
Pages: pages,
}
page.MustRender(w, r, user, company)
}
type pageEntry struct {
Slug string
Title string
}
func collectPageEntries(ctx context.Context, company *auth.Company, conn *database.Conn) ([]*pageEntry, error) {
rows, err := conn.Query(ctx, "select slug, title from page where company_id = $1", company.ID)
if err != nil {
return nil, err
}
defer rows.Close()
var types []*pageEntry
for rows.Next() {
entry := &pageEntry{}
if err = rows.Scan(&entry.Slug, &entry.Title); err != nil {
return nil, err
}
types = append(types, entry)
}
return types, nil
}
type pageIndex struct {
Pages []*pageEntry
}
func (index *pageIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
template.MustRenderAdmin(w, r, user, company, "page/index.gohtml", index)
}
type pageForm struct {
Slug string
Title *form.Input
Content *form.Input
}
func newPageForm() *pageForm {
return &pageForm{
Title: &form.Input{
Name: "title",
},
Content: &form.Input{
Name: "content",
},
}
}
func (f *pageForm) FillFromDatabase(ctx context.Context, conn *database.Conn, slug string) error {
f.Slug = slug
row := conn.QueryRow(ctx, "select title, content from page where slug = $1", slug)
return row.Scan(&f.Title.Val, &f.Content.Val)
}
func (f *pageForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
f.Title.FillValue(r)
f.Content.FillValue(r)
return nil
}
func (f *pageForm) Valid(l *locale.Locale) bool {
v := form.NewValidator(l)
v.CheckRequired(f.Title, l.GettextNoop("Title can not be empty."))
return v.AllOK
}
func (f *pageForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
template.MustRenderAdmin(w, r, user, company, "page/form.gohtml", f)
}
func (f *pageForm) Handle(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, exec func(ctx context.Context)) {
if err := f.Parse(r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := user.VerifyCSRFToken(r); err != nil {
http.Error(w, err.Error(), http.StatusForbidden)
return
}
if !f.Valid(user.Locale) {
if !httplib.IsHTMxRequest(r) {
w.WriteHeader(http.StatusUnprocessableEntity)
}
f.MustRender(w, r, user, company)
return
}
exec(r.Context())
httplib.Redirect(w, r, "/admin/pages", http.StatusSeeOther)
}