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:
@@ -0,0 +1,18 @@
|
||||
{{- /* Markdown images inside a page bundle get resized and lazy-loaded.
|
||||
External or non-resource images are passed through unchanged. */ -}}
|
||||
{{- $src := .Destination -}}
|
||||
{{- $alt := .Text | plainify -}}
|
||||
{{- $caption := .Title -}}
|
||||
{{- with .Page.Resources.GetMatch $src -}}
|
||||
{{- $img := . -}}
|
||||
{{- if gt .Width 1200 -}}{{- $img = .Resize "1200x webp q82" -}}{{- end -}}
|
||||
<figure class="content-figure">
|
||||
<a href="{{ .RelPermalink }}">
|
||||
<img src="{{ $img.RelPermalink }}" width="{{ $img.Width }}" height="{{ $img.Height }}"
|
||||
alt="{{ $alt }}" loading="lazy" decoding="async">
|
||||
</a>
|
||||
{{- with $caption }}<figcaption>{{ . }}</figcaption>{{ end -}}
|
||||
</figure>
|
||||
{{- else -}}
|
||||
<img src="{{ $src | safeURL }}" alt="{{ $alt }}" loading="lazy" decoding="async">
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,13 @@
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="footer-contact">
|
||||
<strong>{{ site.Title }}</strong><br>
|
||||
<a href="mailto:{{ site.Params.email }}">{{ site.Params.email }}</a>
|
||||
</p>
|
||||
<nav class="footer-nav" aria-label="Fußzeile">
|
||||
<a href="{{ "/impressum/" | relURL }}">Impressum</a>
|
||||
{{ with .OutputFormats.Get "rss" }}<a href="{{ .Permalink }}">RSS</a>{{ end }}
|
||||
</nav>
|
||||
<p class="footer-copyright">© {{ now.Year }} {{ site.Title }}</p>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -0,0 +1,15 @@
|
||||
{{- /*
|
||||
Resolve a page's featured image to a Resource.
|
||||
Looks at the `featured` front-matter key first (set by the WordPress import),
|
||||
then falls back to the first image in the page bundle.
|
||||
Returns the Resource, or nothing.
|
||||
*/ -}}
|
||||
{{- $img := "" -}}
|
||||
{{- with .Resources.GetMatch (printf "%s" (.Params.featured | default "")) -}}
|
||||
{{- $img = . -}}
|
||||
{{- else -}}
|
||||
{{- with .Resources.ByType "image" -}}
|
||||
{{- $img = index . 0 -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- return $img -}}
|
||||
@@ -0,0 +1,32 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ .Title }} – {{ site.Title }}{{ end }}</title>
|
||||
|
||||
{{ $description := or .Description .Summary site.Params.description }}
|
||||
<meta name="description" content="{{ $description | plainify | truncate 160 }}">
|
||||
|
||||
<meta property="og:site_name" content="{{ site.Title }}">
|
||||
<meta property="og:title" content="{{ if .IsHome }}{{ site.Title }}{{ else }}{{ .Title }}{{ end }}">
|
||||
<meta property="og:description" content="{{ $description | plainify | truncate 160 }}">
|
||||
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
|
||||
<meta property="og:url" content="{{ .Permalink }}">
|
||||
{{ with partial "func/featured-image.html" . }}
|
||||
<meta property="og:image" content="{{ .Permalink }}">
|
||||
{{ end }}
|
||||
|
||||
{{ with resources.Get "css/main.css" | minify | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}">
|
||||
{{ end }}
|
||||
|
||||
{{ with resources.Get site.Params.logo }}
|
||||
{{ $icon := .Resize "180x180 png" }}
|
||||
<link rel="icon" href="{{ $icon.RelPermalink }}" type="image/png">
|
||||
<link rel="apple-touch-icon" href="{{ $icon.RelPermalink }}">
|
||||
{{ end }}
|
||||
|
||||
{{ with .OutputFormats.Get "rss" }}
|
||||
<link rel="alternate" type="application/rss+xml" href="{{ .Permalink }}" title="{{ site.Title }}">
|
||||
{{ end }}
|
||||
<link rel="canonical" href="{{ .Permalink }}">
|
||||
</head>
|
||||
@@ -0,0 +1,38 @@
|
||||
<header class="site-header">
|
||||
<div class="container site-branding">
|
||||
<a class="site-logo" href="{{ "/" | relURL }}" rel="home">
|
||||
{{ with resources.Get site.Params.logo }}
|
||||
{{ $l := .Resize "160x png" }}
|
||||
<img src="{{ $l.RelPermalink }}" width="{{ $l.Width }}" height="{{ $l.Height }}"
|
||||
alt="Wappen des Motorradclub Giebelwald">
|
||||
{{ end }}
|
||||
</a>
|
||||
<div class="site-titles">
|
||||
<p class="site-title"><a href="{{ "/" | relURL }}" rel="home">{{ site.Title }}</a></p>
|
||||
<p class="site-tagline">{{ site.Params.tagline }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ with resources.Get site.Params.banner }}
|
||||
{{ $banner := .Resize "1600x webp q80" }}
|
||||
<img class="site-banner" src="{{ $banner.RelPermalink }}"
|
||||
width="{{ $banner.Width }}" height="{{ $banner.Height }}"
|
||||
alt="" loading="eager" decoding="async">
|
||||
{{ end }}
|
||||
|
||||
<nav class="site-nav" aria-label="Hauptnavigation">
|
||||
<div class="container">
|
||||
<input type="checkbox" id="nav-toggle" class="nav-toggle">
|
||||
<label for="nav-toggle" class="nav-toggle-label">
|
||||
<span aria-hidden="true">☰</span> Menü
|
||||
</label>
|
||||
<ul class="nav-list">
|
||||
<li><a href="{{ "/" | relURL }}"{{ if .IsHome }} aria-current="page"{{ end }}>Start</a></li>
|
||||
{{ range site.Menus.main }}
|
||||
{{ $active := or ($.IsMenuCurrent "main" .) ($.HasMenuCurrent "main" .) }}
|
||||
<li><a href="{{ .URL }}"{{ if $active }} aria-current="page"{{ end }}>{{ .Name }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -0,0 +1,17 @@
|
||||
{{- /* Teaser card for a news post. Context: the page. */ -}}
|
||||
<article class="post-card">
|
||||
{{ with partial "func/featured-image.html" . }}
|
||||
{{ $thumb := .Fill "480x300 Center webp q80" }}
|
||||
<a class="post-card-image" href="{{ $.RelPermalink }}" tabindex="-1" aria-hidden="true">
|
||||
<img src="{{ $thumb.RelPermalink }}" width="{{ $thumb.Width }}" height="{{ $thumb.Height }}"
|
||||
alt="" loading="lazy" decoding="async">
|
||||
</a>
|
||||
{{ end }}
|
||||
<div class="post-card-body">
|
||||
<h3 class="post-card-title"><a href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
|
||||
<p class="post-card-meta">
|
||||
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date | time.Format site.Params.dateFormat }}</time>
|
||||
</p>
|
||||
<p class="post-card-summary">{{ .Summary | plainify | truncate 160 }}</p>
|
||||
</div>
|
||||
</article>
|
||||
@@ -0,0 +1,12 @@
|
||||
{{- /* Context: a slice of {date, description} rows from data/termine.yaml.
|
||||
Cells may contain newlines (rendered as line breaks) and inline HTML. */ -}}
|
||||
<table class="termine">
|
||||
<tbody>
|
||||
{{ range . }}
|
||||
<tr>
|
||||
<th scope="row">{{ .date | strings.Replace "\n" "<br>" | safeHTML }}</th>
|
||||
<td>{{ .description | strings.Replace "\n" "<br>" | safeHTML }}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -0,0 +1,15 @@
|
||||
{{- /* {{< figure "bild.jpg" "Bildunterschrift" >}} */ -}}
|
||||
{{- $src := or (.Get "src") (.Get 0) -}}
|
||||
{{- $caption := or (.Get "caption") (.Get 1) -}}
|
||||
{{- with .Page.Resources.GetMatch $src -}}
|
||||
{{- $img := .Resize "1200x webp q82" }}
|
||||
<figure class="content-figure">
|
||||
<a href="{{ .RelPermalink }}">
|
||||
<img src="{{ $img.RelPermalink }}" width="{{ $img.Width }}" height="{{ $img.Height }}"
|
||||
alt="{{ $caption | plainify }}" loading="lazy" decoding="async">
|
||||
</a>
|
||||
{{ with $caption }}<figcaption>{{ . | markdownify }}</figcaption>{{ end }}
|
||||
</figure>
|
||||
{{- else -}}
|
||||
{{- warnf "figure: %q not found in %s" $src .Page.Path -}}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,32 @@
|
||||
{{- /*
|
||||
Responsive image grid.
|
||||
{{< gallery "a.jpg" "b.jpg" >}} explicit, ordered
|
||||
{{< gallery >}} every image resource of the page
|
||||
Thumbnails link to the full-size original.
|
||||
*/ -}}
|
||||
{{- $page := .Page -}}
|
||||
{{- $images := slice -}}
|
||||
{{- if .Params -}}
|
||||
{{- range .Params -}}
|
||||
{{- with $page.Resources.GetMatch . -}}
|
||||
{{- $images = $images | append . -}}
|
||||
{{- else -}}
|
||||
{{- warnf "gallery: %q not found in %s" . $page.Path -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- $images = $page.Resources.ByType "image" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $images -}}
|
||||
<div class="gallery">
|
||||
{{- range $images }}
|
||||
{{- $thumb := .Fill "400x300 Center webp q80" }}
|
||||
{{- $full := .Resize "1600x webp q82" }}
|
||||
<a class="gallery-item" href="{{ $full.RelPermalink }}">
|
||||
<img src="{{ $thumb.RelPermalink }}" width="{{ $thumb.Width }}" height="{{ $thumb.Height }}"
|
||||
alt="" loading="lazy" decoding="async">
|
||||
</a>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,15 @@
|
||||
{{- /* {{< pdf "satzung.pdf" "Unsere Satzung" >}} — links a file in static/dokumente/. */ -}}
|
||||
{{- $file := .Get 0 -}}
|
||||
{{- $label := or (.Get 1) $file -}}
|
||||
{{- $path := printf "static/dokumente/%s" $file -}}
|
||||
{{- $size := "" -}}
|
||||
{{- with (os.Stat $path) -}}
|
||||
{{- $size = printf "%.1f MB" (div (float .Size) 1048576.0) -}}
|
||||
{{- else -}}
|
||||
{{- warnf "pdf shortcode: %s not found" $path -}}
|
||||
{{- end -}}
|
||||
<a class="pdf-link" href="{{ printf "/dokumente/%s" $file | relURL }}" download>
|
||||
<span class="pdf-icon" aria-hidden="true">PDF</span>
|
||||
<span class="pdf-label">{{ $label }}</span>
|
||||
{{ with $size }}<span class="pdf-size">{{ . }}</span>{{ end }}
|
||||
</a>
|
||||
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ site.Language.Locale | default "de-DE" }}">
|
||||
{{ partial "head.html" . }}
|
||||
<body>
|
||||
<a class="skip-link" href="#main">Zum Inhalt springen</a>
|
||||
{{ partial "header.html" . }}
|
||||
<main id="main" class="site-main">
|
||||
<div class="container">
|
||||
{{ block "main" . }}{{ end }}
|
||||
</div>
|
||||
</main>
|
||||
{{ partial "footer.html" . }}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,54 @@
|
||||
{{ define "main" }}
|
||||
<section class="home-intro">
|
||||
{{ .Content }}
|
||||
</section>
|
||||
|
||||
{{ with where site.RegularPages "Section" "aktuelles" }}
|
||||
<section class="home-news">
|
||||
<h2>Aktuelles</h2>
|
||||
<div class="post-grid">
|
||||
{{ range first 3 . }}
|
||||
{{ partial "post-card.html" . }}
|
||||
{{ end }}
|
||||
</div>
|
||||
<p class="more-link"><a href="{{ "/aktuelles/" | relURL }}">Alle Beiträge →</a></p>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
{{ with hugo.Data.members }}
|
||||
<section class="home-members">
|
||||
<h2>Unsere Mitglieder</h2>
|
||||
<ul class="member-grid">
|
||||
{{ range . }}
|
||||
<li class="member">
|
||||
{{ with resources.Get .image }}
|
||||
{{ $p := .Fill "240x240 Center webp q80" }}
|
||||
<img src="{{ $p.RelPermalink }}" width="{{ $p.Width }}" height="{{ $p.Height }}"
|
||||
alt="{{ $.Title }}" loading="lazy" decoding="async">
|
||||
{{ end }}
|
||||
<span class="member-name">{{ .name }}</span>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
{{ with hugo.Data.memoriam }}
|
||||
<section class="home-memoriam">
|
||||
<h2>In Erinnerung</h2>
|
||||
<ul class="memoriam-list">
|
||||
{{ range . }}
|
||||
<li class="memoriam">
|
||||
{{ with resources.Get .image }}
|
||||
{{ $p := .Fill "200x200 Center webp q80" }}
|
||||
<img src="{{ $p.RelPermalink }}" width="{{ $p.Width }}" height="{{ $p.Height }}"
|
||||
alt="" loading="lazy" decoding="async">
|
||||
{{ end }}
|
||||
<span class="memoriam-name">{{ .name }}</span>
|
||||
<span class="memoriam-date">✝ {{ .died }}</span>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,26 @@
|
||||
{{ define "main" }}
|
||||
<div class="page">
|
||||
<header class="page-header">
|
||||
<h1>{{ .Title }}</h1>
|
||||
</header>
|
||||
|
||||
{{ with .Content }}<div class="prose">{{ . }}</div>{{ end }}
|
||||
|
||||
{{ $pages := .RegularPages }}
|
||||
{{ if $pages }}
|
||||
{{ if eq .Section "aktuelles" }}
|
||||
<div class="post-grid">
|
||||
{{ range $pages }}
|
||||
{{ partial "post-card.html" . }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ else }}
|
||||
<ul class="page-list">
|
||||
{{ range $pages.ByTitle }}
|
||||
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,26 @@
|
||||
{{ define "main" }}
|
||||
<article class="page">
|
||||
<header class="page-header">
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ if eq .Section "aktuelles" }}
|
||||
<p class="page-meta">
|
||||
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date | time.Format site.Params.dateFormat }}</time>
|
||||
{{ with .Params.tags }}
|
||||
<span class="tags">{{ range . }}<span class="tag">{{ . }}</span>{{ end }}</span>
|
||||
{{ end }}
|
||||
</p>
|
||||
{{ end }}
|
||||
</header>
|
||||
|
||||
<div class="prose">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
|
||||
{{ if eq .Section "aktuelles" }}
|
||||
<nav class="post-nav" aria-label="Weitere Beiträge">
|
||||
{{ with .PrevInSection }}<a class="prev" href="{{ .RelPermalink }}">← {{ .Title }}</a>{{ end }}
|
||||
{{ with .NextInSection }}<a class="next" href="{{ .RelPermalink }}">{{ .Title }} →</a>{{ end }}
|
||||
</nav>
|
||||
{{ end }}
|
||||
</article>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,29 @@
|
||||
{{ define "main" }}
|
||||
<div class="page">
|
||||
<header class="page-header">
|
||||
<h1>{{ .Title }}</h1>
|
||||
</header>
|
||||
|
||||
{{ with .Content }}<div class="prose">{{ . }}</div>{{ end }}
|
||||
|
||||
{{ $years := slice }}
|
||||
{{ range $year, $_ := hugo.Data.termine }}{{ $years = $years | append $year }}{{ end }}
|
||||
{{ $years = sort $years "value" "desc" }}
|
||||
{{ $current := index $years 0 }}
|
||||
|
||||
{{ range $years }}
|
||||
{{ $rows := index hugo.Data.termine . }}
|
||||
{{ if eq . $current }}
|
||||
<section class="termine-current">
|
||||
<h2>Termine {{ . }}</h2>
|
||||
{{ partial "termine-table.html" $rows }}
|
||||
</section>
|
||||
{{ else }}
|
||||
<details class="termine-past">
|
||||
<summary>Termine {{ . }}</summary>
|
||||
{{ partial "termine-table.html" $rows }}
|
||||
</details>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
Reference in New Issue
Block a user