summaryrefslogtreecommitdiff
path: root/internal/web
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-28 12:50:31 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-28 12:50:31 +0200
commit7b220084cf3951c8cde0582efdfcf628afc64336 (patch)
treee74bae03c61b62479ef4f68b046e3345618933c8 /internal/web
parentfeede51697be870ae183a95b513ef64f031dbd0f (diff)
downloadlectio-7b220084cf3951c8cde0582efdfcf628afc64336.tar.gz
lectio-7b220084cf3951c8cde0582efdfcf628afc64336.zip
refactor: remove the niedziela/missalemeum scrapers, bt, traditional_lang
The daily view now computes entirely offline (previous commit), so retire the network path and everything that served it: - Delete internal/tradlit (missalemeum) and the niedziela scraper from internal/liturgy (fetch/store/parse + fixtures); keep Section, DayInfo and ExtractCitation. - Remove the "bt" version everywhere (render gatherBT + branches, config, i18n, web form, TUI) and bible.ToEnglishRef (Polish citation converter). A legacy config carrying "bt" migrates to "wuj" on load (config.migrateBT). - Remove the traditional_lang config field and the -g/--lang flag from all three binaries. - Drop the now-dead flags -R/--refresh, -o/--offline, -u/--update, -C/--clean and the harvest/clean commands. - New defaults: versions = wuj,vul,grb,drb; default_version = vul. Update the README (offline-by-design, no harvest/update), help text, and stale niedziela/bt doc comments. Tests updated for the offline reality; go test ./... and go vet ./... are clean, all three binaries build and run offline (OF + EF, compare, web).
Diffstat (limited to 'internal/web')
-rw-r--r--internal/web/render.go14
-rw-r--r--internal/web/render_test.go16
-rw-r--r--internal/web/server.go52
-rw-r--r--internal/web/server_test.go171
-rw-r--r--internal/web/templates/index.html4
-rw-r--r--internal/web/templates/settings.html7
6 files changed, 95 insertions, 169 deletions
diff --git a/internal/web/render.go b/internal/web/render.go
index 2cbf331..3ea52e3 100644
--- a/internal/web/render.go
+++ b/internal/web/render.go
@@ -191,12 +191,9 @@ func buildColumnViews(secs []liturgy.Section, versions []string, lectionary, lan
return views
}
-// interlinearVersions maps versions through the same bt->wuj substitution
-// render.OfflineVersions performs for offline mode: "bt" (niedziela.pl
-// paragraph text) carries no verse numbers and cannot interleave, so it is
-// dropped, substituting "wuj" (the Polish-language bible version) in its
-// place unless "wuj" was already selected. Reuses render.OfflineVersions
-// rather than duplicating its two-line transform.
+// interlinearVersions maps versions through render.OfflineVersions, which
+// substitutes a legacy "bt" with "wuj" (unless "wuj" is already selected).
+// Every remaining version is a versified corpus that interleaves cleanly.
func interlinearVersions(versions []string) []string {
return render.OfflineVersions(versions)
}
@@ -288,9 +285,8 @@ func webVersionLabel(v, lang string) string {
return v
}
-// readerCorpusVersions are the versions the /reader offers: the four with an
-// embedded full-text corpus. "bt" (the niedziela.pl scrape) has no corpus and
-// cannot be read chapter-by-chapter.
+// readerCorpusVersions are the versions the /reader offers: the four embedded
+// full-text corpora.
var readerCorpusVersions = []string{"wuj", "vul", "grb", "drb"}
// UnionChapters returns the sorted union of chapter numbers a book has across
diff --git a/internal/web/render_test.go b/internal/web/render_test.go
index c05a83c..39bcc58 100644
--- a/internal/web/render_test.go
+++ b/internal/web/render_test.go
@@ -80,13 +80,19 @@ func TestThemeCSSGuardRejectsInvalidNames(t *testing.T) {
}
func TestRenderReadingsEscapesScriptText(t *testing.T) {
+ // An attacker-controlled version code (?v=... reaches RenderReadings
+ // unfiltered) flows to render.GatherVersion as both the column label and the
+ // "(not in %s)" note. RenderReadings must HTML-escape it: it renders through
+ // html/template, never wrapping untrusted text in template.HTML.
+ const evil = "<script>alert(1)</script>"
secs := []liturgy.Section{{
- Heading: "Test",
- PartID: "pierwsze_czytanie",
- Paragraphs: [][]string{{"<script>alert(1)</script>"}},
+ Heading: "Ewangelia",
+ Citation: "J 20, 1. 11-18",
+ Ref: "John 20:1,11-18",
+ PartID: "pierwsze_czytanie",
}}
- html := string(RenderReadings(secs, []string{"bt"}, "new", "horizontal", "pl", liturgy.DayInfo{}))
- if strings.Contains(html, "<script>alert(1)</script>") {
+ html := string(RenderReadings(secs, []string{evil}, "new", "horizontal", "pl", liturgy.DayInfo{}))
+ if strings.Contains(html, evil) {
t.Errorf("raw <script> leaked into rendered output: %q", html)
}
if !strings.Contains(html, "&lt;script&gt;alert(1)&lt;/script&gt;") {
diff --git a/internal/web/server.go b/internal/web/server.go
index fce0f22..6e76d3f 100644
--- a/internal/web/server.go
+++ b/internal/web/server.go
@@ -35,7 +35,7 @@ import (
// bibleVersions is the fixed, Themes-independent list of scripture versions
// the web UI's checkboxes offer -- independent of any one cfg.Versions, so
// every visitor sees the same five choices regardless of their config file.
-var bibleVersions = []string{"bt", "wuj", "vul", "grb", "drb"}
+var bibleVersions = []string{"wuj", "vul", "grb", "drb"}
// server holds the live, mutable config + book table so /settings can apply
// changes to the running process. All handlers read a snapshot via get()/table().
@@ -142,17 +142,6 @@ func requestVersions(cfg config.Config, r *http.Request) []string {
return []string{cfg.DefaultVersion}
}
-// withoutVersion returns versions with every occurrence of drop removed.
-func withoutVersion(versions []string, drop string) []string {
- kept := make([]string, 0, len(versions))
- for _, v := range versions {
- if v != drop {
- kept = append(kept, v)
- }
- }
- return kept
-}
-
// queryBool reads a truthy/falsy query param ("1"/"true"/"on"/"yes" vs.
// "0"/"false"/"off"/"no"), falling back to def when the param is absent or
// unrecognized.
@@ -202,35 +191,19 @@ func resolveQuery(cfg config.Config, r *http.Request) (date, lectionary string,
lectionary = requestLectionary(cfg, r)
all = queryBool(r, "all", cfg.All)
versions = requestVersions(cfg, r)
- // "bt" (the niedziela modern scrape) is invalid for the traditional
- // lectionary and is hidden in the form -- but a box hidden by CSS stays
- // checked, so switching modern->traditional carries a phantom v=bt that
- // EffectiveVersions would substitute to wuj, defeating "no version selected
- // -> nothing". On an explicit form submit (vset) drop that phantom bt; a
- // fresh visit (no vset) keeps bt so its bt->wuj default still shows.
- if lectionary == "traditional" && r.URL.Query().Has("vset") {
- versions = withoutVersion(versions, "bt")
- }
display = requestDisplay(cfg, r)
return date, lectionary, all, versions, display
}
-// loadSections runs the readings router for one request: date/lectionary
-// override cfg, all controls part filtering, and versions is swapped via
-// render.OfflineVersions -- when cfg.Offline (any lectionary needs the
-// network-free set), or when lectionary is "traditional" (pl is the
-// niedziela.pl modern scrape, meaningless for missalemeum) -- before being
-// handed back to the caller for rendering, so the caller's column labels
-// always match what was actually loadable. dayInfo is the day's celebration
-// identity (see liturgy.DayInfo), zero when the source carried none.
+// loadSections computes one request's readings offline: date/lectionary
+// override cfg, all controls part filtering, and versions is passed through
+// render.EffectiveVersions (which maps any legacy "bt" to "wuj") so the
+// caller's column labels always match what was actually loadable. dayInfo is
+// the day's celebration identity (see liturgy.DayInfo), zero when none.
func loadSections(cfg config.Config, lectionary, date string, all bool, versions []string) (secs []liturgy.Section, dayInfo liturgy.DayInfo, effVersions []string, err error) {
cfg.Lectionary = lectionary
effVersions = render.EffectiveVersions(versions, lectionary, cfg.Offline)
- secs, dayInfo, err = readings.Load(cfg, readings.Options{
- Date: date,
- Offline: cfg.Offline,
- All: all,
- })
+ secs, dayInfo, err = readings.Load(cfg, readings.Options{Date: date, All: all})
return secs, dayInfo, effVersions, err
}
@@ -406,7 +379,7 @@ func calendarHandler(cfg config.Config) http.HandlerFunc {
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 {
+ if secs, info, lerr := readings.Load(cfg, readings.Options{Date: d.Format("2006-01-02"), All: false}); lerr == nil {
cd.Name = info.Name
cd.Colour = info.Colour
cd.Citation = readings.GospelCitation(secs)
@@ -799,7 +772,6 @@ func settingsPost(s *server) http.HandlerFunc {
cfg := s.get() // start from live cfg so Parts/SchemaVersion are preserved
cfg.Lectionary = normLect(r.PostForm.Get("lectionary"), cfg.Lectionary)
- cfg.TraditionalLang = pickLang(r.PostForm.Get("traditional_lang"), cfg.TraditionalLang)
cfg.UILanguage = config.NormalizeUILanguage(r.PostForm.Get("ui_language"))
cfg.SiglaStyle = config.NormalizeSiglaStyle(r.PostForm.Get("sigla_style"))
cfg.WebDisplay = config.NormalizeDisplay(r.PostForm.Get("web_display"))
@@ -865,14 +837,6 @@ func normLect(v, def string) string {
return def
}
-// pickLang returns v if it is "pl"/"en", else def.
-func pickLang(v, def string) string {
- if v == "pl" || v == "en" {
- return v
- }
- return def
-}
-
func atoiOr(s string, def int) int {
if n, err := strconv.Atoi(strings.TrimSpace(s)); err == nil {
return n
diff --git a/internal/web/server_test.go b/internal/web/server_test.go
index 12542e5..57286d5 100644
--- a/internal/web/server_test.go
+++ b/internal/web/server_test.go
@@ -15,22 +15,9 @@ import (
"github.com/lukaszkasprzak/lectio/internal/liturgy"
)
-// TestServer exercises NewServer's handler tree end to end via httptest,
-// against the same fixture HTML/hook internal/readings uses (see
-// readings_test.go TestLoadModernRoutes): no real network, no real browser.
+// TestServer exercises NewServer's handler tree end to end via httptest. Every
+// reading is computed offline (no network, no cache, no fixture server).
func TestServer(t *testing.T) {
- html, err := os.ReadFile("../liturgy/testdata/2026-07-22.html")
- if err != nil {
- t.Fatal(err)
- }
- fixtureServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Write(html)
- }))
- defer fixtureServer.Close()
- liturgy.SetBaseURL(fixtureServer.URL + "/liturgia/%s/Ewangelia")
- cacheHome := t.TempDir()
- t.Setenv("XDG_CACHE_HOME", cacheHome)
-
srv := NewServer(config.Default())
t.Run("index page", func(t *testing.T) {
@@ -41,20 +28,24 @@ func TestServer(t *testing.T) {
}
body := rec.Body.String()
// config.Default() -> UILanguage "en", so the gospel heading's part
- // label is localised to "Gospel" (render.LocalizeHeading); the
- // citation stays exactly as scraped.
+ // label is localised to "Gospel" (render.LocalizeHeading).
if !strings.Contains(body, "Gospel") {
t.Errorf("body missing reading heading: %q", body)
}
+ // ?v=wuj -> the gospel is rendered from the Wujek corpus ("grobu" is
+ // distinctly Polish Wujek verse text, not Latin/English).
+ if !strings.Contains(body, "grobu") {
+ t.Errorf("body missing Wujek verse text: %q", body)
+ }
if !strings.Contains(body, "htmx") {
t.Errorf("body missing htmx reference")
}
if !strings.Contains(body, `id="theme"`) {
t.Errorf("body missing theme <link>")
}
- // Name is source-language (Polish), never translated, even
- // though the surrounding chrome is English -- see RenderReadings.
- if !strings.Contains(body, `class="dayinfo"`) || !strings.Contains(body, "Święto św. Marii Magdaleny") {
+ // The offline engine localises the celebration name to the UI language
+ // (en): 2026-07-22 is Saint Mary Magdalene.
+ if !strings.Contains(body, `class="dayinfo"`) || !strings.Contains(body, "Saint Mary Magdalene") {
t.Errorf("body missing day-info header: %q", body)
}
})
@@ -187,18 +178,7 @@ func TestServer(t *testing.T) {
// Regression coverage for the ?date= path-traversal finding: resolveQuery
// must reject anything that isn't YYYY-MM-DD and fall back to today(),
// the same "normalize, don't trust" pattern requestDisplay already uses.
- t.Run("date path traversal does not read a planted cache file", func(t *testing.T) {
- // cacheDir() == filepath.Join(cacheHome, "lectio"), so
- // filepath.Join(cacheDir(), "../evil"+".json") resolves to
- // cacheHome/evil.json -- one level *above* the real cache dir, and
- // only reachable via an unvalidated "../" date. If the marker below
- // ever appears in a response, liturgy.Load read this planted file.
- evilPath := filepath.Join(cacheHome, "evil.json")
- evilJSON := `[{"Heading":"LEAKED-VIA-TRAVERSAL","PartID":"ewangelia","Paragraphs":[["s"]]}]`
- if err := os.WriteFile(evilPath, []byte(evilJSON), 0o644); err != nil {
- t.Fatal(err)
- }
-
+ t.Run("date path traversal falls back to today, same as omitting date", func(t *testing.T) {
baseline := httptest.NewRecorder()
srv.ServeHTTP(baseline, httptest.NewRequest("GET", "/readings?v=wuj", nil)) // no date -> today()
if baseline.Code != http.StatusOK {
@@ -210,12 +190,8 @@ func TestServer(t *testing.T) {
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, want 200", rec.Code)
}
- body := rec.Body.String()
- if strings.Contains(body, "LEAKED-VIA-TRAVERSAL") {
- t.Fatalf("traversal date reached the planted cache file outside the cache dir: %q", body)
- }
- if body != baseline.Body.String() {
- t.Errorf("traversal date did not fall back to today() identically to omitting date\n got: %q\nwant: %q", body, baseline.Body.String())
+ if rec.Body.String() != baseline.Body.String() {
+ t.Errorf("traversal date did not fall back to today() identically to omitting date\n got: %q\nwant: %q", rec.Body.String(), baseline.Body.String())
}
})
@@ -234,23 +210,21 @@ func TestServer(t *testing.T) {
})
}
-// TestBTHiddenForTraditional checks index.html's server-side initial-hidden
-// state for the "bt" version checkbox: hidden (inline style) when the
-// lectionary is traditional (bt is meaningless for missalemeum -- see
-// render.EffectiveVersions), present and visible otherwise. This is
-// presentation-only: it does not touch which versions actually load.
-func TestBTHiddenForTraditional(t *testing.T) {
+// TestBTCheckboxRemoved checks index.html no longer renders a "bt" version
+// checkbox (the niedziela.pl corpus was retired; bibleVersions is now
+// wuj,vul,grb,drb) for either lectionary, while the real version boxes remain.
+func TestBTCheckboxRemoved(t *testing.T) {
srv := NewServer(config.Default())
- trad := httptest.NewRecorder()
- srv.ServeHTTP(trad, httptest.NewRequest("GET", "/?lectionary=traditional", nil))
- if !strings.Contains(trad.Body.String(), `id="ver-bt" style="display:none"`) {
- t.Errorf("bt checkbox not hidden for traditional")
- }
- modern := httptest.NewRecorder()
- srv.ServeHTTP(modern, httptest.NewRequest("GET", "/?lectionary=new", nil))
- b := modern.Body.String()
- if !strings.Contains(b, `id="ver-bt">`) || strings.Contains(b, `id="ver-bt" style="display:none"`) {
- t.Errorf("bt checkbox should be visible for modern")
+ for _, lect := range []string{"traditional", "new"} {
+ rec := httptest.NewRecorder()
+ srv.ServeHTTP(rec, httptest.NewRequest("GET", "/?lectionary="+lect, nil))
+ b := rec.Body.String()
+ if strings.Contains(b, `id="ver-bt"`) {
+ t.Errorf("%s: bt checkbox should no longer be rendered", lect)
+ }
+ if !strings.Contains(b, `id="ver-wuj"`) {
+ t.Errorf("%s: wuj checkbox missing", lect)
+ }
}
}
@@ -325,17 +299,6 @@ func TestRenderOrErrorNoSectionsLang(t *testing.T) {
// TestIndexHTMLLangAttribute checks index.html's <html lang="..."> follows
// cfg.UILanguage (finding §6) instead of being hardcoded "pl".
func TestIndexHTMLLangAttribute(t *testing.T) {
- html, err := os.ReadFile("../liturgy/testdata/2026-07-22.html")
- if err != nil {
- t.Fatal(err)
- }
- fixtureServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Write(html)
- }))
- defer fixtureServer.Close()
- liturgy.SetBaseURL(fixtureServer.URL + "/liturgia/%s/Ewangelia")
- t.Setenv("XDG_CACHE_HOME", t.TempDir())
-
cfg := config.Default()
cfg.UILanguage = "en"
rec := httptest.NewRecorder()
@@ -422,11 +385,11 @@ func TestVsetEmptyShowsNothing(t *testing.T) {
t.Errorf("readings with vset and no v should be empty, got %q", b)
}
- // Fresh visit (no vset): config default (bt for modern) box is checked.
+ // Fresh visit (no vset): the config default (vul) box is checked.
fresh := httptest.NewRecorder()
srv.ServeHTTP(fresh, httptest.NewRequest("GET", "/", nil))
- if !strings.Contains(fresh.Body.String(), `value="bt" checked`) {
- t.Errorf("fresh visit should check the config default (bt) version box")
+ if !strings.Contains(fresh.Body.String(), `value="vul" checked`) {
+ t.Errorf("fresh visit should check the config default (vul) version box")
}
// Full page with vset and no v: no VERSION box checked (mono may be).
@@ -439,32 +402,31 @@ func TestVsetEmptyShowsNothing(t *testing.T) {
}
}
-// TestTraditionalDropsPhantomBT guards the traditional case of "no version ->
-// nothing": a phantom checked-but-hidden bt (carried over from modern) must not
-// substitute to wuj on an explicit form submit, but a fresh visit keeps the
-// bt->wuj default.
-func TestTraditionalDropsPhantomBT(t *testing.T) {
+// TestBTSubstitutesToWuj: the legacy "bt" version has no corpus, so an explicit
+// ?v=bt now renders the Wujek column instead (render.EffectiveVersions maps
+// bt->wuj unconditionally, offline) for both lectionaries -- there is no longer
+// a traditional-only "drop bt" special case. A fresh visit checks the config
+// default (vul), never bt.
+func TestBTSubstitutesToWuj(t *testing.T) {
srv := NewServer(config.Default())
- // Explicit submit, only the phantom bt "checked": empty pane.
- phantom := httptest.NewRecorder()
- srv.ServeHTTP(phantom, httptest.NewRequest("GET", "/readings?vset=1&lectionary=traditional&v=bt", nil))
- if b := strings.TrimSpace(phantom.Body.String()); b != "" {
- t.Errorf("traditional vset+v=bt should be empty, got %d bytes", len(b))
- }
-
- // Explicit submit, bt phantom + a real corpus version: still renders it.
- withWuj := httptest.NewRecorder()
- srv.ServeHTTP(withWuj, httptest.NewRequest("GET", "/readings?vset=1&lectionary=traditional&v=bt&v=wuj", nil))
- if !strings.Contains(withWuj.Body.String(), "block") {
- t.Errorf("traditional vset+v=bt+v=wuj should still render wuj")
+ for _, lect := range []string{"new", "traditional"} {
+ rec := httptest.NewRecorder()
+ srv.ServeHTTP(rec, httptest.NewRequest("GET", "/readings?vset=1&date=2026-07-22&lectionary="+lect+"&v=bt", nil))
+ b := rec.Body.String()
+ if strings.TrimSpace(b) == "" {
+ t.Errorf("%s: vset+v=bt should substitute wuj and render, got empty", lect)
+ }
+ if !strings.Contains(b, "Wujek") {
+ t.Errorf("%s: vset+v=bt should render the Wujek column: %q", lect, b)
+ }
}
- // Fresh visit (no vset): the bt->wuj default is kept and wuj is checked.
+ // Fresh traditional visit (no vset): the config default (vul) is checked.
fresh := httptest.NewRecorder()
srv.ServeHTTP(fresh, httptest.NewRequest("GET", "/?lectionary=traditional", nil))
- if !strings.Contains(fresh.Body.String(), `value="wuj" checked`) {
- t.Errorf("fresh traditional visit should default to wuj (checked)")
+ if !strings.Contains(fresh.Body.String(), `value="vul" checked`) {
+ t.Errorf("fresh traditional visit should default to vul (checked)")
}
}
@@ -521,13 +483,12 @@ func TestSettingsPostAppliesLive(t *testing.T) {
form := url.Values{}
form.Set("lectionary", "new")
- form.Set("traditional_lang", "pl")
form.Set("ui_language", "pl") // change it
form.Set("sigla_style", "auto")
form.Set("web_display", "vertical")
form.Set("web_theme", "transfiguration")
- form.Set("default_version", "bt")
- form["versions"] = []string{"bt", "wuj", "vul", "grb", "drb"}
+ form.Set("default_version", "vul")
+ form["versions"] = []string{"wuj", "vul", "grb", "drb"}
form.Set("books", string(bibleDefaultBooks()))
post := httptest.NewRequest("POST", "/settings", strings.NewReader(form.Encode()))
@@ -554,8 +515,8 @@ func TestSettingsPostInvalidBooks(t *testing.T) {
form.Set("ui_language", "en")
form.Set("web_display", "vertical")
form.Set("web_theme", "transfiguration")
- form.Set("default_version", "bt")
- form["versions"] = []string{"bt"}
+ form.Set("default_version", "vul")
+ form["versions"] = []string{"wuj"}
form.Set("books", "this is not [valid toml")
post := httptest.NewRequest("POST", "/settings", strings.NewReader(form.Encode()))
post.Header.Set("Content-Type", "application/x-www-form-urlencoded")
@@ -584,8 +545,8 @@ func TestSettingsPostEmptyBooksPreservesFile(t *testing.T) {
form.Set("ui_language", "en")
form.Set("web_display", "vertical")
form.Set("web_theme", "transfiguration")
- form.Set("default_version", "bt")
- form["versions"] = []string{"bt"} // no "books" field
+ form.Set("default_version", "vul")
+ form["versions"] = []string{"wuj"} // no "books" field
post := httptest.NewRequest("POST", "/settings", strings.NewReader(form.Encode()))
post.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rec := httptest.NewRecorder()
@@ -634,15 +595,21 @@ func TestBookmarksFlow(t *testing.T) {
}
}
-func TestExportNoReadings(t *testing.T) {
- t.Setenv("XDG_CACHE_HOME", t.TempDir())
- cfg := config.Default()
- cfg.Offline = true // no network; uncached date -> no readings
- srv := NewServer(cfg)
+// TestExportSucceeds: the offline engine computes readings for any valid date,
+// so /export always has content to render (there is no "no readings" 404/500
+// path for a normal date anymore). Even a far-future date exports successfully.
+func TestExportSucceeds(t *testing.T) {
+ srv := NewServer(config.Default())
rec := httptest.NewRecorder()
srv.ServeHTTP(rec, httptest.NewRequest("GET", "/export?fmt=md&date=2099-01-01", nil))
- if rec.Code != http.StatusNotFound && rec.Code != http.StatusInternalServerError {
- t.Errorf("export with no readings status=%d (want 404/500)", rec.Code)
+ if rec.Code != http.StatusOK {
+ t.Fatalf("export status=%d (want 200), body=%q", rec.Code, rec.Body.String())
+ }
+ if ct := rec.Header().Get("Content-Type"); !strings.Contains(ct, "markdown") {
+ t.Errorf("Content-Type = %q, want markdown", ct)
+ }
+ if !strings.Contains(rec.Body.String(), "## Gospel") {
+ t.Errorf("export body missing a gospel section:\n%s", rec.Body.String())
}
}
diff --git a/internal/web/templates/index.html b/internal/web/templates/index.html
index c8a48ed..762ed7b 100644
--- a/internal/web/templates/index.html
+++ b/internal/web/templates/index.html
@@ -36,14 +36,14 @@
</span>
<label>{{.L.Lectionary}}
- <select name="lectionary" onchange="var b=document.getElementById('ver-bt');if(b)b.style.display=this.value==='traditional'?'none':'';">
+ <select name="lectionary">
<option value="new" {{if eq .Lectionary "new"}}selected{{end}}>{{.L.OptModern}}</option>
<option value="traditional" {{if eq .Lectionary "traditional"}}selected{{end}}>{{.L.OptTraditional}}</option>
</select>
</label>
{{range .VersionOpts}}
- <label id="ver-{{.Code}}"{{if and (eq .Code "bt") (eq $.Lectionary "traditional")}} style="display:none"{{end}}><input type="checkbox" name="v" value="{{.Code}}" {{if .Checked}}checked{{end}}> {{.Code}}</label>
+ <label id="ver-{{.Code}}"><input type="checkbox" name="v" value="{{.Code}}" {{if .Checked}}checked{{end}}> {{.Code}}</label>
{{end}}
<label>{{.L.Parts}}
diff --git a/internal/web/templates/settings.html b/internal/web/templates/settings.html
index e2ee39e..f4fae47 100644
--- a/internal/web/templates/settings.html
+++ b/internal/web/templates/settings.html
@@ -30,13 +30,6 @@
</select>
</label>
- <label>{{.L.WebTradLang}}
- <select name="traditional_lang">
- <option value="pl" {{if eq .Cfg.TraditionalLang "pl"}}selected{{end}}>pl</option>
- <option value="en" {{if eq .Cfg.TraditionalLang "en"}}selected{{end}}>en</option>
- </select>
- </label>
-
<label>{{.L.WebUILang}}
<select name="ui_language">
<option value="en" {{if eq .Cfg.UILanguage "en"}}selected{{end}}>en</option>