From 133d8bcfe9033283ade32fc15261f2a3b5e3ccc4 Mon Sep 17 00:00:00 2001 From: yequari Date: Fri, 27 Jun 2025 13:24:03 -0700 Subject: [PATCH] normalize urls, fix website creation --- cmd/web/handlers_guestbook.go | 12 +- cmd/web/handlers_guestbook_test.go | 85 +++ cmd/web/handlers_website.go | 14 +- cmd/web/helpers.go | 16 +- internal/models/mocks/guestbookcomment.go | 17 + internal/models/mocks/website.go | 13 +- internal/models/website.go | 31 +- internal/validator/validator.go | 37 +- .../000005_normalize_site_urls.down.sql | 1 + migrations/000005_normalize_site_urls.up.sql | 1 + ui/views/guestbooks.templ | 2 +- ui/views/guestbooks_templ.go | 4 +- ui/views/websites.templ | 57 +- ui/views/websites_templ.go | 494 ++++++++---------- 14 files changed, 401 insertions(+), 383 deletions(-) create mode 100644 migrations/000005_normalize_site_urls.down.sql create mode 100644 migrations/000005_normalize_site_urls.up.sql diff --git a/cmd/web/handlers_guestbook.go b/cmd/web/handlers_guestbook.go index 1a6e6b3..5234eb4 100644 --- a/cmd/web/handlers_guestbook.go +++ b/cmd/web/handlers_guestbook.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "net/url" "strconv" "time" @@ -258,11 +257,10 @@ func (app *application) postGuestbookCommentCreateRemote(w http.ResponseWriter, return } - if normalizeUrl(r.Header.Get("Origin")) != normalizeUrl(website.SiteUrl) { + if !matchOrigin(r.Header.Get("Origin"), website.Url) { app.clientError(w, http.StatusForbidden) return } - if !website.Guestbook.CanComment() { app.clientError(w, http.StatusForbidden) return @@ -285,12 +283,10 @@ func (app *application) postGuestbookCommentCreateRemote(w http.ResponseWriter, // otherwise redirect to the guestbook by default redirectUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(website.ShortId)) if form.Redirect != "" { - u := url.URL{ - Scheme: "http", - Host: website.SiteUrl, - Path: form.Redirect, + u, err := website.Url.Parse(form.Redirect) + if err == nil { + redirectUrl = u.String() } - redirectUrl = u.String() } if !form.Valid() { diff --git a/cmd/web/handlers_guestbook_test.go b/cmd/web/handlers_guestbook_test.go index c96693b..182f081 100644 --- a/cmd/web/handlers_guestbook_test.go +++ b/cmd/web/handlers_guestbook_test.go @@ -144,3 +144,88 @@ func TestPostGuestbookCommentCreate(t *testing.T) { }) } } + +func TestPostGuestbookCommentCreateRemote(t *testing.T) { + app := newTestApplication(t) + ts := newTestServer(t, app.routes()) + defer ts.Close() + + _, _, body := ts.get(t, fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(1))) + validCSRFToken := extractCSRFToken(t, body) + + const ( + validAuthorName = "John Test" + validAuthorEmail = "test@example.com" + validAuthorSite = "example.com" + validContent = "This is a comment" + ) + + tests := []struct { + name string + authorName string + authorEmail string + authorSite string + content string + csrfToken string + wantCode int + }{ + { + name: "Valid input", + authorName: validAuthorName, + authorEmail: validAuthorEmail, + authorSite: validAuthorSite, + content: validContent, + csrfToken: validCSRFToken, + wantCode: http.StatusSeeOther, + }, + { + name: "Blank name", + authorName: "", + authorEmail: validAuthorEmail, + authorSite: validAuthorSite, + content: validContent, + csrfToken: validCSRFToken, + wantCode: http.StatusUnprocessableEntity, + }, + { + name: "Blank email", + authorName: validAuthorName, + authorEmail: "", + authorSite: validAuthorSite, + content: validContent, + csrfToken: validCSRFToken, + wantCode: http.StatusSeeOther, + }, + { + name: "Blank site", + authorName: validAuthorName, + authorEmail: validAuthorEmail, + authorSite: "", + content: validContent, + csrfToken: validCSRFToken, + wantCode: http.StatusSeeOther, + }, + { + name: "Blank content", + authorName: validAuthorName, + authorEmail: validAuthorEmail, + authorSite: validAuthorSite, + content: "", + csrfToken: validCSRFToken, + wantCode: http.StatusUnprocessableEntity, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + form := url.Values{} + form.Add("authorname", tt.authorName) + form.Add("authoremail", tt.authorEmail) + form.Add("authorsite", tt.authorSite) + form.Add("content", tt.content) + form.Add("csrf_token", tt.csrfToken) + code, _, body := ts.postForm(t, fmt.Sprintf("/websites/%s/guestbook/comments/create/remote", shortIdToSlug(1)), form) + assert.Equal(t, code, tt.wantCode) + assert.Equal(t, body, body) + }) + } +} diff --git a/cmd/web/handlers_website.go b/cmd/web/handlers_website.go index 9e6d2d5..a2b8e9e 100644 --- a/cmd/web/handlers_website.go +++ b/cmd/web/handlers_website.go @@ -33,16 +33,20 @@ func (app *application) postWebsiteCreate(w http.ResponseWriter, r *http.Request form.CheckField(validator.MaxChars(form.Name, 256), "sitename", "This field cannot exceed 256 characters") form.CheckField(validator.NotBlank(form.SiteUrl), "siteurl", "This field cannot be blank") form.CheckField(validator.MaxChars(form.SiteUrl, 512), "siteurl", "This field cannot exceed 512 characters") + form.CheckField(validator.Matches(form.SiteUrl, validator.WebRX), "siteurl", "This field must be a valid URL (including http:// or https://)") u, err := url.Parse(form.SiteUrl) - - if !form.Valid() || err != nil { + if err != nil { + form.CheckField(false, "siteurl", "This field must be a valid URL") + } + if !form.Valid() { data := app.newCommonData(r) w.WriteHeader(http.StatusUnprocessableEntity) views.WebsiteCreate("Add a Website", data, form).Render(r.Context(), w) + return } websiteShortID := app.createShortId() - _, err = app.websites.Insert(websiteShortID, userId, form.Name, u.Host, form.AuthorName) + _, err = app.websites.Insert(websiteShortID, userId, form.Name, u.String(), form.AuthorName) if err != nil { app.serverError(w, r, err) return @@ -78,10 +82,6 @@ func (app *application) getWebsiteList(w http.ResponseWriter, r *http.Request) { app.serverError(w, r, err) return } - if r.Header.Get("HX-Request") == "true" { - views.HxWebsiteList(websites) - return - } data := app.newCommonData(r) views.WebsiteList("My Websites", data, websites).Render(r.Context(), w) } diff --git a/cmd/web/helpers.go b/cmd/web/helpers.go index e1321e0..f86b71b 100644 --- a/cmd/web/helpers.go +++ b/cmd/web/helpers.go @@ -5,9 +5,9 @@ import ( "fmt" "math" "net/http" + "net/url" "runtime/debug" "strconv" - "strings" "time" "git.32bit.cafe/32bitcafe/guestbook/internal/models" @@ -130,11 +130,13 @@ func (app *application) durationToTime(duration string) (time.Time, error) { return result, nil } -func normalizeUrl(url string) string { - r, f := strings.CutPrefix(url, "http://") - if f { - return r +func matchOrigin(origin string, u *url.URL) bool { + o, err := url.Parse(origin) + if err != nil { + return false } - r, _ = strings.CutPrefix(url, "https://") - return r + if o.Host != u.Host { + return false + } + return true } diff --git a/internal/models/mocks/guestbookcomment.go b/internal/models/mocks/guestbookcomment.go index 6a0c972..f8520de 100644 --- a/internal/models/mocks/guestbookcomment.go +++ b/internal/models/mocks/guestbookcomment.go @@ -18,6 +18,12 @@ var mockGuestbookComment = models.GuestbookComment{ IsPublished: true, } +var mockSerializedGuestbookComment = models.GuestbookCommentSerialized{ + AuthorName: "John Test", + CommentText: "Hello, world", + Created: time.Now().Format(time.RFC3339), +} + type GuestbookCommentModel struct{} func (m *GuestbookCommentModel) Insert(shortId uint64, guestbookId, parentId int64, authorName, @@ -45,6 +51,17 @@ func (m *GuestbookCommentModel) GetAll(guestbookId int64) ([]models.GuestbookCom } } +func (m *GuestbookCommentModel) GetAllSerialized(guestbookId int64) ([]models.GuestbookCommentSerialized, error) { + switch guestbookId { + case 1: + return []models.GuestbookCommentSerialized{mockSerializedGuestbookComment}, nil + case 2: + return []models.GuestbookCommentSerialized{}, nil + default: + return []models.GuestbookCommentSerialized{}, models.ErrNoRecord + } +} + func (m *GuestbookCommentModel) GetDeleted(guestbookId int64) ([]models.GuestbookComment, error) { switch guestbookId { default: diff --git a/internal/models/mocks/website.go b/internal/models/mocks/website.go index 83cc69e..1e14872 100644 --- a/internal/models/mocks/website.go +++ b/internal/models/mocks/website.go @@ -1,6 +1,7 @@ package mocks import ( + "net/url" "time" "git.32bit.cafe/32bitcafe/guestbook/internal/models" @@ -22,10 +23,14 @@ var mockGuestbook = models.Guestbook{ } var mockWebsite = models.Website{ - ID: 1, - ShortId: 1, - Name: "Example", - SiteUrl: "example.com", + ID: 1, + ShortId: 1, + Name: "Example", + // SiteUrl: "example.com", + Url: &url.URL{ + Scheme: "http", + Host: "example.com", + }, AuthorName: "John Test", UserId: 1, Created: time.Now(), diff --git a/internal/models/website.go b/internal/models/website.go index 53eb35d..811bedf 100644 --- a/internal/models/website.go +++ b/internal/models/website.go @@ -3,15 +3,17 @@ package models import ( "database/sql" "errors" + "net/url" "strconv" "time" ) type Website struct { - ID int64 - ShortId uint64 - Name string - SiteUrl string + ID int64 + ShortId uint64 + Name string + // SiteUrl string + Url *url.URL AuthorName string UserId int64 Created time.Time @@ -179,7 +181,8 @@ func (m *WebsiteModel) Get(shortId uint64) (Website, error) { } 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) + var u string + err = row.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created) if err != nil { if errors.Is(err, sql.ErrNoRows) { err = ErrNoRecord @@ -189,6 +192,10 @@ func (m *WebsiteModel) Get(shortId uint64) (Website, error) { } return Website{}, err } + w.Url, err = url.Parse(u) + if err != nil { + return Website{}, err + } stmt = `SELECT Id, ShortId, UserId, WebsiteId, Created, IsActive FROM guestbooks WHERE WebsiteId = ? AND Deleted IS NULL` @@ -244,11 +251,16 @@ func (m *WebsiteModel) GetAllUser(userId int64) ([]Website, error) { var websites []Website for rows.Next() { var w Website - err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created, + var u string + err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created, &w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive) if err != nil { return nil, err } + w.Url, err = url.Parse(u) + if err != nil { + return nil, err + } websites = append(websites, w) } if err = rows.Err(); err != nil { @@ -268,11 +280,16 @@ func (m *WebsiteModel) GetAll() ([]Website, error) { var websites []Website for rows.Next() { var w Website - err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &w.SiteUrl, &w.AuthorName, &w.UserId, &w.Created, + var u string + err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created, &w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive) if err != nil { return nil, err } + w.Url, err = url.Parse(u) + if err != nil { + return nil, err + } websites = append(websites, w) } if err = rows.Err(); err != nil { diff --git a/internal/validator/validator.go b/internal/validator/validator.go index 0f57692..62037b2 100644 --- a/internal/validator/validator.go +++ b/internal/validator/validator.go @@ -8,51 +8,52 @@ import ( ) var EmailRX = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") +var WebRX = regexp.MustCompile("^https?:\\/\\/") type Validator struct { - NonFieldErrors []string - FieldErrors map[string]string + NonFieldErrors []string + FieldErrors map[string]string } func (v *Validator) Valid() bool { - return len(v.FieldErrors) == 0 && len(v.NonFieldErrors) == 0 + return len(v.FieldErrors) == 0 && len(v.NonFieldErrors) == 0 } func (v *Validator) AddFieldError(key, message string) { - if v.FieldErrors == nil { - v.FieldErrors = make(map[string]string) - } - if _, exists := v.FieldErrors[key]; !exists { - v.FieldErrors[key] = message - } + if v.FieldErrors == nil { + v.FieldErrors = make(map[string]string) + } + if _, exists := v.FieldErrors[key]; !exists { + v.FieldErrors[key] = message + } } func (v *Validator) AddNonFieldError(message string) { - v.NonFieldErrors = append(v.NonFieldErrors, message) + v.NonFieldErrors = append(v.NonFieldErrors, message) } func (v *Validator) CheckField(ok bool, key, message string) { - if !ok { - v.AddFieldError(key, message) - } + if !ok { + v.AddFieldError(key, message) + } } func NotBlank(value string) bool { - return strings.TrimSpace(value) != "" + return strings.TrimSpace(value) != "" } func MaxChars(value string, n int) bool { - return utf8.RuneCountInString(value) <= n + return utf8.RuneCountInString(value) <= n } func PermittedValue[T comparable](value T, permittedValues ...T) bool { - return slices.Contains(permittedValues, value) + return slices.Contains(permittedValues, value) } func MinChars(value string, n int) bool { - return utf8.RuneCountInString(value) >= n + return utf8.RuneCountInString(value) >= n } func Matches(value string, rx *regexp.Regexp) bool { - return rx.MatchString(value) + return rx.MatchString(value) } diff --git a/migrations/000005_normalize_site_urls.down.sql b/migrations/000005_normalize_site_urls.down.sql new file mode 100644 index 0000000..1d42f9e --- /dev/null +++ b/migrations/000005_normalize_site_urls.down.sql @@ -0,0 +1 @@ +UPDATE websites SET SiteUrl = substr(SiteUrl, 8) WHERE substr(SiteUrl, 1, 4) = 'http'; diff --git a/migrations/000005_normalize_site_urls.up.sql b/migrations/000005_normalize_site_urls.up.sql new file mode 100644 index 0000000..a9ea2b3 --- /dev/null +++ b/migrations/000005_normalize_site_urls.up.sql @@ -0,0 +1 @@ +UPDATE websites SET SiteUrl = 'http://' || SiteUrl WHERE substr(SiteUrl, 1, 4) <> 'http'; diff --git a/ui/views/guestbooks.templ b/ui/views/guestbooks.templ index 8004437..080d947 100644 --- a/ui/views/guestbooks.templ +++ b/ui/views/guestbooks.templ @@ -10,7 +10,7 @@ templ GuestbookDashboardCommentsView(title string, data CommonData, website mode
@wSidebar(website)
-

Comments on { website.SiteUrl }

+

Comments on { website.Name }


if len(comments) == 0 {

No comments yet!

diff --git a/ui/views/guestbooks_templ.go b/ui/views/guestbooks_templ.go index 5b12c21..7d167f9 100644 --- a/ui/views/guestbooks_templ.go +++ b/ui/views/guestbooks_templ.go @@ -59,9 +59,9 @@ func GuestbookDashboardCommentsView(title string, data CommonData, website model return templ_7745c5c3_Err } var templ_7745c5c3_Var3 string - templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(website.SiteUrl) + templ_7745c5c3_Var3, 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: 13, Col: 37} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 13, Col: 34} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { diff --git a/ui/views/websites.templ b/ui/views/websites.templ index db383dd..e8c29d6 100644 --- a/ui/views/websites.templ +++ b/ui/views/websites.templ @@ -16,7 +16,7 @@ templ wSidebar(website models.Website) {

{ website.Name }

Guestbook

    @@ -70,7 +70,7 @@ templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) { if exists { } - +
{{ err, exists = form.FieldErrors["siteurl"] }} @@ -78,7 +78,7 @@ templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) { if exists { } - +
{{ err, exists = form.FieldErrors["authorname"] }} @@ -86,22 +86,18 @@ templ websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) { if exists { } - +
} -templ WebsiteCreateButton() { - -} - templ WebsiteList(title string, data CommonData, websites []models.Website) { @base(title, data) {

My Websites

- @WebsiteCreateButton() + Add Website
@displayWebsites(websites) @@ -171,42 +167,9 @@ templ WebsiteDashboardComingSoon(title string, data CommonData, website models.W } templ WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) { -
- @websiteCreateForm(data.CSRFToken, form) -
-} - -templ embeddableForm(root string, website models.Website) { - {{ postUrl := fmt.Sprintf("https://%s/websites/%s/guestbook/comments/create/remote", root, shortIdToSlug(website.ShortId)) }} - {{formStr := - `
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
` - }} -
-		
-			{ fmt.Sprintf(formStr, postUrl) }
-		
-	
-} - -templ embedJavaScriptSnippet(root string, website models.Website) { + @base(title, data) { +
+ @websiteCreateForm(data.CSRFToken, form) +
+ } } diff --git a/ui/views/websites_templ.go b/ui/views/websites_templ.go index e8aee69..83e490a 100644 --- a/ui/views/websites_templ.go +++ b/ui/views/websites_templ.go @@ -65,7 +65,7 @@ func wSidebar(website models.Website) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(externalUrl(website.SiteUrl)) + var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(externalUrl(website.Url.String())) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4))) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err @@ -271,92 +271,102 @@ func websiteCreateForm(csrfToken string, form forms.WebsiteCreateForm) templ.Com return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } err, exists = form.FieldErrors["siteurl"] - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if exists { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - err, exists = form.FieldErrors["authorname"] - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, " ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if exists { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "") + err, exists = form.FieldErrors["authorname"] + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if exists { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -365,69 +375,6 @@ func WebsiteCreateButton() templ.Component { } func WebsiteList(title string, 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_Var21 := templ.GetChildren(ctx) - if templ_7745c5c3_Var21 == nil { - templ_7745c5c3_Var21 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var22 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "

My Websites

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = WebsiteCreateButton().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = displayWebsites(websites).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) - templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var22), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -func WebsiteDashboard(title string, 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 { @@ -460,7 +407,62 @@ func WebsiteDashboard(title string, data CommonData, website models.Website) tem }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "

My Websites

Add Website
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = displayWebsites(websites).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) + templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var24), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func WebsiteDashboard(title string, 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_Var25 := templ.GetChildren(ctx) + if templ_7745c5c3_Var25 == nil { + templ_7745c5c3_Var25 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var26 := 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, 37, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -468,87 +470,87 @@ func WebsiteDashboard(title string, data CommonData, website models.Website) tem if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - 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: 117, Col: 22} - } - _, 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, "

