aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web/handlers_posts.go
diff options
context:
space:
mode:
authorgrm <grm@eyesin.space>2026-09-14 22:58:08 +0300
committergrm <grm@eyesin.space>2026-09-14 22:58:08 +0300
commit5f9fc2a8667a9438b6336d75026f9a455fc9c4ce (patch)
tree8fa0009b2487e35219bfdbb34f9802704007076e /internal/web/handlers_posts.go
parente14a4250efb828262347f5a4bbc4067cd71d885f (diff)
downloadblogspace-5f9fc2a8667a9438b6336d75026f9a455fc9c4ce.tar.gz
blogspace-5f9fc2a8667a9438b6336d75026f9a455fc9c4ce.tar.bz2
blogspace-5f9fc2a8667a9438b6336d75026f9a455fc9c4ce.zip
Let the blogger set a post's date, when writing it and afterwards
The post date is posts.created_at: it already drives ordering, the archive, the feed and the displayed dates, so making it editable is a form field rather than a new column. The datetime-local input degrades to a text box on old browsers, so the server also accepts the value typed by hand; blank keeps the current date. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
Diffstat (limited to 'internal/web/handlers_posts.go')
-rw-r--r--internal/web/handlers_posts.go25
1 files changed, 23 insertions, 2 deletions
diff --git a/internal/web/handlers_posts.go b/internal/web/handlers_posts.go
index 88d5c20..8327912 100644
--- a/internal/web/handlers_posts.go
+++ b/internal/web/handlers_posts.go
@@ -5,6 +5,7 @@ import (
"net/http"
"strconv"
"strings"
+ "time"
"github.com/gramanas/blogspace/internal/markdown"
"github.com/gramanas/blogspace/internal/slug"
@@ -46,7 +47,7 @@ func (s *Server) handlePostForm(w http.ResponseWriter, r *http.Request) {
s.serverError(w, err)
return
}
- p := &store.Post{Published: true}
+ p := &store.Post{Published: true, CreatedAt: time.Now()}
if r.PathValue("id") != "" {
if p = s.loadPost(w, r); p == nil {
return
@@ -66,7 +67,7 @@ func (s *Server) handlePostSave(w http.ResponseWriter, r *http.Request) {
s.serverError(w, err)
return
}
- p := &store.Post{}
+ p := &store.Post{CreatedAt: time.Now()}
if r.PathValue("id") != "" {
if p = s.loadPost(w, r); p == nil {
return
@@ -84,6 +85,13 @@ func (s *Server) handlePostSave(w http.ResponseWriter, r *http.Request) {
fail := func(status int, msg string) {
s.renderStatus(w, r, status, "dashboard/post_form.html", map[string]any{"post": p, "pages": pages, "error": msg})
}
+ if at := strings.TrimSpace(r.FormValue("posted_at")); at != "" { // blank keeps the current date
+ var ok bool
+ if p.CreatedAt, ok = parsePostDate(at); !ok {
+ fail(http.StatusBadRequest, s.tr(r, "Enter the date as YYYY-MM-DD HH:MM."))
+ return
+ }
+ }
img, err := s.readUpload(r, "inline_image")
if err != nil {
fail(http.StatusBadRequest, s.tr(r, "Image not added:")+" "+err.Error())
@@ -137,6 +145,19 @@ func (s *Server) handlePostSave(w http.ResponseWriter, r *http.Request) {
redirectOK(w, r, "/b/"+blog.Subdomain+"/posts/"+strconv.FormatInt(p.ID, 10)+"/edit", s.tr(r, "Saved. Refresh your blog to see it."))
}
+// parsePostDate reads the post form's date: what a datetime-local input sends,
+// or the same typed by hand where the browser shows a plain text box. The time
+// of day is optional. Times are taken in the server's zone, which is also the
+// zone the blog displays them in.
+func parsePostDate(v string) (time.Time, bool) {
+ for _, layout := range []string{"2006-01-02T15:04", "2006-01-02 15:04", "2006-01-02T15:04:05", "2006-01-02"} {
+ if t, err := time.ParseInLocation(layout, v, time.Local); err == nil {
+ return t, true
+ }
+ }
+ return time.Time{}, false
+}
+
func (s *Server) handlePostDeleteConfirm(w http.ResponseWriter, r *http.Request) {
p := s.loadPost(w, r)
if p == nil {