Bei statischen Webseiten bietet es sich an, sie nach jedem Bearbeiten in fertige HTML-Seiten zu exportieren. Lektor leistet genau das und bietet dabei hohen Komfort.
Wie Jekyll oder Hugo erzeugt Lektor [1] statische Webseiten, doch das zugrundeliegende grafische Backend erinnert an klassische, datenbankbasierte CMS-Anwendungen. Das Editieren der Inhalte kommt der Arbeit mit einer Textverarbeitung näher als bei Jekyll oder Hugo, die eher Programmierer als ihre Zielgruppe sehen.
Zur Installation von Lektor genügt ein Kommandozeilenaufruf, der allerdings keine Paketabhängigkeiten prüft. Stellen Sie also vor der Installation sicher, dass für Ubuntu als Basissystem die Pakete python3-dev, libssl-dev und libffi-dev installiert sind.
Installation
Führen Sie dann für eine lokale Installation in Ihrem Home-Verzeichnis unter ~/.local/lib/lektor/ das Kommando aus der ersten Zeile von Listing 1 aus. Damit Sie hier Lektor direkt ohne vorangestellten Pfad auf der Konsole aufrufen können, muss die Variable PATH den Pfad ~/bin enthalten. Eine systemweite Installation gelingt mit dem Befehl aus der zweiten Zeile von Listing 1.
Listing 1
Installation
$ curl -sf https://www.getlektor.com/installer.py | python3 $ curl -sf https://www.getlektor.com/installer.py | sudo python3
Ein lektor quickstart erzeugt ein simples Projekt mit der entsprechenden Ordnerstruktur (Abbildung 1). Wechseln Sie danach auf der Konsole in den dabei genannten Ordner, und führen Sie lektor server aus. Ein eingebauter Webserver stellt dann das angesprochene Backend unter der URL http://127.0.0.1:5000 bereit. Die Stift-Buttons rechts oben auf den Vorschauseiten öffnen die Eingabemaske zum Bearbeiten der Seite (Abbildung 2). Nach dem Speichern erscheint wieder die Vorschau.

Abbildung 1: Lektor erzeugt statische Webseiten aus blanken HTML- und CSS-Dateien, für die jedes preisgünstige Webhosting-Angebot gut genug ist.

Abbildung 2: Dennoch bringt die Software ein grafisches Backend mit, in dem selbst WordPress-Anwender sich wohlfühlen.
Anders als bei konventionellen Content-Management-Systemen ist das Lektor-Backend nur für den lokalen Rechner bestimmt. Es dient als komfortable Alternative zum (ebenfalls leicht möglichen) direkten Bearbeiten der Textdateien [2], in denen die Software die Seiteninhalte speichert.
Jeder Ordner unterhalb von content/ entspricht einer HTML-Seite, die Ordnerstruktur spiegelt die URL-Pfade der Website wider. Mit lektor build erzeugen Sie eine aktualisierte Version, standardmäßig im Verzeichnis ~/.cache/lektor/builds/ oder im per --output-path angegebenen Ordner. Diesen statischen HTML-Code laden Sie dann auf den Webserver im Netz hoch. Die Software automatisiert diesen Upload auf Wunsch per FTP oder Rsync [3].
Hallo Welt
Die Dokumentation auf der Lektor-Webpräsenz erläutert das System umfassend, wenngleich nicht immer wünschenswert konkret. Im Wesentlichen besteht eine mit Lektor erstellte Webpräsenz aus Datenschemata im Stil von Windows-Ini-Dateien sowie Templates [4]. Listing 2 zeigt ein Datenschema aus einem simplen “Hello-World”-Beispiel, das die zwei Datenfelder Titel und Body definiert.
Listing 2
Einfaches Datenschema
[model]
name = Page
label = {{ this.title }}
[fields.title]
label = Title
type = string
[fields.body]
label = Body
type = markdown
Diese Ini-Datei heißt page.ini und liegt im Ordner models/. Auf einen rein beschreibenden [model]-Abschnitt folgen zwei fields.Feldname-Blöcke, die Datenfelder mit dem auf einen Punkt folgenden Namen definieren. In der GUI erscheinen sie unter dem als label angegebenen Namen. Das Label im [model]-Block benutzt einen Template-Ausdruck in doppelten geschweiften Klammern.
Das bei Lektor eingesetzte Template-System Jinja [5] kommt später noch genauer zur Sprache. Im Moment genügt es zu wissen, dass dank dieses Ausdrucks in der GUI statt eines generischen Namens der Titel einer Seite erscheint. Die einer Seite zugeordnete Datenmodelldefinition (Ini-Datei) kann außerdem in einem [children]-Block festlegen, welches Datenmodell Unterseiten haben dürfen. Ansonsten wählen Sie dafür in der GUI zwischen allen vorliegenden Datenmodellen.
Außer string, einer einfachen, unformatierten Zeichenkette, und dem schon erwähnten Markdown, gibt es weitere Typen [6]. Dazu zählen Ganz- und Dezimalbruchzahlen, ein Zeit- und Datumsfeld, eine auf Wohlgeformtheit überprüfte URL sowie Kontrollkästchen- und Auswahlfelder.
Diese per Ini-Datei erzeugten Felder setzt die Backend-GUI automatisch in eine passende Eingabemaske um. Die Größe eines Felds in der Eingabemaske bestimmen Sie ebenfalls per Datenschemadefinition. Dazu fügen Sie den [fields]-Blöcken ein Attribut width mit Werten wie 1/2 oder 1/3 hinzu. Die Felder erscheinen dann nur in halber respektive gedrittelter Breite, wie in der Mitte von Abbildung 3.

