diff --git a/cmd/web/handlers_guestbook.go b/cmd/web/handlers_guestbook.go index 92b285a..46668bc 100644 --- a/cmd/web/handlers_guestbook.go +++ b/cmd/web/handlers_guestbook.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "errors" "fmt" "net/http" @@ -136,6 +137,29 @@ func (app *application) getGuestbookComments(w http.ResponseWriter, r *http.Requ views.GuestbookDashboardCommentsView("Comments", data, website, website.Guestbook, comments).Render(r.Context(), w) } +func (app *application) getGuestbookCommentsSerialized(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 + } + if !website.Guestbook.Settings.IsVisible || !website.Guestbook.Settings.AllowRemoteHostAccess { + app.clientError(w, http.StatusForbidden) + } + comments, err := app.guestbookComments.GetAll(website.Guestbook.ID) + if err != nil { + app.serverError(w, r, err) + return + } + b, err := json.Marshal(comments) + w.Write(b) +} + func (app *application) getGuestbookCommentCreate(w http.ResponseWriter, r *http.Request) { // TODO: This will be the embeddable form slug := r.PathValue("id") @@ -148,13 +172,21 @@ func (app *application) getGuestbookCommentCreate(w http.ResponseWriter, r *http } return } + s := website.Guestbook.Settings + if !s.IsVisible || !s.AllowRemoteHostAccess || !website.Guestbook.CanComment() { + app.clientError(w, http.StatusForbidden) + } data := app.newCommonData(r) form := forms.CommentCreateForm{} - views.CreateGuestbookComment("New Comment", data, website, website.Guestbook, form).Render(r.Context(), w) + views.EmbeddableGuestbookCommentForm(data, website, form).Render(r.Context(), w) } func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *http.Request) { slug := r.PathValue("id") + headless, err := strconv.ParseBool(r.URL.Query().Get("headless")) + if err != nil { + headless = false + } website, err := app.websites.Get(slugToShortId(slug)) if err != nil { if errors.Is(err, models.ErrNoRecord) { @@ -184,14 +216,17 @@ func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *htt form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank") if !form.Valid() { + data := app.newCommonData(r) + w.WriteHeader(http.StatusUnprocessableEntity) + if headless { + views.EmbeddableGuestbookCommentForm(data, website, form).Render(r.Context(), w) + } // TODO: use htmx to avoid getting comments again comments, err := app.guestbookComments.GetAll(website.Guestbook.ID) if err != nil { app.serverError(w, r, err) return } - data := app.newCommonData(r) - w.WriteHeader(http.StatusUnprocessableEntity) views.GuestbookView("Guestbook", data, website, website.Guestbook, comments, form).Render(r.Context(), w) return } @@ -203,6 +238,9 @@ func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *htt return } app.sessionManager.Put(r.Context(), "flash", "Comment successfully posted!") + if headless { + http.Redirect(w, r, fmt.Sprintf("/websites/%s/guestbook/comments/create", slug), http.StatusSeeOther) + } http.Redirect(w, r, fmt.Sprintf("/websites/%s/guestbook", slug), http.StatusSeeOther) } diff --git a/cmd/web/routes.go b/cmd/web/routes.go index 0b72dda..6faea18 100644 --- a/cmd/web/routes.go +++ b/cmd/web/routes.go @@ -17,8 +17,10 @@ func (app *application) routes() http.Handler { standard := alice.New(app.recoverPanic, app.logRequest, commonHeaders) mux.Handle("/{$}", dynamic.ThenFunc(app.home)) - mux.Handle("POST /websites/{id}/guestbook/comments/create", dynamic.ThenFunc(app.postGuestbookCommentCreate)) mux.Handle("GET /websites/{id}/guestbook", dynamic.ThenFunc(app.getGuestbook)) + mux.Handle("GET /websites/{id}/guestbook/comments", standard.ThenFunc(app.getGuestbookCommentsSerialized)) + mux.Handle("GET /websites/{id}/guestbook/comments/create", dynamic.ThenFunc(app.getGuestbookCommentCreate)) + mux.Handle("POST /websites/{id}/guestbook/comments/create", dynamic.ThenFunc(app.postGuestbookCommentCreate)) mux.Handle("GET /users/register", dynamic.ThenFunc(app.getUserRegister)) mux.Handle("POST /users/register", dynamic.ThenFunc(app.postUserRegister)) mux.Handle("GET /users/login", dynamic.ThenFunc(app.getUserLogin)) @@ -48,7 +50,6 @@ func (app *application) routes() http.Handler { 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)) - mux.Handle("GET /websites/{id}/guestbook/comments/create", protected.ThenFunc(app.getGuestbookCommentCreate)) return standard.Then(mux) } diff --git a/ui/static/js/main.js b/ui/static/js/main.js new file mode 100644 index 0000000..3943564 --- /dev/null +++ b/ui/static/js/main.js @@ -0,0 +1,13 @@ + +const convertDates = () => { + const dates = document.getElementsByTagName("time") + for (let i = 0; i < dates.length; i++) { + const e = dates.item(i) + const d = e.attributes.getNamedItem("datetime").value + const dt = new Date(Date.parse(d)) + const localtime = dt.toLocaleString("en-US", { "year": "numeric", "month": "short", "day": "numeric", "hour": "numeric", "minute": "numeric", "timeZoneName": "short"}) + e.innerText = localtime + } +} + +convertDates() diff --git a/ui/views/guestbooks.templ b/ui/views/guestbooks.templ index 5ad5583..c4835d8 100644 --- a/ui/views/guestbooks.templ +++ b/ui/views/guestbooks.templ @@ -61,7 +61,7 @@ templ GuestbookDashboardCommentView(data CommonData, w models.Website, c models. templ commentForm(form forms.CommentCreateForm) {