Embed your Guestbook

Upload this JavaScript WebComponent to your site and include it in your ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var26 string - templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(``) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 120, Col: 140} - } - _, 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, " tag.

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

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var27 string - templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs( - ` - -`) + templ_7745c5c3_Var27, 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: 129, Col: 8} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 113, Col: 22} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "

Then add the custom elements where you want your form and comments to show up

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - gbUrl := fmt.Sprintf("https://%s/websites/%s/guestbook", data.RootUrl, shortIdToSlug(website.ShortId)) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "
")
+			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "

Embed your Guestbook

Upload this JavaScript WebComponent to your site and include it in your ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var28 string - templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(` -`, gbUrl, gbUrl)) + templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(``) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 140, Col: 72} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 116, Col: 140} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "

If your web host does not allow CORS requests, use an iframe instead

")
+			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, " tag.

")
 			if templ_7745c5c3_Err != nil {
 				return templ_7745c5c3_Err
 			}
 			var templ_7745c5c3_Var29 string
-			templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(``, gbUrl))
+			templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(
+				`
+    
+`)
 			if templ_7745c5c3_Err != nil {
-				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 150, Col: 75}
+				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 125, Col: 8}
 			}
 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
 			if templ_7745c5c3_Err != nil {
 				return templ_7745c5c3_Err
 			}
