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