From 378926240f34759116b19e078d2b2504965f5329 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Fri, 24 Jul 2026 16:56:07 +0200 Subject: export: month calendar PDF (A4 landscape, Monday-first grid, feast name + gospel ref + liturgical colour per day); cli --calendar YYYY-MM; web /calendar + link; readings.GospelCitation; v0.23.0 --- internal/web/server.go | 36 ++++++++++++++++++++++++++++++++++++ internal/web/server_test.go | 18 ++++++++++++++++++ internal/web/templates/index.html | 1 + 3 files changed, 55 insertions(+) (limited to 'internal/web') diff --git a/internal/web/server.go b/internal/web/server.go index 45ab06f..5ebed38 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -69,6 +69,7 @@ func NewServer(cfg config.Config) http.Handler { mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { indexHandler(s.get())(w, r) }) mux.HandleFunc("GET /readings", func(w http.ResponseWriter, r *http.Request) { readingsHandler(s.get())(w, r) }) mux.HandleFunc("GET /export", func(w http.ResponseWriter, r *http.Request) { exportHandler(s.get())(w, r) }) + mux.HandleFunc("GET /calendar", func(w http.ResponseWriter, r *http.Request) { calendarHandler(s.get())(w, r) }) mux.HandleFunc("GET /reader", func(w http.ResponseWriter, r *http.Request) { readerHandler(s.get(), s.table())(w, r) }) mux.HandleFunc("GET /theme.css", func(w http.ResponseWriter, r *http.Request) { themeCSSHandler(s.get())(w, r) }) mux.HandleFunc("GET /settings", settingsGet(s)) @@ -381,6 +382,41 @@ func exportHandler(cfg config.Config) http.HandlerFunc { } } +// calendarHandler serves GET /calendar?month=YYYY-MM&lectionary=: a printable +// A4 PDF month calendar of the celebration names + gospel references. Missing/ +// bad month defaults to the current month. +func calendarHandler(cfg config.Config) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + t, err := time.Parse("2006-01", r.URL.Query().Get("month")) + if err != nil { + t = time.Now() + } + year, month := t.Year(), int(t.Month()) + cfg.Lectionary = requestLectionary(cfg, r) + first := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) + + var days []export.CalendarDay + for d := first; int(d.Month()) == month; d = d.AddDate(0, 0, 1) { + cd := export.CalendarDay{Day: d.Day()} + if secs, info, lerr := readings.Load(cfg, readings.Options{Date: d.Format("2006-01-02"), Offline: cfg.Offline, All: false}); lerr == nil { + cd.Name = info.Name + cd.Colour = info.Colour + cd.Citation = readings.GospelCitation(secs) + } + days = append(days, cd) + } + + data, perr := export.CalendarPDF(year, month, days, cfg.Lectionary, cfg.UILanguage) + if perr != nil { + http.Error(w, perr.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/pdf") + w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="lectio-%04d-%02d-calendar.pdf"`, year, month)) + w.Write(data) + } +} + // renderOrError returns RenderReadings' fragment, or (on a readings.Load // error, or no sections at all for the date) a small escaped error // paragraph, localised via lang -- readings.Load errors are expected in diff --git a/internal/web/server_test.go b/internal/web/server_test.go index 43508ba..284a454 100644 --- a/internal/web/server_test.go +++ b/internal/web/server_test.go @@ -645,3 +645,21 @@ func TestExportNoReadings(t *testing.T) { t.Errorf("export with no readings status=%d (want 404/500)", rec.Code) } } + +func TestCalendarPDF(t *testing.T) { + t.Setenv("XDG_CACHE_HOME", t.TempDir()) + cfg := config.Default() + cfg.Offline = true // no network; empty cells are fine, PDF still renders + srv := NewServer(cfg) + rec := httptest.NewRecorder() + srv.ServeHTTP(rec, httptest.NewRequest("GET", "/calendar?month=2026-07", nil)) + if rec.Code != 200 { + t.Fatalf("calendar status %d", rec.Code) + } + if ct := rec.Header().Get("Content-Type"); ct != "application/pdf" { + t.Errorf("content-type %q", ct) + } + if b := rec.Body.Bytes(); len(b) < 1000 || string(b[:4]) != "%PDF" { + t.Errorf("not a pdf (len=%d)", len(b)) + } +} diff --git a/internal/web/templates/index.html b/internal/web/templates/index.html index cd30208..1e43df4 100644 --- a/internal/web/templates/index.html +++ b/internal/web/templates/index.html @@ -84,6 +84,7 @@ txt md pdf + ยท {{.L.Calendar}}
{{.Reading}}
-- cgit v1.3