emh / watchword
Watchword - import and display of the daily "Losungen"
Package info
github.com/ev-medienhaus/TYPO3-watchword
Type:typo3-cms-extension
pkg:composer/emh/watchword
Requires
- phpoffice/phpspreadsheet: ^2.2
- typo3/cms-core: ^12.4 || ^13.4 || ^14.3
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
TYPO3 extension watchword
Import and display of the daily Losungen (watchwords).
Links
| URL | |
|---|---|
| Repository: | https://github.com/ev-medienhaus/TYPO3-watchword |
| Documentation: | https://github.com/ev-medienhaus/TYPO3-watchword |
| TER: | https://extensions.typo3.org/extension/watchword |
English · Deutsch
Features
- Backend module under Web → Losungen to import, list, edit and delete watchwords
- Two-step Excel/CSV import (upload → preview → confirm) from the official losungen.de export
- Frontend content element Losungen: Tageslosung that shows the watchword for the current calendar day
- Day slider: previous/next navigation loads the adjacent day via Ajax (JSON page type)
- Optional CSS and JavaScript via TypoScript constants (for sites without the main frontend bundle)
- Duplicate-safe import: existing dates are never overwritten or deleted
- Compatible with TYPO3 12.4, 13.4 and 14
Installation
Require this package via Composer:
composer req emh/watchword
Activate the extension in the Extension Manager (or via Composer autoload) and include the static TypoScript template Watchword in your site template.
Configuration
Set the storage folder in the extension configuration:
Admin Tools → Settings → Extension Configuration → watchword
| Setting | Description |
|---|---|
storagePid |
Page ID (sysfolder) where imported watchword records are stored. Default: 0 |
Records are hidden from the regular page-module list (hideTable) and are managed exclusively through the backend module.
TypoScript constants (Template → Constant Editor, category plugin.tx_watchword):
| Constant | Default | Description |
|---|---|---|
plugin.tx_watchword.settings.ajaxTypeNum |
1786976500 |
Frontend page type (?type=) for the JSON Ajax endpoint |
plugin.tx_watchword.settings.showWeekdayAndSundayName |
0 |
Render weekday and optional Sunday/holiday name next to the date |
plugin.tx_watchword.includeJavascript |
0 |
Load EXT:watchword/Resources/Public/JavaScript/Watchword.js |
plugin.tx_watchword.includeCss |
0 |
Load EXT:watchword/Resources/Public/Css/Watchword.css |
Set includeJavascript and includeCss to 1 if the site does not already ship the watchwords script and stylesheet (for example the WBK frontend bundle). Do not enable them together with the same assets from the sitepackage, otherwise click handlers and styles are applied twice.
Backend module: import and list
The module lives under Web → Losungen. Backend users need module access (user).
Overview (index)
The overview lists all imported watchwords, ordered by date:
- Year filter – restrict the list to one year, or show all years
- Pagination – 50 records per page
- Columns – date, weekday, Sunday/holiday name, Losung (verse + text), Lehrtext (verse + text)
- Edit – opens the record in the TYPO3 record editor
- Delete – soft-deletes the record via DataHandler (with a confirmation dialog)
The Excel/CSV importieren button in the module header starts the import workflow.
Import workflow
Import is always a two-step process. Nothing is written to the database until the editor confirms the preview.
Upload file → Parse & validate → Preview → Confirm → Insert new rows
Step 1 – Upload
- Open Web → Losungen and click Excel/CSV importieren.
- Select the original Excel file from losungen.de, or a CSV file with the same columns.
- Allowed extensions:
.xlsx,.xls,.csv. - Click Datei prüfen.
The file is stored temporarily under var/transient/watchword/ with a random token. It is never persisted as a FAL file.
Step 2 – Preview
The service parses the file and shows a summary:
| Counter | Meaning |
|---|---|
| New records | Valid rows whose date does not yet exist in the database |
| Already present | Duplicates in the file or dates that already exist – these are skipped |
| Invalid rows | Rows with missing/invalid date or empty required columns – skipped |
Invalid rows can be inspected in an expandable table (row number + reason).
- If there are new records: Datensätze jetzt importieren writes them.
- If there are none: the import is aborted, no data is stored.
Step 3 – Confirm
On confirm, the file is parsed and validated again against the current database. The preview result from the browser is never trusted for the write. New rows are inserted in a single database transaction on the configured storagePid. The temporary file is deleted afterwards.
A flash message reports how many records were imported, how many duplicates were skipped, and how many invalid rows were skipped.
File format
The first row must be a header. Column order does not matter; columns are mapped by name.
| Column | Required | Description |
|---|---|---|
Datum |
yes | Date (Excel date, d.m.Y, Y-m-d or d.m.y) |
Wtag |
yes | Weekday |
Sonntag |
no | Sunday / holiday name |
Losungsvers |
yes | Watchword verse reference |
Losungstext |
yes | Watchword text |
Lehrtextvers |
yes | Teaching-text verse reference |
Lehrtext |
yes | Teaching text |
CSV files: delimiter (,, ; or tab) and encoding are detected automatically.
Empty rows are ignored. Dates that appear twice in the same file count as duplicates; only the first occurrence is imported.
Import never updates or deletes existing watchwords. Re-importing the same year only inserts dates that are not yet in the table (unique key on date).
If the header is invalid, the import is aborted immediately and the temp file is removed.
Frontend: list view (daily watchword)
The plugin Losungen: Tageslosung (watchword_list) is a content element. Add it via the New Content Element wizard.
Behaviour
- Looks up the watchword for today (calendar date in UTC, time stripped).
- Renders date, Losung and Lehrtext. Weekday and Sunday/holiday name next to the date are off by default.
- Previous/next buttons load the adjacent calendar day via Ajax without a page reload.
- If no record exists for today, a fallback message is shown: Für heute ist noch keine Losung hinterlegt.
The action is registered as non-cacheable (USER_INT). The result changes every calendar day, independent of the page cache lifetime.
The plugin has no FlexForm settings. Storage page is not respected in the frontend query: all non-hidden, non-deleted records are considered.
Day slider (Ajax)
The navigation buttons data-watchwords-previous and data-watchwords-next request the previous or next day. The endpoint URL is written to data-watchwords-endpoint (current page + type={ajaxTypeNum}).
Example request:
/?type=1786976500&date=18.08.2026&direction=previous
| Parameter | Values | Description |
|---|---|---|
type |
ajaxTypeNum |
TYPO3 page type of the JSON endpoint |
date |
d.m.Y or Y-m-d |
Currently displayed day |
direction |
previous or next |
Adjacent calendar day. Omit to return the given date |
JSON response (used to replace date, quotes and verse references in the widget). weekday and sundayName are empty unless plugin.tx_watchword.settings.showWeekdayAndSundayName = 1:
{
"date": "17.08.2026",
"dateTime": "2026-08-17",
"weekday": "",
"sundayName": "",
"content": [
{ "quote": "Losungstext", "reference": "Losungsvers", "url": "https://example.org/page" },
{ "quote": "Lehrtext", "reference": "Lehrtextvers", "url": "https://example.org/page" }
]
}
If no record exists for that day, the endpoint returns HTTP 404. The script then shows Losung konnte nicht geladen werden.
date and direction are excluded from the TYPO3 cHash calculation so the GET request works without a cache hash.
CSS and JavaScript
Standalone assets (no dependency on the site frontend bundle):
| Asset | Path |
|---|---|
| JavaScript | EXT:watchword/Resources/Public/JavaScript/Watchword.js |
| CSS | EXT:watchword/Resources/Public/Css/Watchword.css |
Enable them with TypoScript constants:
plugin.tx_watchword.includeJavascript = 1
plugin.tx_watchword.includeCss = 1
This registers:
page.includeJSFooter.txWatchword = EXT:watchword/Resources/Public/JavaScript/Watchword.js
page.includeCSS.txWatchword = EXT:watchword/Resources/Public/Css/Watchword.css
The CSS covers the full widget (navigation, quotes, share button, footer, loading state) including .watchwords--default (accent colour #2481c5). Add that modifier class on the root element if you want the default blue header without relying on --color-80 from the site theme.
Weekday and Sunday/holiday name
By default the title shows only the date (17.08.2026). Weekday and Sunday/holiday name (Mo – Pfingstmontag) are not rendered.
Enable them with the TypoScript constant (Constant Editor or sitepackage):
plugin.tx_watchword.settings.showWeekdayAndSundayName = 1
The title then looks like 17.08.2026 (Mo) or, if a Sunday/holiday name exists, 17.08.2026 (Mo – Pfingstmontag). Ajax previous/next uses the same setting: the JSON leaves weekday and sundayName empty when it is off.
Template
Default template: EXT:watchword/Resources/Private/Templates/Watchword/List.html
Override via TypoScript:
plugin.tx_watchword {
view {
templateRootPaths.20 = EXT:mysitepackage/Resources/Private/Templates/Watchword/
}
}
Markup uses BEM classes. Style them in the sitepackage, or load the extension CSS (see above):
.watchwords.watchwords--default.watchwords__navigation.watchwords__title.watchwords__body.watchwords__quote.watchwords__reference.watchwords__share.watchwords__footer
Deutsch · English
Funktionen
- Backend-Modul unter Web → Losungen zum Importieren, Auflisten, Bearbeiten und Löschen
- Zweistufiger Excel-/CSV-Import (Upload → Vorschau → Bestätigung) aus dem offiziellen Export von losungen.de
- Frontend-Inhaltselement Losungen: Tageslosung, das die Losung des aktuellen Kalendertags anzeigt
- Tages-Slider: Vor/Zurück lädt den Nachbar-Tag per Ajax (JSON-Page-Type)
- Optionales CSS und JavaScript per TypoScript-Konstanten (für Sites ohne Frontend-Bundle)
- Duplikatsicherer Import: vorhandene Daten werden weder überschrieben noch gelöscht
- Kompatibel mit TYPO3 12.4, 13.4 und 14
Installation
Paket per Composer einbinden:
composer req emh/watchword
Extension aktivieren und das statische TypoScript-Template Watchword im Seiten-Template einbinden.
Konfiguration
Speicherordner in der Extension-Konfiguration setzen:
Admin Tools → Settings → Extension Configuration → watchword
| Einstellung | Beschreibung |
|---|---|
storagePid |
Seiten-ID (Sysordner), in dem importierte Losungen gespeichert werden. Standard: 0 |
Die Datensätze sind in der normalen Listenansicht des Seitenmoduls ausgeblendet (hideTable) und werden ausschließlich über das Backend-Modul verwaltet.
TypoScript-Konstanten (Template → Constant Editor, Kategorie plugin.tx_watchword):
| Konstante | Standard | Beschreibung |
|---|---|---|
plugin.tx_watchword.settings.ajaxTypeNum |
1786976500 |
Frontend-Page-Type (?type=) für den JSON-Ajax-Endpunkt |
plugin.tx_watchword.settings.showWeekdayAndSundayName |
0 |
Wochentag und optionalen Sonntags-/Feiertagsnamen neben dem Datum anzeigen |
plugin.tx_watchword.includeJavascript |
0 |
Lädt EXT:watchword/Resources/Public/JavaScript/Watchword.js |
plugin.tx_watchword.includeCss |
0 |
Lädt EXT:watchword/Resources/Public/Css/Watchword.css |
includeJavascript und includeCss auf 1 setzen, wenn die Site das Watchwords-Script und -Stylesheet nicht bereits mitbringt (z. B. das WBK-Frontend-Bundle). Nicht zusammen mit denselben Assets aus dem Sitepackage aktivieren, sonst werden Klicks und Styles doppelt angewendet.
Backend-Modul: Import und Übersicht
Das Modul liegt unter Web → Losungen. Backend-Benutzer benötigen Modulzugriff (user).
Übersicht (Index)
Die Übersicht listet alle importierten Losungen, sortiert nach Datum:
- Jahresfilter – Liste auf ein Jahr einschränken oder alle Jahre anzeigen
- Paginierung – 50 Datensätze pro Seite
- Spalten – Datum, Wochentag, Sonntag/Feiertag, Losung (Vers + Text), Lehrtext (Vers + Text)
- Bearbeiten – öffnet den Datensatz im TYPO3-Record-Editor
- Löschen – Soft-Delete über den DataHandler (mit Bestätigungsdialog)
Der Button Excel/CSV importieren in der Modul-Kopfzeile startet den Import.
Import-Ablauf
Der Import erfolgt immer in zwei Schritten. In die Datenbank wird erst geschrieben, wenn die Vorschau bestätigt wird.
Datei hochladen → Parsen & Prüfen → Vorschau → Bestätigen → Neue Zeilen einfügen
Schritt 1 – Upload
- Web → Losungen öffnen und Excel/CSV importieren klicken.
- Die Original-Exceldatei von losungen.de oder eine CSV-Datei mit denselben Spalten wählen.
- Erlaubte Endungen:
.xlsx,.xls,.csv. - Datei prüfen klicken.
Die Datei wird temporär unter var/transient/watchword/ mit einem Zufallstoken abgelegt. Sie wird nicht als FAL-Datei gespeichert.
Schritt 2 – Vorschau
Der Service parst die Datei und zeigt eine Zusammenfassung:
| Zähler | Bedeutung |
|---|---|
| Neue Datensätze | Gültige Zeilen, deren Datum noch nicht in der Datenbank existiert |
| Bereits vorhanden | Duplikate in der Datei oder bereits vorhandene Daten – werden übersprungen |
| Ungültige Zeilen | Zeilen mit fehlendem/ungültigem Datum oder leeren Pflichtfeldern – übersprungen |
Ungültige Zeilen können in einer aufklappbaren Tabelle eingesehen werden (Zeilennummer + Grund).
- Gibt es neue Datensätze: Datensätze jetzt importieren schreibt sie.
- Gibt es keine: der Import wird abgebrochen, es werden keine Daten gespeichert.
Schritt 3 – Bestätigung
Beim Bestätigen wird die Datei erneut gegen den aktuellen Datenbankstand geparst und geprüft. Das Vorschau-Ergebnis aus dem Browser wird für den Schreibvorgang nicht vertraut. Neue Zeilen werden in einer Transaktion auf der konfigurierten storagePid eingefügt. Die temporäre Datei wird anschließend gelöscht.
Eine Flash-Meldung zeigt, wie viele Datensätze importiert, wie viele Duplikate übersprungen und wie viele ungültige Zeilen übersprungen wurden.
Dateiformat
Die erste Zeile muss eine Kopfzeile sein. Die Spaltenreihenfolge ist egal; die Zuordnung erfolgt über den Spaltennamen.
| Spalte | Pflicht | Beschreibung |
|---|---|---|
Datum |
ja | Datum (Excel-Datum, d.m.Y, Y-m-d oder d.m.y) |
Wtag |
ja | Wochentag |
Sonntag |
nein | Name des Sonntags / Feiertags |
Losungsvers |
ja | Bibelstelle der Losung |
Losungstext |
ja | Text der Losung |
Lehrtextvers |
ja | Bibelstelle des Lehrtexts |
Lehrtext |
ja | Text des Lehrtexts |
CSV: Trennzeichen (,, ; oder Tab) und Zeichensatz werden automatisch erkannt.
Leere Zeilen werden ignoriert. Kommt ein Datum in derselben Datei zweimal vor, zählt es als Duplikat; nur das erste Vorkommen wird importiert.
Der Import ändert oder löscht niemals vorhandene Losungen. Ein erneuter Import desselben Jahres fügt nur Daten ein, die noch nicht in der Tabelle stehen (Unique-Key auf date).
Stimmt die Kopfzeile nicht, wird der Import sofort abgebrochen und die Temp-Datei entfernt.
Frontend: List-View (Tageslosung)
Das Plugin Losungen: Tageslosung (watchword_list) ist ein Inhaltselement. Es wird über den Assistenten „Neues Inhaltselement“ eingefügt.
Verhalten
- Sucht die Losung für heute (Kalenderdatum in UTC, ohne Uhrzeit).
- Zeigt Datum, Losung und Lehrtext. Wochentag und Sonntags-/Feiertagsname neben dem Datum sind standardmäßig aus.
- Die Buttons Vor/Zurück laden den benachbarten Kalendertag per Ajax, ohne Seitenreload.
- Fehlt ein Datensatz für heute, erscheint der Hinweis: Für heute ist noch keine Losung hinterlegt.
Die Action ist nicht cachebar (USER_INT). Das Ergebnis wechselt mit dem Kalendertag, unabhängig von der Cache-Lebensdauer der Seite.
Das Plugin hat keine FlexForm-Einstellungen. Die Storage-Seite wird in der Frontend-Abfrage nicht berücksichtigt: alle nicht versteckten, nicht gelöschten Datensätze werden einbezogen.
Tages-Slider (Ajax)
Die Navigationsbuttons data-watchwords-previous und data-watchwords-next fordern den vorherigen bzw. nächsten Tag an. Die Endpunkt-URL steht in data-watchwords-endpoint (aktuelle Seite + type={ajaxTypeNum}).
Beispiel-Request:
/?type=1786976500&date=18.08.2026&direction=previous
| Parameter | Werte | Bedeutung |
|---|---|---|
type |
ajaxTypeNum |
TYPO3-Page-Type des JSON-Endpunkts |
date |
d.m.Y oder Y-m-d |
aktuell angezeigter Tag |
direction |
previous oder next |
Nachbar-Kalendertag. Weglassen, um genau dieses Datum zu liefern |
JSON-Antwort (ersetzt Datum, Zitate und Bibelstellen im Widget). weekday und sundayName bleiben leer, solange plugin.tx_watchword.settings.showWeekdayAndSundayName = 1 nicht gesetzt ist:
{
"date": "17.08.2026",
"dateTime": "2026-08-17",
"weekday": "",
"sundayName": "",
"content": [
{ "quote": "Losungstext", "reference": "Losungsvers", "url": "https://example.org/page" },
{ "quote": "Lehrtext", "reference": "Lehrtextvers", "url": "https://example.org/page" }
]
}
Fehlt ein Datensatz für den Tag, antwortet der Endpunkt mit HTTP 404. Das Script zeigt dann Losung konnte nicht geladen werden.
date und direction sind von der TYPO3-cHash-Berechnung ausgenommen, damit der GET-Request ohne Cache-Hash funktioniert.
CSS und JavaScript
Eigenständige Assets (keine Abhängigkeit vom Site-Frontend-Bundle):
| Asset | Pfad |
|---|---|
| JavaScript | EXT:watchword/Resources/Public/JavaScript/Watchword.js |
| CSS | EXT:watchword/Resources/Public/Css/Watchword.css |
Aktivieren per TypoScript-Konstanten:
plugin.tx_watchword.includeJavascript = 1
plugin.tx_watchword.includeCss = 1
Das registriert:
page.includeJSFooter.txWatchword = EXT:watchword/Resources/Public/JavaScript/Watchword.js
page.includeCSS.txWatchword = EXT:watchword/Resources/Public/Css/Watchword.css
Das CSS deckt das komplette Widget ab (Navigation, Zitate, Teilen-Button, Footer, Ladezustand), inklusive .watchwords--default (Akzentfarbe #2481c5). Die Modifier-Klasse am Wurzelelement setzen, wenn der blaue Standard-Header ohne --color-80 aus dem Site-Theme gelten soll.
Wochentag und Sonntags-/Feiertagsname
Standardmäßig zeigt der Titel nur das Datum (17.08.2026). Wochentag und Sonntags-/Feiertagsname (Mo – Pfingstmontag) werden nicht gerendert.
Einschalten über die TypoScript-Konstante (Constant Editor oder Sitepackage):
plugin.tx_watchword.settings.showWeekdayAndSundayName = 1
Der Titel lautet dann 17.08.2026 (Mo) bzw. bei vorhandenem Sonntags-/Feiertagsnamen 17.08.2026 (Mo – Pfingstmontag). Die Ajax-Navigation nutzt dieselbe Einstellung: in der JSON bleiben weekday und sundayName leer, wenn sie aus ist.
Template
Standard-Template: EXT:watchword/Resources/Private/Templates/Watchword/List.html
Überschreiben per TypoScript:
plugin.tx_watchword {
view {
templateRootPaths.20 = EXT:mysitepackage/Resources/Private/Templates/Watchword/
}
}
Das Markup verwendet BEM-Klassen. Styling im Sitepackage oder über das Extension-CSS (siehe oben):
.watchwords.watchwords--default.watchwords__navigation.watchwords__title.watchwords__body.watchwords__quote.watchwords__reference.watchwords__share.watchwords__footer