Implement guestbook settings #20
@ -4,6 +4,7 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/forms"
 | 
			
		||||
@ -23,6 +24,17 @@ func (app *application) getGuestbook(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !website.Guestbook.Settings.IsVisible {
 | 
			
		||||
		u := app.getCurrentUser(r)
 | 
			
		||||
		if u == nil {
 | 
			
		||||
			app.clientError(w, http.StatusForbidden)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if u.ID != website.UserId {
 | 
			
		||||
			app.clientError(w, http.StatusForbidden)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	comments, err := app.guestbookComments.GetAll(website.Guestbook.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
@ -32,6 +44,78 @@ func (app *application) getGuestbook(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	views.GuestbookView("Guestbook", data, website, website.Guestbook, comments, forms.CommentCreateForm{}).Render(r.Context(), w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) getGuestbookSettings(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	slug := r.PathValue("id")
 | 
			
		||||
	website, err := app.websites.Get(slugToShortId(slug))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, models.ErrNoRecord) {
 | 
			
		||||
			http.NotFound(w, r)
 | 
			
		||||
		} else {
 | 
			
		||||
			app.serverError(w, r, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	data := app.newCommonData(r)
 | 
			
		||||
	views.GuestbookSettingsView(data, website).Render(r.Context(), w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) putGuestbookSettings(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	slug := r.PathValue("id")
 | 
			
		||||
	website, err := app.websites.Get(slugToShortId(slug))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, models.ErrNoRecord) {
 | 
			
		||||
			http.NotFound(w, r)
 | 
			
		||||
		} else {
 | 
			
		||||
			app.serverError(w, r, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var form forms.GuestbookSettingsForm
 | 
			
		||||
	err = app.decodePostForm(r, &form)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.clientError(w, http.StatusBadRequest)
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	form.CheckField(validator.PermittedValue(form.Visibility, "true", "false"), "gb_visible", "Invalid value")
 | 
			
		||||
	form.CheckField(validator.PermittedValue(form.CommentingEnabled, models.ValidDisableDurations...), "gb_visible", "Invalid value")
 | 
			
		||||
	form.CheckField(validator.PermittedValue(form.WidgetsEnabled, "true", "false"), "gb_remote", "Invalid value")
 | 
			
		||||
	if !form.Valid() {
 | 
			
		||||
		// TODO: rerender template with errors
 | 
			
		||||
		app.clientError(w, http.StatusUnprocessableEntity)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c, err := strconv.ParseBool(form.CommentingEnabled)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		website.Guestbook.Settings.IsCommentingEnabled = false
 | 
			
		||||
		website.Guestbook.Settings.ReenableCommenting, err = app.durationToTime(form.CommentingEnabled)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			app.serverError(w, r, err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		website.Guestbook.Settings.IsCommentingEnabled = c
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// can skip error checking for these two since we verify valid values above
 | 
			
		||||
	website.Guestbook.Settings.IsVisible, err = strconv.ParseBool(form.Visibility)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
	}
 | 
			
		||||
	website.Guestbook.Settings.AllowRemoteHostAccess, err = strconv.ParseBool(form.WidgetsEnabled)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
	}
 | 
			
		||||
	err = app.websites.UpdateGuestbookSettings(website.Guestbook.ID, website.Guestbook.Settings)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	app.sessionManager.Put(r.Context(), "flash", "Settings changed successfully")
 | 
			
		||||
	data := app.newCommonData(r)
 | 
			
		||||
	w.Header().Add("HX-Refresh", "true")
 | 
			
		||||
	views.GuestbookSettingsView(data, website).Render(r.Context(), w)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) getGuestbookComments(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	slug := r.PathValue("id")
 | 
			
		||||
	website, err := app.websites.Get(slugToShortId(slug))
 | 
			
		||||
@ -64,7 +148,6 @@ func (app *application) getGuestbookCommentCreate(w http.ResponseWriter, r *http
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data := app.newCommonData(r)
 | 
			
		||||
	form := forms.CommentCreateForm{}
 | 
			
		||||
	views.CreateGuestbookComment("New Comment", data, website, website.Guestbook, form).Render(r.Context(), w)
 | 
			
		||||
@ -82,6 +165,11 @@ func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *htt
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !website.Guestbook.CanComment() {
 | 
			
		||||
		app.clientError(w, http.StatusForbidden)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var form forms.CommentCreateForm
 | 
			
		||||
	err = app.decodePostForm(r, &form)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -98,6 +186,7 @@ func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *htt
 | 
			
		||||
	form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank")
 | 
			
		||||
 | 
			
		||||
	if !form.Valid() {
 | 
			
		||||
		// TODO: use htmx to avoid getting comments again
 | 
			
		||||
		comments, err := app.guestbookComments.GetAll(website.Guestbook.ID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			app.serverError(w, r, err)
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ package main
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/forms"
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
@ -39,7 +40,8 @@ func (app *application) postUserRegister(w http.ResponseWriter, r *http.Request)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	shortId := app.createShortId()
 | 
			
		||||
	err = app.users.Insert(shortId, form.Name, form.Email, form.Password)
 | 
			
		||||
	settings := DefaultUserSettings()
 | 
			
		||||
	err = app.users.Insert(shortId, form.Name, form.Email, form.Password, settings)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, models.ErrDuplicateEmail) {
 | 
			
		||||
			form.AddFieldError("email", "Email address is already in use")
 | 
			
		||||
@ -129,3 +131,39 @@ func (app *application) getUser(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	data := app.newCommonData(r)
 | 
			
		||||
	views.UserProfile(user.Username, data, user).Render(r.Context(), w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) getUserSettings(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	data := app.newCommonData(r)
 | 
			
		||||
	views.UserSettingsView(data, app.timezones).Render(r.Context(), w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) putUserSettings(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	user := app.getCurrentUser(r)
 | 
			
		||||
	var form forms.UserSettingsForm
 | 
			
		||||
	err := app.decodePostForm(r, &form)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.clientError(w, http.StatusBadRequest)
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	form.CheckField(validator.PermittedValue(form.LocalTimezone, app.timezones...), "timezone", "Invalid value")
 | 
			
		||||
	if !form.Valid() {
 | 
			
		||||
		// TODO: rerender template with errors
 | 
			
		||||
		app.clientError(w, http.StatusUnprocessableEntity)
 | 
			
		||||
	}
 | 
			
		||||
	user.Settings.LocalTimezone, err = time.LoadLocation(form.LocalTimezone)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	err = app.users.UpdateUserSettings(user.ID, user.Settings)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	app.sessionManager.Put(r.Context(), "flash", "Settings changed successfully")
 | 
			
		||||
	data := app.newCommonData(r)
 | 
			
		||||
	w.Header().Add("HX-Refresh", "true")
 | 
			
		||||
	views.UserSettingsView(data, app.timezones).Render(r.Context(), w)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -39,25 +39,13 @@ func (app *application) postWebsiteCreate(w http.ResponseWriter, r *http.Request
 | 
			
		||||
		views.WebsiteCreate("Add a Website", data, form).Render(r.Context(), w)
 | 
			
		||||
	}
 | 
			
		||||
	websiteShortID := app.createShortId()
 | 
			
		||||
	websiteId, err := app.websites.Insert(websiteShortID, userId, form.Name, form.SiteUrl, form.AuthorName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// TODO: how to handle website creation success but guestbook creation failure?
 | 
			
		||||
	guestbookShortID := app.createShortId()
 | 
			
		||||
	_, err = app.guestbooks.Insert(guestbookShortID, userId, websiteId)
 | 
			
		||||
	_, err = app.websites.Insert(websiteShortID, userId, form.Name, form.SiteUrl, form.AuthorName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		app.serverError(w, r, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	app.sessionManager.Put(r.Context(), "flash", "Website successfully registered!")
 | 
			
		||||
	if r.Header.Get("HX-Request") == "true" {
 | 
			
		||||
		w.Header().Add("HX-Trigger", "newWebsite")
 | 
			
		||||
		views.WebsiteCreateButton().Render(r.Context(), w)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	http.Redirect(w, r, fmt.Sprintf("/websites/%s", shortIdToSlug(websiteShortID)), http.StatusSeeOther)
 | 
			
		||||
	http.Redirect(w, r, fmt.Sprintf("/websites/%s/dashboard", shortIdToSlug(websiteShortID)), http.StatusSeeOther)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) getWebsiteDashboard(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
@ -88,7 +76,8 @@ func (app *application) getWebsiteList(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if r.Header.Get("HX-Request") == "true" {
 | 
			
		||||
		w.Header().Add("HX-Trigger", "newWebsite")
 | 
			
		||||
		views.HxWebsiteList(websites)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	data := app.newCommonData(r)
 | 
			
		||||
	views.WebsiteList("My Websites", data, websites).Render(r.Context(), w)
 | 
			
		||||
 | 
			
		||||
@ -23,8 +23,7 @@ func (app *application) serverError(w http.ResponseWriter, r *http.Request, err
 | 
			
		||||
 | 
			
		||||
	app.logger.Error(err.Error(), "method", method, "uri", uri)
 | 
			
		||||
	if app.debug {
 | 
			
		||||
		http.Error(w, string(debug.Stack()), http.StatusInternalServerError)
 | 
			
		||||
		app.logger.Error(err.Error(), "method", method, "uri", uri, "stack", string(debug.Stack()))
 | 
			
		||||
		http.Error(w, err.Error()+"\n"+string(debug.Stack()), http.StatusInternalServerError)
 | 
			
		||||
	}
 | 
			
		||||
	http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
 | 
			
		||||
}
 | 
			
		||||
@ -112,3 +111,19 @@ func (app *application) newCommonData(r *http.Request) views.CommonData {
 | 
			
		||||
		IsHtmx:          r.Header.Get("Hx-Request") == "true",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DefaultUserSettings() models.UserSettings {
 | 
			
		||||
	return models.UserSettings{
 | 
			
		||||
		LocalTimezone: time.Now().UTC().Location(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (app *application) durationToTime(duration string) (time.Time, error) {
 | 
			
		||||
	var result time.Time
 | 
			
		||||
	offset, err := time.ParseDuration(duration)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return result, nil
 | 
			
		||||
	}
 | 
			
		||||
	result = time.Now().UTC().Add(offset)
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,9 @@ import (
 | 
			
		||||
	"log/slog"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
	"unicode"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
	"github.com/alexedwards/scs/sqlite3store"
 | 
			
		||||
@ -20,12 +22,12 @@ type application struct {
 | 
			
		||||
	sequence          uint16
 | 
			
		||||
	logger            *slog.Logger
 | 
			
		||||
	websites          *models.WebsiteModel
 | 
			
		||||
	guestbooks        *models.GuestbookModel
 | 
			
		||||
	users             *models.UserModel
 | 
			
		||||
	guestbookComments *models.GuestbookCommentModel
 | 
			
		||||
	sessionManager    *scs.SessionManager
 | 
			
		||||
	formDecoder       *schema.Decoder
 | 
			
		||||
	debug             bool
 | 
			
		||||
	timezones         []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
@ -55,11 +57,22 @@ func main() {
 | 
			
		||||
		logger:            logger,
 | 
			
		||||
		sessionManager:    sessionManager,
 | 
			
		||||
		websites:          &models.WebsiteModel{DB: db},
 | 
			
		||||
		guestbooks:        &models.GuestbookModel{DB: db},
 | 
			
		||||
		users:             &models.UserModel{DB: db},
 | 
			
		||||
		users:             &models.UserModel{DB: db, Settings: make(map[string]models.Setting)},
 | 
			
		||||
		guestbookComments: &models.GuestbookCommentModel{DB: db},
 | 
			
		||||
		formDecoder:       formDecoder,
 | 
			
		||||
		debug:             *debug,
 | 
			
		||||
		timezones:         getAvailableTimezones(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = app.users.InitializeSettingsMap()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Error(err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	err = app.websites.InitializeSettingsMap()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Error(err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tlsConfig := &tls.Config{
 | 
			
		||||
@ -97,3 +110,50 @@ func openDB(dsn string) (*sql.DB, error) {
 | 
			
		||||
	}
 | 
			
		||||
	return db, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getAvailableTimezones() []string {
 | 
			
		||||
	var zones []string
 | 
			
		||||
	var zoneDirs = []string{
 | 
			
		||||
		"/usr/share/zoneinfo/",
 | 
			
		||||
		"/usr/share/lib/zoneinfo/",
 | 
			
		||||
		"/usr/lib/locale/TZ/",
 | 
			
		||||
	}
 | 
			
		||||
	for _, zd := range zoneDirs {
 | 
			
		||||
		zones = walkTzDir(zd, zones)
 | 
			
		||||
		for idx, zone := range zones {
 | 
			
		||||
			zones[idx] = strings.ReplaceAll(zone, zd+"/", "")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return zones
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func walkTzDir(path string, zones []string) []string {
 | 
			
		||||
	fileInfos, err := os.ReadDir(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return zones
 | 
			
		||||
	}
 | 
			
		||||
	isAlpha := func(s string) bool {
 | 
			
		||||
		for _, r := range s {
 | 
			
		||||
			if !unicode.IsLetter(r) {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, info := range fileInfos {
 | 
			
		||||
		if info.Name() != strings.ToUpper(info.Name()[:1])+info.Name()[1:] {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if !isAlpha(info.Name()[:1]) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		newPath := path + "/" + info.Name()
 | 
			
		||||
		if info.IsDir() {
 | 
			
		||||
			zones = walkTzDir(newPath, zones)
 | 
			
		||||
		} else {
 | 
			
		||||
			zones = append(zones, newPath)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return zones
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,8 @@ func (app *application) routes() http.Handler {
 | 
			
		||||
	// mux.Handle("GET /users", protected.ThenFunc(app.getUsersList))
 | 
			
		||||
	mux.Handle("GET /users/{id}", protected.ThenFunc(app.getUser))
 | 
			
		||||
	mux.Handle("POST /users/logout", protected.ThenFunc(app.postUserLogout))
 | 
			
		||||
	mux.Handle("GET /users/settings", protected.ThenFunc(app.notImplemented))
 | 
			
		||||
	mux.Handle("GET /users/settings", protected.ThenFunc(app.getUserSettings))
 | 
			
		||||
	mux.Handle("PUT /users/settings", protected.ThenFunc(app.putUserSettings))
 | 
			
		||||
	mux.Handle("GET /users/privacy", protected.ThenFunc(app.notImplemented))
 | 
			
		||||
	mux.Handle("GET /guestbooks", protected.ThenFunc(app.getAllGuestbooks))
 | 
			
		||||
 | 
			
		||||
@ -40,7 +41,8 @@ func (app *application) routes() http.Handler {
 | 
			
		||||
	mux.Handle("GET /websites/{id}/dashboard/guestbook/comments/queue", protected.ThenFunc(app.getCommentQueue))
 | 
			
		||||
	mux.Handle("DELETE /websites/{id}/dashboard/guestbook/comments/{commentId}", protected.ThenFunc(app.deleteGuestbookComment))
 | 
			
		||||
	mux.Handle("PUT /websites/{id}/dashboard/guestbook/comments/{commentId}", protected.ThenFunc(app.putHideGuestbookComment))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/dashboard/guestbook/blocklist", protected.ThenFunc(app.getComingSoon))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/dashboard/guestbook/settings", protected.ThenFunc(app.getGuestbookSettings))
 | 
			
		||||
	mux.Handle("PUT /websites/{id}/dashboard/guestbook/settings", protected.ThenFunc(app.putGuestbookSettings))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/dashboard/guestbook/comments/trash", protected.ThenFunc(app.getCommentTrash))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/dashboard/guestbook/themes", protected.ThenFunc(app.getComingSoon))
 | 
			
		||||
	mux.Handle("GET /websites/{id}/dashboard/guestbook/customize", protected.ThenFunc(app.getComingSoon))
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										76
									
								
								db/create-settings-tables.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								db/create-settings-tables.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,76 @@
 | 
			
		||||
CREATE TABLE setting_groups (
 | 
			
		||||
    Id integer primary key autoincrement,
 | 
			
		||||
    Description varchar(256) NOT NULL
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE setting_data_types (
 | 
			
		||||
    Id integer primary key autoincrement,
 | 
			
		||||
    Description varchar(64) NOT NULL
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE settings (
 | 
			
		||||
    Id integer primary key autoincrement,
 | 
			
		||||
    Description varchar(256) NOT NULL,
 | 
			
		||||
    Constrained boolean NOT NULL,
 | 
			
		||||
    DataType integer NOT NULL,
 | 
			
		||||
    SettingGroup int NOT NULL,
 | 
			
		||||
    MinValue varchar(6),
 | 
			
		||||
    MaxValue varchar(6),
 | 
			
		||||
    FOREIGN KEY (DataType) REFERENCES setting_data_types(Id)
 | 
			
		||||
        ON DELETE RESTRICT
 | 
			
		||||
        ON UPDATE RESTRICT,
 | 
			
		||||
    FOREIGN KEY (SettingGroup) REFERENCES setting_groups(Id)
 | 
			
		||||
        ON DELETE RESTRICT
 | 
			
		||||
        ON UPDATE RESTRICT
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE allowed_setting_values (
 | 
			
		||||
    Id integer primary key autoincrement,
 | 
			
		||||
    SettingId integer NOT NULL,
 | 
			
		||||
    ItemValue varchar(256),
 | 
			
		||||
    Caption varchar(256),
 | 
			
		||||
    FOREIGN KEY (SettingId) REFERENCES settings(Id)
 | 
			
		||||
        ON DELETE RESTRICT
 | 
			
		||||
        ON UPDATE RESTRICT
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE user_settings (
 | 
			
		||||
    Id integer primary key autoincrement,
 | 
			
		||||
    UserId integer NOT NULL,
 | 
			
		||||
    SettingId integer NOT NULL,
 | 
			
		||||
    AllowedSettingValueId integer,
 | 
			
		||||
    UnconstrainedValue varchar(256),
 | 
			
		||||
    FOREIGN KEY (UserId) REFERENCES users(Id)
 | 
			
		||||
        ON DELETE RESTRICT
 | 
			
		||||
        ON UPDATE RESTRICT,
 | 
			
		||||
    FOREIGN KEY (SettingId) REFERENCES settings(Id)
 | 
			
		||||
        ON DELETE RESTRICT
 | 
			
		||||
        ON UPDATE RESTRICT,
 | 
			
		||||
    FOREIGN KEY (AllowedSettingValueId) REFERENCES allowed_setting_values(Id)
 | 
			
		||||
        ON DELETE RESTRICT
 | 
			
		||||
        ON UPDATE RESTRICT
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE guestbook_settings (
 | 
			
		||||
    Id integer primary key autoincrement,
 | 
			
		||||
    GuestbookId integer NOT NULL,
 | 
			
		||||
    SettingId integer NOT NULL,
 | 
			
		||||
    AllowedSettingValueId integer,
 | 
			
		||||
    UnconstrainedValue varchar(256),
 | 
			
		||||
    FOREIGN KEY (GuestbookId) REFERENCES guestbooks(Id)
 | 
			
		||||
        ON DELETE RESTRICT
 | 
			
		||||
        ON UPDATE RESTRICT,
 | 
			
		||||
    FOREIGN KEY (SettingId) REFERENCES settings(Id)
 | 
			
		||||
        ON DELETE RESTRICT
 | 
			
		||||
        ON UPDATE RESTRICT,
 | 
			
		||||
    FOREIGN KEY (AllowedSettingValueId) REFERENCES allowed_setting_values(Id)
 | 
			
		||||
        ON DELETE RESTRICT
 | 
			
		||||
        ON UPDATE RESTRICT
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
INSERT INTO setting_groups (Description) VALUES ('guestbook');
 | 
			
		||||
INSERT INTO setting_groups (Description) VALUES ('user');
 | 
			
		||||
 | 
			
		||||
INSERT INTO setting_data_types (Description) VALUES ('alphanumeric');
 | 
			
		||||
INSERT INTO setting_data_types (Description) VALUES ('integer');
 | 
			
		||||
INSERT INTO setting_data_types (Description) VALUES ('datetime');
 | 
			
		||||
							
								
								
									
										9
									
								
								db/create-settings.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								db/create-settings.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
INSERT INTO setting_groups (Description) VALUES ("guestbook");
 | 
			
		||||
INSERT INTO setting_groups (Description) VALUES ("user");
 | 
			
		||||
 | 
			
		||||
INSERT INTO setting_data_types (Description) VALUES ("alphanumeric")
 | 
			
		||||
INSERT INTO setting_data_types (Description) VALUES ("integer")
 | 
			
		||||
INSERT INTO setting_data_types (Description) VALUES ("datetime")
 | 
			
		||||
 | 
			
		||||
INSERT INTO settings (Description, Constrained, DataType, SettingGroup)
 | 
			
		||||
VALUES ("Local Timezone", 0, 1, 1);
 | 
			
		||||
@ -29,3 +29,15 @@ type WebsiteCreateForm struct {
 | 
			
		||||
	AuthorName          string `schema:"authorname"`
 | 
			
		||||
	validator.Validator `schema:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserSettingsForm struct {
 | 
			
		||||
	LocalTimezone       string `schema:"timezones"`
 | 
			
		||||
	validator.Validator `schema:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GuestbookSettingsForm struct {
 | 
			
		||||
	Visibility          string `schema:"gb_visible"`
 | 
			
		||||
	CommentingEnabled   string `schema:"gb_commenting"`
 | 
			
		||||
	WidgetsEnabled      string `schema:"gb_remote"`
 | 
			
		||||
	validator.Validator `schema:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,9 +3,11 @@ package models
 | 
			
		||||
import "errors"
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
    ErrNoRecord = errors.New("models: no matching record found")
 | 
			
		||||
	ErrNoRecord = errors.New("models: no matching record found")
 | 
			
		||||
 | 
			
		||||
    ErrInvalidCredentials = errors.New("models: invalid credentials")
 | 
			
		||||
	ErrInvalidCredentials = errors.New("models: invalid credentials")
 | 
			
		||||
 | 
			
		||||
    ErrDuplicateEmail = errors.New("models: duplicate email")
 | 
			
		||||
	ErrDuplicateEmail = errors.New("models: duplicate email")
 | 
			
		||||
 | 
			
		||||
	ErrInvalidSettingValue = errors.New("models: invalid setting value")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -1,72 +0,0 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Guestbook struct {
 | 
			
		||||
	ID        int64
 | 
			
		||||
	ShortId   uint64
 | 
			
		||||
	UserId    int64
 | 
			
		||||
	WebsiteId int64
 | 
			
		||||
	Created   time.Time
 | 
			
		||||
	Deleted   time.Time
 | 
			
		||||
	IsActive  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GuestbookModel struct {
 | 
			
		||||
	DB *sql.DB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookModel) Insert(shortId uint64, userId int64, websiteId int64) (int64, error) {
 | 
			
		||||
	stmt := `INSERT INTO guestbooks (ShortId, UserId, WebsiteId, Created, IsActive)
 | 
			
		||||
    VALUES(?, ?, ?, ?, TRUE)`
 | 
			
		||||
	result, err := m.DB.Exec(stmt, shortId, userId, websiteId, time.Now().UTC())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
	id, err := result.LastInsertId()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
	return id, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookModel) Get(shortId uint64) (Guestbook, error) {
 | 
			
		||||
	stmt := `SELECT Id, ShortId, UserId, WebsiteId, Created, Deleted, IsActive FROM guestbooks
 | 
			
		||||
    WHERE ShortId = ?`
 | 
			
		||||
	row := m.DB.QueryRow(stmt, shortId)
 | 
			
		||||
	var g Guestbook
 | 
			
		||||
	var t sql.NullTime
 | 
			
		||||
	err := row.Scan(&g.ID, &g.ShortId, &g.UserId, &g.WebsiteId, &g.Created, &t, &g.IsActive)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Guestbook{}, err
 | 
			
		||||
	}
 | 
			
		||||
	if t.Valid {
 | 
			
		||||
		g.Deleted = t.Time
 | 
			
		||||
	}
 | 
			
		||||
	return g, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookModel) GetAll(userId int64) ([]Guestbook, error) {
 | 
			
		||||
	stmt := `SELECT Id, ShortId, UserId, WebsiteId, Created, IsActive FROM guestbooks
 | 
			
		||||
    WHERE UserId = ? AND DELETED IS NULL`
 | 
			
		||||
	rows, err := m.DB.Query(stmt, userId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var guestbooks []Guestbook
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		var g Guestbook
 | 
			
		||||
		err = rows.Scan(&g.ID, &g.ShortId, &g.UserId, &g.WebsiteId, &g.Created, &g.IsActive)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		guestbooks = append(guestbooks, g)
 | 
			
		||||
	}
 | 
			
		||||
	if err = rows.Err(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return guestbooks, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										154
									
								
								internal/models/settings.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								internal/models/settings.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,154 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SettingGroup struct {
 | 
			
		||||
	id          int
 | 
			
		||||
	description string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *SettingGroup) Id() int {
 | 
			
		||||
	return g.id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *SettingGroup) Description() string {
 | 
			
		||||
	return g.description
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	SETTING_GROUP_USER      = "user"
 | 
			
		||||
	SETTING_GROUP_GUESTBOOK = "guestbook"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SettingDataType struct {
 | 
			
		||||
	id          int
 | 
			
		||||
	description string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *SettingDataType) Id() int {
 | 
			
		||||
	return d.id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *SettingDataType) Description() string {
 | 
			
		||||
	return d.description
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	SETTING_TYPE_INTEGER = "integer"
 | 
			
		||||
	SETTING_TYPE_STRING  = "alphanumeric"
 | 
			
		||||
	SETTING_TYPE_DATE    = "datetime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Setting struct {
 | 
			
		||||
	id           int
 | 
			
		||||
	description  string
 | 
			
		||||
	constrained  bool
 | 
			
		||||
	dataType     SettingDataType
 | 
			
		||||
	settingGroup SettingGroup
 | 
			
		||||
	minValue     string
 | 
			
		||||
	maxValue     string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) Id() int {
 | 
			
		||||
	return s.id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) Description() string {
 | 
			
		||||
	return s.description
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) Constrained() bool {
 | 
			
		||||
	return s.constrained
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) DataType() SettingDataType {
 | 
			
		||||
	return s.dataType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) SettingGroup() SettingGroup {
 | 
			
		||||
	return s.settingGroup
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) MinValue() string {
 | 
			
		||||
	return s.minValue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) MaxValue() string {
 | 
			
		||||
	return s.maxValue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) Validate(value string) bool {
 | 
			
		||||
	switch s.dataType.description {
 | 
			
		||||
	case SETTING_TYPE_INTEGER:
 | 
			
		||||
		return s.validateInt(value)
 | 
			
		||||
	case SETTING_TYPE_STRING:
 | 
			
		||||
		return s.validateAlphanum(value)
 | 
			
		||||
	case SETTING_TYPE_DATE:
 | 
			
		||||
		return s.validateDatetime(value)
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) validateInt(value string) bool {
 | 
			
		||||
	v, err := strconv.ParseInt(value, 10, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	var min int64
 | 
			
		||||
	var max int64
 | 
			
		||||
	if len(s.minValue) > 0 {
 | 
			
		||||
		min, err = strconv.ParseInt(s.minValue, 10, 0)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		if v < min {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(s.maxValue) > 0 {
 | 
			
		||||
		max, err = strconv.ParseInt(s.maxValue, 10, 0)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		if v < max {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) validateDatetime(value string) bool {
 | 
			
		||||
	v, err := time.Parse(time.RFC3339, value)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	var min time.Time
 | 
			
		||||
	var max time.Time
 | 
			
		||||
 | 
			
		||||
	if len(s.minValue) > 0 {
 | 
			
		||||
		min, err = time.Parse(time.RFC3339, s.minValue)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		if v.Before(min) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(s.maxValue) > 0 {
 | 
			
		||||
		max, err = time.Parse(time.RFC3339, s.maxValue)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		if v.After(max) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Setting) validateAlphanum(value string) bool {
 | 
			
		||||
	return len(value) >= 0
 | 
			
		||||
}
 | 
			
		||||
@ -10,6 +10,14 @@ import (
 | 
			
		||||
	"golang.org/x/crypto/bcrypt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UserSettings struct {
 | 
			
		||||
	LocalTimezone *time.Location
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	SettingUserTimezone = "local_timezone"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type User struct {
 | 
			
		||||
	ID             int64
 | 
			
		||||
	ShortId        uint64
 | 
			
		||||
@ -19,20 +27,54 @@ type User struct {
 | 
			
		||||
	IsBanned       bool
 | 
			
		||||
	HashedPassword []byte
 | 
			
		||||
	Created        time.Time
 | 
			
		||||
	Settings       UserSettings
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserModel struct {
 | 
			
		||||
	DB *sql.DB
 | 
			
		||||
	DB       *sql.DB
 | 
			
		||||
	Settings map[string]Setting
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) Insert(shortId uint64, username string, email string, password string) error {
 | 
			
		||||
func (m *UserModel) InitializeSettingsMap() error {
 | 
			
		||||
	if m.Settings == nil {
 | 
			
		||||
		m.Settings = make(map[string]Setting)
 | 
			
		||||
	}
 | 
			
		||||
	stmt := `SELECT settings.Id, settings.Description, Constrained, d.Id, d.Description, g.Id, g.Description, MinValue, MaxValue
 | 
			
		||||
        FROM settings
 | 
			
		||||
        LEFT JOIN setting_data_types d ON settings.DataType = d.Id
 | 
			
		||||
        LEFT JOIN setting_groups g ON settings.SettingGroup = g.Id
 | 
			
		||||
        WHERE SettingGroup = (SELECT Id FROM setting_groups WHERE Description = 'user' LIMIT 1)`
 | 
			
		||||
	result, err := m.DB.Query(stmt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for result.Next() {
 | 
			
		||||
		var s Setting
 | 
			
		||||
		var mn sql.NullString
 | 
			
		||||
		var mx sql.NullString
 | 
			
		||||
		err := result.Scan(&s.id, &s.description, &s.constrained, &s.dataType.id, &s.dataType.description, &s.settingGroup.id, &s.settingGroup.description, &mn, &mx)
 | 
			
		||||
		if mn.Valid {
 | 
			
		||||
			s.minValue = mn.String
 | 
			
		||||
		}
 | 
			
		||||
		if mx.Valid {
 | 
			
		||||
			s.maxValue = mx.String
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		m.Settings[s.description] = s
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) Insert(shortId uint64, username string, email string, password string, settings UserSettings) error {
 | 
			
		||||
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	stmt := `INSERT INTO users (ShortId, Username, Email, IsBanned, HashedPassword, Created)
 | 
			
		||||
    VALUES (?, ?, ?, FALSE, ?, ?)`
 | 
			
		||||
	_, err = m.DB.Exec(stmt, shortId, username, email, hashedPassword, time.Now().UTC())
 | 
			
		||||
	result, err := m.DB.Exec(stmt, shortId, username, email, hashedPassword, time.Now().UTC())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if sqliteError, ok := err.(sqlite3.Error); ok {
 | 
			
		||||
			if sqliteError.ExtendedCode == 2067 && strings.Contains(sqliteError.Error(), "Email") {
 | 
			
		||||
@ -41,6 +83,14 @@ func (m *UserModel) Insert(shortId uint64, username string, email string, passwo
 | 
			
		||||
		}
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	id, err := result.LastInsertId()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = m.initializeUserSettings(id, settings)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -55,6 +105,11 @@ func (m *UserModel) Get(id uint64) (User, error) {
 | 
			
		||||
		}
 | 
			
		||||
		return User{}, err
 | 
			
		||||
	}
 | 
			
		||||
	settings, err := m.GetSettings(u.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return u, err
 | 
			
		||||
	}
 | 
			
		||||
	u.Settings = settings
 | 
			
		||||
	return u, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -69,6 +124,11 @@ func (m *UserModel) GetById(id int64) (User, error) {
 | 
			
		||||
		}
 | 
			
		||||
		return User{}, err
 | 
			
		||||
	}
 | 
			
		||||
	settings, err := m.GetSettings(u.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return u, err
 | 
			
		||||
	}
 | 
			
		||||
	u.Settings = settings
 | 
			
		||||
	return u, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -125,3 +185,75 @@ func (m *UserModel) Exists(id int64) (bool, error) {
 | 
			
		||||
	err := m.DB.QueryRow(stmt, id).Scan(&exists)
 | 
			
		||||
	return exists, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) GetSettings(userId int64) (UserSettings, error) {
 | 
			
		||||
	stmt := `SELECT u.SettingId, a.ItemValue, u.UnconstrainedValue FROM user_settings AS u
 | 
			
		||||
			LEFT JOIN allowed_setting_values AS a ON u.AllowedSettingValueId = a.Id
 | 
			
		||||
			WHERE UserId = ?`
 | 
			
		||||
	var settings UserSettings
 | 
			
		||||
	rows, err := m.DB.Query(stmt, userId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return settings, err
 | 
			
		||||
	}
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		var id int
 | 
			
		||||
		var itemValue sql.NullString
 | 
			
		||||
		var unconstrainedValue sql.NullString
 | 
			
		||||
		err = rows.Scan(&id, &itemValue, &unconstrainedValue)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return settings, err
 | 
			
		||||
		}
 | 
			
		||||
		switch id {
 | 
			
		||||
		case m.Settings[SettingUserTimezone].id:
 | 
			
		||||
			settings.LocalTimezone, err = time.LoadLocation(unconstrainedValue.String)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic(err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return settings, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) initializeUserSettings(userId int64, settings UserSettings) error {
 | 
			
		||||
	stmt := `INSERT INTO user_settings (UserId, SettingId, AllowedSettingValueId, UnconstrainedValue) 
 | 
			
		||||
		VALUES (?, ?, ?, ?)`
 | 
			
		||||
	_, err := m.DB.Exec(stmt, userId, m.Settings[SettingUserTimezone].id, nil, settings.LocalTimezone.String())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) UpdateUserSettings(userId int64, settings UserSettings) error {
 | 
			
		||||
	err := m.UpdateSetting(userId, m.Settings[SettingUserTimezone], settings.LocalTimezone.String())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) UpdateSetting(userId int64, setting Setting, value string) error {
 | 
			
		||||
	valid := setting.Validate(value)
 | 
			
		||||
	if !valid {
 | 
			
		||||
		return ErrInvalidSettingValue
 | 
			
		||||
	}
 | 
			
		||||
	stmt := `UPDATE user_settings SET
 | 
			
		||||
				AllowedSettingValueId=IFNULL(
 | 
			
		||||
					(SELECT Id FROM allowed_setting_values WHERE SettingId = user_settings.SettingId AND ItemValue = ?), AllowedSettingValueId
 | 
			
		||||
				),
 | 
			
		||||
				UnconstrainedValue=(SELECT ? FROM settings WHERE settings.Id = user_settings.SettingId AND settings.Constrained=0)
 | 
			
		||||
			WHERE userId = ?
 | 
			
		||||
			AND SettingId = (SELECT Id from Settings WHERE Description=?);`
 | 
			
		||||
	result, err := m.DB.Exec(stmt, value, value, userId, setting.description)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	rows, err := result.RowsAffected()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if rows != 1 {
 | 
			
		||||
		return ErrInvalidSettingValue
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,8 @@ package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -17,51 +19,206 @@ type Website struct {
 | 
			
		||||
	Guestbook  Guestbook
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GuestbookSettings struct {
 | 
			
		||||
	IsCommentingEnabled   bool
 | 
			
		||||
	ReenableCommenting    time.Time
 | 
			
		||||
	IsVisible             bool
 | 
			
		||||
	FilteredWords         []string
 | 
			
		||||
	AllowRemoteHostAccess bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var ValidDisableDurations = []string{"true", "false", "1h", "4h", "8h", "24h", "72h", "168h"}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	SettingGbCommentingEnabled = "commenting_enabled"
 | 
			
		||||
	SettingGbReenableComments  = "reenable_comments"
 | 
			
		||||
	SettingGbVisible           = "is_visible"
 | 
			
		||||
	SettingGbFilteredWords     = "filtered_words"
 | 
			
		||||
	SettingGbAllowRemote       = "remote_enabled"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Guestbook struct {
 | 
			
		||||
	ID        int64
 | 
			
		||||
	ShortId   uint64
 | 
			
		||||
	UserId    int64
 | 
			
		||||
	WebsiteId int64
 | 
			
		||||
	Created   time.Time
 | 
			
		||||
	Deleted   time.Time
 | 
			
		||||
	IsActive  bool
 | 
			
		||||
	Settings  GuestbookSettings
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g Guestbook) CanComment() bool {
 | 
			
		||||
	now := time.Now().UTC()
 | 
			
		||||
	return g.Settings.IsCommentingEnabled && g.Settings.ReenableCommenting.Before(now)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type WebsiteModel struct {
 | 
			
		||||
	DB *sql.DB
 | 
			
		||||
	DB       *sql.DB
 | 
			
		||||
	Settings map[string]Setting
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) InitializeSettingsMap() error {
 | 
			
		||||
	if m.Settings == nil {
 | 
			
		||||
		m.Settings = make(map[string]Setting)
 | 
			
		||||
	}
 | 
			
		||||
	stmt := `SELECT settings.Id, settings.Description, Constrained, d.Id, d.Description, g.Id, g.Description, MinValue, MaxValue
 | 
			
		||||
        FROM settings
 | 
			
		||||
        LEFT JOIN setting_data_types d ON settings.DataType = d.Id
 | 
			
		||||
        LEFT JOIN setting_groups g ON settings.SettingGroup = g.Id
 | 
			
		||||
        WHERE SettingGroup = (SELECT Id FROM setting_groups WHERE Description = 'guestbook' LIMIT 1)`
 | 
			
		||||
	result, err := m.DB.Query(stmt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for result.Next() {
 | 
			
		||||
		var s Setting
 | 
			
		||||
		var mn sql.NullString
 | 
			
		||||
		var mx sql.NullString
 | 
			
		||||
		err := result.Scan(&s.id, &s.description, &s.constrained, &s.dataType.id, &s.dataType.description, &s.settingGroup.id, &s.settingGroup.description, &mn, &mx)
 | 
			
		||||
		if mn.Valid {
 | 
			
		||||
			s.minValue = mn.String
 | 
			
		||||
		}
 | 
			
		||||
		if mx.Valid {
 | 
			
		||||
			s.maxValue = mx.String
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		m.Settings[s.description] = s
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) Insert(shortId uint64, userId int64, siteName, siteUrl, authorName string) (int64, error) {
 | 
			
		||||
	stmt := `INSERT INTO websites (ShortId, Name, SiteUrl, AuthorName, UserId, Created)
 | 
			
		||||
			VALUES (?, ?, ?, ?, ?, ?)`
 | 
			
		||||
	result, err := m.DB.Exec(stmt, shortId, siteName, siteUrl, authorName, userId, time.Now().UTC())
 | 
			
		||||
	tx, err := m.DB.Begin()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
	id, err := result.LastInsertId()
 | 
			
		||||
 | 
			
		||||
	result, err := tx.Exec(stmt, shortId, siteName, siteUrl, authorName, userId, time.Now().UTC())
 | 
			
		||||
	// result, err := m.DB.Exec(stmt, shortId, siteName, siteUrl, authorName, userId, time.Now().UTC())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if rollbackError := tx.Rollback(); rollbackError != nil {
 | 
			
		||||
			return -1, err
 | 
			
		||||
		}
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
	return id, nil
 | 
			
		||||
	websiteId, err := result.LastInsertId()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if rollbackError := tx.Rollback(); rollbackError != nil {
 | 
			
		||||
			return -1, err
 | 
			
		||||
		}
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stmt = `INSERT INTO guestbooks (ShortId, UserId, WebsiteId, Created, IsActive)
 | 
			
		||||
    VALUES(?, ?, ?, ?, TRUE)`
 | 
			
		||||
	result, err = tx.Exec(stmt, shortId, userId, websiteId, time.Now().UTC())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if rollbackError := tx.Rollback(); rollbackError != nil {
 | 
			
		||||
			return -1, err
 | 
			
		||||
		}
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
	guestbookId, err := result.LastInsertId()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if rollbackError := tx.Rollback(); rollbackError != nil {
 | 
			
		||||
			return -1, err
 | 
			
		||||
		}
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	settings := GuestbookSettings{
 | 
			
		||||
		IsCommentingEnabled:   true,
 | 
			
		||||
		IsVisible:             true,
 | 
			
		||||
		AllowRemoteHostAccess: true,
 | 
			
		||||
	}
 | 
			
		||||
	stmt = `INSERT INTO guestbook_settings (GuestbookId, SettingId, AllowedSettingValueId, UnconstrainedValue) VALUES 
 | 
			
		||||
	(?, ?, ?, ?),
 | 
			
		||||
	(?, ?, ?, ?),
 | 
			
		||||
	(?, ?, ?, ?),
 | 
			
		||||
	(?, ?, ?, ?)`
 | 
			
		||||
	_, err = tx.Exec(stmt,
 | 
			
		||||
		guestbookId, m.Settings[SettingGbCommentingEnabled].id, settings.IsCommentingEnabled, nil,
 | 
			
		||||
		guestbookId, m.Settings[SettingGbReenableComments].id, nil, settings.ReenableCommenting.Format(time.RFC3339),
 | 
			
		||||
		guestbookId, m.Settings[SettingGbVisible].id, settings.IsVisible, nil,
 | 
			
		||||
		guestbookId, m.Settings[SettingGbAllowRemote].id, settings.AllowRemoteHostAccess, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if rollbackError := tx.Rollback(); rollbackError != nil {
 | 
			
		||||
			return -1, err
 | 
			
		||||
		}
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := tx.Commit(); err != nil {
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
	return websiteId, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) Get(shortId uint64) (Website, error) {
 | 
			
		||||
	stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created,
 | 
			
		||||
	g.Id, g.ShortId, g.Created, g.IsActive
 | 
			
		||||
	FROM websites AS w INNER JOIN guestbooks AS g ON w.Id = g.WebsiteId
 | 
			
		||||
	stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created
 | 
			
		||||
	FROM websites AS w
 | 
			
		||||
	WHERE w.ShortId = ? AND w.DELETED IS NULL`
 | 
			
		||||
	row := m.DB.QueryRow(stmt, shortId)
 | 
			
		||||
	var w Website
 | 
			
		||||
	err := row.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created,
 | 
			
		||||
		&w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive)
 | 
			
		||||
	tx, err := m.DB.Begin()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Website{}, nil
 | 
			
		||||
	}
 | 
			
		||||
	row := tx.QueryRow(stmt, shortId)
 | 
			
		||||
	var w Website
 | 
			
		||||
	err = row.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, sql.ErrNoRows) {
 | 
			
		||||
			err = ErrNoRecord
 | 
			
		||||
		}
 | 
			
		||||
		if rollbackErr := tx.Rollback(); rollbackErr != nil {
 | 
			
		||||
			return Website{}, err
 | 
			
		||||
		}
 | 
			
		||||
		return Website{}, err
 | 
			
		||||
	}
 | 
			
		||||
	return w, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) GetById(id int64) (Website, error) {
 | 
			
		||||
	stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created,
 | 
			
		||||
	g.Id, g.ShortId, g.Created, g.IsActive
 | 
			
		||||
	FROM websites AS w INNER JOIN guestbooks AS g ON w.Id = g.WebsiteId
 | 
			
		||||
	WHERE w.Id = ? AND w.DELETED IS NULL`
 | 
			
		||||
	row := m.DB.QueryRow(stmt, id)
 | 
			
		||||
	var w Website
 | 
			
		||||
	err := row.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created,
 | 
			
		||||
		&w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive)
 | 
			
		||||
	stmt = `SELECT Id, ShortId, UserId, WebsiteId, Created, IsActive FROM guestbooks
 | 
			
		||||
    WHERE WebsiteId = ? AND Deleted IS NULL`
 | 
			
		||||
	row = tx.QueryRow(stmt, w.ID)
 | 
			
		||||
	var g Guestbook
 | 
			
		||||
	err = row.Scan(&g.ID, &g.ShortId, &g.UserId, &g.WebsiteId, &g.Created, &g.IsActive)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, sql.ErrNoRows) {
 | 
			
		||||
			err = ErrNoRecord
 | 
			
		||||
		}
 | 
			
		||||
		if rollbackErr := tx.Rollback(); rollbackErr != nil {
 | 
			
		||||
			return Website{}, err
 | 
			
		||||
		}
 | 
			
		||||
		return Website{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gbSettings, err := m.getGuestbookSettings(tx, g.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if errors.Is(err, sql.ErrNoRows) {
 | 
			
		||||
			err = ErrNoRecord
 | 
			
		||||
		}
 | 
			
		||||
		if rollbackErr := tx.Rollback(); rollbackErr != nil {
 | 
			
		||||
			return Website{}, err
 | 
			
		||||
		}
 | 
			
		||||
		return Website{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = tx.Commit()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Website{}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if comment disable setting has expired, enable commenting
 | 
			
		||||
	commentingReenabled := time.Now().UTC().After(gbSettings.ReenableCommenting)
 | 
			
		||||
	if commentingReenabled {
 | 
			
		||||
		gbSettings.IsCommentingEnabled = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g.Settings = gbSettings
 | 
			
		||||
	w.Guestbook = g
 | 
			
		||||
	return w, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -113,3 +270,92 @@ func (m *WebsiteModel) GetAll() ([]Website, error) {
 | 
			
		||||
	}
 | 
			
		||||
	return websites, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) getGuestbookSettings(tx *sql.Tx, guestbookId int64) (GuestbookSettings, error) {
 | 
			
		||||
	stmt := `SELECT g.SettingId, a.ItemValue, g.UnconstrainedValue FROM guestbook_settings AS g
 | 
			
		||||
			LEFT JOIN allowed_setting_values AS a ON g.AllowedSettingValueId = a.Id
 | 
			
		||||
			WHERE GuestbookId = ?`
 | 
			
		||||
	var settings GuestbookSettings
 | 
			
		||||
	rows, err := tx.Query(stmt, guestbookId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return settings, err
 | 
			
		||||
	}
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		var id int
 | 
			
		||||
		var itemValue sql.NullString
 | 
			
		||||
		var unconstrainedValue sql.NullString
 | 
			
		||||
		err = rows.Scan(&id, &itemValue, &unconstrainedValue)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return settings, err
 | 
			
		||||
		}
 | 
			
		||||
		switch id {
 | 
			
		||||
		case m.Settings[SettingGbCommentingEnabled].id:
 | 
			
		||||
			settings.IsCommentingEnabled, err = strconv.ParseBool(itemValue.String)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return settings, err
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		case m.Settings[SettingGbReenableComments].id:
 | 
			
		||||
			settings.ReenableCommenting, err = time.Parse(time.RFC3339, unconstrainedValue.String)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return settings, err
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		case m.Settings[SettingGbVisible].id:
 | 
			
		||||
			settings.IsVisible, err = strconv.ParseBool(itemValue.String)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return settings, err
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		case m.Settings[SettingGbAllowRemote].id:
 | 
			
		||||
			settings.AllowRemoteHostAccess, err = strconv.ParseBool(itemValue.String)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return settings, err
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return settings, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) UpdateGuestbookSettings(guestbookId int64, settings GuestbookSettings) error {
 | 
			
		||||
	err := m.UpdateSetting(guestbookId, m.Settings[SettingGbVisible], strconv.FormatBool(settings.IsVisible))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = m.UpdateSetting(guestbookId, m.Settings[SettingGbAllowRemote], strconv.FormatBool(settings.AllowRemoteHostAccess))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = m.UpdateSetting(guestbookId, m.Settings[SettingGbCommentingEnabled], strconv.FormatBool(settings.IsCommentingEnabled))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = m.UpdateSetting(guestbookId, m.Settings[SettingGbReenableComments], settings.ReenableCommenting.Format(time.RFC3339))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) UpdateSetting(guestbookId int64, setting Setting, value string) error {
 | 
			
		||||
	stmt := `UPDATE guestbook_settings SET
 | 
			
		||||
				AllowedSettingValueId=IFNULL(
 | 
			
		||||
					(SELECT Id FROM allowed_setting_values WHERE SettingId = guestbook_settings.SettingId AND ItemValue = ?), AllowedSettingValueId
 | 
			
		||||
				), 
 | 
			
		||||
				UnconstrainedValue=(SELECT ? FROM settings WHERE settings.Id = guestbook_settings.SettingId AND settings.Constrained=0)
 | 
			
		||||
			WHERE GuestbookId = ?
 | 
			
		||||
			AND SettingId = (SELECT Id from Settings WHERE Description=?);`
 | 
			
		||||
	result, err := m.DB.Exec(stmt, value, value, guestbookId, setting.description)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	rows, err := result.RowsAffected()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if rows != 1 {
 | 
			
		||||
		return ErrInvalidSettingValue
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,168 +3,229 @@ package views
 | 
			
		||||
import "fmt"
 | 
			
		||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/forms"
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
templ GuestbookDashboardCommentsView(title string, data CommonData, website models.Website, guestbook models.Guestbook, comments []models.GuestbookComment) {
 | 
			
		||||
    @base(title, data) {
 | 
			
		||||
        <div id="dashboard">
 | 
			
		||||
            @wSidebar(website)
 | 
			
		||||
            <div>
 | 
			
		||||
                <h1>Comments on { website.SiteUrl }</h1>
 | 
			
		||||
                <hr>
 | 
			
		||||
                if len(comments) == 0 {
 | 
			
		||||
                    <p>No comments yet!</p>
 | 
			
		||||
                }
 | 
			
		||||
                for  _, c := range comments {
 | 
			
		||||
                    @GuestbookDashboardCommentView(data, website, c)
 | 
			
		||||
                }
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    }
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<div id="dashboard">
 | 
			
		||||
			@wSidebar(website)
 | 
			
		||||
			<div>
 | 
			
		||||
				<h1>Comments on { website.SiteUrl }</h1>
 | 
			
		||||
				<hr/>
 | 
			
		||||
				if len(comments) == 0 {
 | 
			
		||||
					<p>No comments yet!</p>
 | 
			
		||||
				}
 | 
			
		||||
				for  _, c := range comments {
 | 
			
		||||
					@GuestbookDashboardCommentView(data, website, c)
 | 
			
		||||
				}
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ GuestbookDashboardCommentView(data CommonData, w models.Website, c models.GuestbookComment) {
 | 
			
		||||
    {{ commentUrl := fmt.Sprintf("%s/dashboard/guestbook/comments/%s", wUrl(w), shortIdToSlug(c.ShortId)) }}
 | 
			
		||||
    {{ hxHeaders := fmt.Sprintf("{\"X-CSRF-Token\": \"%s\"}", data.CSRFToken) }}
 | 
			
		||||
    <div class="comment">
 | 
			
		||||
        <div>
 | 
			
		||||
            if c.Deleted.IsZero() {
 | 
			
		||||
                <button class="danger" hx-delete={ commentUrl } hx-target="closest div.comment" hx-headers={ hxHeaders }>Delete</button>
 | 
			
		||||
                <button class="outline" hx-put={ commentUrl } hx-target="closest div.comment" hx-headers={ hxHeaders }>
 | 
			
		||||
                    if !c.IsPublished {
 | 
			
		||||
                        Publish
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Hide
 | 
			
		||||
                    }
 | 
			
		||||
                </button> 
 | 
			
		||||
            }
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
            <strong>{ c.AuthorName }</strong>
 | 
			
		||||
            if len(c.AuthorEmail) > 0 {
 | 
			
		||||
                {{ email := "mailto:" + c.AuthorEmail}}
 | 
			
		||||
                | <a href={ templ.URL(email) } target="_blank">{ c.AuthorEmail }</a>
 | 
			
		||||
            }
 | 
			
		||||
            if len(c.AuthorSite) > 0 {
 | 
			
		||||
                | <a href={ templ.URL(externalUrl(c.AuthorSite))} target="_blank">{ c.AuthorSite }</a>
 | 
			
		||||
            }
 | 
			
		||||
            <p>
 | 
			
		||||
                { c.Created.Format("01-02-2006 03:04PM") }
 | 
			
		||||
            </p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <p>
 | 
			
		||||
            { c.CommentText }
 | 
			
		||||
        </p>
 | 
			
		||||
        <hr>
 | 
			
		||||
    </div>
 | 
			
		||||
	{{ commentUrl := fmt.Sprintf("%s/dashboard/guestbook/comments/%s", wUrl(w), shortIdToSlug(c.ShortId)) }}
 | 
			
		||||
	{{ hxHeaders := fmt.Sprintf("{\"X-CSRF-Token\": \"%s\"}", data.CSRFToken) }}
 | 
			
		||||
	<div class="comment">
 | 
			
		||||
		<div>
 | 
			
		||||
			if c.Deleted.IsZero() {
 | 
			
		||||
				<button class="danger" hx-delete={ commentUrl } hx-target="closest div.comment" hx-headers={ hxHeaders }>Delete</button>
 | 
			
		||||
				<button class="outline" hx-put={ commentUrl } hx-target="closest div.comment" hx-headers={ hxHeaders }>
 | 
			
		||||
					if !c.IsPublished {
 | 
			
		||||
						Publish
 | 
			
		||||
					} else {
 | 
			
		||||
						Hide
 | 
			
		||||
					}
 | 
			
		||||
				</button>
 | 
			
		||||
			}
 | 
			
		||||
		</div>
 | 
			
		||||
		<div>
 | 
			
		||||
			<strong>{ c.AuthorName }</strong>
 | 
			
		||||
			if len(c.AuthorEmail) > 0 {
 | 
			
		||||
				{{ email := "mailto:" + c.AuthorEmail }}
 | 
			
		||||
				| <a href={ templ.URL(email) } target="_blank">{ c.AuthorEmail }</a>
 | 
			
		||||
			}
 | 
			
		||||
			if len(c.AuthorSite) > 0 {
 | 
			
		||||
				| <a href={ templ.URL(externalUrl(c.AuthorSite)) } target="_blank">{ c.AuthorSite }</a>
 | 
			
		||||
			}
 | 
			
		||||
			<p>
 | 
			
		||||
				{ c.Created.In(data.CurrentUser.Settings.LocalTimezone).Format("01-02-2006 03:04PM") }
 | 
			
		||||
			</p>
 | 
			
		||||
		</div>
 | 
			
		||||
		<p>
 | 
			
		||||
			{ c.CommentText }
 | 
			
		||||
		</p>
 | 
			
		||||
		<hr/>
 | 
			
		||||
	</div>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ commentForm(form forms.CommentCreateForm) {
 | 
			
		||||
    <div>
 | 
			
		||||
        <label for="authorname">Name: </label>
 | 
			
		||||
        {{ error, exists := form.FieldErrors["authorName"] }}
 | 
			
		||||
        if exists {
 | 
			
		||||
        <label class="error">{ error }</label>
 | 
			
		||||
        }
 | 
			
		||||
        <input type="text" name="authorname" id="authorname">
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        <label for="authoremail">Email: </label>
 | 
			
		||||
        {{ error, exists = form.FieldErrors["authorEmail"] }}
 | 
			
		||||
        if exists {
 | 
			
		||||
        <label class="error">{ error }</label>
 | 
			
		||||
        }
 | 
			
		||||
        <input type="text" name="authoremail" id="authoremail">
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        <label for="authorsite">Site Url: </label>
 | 
			
		||||
        {{ error, exists = form.FieldErrors["authorSite"] }}
 | 
			
		||||
        if exists {
 | 
			
		||||
        <label class="error">{ error }</label>
 | 
			
		||||
        }
 | 
			
		||||
        <input type="text" name="authorsite" id="authorsite">
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        <label for="content">Comment: </label>
 | 
			
		||||
        {{ error, exists = form.FieldErrors["content"] }}
 | 
			
		||||
        if exists {
 | 
			
		||||
        <label class="error">{ error }</label>
 | 
			
		||||
        }
 | 
			
		||||
        <textarea name="content" id="content"></textarea>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        <input type="submit" value="Submit">
 | 
			
		||||
    </div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<label for="authorname">Name: </label>
 | 
			
		||||
		{{ error, exists := form.FieldErrors["authorName"] }}
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ error }</label>
 | 
			
		||||
		}
 | 
			
		||||
		<input type="text" name="authorname" id="authorname"/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<label for="authoremail">Email: </label>
 | 
			
		||||
		{{ error, exists = form.FieldErrors["authorEmail"] }}
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ error }</label>
 | 
			
		||||
		}
 | 
			
		||||
		<input type="text" name="authoremail" id="authoremail"/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<label for="authorsite">Site Url: </label>
 | 
			
		||||
		{{ error, exists = form.FieldErrors["authorSite"] }}
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ error }</label>
 | 
			
		||||
		}
 | 
			
		||||
		<input type="text" name="authorsite" id="authorsite"/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<label for="content">Comment: </label>
 | 
			
		||||
		{{ error, exists = form.FieldErrors["content"] }}
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ error }</label>
 | 
			
		||||
		}
 | 
			
		||||
		<textarea name="content" id="content"></textarea>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<input type="submit" value="Submit"/>
 | 
			
		||||
	</div>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ GuestbookView(title string, data CommonData, website models.Website, guestbook models.Guestbook, comments []models.GuestbookComment, form forms.CommentCreateForm) {
 | 
			
		||||
    {{ postUrl := fmt.Sprintf("/websites/%s/guestbook/comments/create", shortIdToSlug(website.ShortId)) }}
 | 
			
		||||
    if data.IsHtmx {
 | 
			
		||||
        @commentForm(form)
 | 
			
		||||
    } else {
 | 
			
		||||
        <html>
 | 
			
		||||
            <head>
 | 
			
		||||
            <title>{ title }</title>
 | 
			
		||||
            <link href="/static/css/classless.min.css" rel="stylesheet">
 | 
			
		||||
            </head>
 | 
			
		||||
            <body>
 | 
			
		||||
                <main>
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <h1>Guestbook for { website.Name }</h1>
 | 
			
		||||
                        <form action={ templ.URL(postUrl) } method="post">
 | 
			
		||||
                            <input type="hidden" name="csrf_token" value={data.CSRFToken}>
 | 
			
		||||
                            @commentForm(form)
 | 
			
		||||
                        </form>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div id="comments">
 | 
			
		||||
                        if len(comments) == 0 {
 | 
			
		||||
                            <p>No comments yet!</p>
 | 
			
		||||
                        }
 | 
			
		||||
                        for _, c := range comments {
 | 
			
		||||
                            <div>
 | 
			
		||||
                                <h3>{ c.AuthorName }</h3>
 | 
			
		||||
                                { c.Created.Format("01-02-2006 03:04PM") }
 | 
			
		||||
                                <p>
 | 
			
		||||
                                    { c.CommentText }
 | 
			
		||||
                                </p>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        }
 | 
			
		||||
                    </div>
 | 
			
		||||
                    </main>
 | 
			
		||||
                </body>
 | 
			
		||||
            </html>
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
	{{ postUrl := fmt.Sprintf("/websites/%s/guestbook/comments/create", shortIdToSlug(website.ShortId)) }}
 | 
			
		||||
	if data.IsHtmx {
 | 
			
		||||
		@commentForm(form)
 | 
			
		||||
	} else {
 | 
			
		||||
		<html>
 | 
			
		||||
			<head>
 | 
			
		||||
				<title>{ title }</title>
 | 
			
		||||
				<link href="/static/css/classless.min.css" rel="stylesheet"/>
 | 
			
		||||
			</head>
 | 
			
		||||
			<body>
 | 
			
		||||
				<main>
 | 
			
		||||
					<div>
 | 
			
		||||
						<h1>Guestbook for { website.Name }</h1>
 | 
			
		||||
						<form action={ templ.URL(postUrl) } method="post">
 | 
			
		||||
							<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
 | 
			
		||||
							@commentForm(form)
 | 
			
		||||
						</form>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div id="comments">
 | 
			
		||||
						if len(comments) == 0 {
 | 
			
		||||
							<p>No comments yet!</p>
 | 
			
		||||
						}
 | 
			
		||||
						for _, c := range comments {
 | 
			
		||||
							<div>
 | 
			
		||||
								<h3>{ c.AuthorName }</h3>
 | 
			
		||||
								{ c.Created.Format("01-02-2006 03:04PM") }
 | 
			
		||||
								<p>
 | 
			
		||||
									{ c.CommentText }
 | 
			
		||||
								</p>
 | 
			
		||||
							</div>
 | 
			
		||||
						}
 | 
			
		||||
					</div>
 | 
			
		||||
				</main>
 | 
			
		||||
			</body>
 | 
			
		||||
		</html>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    templ CreateGuestbookComment(title string, data CommonData, website models.Website, guestbook models.Guestbook, form forms.CommentCreateForm) {
 | 
			
		||||
        {{ postUrl := fmt.Sprintf("/websites/%s/guestbook/comments/create", shortIdToSlug(website.ShortId)) }}
 | 
			
		||||
        if data.IsHtmx {
 | 
			
		||||
            <form hx-post={ postUrl } hx-target="closest div">
 | 
			
		||||
            @commentForm(form)
 | 
			
		||||
        </form>
 | 
			
		||||
    } else {
 | 
			
		||||
        @base(title, data) {
 | 
			
		||||
            <form action={ templ.URL(postUrl) } method="post">
 | 
			
		||||
                @commentForm(form)
 | 
			
		||||
            </form>
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
templ CreateGuestbookComment(title string, data CommonData, website models.Website, guestbook models.Guestbook, form forms.CommentCreateForm) {
 | 
			
		||||
	{{ postUrl := fmt.Sprintf("/websites/%s/guestbook/comments/create", shortIdToSlug(website.ShortId)) }}
 | 
			
		||||
	if data.IsHtmx {
 | 
			
		||||
		<form hx-post={ postUrl } hx-target="closest div">
 | 
			
		||||
			@commentForm(form)
 | 
			
		||||
		</form>
 | 
			
		||||
	} else {
 | 
			
		||||
		@base(title, data) {
 | 
			
		||||
			<form action={ templ.URL(postUrl) } method="post">
 | 
			
		||||
				@commentForm(form)
 | 
			
		||||
			</form>
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ settingRadio(selected bool, name, id, value string) {
 | 
			
		||||
	<input type="radio" name={ name } id={ id } value={ value } selected?={ selected }/>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ GuestbookSettingsView(data CommonData, website models.Website) {
 | 
			
		||||
	{{ putUrl := fmt.Sprintf("/websites/%s/dashboard/guestbook/settings", shortIdToSlug(website.ShortId)) }}
 | 
			
		||||
	{{ gb := website.Guestbook }}
 | 
			
		||||
	@base("Guestbook Settings", data) {
 | 
			
		||||
		<div id="dashboard">
 | 
			
		||||
			@wSidebar(website)
 | 
			
		||||
			<div>
 | 
			
		||||
				<h1>Guestbook Settings</h1>
 | 
			
		||||
				<form hx-put={ putUrl }>
 | 
			
		||||
					<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
 | 
			
		||||
					<div>
 | 
			
		||||
						<label>Guestbook Visibility</label>
 | 
			
		||||
						<label for="gb_visible_true">
 | 
			
		||||
							<input type="radio" name="gb_visible" id="gb_visible_true" value="true" checked?={ gb.Settings.IsVisible }/>
 | 
			
		||||
							Public
 | 
			
		||||
						</label>
 | 
			
		||||
						<label for="gb_visible_false">
 | 
			
		||||
							<input type="radio" name="gb_visible" id="gb_visible_false" value="false" checked?={ !gb.Settings.IsVisible }/>
 | 
			
		||||
							Private
 | 
			
		||||
						</label>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div>
 | 
			
		||||
						<label>Guestbook Commenting</label>
 | 
			
		||||
						<select name="gb_commenting" id="gb-commenting">
 | 
			
		||||
							<option value="true" selected?={ gb.Settings.IsCommentingEnabled }>Enabled</option>
 | 
			
		||||
							<option value="1h">Disabled for 1 Hour</option>
 | 
			
		||||
							<option value="4h">Disabled for 4 Hours</option>
 | 
			
		||||
							<option value="8h">Disabled for 8 Hours</option>
 | 
			
		||||
							<option value="24h">Disabled for 1 Day</option>
 | 
			
		||||
							<option value="72h">Disabled for 3 Days</option>
 | 
			
		||||
							<option value="168h">Disabled for 7 Days</option>
 | 
			
		||||
							<option value="false" selected?={ !gb.Settings.IsCommentingEnabled }>Disabled</option>
 | 
			
		||||
						</select>
 | 
			
		||||
						if !website.Guestbook.CanComment() {
 | 
			
		||||
							{{ localtime := gb.Settings.ReenableCommenting.In(data.CurrentUser.Settings.LocalTimezone) }}
 | 
			
		||||
							<label>Commenting re-enabled on <time value={ localtime.Format(time.RFC3339) }>{ localtime.Format("2 January 2006") } at { localtime.Format("3:04PM MST") }</time></label>
 | 
			
		||||
						}
 | 
			
		||||
					</div>
 | 
			
		||||
					<div>
 | 
			
		||||
						<label>Enable Widgets</label>
 | 
			
		||||
						<label for="gb_remote_true">
 | 
			
		||||
							<input type="radio" name="gb_remote" id="gb_remote_true" value="true" checked?={ gb.Settings.AllowRemoteHostAccess }/>
 | 
			
		||||
							Yes
 | 
			
		||||
						</label>
 | 
			
		||||
						<label for="gb_remote_false">
 | 
			
		||||
							<input type="radio" name="gb_remote" id="gb_remote_false" value="false" checked?={ !gb.Settings.AllowRemoteHostAccess }/>
 | 
			
		||||
							No
 | 
			
		||||
						</label>
 | 
			
		||||
					</div>
 | 
			
		||||
					<input type="submit" value="Submit"/>
 | 
			
		||||
				</form>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ AllGuestbooksView(data CommonData, websites []models.Website) {
 | 
			
		||||
    @base("All Guestbooks", data) {
 | 
			
		||||
        <div>
 | 
			
		||||
            <h1>All Guestbooks</h1>
 | 
			
		||||
            <p>
 | 
			
		||||
                This page exists only for testing the service.
 | 
			
		||||
            </p>
 | 
			
		||||
            <ul>
 | 
			
		||||
                for _, w := range websites {
 | 
			
		||||
                    <li>
 | 
			
		||||
                        {{  gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId))}}
 | 
			
		||||
                        <a href={ templ.URL(gbUrl) } target="_blank">{ w.Name }</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                }
 | 
			
		||||
            </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
	@base("All Guestbooks", data) {
 | 
			
		||||
		<div>
 | 
			
		||||
			<h1>All Guestbooks</h1>
 | 
			
		||||
			<p>
 | 
			
		||||
				This page exists only for testing the service.
 | 
			
		||||
			</p>
 | 
			
		||||
			<ul>
 | 
			
		||||
				for _, w := range websites {
 | 
			
		||||
					<li>
 | 
			
		||||
						{{ gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId)) }}
 | 
			
		||||
						<a href={ templ.URL(gbUrl) } target="_blank">{ w.Name }</a>
 | 
			
		||||
					</li>
 | 
			
		||||
				}
 | 
			
		||||
			</ul>
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ import templruntime "github.com/a-h/templ/runtime"
 | 
			
		||||
import "fmt"
 | 
			
		||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/forms"
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
func GuestbookDashboardCommentsView(title string, data CommonData, website models.Website, guestbook models.Guestbook, comments []models.GuestbookComment) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
@ -60,7 +61,7 @@ func GuestbookDashboardCommentsView(title string, data CommonData, website model
 | 
			
		||||
			var templ_7745c5c3_Var3 string
 | 
			
		||||
			templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(website.SiteUrl)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 12, Col: 49}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 13, Col: 37}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -131,7 +132,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G
 | 
			
		||||
			var templ_7745c5c3_Var5 string
 | 
			
		||||
			templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(commentUrl)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 31, Col: 61}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 32, Col: 49}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -144,7 +145,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G
 | 
			
		||||
			var templ_7745c5c3_Var6 string
 | 
			
		||||
			templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(hxHeaders)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 31, Col: 118}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 32, Col: 106}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -157,7 +158,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G
 | 
			
		||||
			var templ_7745c5c3_Var7 string
 | 
			
		||||
			templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(commentUrl)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 32, Col: 59}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 33, Col: 47}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -170,7 +171,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G
 | 
			
		||||
			var templ_7745c5c3_Var8 string
 | 
			
		||||
			templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(hxHeaders)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 32, Col: 116}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 33, Col: 104}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -203,7 +204,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G
 | 
			
		||||
		var templ_7745c5c3_Var9 string
 | 
			
		||||
		templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 42, Col: 34}
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 43, Col: 25}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -231,7 +232,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G
 | 
			
		||||
			var templ_7745c5c3_Var11 string
 | 
			
		||||
			templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorEmail)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 45, Col: 78}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 46, Col: 66}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -259,7 +260,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G
 | 
			
		||||
			var templ_7745c5c3_Var13 string
 | 
			
		||||
			templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorSite)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 48, Col: 96}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 49, Col: 85}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -275,9 +276,9 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var14 string
 | 
			
		||||
		templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(c.Created.Format("01-02-2006 03:04PM"))
 | 
			
		||||
		templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(c.Created.In(data.CurrentUser.Settings.LocalTimezone).Format("01-02-2006 03:04PM"))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 51, Col: 56}
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 52, Col: 88}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -290,7 +291,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G
 | 
			
		||||
		var templ_7745c5c3_Var15 string
 | 
			
		||||
		templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(c.CommentText)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 55, Col: 27}
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 56, Col: 18}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -338,7 +339,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component {
 | 
			
		||||
			var templ_7745c5c3_Var17 string
 | 
			
		||||
			templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(error)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 66, Col: 36}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 67, Col: 31}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -362,7 +363,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component {
 | 
			
		||||
			var templ_7745c5c3_Var18 string
 | 
			
		||||
			templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(error)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 74, Col: 36}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 75, Col: 31}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -386,7 +387,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component {
 | 
			
		||||
			var templ_7745c5c3_Var19 string
 | 
			
		||||
			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(error)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 82, Col: 36}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 83, Col: 31}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -410,7 +411,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component {
 | 
			
		||||
			var templ_7745c5c3_Var20 string
 | 
			
		||||
			templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(error)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 90, Col: 36}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 91, Col: 31}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -464,7 +465,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb
 | 
			
		||||
			var templ_7745c5c3_Var22 string
 | 
			
		||||
			templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(title)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 106, Col: 26}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 107, Col: 18}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -477,7 +478,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb
 | 
			
		||||
			var templ_7745c5c3_Var23 string
 | 
			
		||||
			templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 112, Col: 56}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 113, Col: 38}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -499,7 +500,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb
 | 
			
		||||
			var templ_7745c5c3_Var25 string
 | 
			
		||||
			templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 114, Col: 88}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 115, Col: 68}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -531,7 +532,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb
 | 
			
		||||
				var templ_7745c5c3_Var26 string
 | 
			
		||||
				templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 124, Col: 50}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 125, Col: 26}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -544,7 +545,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb
 | 
			
		||||
				var templ_7745c5c3_Var27 string
 | 
			
		||||
				templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(c.Created.Format("01-02-2006 03:04PM"))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 125, Col: 72}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 126, Col: 48}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -557,7 +558,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb
 | 
			
		||||
				var templ_7745c5c3_Var28 string
 | 
			
		||||
				templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(c.CommentText)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 127, Col: 51}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 128, Col: 24}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -607,7 +608,7 @@ func CreateGuestbookComment(title string, data CommonData, website models.Websit
 | 
			
		||||
			var templ_7745c5c3_Var30 string
 | 
			
		||||
			templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(postUrl)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 141, Col: 35}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 142, Col: 25}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -670,7 +671,7 @@ func CreateGuestbookComment(title string, data CommonData, website models.Websit
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func AllGuestbooksView(data CommonData, websites []models.Website) templ.Component {
 | 
			
		||||
func settingRadio(selected bool, name, id, value string) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
@ -691,7 +692,87 @@ func AllGuestbooksView(data CommonData, websites []models.Website) templ.Compone
 | 
			
		||||
			templ_7745c5c3_Var33 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Var34 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<input type=\"radio\" name=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var34 string
 | 
			
		||||
		templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(name)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 155, Col: 32}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\" id=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var35 string
 | 
			
		||||
		templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(id)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 155, Col: 42}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\" value=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var36 string
 | 
			
		||||
		templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(value)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 155, Col: 58}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		if selected {
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, " selected")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, ">")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GuestbookSettingsView(data CommonData, website models.Website) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
			return templ_7745c5c3_CtxErr
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
		if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err == nil {
 | 
			
		||||
					templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var37 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var37 == nil {
 | 
			
		||||
			templ_7745c5c3_Var37 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		putUrl := fmt.Sprintf("/websites/%s/dashboard/guestbook/settings", shortIdToSlug(website.ShortId))
 | 
			
		||||
		gb := website.Guestbook
 | 
			
		||||
		templ_7745c5c3_Var38 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
			templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
			if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
@ -703,50 +784,241 @@ func AllGuestbooksView(data CommonData, websites []models.Website) templ.Compone
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<div><h1>All Guestbooks</h1><p>This page exists only for testing the service.</p><ul>")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<div id=\"dashboard\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			for _, w := range websites {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "<li>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId))
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<a href=\"")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var35 templ.SafeURL = templ.URL(gbUrl)
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var35)))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\" target=\"_blank\">")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var36 string
 | 
			
		||||
				templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(w.Name)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 164, Col: 77}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "</a></li>")
 | 
			
		||||
			templ_7745c5c3_Err = wSidebar(website).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "<div><h1>Guestbook Settings</h1><form hx-put=\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var39 string
 | 
			
		||||
			templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(putUrl)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 166, Col: 25}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "\"><input type=\"hidden\" name=\"csrf_token\" value=\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var40 string
 | 
			
		||||
			templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 167, Col: 66}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "\"><div><label>Guestbook Visibility</label> <label for=\"gb_visible_true\"><input type=\"radio\" name=\"gb_visible\" id=\"gb_visible_true\" value=\"true\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if gb.Settings.IsVisible {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, " checked")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "</ul></div>")
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "> Public</label> <label for=\"gb_visible_false\"><input type=\"radio\" name=\"gb_visible\" id=\"gb_visible_false\" value=\"false\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if !gb.Settings.IsVisible {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, " checked")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "> Private</label></div><div><label>Guestbook Commenting</label> <select name=\"gb_commenting\" id=\"gb-commenting\"><option value=\"true\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if gb.Settings.IsCommentingEnabled {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, " selected")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, ">Enabled</option> <option value=\"1h\">Disabled for 1 Hour</option> <option value=\"4h\">Disabled for 4 Hours</option> <option value=\"8h\">Disabled for 8 Hours</option> <option value=\"24h\">Disabled for 1 Day</option> <option value=\"72h\">Disabled for 3 Days</option> <option value=\"168h\">Disabled for 7 Days</option> <option value=\"false\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if !gb.Settings.IsCommentingEnabled {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, " selected")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, ">Disabled</option></select> ")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if !website.Guestbook.CanComment() {
 | 
			
		||||
				localtime := gb.Settings.ReenableCommenting.In(data.CurrentUser.Settings.LocalTimezone)
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "<label>Commenting re-enabled on <time value=\"")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var41 string
 | 
			
		||||
				templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(localtime.Format(time.RFC3339))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 193, Col: 83}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "\">")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var42 string
 | 
			
		||||
				templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(localtime.Format("2 January 2006"))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 193, Col: 122}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, " at ")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var43 string
 | 
			
		||||
				templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(localtime.Format("3:04PM MST"))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 193, Col: 160}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "</time></label>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "</div><div><label>Enable Widgets</label> <label for=\"gb_remote_true\"><input type=\"radio\" name=\"gb_remote\" id=\"gb_remote_true\" value=\"true\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if gb.Settings.AllowRemoteHostAccess {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, " checked")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "> Yes</label> <label for=\"gb_remote_false\"><input type=\"radio\" name=\"gb_remote\" id=\"gb_remote_false\" value=\"false\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			if !gb.Settings.AllowRemoteHostAccess {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, " checked")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "> No</label></div><input type=\"submit\" value=\"Submit\"></form></div></div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		templ_7745c5c3_Err = base("All Guestbooks", data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var34), templ_7745c5c3_Buffer)
 | 
			
		||||
		templ_7745c5c3_Err = base("Guestbook Settings", data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var38), templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func AllGuestbooksView(data CommonData, websites []models.Website) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
			return templ_7745c5c3_CtxErr
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
		if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err == nil {
 | 
			
		||||
					templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var44 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var44 == nil {
 | 
			
		||||
			templ_7745c5c3_Var44 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Var45 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
			templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
			if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
				defer func() {
 | 
			
		||||
					templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
					if templ_7745c5c3_Err == nil {
 | 
			
		||||
						templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
					}
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "<div><h1>All Guestbooks</h1><p>This page exists only for testing the service.</p><ul>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			for _, w := range websites {
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "<li>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId))
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "<a href=\"")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var46 templ.SafeURL = templ.URL(gbUrl)
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var46)))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "\" target=\"_blank\">")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				var templ_7745c5c3_Var47 string
 | 
			
		||||
				templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(w.Name)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 225, Col: 59}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "</a></li>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "</ul></div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		templ_7745c5c3_Err = base("All Guestbooks", data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var45), templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -5,79 +5,99 @@ import (
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
templ UserLogin (title string, data CommonData, form forms.UserLoginForm) {
 | 
			
		||||
    @base(title, data) {
 | 
			
		||||
        <h1>Login</h1>
 | 
			
		||||
        <form action="/users/login" method="POST" novalidate>
 | 
			
		||||
            <input type="hidden" name="csrf_token" value={ data.CSRFToken }>
 | 
			
		||||
            for _, error := range form.NonFieldErrors {
 | 
			
		||||
                <div class="error">{ error }</div>
 | 
			
		||||
            }
 | 
			
		||||
            <div>
 | 
			
		||||
                <label>Email: </label>
 | 
			
		||||
                {{ error, exists := form.FieldErrors["email"] }}
 | 
			
		||||
                if exists {
 | 
			
		||||
                    <label class="error">{ error }</label>
 | 
			
		||||
                }
 | 
			
		||||
                <input type="email" name="email" value={form.Email}>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
                <label>Password: </label>
 | 
			
		||||
                {{ error, exists = form.FieldErrors["password"] }}
 | 
			
		||||
                if exists {
 | 
			
		||||
                    <label class="error">{ error }</label>
 | 
			
		||||
                }
 | 
			
		||||
                <input type="password" name="password">
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
                <input type="submit" value="login">
 | 
			
		||||
            </div>
 | 
			
		||||
        </form>
 | 
			
		||||
    }
 | 
			
		||||
templ UserLogin(title string, data CommonData, form forms.UserLoginForm) {
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<h1>Login</h1>
 | 
			
		||||
		<form action="/users/login" method="POST" novalidate>
 | 
			
		||||
			<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
 | 
			
		||||
			for _, error := range form.NonFieldErrors {
 | 
			
		||||
				<div class="error">{ error }</div>
 | 
			
		||||
			}
 | 
			
		||||
			<div>
 | 
			
		||||
				<label>Email: </label>
 | 
			
		||||
				{{ error, exists := form.FieldErrors["email"] }}
 | 
			
		||||
				if exists {
 | 
			
		||||
					<label class="error">{ error }</label>
 | 
			
		||||
				}
 | 
			
		||||
				<input type="email" name="email" value={ form.Email }/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<label>Password: </label>
 | 
			
		||||
				{{ error, exists = form.FieldErrors["password"] }}
 | 
			
		||||
				if exists {
 | 
			
		||||
					<label class="error">{ error }</label>
 | 
			
		||||
				}
 | 
			
		||||
				<input type="password" name="password"/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<input type="submit" value="login"/>
 | 
			
		||||
			</div>
 | 
			
		||||
		</form>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ UserRegistration (title string, data CommonData, form forms.UserRegistrationForm) {
 | 
			
		||||
    @base(title, data) {
 | 
			
		||||
        <h1>User Registration</h1>
 | 
			
		||||
        <form action="/users/register" method="post">
 | 
			
		||||
            <input type="hidden" name="csrf_token" value={ data.CSRFToken }>
 | 
			
		||||
            <div>
 | 
			
		||||
                {{ error, exists := form.FieldErrors["name"] }}
 | 
			
		||||
                <label for="username">Username: </label>
 | 
			
		||||
                if exists {
 | 
			
		||||
                    <label class="error">{ error }</label>
 | 
			
		||||
                }
 | 
			
		||||
                <input type="text" name="username" id="username" value={form.Name} required />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
                {{ error, exists = form.FieldErrors["email"] }}
 | 
			
		||||
                <label for="email">Email: </label>
 | 
			
		||||
                if exists {
 | 
			
		||||
                    <label class="error">{ error }</label>
 | 
			
		||||
                }
 | 
			
		||||
                <input type="text" name="email" id="email" value={form.Email} required />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
                {{ error, exists = form.FieldErrors["password"] }}
 | 
			
		||||
                <label for="password">Password: </label>
 | 
			
		||||
                if exists {
 | 
			
		||||
                    <label class="error">{ error }</label>
 | 
			
		||||
                }
 | 
			
		||||
                <input type="password" name="password" id="password" />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
                <input type="submit" value="Register" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </form>
 | 
			
		||||
    }
 | 
			
		||||
templ UserRegistration(title string, data CommonData, form forms.UserRegistrationForm) {
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<h1>User Registration</h1>
 | 
			
		||||
		<form action="/users/register" method="post">
 | 
			
		||||
			<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
 | 
			
		||||
			<div>
 | 
			
		||||
				{{ error, exists := form.FieldErrors["name"] }}
 | 
			
		||||
				<label for="username">Username: </label>
 | 
			
		||||
				if exists {
 | 
			
		||||
					<label class="error">{ error }</label>
 | 
			
		||||
				}
 | 
			
		||||
				<input type="text" name="username" id="username" value={ form.Name } required/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div>
 | 
			
		||||
				{{ error, exists = form.FieldErrors["email"] }}
 | 
			
		||||
				<label for="email">Email: </label>
 | 
			
		||||
				if exists {
 | 
			
		||||
					<label class="error">{ error }</label>
 | 
			
		||||
				}
 | 
			
		||||
				<input type="text" name="email" id="email" value={ form.Email } required/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div>
 | 
			
		||||
				{{ error, exists = form.FieldErrors["password"] }}
 | 
			
		||||
				<label for="password">Password: </label>
 | 
			
		||||
				if exists {
 | 
			
		||||
					<label class="error">{ error }</label>
 | 
			
		||||
				}
 | 
			
		||||
				<input type="password" name="password" id="password"/>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<input type="submit" value="Register"/>
 | 
			
		||||
			</div>
 | 
			
		||||
		</form>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ UserProfile (title string, data CommonData, user models.User) {
 | 
			
		||||
    @base(title, data) {
 | 
			
		||||
        <h1>{ user.Username }</h1>
 | 
			
		||||
        <p>{ user.Email }</p>
 | 
			
		||||
    }
 | 
			
		||||
templ UserProfile(title string, data CommonData, user models.User) {
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<h1>{ user.Username }</h1>
 | 
			
		||||
		<p>{ user.Email }</p>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ UserSettings () {
 | 
			
		||||
templ UserSettingsView(data CommonData, timezones []string) {
 | 
			
		||||
	{{ user := data.CurrentUser }}
 | 
			
		||||
	@base("User Settings", data) {
 | 
			
		||||
		<div>
 | 
			
		||||
			<h1>User Settings</h1>
 | 
			
		||||
			<form hx-put="/users/settings">
 | 
			
		||||
				<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
 | 
			
		||||
				<label>Local Timezone</label>
 | 
			
		||||
				<select name="timezones" id="timezone-select">
 | 
			
		||||
					for _, tz := range timezones {
 | 
			
		||||
						if tz == user.Settings.LocalTimezone.String() {
 | 
			
		||||
							<option value={ tz } selected="true">{ tz }</option>
 | 
			
		||||
						} else {
 | 
			
		||||
							<option value={ tz }>{ tz }</option>
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				</select>
 | 
			
		||||
				<input type="submit" value="Submit"/>
 | 
			
		||||
			</form>
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -53,7 +53,7 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
 | 
			
		||||
			var templ_7745c5c3_Var3 string
 | 
			
		||||
			templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 12, Col: 73}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 12, Col: 64}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -71,7 +71,7 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
 | 
			
		||||
				var templ_7745c5c3_Var4 string
 | 
			
		||||
				templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(error)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 14, Col: 42}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 14, Col: 30}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -95,7 +95,7 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
 | 
			
		||||
				var templ_7745c5c3_Var5 string
 | 
			
		||||
				templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(error)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 20, Col: 48}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 20, Col: 33}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -113,7 +113,7 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
 | 
			
		||||
			var templ_7745c5c3_Var6 string
 | 
			
		||||
			templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 22, Col: 66}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 22, Col: 55}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -132,7 +132,7 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
 | 
			
		||||
				var templ_7745c5c3_Var7 string
 | 
			
		||||
				templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(error)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 28, Col: 48}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 28, Col: 33}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -197,7 +197,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
 | 
			
		||||
			var templ_7745c5c3_Var10 string
 | 
			
		||||
			templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 43, Col: 73}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 43, Col: 64}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -220,7 +220,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
 | 
			
		||||
				var templ_7745c5c3_Var11 string
 | 
			
		||||
				templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(error)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 48, Col: 48}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 48, Col: 33}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -238,7 +238,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
 | 
			
		||||
			var templ_7745c5c3_Var12 string
 | 
			
		||||
			templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(form.Name)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 50, Col: 81}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 50, Col: 70}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -261,7 +261,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
 | 
			
		||||
				var templ_7745c5c3_Var13 string
 | 
			
		||||
				templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(error)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 56, Col: 48}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 56, Col: 33}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -279,7 +279,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
 | 
			
		||||
			var templ_7745c5c3_Var14 string
 | 
			
		||||
			templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 58, Col: 76}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 58, Col: 65}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -302,7 +302,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
 | 
			
		||||
				var templ_7745c5c3_Var15 string
 | 
			
		||||
				templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(error)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 64, Col: 48}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 64, Col: 33}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -367,7 +367,7 @@ func UserProfile(title string, data CommonData, user models.User) templ.Componen
 | 
			
		||||
			var templ_7745c5c3_Var18 string
 | 
			
		||||
			templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(user.Username)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 77, Col: 27}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 77, Col: 21}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -380,7 +380,7 @@ func UserProfile(title string, data CommonData, user models.User) templ.Componen
 | 
			
		||||
			var templ_7745c5c3_Var19 string
 | 
			
		||||
			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 78, Col: 23}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 78, Col: 17}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -400,7 +400,7 @@ func UserProfile(title string, data CommonData, user models.User) templ.Componen
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func UserSettings() templ.Component {
 | 
			
		||||
func UserSettingsView(data CommonData, timezones []string) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
@ -421,6 +421,111 @@ func UserSettings() templ.Component {
 | 
			
		||||
			templ_7745c5c3_Var20 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		user := data.CurrentUser
 | 
			
		||||
		templ_7745c5c3_Var21 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
			templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
			if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
				defer func() {
 | 
			
		||||
					templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
					if templ_7745c5c3_Err == nil {
 | 
			
		||||
						templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
					}
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<div><h1>User Settings</h1><form hx-put=\"/users/settings\"><input type=\"hidden\" name=\"csrf_token\" value=\"")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			var templ_7745c5c3_Var22 string
 | 
			
		||||
			templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 88, Col: 65}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\"> <label>Local Timezone</label> <select name=\"timezones\" id=\"timezone-select\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			for _, tz := range timezones {
 | 
			
		||||
				if tz == user.Settings.LocalTimezone.String() {
 | 
			
		||||
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<option value=\"")
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					var templ_7745c5c3_Var23 string
 | 
			
		||||
					templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(tz)
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 93, Col: 25}
 | 
			
		||||
					}
 | 
			
		||||
					_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\" selected=\"true\">")
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					var templ_7745c5c3_Var24 string
 | 
			
		||||
					templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(tz)
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 93, Col: 48}
 | 
			
		||||
					}
 | 
			
		||||
					_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</option>")
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<option value=\"")
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					var templ_7745c5c3_Var25 string
 | 
			
		||||
					templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(tz)
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 95, Col: 25}
 | 
			
		||||
					}
 | 
			
		||||
					_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "\">")
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					var templ_7745c5c3_Var26 string
 | 
			
		||||
					templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(tz)
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 95, Col: 32}
 | 
			
		||||
					}
 | 
			
		||||
					_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</option>")
 | 
			
		||||
					if templ_7745c5c3_Err != nil {
 | 
			
		||||
						return templ_7745c5c3_Err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</select> <input type=\"submit\" value=\"Submit\"></form></div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		templ_7745c5c3_Err = base("User Settings", data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var21), templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,151 +4,141 @@ import "fmt"
 | 
			
		||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/forms"
 | 
			
		||||
 | 
			
		||||
func wUrl (w models.Website) string {
 | 
			
		||||
    return fmt.Sprintf("/websites/%s", shortIdToSlug(w.ShortId))
 | 
			
		||||
func wUrl(w models.Website) string {
 | 
			
		||||
	return fmt.Sprintf("/websites/%s", shortIdToSlug(w.ShortId))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ wSidebar(website models.Website) {
 | 
			
		||||
    {{ dashUrl := wUrl(website) + "/dashboard" }}
 | 
			
		||||
    {{ gbUrl := wUrl(website) + "/guestbook" }}
 | 
			
		||||
    <nav>
 | 
			
		||||
        <div>
 | 
			
		||||
            <h2>{ website.Name}</h2>
 | 
			
		||||
            <ul>
 | 
			
		||||
                <li><a href={ templ.URL(dashUrl) }>Dashboard</a></li>
 | 
			
		||||
                <li><a href={ templ.URL(externalUrl(website.SiteUrl)) } target="_blank">View Website</a></li>
 | 
			
		||||
            </ul>
 | 
			
		||||
            <h3>Guestbook</h3>
 | 
			
		||||
            <ul>
 | 
			
		||||
                <li><a href={ templ.URL(gbUrl) } target="_blank">View Guestbook</a></li>
 | 
			
		||||
            </ul>
 | 
			
		||||
            <ul>
 | 
			
		||||
                <li><a href={ templ.URL(dashUrl + "/guestbook/comments") }>Manage messages</a></li>
 | 
			
		||||
                <li><a href={ templ.URL(dashUrl + "/guestbook/comments/queue") }>Review message queue</a></li>
 | 
			
		||||
                <li><a href={ templ.URL(dashUrl + "/guestbook/blocklist") }>Block users</a></li>
 | 
			
		||||
                <li><a href={ templ.URL(dashUrl + "/guestbook/comments/trash") }>Trash</a></li>
 | 
			
		||||
            </ul>
 | 
			
		||||
            <ul>
 | 
			
		||||
                <li><a href={ templ.URL(dashUrl + "/guestbook/themes") }>Themes</a></li>
 | 
			
		||||
                <li><a href={ templ.URL(dashUrl + "/guestbook/customize") }>Custom CSS</a></li>
 | 
			
		||||
            </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
            <h3>Feeds</h3>
 | 
			
		||||
            <p>Coming Soon</p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
            <h3>Account</h3>
 | 
			
		||||
            <ul>
 | 
			
		||||
                <li><a href="/users/settings">Settings</a></li>
 | 
			
		||||
                <li><a href="/users/privacy">Privacy</a></li>
 | 
			
		||||
                <li><a href="/help">Help</a></li>
 | 
			
		||||
            </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
    </nav>
 | 
			
		||||
	{{ dashUrl := wUrl(website) + "/dashboard" }}
 | 
			
		||||
	{{ gbUrl := wUrl(website) + "/guestbook" }}
 | 
			
		||||
	<nav>
 | 
			
		||||
		<div>
 | 
			
		||||
			<h2>{ website.Name }</h2>
 | 
			
		||||
			<ul>
 | 
			
		||||
				<li><a href={ templ.URL(dashUrl) }>Dashboard</a></li>
 | 
			
		||||
				<li><a href={ templ.URL(externalUrl(website.SiteUrl)) } target="_blank">View Website</a></li>
 | 
			
		||||
			</ul>
 | 
			
		||||
			<h3>Guestbook</h3>
 | 
			
		||||
			<ul>
 | 
			
		||||
				<li><a href={ templ.URL(gbUrl) } target="_blank">View Guestbook</a></li>
 | 
			
		||||
			</ul>
 | 
			
		||||
			<ul>
 | 
			
		||||
				<li><a href={ templ.URL(dashUrl + "/guestbook/comments") }>Manage messages</a></li>
 | 
			
		||||
				<li><a href={ templ.URL(dashUrl + "/guestbook/comments/queue") }>Review message queue</a></li>
 | 
			
		||||
				<li><a href={ templ.URL(dashUrl + "/guestbook/comments/trash") }>Trash</a></li>
 | 
			
		||||
				<li><a href={ templ.URL(dashUrl + "/guestbook/settings") }>Settings</a></li>
 | 
			
		||||
			</ul>
 | 
			
		||||
			<ul>
 | 
			
		||||
				<li><a href={ templ.URL(dashUrl + "/guestbook/themes") }>Themes</a></li>
 | 
			
		||||
				<li><a href={ templ.URL(dashUrl + "/guestbook/customize") }>Custom CSS</a></li>
 | 
			
		||||
			</ul>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div>
 | 
			
		||||
			<h3>Feeds</h3>
 | 
			
		||||
			<p>Coming Soon</p>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div>
 | 
			
		||||
			<h3>Account</h3>
 | 
			
		||||
			<ul>
 | 
			
		||||
				<li><a href="/users/settings">Settings</a></li>
 | 
			
		||||
				<li><a href="/users/privacy">Privacy</a></li>
 | 
			
		||||
				<li><a href="/help">Help</a></li>
 | 
			
		||||
			</ul>
 | 
			
		||||
		</div>
 | 
			
		||||
	</nav>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ displayWebsites (websites []models.Website) {
 | 
			
		||||
    if len(websites) == 0 {
 | 
			
		||||
        <p>No Websites yet. <a href="">Register a website.</a></p>
 | 
			
		||||
    } else {
 | 
			
		||||
        <ul id="websites" hx-get="/websites" hx-trigger="newWebsite from:body" hx-swap="outerHTML">
 | 
			
		||||
            for _, w := range websites {
 | 
			
		||||
                <li>
 | 
			
		||||
                    <a href={ templ.URL(wUrl(w) + "/dashboard")}>{ w.Name }</a>
 | 
			
		||||
                </li>
 | 
			
		||||
            }
 | 
			
		||||
        </ul>
 | 
			
		||||
    }
 | 
			
		||||
templ displayWebsites(websites []models.Website) {
 | 
			
		||||
	if len(websites) == 0 {
 | 
			
		||||
		<p>No Websites yet. <a href="">Register a website.</a></p>
 | 
			
		||||
	} else {
 | 
			
		||||
		<ul id="websites" hx-get="/websites" hx-trigger="newWebsite from:body" hx-swap="outerHTML">
 | 
			
		||||
			for _, w := range websites {
 | 
			
		||||
				<li>
 | 
			
		||||
					<a href={ templ.URL(wUrl(w) + "/dashboard") }>{ w.Name }</a>
 | 
			
		||||
				</li>
 | 
			
		||||
			}
 | 
			
		||||
		</ul>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) {
 | 
			
		||||
    <input type="hidden" name="csrf_token" value={csrfToken}>
 | 
			
		||||
    <div>
 | 
			
		||||
        {{ err, exists := form.FieldErrors["sitename"]}}
 | 
			
		||||
        <label for="sitename">Site Name: </label>
 | 
			
		||||
        if exists {
 | 
			
		||||
            <label class="error">{ err }</label>
 | 
			
		||||
        }
 | 
			
		||||
        <input type="text" name="sitename" id="sitename" required />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        {{ err, exists = form.FieldErrors["siteurl"] }}
 | 
			
		||||
        <label for="siteurl">Site URL: </label>
 | 
			
		||||
        if exists {
 | 
			
		||||
            <label class="error">{ err }</label>
 | 
			
		||||
        }
 | 
			
		||||
        <input type="text" name="siteurl" id="siteurl" required />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        {{ err, exists = form.FieldErrors["authorname"] }}
 | 
			
		||||
        <label for="authorname">Site Author: </label>
 | 
			
		||||
        if exists {
 | 
			
		||||
            <label class="error">{ err }</label>
 | 
			
		||||
        }
 | 
			
		||||
        <input type="text" name="authorname" id="authorname" required />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        <button type="submit">Submit</button>
 | 
			
		||||
    </div>
 | 
			
		||||
	<input type="hidden" name="csrf_token" value={ csrfToken }/>
 | 
			
		||||
	<div>
 | 
			
		||||
		{{ err, exists := form.FieldErrors["sitename"] }}
 | 
			
		||||
		<label for="sitename">Site Name: </label>
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ err }</label>
 | 
			
		||||
		}
 | 
			
		||||
		<input type="text" name="sitename" id="sitename" required/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		{{ err, exists = form.FieldErrors["siteurl"] }}
 | 
			
		||||
		<label for="siteurl">Site URL: </label>
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ err }</label>
 | 
			
		||||
		}
 | 
			
		||||
		<input type="text" name="siteurl" id="siteurl" required/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		{{ err, exists = form.FieldErrors["authorname"] }}
 | 
			
		||||
		<label for="authorname">Site Author: </label>
 | 
			
		||||
		if exists {
 | 
			
		||||
			<label class="error">{ err }</label>
 | 
			
		||||
		}
 | 
			
		||||
		<input type="text" name="authorname" id="authorname" required/>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<button type="submit">Submit</button>
 | 
			
		||||
	</div>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ WebsiteCreateButton() {
 | 
			
		||||
    <button hx-get="/websites/create" hx-target="closest div">Add Website</button>
 | 
			
		||||
	<button hx-get="/websites/create" hx-target="closest div">Add Website</button>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ WebsiteList(title string, data CommonData, websites []models.Website) {
 | 
			
		||||
    if data.IsHtmx {
 | 
			
		||||
        @displayWebsites(websites)
 | 
			
		||||
    } else {
 | 
			
		||||
        @base(title, data) {
 | 
			
		||||
            <h1>My Websites</h1>
 | 
			
		||||
            <div>
 | 
			
		||||
                @WebsiteCreateButton()
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
                @displayWebsites(websites)
 | 
			
		||||
            </div>
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<h1>My Websites</h1>
 | 
			
		||||
		<div>
 | 
			
		||||
			@WebsiteCreateButton()
 | 
			
		||||
		</div>
 | 
			
		||||
		<div>
 | 
			
		||||
			@displayWebsites(websites)
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ WebsiteDashboard(title string, data CommonData, website models.Website) {
 | 
			
		||||
    @base(title, data) {
 | 
			
		||||
        <div id="dashboard">
 | 
			
		||||
            @wSidebar(website) 
 | 
			
		||||
            <div>
 | 
			
		||||
                <h1>{ website.Name }</h1>
 | 
			
		||||
                <p>
 | 
			
		||||
                    Stats and stuff will go here.
 | 
			
		||||
                </p>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    }
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<div id="dashboard">
 | 
			
		||||
			@wSidebar(website)
 | 
			
		||||
			<div>
 | 
			
		||||
				<h1>{ website.Name }</h1>
 | 
			
		||||
				<p>
 | 
			
		||||
					Stats and stuff will go here.
 | 
			
		||||
				</p>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ WebsiteDashboardComingSoon(title string, data CommonData, website models.Website) {
 | 
			
		||||
    @base(title, data) {
 | 
			
		||||
        <div id="dashboard">
 | 
			
		||||
            @wSidebar(website) 
 | 
			
		||||
            <div>
 | 
			
		||||
                <h1>{ website.Name }</h1>
 | 
			
		||||
                <p>
 | 
			
		||||
                    Coming Soon
 | 
			
		||||
                </p>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    }
 | 
			
		||||
	@base(title, data) {
 | 
			
		||||
		<div id="dashboard">
 | 
			
		||||
			@wSidebar(website)
 | 
			
		||||
			<div>
 | 
			
		||||
				<h1>{ website.Name }</h1>
 | 
			
		||||
				<p>
 | 
			
		||||
					Coming Soon
 | 
			
		||||
				</p>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
templ WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) {
 | 
			
		||||
    if data.IsHtmx {
 | 
			
		||||
        <form hx-post="/websites/create" hx-target="closest div">
 | 
			
		||||
            @websiteCreateForm(data.CSRFToken, form)
 | 
			
		||||
        </form>
 | 
			
		||||
    } else {
 | 
			
		||||
        <form action="/websites/create" method="post">
 | 
			
		||||
            @websiteCreateForm(data.CSRFToken, form)
 | 
			
		||||
        </form>
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
	<form hx-post="/websites/create" hx-target="closest div">
 | 
			
		||||
		@websiteCreateForm(data.CSRFToken, form)
 | 
			
		||||
	</form>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								ui/views/websites_hx.templ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								ui/views/websites_hx.templ
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
package views
 | 
			
		||||
 | 
			
		||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
 | 
			
		||||
templ HxWebsiteList(websites []models.Website) {
 | 
			
		||||
	@displayWebsites(websites)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								ui/views/websites_hx_templ.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								ui/views/websites_hx_templ.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
// Code generated by templ - DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
// templ: version: v0.3.833
 | 
			
		||||
package views
 | 
			
		||||
 | 
			
		||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
 | 
			
		||||
 | 
			
		||||
import "github.com/a-h/templ"
 | 
			
		||||
import templruntime "github.com/a-h/templ/runtime"
 | 
			
		||||
 | 
			
		||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
 | 
			
		||||
func HxWebsiteList(websites []models.Website) templ.Component {
 | 
			
		||||
	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
 | 
			
		||||
			return templ_7745c5c3_CtxErr
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
		if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err == nil {
 | 
			
		||||
					templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.InitializeContext(ctx)
 | 
			
		||||
		templ_7745c5c3_Var1 := templ.GetChildren(ctx)
 | 
			
		||||
		if templ_7745c5c3_Var1 == nil {
 | 
			
		||||
			templ_7745c5c3_Var1 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		templ_7745c5c3_Err = displayWebsites(websites).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ = templruntime.GeneratedTemplate
 | 
			
		||||
@ -46,7 +46,7 @@ func wSidebar(website models.Website) templ.Component {
 | 
			
		||||
		var templ_7745c5c3_Var2 string
 | 
			
		||||
		templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 16, Col: 30}
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 16, Col: 21}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -101,21 +101,21 @@ func wSidebar(website models.Website) templ.Component {
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var8 templ.SafeURL = templ.URL(dashUrl + "/guestbook/blocklist")
 | 
			
		||||
		var templ_7745c5c3_Var8 templ.SafeURL = templ.URL(dashUrl + "/guestbook/comments/trash")
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var8)))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\">Block users</a></li><li><a href=\"")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\">Trash</a></li><li><a href=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		var templ_7745c5c3_Var9 templ.SafeURL = templ.URL(dashUrl + "/guestbook/comments/trash")
 | 
			
		||||
		var templ_7745c5c3_Var9 templ.SafeURL = templ.URL(dashUrl + "/guestbook/settings")
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var9)))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\">Trash</a></li></ul><ul><li><a href=\"")
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\">Settings</a></li></ul><ul><li><a href=\"")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
@ -189,7 +189,7 @@ func displayWebsites(websites []models.Website) templ.Component {
 | 
			
		||||
				var templ_7745c5c3_Var14 string
 | 
			
		||||
				templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(w.Name)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 58, Col: 73}
 | 
			
		||||
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 58, Col: 59}
 | 
			
		||||
				}
 | 
			
		||||
				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -237,7 +237,7 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com
 | 
			
		||||
		var templ_7745c5c3_Var16 string
 | 
			
		||||
		templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(csrfToken)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 66, Col: 59}
 | 
			
		||||
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 66, Col: 57}
 | 
			
		||||
		}
 | 
			
		||||
		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -260,7 +260,7 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com
 | 
			
		||||
			var templ_7745c5c3_Var17 string
 | 
			
		||||
			templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(err)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 71, Col: 38}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 71, Col: 29}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -288,7 +288,7 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com
 | 
			
		||||
			var templ_7745c5c3_Var18 string
 | 
			
		||||
			templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(err)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 79, Col: 38}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 79, Col: 29}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -316,7 +316,7 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com
 | 
			
		||||
			var templ_7745c5c3_Var19 string
 | 
			
		||||
			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(err)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 87, Col: 38}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 87, Col: 29}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -385,50 +385,43 @@ func WebsiteList(title string, data CommonData, websites []models.Website) templ
 | 
			
		||||
			templ_7745c5c3_Var21 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		if data.IsHtmx {
 | 
			
		||||
		templ_7745c5c3_Var22 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
			templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
			if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
				defer func() {
 | 
			
		||||
					templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
					if templ_7745c5c3_Err == nil {
 | 
			
		||||
						templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
					}
 | 
			
		||||
				}()
 | 
			
		||||
			}
 | 
			
		||||
			ctx = templ.InitializeContext(ctx)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<h1>My Websites</h1><div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = WebsiteCreateButton().Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</div><div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = displayWebsites(websites).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			templ_7745c5c3_Var22 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 | 
			
		||||
				templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 | 
			
		||||
				templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
 | 
			
		||||
				if !templ_7745c5c3_IsBuffer {
 | 
			
		||||
					defer func() {
 | 
			
		||||
						templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
 | 
			
		||||
						if templ_7745c5c3_Err == nil {
 | 
			
		||||
							templ_7745c5c3_Err = templ_7745c5c3_BufErr
 | 
			
		||||
						}
 | 
			
		||||
					}()
 | 
			
		||||
				}
 | 
			
		||||
				ctx = templ.InitializeContext(ctx)
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<h1>My Websites</h1><div>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = WebsiteCreateButton().Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</div><div>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = displayWebsites(websites).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</div>")
 | 
			
		||||
				if templ_7745c5c3_Err != nil {
 | 
			
		||||
					return templ_7745c5c3_Err
 | 
			
		||||
				}
 | 
			
		||||
				return nil
 | 
			
		||||
			})
 | 
			
		||||
			templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var22), templ_7745c5c3_Buffer)
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</div>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var22), templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
@ -482,7 +475,7 @@ func WebsiteDashboard(title string, data CommonData, website models.Website) tem
 | 
			
		||||
			var templ_7745c5c3_Var25 string
 | 
			
		||||
			templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 121, Col: 34}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 117, Col: 22}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -550,7 +543,7 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We
 | 
			
		||||
			var templ_7745c5c3_Var28 string
 | 
			
		||||
			templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 135, Col: 34}
 | 
			
		||||
				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 131, Col: 22}
 | 
			
		||||
			}
 | 
			
		||||
			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
@ -591,32 +584,17 @@ func WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm)
 | 
			
		||||
			templ_7745c5c3_Var29 = templ.NopComponent
 | 
			
		||||
		}
 | 
			
		||||
		ctx = templ.ClearChildren(ctx)
 | 
			
		||||
		if data.IsHtmx {
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<form hx-post=\"/websites/create\" hx-target=\"closest div\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = websiteCreateForm(data.CSRFToken, form).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</form>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<form action=\"/websites/create\" method=\"post\">")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = websiteCreateForm(data.CSRFToken, form).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</form>")
 | 
			
		||||
			if templ_7745c5c3_Err != nil {
 | 
			
		||||
				return templ_7745c5c3_Err
 | 
			
		||||
			}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<form hx-post=\"/websites/create\" hx-target=\"closest div\">")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = websiteCreateForm(data.CSRFToken, form).Render(ctx, templ_7745c5c3_Buffer)
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</form>")
 | 
			
		||||
		if templ_7745c5c3_Err != nil {
 | 
			
		||||
			return templ_7745c5c3_Err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user