diff --git a/cmd/web/handlers_guestbook.go b/cmd/web/handlers_guestbook.go index ce531b2..1a6e6b3 100644 --- a/cmd/web/handlers_guestbook.go +++ b/cmd/web/handlers_guestbook.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net/http" + "net/url" "strconv" "time" @@ -279,10 +280,18 @@ func (app *application) postGuestbookCommentCreateRemote(w http.ResponseWriter, form.CheckField(validator.MaxChars(form.AuthorEmail, 256), "authorEmail", "This field cannot be more than 256 characters long") form.CheckField(validator.MaxChars(form.AuthorSite, 256), "authorSite", "This field cannot be more than 256 characters long") form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank") - // TODO: Add optional field filled with window.location.href - // If it is populated, use as redirect URL - // Else redirect to homepage as stored in the website struct - redirectUrl := r.Header.Get("Referer") + + // if redirect path is filled out, redirect to that path on the website host + // 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, + } + redirectUrl = u.String() + } if !form.Valid() { views.GuestbookCommentCreateRemoteErrorView(redirectUrl, "Invalid Input").Render(r.Context(), w) diff --git a/cmd/web/handlers_website.go b/cmd/web/handlers_website.go index cb94c72..9e6d2d5 100644 --- a/cmd/web/handlers_website.go +++ b/cmd/web/handlers_website.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net/http" + "net/url" "git.32bit.cafe/32bitcafe/guestbook/internal/forms" "git.32bit.cafe/32bitcafe/guestbook/internal/models" @@ -33,13 +34,15 @@ func (app *application) postWebsiteCreate(w http.ResponseWriter, r *http.Request 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") - if !form.Valid() { + u, err := url.Parse(form.SiteUrl) + + if !form.Valid() || err != nil { data := app.newCommonData(r) w.WriteHeader(http.StatusUnprocessableEntity) views.WebsiteCreate("Add a Website", data, form).Render(r.Context(), w) } websiteShortID := app.createShortId() - _, err = app.websites.Insert(websiteShortID, userId, form.Name, form.SiteUrl, form.AuthorName) + _, err = app.websites.Insert(websiteShortID, userId, form.Name, u.Host, form.AuthorName) if err != nil { app.serverError(w, r, err) return diff --git a/internal/forms/forms.go b/internal/forms/forms.go index b5edd31..30abaa3 100644 --- a/internal/forms/forms.go +++ b/internal/forms/forms.go @@ -20,6 +20,7 @@ type CommentCreateForm struct { AuthorEmail string `schema:"authoremail"` AuthorSite string `schema:"authorsite"` Content string `schema:"content"` + Redirect string `schema:"redirect"` validator.Validator `schema:"-"` } diff --git a/ui/static/js/guestbook.js b/ui/static/js/guestbook.js index 9af432e..8a6dcac 100644 --- a/ui/static/js/guestbook.js +++ b/ui/static/js/guestbook.js @@ -1,6 +1,74 @@ +class GuestbookForm extends HTMLElement { + static get observedAttributes() { + return ['guestbook']; + } + + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + this._guestbook = this.getAttribute('guestbook') || ''; + this._postUrl = `${this._guestbook}/comments/create/remote` + this.render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === 'guestbook') { + this._guestbook = newValue; + this.render(); + } + } + + render() { + this.shadowRoot.innerHTML = ` + +
+ `; + } +} + class CommentList extends HTMLElement { static get observedAttributes() { - return ['src']; + return ['guestbook']; } constructor() { @@ -12,26 +80,28 @@ class CommentList extends HTMLElement { } connectedCallback() { - if (this.hasAttribute('src')) { + if (this.hasAttribute('guestbook')) { this.fetchComments(); } } attributeChangedCallback(name, oldValue, newValue) { - if (name === 'src' && oldValue !== newValue) { + if (name === 'guestbook' && oldValue !== newValue) { this.fetchComments(); } } async fetchComments() { - const src = this.getAttribute('src'); - if (!src) return; + const guestbook = this.getAttribute('guestbook'); + if (!guestbook) return; this.loading = true; this.error = null; this.render(); + const commentsUrl = `${guestbook}/comments` + try { - const response = await fetch(src); + const response = await fetch(commentsUrl); if (!response.ok) throw new Error(`HTTP error: ${response.status}`); const data = await response.json(); this.comments = Array.isArray(data) ? data : []; @@ -102,4 +172,6 @@ class CommentList extends HTMLElement { } } -customElements.define('comment-list', CommentList); +customElements.define('guestbook-form', GuestbookForm); +customElements.define('guestbook-comments', CommentList); + diff --git a/ui/views/websites.templ b/ui/views/websites.templ index 1c7f1be..db383dd 100644 --- a/ui/views/websites.templ +++ b/ui/views/websites.templ @@ -116,15 +116,6 @@ templ WebsiteDashboard(title string, data CommonData, website models.Website) {- Use this form to allow readers of your website to comment on your guestbook! -
-
Upload this JavaScript WebComponent to your site and include it in your { `` }
tag.
- Then add the custom element where you want the comments to show up + Then add the custom elements where you want your form and comments to show up
- {{ getUrl := fmt.Sprintf("https://%s/websites/%s/guestbook/comments", data.RootUrl, shortIdToSlug(website.ShortId)) }} + {{ gbUrl := fmt.Sprintf("https://%s/websites/%s/guestbook", data.RootUrl, shortIdToSlug(website.ShortId)) }} //
- { fmt.Sprintf(` `, getUrl) }
+ { fmt.Sprintf(`
+ `, gbUrl, gbUrl) }
+
+
+ + If your web host does not allow CORS requests, use an iframe instead +
+
+
+ { fmt.Sprintf(``, gbUrl) }
- @embedJavaScriptSnippet(data.RootUrl, website)
Use this form to allow readers of your website to comment on your guestbook!
Upload this JavaScript WebComponent to your site and include it in your Upload this JavaScript WebComponent to your site and include it in your Then add the custom element where you want the comments to show up Then add the custom elements where you want your form and comments to show up If your web host does not allow CORS requests, use an iframe instead")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -574,12 +572,12 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var29 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var29 == nil {
- templ_7745c5c3_Var29 = templ.NopComponent
+ templ_7745c5c3_Var30 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var30 == nil {
+ templ_7745c5c3_Var30 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var30 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_Var31 := 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 {
@@ -591,7 +589,7 @@ func WebsiteDashboardComingSoon(title string, data CommonData, website models.We
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "Embed your Guestbook
")
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: 129, Col: 140}
+ 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, 40, "
tag.")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "
tag.")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -512,39 +504,45 @@ func WebsiteDashboard(title string, data CommonData, website models.Website) tem
`)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 138, Col: 8}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 129, Col: 8}
}
_, 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, 41, "
")
+ 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, 42, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var28 string
- templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = embedJavaScriptSnippet(data.RootUrl, website).Render(ctx, templ_7745c5c3_Buffer)
+ var templ_7745c5c3_Var29 string
+ templ_7745c5c3_Var29, 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: 150, Col: 75}
+ }
+ _, 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, 44, "
Coming Soon
Coming Soon
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var34 string
- templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(formStr, postUrl))
+ 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: 205, Col: 34}
+ 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_Var34))
+ _, 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, 51, "
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -744,9 +742,9 @@ func embedJavaScriptSnippet(root string, website models.Website) templ.Component
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var35 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var35 == nil {
- templ_7745c5c3_Var35 = templ.NopComponent
+ templ_7745c5c3_Var36 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var36 == nil {
+ templ_7745c5c3_Var36 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
return nil