Implement guestbook settings #20
| @ -4,6 +4,7 @@ import ( | |||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"git.32bit.cafe/32bitcafe/guestbook/internal/forms" | 	"git.32bit.cafe/32bitcafe/guestbook/internal/forms" | ||||||
| @ -23,6 +24,17 @@ func (app *application) getGuestbook(w http.ResponseWriter, r *http.Request) { | |||||||
| 		} | 		} | ||||||
| 		return | 		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) | 	comments, err := app.guestbookComments.GetAll(website.Guestbook.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		app.serverError(w, r, err) | 		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) | 	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) { | func (app *application) getGuestbookComments(w http.ResponseWriter, r *http.Request) { | ||||||
| 	slug := r.PathValue("id") | 	slug := r.PathValue("id") | ||||||
| 	website, err := app.websites.Get(slugToShortId(slug)) | 	website, err := app.websites.Get(slugToShortId(slug)) | ||||||
| @ -64,7 +148,6 @@ func (app *application) getGuestbookCommentCreate(w http.ResponseWriter, r *http | |||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	data := app.newCommonData(r) | 	data := app.newCommonData(r) | ||||||
| 	form := forms.CommentCreateForm{} | 	form := forms.CommentCreateForm{} | ||||||
| 	views.CreateGuestbookComment("New Comment", data, website, website.Guestbook, form).Render(r.Context(), w) | 	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 | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if !website.Guestbook.CanComment() { | ||||||
|  | 		app.clientError(w, http.StatusForbidden) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	var form forms.CommentCreateForm | 	var form forms.CommentCreateForm | ||||||
| 	err = app.decodePostForm(r, &form) | 	err = app.decodePostForm(r, &form) | ||||||
| 	if err != nil { | 	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") | 	form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank") | ||||||
| 
 | 
 | ||||||
| 	if !form.Valid() { | 	if !form.Valid() { | ||||||
|  | 		// TODO: use htmx to avoid getting comments again | ||||||
| 		comments, err := app.guestbookComments.GetAll(website.Guestbook.ID) | 		comments, err := app.guestbookComments.GetAll(website.Guestbook.ID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			app.serverError(w, r, err) | 			app.serverError(w, r, err) | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ package main | |||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"git.32bit.cafe/32bitcafe/guestbook/internal/forms" | 	"git.32bit.cafe/32bitcafe/guestbook/internal/forms" | ||||||
| 	"git.32bit.cafe/32bitcafe/guestbook/internal/models" | 	"git.32bit.cafe/32bitcafe/guestbook/internal/models" | ||||||
| @ -39,7 +40,8 @@ func (app *application) postUserRegister(w http.ResponseWriter, r *http.Request) | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	shortId := app.createShortId() | 	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 err != nil { | ||||||
| 		if errors.Is(err, models.ErrDuplicateEmail) { | 		if errors.Is(err, models.ErrDuplicateEmail) { | ||||||
| 			form.AddFieldError("email", "Email address is already in use") | 			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) | 	data := app.newCommonData(r) | ||||||
| 	views.UserProfile(user.Username, data, user).Render(r.Context(), w) | 	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) | 		views.WebsiteCreate("Add a Website", data, form).Render(r.Context(), w) | ||||||
| 	} | 	} | ||||||
| 	websiteShortID := app.createShortId() | 	websiteShortID := app.createShortId() | ||||||
| 	websiteId, err := app.websites.Insert(websiteShortID, userId, form.Name, form.SiteUrl, form.AuthorName) | 	_, 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) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		app.serverError(w, r, err) | 		app.serverError(w, r, err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	app.sessionManager.Put(r.Context(), "flash", "Website successfully registered!") | 	app.sessionManager.Put(r.Context(), "flash", "Website successfully registered!") | ||||||
| 	if r.Header.Get("HX-Request") == "true" { | 	http.Redirect(w, r, fmt.Sprintf("/websites/%s/dashboard", shortIdToSlug(websiteShortID)), http.StatusSeeOther) | ||||||
| 		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) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (app *application) getWebsiteDashboard(w http.ResponseWriter, r *http.Request) { | 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 | 		return | ||||||
| 	} | 	} | ||||||
| 	if r.Header.Get("HX-Request") == "true" { | 	if r.Header.Get("HX-Request") == "true" { | ||||||
| 		w.Header().Add("HX-Trigger", "newWebsite") | 		views.HxWebsiteList(websites) | ||||||
|  | 		return | ||||||
| 	} | 	} | ||||||
| 	data := app.newCommonData(r) | 	data := app.newCommonData(r) | ||||||
| 	views.WebsiteList("My Websites", data, websites).Render(r.Context(), w) | 	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) | 	app.logger.Error(err.Error(), "method", method, "uri", uri) | ||||||
| 	if app.debug { | 	if app.debug { | ||||||
| 		http.Error(w, string(debug.Stack()), http.StatusInternalServerError) | 		http.Error(w, err.Error()+"\n"+string(debug.Stack()), http.StatusInternalServerError) | ||||||
| 		app.logger.Error(err.Error(), "method", method, "uri", uri, "stack", string(debug.Stack())) |  | ||||||
| 	} | 	} | ||||||
| 	http.Error(w, http.StatusText(http.StatusInternalServerError), 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", | 		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" | 	"log/slog" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  | 	"unicode" | ||||||
| 
 | 
 | ||||||
| 	"git.32bit.cafe/32bitcafe/guestbook/internal/models" | 	"git.32bit.cafe/32bitcafe/guestbook/internal/models" | ||||||
| 	"github.com/alexedwards/scs/sqlite3store" | 	"github.com/alexedwards/scs/sqlite3store" | ||||||
| @ -20,12 +22,12 @@ type application struct { | |||||||
| 	sequence          uint16 | 	sequence          uint16 | ||||||
| 	logger            *slog.Logger | 	logger            *slog.Logger | ||||||
| 	websites          *models.WebsiteModel | 	websites          *models.WebsiteModel | ||||||
| 	guestbooks        *models.GuestbookModel |  | ||||||
| 	users             *models.UserModel | 	users             *models.UserModel | ||||||
| 	guestbookComments *models.GuestbookCommentModel | 	guestbookComments *models.GuestbookCommentModel | ||||||
| 	sessionManager    *scs.SessionManager | 	sessionManager    *scs.SessionManager | ||||||
| 	formDecoder       *schema.Decoder | 	formDecoder       *schema.Decoder | ||||||
| 	debug             bool | 	debug             bool | ||||||
|  | 	timezones         []string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func main() { | func main() { | ||||||
| @ -55,11 +57,22 @@ func main() { | |||||||
| 		logger:            logger, | 		logger:            logger, | ||||||
| 		sessionManager:    sessionManager, | 		sessionManager:    sessionManager, | ||||||
| 		websites:          &models.WebsiteModel{DB: db}, | 		websites:          &models.WebsiteModel{DB: db}, | ||||||
| 		guestbooks:        &models.GuestbookModel{DB: db}, | 		users:             &models.UserModel{DB: db, Settings: make(map[string]models.Setting)}, | ||||||
| 		users:             &models.UserModel{DB: db}, |  | ||||||
| 		guestbookComments: &models.GuestbookCommentModel{DB: db}, | 		guestbookComments: &models.GuestbookCommentModel{DB: db}, | ||||||
| 		formDecoder:       formDecoder, | 		formDecoder:       formDecoder, | ||||||
| 		debug:             *debug, | 		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{ | 	tlsConfig := &tls.Config{ | ||||||
| @ -97,3 +110,50 @@ func openDB(dsn string) (*sql.DB, error) { | |||||||
| 	} | 	} | ||||||
| 	return db, nil | 	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", protected.ThenFunc(app.getUsersList)) | ||||||
| 	mux.Handle("GET /users/{id}", protected.ThenFunc(app.getUser)) | 	mux.Handle("GET /users/{id}", protected.ThenFunc(app.getUser)) | ||||||
| 	mux.Handle("POST /users/logout", protected.ThenFunc(app.postUserLogout)) | 	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 /users/privacy", protected.ThenFunc(app.notImplemented)) | ||||||
| 	mux.Handle("GET /guestbooks", protected.ThenFunc(app.getAllGuestbooks)) | 	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("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("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("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/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/themes", protected.ThenFunc(app.getComingSoon)) | ||||||
| 	mux.Handle("GET /websites/{id}/dashboard/guestbook/customize", 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"` | 	AuthorName          string `schema:"authorname"` | ||||||
| 	validator.Validator `schema:"-"` | 	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" | import "errors" | ||||||
| 
 | 
 | ||||||
| var ( | 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" | 	"golang.org/x/crypto/bcrypt" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type UserSettings struct { | ||||||
|  | 	LocalTimezone *time.Location | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	SettingUserTimezone = "local_timezone" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| type User struct { | type User struct { | ||||||
| 	ID             int64 | 	ID             int64 | ||||||
| 	ShortId        uint64 | 	ShortId        uint64 | ||||||
| @ -19,20 +27,54 @@ type User struct { | |||||||
| 	IsBanned       bool | 	IsBanned       bool | ||||||
| 	HashedPassword []byte | 	HashedPassword []byte | ||||||
| 	Created        time.Time | 	Created        time.Time | ||||||
|  | 	Settings       UserSettings | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type UserModel struct { | 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) | 	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	stmt := `INSERT INTO users (ShortId, Username, Email, IsBanned, HashedPassword, Created) | 	stmt := `INSERT INTO users (ShortId, Username, Email, IsBanned, HashedPassword, Created) | ||||||
|     VALUES (?, ?, ?, FALSE, ?, ?)` |     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 err != nil { | ||||||
| 		if sqliteError, ok := err.(sqlite3.Error); ok { | 		if sqliteError, ok := err.(sqlite3.Error); ok { | ||||||
| 			if sqliteError.ExtendedCode == 2067 && strings.Contains(sqliteError.Error(), "Email") { | 			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 | 		return err | ||||||
| 	} | 	} | ||||||
|  | 	id, err := result.LastInsertId() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = m.initializeUserSettings(id, settings) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -55,6 +105,11 @@ func (m *UserModel) Get(id uint64) (User, error) { | |||||||
| 		} | 		} | ||||||
| 		return User{}, err | 		return User{}, err | ||||||
| 	} | 	} | ||||||
|  | 	settings, err := m.GetSettings(u.ID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return u, err | ||||||
|  | 	} | ||||||
|  | 	u.Settings = settings | ||||||
| 	return u, nil | 	return u, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -69,6 +124,11 @@ func (m *UserModel) GetById(id int64) (User, error) { | |||||||
| 		} | 		} | ||||||
| 		return User{}, err | 		return User{}, err | ||||||
| 	} | 	} | ||||||
|  | 	settings, err := m.GetSettings(u.ID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return u, err | ||||||
|  | 	} | ||||||
|  | 	u.Settings = settings | ||||||
| 	return u, nil | 	return u, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -125,3 +185,75 @@ func (m *UserModel) Exists(id int64) (bool, error) { | |||||||
| 	err := m.DB.QueryRow(stmt, id).Scan(&exists) | 	err := m.DB.QueryRow(stmt, id).Scan(&exists) | ||||||
| 	return exists, err | 	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 ( | import ( | ||||||
| 	"database/sql" | 	"database/sql" | ||||||
|  | 	"errors" | ||||||
|  | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -17,51 +19,206 @@ type Website struct { | |||||||
| 	Guestbook  Guestbook | 	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 { | 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) { | func (m *WebsiteModel) Insert(shortId uint64, userId int64, siteName, siteUrl, authorName string) (int64, error) { | ||||||
| 	stmt := `INSERT INTO websites (ShortId, Name, SiteUrl, AuthorName, UserId, Created) | 	stmt := `INSERT INTO websites (ShortId, Name, SiteUrl, AuthorName, UserId, Created) | ||||||
| 			VALUES (?, ?, ?, ?, ?, ?)` | 			VALUES (?, ?, ?, ?, ?, ?)` | ||||||
| 	result, err := m.DB.Exec(stmt, shortId, siteName, siteUrl, authorName, userId, time.Now().UTC()) | 	tx, err := m.DB.Begin() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return -1, err | 		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 err != nil { | ||||||
|  | 		if rollbackError := tx.Rollback(); rollbackError != nil { | ||||||
|  | 			return -1, err | ||||||
|  | 		} | ||||||
| 		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) { | func (m *WebsiteModel) Get(shortId uint64) (Website, error) { | ||||||
| 	stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created, | 	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 | ||||||
| 	FROM websites AS w INNER JOIN guestbooks AS g ON w.Id = g.WebsiteId |  | ||||||
| 	WHERE w.ShortId = ? AND w.DELETED IS NULL` | 	WHERE w.ShortId = ? AND w.DELETED IS NULL` | ||||||
| 	row := m.DB.QueryRow(stmt, shortId) | 	tx, err := m.DB.Begin() | ||||||
| 	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) |  | ||||||
| 	if err != nil { | 	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 Website{}, err | ||||||
| 	} | 	} | ||||||
| 	return w, nil |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func (m *WebsiteModel) GetById(id int64) (Website, error) { | 	stmt = `SELECT Id, ShortId, UserId, WebsiteId, Created, IsActive FROM guestbooks | ||||||
| 	stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created, |     WHERE WebsiteId = ? AND Deleted IS NULL` | ||||||
| 	g.Id, g.ShortId, g.Created, g.IsActive | 	row = tx.QueryRow(stmt, w.ID) | ||||||
| 	FROM websites AS w INNER JOIN guestbooks AS g ON w.Id = g.WebsiteId | 	var g Guestbook | ||||||
| 	WHERE w.Id = ? AND w.DELETED IS NULL` | 	err = row.Scan(&g.ID, &g.ShortId, &g.UserId, &g.WebsiteId, &g.Created, &g.IsActive) | ||||||
| 	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) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | 		if errors.Is(err, sql.ErrNoRows) { | ||||||
|  | 			err = ErrNoRecord | ||||||
|  | 		} | ||||||
|  | 		if rollbackErr := tx.Rollback(); rollbackErr != nil { | ||||||
|  | 			return Website{}, err | ||||||
|  | 		} | ||||||
| 		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 | 	return w, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -113,3 +270,92 @@ func (m *WebsiteModel) GetAll() ([]Website, error) { | |||||||
| 	} | 	} | ||||||
| 	return websites, nil | 	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 "fmt" | ||||||
| import "git.32bit.cafe/32bitcafe/guestbook/internal/models" | import "git.32bit.cafe/32bitcafe/guestbook/internal/models" | ||||||
| import "git.32bit.cafe/32bitcafe/guestbook/internal/forms" | 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) { | templ GuestbookDashboardCommentsView(title string, data CommonData, website models.Website, guestbook models.Guestbook, comments []models.GuestbookComment) { | ||||||
|     @base(title, data) { | 	@base(title, data) { | ||||||
|         <div id="dashboard"> | 		<div id="dashboard"> | ||||||
|             @wSidebar(website) | 			@wSidebar(website) | ||||||
|             <div> | 			<div> | ||||||
|                 <h1>Comments on { website.SiteUrl }</h1> | 				<h1>Comments on { website.SiteUrl }</h1> | ||||||
|                 <hr> | 				<hr/> | ||||||
|                 if len(comments) == 0 { | 				if len(comments) == 0 { | ||||||
|                     <p>No comments yet!</p> | 					<p>No comments yet!</p> | ||||||
|                 } | 				} | ||||||
|                 for  _, c := range comments { | 				for  _, c := range comments { | ||||||
|                     @GuestbookDashboardCommentView(data, website, c) | 					@GuestbookDashboardCommentView(data, website, c) | ||||||
|                 } | 				} | ||||||
|             </div> | 			</div> | ||||||
|         </div> | 		</div> | ||||||
|     } | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ GuestbookDashboardCommentView(data CommonData, w models.Website, c models.GuestbookComment) { | templ GuestbookDashboardCommentView(data CommonData, w models.Website, c models.GuestbookComment) { | ||||||
|     {{ commentUrl := fmt.Sprintf("%s/dashboard/guestbook/comments/%s", wUrl(w), shortIdToSlug(c.ShortId)) }} | 	{{ commentUrl := fmt.Sprintf("%s/dashboard/guestbook/comments/%s", wUrl(w), shortIdToSlug(c.ShortId)) }} | ||||||
|     {{ hxHeaders := fmt.Sprintf("{\"X-CSRF-Token\": \"%s\"}", data.CSRFToken) }} | 	{{ hxHeaders := fmt.Sprintf("{\"X-CSRF-Token\": \"%s\"}", data.CSRFToken) }} | ||||||
|     <div class="comment"> | 	<div class="comment"> | ||||||
|         <div> | 		<div> | ||||||
|             if c.Deleted.IsZero() { | 			if c.Deleted.IsZero() { | ||||||
|                 <button class="danger" hx-delete={ commentUrl } hx-target="closest div.comment" hx-headers={ hxHeaders }>Delete</button> | 				<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 }> | 				<button class="outline" hx-put={ commentUrl } hx-target="closest div.comment" hx-headers={ hxHeaders }> | ||||||
|                     if !c.IsPublished { | 					if !c.IsPublished { | ||||||
|                         Publish | 						Publish | ||||||
|                     } else { | 					} else { | ||||||
|                         Hide | 						Hide | ||||||
|                     } | 					} | ||||||
|                 </button>  | 				</button> | ||||||
|             } | 			} | ||||||
|         </div> | 		</div> | ||||||
|         <div> | 		<div> | ||||||
|             <strong>{ c.AuthorName }</strong> | 			<strong>{ c.AuthorName }</strong> | ||||||
|             if len(c.AuthorEmail) > 0 { | 			if len(c.AuthorEmail) > 0 { | ||||||
|                 {{ email := "mailto:" + c.AuthorEmail}} | 				{{ email := "mailto:" + c.AuthorEmail }} | ||||||
|                 | <a href={ templ.URL(email) } target="_blank">{ c.AuthorEmail }</a> | 				| <a href={ templ.URL(email) } target="_blank">{ c.AuthorEmail }</a> | ||||||
|             } | 			} | ||||||
|             if len(c.AuthorSite) > 0 { | 			if len(c.AuthorSite) > 0 { | ||||||
|                 | <a href={ templ.URL(externalUrl(c.AuthorSite))} target="_blank">{ c.AuthorSite }</a> | 				| <a href={ templ.URL(externalUrl(c.AuthorSite)) } target="_blank">{ c.AuthorSite }</a> | ||||||
|             } | 			} | ||||||
|             <p> | 			<p> | ||||||
|                 { c.Created.Format("01-02-2006 03:04PM") } | 				{ c.Created.In(data.CurrentUser.Settings.LocalTimezone).Format("01-02-2006 03:04PM") } | ||||||
|             </p> | 			</p> | ||||||
|         </div> | 		</div> | ||||||
|         <p> | 		<p> | ||||||
|             { c.CommentText } | 			{ c.CommentText } | ||||||
|         </p> | 		</p> | ||||||
|         <hr> | 		<hr/> | ||||||
|     </div> | 	</div> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ commentForm(form forms.CommentCreateForm) { | templ commentForm(form forms.CommentCreateForm) { | ||||||
|     <div> | 	<div> | ||||||
|         <label for="authorname">Name: </label> | 		<label for="authorname">Name: </label> | ||||||
|         {{ error, exists := form.FieldErrors["authorName"] }} | 		{{ error, exists := form.FieldErrors["authorName"] }} | ||||||
|         if exists { | 		if exists { | ||||||
|         <label class="error">{ error }</label> | 			<label class="error">{ error }</label> | ||||||
|         } | 		} | ||||||
|         <input type="text" name="authorname" id="authorname"> | 		<input type="text" name="authorname" id="authorname"/> | ||||||
|     </div> | 	</div> | ||||||
|     <div> | 	<div> | ||||||
|         <label for="authoremail">Email: </label> | 		<label for="authoremail">Email: </label> | ||||||
|         {{ error, exists = form.FieldErrors["authorEmail"] }} | 		{{ error, exists = form.FieldErrors["authorEmail"] }} | ||||||
|         if exists { | 		if exists { | ||||||
|         <label class="error">{ error }</label> | 			<label class="error">{ error }</label> | ||||||
|         } | 		} | ||||||
|         <input type="text" name="authoremail" id="authoremail"> | 		<input type="text" name="authoremail" id="authoremail"/> | ||||||
|     </div> | 	</div> | ||||||
|     <div> | 	<div> | ||||||
|         <label for="authorsite">Site Url: </label> | 		<label for="authorsite">Site Url: </label> | ||||||
|         {{ error, exists = form.FieldErrors["authorSite"] }} | 		{{ error, exists = form.FieldErrors["authorSite"] }} | ||||||
|         if exists { | 		if exists { | ||||||
|         <label class="error">{ error }</label> | 			<label class="error">{ error }</label> | ||||||
|         } | 		} | ||||||
|         <input type="text" name="authorsite" id="authorsite"> | 		<input type="text" name="authorsite" id="authorsite"/> | ||||||
|     </div> | 	</div> | ||||||
|     <div> | 	<div> | ||||||
|         <label for="content">Comment: </label> | 		<label for="content">Comment: </label> | ||||||
|         {{ error, exists = form.FieldErrors["content"] }} | 		{{ error, exists = form.FieldErrors["content"] }} | ||||||
|         if exists { | 		if exists { | ||||||
|         <label class="error">{ error }</label> | 			<label class="error">{ error }</label> | ||||||
|         } | 		} | ||||||
|         <textarea name="content" id="content"></textarea> | 		<textarea name="content" id="content"></textarea> | ||||||
|     </div> | 	</div> | ||||||
|     <div> | 	<div> | ||||||
|         <input type="submit" value="Submit"> | 		<input type="submit" value="Submit"/> | ||||||
|     </div> | 	</div> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ GuestbookView(title string, data CommonData, website models.Website, guestbook models.Guestbook, comments []models.GuestbookComment, form forms.CommentCreateForm) { | 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)) }} | 	{{ postUrl := fmt.Sprintf("/websites/%s/guestbook/comments/create", shortIdToSlug(website.ShortId)) }} | ||||||
|     if data.IsHtmx { | 	if data.IsHtmx { | ||||||
|         @commentForm(form) | 		@commentForm(form) | ||||||
|     } else { | 	} else { | ||||||
|         <html> | 		<html> | ||||||
|             <head> | 			<head> | ||||||
|             <title>{ title }</title> | 				<title>{ title }</title> | ||||||
|             <link href="/static/css/classless.min.css" rel="stylesheet"> | 				<link href="/static/css/classless.min.css" rel="stylesheet"/> | ||||||
|             </head> | 			</head> | ||||||
|             <body> | 			<body> | ||||||
|                 <main> | 				<main> | ||||||
|                     <div> | 					<div> | ||||||
|                         <h1>Guestbook for { website.Name }</h1> | 						<h1>Guestbook for { website.Name }</h1> | ||||||
|                         <form action={ templ.URL(postUrl) } method="post"> | 						<form action={ templ.URL(postUrl) } method="post"> | ||||||
|                             <input type="hidden" name="csrf_token" value={data.CSRFToken}> | 							<input type="hidden" name="csrf_token" value={ data.CSRFToken }/> | ||||||
|                             @commentForm(form) | 							@commentForm(form) | ||||||
|                         </form> | 						</form> | ||||||
|                     </div> | 					</div> | ||||||
|                     <div id="comments"> | 					<div id="comments"> | ||||||
|                         if len(comments) == 0 { | 						if len(comments) == 0 { | ||||||
|                             <p>No comments yet!</p> | 							<p>No comments yet!</p> | ||||||
|                         } | 						} | ||||||
|                         for _, c := range comments { | 						for _, c := range comments { | ||||||
|                             <div> | 							<div> | ||||||
|                                 <h3>{ c.AuthorName }</h3> | 								<h3>{ c.AuthorName }</h3> | ||||||
|                                 { c.Created.Format("01-02-2006 03:04PM") } | 								{ c.Created.Format("01-02-2006 03:04PM") } | ||||||
|                                 <p> | 								<p> | ||||||
|                                     { c.CommentText } | 									{ c.CommentText } | ||||||
|                                 </p> | 								</p> | ||||||
|                             </div> | 							</div> | ||||||
|                         } | 						} | ||||||
|                     </div> | 					</div> | ||||||
|                     </main> | 				</main> | ||||||
|                 </body> | 			</body> | ||||||
|             </html> | 		</html> | ||||||
|         } | 	} | ||||||
|     } | } | ||||||
| 
 | 
 | ||||||
|     templ CreateGuestbookComment(title string, data CommonData, website models.Website, guestbook models.Guestbook, form forms.CommentCreateForm) { | 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)) }} | 	{{ postUrl := fmt.Sprintf("/websites/%s/guestbook/comments/create", shortIdToSlug(website.ShortId)) }} | ||||||
|         if data.IsHtmx { | 	if data.IsHtmx { | ||||||
|             <form hx-post={ postUrl } hx-target="closest div"> | 		<form hx-post={ postUrl } hx-target="closest div"> | ||||||
|             @commentForm(form) | 			@commentForm(form) | ||||||
|         </form> | 		</form> | ||||||
|     } else { | 	} else { | ||||||
|         @base(title, data) { | 		@base(title, data) { | ||||||
|             <form action={ templ.URL(postUrl) } method="post"> | 			<form action={ templ.URL(postUrl) } method="post"> | ||||||
|                 @commentForm(form) | 				@commentForm(form) | ||||||
|             </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) { | templ AllGuestbooksView(data CommonData, websites []models.Website) { | ||||||
|     @base("All Guestbooks", data) { | 	@base("All Guestbooks", data) { | ||||||
|         <div> | 		<div> | ||||||
|             <h1>All Guestbooks</h1> | 			<h1>All Guestbooks</h1> | ||||||
|             <p> | 			<p> | ||||||
|                 This page exists only for testing the service. | 				This page exists only for testing the service. | ||||||
|             </p> | 			</p> | ||||||
|             <ul> | 			<ul> | ||||||
|                 for _, w := range websites { | 				for _, w := range websites { | ||||||
|                     <li> | 					<li> | ||||||
|                         {{  gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId))}} | 						{{ gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId)) }} | ||||||
|                         <a href={ templ.URL(gbUrl) } target="_blank">{ w.Name }</a> | 						<a href={ templ.URL(gbUrl) } target="_blank">{ w.Name }</a> | ||||||
|                     </li> | 					</li> | ||||||
|                 } | 				} | ||||||
|             </ul> | 			</ul> | ||||||
|         </div> | 		</div> | ||||||
|     } | 	} | ||||||
| } | } | ||||||
| @ -11,6 +11,7 @@ import templruntime "github.com/a-h/templ/runtime" | |||||||
| import "fmt" | import "fmt" | ||||||
| import "git.32bit.cafe/32bitcafe/guestbook/internal/models" | import "git.32bit.cafe/32bitcafe/guestbook/internal/models" | ||||||
| import "git.32bit.cafe/32bitcafe/guestbook/internal/forms" | 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 { | 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) { | 	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 | 			var templ_7745c5c3_Var3 string | ||||||
| 			templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(website.SiteUrl) | 			templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(website.SiteUrl) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -131,7 +132,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G | |||||||
| 			var templ_7745c5c3_Var5 string | 			var templ_7745c5c3_Var5 string | ||||||
| 			templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(commentUrl) | 			templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(commentUrl) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -144,7 +145,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G | |||||||
| 			var templ_7745c5c3_Var6 string | 			var templ_7745c5c3_Var6 string | ||||||
| 			templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(hxHeaders) | 			templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(hxHeaders) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -157,7 +158,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G | |||||||
| 			var templ_7745c5c3_Var7 string | 			var templ_7745c5c3_Var7 string | ||||||
| 			templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(commentUrl) | 			templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(commentUrl) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -170,7 +171,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G | |||||||
| 			var templ_7745c5c3_Var8 string | 			var templ_7745c5c3_Var8 string | ||||||
| 			templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(hxHeaders) | 			templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(hxHeaders) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -203,7 +204,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G | |||||||
| 		var templ_7745c5c3_Var9 string | 		var templ_7745c5c3_Var9 string | ||||||
| 		templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName) | 		templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		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)) | 		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		if templ_7745c5c3_Err != nil { | ||||||
| @ -231,7 +232,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G | |||||||
| 			var templ_7745c5c3_Var11 string | 			var templ_7745c5c3_Var11 string | ||||||
| 			templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorEmail) | 			templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorEmail) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -259,7 +260,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G | |||||||
| 			var templ_7745c5c3_Var13 string | 			var templ_7745c5c3_Var13 string | ||||||
| 			templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorSite) | 			templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorSite) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -275,9 +276,9 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G | |||||||
| 			return templ_7745c5c3_Err | 			return templ_7745c5c3_Err | ||||||
| 		} | 		} | ||||||
| 		var templ_7745c5c3_Var14 string | 		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 { | 		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)) | 		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		if templ_7745c5c3_Err != nil { | ||||||
| @ -290,7 +291,7 @@ func GuestbookDashboardCommentView(data CommonData, w models.Website, c models.G | |||||||
| 		var templ_7745c5c3_Var15 string | 		var templ_7745c5c3_Var15 string | ||||||
| 		templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(c.CommentText) | 		templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(c.CommentText) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		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)) | 		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		if templ_7745c5c3_Err != nil { | ||||||
| @ -338,7 +339,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component { | |||||||
| 			var templ_7745c5c3_Var17 string | 			var templ_7745c5c3_Var17 string | ||||||
| 			templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(error) | 			templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(error) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -362,7 +363,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component { | |||||||
| 			var templ_7745c5c3_Var18 string | 			var templ_7745c5c3_Var18 string | ||||||
| 			templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(error) | 			templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(error) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -386,7 +387,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component { | |||||||
| 			var templ_7745c5c3_Var19 string | 			var templ_7745c5c3_Var19 string | ||||||
| 			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(error) | 			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(error) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -410,7 +411,7 @@ func commentForm(form forms.CommentCreateForm) templ.Component { | |||||||
| 			var templ_7745c5c3_Var20 string | 			var templ_7745c5c3_Var20 string | ||||||
| 			templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(error) | 			templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(error) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -464,7 +465,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb | |||||||
| 			var templ_7745c5c3_Var22 string | 			var templ_7745c5c3_Var22 string | ||||||
| 			templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(title) | 			templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(title) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -477,7 +478,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb | |||||||
| 			var templ_7745c5c3_Var23 string | 			var templ_7745c5c3_Var23 string | ||||||
| 			templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) | 			templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -499,7 +500,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb | |||||||
| 			var templ_7745c5c3_Var25 string | 			var templ_7745c5c3_Var25 string | ||||||
| 			templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken) | 			templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -531,7 +532,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb | |||||||
| 				var templ_7745c5c3_Var26 string | 				var templ_7745c5c3_Var26 string | ||||||
| 				templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName) | 				templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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)) | 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				if templ_7745c5c3_Err != nil { | ||||||
| @ -544,7 +545,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb | |||||||
| 				var templ_7745c5c3_Var27 string | 				var templ_7745c5c3_Var27 string | ||||||
| 				templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(c.Created.Format("01-02-2006 03:04PM")) | 				templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(c.Created.Format("01-02-2006 03:04PM")) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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)) | 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				if templ_7745c5c3_Err != nil { | ||||||
| @ -557,7 +558,7 @@ func GuestbookView(title string, data CommonData, website models.Website, guestb | |||||||
| 				var templ_7745c5c3_Var28 string | 				var templ_7745c5c3_Var28 string | ||||||
| 				templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(c.CommentText) | 				templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(c.CommentText) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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)) | 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				if templ_7745c5c3_Err != nil { | ||||||
| @ -607,7 +608,7 @@ func CreateGuestbookComment(title string, data CommonData, website models.Websit | |||||||
| 			var templ_7745c5c3_Var30 string | 			var templ_7745c5c3_Var30 string | ||||||
| 			templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(postUrl) | 			templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(postUrl) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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) { | 	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 | 		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | ||||||
| 		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | 		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 | 			templ_7745c5c3_Var33 = templ.NopComponent | ||||||
| 		} | 		} | ||||||
| 		ctx = templ.ClearChildren(ctx) | 		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_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | ||||||
| 			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) | 			templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) | ||||||
| 			if !templ_7745c5c3_IsBuffer { | 			if !templ_7745c5c3_IsBuffer { | ||||||
| @ -703,50 +784,241 @@ func AllGuestbooksView(data CommonData, websites []models.Website) templ.Compone | |||||||
| 				}() | 				}() | ||||||
| 			} | 			} | ||||||
| 			ctx = templ.InitializeContext(ctx) | 			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 { | 			if templ_7745c5c3_Err != nil { | ||||||
| 				return templ_7745c5c3_Err | 				return templ_7745c5c3_Err | ||||||
| 			} | 			} | ||||||
| 			for _, w := range websites { | 			templ_7745c5c3_Err = wSidebar(website).Render(ctx, templ_7745c5c3_Buffer) | ||||||
| 				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "<li>") | 			if templ_7745c5c3_Err != nil { | ||||||
| 				if templ_7745c5c3_Err != nil { | 				return templ_7745c5c3_Err | ||||||
| 					return templ_7745c5c3_Err | 			} | ||||||
| 				} | 			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "<div><h1>Guestbook Settings</h1><form hx-put=\"") | ||||||
| 				gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId)) | 			if templ_7745c5c3_Err != nil { | ||||||
| 				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<a href=\"") | 				return templ_7745c5c3_Err | ||||||
| 				if templ_7745c5c3_Err != nil { | 			} | ||||||
| 					return templ_7745c5c3_Err | 			var templ_7745c5c3_Var39 string | ||||||
| 				} | 			templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(putUrl) | ||||||
| 				var templ_7745c5c3_Var35 templ.SafeURL = templ.URL(gbUrl) | 			if templ_7745c5c3_Err != nil { | ||||||
| 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var35))) | 				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 166, Col: 25} | ||||||
| 				if templ_7745c5c3_Err != nil { | 			} | ||||||
| 					return templ_7745c5c3_Err | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) | ||||||
| 				} | 			if templ_7745c5c3_Err != nil { | ||||||
| 				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\" target=\"_blank\">") | 				return templ_7745c5c3_Err | ||||||
| 				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 { | ||||||
| 				var templ_7745c5c3_Var36 string | 				return templ_7745c5c3_Err | ||||||
| 				templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(w.Name) | 			} | ||||||
| 				if templ_7745c5c3_Err != nil { | 			var templ_7745c5c3_Var40 string | ||||||
| 					return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 164, Col: 77} | 			templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken) | ||||||
| 				} | 			if templ_7745c5c3_Err != nil { | ||||||
| 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36)) | 				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 167, Col: 66} | ||||||
| 				if templ_7745c5c3_Err != nil { | 			} | ||||||
| 					return templ_7745c5c3_Err | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) | ||||||
| 				} | 			if templ_7745c5c3_Err != nil { | ||||||
| 				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "</a></li>") | 				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 { | 				if templ_7745c5c3_Err != nil { | ||||||
| 					return templ_7745c5c3_Err | 					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 { | 			if templ_7745c5c3_Err != nil { | ||||||
| 				return templ_7745c5c3_Err | 				return templ_7745c5c3_Err | ||||||
| 			} | 			} | ||||||
| 			return nil | 			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 { | 		if templ_7745c5c3_Err != nil { | ||||||
| 			return templ_7745c5c3_Err | 			return templ_7745c5c3_Err | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -5,79 +5,99 @@ import ( | |||||||
| 	"git.32bit.cafe/32bitcafe/guestbook/internal/models" | 	"git.32bit.cafe/32bitcafe/guestbook/internal/models" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| templ UserLogin (title string, data CommonData, form forms.UserLoginForm) { | templ UserLogin(title string, data CommonData, form forms.UserLoginForm) { | ||||||
|     @base(title, data) { | 	@base(title, data) { | ||||||
|         <h1>Login</h1> | 		<h1>Login</h1> | ||||||
|         <form action="/users/login" method="POST" novalidate> | 		<form action="/users/login" method="POST" novalidate> | ||||||
|             <input type="hidden" name="csrf_token" value={ data.CSRFToken }> | 			<input type="hidden" name="csrf_token" value={ data.CSRFToken }/> | ||||||
|             for _, error := range form.NonFieldErrors { | 			for _, error := range form.NonFieldErrors { | ||||||
|                 <div class="error">{ error }</div> | 				<div class="error">{ error }</div> | ||||||
|             } | 			} | ||||||
|             <div> | 			<div> | ||||||
|                 <label>Email: </label> | 				<label>Email: </label> | ||||||
|                 {{ error, exists := form.FieldErrors["email"] }} | 				{{ error, exists := form.FieldErrors["email"] }} | ||||||
|                 if exists { | 				if exists { | ||||||
|                     <label class="error">{ error }</label> | 					<label class="error">{ error }</label> | ||||||
|                 } | 				} | ||||||
|                 <input type="email" name="email" value={form.Email}> | 				<input type="email" name="email" value={ form.Email }/> | ||||||
|             </div> | 			</div> | ||||||
|             <div> | 			<div> | ||||||
|                 <label>Password: </label> | 				<label>Password: </label> | ||||||
|                 {{ error, exists = form.FieldErrors["password"] }} | 				{{ error, exists = form.FieldErrors["password"] }} | ||||||
|                 if exists { | 				if exists { | ||||||
|                     <label class="error">{ error }</label> | 					<label class="error">{ error }</label> | ||||||
|                 } | 				} | ||||||
|                 <input type="password" name="password"> | 				<input type="password" name="password"/> | ||||||
|             </div> | 			</div> | ||||||
|             <div> | 			<div> | ||||||
|                 <input type="submit" value="login"> | 				<input type="submit" value="login"/> | ||||||
|             </div> | 			</div> | ||||||
|         </form> | 		</form> | ||||||
|     } | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ UserRegistration (title string, data CommonData, form forms.UserRegistrationForm) { | templ UserRegistration(title string, data CommonData, form forms.UserRegistrationForm) { | ||||||
|     @base(title, data) { | 	@base(title, data) { | ||||||
|         <h1>User Registration</h1> | 		<h1>User Registration</h1> | ||||||
|         <form action="/users/register" method="post"> | 		<form action="/users/register" method="post"> | ||||||
|             <input type="hidden" name="csrf_token" value={ data.CSRFToken }> | 			<input type="hidden" name="csrf_token" value={ data.CSRFToken }/> | ||||||
|             <div> | 			<div> | ||||||
|                 {{ error, exists := form.FieldErrors["name"] }} | 				{{ error, exists := form.FieldErrors["name"] }} | ||||||
|                 <label for="username">Username: </label> | 				<label for="username">Username: </label> | ||||||
|                 if exists { | 				if exists { | ||||||
|                     <label class="error">{ error }</label> | 					<label class="error">{ error }</label> | ||||||
|                 } | 				} | ||||||
|                 <input type="text" name="username" id="username" value={form.Name} required /> | 				<input type="text" name="username" id="username" value={ form.Name } required/> | ||||||
|             </div> | 			</div> | ||||||
|             <div> | 			<div> | ||||||
|                 {{ error, exists = form.FieldErrors["email"] }} | 				{{ error, exists = form.FieldErrors["email"] }} | ||||||
|                 <label for="email">Email: </label> | 				<label for="email">Email: </label> | ||||||
|                 if exists { | 				if exists { | ||||||
|                     <label class="error">{ error }</label> | 					<label class="error">{ error }</label> | ||||||
|                 } | 				} | ||||||
|                 <input type="text" name="email" id="email" value={form.Email} required /> | 				<input type="text" name="email" id="email" value={ form.Email } required/> | ||||||
|             </div> | 			</div> | ||||||
|             <div> | 			<div> | ||||||
|                 {{ error, exists = form.FieldErrors["password"] }} | 				{{ error, exists = form.FieldErrors["password"] }} | ||||||
|                 <label for="password">Password: </label> | 				<label for="password">Password: </label> | ||||||
|                 if exists { | 				if exists { | ||||||
|                     <label class="error">{ error }</label> | 					<label class="error">{ error }</label> | ||||||
|                 } | 				} | ||||||
|                 <input type="password" name="password" id="password" /> | 				<input type="password" name="password" id="password"/> | ||||||
|             </div> | 			</div> | ||||||
|             <div> | 			<div> | ||||||
|                 <input type="submit" value="Register" /> | 				<input type="submit" value="Register"/> | ||||||
|             </div> | 			</div> | ||||||
|         </form> | 		</form> | ||||||
|     } | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ UserProfile (title string, data CommonData, user models.User) { | templ UserProfile(title string, data CommonData, user models.User) { | ||||||
|     @base(title, data) { | 	@base(title, data) { | ||||||
|         <h1>{ user.Username }</h1> | 		<h1>{ user.Username }</h1> | ||||||
|         <p>{ user.Email }</p> | 		<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 | 			var templ_7745c5c3_Var3 string | ||||||
| 			templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken) | 			templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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 | 				var templ_7745c5c3_Var4 string | ||||||
| 				templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(error) | 				templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(error) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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)) | 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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 | 				var templ_7745c5c3_Var5 string | ||||||
| 				templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(error) | 				templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(error) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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)) | 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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 | 			var templ_7745c5c3_Var6 string | ||||||
| 			templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email) | 			templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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 | 				var templ_7745c5c3_Var7 string | ||||||
| 				templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(error) | 				templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(error) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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)) | 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				if templ_7745c5c3_Err != nil { | ||||||
| @ -197,7 +197,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration | |||||||
| 			var templ_7745c5c3_Var10 string | 			var templ_7745c5c3_Var10 string | ||||||
| 			templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken) | 			templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -220,7 +220,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration | |||||||
| 				var templ_7745c5c3_Var11 string | 				var templ_7745c5c3_Var11 string | ||||||
| 				templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(error) | 				templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(error) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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)) | 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				if templ_7745c5c3_Err != nil { | ||||||
| @ -238,7 +238,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration | |||||||
| 			var templ_7745c5c3_Var12 string | 			var templ_7745c5c3_Var12 string | ||||||
| 			templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(form.Name) | 			templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(form.Name) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -261,7 +261,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration | |||||||
| 				var templ_7745c5c3_Var13 string | 				var templ_7745c5c3_Var13 string | ||||||
| 				templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(error) | 				templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(error) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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)) | 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				if templ_7745c5c3_Err != nil { | ||||||
| @ -279,7 +279,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration | |||||||
| 			var templ_7745c5c3_Var14 string | 			var templ_7745c5c3_Var14 string | ||||||
| 			templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email) | 			templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -302,7 +302,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration | |||||||
| 				var templ_7745c5c3_Var15 string | 				var templ_7745c5c3_Var15 string | ||||||
| 				templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(error) | 				templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(error) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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)) | 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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 | 			var templ_7745c5c3_Var18 string | ||||||
| 			templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(user.Username) | 			templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(user.Username) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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 | 			var templ_7745c5c3_Var19 string | ||||||
| 			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email) | 			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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) { | 	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 | 		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | ||||||
| 		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | 		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | ||||||
| @ -421,6 +421,111 @@ func UserSettings() templ.Component { | |||||||
| 			templ_7745c5c3_Var20 = templ.NopComponent | 			templ_7745c5c3_Var20 = templ.NopComponent | ||||||
| 		} | 		} | ||||||
| 		ctx = templ.ClearChildren(ctx) | 		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 | 		return nil | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,151 +4,141 @@ import "fmt" | |||||||
| import "git.32bit.cafe/32bitcafe/guestbook/internal/models" | import "git.32bit.cafe/32bitcafe/guestbook/internal/models" | ||||||
| import "git.32bit.cafe/32bitcafe/guestbook/internal/forms" | import "git.32bit.cafe/32bitcafe/guestbook/internal/forms" | ||||||
| 
 | 
 | ||||||
| func wUrl (w models.Website) string { | func wUrl(w models.Website) string { | ||||||
|     return fmt.Sprintf("/websites/%s", shortIdToSlug(w.ShortId)) | 	return fmt.Sprintf("/websites/%s", shortIdToSlug(w.ShortId)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ wSidebar(website models.Website) { | templ wSidebar(website models.Website) { | ||||||
|     {{ dashUrl := wUrl(website) + "/dashboard" }} | 	{{ dashUrl := wUrl(website) + "/dashboard" }} | ||||||
|     {{ gbUrl := wUrl(website) + "/guestbook" }} | 	{{ gbUrl := wUrl(website) + "/guestbook" }} | ||||||
|     <nav> | 	<nav> | ||||||
|         <div> | 		<div> | ||||||
|             <h2>{ website.Name}</h2> | 			<h2>{ website.Name }</h2> | ||||||
|             <ul> | 			<ul> | ||||||
|                 <li><a href={ templ.URL(dashUrl) }>Dashboard</a></li> | 				<li><a href={ templ.URL(dashUrl) }>Dashboard</a></li> | ||||||
|                 <li><a href={ templ.URL(externalUrl(website.SiteUrl)) } target="_blank">View Website</a></li> | 				<li><a href={ templ.URL(externalUrl(website.SiteUrl)) } target="_blank">View Website</a></li> | ||||||
|             </ul> | 			</ul> | ||||||
|             <h3>Guestbook</h3> | 			<h3>Guestbook</h3> | ||||||
|             <ul> | 			<ul> | ||||||
|                 <li><a href={ templ.URL(gbUrl) } target="_blank">View Guestbook</a></li> | 				<li><a href={ templ.URL(gbUrl) } target="_blank">View Guestbook</a></li> | ||||||
|             </ul> | 			</ul> | ||||||
|             <ul> | 			<ul> | ||||||
|                 <li><a href={ templ.URL(dashUrl + "/guestbook/comments") }>Manage messages</a></li> | 				<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/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> | ||||||
|                 <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> | ||||||
|             <ul> | 			<ul> | ||||||
|                 <li><a href={ templ.URL(dashUrl + "/guestbook/themes") }>Themes</a></li> | 				<li><a href={ templ.URL(dashUrl + "/guestbook/themes") }>Themes</a></li> | ||||||
|                 <li><a href={ templ.URL(dashUrl + "/guestbook/customize") }>Custom CSS</a></li> | 				<li><a href={ templ.URL(dashUrl + "/guestbook/customize") }>Custom CSS</a></li> | ||||||
|             </ul> | 			</ul> | ||||||
|         </div> | 		</div> | ||||||
|         <div> | 		<div> | ||||||
|             <h3>Feeds</h3> | 			<h3>Feeds</h3> | ||||||
|             <p>Coming Soon</p> | 			<p>Coming Soon</p> | ||||||
|         </div> | 		</div> | ||||||
|         <div> | 		<div> | ||||||
|             <h3>Account</h3> | 			<h3>Account</h3> | ||||||
|             <ul> | 			<ul> | ||||||
|                 <li><a href="/users/settings">Settings</a></li> | 				<li><a href="/users/settings">Settings</a></li> | ||||||
|                 <li><a href="/users/privacy">Privacy</a></li> | 				<li><a href="/users/privacy">Privacy</a></li> | ||||||
|                 <li><a href="/help">Help</a></li> | 				<li><a href="/help">Help</a></li> | ||||||
|             </ul> | 			</ul> | ||||||
|         </div> | 		</div> | ||||||
|     </nav> | 	</nav> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ displayWebsites (websites []models.Website) { | templ displayWebsites(websites []models.Website) { | ||||||
|     if len(websites) == 0 { | 	if len(websites) == 0 { | ||||||
|         <p>No Websites yet. <a href="">Register a website.</a></p> | 		<p>No Websites yet. <a href="">Register a website.</a></p> | ||||||
|     } else { | 	} else { | ||||||
|         <ul id="websites" hx-get="/websites" hx-trigger="newWebsite from:body" hx-swap="outerHTML"> | 		<ul id="websites" hx-get="/websites" hx-trigger="newWebsite from:body" hx-swap="outerHTML"> | ||||||
|             for _, w := range websites { | 			for _, w := range websites { | ||||||
|                 <li> | 				<li> | ||||||
|                     <a href={ templ.URL(wUrl(w) + "/dashboard")}>{ w.Name }</a> | 					<a href={ templ.URL(wUrl(w) + "/dashboard") }>{ w.Name }</a> | ||||||
|                 </li> | 				</li> | ||||||
|             } | 			} | ||||||
|         </ul> | 		</ul> | ||||||
|     } | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) { | templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) { | ||||||
|     <input type="hidden" name="csrf_token" value={csrfToken}> | 	<input type="hidden" name="csrf_token" value={ csrfToken }/> | ||||||
|     <div> | 	<div> | ||||||
|         {{ err, exists := form.FieldErrors["sitename"]}} | 		{{ err, exists := form.FieldErrors["sitename"] }} | ||||||
|         <label for="sitename">Site Name: </label> | 		<label for="sitename">Site Name: </label> | ||||||
|         if exists { | 		if exists { | ||||||
|             <label class="error">{ err }</label> | 			<label class="error">{ err }</label> | ||||||
|         } | 		} | ||||||
|         <input type="text" name="sitename" id="sitename" required /> | 		<input type="text" name="sitename" id="sitename" required/> | ||||||
|     </div> | 	</div> | ||||||
|     <div> | 	<div> | ||||||
|         {{ err, exists = form.FieldErrors["siteurl"] }} | 		{{ err, exists = form.FieldErrors["siteurl"] }} | ||||||
|         <label for="siteurl">Site URL: </label> | 		<label for="siteurl">Site URL: </label> | ||||||
|         if exists { | 		if exists { | ||||||
|             <label class="error">{ err }</label> | 			<label class="error">{ err }</label> | ||||||
|         } | 		} | ||||||
|         <input type="text" name="siteurl" id="siteurl" required /> | 		<input type="text" name="siteurl" id="siteurl" required/> | ||||||
|     </div> | 	</div> | ||||||
|     <div> | 	<div> | ||||||
|         {{ err, exists = form.FieldErrors["authorname"] }} | 		{{ err, exists = form.FieldErrors["authorname"] }} | ||||||
|         <label for="authorname">Site Author: </label> | 		<label for="authorname">Site Author: </label> | ||||||
|         if exists { | 		if exists { | ||||||
|             <label class="error">{ err }</label> | 			<label class="error">{ err }</label> | ||||||
|         } | 		} | ||||||
|         <input type="text" name="authorname" id="authorname" required /> | 		<input type="text" name="authorname" id="authorname" required/> | ||||||
|     </div> | 	</div> | ||||||
|     <div> | 	<div> | ||||||
|         <button type="submit">Submit</button> | 		<button type="submit">Submit</button> | ||||||
|     </div> | 	</div> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ WebsiteCreateButton() { | 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) { | templ WebsiteList(title string, data CommonData, websites []models.Website) { | ||||||
|     if data.IsHtmx { | 	@base(title, data) { | ||||||
|         @displayWebsites(websites) | 		<h1>My Websites</h1> | ||||||
|     } else { | 		<div> | ||||||
|         @base(title, data) { | 			@WebsiteCreateButton() | ||||||
|             <h1>My Websites</h1> | 		</div> | ||||||
|             <div> | 		<div> | ||||||
|                 @WebsiteCreateButton() | 			@displayWebsites(websites) | ||||||
|             </div> | 		</div> | ||||||
|             <div> | 	} | ||||||
|                 @displayWebsites(websites) |  | ||||||
|             </div> |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ WebsiteDashboard(title string, data CommonData, website models.Website) { | templ WebsiteDashboard(title string, data CommonData, website models.Website) { | ||||||
|     @base(title, data) { | 	@base(title, data) { | ||||||
|         <div id="dashboard"> | 		<div id="dashboard"> | ||||||
|             @wSidebar(website)  | 			@wSidebar(website) | ||||||
|             <div> | 			<div> | ||||||
|                 <h1>{ website.Name }</h1> | 				<h1>{ website.Name }</h1> | ||||||
|                 <p> | 				<p> | ||||||
|                     Stats and stuff will go here. | 					Stats and stuff will go here. | ||||||
|                 </p> | 				</p> | ||||||
|             </div> | 			</div> | ||||||
|         </div> | 		</div> | ||||||
|     } | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ WebsiteDashboardComingSoon(title string, data CommonData, website models.Website) { | templ WebsiteDashboardComingSoon(title string, data CommonData, website models.Website) { | ||||||
|     @base(title, data) { | 	@base(title, data) { | ||||||
|         <div id="dashboard"> | 		<div id="dashboard"> | ||||||
|             @wSidebar(website)  | 			@wSidebar(website) | ||||||
|             <div> | 			<div> | ||||||
|                 <h1>{ website.Name }</h1> | 				<h1>{ website.Name }</h1> | ||||||
|                 <p> | 				<p> | ||||||
|                     Coming Soon | 					Coming Soon | ||||||
|                 </p> | 				</p> | ||||||
|             </div> | 			</div> | ||||||
|         </div> | 		</div> | ||||||
|     } | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| templ WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) { | templ WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) { | ||||||
|     if data.IsHtmx { | 	<form hx-post="/websites/create" hx-target="closest div"> | ||||||
|         <form hx-post="/websites/create" hx-target="closest div"> | 		@websiteCreateForm(data.CSRFToken, form) | ||||||
|             @websiteCreateForm(data.CSRFToken, form) | 	</form> | ||||||
|         </form> |  | ||||||
|     } else { |  | ||||||
|         <form action="/websites/create" method="post"> |  | ||||||
|             @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 | 		var templ_7745c5c3_Var2 string | ||||||
| 		templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) | 		templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		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)) | 		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		if templ_7745c5c3_Err != nil { | ||||||
| @ -101,21 +101,21 @@ func wSidebar(website models.Website) templ.Component { | |||||||
| 		if templ_7745c5c3_Err != nil { | 		if templ_7745c5c3_Err != nil { | ||||||
| 			return templ_7745c5c3_Err | 			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))) | 		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var8))) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		if templ_7745c5c3_Err != nil { | ||||||
| 			return templ_7745c5c3_Err | 			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 { | 		if templ_7745c5c3_Err != nil { | ||||||
| 			return templ_7745c5c3_Err | 			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))) | 		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var9))) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		if templ_7745c5c3_Err != nil { | ||||||
| 			return templ_7745c5c3_Err | 			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 { | 		if templ_7745c5c3_Err != nil { | ||||||
| 			return templ_7745c5c3_Err | 			return templ_7745c5c3_Err | ||||||
| 		} | 		} | ||||||
| @ -189,7 +189,7 @@ func displayWebsites(websites []models.Website) templ.Component { | |||||||
| 				var templ_7745c5c3_Var14 string | 				var templ_7745c5c3_Var14 string | ||||||
| 				templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(w.Name) | 				templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(w.Name) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				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)) | 				_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) | ||||||
| 				if templ_7745c5c3_Err != nil { | 				if templ_7745c5c3_Err != nil { | ||||||
| @ -237,7 +237,7 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com | |||||||
| 		var templ_7745c5c3_Var16 string | 		var templ_7745c5c3_Var16 string | ||||||
| 		templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(csrfToken) | 		templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(csrfToken) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		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)) | 		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) | ||||||
| 		if templ_7745c5c3_Err != nil { | 		if templ_7745c5c3_Err != nil { | ||||||
| @ -260,7 +260,7 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com | |||||||
| 			var templ_7745c5c3_Var17 string | 			var templ_7745c5c3_Var17 string | ||||||
| 			templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(err) | 			templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(err) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -288,7 +288,7 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com | |||||||
| 			var templ_7745c5c3_Var18 string | 			var templ_7745c5c3_Var18 string | ||||||
| 			templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(err) | 			templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(err) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -316,7 +316,7 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com | |||||||
| 			var templ_7745c5c3_Var19 string | 			var templ_7745c5c3_Var19 string | ||||||
| 			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(err) | 			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(err) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -385,50 +385,43 @@ func WebsiteList(title string, data CommonData, websites []models.Website) templ | |||||||
| 			templ_7745c5c3_Var21 = templ.NopComponent | 			templ_7745c5c3_Var21 = templ.NopComponent | ||||||
| 		} | 		} | ||||||
| 		ctx = templ.ClearChildren(ctx) | 		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) | 			templ_7745c5c3_Err = displayWebsites(websites).Render(ctx, templ_7745c5c3_Buffer) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| 				return templ_7745c5c3_Err | 				return templ_7745c5c3_Err | ||||||
| 			} | 			} | ||||||
| 		} else { | 			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</div>") | ||||||
| 			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) |  | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| 				return templ_7745c5c3_Err | 				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 | 		return nil | ||||||
| 	}) | 	}) | ||||||
| @ -482,7 +475,7 @@ func WebsiteDashboard(title string, data CommonData, website models.Website) tem | |||||||
| 			var templ_7745c5c3_Var25 string | 			var templ_7745c5c3_Var25 string | ||||||
| 			templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) | 			templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -550,7 +543,7 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We | |||||||
| 			var templ_7745c5c3_Var28 string | 			var templ_7745c5c3_Var28 string | ||||||
| 			templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) | 			templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			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)) | 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) | ||||||
| 			if templ_7745c5c3_Err != nil { | 			if templ_7745c5c3_Err != nil { | ||||||
| @ -591,32 +584,17 @@ func WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) | |||||||
| 			templ_7745c5c3_Var29 = templ.NopComponent | 			templ_7745c5c3_Var29 = templ.NopComponent | ||||||
| 		} | 		} | ||||||
| 		ctx = templ.ClearChildren(ctx) | 		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\">") | ||||||
| 			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<form hx-post=\"/websites/create\" hx-target=\"closest div\">") | 		if templ_7745c5c3_Err != nil { | ||||||
| 			if templ_7745c5c3_Err != nil { | 			return templ_7745c5c3_Err | ||||||
| 				return templ_7745c5c3_Err | 		} | ||||||
| 			} | 		templ_7745c5c3_Err = websiteCreateForm(data.CSRFToken, form).Render(ctx, templ_7745c5c3_Buffer) | ||||||
| 			templ_7745c5c3_Err = websiteCreateForm(data.CSRFToken, form).Render(ctx, templ_7745c5c3_Buffer) | 		if templ_7745c5c3_Err != nil { | ||||||
| 			if templ_7745c5c3_Err != nil { | 			return templ_7745c5c3_Err | ||||||
| 				return templ_7745c5c3_Err | 		} | ||||||
| 			} | 		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</form>") | ||||||
| 			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</form>") | 		if templ_7745c5c3_Err != nil { | ||||||
| 			if templ_7745c5c3_Err != nil { | 			return templ_7745c5c3_Err | ||||||
| 				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 |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 		return nil | 		return nil | ||||||
| 	}) | 	}) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user