-			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "

Then add the custom elements where you want your form and comments to show up

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + gbUrl := fmt.Sprintf("https://%s/websites/%s/guestbook", data.RootUrl, shortIdToSlug(website.ShortId)) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "
")
+			if templ_7745c5c3_Err != nil {
+				return templ_7745c5c3_Err
+			}
+			var templ_7745c5c3_Var30 string
+			templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`
+`, gbUrl, gbUrl))
+			if templ_7745c5c3_Err != nil {
+				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 136, Col: 72}
+			}
+			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
+			if templ_7745c5c3_Err != nil {
+				return templ_7745c5c3_Err
+			}
+			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "

If your web host does not allow CORS requests, use an iframe instead

")
+			if templ_7745c5c3_Err != nil {
+				return templ_7745c5c3_Err
+			}
+			var templ_7745c5c3_Var31 string
+			templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(``, gbUrl))
+			if templ_7745c5c3_Err != nil {
+				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 146, Col: 75}
+			}
+			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
+			if templ_7745c5c3_Err != nil {
+				return templ_7745c5c3_Err
+			}
+			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var24), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var26), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -572,12 +574,12 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var30 := templ.GetChildren(ctx) - if templ_7745c5c3_Var30 == nil { - templ_7745c5c3_Var30 = templ.NopComponent + templ_7745c5c3_Var32 := templ.GetChildren(ctx) + if templ_7745c5c3_Var32 == nil { + templ_7745c5c3_Var32 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var31 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var33 := 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 { @@ -589,7 +591,7 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -597,26 +599,26 @@ 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, 45, "

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

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var32 string - templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(website.Name) + var templ_7745c5c3_Var34 string + templ_7745c5c3_Var34, 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: 164, Col: 22} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 160, Col: 22} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) + _, 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, 46, "

