/* * SPDX-FileCopyrightText: 2023 jordi fita mas * 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) }