Abbildung 3: Zu den Spezialitäten von Lektor zählen einfach zu erzeugende Gruppen von Eingabefeldern, die zum Beispiel aus dem Dateinamen, einer Größenangabe und der Bildunterschrift den Bildkasten links erzeugen.
Abbildungen
Der in Abbildung 3 rot umrahmte Bereich demonstriert ein besonderes Leistungsmerkmal von Lektor: Flows [7] genannte, in variabler Anzahl und Abfolge hinzufügbare Feldgruppen. Der Haupttext setzt sich aus einem Text-Block, einer Abbildung, einem Kasten und abschließend wieder einem Textblock zusammen. Prinzipiell wäre aber jede beliebige Abfolge dieser drei Elemente ohne Anpassen des Datenschemas möglich.
Abbildungen erzeugt Lektor im Beispiel aus dem Dateinamen des Bilds, der gewählten Ausrichtung und Größe sowie einer Bildunterschrift. Kästen bestehen aus der Überschrift und einem Text, Textblöcke ausschließlich aus Text im Markdown-Format. Das erweiterte Datenmodell page.ini, das der Feldanordnung in Abbildung 3 entspricht, zeigt Listing 3.
Listing 3
Flows einsetzen
[model]
name = Page
label = {{ this.title }}
[fields.title]
label = Titel
type = string
[fields.headline]
label = Überschrift
type = string
[fields.teaser]
label = Vorspann
type = text
[fields.main]
label = Haupttext
type = flow
flow_blocks = text, box, image
Entscheidend ist der Block [fields.main], der das Datenfeld Haupttext als Datentyp flow definiert und als flow_blocks die Typen text, box und image gestattet. Diese Blockauswahl verweist auf die eigenständigen Datenmodelle text.ini, box.ini und image.ini.
Die technische Umsetzung dieser Unterobjekte unterscheidet sich kaum von der eigenständiger Seiten: Wieder definiert die Ini-Datei das Datenmodell. Der einzige Unterschied zu den seitengebundenen Datenmodellen besteht darin, dass der erste Block hier [block] heißt statt [model]. Sonst bleibt die Syntax gleich, auch die zur Definition von Feldern. Die Flow-Modelle liegen im Ordner flowblocks/ statt in models/. Listing 4 zeigt die Ini-Datei für das image-Modell.
Listing 4
Image-Flowblock
[block] name = Abbildung button_label = Bild [fields.filename] label = Dateiname type = string width = 1/3 [fields.alignment] label = Ausrichtung type = select choices = left, right choice_labels = linksbündig, rechtsbündig default = left width = 1/3 [fields.size] label = Größe type = select choices = 150, 300, 450 choice_labels = klein, mittel, groß default = 300 width = 1/3 [fields.caption] label = Bildunterschrift type = string
Templates
Lektor erzeugt den HTML-Code für die Seiten aus den vorliegenden Daten mithilfe von Templates [5]. Sie tragen bis auf die Endung denselben Namen wie das zugehörige Datenmodell und finden sich im Ordner templates/. Die Seite aus Abbildung 3 entsteht mithilfe der Datei page.html, die den Code aus Listing 5 enthält.
Listing 5
Abgeleitetes Template
{% extends "layout.html" %}
{% block title %}{{ this.title }}{% endblock %}
{% block body %}
<h2>{{ this.headline }}</h2>
<b>{{this.teaser}}</b>
{% for blk in this.main.blocks %}
{{ blk }}
{% endfor %}
{% endblock %}
Die Angaben this.headline und this.teaser in doppelt geschweiften Klammern geben schlicht den Inhalt der nach this genannten Felder zurück. Am Ende steht eine Schleife, die über die vom Anwender in variabler Anzahl einbettbaren Blocks Text, Kasten oder Abbildung iteriert. Dabei verweist this wie bei statischen Feldern auf die aktuelle Seite, main auf die Felddefinition [fields.main] in page.ini, und blocks auf die von Lektor erstellte Liste der eingefügten Blöcke.
Etwas schwieriger zu verstehen sind die in {% %} eingebetteten block– und extends-Direktiven: Sie sorgen dafür, dass nicht jedes Template Boilerplates wie den HTML-Header zu wiederholen braucht. Er ist in der im Code referenzierten Datei layout.html enthalten, die page.html erweitert.
Konkret heißt das: layout.html packt als Erweiterung vorgesehene Blöcke ({% block Name %}) in das Grundgerüst der Seite. Diese überschreibt das erbende Template mit seiner endgültigen Fassung [8]. Das übergeordnete Template bestimmt die Position in der fertigen HTML-Datei.
Auch zu jedem Block gehört ein eigenes Template, diesmal aus dem Ordner templates/blocks/. Wieder entsprechen die Namen der Blockdefinitionen denen der Ini-Dateien, die die Datenfelder definieren, sprich in unserem Beispiel text.html, box.html und image.html. Während die beiden erstgenannten lediglich Datenfeldinhalte in HTML-Tags einbetten, nutzt image.html die Lektor-API [9] zur Bildbearbeitung (Listing 6).
Listing 6
Block-Template für Bilder
{% set my_image = site.get('/').attachments.get(this.filename) %}
{% set thumb = my_image.thumbnail(this.size) %}
<div style="width: {{ thumb.width }}px;
background-color: #daeef3;
float: {{ this.alignment }};
margin: 15px;
padding: 5px; ";>
<img src="{{ my_image.thumbnail(this.size)|url }}" alt="">
<br>{{ this.caption }}
</div>
Die erste Zeile lädt das Attachment mit dem Dateinamen aus dem Feld filename. Lektor betrachtet alles im einer Seite zugeordneten Verzeichnis als Attachment, außer content.lr (den Seiteninhalt). Die GUI enthält einen Button, um Dateien dorthin zu kopieren.
Die zweite Zeile skaliert das Bild auf die Größe aus dem Datenfeld Größe, also die im Listing für fields.size angegebenen Zahlenwerte 150, 300 oder 450. In der GUI sind stattdessen die choice_labels namens klein, mittel oder groß zu sehen. In beiden Fällen entsteht ein Python-Objekt. Die dritte Zeile nutzt das zum skalierten Bild gehörige Objekt, um per CSS die Kastenbreite zu setzen.
Andere CMS-Systeme betten Bilder mithilfe eines speziellen Codes in den Fließtext ein, sodass man sie leicht an eine andere Stelle kopieren kann. In Lektor müssen Sie stattdessen Text aus dem vorausgehenden Text-Feld in ein folgendes kopieren. Dafür entschädigen komfortable Eingabemasken anstelle des fehlerträchtigen Eintippens von Code.
Plugins
Prinzipiell wäre es möglich, für Lektor ein Plugin zu schreiben, das die Markdown-Syntax und ihr simples Tag zum Einbetten von Bildern erweitert: Das Lektor-Framework bringt eine Plugin-Schnittstelle mit [10]. Ein fertiges Plugin für diese oder ähnliche Erweiterungen des Markdown-Eingabeformats gib es aber offenbar nicht. Dasselbe gilt für ein Syntax-Highlighting für die Markdown-Eingabefelder.
Insgesamt stehen etwa 20 Plugins [11] mit so unterschiedlichen Funktionen wie Minifizierung von HTML, der abgekürzten Vorschau von Blogposts oder der Bereitstellung von Asciidoc-Support bereit. Außerdem gibt es Templates zum Erzeugen von Atom-Feeds oder Inhaltsverzeichnissen und etliche weitere.
Das Codebeispiel zum Skalieren von Bildern hat bereits die von Lektor bereitgestellte Python-API genutzt. Zu den Highlights dieser Schnittstelle gehören der Zugriff auf Inhalte beliebiger Seiten (im Beispiel mit site.get()) sowie Abfragen [12], die mehrere zu bestimmten Kriterien passende Seiten herauspicken. Hinzu kommt eine Paginierungsfunktion [13], die zum Beispiel das Erstellen von Blog-Systemen [14] zum Kinderspiel macht.
Fazit
Lektor erstellt aus Datenschemadefinitionen im Stil von Ini-Dateien automatisch übersichtliche Eingabemasken, die Sie ähnlich leicht bedienen wie in WordPress. Die Entwicklung der Website gelingt dank simpler Datenschemadefinition und einer in Grundzügen simplen Template-Sprache ohne tiefere Programmierkenntnisse.
Alle Abfragen und API-Funktionen laufen nur lokal beim Erzeugen der zum Veröffentlichen bestimmten Site ab, kostet also keine Server-Performance. Dem steht der Nachteil gegenüber, dass Lektor sich nicht für Echtzeit-Kommentarfunktionen eignet. Die Entwickler empfehlen, stattdessen Disqus [15] einzubinden. (cla)
Infos
-
Lektor: https://www.getlektor.com
-
Content-File-Format: https://www.getlektor.com/docs/content/#content-file-format
-
Deployment: https://www.getlektor.com/docs/cli/deploy/
-
Datenmodelle: https://www.getlektor.com/docs/models/
-
Template-System Jinja: https://jinja.palletsprojects.com/en/2.11.x/templates/
-
Datentypen: https://www.getlektor.com/docs/api/db/types/
-
Template-Vererbung: https://jinja.palletsprojects.com/en/2.11.x/templates/#template-inheritance
-
Bildbearbeitung: https://www.getlektor.com/docs/templates/imageops/
-
Lektor-Plugins und -Templates: https://www.getlektor.com/plugins/
-
Paginierung: https://www.getlektor.com/docs/guides/pagination/
-
Blog-Howto: https://www.getlektor.com/docs/guides/blog/
-
Disqus: https://disqus.com






Interessanter Artikel.
Leider werden die Quellen für die in Abbildung 3 gezeigte Webseite nicht komplett dargestellt, so dass ich nicht in der Lage bin, so eine Seite nachzubauen. Sowie ich das Feld vom Typ flow in mein Model einbaue, wirft Lektor wahlweise mit Build Fehlern um sich oder der Editor funktioniert nicht mehr – man sieht lediglich eine weiße Seite.
Mir ist klar, dass der komplette Abdruck aller zugehörigen Dateien den Rahmen des Artikels sprengen würde, aber man könnte ja zumindest ein Archiv mit dem Projekt als Download anbieten.
Viele Grüße,
Roland Schwarzkopf