Coming Soon

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

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_Var31), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var33), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -640,113 +642,41 @@ func WebsiteCreate(title string, data CommonData, form forms.WebsiteCreateForm) }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var33 := templ.GetChildren(ctx) - if templ_7745c5c3_Var33 == nil { - templ_7745c5c3_Var33 = templ.NopComponent + templ_7745c5c3_Var35 := templ.GetChildren(ctx) + if templ_7745c5c3_Var35 == nil { + templ_7745c5c3_Var35 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "
") + templ_7745c5c3_Var36 := 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, 48, "") + 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, 49, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) + templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var36), templ_7745c5c3_Buffer) 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, 48, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -func embeddableForm(root string, 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_Var34 := templ.GetChildren(ctx) - if templ_7745c5c3_Var34 == nil { - templ_7745c5c3_Var34 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - postUrl := fmt.Sprintf("https://%s/websites/%s/guestbook/comments/create/remote", root, shortIdToSlug(website.ShortId)) - formStr := - `
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
` - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "
")
-		if templ_7745c5c3_Err != nil {
-			return templ_7745c5c3_Err
-		}
-		var templ_7745c5c3_Var35 string
-		templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(formStr, postUrl))
-		if templ_7745c5c3_Err != nil {
-			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 206, Col: 34}
-		}
-		_, 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, 50, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -func embedJavaScriptSnippet(root string, 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_Var36 := templ.GetChildren(ctx) - if templ_7745c5c3_Var36 == nil { - templ_7745c5c3_Var36 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) return nil }) }