Migrate WordPress site to Hugo

Replaces the WordPress 7.0.2 / HitMag site with a static Hugo build,
deployed by Gitea Actions over rsync/SSH.

Content: 26 files (9 pages + 17 posts) as Markdown page bundles, plus
127 of 165 media attachments. The remaining 38 are media-library
leftovers that appear nowhere on the live site; they are listed in
MIGRATION.md.

The WXR export contains neither media binaries nor widgets, so both were
recovered from the live host before it is retired:
  - tools/fetch_media.py downloads all uploads, capping them at 2000px
  - tools/build_data.py scrapes the "Unsere Mitglieder" and
    "In Erinnerung" widgets into data/*.yaml

TablePress' six Termine tables became data/termine.yaml, rendered with
the newest year expanded and earlier years collapsed. Old permalinks are
not preserved (clean slugs, no redirects, as agreed).

Custom layouts, no third-party theme. Plain CSS, since the pinned Hugo
0.164.0 is the non-extended build and cannot compile Sass.

Verified: clean build with zero warnings, 718 internal links checked and
none broken, no surviving wp-content URLs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 19:20:11 +02:00
parent eccf30584a
commit bef5182545
188 changed files with 2770 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
# WordPress → Hugo Migration
Record of the one-time import from the WordPress export at
`wordpress-export/motorradclubgiebelwaldev.WordPress.2026-07-28.xml`
(WordPress 7.0.2, HitMag theme, TablePress, `widgets_on_pages`).
## Result
| | |
|---|---|
| Content files | 26 (9 pages + 17 posts) |
| Media placed | 127 / 165 attachments |
| Internal links checked | 718, 0 broken |
| Stale `wp-content/` URLs | 0 |
Re-verify at any time with:
```sh
hugo --gc --minify
python tools/verify_migration.py
```
## URL changes
Old permalinks were **not** preserved (deliberate — no redirects were required).
| Old | New |
|---|---|
| `/2026/01/13/29-o-fahrt-zum-50-jubilaum/` | `/aktuelles/29-o-fahrt-zum-50-jubilaeum/` |
| `/2025/01/03/termin/` | `/aktuelles/28-o-fahrt-2025/` |
| `/2022/06/09/o-fahrt-2019/` | `/aktuelles/25-o-fahrt-2022/` |
| `/40-jahre-mcg/` | `/archiv/40-jahre-mcg/` |
| `/familenausfahrt-2019/` | `/archiv/familienausfahrt-2019/` |
| `/bilder-familienausfahrt/` | `/archiv/bilder-familienausfahrt/` |
| `/familienfahrt-2022/` | `/archiv/familienfahrt-2022/` |
| `/vergangene-o-fahren/` | `/archiv/vergangene-o-fahrten/` |
| `/aktuelles/`, `/termine/`, `/downloads/`, `/impressum/` | unchanged |
Two post slugs were misleading in WordPress and were renamed to match their own
titles: `o-fahrt-2019``25-o-fahrt-2022`, `termin``28-o-fahrt-2025`.
Umlaut slugs were transliterated (`jubilaum``jubilaeum`).
## What was dropped, and why
| WP id | Item | Reason |
|---|---|---|
| 3 | "Privacy Policy" | Stock English WordPress template, still a draft, references an old bare IP address. Not usable. The Impressum already carries a `Datenschutz` section, which was migrated verbatim. |
| 12 | "Home" | Body was the placeholder *"Dies ist nur eine Demo Seite"*. The real homepage came from the HitMag magazine template plus widgets. |
| 410 | untitled draft post | Empty. |
## Things the export did not contain
The WXR format carries neither media binaries nor widgets. Both were recovered
from the live site before it is retired:
- **Media** — `tools/fetch_media.py` downloads all 165 uploads, caps them at
2000 px on the long edge and re-encodes JPEG at q82. Cached in `.cache/`
(gitignored); only files actually used are committed.
- **Widgets** — the homepage *Unsere Mitglieder* grid (20 portraits) and the
sidebar *In Erinnerung* block were WordPress widgets. `tools/build_data.py`
scrapes them off the rendered homepage into `data/members.yaml` and
`data/memoriam.yaml`.
Note: the WordPress gallery widget **shuffles member order on every request**,
so the original order carried no meaning. Members are now sorted by name.
## Structural changes
- **Termine** — the six TablePress tables (`Termine 2020``2026`) became
`data/termine.yaml`. WordPress only ever embedded table id 7 (*Termine 2026*)
on one page; the other five were orphaned. The `/termine/` page now shows the
newest year expanded with earlier years in collapsible sections.
- **Aktuelles** — was a page containing `[widgets_on_pages id="1"]`. It is now a
real Hugo section with an RSS feed.
- **Categories** — `uncategorized` and `aktuelles` were noise and were dropped.
`ofahrt` became the tag `O-Fahrt`.
## Data defects found in the export
- Post 81 (*Ehrungen von Mitgliedern 2018*) references `wp-image-83`, but
attachment 83 no longer exists — the media record was deleted while the upload
survived. `tools/wp2hugo.py` recovers such references by matching the `src`
filename instead.
- Almost every page ends with an empty `<p></p>` paragraph; these are stripped.
- WordPress wrote emphasis with the whitespace *inside* the markers
(`<strong>Samstag </strong>`), which is not valid CommonMark emphasis. The
converter moves the whitespace outside.
## Unused media (38 files, downloaded but not committed)
These exist in the WordPress media library but appear nowhere on the live site.
They are still in `.cache/uploads/` if any are wanted later.
- **Superseded member portraits (2019)** — `Bene_2`, `Burckhardt_2`, `Christian_2`,
`Christoph_2`, `Christopher_2`, `HeikeF`, `HeikeR`, `Henning`, `JensB`,
`Katrin_2`, `Marcel_2`, `Marvin_2`, `Rainer`, `Steffi_und_Sabine`, `Sven_2`,
`Timo_1`, `Timo_2`, `Udo`, `Uwe`, `Gruppenbildanja`. Replaced by the 2022
`*_web.jpg` set.
- **Former members** — `Jannik_web.jpg`, `Timo_web.jpg` (present in the library,
removed from the live member grid).
- **Old theme assets** — `title.jpg`, `cropped-title.jpg`, `bg.jpeg`,
`Banner_small_web.jpg` (uncropped), and six `cropped-Emblem_transparent*.png`
logo crops. The site now uses `assets/img/logo.png` and `assets/img/banner.jpg`.
- **Orphans** — `SZ-Artikel.jpg`, `Banner.jpg` (2023), `1-1.jpeg`, `6-scaled.jpg`,
`IMG_20190209_205905-3.jpg`, `jahresfahrt-2019.jpg`.
## Open items
- `content/_index.md` carries a **placeholder intro** marked with a `TODO`. There
was no homepage text in WordPress to migrate — please replace it.
- No separate Datenschutzerklärung page exists. The site is fully static with no
cookies, forms or third-party embeds, and the Impressum contains a Datenschutz
section. Worth a legal review before the production cutover.