WordPress 5.0: Das hat der neue Editor „Gutenberg“ zu bieten
Für die WordPress-Gemeinde ist es eine kleine Revolution: Bisher verfügte das quelloffene Content-Management-System (CMS) über ein einzelnes Editorfeld und konnte nur über Plugins oder Kenntnisse in HTML und CSS für komplexere Inhaltslayouts genutzt werden. Mit WordPress 5.0 hat sich das geändert, da die Version einen neuen Editor im Gepäck hat, der unter dem Codenamen „Gutenberg“ bekannt ist – benannt nach dem Erfinder des Buchdrucks, Johannes Gutenberg. Damit wird es Nutzern des CMS möglich, Inhalte aus einzelnen Inhaltsblöcken zu erstellen.
Es gibt beispielsweise statische Blöcke wie Überschriften, Absätze, Bilder und Tabellen. Darüber hinaus sind auch dynamische Blöcke möglich, um zum Beispiel eine Liste der letzten Beiträge anzuzeigen. Auf diese Weise lassen sich mit dem neuen Editor auch vergleichsweise komplexe Inhaltslayouts für Beiträge und auch ganze Seiten erstellen. Eine Bearbeitung mit
Shortcodes oder in der HTML-Ansicht ist dafür nicht mehr notwendig – sofern das gewählte Theme die neue Funktion unterstützt.
Neben den vielen neuen Möglichkeiten bedeutet der neue Editor eine große Umstellung in erster Linie für die „normalen“ Nutzer von WordPress, aber auch für alle Entwickler von Plugins und Themes. Der Editor kann als WordPress-Plugin getestet werden, was vor dem Update auf 5.0 jedem zu empfehlen ist – schon um zu prüfen, ob alle installierten Plugins, die etwas in der Bearbeitungsansicht verändern, weiter funktionieren. Bei vielen Themes sollte es allerdings keine großen Probleme geben, sofern sie sich hauptsächlich um die Darstellung der Inhalte kümmern und nicht mit Funktionen gespickt sind, die in Plugins gehören.
Falls etwas nicht funktioniert und es keine Alternative zu dem problematischen Plugin oder Theme gibt (oder der neue Editor aus anderen Gründen nicht genutzt werden soll), lässt sich der neue Editor über ein Plugin deaktivieren, sodass nicht mit dem Update auf 5.0 gewartet werden muss, bis alle eingesetzten Plugins und Themes kompatibel sind. Jetzt aber zum für Entwickler spannenden Teil: der Erweiterbarkeit des neuen Editors durch eigene Inhaltsblöcke.
Erstellung eines eigenen Blocks
In diesem Beitrag geht es um die Erstellung eines Autorenboxblocks, der Bild, Namen und Beschreibung enthält. Der Code dazu liegt auf Github, im Artikel ist er teilweise gekürzt. Getestet wurde der Code dieses Artikels mit Gutenberg 3.
Voraussetzungen für die Entwicklung
Neben einem Webserver und installiertem WordPress mit aktivem Gutenberg-Plugin braucht es ein leeres Plugin und Babel für die Kompilierung von modernem JavaScript zu JavaScript nach dem ES5-Standard für breite Browserunterstützung. Außerdem Sass und einen Taskrunner wie Webpack oder Gulp, um die Kompilierung zu automatisieren und die Ergebnisse am richtigen Ort auszugeben.
Die Beschreibung der Installation und Konfiguration dieser Tools würde den Rahmen dieses Artikels sprengen – die offiziellen Dokumentationen sollten da gut weiterhelfen. Ein Beispiel für eine funktionierende Konfiguration gibt es zudem im Github-Repo zum Artikel. Für das WordPress-Plugin wird im Ordner wp-content/plugins
ein Verzeichnis t3n-gutenberg-block
und darin eine t3n-gutenberg-bloroock.php
erstellt und mit folgendem Inhalt gefüllt:
<?php /** * Plugin Name: t3n Gutenberg Block * Description: Example Gutenberg block. * Version: 0.1.0 * Author: Florian Brinkmann * License: GPL v2 http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ if ( ! defined( 'WPINC' ) ) { die; }
Der Kommentar enthält die für WordPress wichtigen Informationen, damit das Projekt als Plugin erkannt wird. Über ! defined( 'WPINC' )
wird geprüft, ob das Plugin nicht im WordPress-Umfeld ausgeführt wird – in diesem Fall wird das Programm abgebrochen. Damit sind die Voraussetzungen geschaffen und es kann losgehen mit der Erstellung des Blocks.
Einbindung von Skript und Stylesheets
Neben dem Plugin-Header wird in der PHP-Datei nur noch die Einbindung verschiedener Dateien geregelt. Alles Weitere läuft dann über JavaScript. Zunächst wird das Skript zur Registrierung des Blocks und eine Stylesheet-Datei für das Backend eingebunden – die Benennung der Dateien ist von Zac Gordons Gutenberg-Kurs inspiriert:
add_action( 'enqueue_block_editor_assets', function() { wp_enqueue_script( 't3n53-editor-script', plugins_url( 'assets/js/editor.blocks.js', __FILE__ ), [ 'wp-blocks', 'wp-element' ] ); wp_enqueue_style( 't3n53-editor-style', plugins_url( 'assets/css/editor.blocks.css', __FILE__ ) ); } );
enqueue_block_editor_assets
ist ein neuer Action-Hook, um Dateien für Blöcke in der Editoransicht bereitzustellen. Über wp_enqueue_script()
wird das Skript assets/js/editor.blocks.js
eingebunden. Als Abhängigkeiten werden die Skripte wp-blocks
und wp-element
definiert, damit wir in dem Blockskript auf Bestandteile dieser Skripte des neuen Editors zugreifen können. Über wp_enqueue_style()
wird anschließend die CSS-Datei assets/css/editor.blocks.css
eingebunden. Anschließend werden Styles für Frontend und Backend bereitgestellt:
add_action( 'enqueue_block_assets', function() { wp_enqueue_style( 't3n53-frontend-style', plugins_url( 'assets/css/frontend.blocks.css', __FILE__ ) ); } );
Auch hier kommt mit enqueue_block_assets
ein neuer Action-Hook zum Einsatz. Unterschied zu enqueue_block_editor_assets
ist, dass er im Frontend und Backend läuft.
Wenn hierüber also ein Skript oder Stylesheet nur für das Frontend eingebunden werden sollte, müsste dafür mit ! is_admin()
geprüft werden, ob es sich um einen Frontend-Aufruf handelt.
Die drei Dateien, die in den zwei Code-Blöcken eingebunden werden, sind jeweils das Ergebnis der Kompilierung des JavaScripts via Babel und vom Sass zu CSS. Erzeugt werden müssen die Dateien mit dem korrekten Namen und im richtigen Verzeichnis der eingesetzten Taskrunner.
Registrierung des Blocks
Innerhalb des t3n-gutenberg-block-Ordners
wird ein Ordner blocks
und darin ein Verzeichnis author-box
erstellt. Darin finden die ganzen Dateien für den Block Platz. Es werden benötigt:
- index.js, die nach Babel-Kompilierung als assets/js/editor.blocks.js gespeichert wird.
- frontend.scss, die nach der Kompilierung zu CSS als assets/css/frontend.blocks.css gespeichert wird.
- editor.scss, die nach der Kompilierung als assets/css/editor.blocks.css gespeichert wird.
Damit sind alle notwendigen Dateien angelegt. Der Taskrunner muss dafür sorgen, dass die Kompilierungsergebnisse mit den angegebenen Namen in den entsprechenden Verzeichnissen ausgegeben werden, von wo aus sie mit wp_enqueue_style() und wp_enqueue_script() eingebunden werden.
Importieren der notwendigen Komponenten
In der index.js
werden zunächst die notwendigen Komponenten aus dem neuen Editor importiert:
const { Fragment } = wp.element; const { registerBlockType } = wp.blocks; const { MediaPlaceholder, BlockControls, RichText } = wp.editor; const { IconButton, Toolbar, TextControl } = wp.components;
wp.element
ist ein Abstraktionslayer für React, es wird hier also quasiReact.Fragment
importiert.registerBlockType
ist die Funktion zur Registrierung des Blocks.- Die übrigen Komponenten werden genutzt, um den Block abzubilden.
Um herauszufinden, welche Komponenten vorhanden sind, lohnt sich ein Blick in das Github-Repo des Editors. Im ackages/components/src-Verzeichnis
liegen generische React-Komponenten, die zur Erstellung gängiger UI-Elemente genutzt werden können, wie beispielsweise IconButton. packages/editor/src/components
beinhaltet über Komponenten, die spezifisch zur Erstellung eigener Editorlayouts gedacht sind, wie etwa RichText.
Aufruf der Registerblocktype-Funktion
Die entscheidende Funktion, damit der neue Editor den eigenen Block erkennt, ist registerBlockType()
. So sieht der erste Teil dieser Registrierung aus:
registerBlockType( 't3n53/author-box', { title: 'Autorenbox', icon: 'admin-users', category: 'common', attributes: { name: { type: 'string', source: 'text', selector: '.author-name', default: ", }, imgSrc: { type: 'string', source: 'attribute'‘, attribute: 'src', selector: 'img', }, imgId: { type: 'number', }, imgAlt: { type: 'string'‘, source: 'attribute', attribute: 'alt', selector: 'img', }, description: { type: 'array', source: 'children', selector: '.author-description', }, },
Als erster Parameter wird ein eindeutiger Bezeichner des Blocks angegeben, der sich aus einem Namespace (hier: t3n53
) und dem Namen des Blocks zusammensetzt. Der zweite Parameter ist ein Objekt mit Einstellungen, von denen hier nicht alle genutzt werden:
title
gibt den Blocktitel an.icon
ist das Icon des Blocks. Hier kann der Name eines Dashicons oder ein SVG-Icon angegeben werden.category
ist die Kategorie, in der der Block aufgelistet werden soll.attributes
sind quasi die Daten des Blocks. Das sind für diesen Block der Autorenname (name), die URL des Bildes (imgSrc), die ID des Bildes (imgID), der Alternativtext des Bildes (imgAlt) sowie die Autorenbeschreibung (description).
Für die Attribute wird angegeben:
- welchen Typ das Attribut hat,
- optional mit
source
, wie der Attributwert vom gespeicherten Beitragsinhalt extrahiert werden soll, (children
heißt, dass Kindknoten des Elements extrahiert und als Array zurückgegeben werden, sodass zum Beispiel bei einem Absatz mit einer Verlinkung auch der Link mit gespeichert wird), - optional, welches HTML-Attribut genutzt werden soll, falls es sich um einen Wert aus einem Attribut handelt,
- optional, welcher Selektor den Wert enthält.
Wenn kein source
angegeben ist, wird der Wert im Blockkommentar gespeichert, der in der post_content-Datenbanktabelle
jeden Block umschließt und von WordPress für das Blockparsing genutzt wird. Das sieht beispielsweise so aus:
<!-- wp:t3n53/author-box {"imgId":1573} -->
BlockMarkup <!-- /wp:t3n53/author-box -->
Die Ansicht des Blocks im Backend
Über den edit
-Key des Setting-Objekts der registerBlockType
-Funktion wird angegeben, wie der Block im Backend dargestellt wird (um Platz zu sparen, wurden hier ein paar HTML-Elemente und Attribute von Komponenten weggelassen – den kompletten Code gibt es im Github-Repo):
edit: props => { const { attributes: { name, imgSrc, imgId, imgAlt, description }, className, setAttributes, isSelected } = props; const onSelectImg = ( img ) => { if ( ! img || ! img.url ) { setAttributes( { imgSrc: undefined, imgId: undefined } ); return; } setAttributes( { imgSrc: img.url, imgId: img.id, imgAlt: img.alt } ); } return ( <div className={ className }> { ! imgId ? ( <MediaPlaceholder /* […] */ onSelect={ onSelectImg } /> ) : ( <Fragment> <img src={ imgSrc } alt={ imgAlt } /> { isSelected ? ( <IconButton /* […] */ onClick={ () => setAttributes( { imgSrc: null, imgId: null, imgAlt: null } ) } /> ) : " } </Fragment> ) } <TextControl /* […] */ value={ name } onChange={ ( name ) => setAttributes( { name } ) } /> <RichText /* […] */ value={ description } onChange={ ( description ) => setAttributes( { description } ) } /> </div> ); },
Zunächst werden notwendige Konstanten definiert. Aus dem props.attributes-Objekt
werden die eigenen Attribute und der von WordPress generierte Klassenname für den Block (className)
geholt, und aus props
die Funktion zum Ändern der Attribute (setAttributes)
sowie isSelected
. onSelectImg
prüft, ob ein Bild gewählt ist, und setzt die Bildattribute für das Bild entsprechend. Innerhalb des return
-Teils wird der Block erstellt, der im Backend angezeigt wird. Wenn keine imgId
gesetzt ist, wird die MediaPlaceholder
-Komponente dargestellt, über die der Nutzer ein Bild hochladen oder aus der Mediathek wählen kann. Wenn ein Bild gewählt wird, wird onSelectImage
ausgeführt. Ist ein Bild ausgewählt, wird es angezeigt. Wenn der Block dann aktiv ist (isSelected
ist true
), wird ein Iconbutton angezeigt, der bei einem Klick die Bildattribute auf Null
setzt und so wieder die Anzeige von MediaPlaceholder
triggert. Über eine TextControl-Komponente wird ein einfaches Textfeld angezeigt, das das name-Attribut befüllt. Des Weiteren wird für die Autorenbeschreibung ein RichText-Feld angezeigt, in dem der Nutzer auch grundlegende Formatierungen und Verlinkungen verwenden kann.
Die Funktion zum Speichern des Blockinhalts
Jetzt fehlt noch die save
-Funktion, die festlegt, wie der Blockinhalt gespeichert wird. Im Gegensatz zu der edit
-Funktion ist sie recht kurz:
save: props => { const { className, imgSrc, imgAlt, description, name } = props.attributes; return ( < div className={ className }> < div className='author-avatar'> < img src={ imgSrc } alt={ imgAlt } /> < /div> < div className='author-meta'> < p className='author-name'>{ name }</p> < div className='author-description'> { description } < /div> < /div> < /div> ); },
Zuerst werden wieder die nötigen Attribute geholt und anschließend mit return
das Markup definiert, das gespeichert wird. Nach ein paar Zeilen Styling in den beiden CSS-Dateien sieht das Ergebnis im Backend aus wie in der Abbildung oben – einmal ohne Inhalt und einmal mit, jeweils im ausgewählten Zustand.
Fazit
Der Release-Zyklus für die neue Version startete im Dezember letzen Jahres. Er war eine große Umstellung für die WordPress-Nutzer aber auch Entwickler von Plugins und Themes. Trotzdem bietet der neue Editor mehr Freiheiten bei der Gestaltung von Inhalten in WordPress, ohne dass umständliche Shortcodesyntax genutzt, ein Pagebuilder installiert oder HTML-Kenntnisse erlernt werden müssen. Vorab sollte der neue Editor über das Plugin aber getestet werden, um Kompatibilitätsprobleme frühzeitig auszuschließen.
Für die Einarbeitung in die Entwicklung mit dem neuen Editor sind das Gutenberg-Repo sowie das Handbuch aus den Readme-Dateien des Repos eine gute Anlaufstelle. Der Code der Core-Blöcke (in core-blocks
) liefert Informationen darüber, mit welchen Komponenten Standardblöcke umgesetzt wurden, worüber sich mindestens der Lösungsansatz für viele Anwendungsfälle ergibt.