diff --git a/cmd/web/handlers_website.go b/cmd/web/handlers_website.go index d1c8d7c..add16a1 100644 --- a/cmd/web/handlers_website.go +++ b/cmd/web/handlers_website.go @@ -113,6 +113,7 @@ func (app *application) getWebsiteSettings(w http.ResponseWriter, r *http.Reques } else { app.serverError(w, r, err) } + return } var form forms.WebsiteSettingsForm data := app.newCommonData(r) @@ -128,6 +129,7 @@ func (app *application) putWebsiteSettings(w http.ResponseWriter, r *http.Reques } else { app.serverError(w, r, err) } + return } var form forms.WebsiteSettingsForm @@ -185,3 +187,39 @@ func (app *application) putWebsiteSettings(w http.ResponseWriter, r *http.Reques views.SettingsForm(data, website, forms.WebsiteSettingsForm{}, "Settings changed successfully").Render(r.Context(), w) } + +func (app *application) deleteWebsite(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) + } + return + } + var form forms.WebsiteDeleteForm + err = app.decodePostForm(r, &form) + if err != nil { + app.serverError(w, r, err) + return + } + + form.CheckField(validator.Equals(website.Name, form.Delete), "delete", "Input must match site name exactly") + if !form.Valid() { + data := app.newCommonData(r) + views.DeleteForm(data, website, form).Render(r.Context(), w) + return + } + + err = app.websites.Delete(website.ID) + if err != nil { + app.serverError(w, r, err) + return + } + + app.sessionManager.Put(r.Context(), "flash", "Website Deleted") + w.Header().Add("HX-Redirect", "/websites") + w.WriteHeader(http.StatusOK) +} diff --git a/cmd/web/routes.go b/cmd/web/routes.go index 0528c63..89884d5 100644 --- a/cmd/web/routes.go +++ b/cmd/web/routes.go @@ -50,7 +50,8 @@ func (app *application) routes() http.Handler { 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/settings", protected.ThenFunc(app.getWebsiteSettings)) - mux.Handle("PUT /websites/{id}/dashboard/settings", protected.ThenFunc(app.putWebsiteSettings)) + mux.Handle("PUT /websites/{id}/settings", protected.ThenFunc(app.putWebsiteSettings)) + mux.Handle("PUT /websites/{id}", protected.ThenFunc(app.deleteWebsite)) 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)) diff --git a/internal/forms/forms.go b/internal/forms/forms.go index 99d8d1c..ebb7b25 100644 --- a/internal/forms/forms.go +++ b/internal/forms/forms.go @@ -31,6 +31,11 @@ type WebsiteCreateForm struct { validator.Validator `schema:"-"` } +type WebsiteDeleteForm struct { + Delete string `schema:"delete"` + validator.Validator `schema:"-"` +} + type UserSettingsForm struct { LocalTimezone string `schema:"timezones"` validator.Validator `schema:"-"` diff --git a/internal/models/mocks/website.go b/internal/models/mocks/website.go index 21f2800..bc50020 100644 --- a/internal/models/mocks/website.go +++ b/internal/models/mocks/website.go @@ -72,6 +72,9 @@ func (m *WebsiteModel) GetAll() ([]models.Website, error) { func (m *WebsiteModel) Update(w models.Website) error { return nil } +func (m *WebsiteModel) Delete(websiteId int64) error { + return nil +} func (m *WebsiteModel) InitializeSettingsMap() error { return nil diff --git a/internal/models/website.go b/internal/models/website.go index cdc4102..6385530 100644 --- a/internal/models/website.go +++ b/internal/models/website.go @@ -101,6 +101,7 @@ type WebsiteModelInterface interface { InitializeSettingsMap() error UpdateGuestbookSettings(guestbookId int64, settings GuestbookSettings) error UpdateSetting(guestbookId int64, setting Setting, value string) error + Delete(websiteId int64) error } func (m *WebsiteModel) Insert(shortId uint64, userId int64, siteName, siteUrl, authorName string) (int64, error) { @@ -244,7 +245,7 @@ func (m *WebsiteModel) GetAllUser(userId 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.UserId = ?` + WHERE w.UserId = ? AND w.Deleted IS NULL` rows, err := m.DB.Query(stmt, userId) if err != nil { return nil, err @@ -273,7 +274,7 @@ func (m *WebsiteModel) GetAllUser(userId int64) ([]Website, error) { func (m *WebsiteModel) GetAll() ([]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` + FROM websites AS w INNER JOIN guestbooks AS g ON w.Id = g.WebsiteId WHERE w.Deleted IS NULL` rows, err := m.DB.Query(stmt) if err != nil { return nil, err @@ -314,6 +315,21 @@ func (m *WebsiteModel) Update(w Website) error { return nil } +func (m *WebsiteModel) Delete(websiteId int64) error { + stmt := `UPDATE websites SET Deleted = ? WHERE ID = ?` + r, err := m.DB.Exec(stmt, time.Now().UTC(), websiteId) + if err != nil { + return err + } + if rows, err := r.RowsAffected(); rows != 1 { + if err != nil { + return err + } + return errors.New("Failed to update website") + } + return 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 diff --git a/internal/validator/validator.go b/internal/validator/validator.go index 62037b2..5991605 100644 --- a/internal/validator/validator.go +++ b/internal/validator/validator.go @@ -57,3 +57,7 @@ func MinChars(value string, n int) bool { func Matches(value string, rx *regexp.Regexp) bool { return rx.MatchString(value) } + +func Equals(expected, actual string) bool { + return expected == actual +} diff --git a/ui/views/websites.templ b/ui/views/websites.templ index 03b9168..fab8123 100644 --- a/ui/views/websites.templ +++ b/ui/views/websites.templ @@ -232,7 +232,7 @@ templ guestbookSettingsForm(data CommonData, website models.Website, gb models.G } templ SettingsForm(data CommonData, website models.Website, form forms.WebsiteSettingsForm, msg string) { - {{ putUrl := fmt.Sprintf("/websites/%s/dashboard/settings", shortIdToSlug(website.ShortId)) }} + {{ putUrl := fmt.Sprintf("/websites/%s/settings", shortIdToSlug(website.ShortId)) }} {{ gb := website.Guestbook }}

@@ -247,6 +247,22 @@ templ SettingsForm(data CommonData, website models.Website, form forms.WebsiteSe

} +templ DeleteForm(data CommonData, website models.Website, form forms.WebsiteDeleteForm) { + {{ putUrl := fmt.Sprintf("/websites/%s", shortIdToSlug(website.ShortId)) }} +
+ +

Delete Website

+

Deleting a website is permanent. Be absolutely sure before proceeding.

+ {{ err, exists := form.FieldErrors["delete"] }} + + if exists { + + } + + +
+} + templ WebsiteDashboardSettings(data CommonData, website models.Website, form forms.WebsiteSettingsForm) { {{ title := fmt.Sprintf("%s - Settings", website.Name) }} @base(title, data) { @@ -254,14 +270,7 @@ templ WebsiteDashboardSettings(data CommonData, website models.Website, form for @wSidebar(website)
@SettingsForm(data, website, form, "") -
- -

Delete Website

-

Deleting a website is permanent. Be absolutely sure before proceeding.

- - - -
+ @DeleteForm(data, website, forms.WebsiteDeleteForm{})
} diff --git a/ui/views/websites_templ.go b/ui/views/websites_templ.go index 258a76d..e22ed5c 100644 --- a/ui/views/websites_templ.go +++ b/ui/views/websites_templ.go @@ -872,7 +872,7 @@ func SettingsForm(data CommonData, website models.Website, form forms.WebsiteSet templ_7745c5c3_Var41 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - putUrl := fmt.Sprintf("/websites/%s/dashboard/settings", shortIdToSlug(website.ShortId)) + putUrl := fmt.Sprintf("/websites/%s/settings", shortIdToSlug(website.ShortId)) gb := website.Guestbook templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "

Delete Website

Deleting a website is permanent. Be absolutely sure before proceeding.

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + err, exists := form.FieldErrors["delete"] + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if exists { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func WebsiteDashboardSettings(data CommonData, website models.Website, form forms.WebsiteSettingsForm) 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_Var49 := templ.GetChildren(ctx) + if templ_7745c5c3_Var49 == nil { + templ_7745c5c3_Var49 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) title := fmt.Sprintf("%s - Settings", website.Name) - templ_7745c5c3_Var46 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var50 := 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 { @@ -967,7 +1051,7 @@ func WebsiteDashboardSettings(data CommonData, website models.Website, form form }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -975,7 +1059,7 @@ func WebsiteDashboardSettings(data CommonData, website models.Website, form form if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -983,26 +1067,17 @@ func WebsiteDashboardSettings(data CommonData, website models.Website, form form if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "

Delete Website

Deleting a website is permanent. Be absolutely sure before proceeding.

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var46), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var50), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1026,12 +1101,12 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var48 := templ.GetChildren(ctx) - if templ_7745c5c3_Var48 == nil { - templ_7745c5c3_Var48 = templ.NopComponent + templ_7745c5c3_Var51 := templ.GetChildren(ctx) + if templ_7745c5c3_Var51 == nil { + templ_7745c5c3_Var51 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var49 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var52 := 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 { @@ -1043,7 +1118,7 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1051,13 +1126,13 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "

Coming Soon

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "

Coming Soon

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var49), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var52), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1081,12 +1156,12 @@ func WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var50 := templ.GetChildren(ctx) - if templ_7745c5c3_Var50 == nil { - templ_7745c5c3_Var50 = templ.NopComponent + templ_7745c5c3_Var53 := templ.GetChildren(ctx) + if templ_7745c5c3_Var53 == nil { + templ_7745c5c3_Var53 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var51 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var54 := 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 { @@ -1098,7 +1173,7 @@ func WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1106,13 +1181,13 @@ func WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var51), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var54), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }