Befreiung vom (digitalen) Überfluss

OnePage Website mit HUGO

Basierend auf dem statischen Website-Generator HUGO hat die hier vorgestellte Website zwei Besonderheiten: Die einzelnen Abschnitte werden als Page-Bundles verwaltet und es gibt ein Lowtech-CMS für kleinere Aktualisierungen.

https://evawilhelm.de

Die OnePage-Website evawilhelm.de basiert auf dem statischen Website-Generator HUGO und hat zwei Besonderheiten:

  1. Die einzelnen Abschnitte (Sections) werden in HUGO als Page-Bundles verwaltet. Dies ermöglicht eine hohe Flexibilität bei der Erstellung und Pflege der Seite.
  2. Ich habe eine Art “Lowtech-CMS” entwickelt, mit dem die Website-Inhaberin einzelne Bereiche der Website über den Webbrowser ganz einfach aktualisieren kann.

Ziel und Konzept

Mir liegt es am Herzen, schön gestaltete, emotional ansprechende Websites zu erstellen, die gleichzeitig “schlank” sind – also ein geringes Datenvolumen haben und damit weniger CO2 emittieren und datenschutzfreundlich sind.

Ich bin der Meinung, dass für viele, einfachere Websites ein großes CMS, wie z.B. Wordpress, Typo3, Drupal, Joomla (und wie sie alle heißen) vollkommen überqualifiziert ist. Dies besteht buchstäblich aus zehntausenden von php-Dateien, muss regelmäßig aktualisiert werden und ist auch sonst – alleine wegen der Komplexität – ständig davon bedroht, gehackt zu werden. Es gibt Mindestanforderungen an den Webserver, so dass man oft nicht mit dem günstigsten Hostingtarif auskommt. Das CMS generiert die Website “on the fly” aus einer Datenbank. Das alles belastet den Server, erhöht den CO2-Ausstoß und macht die Seiten langsamer beim Abruf.

Und … es ist eigentlich oftmals überflüssig: Viele Websites werden nur selten aktualisiert. Entweder, weil es nicht nötig ist, oder weil die Website-Besitzerin dafür keine Zeit findet. Ein flexibles CMS oder Baukastensystem birgt zudem die Gefahr, dass der Redakteur das ursprünglich runde Website-Konzept nach und nach zerfasert.

Ich denke, es wäre in manchen Fällen sinnvoller, eine statische Website zu erstellen und dann, wenn vielleicht im Laufe des Jahres mal eine Kleinigkeit geändert werden muss, die Agentur zu beauftragen. Das geht i.d.R. wesentlich schneller, als wenn man es selbst versucht (spart also Zeit und vor allem Nerven) und oft sind es dann doch nicht nur Texte, die angepasst werden müssen. Wenn es aber z.B. einen neuen Arbeitsbereich oder ein neues Projekt gibt, dann kann die Agentur passende Lösungen vorschlagen, und das Design oder die Struktur der Seite entsprechend anpassen.

Es kann aber gute Gründe geben, warum dieser Ansatz nicht attraktiv erscheint. Der größte Teil der Seite muss zwar vielleicht nicht regelmäßig aktualisiert werden, aber es gibt diesen einen Bereich, wo aktuelle Termine veröffentlicht werden, oder ein aktueller Hinweistext optional erscheinen soll.

Ich habe hierfür folgende Strategie gewählt: Ich benutze HUGO als statischen Website-Generator. Damit kann ich sehr schnell und flexibel hochwertige Websites erstellen, die (zumindest von uns) einfach aktualisiert werden können.

Dazu kommt eine Art “Lowtech-CMS”, das letztlich aus einer einzigen php-Datei besteht und der Kundin die Möglichkeit gibt, einzelne Texte auf der Seite selbst auszutauschen.


HUGO Implementierung

Grundaufbau

  • Die einzelnen Abschnitte (Sections) des Themes werden als Page-Bundles verwaltet. Die Navigation wird automatisch daraus erzeugt. Dies ermöglicht das einfache Ändern der Reihenfolge oder die Ergänzung weiterer Abschnitte
  • Die Gestaltung/Art der Abschnitte wird durch Partials realisiert, die im Frontmatter der Seite adressiert werden.
  • Das Template der Titelseite (index.html) holt alle Abschnitte aus dem Content-Ordner und ruft jeweils das gewählte Partial auf.
  • Sofern der Abschnitt Bilder hat, werden diese aus dem img-Ordner des Page-Bundles geholt und automatisch skaliert.
  • Auch komplexere Strukturen, wie z.B. der Abschnitt “Frauenkreis” ist so realisiert worden.


Struktur der Inhalte:

content
├── _index.md
├── abschnitte
│   ├── willkommen
│   │   ├── img
│   │   │    └── image-1.jpg
│   │   └── index.md
...
│   └── frauenkreis
│       ├── devah
│       │   ├── img
│       │   │    ├── image-1.jpg
│       │   │    ...
│       │   │    └── image-3.jpg
│       │   └── index.md
│       ├── harburg
│       │   ├── img
│       │   │    ├── image-1.jpg
│       │   │    ...
│       │   │    └── image-4.jpg
│       │   └── index.md
│       └── _index.md
...
└── impressum.md


Zugriff auf die Inhalte im Template index.html:

​         	{{ with ( $.Site.GetPage "/abschnitte" ) }}
​                	{{ range $idx, $value := .Pages }}
​                		{{- $layout := .Params.Tpl | default "simple" -}}
​                		{{- $layout = print $layout ".html" -}}
​                		{{- partial $layout . -}}
​			{{ end }}
​		{{ end }}


Beispiel eines Frontmatters (für den Abschnitt “Willkommen”):


---
dachzeile: "Herzlich Willkommen!"
title: "Schön, dass du da bist!"
tpl: "text-zentriert-bild"
weight: 10
---


index.html ruft dann (in diesem Beispiel) das Partial “text-zentriert-bild.html” auf, das den HTML-Code für eine Section enthält:

     <section id="{{ .File.ContentBaseName }}">
        <div class="container spacer-double-lg border-bottom">
           <div class="row justify-content-between align-items-center">
              <div class="col-md-5 mb-5 mb-lg-0 text-center" style="margin-top:3rem;">
                 <div class="mb-4">
		{{ with .Params.Dachzeile }}<span class="text-uppercase text-secondary font-secondary font-size-12">{{ . }}</span>{{ end }}
                    <h1 class="mb-0">{{ .Title }}</h1>
                    <img class=" max-width-4" src="svg/title-line.svg" alt="Trennlinie">
		{{ with .Description | safeHTML }}<p><em>{{ . }}</em></p>{{ end }}
                 </div>
                 {{ .Content }}
              </div>
              <div class="col-md-6">
                 <div class="position-relative">
                 	{{- $size := .Site.Params.imgSizeLarge -}}
                 	{{- $title := .Title -}}
		{{ $filter := "img/*" }}
		{{- with .Resources.Match $filter -}}
			{{- range first 1 . -}}
				{{- $scaled := .Resize $size -}}
				<img class="img-fluid w-100" src="{{ $scaled.RelPermalink }}" alt="{{ $title  | htmlEscape }}" loading="lazy">
    			{{- end -}}
    		{{- end -}}
                 </div>
              </div>
           </div>
        </div>
     </section>

nanocms

Ich habe ausgehend von dieser Anleitung php-Code in die HUGO-Website integriert: How to include a PHP form in HUGO. Auf diesem Weg habe ich die Seite nanocms.php realisiert, die wie folgt funktioniert:

In der HTML-Seite werden Bereiche, die editierbar sein sollen, mit
<span id="FELDNAME"></span>
gekennzeichnet.

nanocms.php bietet für jedes dieser Felder ein Formularfeld, speichert die Eingaben in einer JSON-Datei data.json, die außerhalb des /public-Ordners liegt und ersetzt die Platzhalter per Regulärem Ausdruck auf der Basis der JSON-Daten. Auf diesem Weg kann eine Redakteurin die gekennzeichneten Felder ganz einfach selbst aktualisieren, und es kommt zu keinem Konflikt, wenn die gesamte Seite von der Agentur geändert und mit HUGO neu erzeugt wird.

nanocms.php:

<?php
// Datei-Pfade
$datenDatei = '../data.json';
$html = 'index.html';
$result = '';

// Security
$pruef_user = 'admin'; // set Userdata
$pruef_pwd  = 'your password';

$user = isset($_POST['user']) ? htmlspecialchars($_POST['user']) : '';
$password = isset($_POST['password']) ? htmlspecialchars($_POST['password']) : '';

// editierbare Bereiche definieren
    $felder = array(
        array(
            'name' => 'feld1', // Platzhalter in der HTML-Datei ist "name" uppercase: <span id="NAME">...</span>
            'label' => 'Termin-Jahrgang',
            'input' => 'input-text', // Variante: textarea
            'placeholder' => 'span'
        ),
        array(
            'name' => 'feld2',
            'label' => 'Termine im Frauenkreis Harburg, Zeile 1',
            'input' => 'input-text',
            'placeholder' => 'span'
        ),
        array(
            'name' => 'feld3',
            'label' => 'Termine im Frauenkreis Harburg, Zeile 2',
            'input' => 'input-text',
            'placeholder' => 'span'
        ),
        array(
            'name' => 'feld4',
            'label' => 'Termine feste Gruppe Devah, Zeile 1',
            'input' => 'input-text',
            'placeholder' => 'span'
        ),
        array(
            'name' => 'feld5',
            'label' => 'Termine feste Gruppe Devah, Zeile 2',
            'input' => 'input-text',
            'placeholder' => 'span'
        )
    );
    
function markdownToHtml($markdown) {

    // strong
    $markdown = preg_replace('/\*\*(.*?)\*\*/', '<strong>$1</strong>', $markdown);

    // em
    $markdown = preg_replace('/\*(.*?)\*/', '<em>$1</em>', $markdown);

    // a
    $markdown = preg_replace('/\[(.*?)\]\((.*?)\)/', '<a href="$2">$1</a>', $markdown);

    return $markdown;
}

// Überprüfen, ob das Formular abgesendet wurde
if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    // Array für die Feldwerte erstellen
    $daten = array();

    // Feldwerte aus dem Formular abrufen und in das Datenarray speichern
    foreach ($felder as $feld) {
        $name = $feld['name'];
        if (isset($_POST[$name])) {
            $daten[$name] = htmlspecialchars($_POST[$name]);
        }
    }

    // Array in JSON umwandeln
    $datenJson = json_encode($daten);

    if (($pruef_user===$user)&&($pruef_pwd===$password)) {
        // Daten in die JSONdatei schreiben
        file_put_contents($datenDatei, $datenJson);

        // Platzhalter in index.html ersetzen
        $sourceHTML = file_get_contents($html);
        foreach ($felder as $feld) {
    	    $name = $feld['name'];
    	    $placeholder = $feld['placeholder'];
    	    $formatted_data = markdownToHtml($daten[$name]);
            $sourceHTML = preg_replace('/<' . $placeholder .' id="' . strtoupper($name) . '">(.*?)<\/' . $placeholder .'>/', '<' . $placeholder .' id="' . strtoupper($name) . '">' . $formatted_data . '</' . $placeholder .'>', $sourceHTML);
        }
        file_put_contents($html, $sourceHTML);
        $result = "<h4 style=\"color:green;\">Änderungen gespeichert!</h4><br>";
    } else {
        $result = "<h4 style=\"color:red;\">Falscher Benutzername oder Passwort!</h4><p style=\"color:red;\">(Änderungen wurden nicht veröffentlicht)</p><br>";
    }
} else {
    // Daten aus der JSONdatei auslesen
    $datenJson = file_get_contents($datenDatei);
    $daten = json_decode($datenJson, true);
}
?>

                     <?php echo $result ?>
                     <!-- FORMULAR -->
    <form action="nanocms.php" style="margin-bottom:2rem;" method="post">
        <?php foreach ($felder as $feld) { ?>
            <label for="<?php echo $feld['name']; ?>"><?php echo $feld['label']; ?></label>
            <?php if ($feld['input'] === 'input-text') { ?>
                <input type="text" class="form-control mb-3" name="<?php echo $feld['name']; ?>" placeholder="<?php echo $feld['label']; ?>" value="<?php echo isset($daten[$feld['name']]) ? $daten[$feld['name']] : ''; ?>">
            <?php } else { ?>
                <textarea class="form-control mb-3 resize-n" rows="6" name="<?php echo $feld['name']; ?>" placeholder="<?php echo $feld['label']; ?>"><?php echo isset($daten[$feld['name']]) ? $daten[$feld['name']] : ''; ?></textarea>
            <?php } ?>
        <?php } ?>
        <label for="user">Benutzername</label>
        <input type="text" class="form-control mb-3" name="user" value="<?php echo $user; ?>">
         <label for="password">Passwort</label>
        <input type="password" class="form-control mb-3" name="password" value="<?php echo $password; ?>">       
        <button class="btn w-50 btn-primary" type="submit" accesskey="s" style="margin-top:2rem;">
            <span class="mn-top">Änderung <u>s</u>peichern</span>
        </button>
        <span style="float:right;font-size:13px;padding-top:0.7rem;"><a href="nanocms.php">Logout</a></span>
    </form>
    		    <!-- FORMULAR ENDE -->


« Datenschutzfreundliche Youtube-Thumbnails für HUGO | E-Books mit HUGO »