Hinzufügen eines formularspezifischen Gravity Forms Hook zu deinem Code

Wer meinem Blog regelmäßig liest, wird sicher festgestellt haben, dass ich in vielen Projekten Gravity Forms verwende. Ich mag die große Vielfalt an Funktionen des Plugins (und seiner Erweiterungen) sehr, aber vielleicht noch sehr viel mehr die Möglichkeit, viele Dinge anzupassen. Ich habe auch dabei geholfen eine Erweiterung zu schreiben, mit er es möglich ist, alle Dateien eines Eintrags (oder mehrerer Einträge) auf einmal herunterzuladen. Diese Woche haben wir eine neue Version veröffentlicht, die eine kleine aber wichtige Neuerung enthält: die Möglichkeit, Hooks nur für bestimmte Formular zu verwenden.

Hooks in Gravity Forms verwenden

Wenn man Gravity Forms Funktionalitäten anpassen möchte, dann funktioniert das genau wie im Core oder in Plugins/Themes. Ihr verwendet die Funktionen add_action() oder add_filter() für euren eigenen Code. Und Gravity Forms hat sehr viele Actions und Filter.

Sehen wir uns als Beispiel die gform_pre_submission Action an. Ihr könnt diese verwenden, um einen Eintrag zu verändern, bevor Benachrichtigungen versendet wurden und vor der Speicherung des Eintrags in der Datenbank. Ein Anwendungsfall könnte wie folgt aussehen:

function my_gform_pre_submission( $form ) {
    $_POST['input_1'] = strtolower( $_POST['input_1'] );
}
add_action( 'gform_pre_submission', 'my_gform_pre_submission' );

Dies würde den Wert vom Formularfeld mit der ID 1 (input_1) nehmen und den Textwert in Kleinbuchstaben ändern. Dies würde allerdings für jedes einzelne Formular angewendet werden. Wenn ihr es nur für ein Formular anwenden wollt, dann müsst ihr es für die Action so schreiben, dass sie nur für dieses eine Formular greift. Glücklicherweise ist bei Gravity Forms genau das möglich, denn ihr könnt Hook-Namen dynamisch verwenden. Ersetzt hierzu den add_action Aufruf wie folgt:

add_action( 'gform_pre_submission_5', 'my_gform_pre_submission' );

Dieser Action-Name hat einen Suffix _5 und wird daher nur für das Formular mit der ID 5 aufgerufen. Ich hatte das bereits im Blogbeitrag Dynamische Formular-Hooks für GravityForms Anfang des Jahres erklärt und dabei auch beschrieben, wie man mit diesen statischen Werten für die IDs der Formulare in den Hook-Namen umgehen kann.

Nachdem wir also in dieser kleinen Einführung noch einmal die Grundlagen wiederholt habe, können wir in das eigentliche Thema einsteigen und uns ansehen, wie ihr Hooks nur für bestimmte Formulare in eurem eigenen Code verwendet.

Hooks in eurem Code anbieten

Wenn ihr eine Veränderung von Teilen eures Codes erlauben möchtet, dann müsst ihr dazu eine diese beiden Funktionen verwenden:

  1. apply_filters()
  2. do_action()

Ihr könnt euch sicherlich schon denken, welche Funktion hier jeweils für welche Art von Hook verwendet wird. Der Unterschied besteht darin, dass ein Filter einen Wert zurückgibt, eine Action aber nicht. Wenn ihr also einen Filter verwenden möchtet, dann sieht es in etwa wie folgt aus:

$value = apply_filters( 'filter_name', $value, $arg1, $arg2 );

Ihr definiert also einen Namen für den Filter und übergebt die Variable, die verändert werden soll. Optional könnt ihr weitere Argumente übergeben, mit deren Hilfe ihr dann innerhalb der Callback-Funktion entschieden könnt, wie ihr den Wert verändert.

Für eine Action sieht es ziemlich ähnlich aus. Ihr speichert aber keinen Rückgabewert, da die Funktion auch nichts zurückgibt:

do_action( 'action_name', $arg1, $arg2 );

Und weil die Funktion keinen Wert verändert, müsst ihr auch nicht zwingend eine Variable übergeben, es sind also alle Argumente optional. Viele Actions bekommen gar keinen Wert übergeben, wie etwa die wp_head Action.

Den Hook formularspezifischen machen

Nachdem wir nun also gelernt haben, wie man Hooks zum eigenen Code hinzufügt, sehen wir uns jetzt an, wie man diese nur für bestimmte Formular ausführen lässt. In der offiziellen Dokumentation von Gravity Forms findet sich nur eine Seite zu Actions, aber leider ohne viel Erklärung. Die Veränderungen, die ihr machen müsst, sind aber für Actions und Filter identisch. Alles, was ihr tun müsst, ist die Funktionen mit einem gf_ Präfix zu versehen und als ersten Parameter ein Array mit dem Hook-Namen und der Formular-ID zu übergeben. Für einen Filter sieht es dann wie folgt aus:

$value = gf_apply_filters( array( 'filter_name' $form_id ), $value, $arg1, $arg2 );

In diesem Beispiel gehen wir davon aus, dass die Formular-ID in der Variablen $form_id gespeichert ist, sie könnte aber auch in $form['id'], $entry['form_id'] oder etwas Ähnlichem enthalten sein. Ihr müsst also nur sicherstellen, dass ihr sie an die Funktion übergebt. Für eine Action sieht der Funktionsaufruf dann wie folgt aus:

gf_do_action( array( 'action_name' $form_id ), $arg1, $arg2 )

Das war auch schon alles, was ihr tun müsst!

Fazit

Wenn ihr eine Erweiterung, ein Plugin oder eigenen Code für Gravity Forms schreibt, ist es immer gut, wenn ihr euch überlegt, an welchen Stellen jemand eventuell euren Code anpassen möchte. Hierfür dann Hooks zur Verfügung zu stellen, kann euren Code sehr viel besser nutzbar machen. Und wenn ihr diese Hooks hinzufügt, dann verwendet dabei immer die Gravity Forms Funktionen, damit sie spezifisch für individuelle Formulare verwendet werden können.

Optionen zu einem Block hinzufügen

Nachdem wir uns im letzten Beitrag angesehen haben, wie wir modernes JavaScript verwenden können, um einen Block zu schreiben, möchte ich heute zeigen, wieso man das machen möchte, indem wir dem Block Optionen hinzufügen. Ich werde euch dabei nur die Änderungen zeigen, die ihr vornehmen müsst.

Erstellen einer „edit“ Komponente

Als ersten Schritt entfernen die edit() Funktion aus der index.js Datei und verschieben den Code in eine eigene Datei. Die index.js hat dann also noch den folgenden Inhalt:

import { registerBlockType } from '@wordpress/blocks';
import './editor.scss';
import './style.scss';
import Edit from './edit';
import metadata from './block.json';

registerBlockType(
	metadata.name,
	{
		edit: Edit
	}
);

Wir importieren die neue Edit Komponente und weisen sie den Optionen des Blocks zu, während wir ihn registrieren. Wir entfernen weiterhin den Import für die ServerSideRender Komponente, da diese nicht mehr länger in der index.js Datei verwendet wird.

Implementieren der Komponente

Nachdem wir unsere neue Datei importiert haben, müssen wir in dieser die ServerSideRender Komponente erneut umsetzen und alle Dinge hinzufügen, wie wir für unsere Option brauchen. Das Ergebnis sieht wie folgt aus:

import ServerSideRender from '@wordpress/server-side-render';
import { InspectorControls } from '@wordpress/block-editor';
import { Disabled, PanelBody, RangeControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import './editor.scss';

export default function Edit( props ) {
	const {
		attributes,
		setAttributes,
	} = props;
	const {
		postsToShow,
	} = attributes;

	return (
		<>
			<InspectorControls>
				<PanelBody
					title={ __( 'Settings', 'rpssrb' ) }
				>
					<RangeControl
						label={ __( 'Number of items', 'rpssrb' ) }
						value={ postsToShow }
						onChange={ ( value ) =>
							setAttributes( { postsToShow: value } )
						}
						min="1"
						max="100"
					/>
				</PanelBody>
			</InspectorControls>
			<div { ...useBlockProps() }>
				<Disabled>
					<ServerSideRender
						block="rpssrb/random-posts"
						attributes={ attributes }
					/>
				</Disabled>
			</div>
		</>
	);
}

OK, das ist jetzt natürlich sehr viel mehr als zuvor. Aber schauen wir uns jeden Teil einzeln an, um zu verstehen, was hier genau passiert.

Schritt 1: Komponenten importieren

In unserem neuen Code verwenden wir einige React-Komponenten. Diese erhalten wir aus verschiedenen WordPress-Packages, die wir alle in den Zeile 2-5 importieren.

Schritt 2: Erstellen einer Funktion und Verwendung der Komponenten-Properties

Wir erstellen zuerst einmal eine Funktion Edit und exportieren diese direkt wieder als Standard-Funktion. Damit kann sie dann in der index.js Datei verwendet werden, wie wir im ersten Code-Beispiel gesehen haben. Die „edit“ und „save“ Funktionen erhalten immer automatisch die Properties (Eigenschaften) übergeben. Wir können die Properties, die wir benötigen, dann gezielt „extrahieren“. Die attributes Property ist ein Objekt, welches alle Attribute zu einem spezifischen Block enthält. Es verwendet einen „React-State“, um die Benutzeroberfläche in Echtzeit zu aktualisieren, sobald sich einer der Werte ändert. Die setAttributes Property ist eine Funktion, die dazu verwendet wird, die Werte aus des attributes Objekts um „State-Objekt“ zu aktualisieren.

Da attributes auch wieder ein Objekt ist, können wir auch hier wieder ein paar Werte extrahieren und eigenen Variablen zuweisen. Wir machen das mit dem postsToShow, welches wir später verwenden werden.

Schritt 3: Die „Inspector-Controls“ implementieren

Nachdem wir nun das Attribut haben, welches wir ändern wollen, können wir das Kontrollfeld implementieren, mit dem es angepasst werden kann. Das Erste, was euch hier vielleicht auffällt und im ersten Moment merkwürdig vorkommen könnte, ist der „leere Tag“ <> in Zeile 12. Dieser ist notwendig, da die Funktion nur einen einzelnen Tag haben darf, der zurückgegeben wird. Wir verschachteln also die restlichen JSX-Tags in diesem einen leeren Tag.

Als Nächstes öffnen wir die <InspectorControls> Komponente, welche alle Elemente enthält, die in der Block-Sidebar angezeigt werden. Darin können wir <PanelBody> verwenden, damit unsere Option eine schöne Überschrift bekommt und zuletzt fügen wir mit <RangeControl> dann das Kontrollfeld hinzu, welches für die Anpassung der Option verwendet wird. Das Ergebnis sieht dann wie folgt aus:

The InspectorControls with a PanelBody and the title "Settings" as well as a RangeControl with the label "Number of item" with a selected value of 5.

Wir haben nun also einen schönen kleinen „Bereichs-Slider“, mit dem wir festlegen können, wie viele Posts angezeigt werden können. Mit den min und max Attributen können wir festlegen, in welchem Bereich dieser Wert liegen darf. Der Anfangswert (value) wird aus dem zuvor definierten postsToShow Attribut ausgelesen und bei einem onChange Event wird dann über die Funktion setAttribues der Wert im „State“ verändert. Einige Dinge werden euch nicht auf Anhieb klar sein, aber wenn ihr eine Zeit damit gearbeitet habt, werdet ihr es bald verstehen.

Schritt 4: Den Block rendern

Nachdem wir jetzt unser Kontrollfeld für die Einstellung der Anzahl an Posts haben, müssen wir diese Posts in unserer neuen Edit Komponente auch rendern. Hierzu verwenden wir erneut die <ServerSideRender> Komponente und übergeben jetzt zusätzlich die attributes an den Tag, damit sich die Komponente jedes Mal von selbst aktualisiert, wenn wir die Anzahl der anzuzeigenden Beiträge verändern.

Wir verschachteln das Ganz zusätzlich noch in einem <div>, welchem wir alle Block-Properties hinzufügen und zusätzlich noch in einer <Disabled> Komponente, damit wir unseren Block im Block-Editor auch einfach auswählen können (statt durch einen Klick einem der Post-Permalinks zu folgen).

Schritt 5: Registrierung der Attribute für den Block

Jedes Mal, wenn ihr in einem Block Attribute verwenden wollt, dann müsst ihr diese zuvor registrieren. Das wird in der block.js Datei gemacht. Die neue Datei sieht dann wie folgt aus:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 2,
	"name": "rpssrb/random-posts",
	"version": "0.1.0",
	"title": "Random Posts",
	"category": "widgets",
	"icon": "smiley",
	"description": "An example for a ServerSideRender block using minimal ES6 code.",
	"supports": {
		"html": false
	},
	"textdomain": "random-posts-server-side-render-block-es6",
	"attributes": {
		"postsToShow": {
			"type": "number",
			"default": 5
		}
	},
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css"
}

Schritt 6: Verwendung der Option in der PHP-Callback-Funktion

Nachdem wir nun also unsere Option haben und diese auch speichern können, müssen wir sie auch in unserer PHP-Callback-Funktion verwenden. Aber glücklicherweise ist das recht einfach, denn die Option wird automatisch an unsere Funktion übergeben:

function rpssrb_render_callback( $atts ) {
	$args = [
		'post_type'      => 'post',
		'orderby'        => 'rand',
		'posts_per_page' => (int) $atts['postsToShow'],
	];

	// Run the query and render the posts as in the previous blog posts ...
}

Das war es auch schon! Ich würde euch nun empfehlen, euch einmal die verschiedenen Komponenten aus dem Block-Editor-Handbuch anzusehen. Da sind viele sehr nützliche Komponenten dabei, um Optionen für euren Block zu bauen. Denkt nur immer daran, auch die Attribute zu registrieren.

Fazit

Ich weiß, dass das sehr viel mehr Code war, als in den zwei vorherigen Beiträgen. Aber wenn ihr die „Inspector-Controls“ erst einmal grundlegen eingerichtet habt, könnt ihr recht einfach neue Kontrollfelder hinzufügen.

Wie ich bereits im ersten Beitrag dieser kleinen Serie beschrieben habe, könnte man die Dateien auch noch weiter aufteilen. Wenn beispielsweise die „Inspector-Control“ sehr komplex werden, dann möchtet ihr sie vielleicht in eine andere Datei verschieben. Aber in diesem kleinen Beispiel wollte ich es wieder so einfach wir möglich gestalten und habe sie bei einer edit.js Datei belassen.

Ein erster einfacher Block mit etwas ES6 Code – es ist nicht so furchteinflößend, wie es vielleicht klingt

Ich bin gerade im Urlaub und hatte ein paar technikfreie Tage, daher kommt mein Blog-Beitrag diese Woche ein wenig später. In meinem vorherigen Beitrag habe ich euch gezeigt, wie ihr einen Block ohne ES6/React Code schreiben könnt. Diese Woche möchte ich euch nun zeigen, was ihr mindestens tun müsste, um das volle Potenzial aus der modernen JavaScript-Entwicklung zu holen, ohne dabei zu komplex zu werden.

Erstellen eines Blocks mit dem „create-block“ Skript

Wie ich bereits im letzten Beitrag erwähnt hatte, würde ich euch sehr stark empfehlen, euch das create-block Paket anzusehen, mit dem ihr ein kleines Plugin erstellen könnt. Es erstellt eine ganz Menge von Dateien, mit denen ihr dann sofort loslegen könnt. Wir erstellen hier nun das gleiche Beispiel wie auch dem letzten Beitrag, indem wir innerhalb des wp-content/plugins Ordners den folgenden Befehl ausführen:

npx @wordpress/create-block random-posts-server-side-render-block-es6

Dies erstellt die folgenden Dateien im neu erstellten Plugin-Ordner:

$ tree
.
├── node_modules
├── package.json
├── random-posts-server-side-render-block-es6.php
├── readme.txt
└── src
    ├── block.json
    ├── edit.js
    ├── editor.scss
    ├── index.js
    ├── save.js
    └── style.scss

Im node_modules Ordner befinden sich alle Dateien, die für die Kompilierung von ES6 Code notwendig ist, damit der Browser diesen ausführen kann. Die package.json Datei definiert alle dazu notwendigen Abhängigkeiten sowie die Befehle, die ausgeführt werden.

Die Block-Definition

Die wichtigste Datei in einem „Block-Plugin“ ist die src/block.json Datei. In dieser werden Name, Titel, Icon und andere Details definiert:

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 2,
    "name": "rpssrb/random-posts",
    "version": "0.1.0",
    "title": "Random Posts",
    "category": "widgets",
    "icon": "smiley",
    "description": "An example for a ServerSideRender block using minimal ES6 code.",
    "supports": {
        "html": false
    },
    "textdomain": "random-posts-server-side-render-block-es6",
    "editorScript": "file:./index.js",
    "editorStyle": "file:./index.css",
    "style": "file:./style-index.css"
}

Am Ende dieser Datei findet ihr drei sehr nützliche Zeilen. Diese definieren, wo die Asset-Dateien liegen. Während wir also im letzten Beitrag noch wp_enqueue_scripts verwendet haben, um die JavaScript-Datei zu laden, bekommen wir das nun einfach so dazu. Es ist also nicht mehr notwendig in PHP zu prüfen, ob die Datei existiert, diese mit ihren Abhängigkeiten zu laden und einen Timestamp für das Caching anzuhängen. Allein dieses kleine Feature ist es schon wert, dass man sich ein wenig mehr mit moderner Block-Entwicklung beschäftigt.

Ihr könntet nun weiterhin ES5 Code in diese JavaScript-Datei schreiben, aber sehen wir uns doch unser Beispiel mal mit ein wenig modernem JavaScript-Code an.

Registrierung des Server-Side-Rendered-Blocks mit ES6

Wie ihr oben im Auszug des Dateisystems gesehen habt, werden mit dem Skript drei JS Dateien erzeugt. Die index.js Datei registriert den Block. Die edit.js Datei ist zuständig für die Ausgabe des Blocks im Block-Editor, inkl. der Einstellungsoptionen. Die save.js wird abschließend verwendet, um den Block in den Inhalt des Beitrags zu speichern. Diese beiden zusätzlichen Dateien können verwendet werden, aber man kann auch alles in die index.js schreiben. Bei wirklich komplexen Blöcken würde man die Dateien sogar noch mehr aufspalten und beispielsweise die edit.js in kleinere (wiederverwendbare) Teile zerlegen. Aber um es weiterhin möglichst einfach zu halten, verwenden wir nur die index.js Datei:

import ServerSideRender from '@wordpress/server-side-render';
import { registerBlockType } from '@wordpress/blocks';
import './editor.scss';
import './style.scss';
import metadata from './block.json';

registerBlockType(
    metadata.name,
    {
        edit() {
            return (
                <ServerSideRender
                    block="rpssrb/random-posts"
                />
            );
        }
    }
);

Um eine Komponente oder Funktion in ES6 nutzen zu können, müssen wir sie zuerst importieren. Wir werden in dem Beispiel die Komponente ServerSideRender sowie die Funktion registerBlockType einsetzen. Wenn ihr den Code insgesamt mit unserem Beispiel von letzter Woche vergleicht, dann werdet ihr feststellen, dass er gar nicht so anders aussieht. Dieser „komisch aussehende HTML-Tag“ ist JSX, eine Erweiterung für HTML, die von React verwendet wird, um dessen Komponenten zu erstellen. Wenn man an diese Komponenten einen Parameter übergeben möchte, dann verwendet man dazu Attribute, genau wie in HTML (anstatt ein JSON-Objekt zu verwenden, wie in der ES5-Variante). In diesen Attributen kann man auch direkt JavaScript-Code verwenden. Für unsere ServerSideRender Komponente übergeben wir aber nur einen statischen Blocknamen.

Euch ist vielleicht ebenfalls aufgefallen, dass wir den Namen des Blocks für die registerBlockType Funktion aus dem metadata Objekt erhalten, das wir ebenfalls importieren. Das gibt uns die Möglichkeit, auf alle darin deklarierten Werte zuzugreifen. In diesem sehr einfachen Beispiel ist das nur der Name. Wir könnten sogar auch den Wert für das block Attribut der ServerSideRender dynamisch aus den Metadaten setzen, aber wir verwenden hier einen statischen String.

Die zwei Zeilen, die ich bisher noch nicht angesprochen habe, sind die Imports für die SCSS-Dateien. Wenn ihr das @wordpress/scripts Paket verwendet, das mit dem npx Befehl oben installiert wird, dann bekommt ihr eine Sass-Kompilierung einfach mit dazu. Ihr benennt einfach zwei Dateien mit editor.scss und style.scss und diese werden dann automatisch nur für den Block-Editor oder auch für das Frontend kompiliert.

Jetzt, nachdem ihr diesen schönen Code geschrieben habt, könnt ihr ihn endlich kompilieren. Wenn ihr das nur einmal machen wollt (in einer minifizierten Version, vorgesehen für den produktiven Einsatz), dann führt ihr npm run build aus. Während der aktiven Entwicklung verwendet ihr aber eher npm run start, um immer eine aktuelle (ausführliche und somit besser für das Debugging geeignete) Version zu haben, die den Code widerspiegelt, den ihr gerade schreibt.

Registrierung der PHP-Callback-Funktion für den Server-Side-Rendered-Block

Da wir hier noch immer einen Block schreiben, der seinen Inhalt aus dem PHP-Backend bekommt, müssten wir noch immer eine Callback-Funktion registrieren. Das sieht ziemlich ähnlich zu unserem vorherigen Beispiel aus. Allerdings verwenden wir hier die index.js Datei, um den Block in PHP zu registrieren und fügen die Callback-Funktion über die weiteren Parameter hinzu.

function create_block_rpssrb_block_init() {
	register_block_type(
		__DIR__ . '/build',
		[
			'render_callback' => 'rpssrb_render_callback',
		]
	);
}
add_action( 'init', 'create_block_rpssrb_block_init' );

Statt den Blocknamen zu verwenden, geben wir also nur den Pfad zum build Ordner an, der durch das Build-Skript erstellt wird. In diesem Ordner sucht PHP nach einer index.js Datei und darin dann nach dem Namen für den Block.

Wir verwenden auch weiterhin die gleiche Callback-Funktion aus dem vorherigen Beitrag. Auch hier können wir wieder den „Bonus“ haben, um die gleiche Callback-Funktion auch mit einem Shortcode zu verwenden.

Fazit

Es ist nicht wirklich schwer, einen Block mit ES6 Syntax zu schreiben. Wenn man dazu noch das create-block Paket (oder das @wordpress/script Paket) verwendet, wird auch der schwierige Teil für die Kompilierung sehr viel einfacher. Das Einzige, was ihr noch tun müsst – und das etwas schwerer sein kann – ist die Installation von node sowie die anschließende Installation von npm auf eurem System. Aber selbst dann müsst ihr dafür sogar, dann diese beiden immer aktuell und kompatibel mit den Paketen sind, die ihr verwendet. Das kann von Zeit zu Zeit schon etwas nervig sein.

Den Grund für all das werde ich in meinem nächsten Blogbeitrag behandeln, wenn wir ein paar einfache Einstellungsoptionen zum Block hinzufügen, um ihn noch besser nutzbar zu machen.

Einen Shortcode durch einen Block ersetzen, mit so wenig und so einfachem Code wie möglich

Mitte der Woche hatte Topher auf Twitter eine interessante Frage zum aktuellen Stand der Block-Entwicklung gestellt:

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Übersetzung: Ich habe es mir schon lange nicht mehr angesehen, ist es schon so einfach einen Gutenberg Block zu schreiben wie einen Shortcode?

Das hat mich ein wenig zum Nachdenken gebracht, wie ich diese Frage übersetzen würde. Als ich meine ersten Blöcke geschrieben habe, waren es meisten „Server-Side-Rendered-Blocks“. Bei diesen Blöcken wird der Inhalt nicht direkt im Editor erzeugt, sondern von PHP-Code in einer Callback-Funktion gerendert, so wie man es von Shortcodes her kennt.

Der andere komplizierte Teil bei der Entwicklung von Blöcken ist die Notwendigkeit so viele neue Dinge zu lernen: React, JSX, Kompilierung von modernem JavaScript mit webpack und vieles mehr. Das alles zusammen macht es eben nicht „so einfach“ wie die Arbeit mit Shortcodes. Aber ich sollte dennoch einmal zeigen, wie man mit wenig Code einen Block entwickeln kann, der einen Shortcode ersetzt, ohne dabei all diese neuen Dinge lernen zu müssen.

Einen Server-Side-Rendered-Block entwickeln – der PHP Teil

Der einfachste Teil ist die Registrierung des Blocks und der Render-Callback-Funktion in PHP. Während man bei einem Shortcode die Funktion add_shortcode()für die Callback-Funktion verwenden würde, ist es ein Parameter bei der Registrierung eines Blocks in PHP:

function rpssrb_init() {
	register_block_type(
		'rpssrb/random-posts',
		[
			'render_callback' => 'rpssrb_render_callback',
		]
	);
}
add_action( 'init', 'rpssrb_init' );

Für diesen Blogbeitrag erstellen wir einen Block, der zufällig ein paar Beiträge rendert. Der Inhalt der Callback-Funktion ist hier sehr ähnlich zu der eines Shortcodes. Ihr könnt auch die gleichen Hilfsfunktionen wie bei einem Shortcode verwenden. Der Callback könnte wie folgt aussehen:

function rpssrb_render_callback( $atts ) {
	$atts = shortcode_atts(
		[
			'post_type'      => 'post',
			'orderby'        => 'rand',
			'posts_per_page' => 5,
		],
		$atts
	);

	$query = new WP_Query( $atts );

	$output = '';
	if ( $query->have_posts() ) {
		$output .= '<ul>';
		while ( $query->have_posts() ) {
			$query->the_post();
			$output .= sprintf(
				'<li><a href=%s>%s</a></li>',
				get_permalink(),
				get_the_title()
			);
		}
		$output .= '</ul>';
	}

	return $output;
}

Wir setzen ein paar Standard-Attribute und führen dann eine WP_Query aus, um 5 zufällige Beiträge zu bekommen. Diese geben wir dann in einer einfachen unsortierten Liste aus, wobei wir einfach den Titel ausgeben und auf den Beitrag verlinken.

Implementierung des Blocks in JavaScript – nur mit ES5 Code

Wie schon zuvor erwähnt ist das schwierigste bei der Block-Programmierung das Erlernen von React, JSX usw. und die Kompilierung davon. Wenn ihr euch damit noch nicht auskennt, kann es wirklich kompliziert werden. Daher zeige ich euch hier, wie ich mit etwas ES5 („altem JavaScript“) einen Block schreiben könnt. Das sieht im einfachsten Fall wie folgt aus:

wp.blocks.registerBlockType(
	'rpssrb/random-posts',
	{
		title: 'Random Posts',
		edit: function () {
			return wp.element.createElement(
				wp.serverSideRender,
				{
					block: 'rpssrb/random-posts'
				}
			);
		}
	}
);

Wir verwenden die wp.blocks.registerBlockType Funktion im ES5-Stil und geben ansonsten nur einen title und eine edit Funktion an. In dieser Funktion erstellen wir dann ein React-Element mit der wp.serverSideRender Komponente.

Die edit Funktion wird verwendet, um den Inhalt des Blocks im Editor anzuzeigen (in unserem Fall also das Ergebnis des PHP-Callback-Funktion). Wir brauchen diese Funktion nicht einmal zwingen, würden dann aber nichts im Block-Editor sehen (der Block würde aber auch keinen Raum einnehmen, da er leer ist), im Frontend würden wir aber die Ausgabe sehen.

Laden der JavaScript-Datei

Dieser JavaScript-Code muss nun geladen werden, wenn der Block-Editor verwendet wird. Falls ihr schon eine Datei habt, die im Block-Editor geladen wird, könnt ihr den Code in diese Datei speichern. Ansonsten ladet ihr die Datei einfach wie jede andere, verwendet dabei aber einen anderen Hook:

function rpssrb_register_scripts() {
	wp_enqueue_script(
		'random-posts-server-side-render-block',
		plugin_dir_url( __FILE__ ) . 'index.js',
		[ 'wp-blocks', 'wp-server-side-render' ],
		filemtime( plugin_dir_path( __FILE__ ) . 'index.js' ),
		true
	);
}
add_action( 'enqueue_block_editor_assets', 'rpssrb_register_scripts' );

In den Abhängigkeiten fügen wir noch wp-blocks und wp-server-side-render, wobei diese eigentlich normalerweise ohnehin geladen werden, wenn der Block-Editor verwendet wird.

Bonus: auch einen Shortcode verwenden

Da wir schon eine Callback-Funktion geschrieben, die genau so auch bei einem Shortcode genutzt werden kann, können wir mit einer einzelnen weiteren Zeile diese auch als Shortcode nutzbar machen (z.B. mit dem Shortcode-Block):

add_shortcode( 'rpssrb_random_posts', 'rpssrb_render_callback' );

Einschränkung: die Verwendung von Attributen ist kompliziert!

Der Block, den wir gebaut haben, kann keine Attribute verwenden. Diese zum Bock hinzuzufügen wäre einfach, dafür dann aber Kontroll-Elemente für eine einfache Verwendung zu implementieren ist nicht so einfach. Es wäre zwar mit ES5 möglich, aber ich würde dies nicht empfehlen.

Konsequenz: Learn JavaScript, deeply.

Wie uns Matt beim WCUS 1015 als Hausaufgabe mitgegeben hat, würde auch ich sehr empfehlen, mit dem Lernen von modernem JavaScript anzufangen. Der schwierigste Teil ist am Anfang dabei die Kompilierung mit webpack. Falls ihr gar keine Idee habt, wie ihr starten sollt, dann würde ich der Antwort von Birgit auf den Tweet zustimmen: verwendet das create-block Paket.

Mit dem create-block Paket könnt ihr nicht nur einen funktionierenden Block innerhalb eines WordPress Plugins erstellen, es installiert euch auch all die JavaScript-Pakete, die ihr für die Kompilierung von modernem JavaScript benötigt. Dazu verwendet es das @wordpress/scripts Paket und da Haupt-Skript, das ihr hiervon verwenden würdet, ist das start Skript. Versucht es einfach mal selbst aus und lest ein wenig in der Dokumentation, wie man Attribute hinzufügt, um euren Block noch einfacher nutzbar zu machen.

Fazit: einen Block zu erstellen ist einfach, aber es reicht eventuell nicht aus

Während ihr also einen Block schreiben könnt, der nur eine PHP-Datei und eine JavaScript-Datei mit ES5-Syntax benötigt, würde ich es auch nicht unbedingt empfehlen. Sobald ihr euch einmal mit den Grundlagen der Block-Erstellung vertraut gemacht habt, könnt ihr die Nutzbarkeit erheblich verbessern. Es klingt sicher am Anfang etwas einschüchternd und ihr werdet sicher in alle möglichen Fehler laufen, aber es ist es wert.

Falls ihr den vollständigen Code zu diesem Beitrag sehen wollt, dann findet ihr ihn auf GitHub als Plugin.

Mein Blog ist jetzt ein Teenie!

Schon 13 Jahre schreibe ich hier auf diesem Blog für euch. In den letzten Jahren auch immer sehr regelmäßig. Damit ist mein Blog nun im Teenager-Alter angekommen. Ob er damit auch anfängt verhaltensauffällig zu werden, ist abzuwarten ?

Fleißig Bloggen in Deutsch und Englisch

Mindestens einmal alle 14 Tage habe ich auch in den vergangenen 12 Monaten einen Beitrag geschrieben und diesen mit #project26 bzw. #projekt26 auf Twitter mit euch geteilt. Leider musste ich feststellen, dass nur noch sehr wenige aktiv beim Projekt 26 mitmachen.

Ein neuer Besucherrekord!

Ich hatte zu Januar 2021 meine Statistiken von Google Analytics auf Matomo umgestellt. Daher sind die Zahlen vielleicht nicht ganz vergleichbar, aber ich denke, dass ich einen neuen Besucherrekord verkünden kann: am 25. Januar 2022 hatte ich 809 Seitenaufrufe. Interessanterweise entfallen davon 131 auf Archivseiten und der Rest verteilt sich auf viele Beiträge. Aber auch die beliebtesten Beiträge habe ich wieder ermittelt.

Top3 im letzten Jahr

  1. Direkte Downloads mit dem „download“ Attribute
  2. Formatierten Quellcode mit Syntaxhervorhebung in Word einfügen
  3. Arrays und andere komplexe Daten mit PHP in einer MySQL-Datenbank speichern

Der ehemalige Platz 3 ist um einen abgerutscht und ein altbekannter Beitrag hat es wieder in die Top3 geschafft. Aber auch an der Spitze gab es einen Platzwechsel zwischen Platz 1 und 2 im Vergleich zum Vorjahr, einen recht deutlichen sogar.

Ein neues Theme

Nein, keine Angst, ihr habt es nicht übersehen, noch ist es nicht da ? Aber ich habe mir fest vorgenommen das Theme zu ersetzen. Nicht optisch, aber technisch. Ich habe noch immer die Idee, das Waipoua Theme als FSE-Theme nachzubauen. Das wird eine spannende, aber sicher auch herausfordernde Aufgabe. Aber dafür habe ich jetzt ein wenig mehr Zeit.

WordCamp Europe 2022

Eine Sache, die mich nämlich im vergangenen Jahr sehr beschäftigt hat, war die Organisation des WordCamp Europe 2022, das endlich in Porto stattfinden konnte. Mit über 2300 Teilnehmenden aus 91 Ländern konnte ich die wunderschöne Stadt, das Land und vor allem seine freundlichen Menschen und das tolle Essen kennenlernen. Aber nun ist für mich erst einmal eine Auszeit von der Organisation von WordCamps angesagt und ich konzentriere mich auch andere Dinge. Zum Abschluss meines Beitrags gibt es daher als traditionelles Video dieses Mal eines passenden zum WordCamp Europe, nämlich das mit der Ankündigung zum Event im nächsten Jahr!

Click here to display content from YouTube.
Erfahre mehr in der Datenschutzerklärung von YouTube.

WordCamp Europe 2022 – Die Community trifft sich endlich in Porto

Es ist drei Jahre her. Drei Jahre, seitdem wir unser letztes WordCamp Europe mit Teilnehmenden vor Ort hatten, 2019 in Berlin. Damals war ich der Local Lead und bin anschließend dem nächsten Organisationsteam als einer von drei Global Leads beigetreten. Dann kam die Pandemie und hat die Welt verändert und WordCamp fanden nur noch online statt. Obwohl das WCEU dieses Jahr nicht das erste Event mit Teilnehmenden vor Ort war, so war es doch das größte und es fühlte sich – fast – wie immer an.

Meine erste Reise nach Portugal

Schon 2020 sollte ich zu einem Besuch der Venue nach Porto reisen, die wir für das WCEU 2020 verwenden wollten. Aber die Pandemie traf und alle, wir verlegten das WordCamp in die Online-Welt und stoppten alle lokalen Organisationstätigkeiten. Dann sollte ich eigentlich dieses Jahr im Februar erneut zu einem Besuch der Venue nach Porto reisen, dieses Mal mit dem neuen Organisationsteam, aber nur wenige Tage zuvor bekam ich das erste Mal Corona. Somit war es dann am Montag, dem 30. Mai endlich so weit und ich konnte Portugal das erste Mal bereisen.

Vorbereitung des Events

Am Dienstag hatte ich dann auch gleich die Gelegenheit mir die grandiose Venue selbst anzusehen, die Super Bock Arena – Pavilhão Rosa Mota. Die Crew war gerade dabei, die Expo-Area und den Bildschirm für Track 1 vorzubereiten:

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Ich hatte auch die Gelegenheit, ein paar Mitglieder des Organisationsteams persönlich kennenzulernen und hatte ein Abendessen mit einigen von ihnen. Da die meisten Dinge schon durch die verschiedenen Team vorbereitet waren, konnte ich sogar am Mittwochabend an der „Piraten-Party“ teilnehmen, die auch eine Fahrt auf dem Fluss beinhaltete:

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Auftakt mit dem Contributor Day

Wie viele andere WordCamp auch hatten wir einen Contrbutor Day am Donnerstag organisiert.

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Der Contributor Day fand vor der Bühne von Track 1 statt. Wir hatten etwa 800 Contributoren, viele von ihnen waren zum ersten Mal dabei. Das war ein neuer Rekord für WordCamps!

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Der erste Konferenztag

Freitag war der erste Tag mit Vorträgen und Workshops. Die Tische vom Contributor Day wurden durch Sitzplätze für die Vorträge ersetzt:

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Ich hatte die Ehre das WordCamp Europe 2022 mit unserem Local Lead Jose Freitas und den drei anderen Global Leads Lesley Molecke, Moncho (José Ramón Padrón García) und Taeke Reijenga zu eröffnen:

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Leider konnte ich mir am ersten Tag keine Talks anhören. Lediglich die erste Hälfte von Majas Vortrag konnte ich sehen, bevor ich wieder zu Organisationsarbeit eilen musste.

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Wie auch in Berlin 2019 hatten wir mehr als nur Vorträge und Workshops zu bieten. Wir haben den „Wellness Track“ weitergeführt, in dem es Yoga und Meditation im wunderschönen Park außerhalb der Venue gab. Weiterhin hatten wir auch wieder das WP Café, in dem Teilnehmende zu verschiedenen Themen diskutieren konnten und erstmals hatten wir auch ein Live-Streaming-Studio. Hier wurden verschiedene Personen des Events interviewt und diese dann in den Livestream von Track 1 in den Pausen zwischen den Vorträgen übertragen. Ich hatte hier auch einmal die Gelegenheit in einer Pause in einem Interview dabei zu sein:

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Der zweite Konferenztag

Samstag war schon wieder der letzte Tag des WordCamp Europe. Dieser Tag vergeht für Organizer in der Regel wie im Flug. Am Ende konnten wir etwa 2304 Teilnehmende in Porto begrüßen und mit vielen von ihnen kurz vor dem Mittagessen ein Familienfoto machen:

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Der letzte Talk des WordCamp Europe 2022 war eine Fragestunde mit Josepha and Matt:

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Nach dieser Session war es dann Zeit für die Verabschiedung. Wir hatten insgesamt 91 Organizer und 164 Volunteers, die alle dabei geholfen haben, dies alles möglich zu machen und ein großartiges Event mit Menschen vor Ort zu organisieren.

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Und wie immer haben wir auch dieses Mal am Ende bekannt gegeben, in welcher Stadt das nächste WordCamp Europe stattfinden wird und das neue Organisationsteam begrüßt …

WordCamp Europe 2022 Athen

Nächstes Jahr trifft sich die WordPress Community in Athen in Griechenland! Ein weiters Land, das ich bisher noch nicht besucht habe.

Hier klicken, um den Inhalt von twitter.com anzuzeigen

Zeit, meinen „Hut an den Nagel zu hängen“

Für mich war dieses Jahr das letzte als WordCamp Europe Organizer. Ich bin 2017 dem Team beigetreten und habe nur letztes Jahr für die zweite Online-Ausgabe eine Pause eingelegt. Der „Tradition“ folgend war ich Local Lead, dann Global Lead und werde dann vermutlich nächstes Jahr einen Vortrag halten, vielleicht ja zusammen mit den anderen Global Leads aus 2020 und 2022. So sehr ich es auch geliebt habe, diese vielen Events zu organisieren, ist es doch an der Zeit, Platz für neue Community-Mitglieder zu machen. Ich freue mich schon sehr darauf zu sehen, womit und das Team des WCEU 2023 überraschen wird!

Änderungen an PHP ini Werten machen, ohne dass diese bei einem Update verloren gehen

Wenn ihr schon einmal selbst einen Server aufgesetzt habt, dann wird euch sicher auch aufgefallen sein, dass viele Standardwerte für PHP-Variablen nicht mehr zeitgemäß für eine modere Website sind. Die upload_max_filesize ist beispielsweise auf nur 2 MB festgelegt. Ihr könnt also keine Datei, die größer als 2 MB groß ist, in die WordPress Mediathek hochladen. Diesen Wert wollt ihr also ziemlich wahrscheinlich ändern. Aber wie könnt ihr den Wert am besten ohne zu riskieren, dass eure Anpassung wieder zurückgesetzt werden, sobald ihr PHP oder den Server aktualisiert.

Ändern der globalen PHP ini Werte

Dieser Blog läuft auf einem Ubuntu 22.04 Server mit PHP-FPM und einem nginx Webserver. Wenn ich also Änderungen an einer PHP ini Variable machen möchte, dann muss ich die Änderungen in der Datei /etc/php/8.1/fpm/php.ini global vornehmen. Wie ihr im Dateipfad sehen könnt, müsste ich diese Änderungen dann in jeder php.ini Datei für jede PHP Version vornehmen. Ihr müsstet sie auch für jede „Server API“ vornehmen, wenn ihr also zusätzlich noch CGI/FastCGI für manche Seiten verwendet, dann müsstet ihr auch die Datei /etc/php/8.1/fpm/php.ini anpassen.

Der Vorteil dieser Änderung ist, dass sie für alle Seiten gilt, die auf diesem Server gehostet werden, ihr könnt hier also gute Standardwerte für jede Seite einstellen. Wenn der Paketmanager versucht, die Konfigurationsdateien zu aktualisieren, wird er Änderungen feststellen und euch fragen, ob ihr die neue Datei verwenden wollt, die alte Datei, oder ob ihr euch die Änderungen genauer ansehen und das Problem selbst lösen wollt. Abhängig davon, wie viele Änderungen ihr gemacht habt, kann das recht kompliziert werden.

Um globale Änderungen an PHP ini Werte vorzunehmen, habe ich daher eine neue Datei mit dem Namen /etc/php/conf.d/90-custom.ini erstellt, die per Symlink in den verschiedenen Konfigurationspfaden eingebunden ist:

$ tree /etc/php/   
/etc/php/
|-- 7.3
|   |-- cgi
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/7.3/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   `-- php.ini
|   |-- cli
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/7.3/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   `-- php.ini
|   |-- fpm
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/7.3/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   |-- php-fpm.conf
|   |   |-- php.ini
|   |   `-- ...
|-- ...
|-- 8.1
|   |-- cgi
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/8.1/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   `-- php.ini
|   |-- cli
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/8.1/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   `-- php.ini
|   |-- fpm
|   |   |-- conf.d
|   |   |   |-- 10-mysqlnd.ini -> /etc/php/8.1/mods-available/mysqlnd.ini
|   |   |   |-- ...
|   |   |   `-- 90-custom.ini -> /etc/php/conf.d/90-custom.ini
|   |   |-- php-fpm.conf
|   |   |-- php.ini
|   |   `-- ...
|-- ...
|-- conf.d
|   `-- 90-custom.ini

Da die neue Datei 90-custom.ini nicht vom Paketmanager hinzugefügt/überschrieben wird, sind sämtliche Anpassungen dazu sicher vor Upgrades.

Ändern von PHP ini Werten pro Seite

Manchmal möchte man bestimmte Seite nur für eine spezifische Seite ändern. Dann würde die Verwendung der globalen Datei nicht funktionieren. Falls ihr den Apache Webserver verwendet, dann könntet ihr eventuell die Werte über die Apache Konfigurationsdateien anpassen oder sogar die .htaccess Datei verwenden. In dieser Datei würde man dann eine Anweisung wie php_value upload_max_filesize 64M verwenden, um etwa das Upload-Limit zu erhöhen. Das funktioniert allerdings nur, wenn ihr PHP als Apachemodul einsetzt.

Wenn ihr Apache mit PHP-FPM oder einen anderen Webserver verwendet – so wie ich nginx einsetze – dann könnt ihr keine .htaccess Datei nutzen, um Änderungen an den PHP ini Werten vorzunehmen.

Verwendung der „user_ini“ Datei für Änderungen pro Seite

Glücklicherweise gibt es einen anderen Weg, um einen PHP ini Wert zu ändern. Dies kann erreicht weden, indem ihr die „user_ini“ Datei verwendet, die in der Regel im Hauptverzeichnis der Website gespeichert wird. Zuerst einmal müsst ihr herausfinden, sie diese Datei heißen muss. Dies könnt ihr mit der phpinfo() Funktion oder mit dem Befehl php -i tun und darin dann nach dem Wert von user_ini.filename suchen. Ihr erstellt dann eine Datei mit diesen Namen und macht darin die Anpassungen in der gleichen Art und Weise wie in einer php.ini Datei, wie z.B. upload_max_filesize = 64M um das Upload-Limit zu ändern.

Nachdem ihr die Änderungen gemacht habt und dann kontrolliert, ob sie funktionieren, dann wird es vermutlich nicht der Fall sein. Das heißt aber nicht, dass ihr etwas falsch gemacht habt. Es bedeutet vermutlich nur, dass die Datei noch nicht erneut vom PHP-Prozess eingelesen wurde, denn die Datei wird gecacht. Wie lange, könnt ihr über den Wert von user_ini.cache_ttl herausfinden. Dies ist die Zeit in Sekunden für das Caching der Datei. Bei einem Wert von 300 müsst ihr also bis zu 5 Minuten warten, bis die Änderungen für die Seite angenommen werden. Wenn die Änderungen sofort greifen sollen, dann müsst ihr den PHP-Prozess (wie etwa PHP-FPM) oder den Webserver (bei der Verwendung der PHP Apachemoduls) neu starten.

Fazit

Es gibt mehrere Wege Änderungen an PHP ini Werten zu machen. Wenn ihr also Anpassungen machen müsst, dann tut dies mit einer Methode, die nicht zu Problemen mit Updates führt (oder bei der eure Anpassungen bei einem Upgrade verloren gehen). Für meinen eigenen Server habe ich mich für den ersten Ansatz entschieden, der eine globale ini Datei verwendet. Die user_ini Datei hat aber den Vorteil, dass diese Seite sehr einfach zu einem neuen Server übertragen werden können, wenn ihr die Seite migriert. Sie ist also gerade dann sinnvoll, wenn das PHP-System sehr spezifische Werte benötigt.

Den Matomo Opt-out iframe reparieren, wenn sichere Server-Header verwendet werden

Letztes Jahr habe ich einen Beitrag darüber geschrieben, wie man eine Website mit sicheren Server-Headern besser schützen kann. Wenn man etwas an den Security-Headern ändert, dann besteht immer die Möglichkeit, dass Dinge nicht mehr funktionieren, da sie „weniger sichere“ Einstellungen benötigen. Eines dieser Beispiele ist der Matomo Opt-out iframe und in diesem Beitrag möchte ich euch zeigen, wie ihr das Problem lösen könnt.

Das Opt-out wird vom Browser blockiert

Wenn ihr Matomo zum Tracken einsetzt und Statistiken zu den Zugriffen auf eurer Seite erhebt, dann solltet ihr das immer in der datenschutzfreundlichsten Art und Weite tun. Ihr solltet hierzu das Tracking ohne Cookies durchführen und auch die Do-not-Track Unterstützung aktivieren. Aber selbst dann muss es möglich sein, dass jemand dem Tracking durch ein Opt-out widerspricht. Hierzu könnt ihr in den Privatsphäre-Einstellungen einen Opt-out iframe erstellen. Diesen fügt ihr dann auf eurer Datenschutzseite ein. Wenn ihr aber mit Security-Headern für Matomo macht, dann könnte es sein, dass ihr folgende Fehlermeldung im Browser seht:

Refused to display ‚https://matomo.example.com/‘ in a frame because it set ‚X-Frame-Options‘ to ’sameorigin‘.

Das passiert, wenn die Security-Header zu eng eingestellt sind und eine Einbindung der Seite auf einer anderen Domain verboten wurde.

Externe Domains erlauben

Eine Sache, die ihr nun tun könntet, wäre es, den Wert für X-Frame-Options so zu ändern, dass er weniger sicher ist. Aber das wollen wir natürlich nicht. Stattdessen könnt ihr die Einbindung des iframe auf bestimmten Seiten explizit erlauben. Dies macht ihr mit dem Content-Security-Policy Header:

Apache

Header set Content-Security-Policy "frame-ancestors 'self' https://example.com/ https://www.example.com/"

nginx

add_header Content-Security-Policy "frame-ancestors 'self' https://example.com/ https://www.example.com/"

Ihr habt vielleicht schon eine Content-Security-Policy in euren Server-Headern. I diesem Fall könnt ihr diese Optionen einfach anhängen. Wenn ihr nicht wisst, die man zwei Optionen kombiniert, dann kann euch vielleicht der Generator von Report UI dabei helfen.

Fazit

Die Absicherung einer Website ist sehr wichtig. Aber manchmal kann ein höherer Grad an Sicherheit auch anderen Dingen in die Quere kommen. Daher solltet ihr immer alle Funktionen testen, sobald ihr etwas an den Server-Headern verändert habt. Wenn plötzlich das Opt-out vom Tracking nicht mehr möglich ist, dann habt ihr zwar vielleicht ein Sicherheitsproblem gelöst, dafür aber ein Datenschutzproblem erzeugt.

Die Bearbeitung von Inhalten durch mehrere Personen erlauben

WordPress hat verschiedene Rollen und in der Regel ist es keine gute Idee allen die „Administrator“ Rolle zuzuweisen. Die nächstkleinere Rolle wäre „Redakteur“. Hiermit können alle Inhalte bearbeitet werden, eigene und die von anderen. Aber ich manchen Fällen ist auch das zu weitreichend.

Die Bearbeitung eigener Inhalte erlauben

Möchte man nur die Bearbeitung von eigenen Seiten und Beiträgen erlauben, dann sollte am besten die Rolle „Autor“ zugewiesen werden. Mit dieser Rolle können zwar die Inhalte andere angesehen werden, eine Bearbeitung ist aber nicht möglich. Das ist perfekt, wenn man Externen Zugriff auf „die eigene Seite“ geben möchte, beispielsweise auf das Profil einer Firma. Die Firma kann dann ihr eigenes Profil erstellen oder man erstellt es für die Firma und weist dann deren Profil als Autor der Seite zu.

Aber früher oder später kommt dann die Anforderung, dass mehr als eine Person aus dem Unternehmen die Seite bearbeiten können soll. Da aber WordPress immer einen User als Autor pro Seite erlaubt, ist das nicht einfach möglich, denn man möchte dem Unternehmen ja auch nicht vorschlagen, das Passwort zu teilen. Es muss also eine bessere Lösung her.

Das „Co-Authors Plus“ Plugin zur Rettung

Durch einen Tweet, in dem einige häufig verwendete Plugin genannt wurden, bin ich auf das Plugin Co-Authors Plus aufmerksam geworden, das es schon ziemlich lange gibt. Es erlaubt es einem mehr als einen Autor einer Seite oder einem Beitrag zuzuweisen. Der erste Autor wird hierbei in der Einzelansicht ausgegeben. Das Plugin stellt aber auch Funktionen bereit (die man dann im Child-Theme verwendet), mit denen die anderen ausgegeben werden können.

Diese verschiedenen Autoren können aber auch andere Rollen haben. Jeder Zugang, egal ob primärer/erster oder weiterer Autor, kann die Seite bearbeiten, solange dessen Rolle es ohnehin erlauben würden.

Da WordPress den Autoren eines Beitrags oder einer Seite normalerweise in einer einzelnen Spalte in der wp_posts Tabelle speicher, hat es mich sehr interessiert, wie es im Plugin gelöst wurde. Tatsächlich wird hierfür ein Custom-Post-Type verwendet, der die Zuordnung übernimmt.

Fazit

Während die normalen Rollen von WordPress in den meisten Fällen ausreichen, ist der Wunsch nach einer Bearbeitung durch mehrere Personen so ein Fall, in dem ich mir eine Lösung durch den Core gewünscht hätte. Mit diesem zusätzlichen Plugin (welches noch immer Updates erhält und sich auch gut in den Block-Editor und die „QuickEdit“ Funktion integriert), kann auch dies recht einfach erreicht werden.

Den „ERROR 1153“ beim Import großer Datenbanken lösen

Migrationen von WordPress-Seiten ist etwas, das ich wöchentlich mehrfach tue. Viele Migrationen sind zwischen lokalen Umgebungen oder Production und Staging. Nicht alle Quell- und Ziel-Systeme haben dabei unbedingt die gleichen Einstellungen. Manchmal muss man eine Seite mit einer großen Datenbank migrieren und stößt dabei eventuell auf diesen Fehler:

ERROR 1153 (08S01) at line 56: Got a packet bigger than 'max_allowed_packet' bytes

Das kann passieren, wenn ein einzelnes INSERT Statement beim Import zu groß ist. In diesem Beitrag möchte ich euch Tipps geben, wie ihr eine solche Datenbank importieren könnt.

Erstellen eines neuen Backups mit kleineren Imports

Wenn man eine Datenbank exportiert, dann verwendet man hierzu vermutlich das mysqldump Kommand, den wp db export Befehl oder ein Datenbank-Management-Tool wie Adminer oder phpMyAdmin. In den Standardeinstellungen werden hierbei INSERT Statements erstellt, die nicht nur eine Zeile in die Tabelle importieren, sondern mehrere auf einmal. Das ist oft auch eine gute Idee, da es den Import beschleunigt. Aber eben in genau diesen Fällen kommt es dann zu dem beschriebenen Fehler.

Um dies zu lösen, könnt ihr versuchen, einen Datenbank-Export zu erstellen, der immer nur ein Zeile in jedem INSERT Statement macht. Damit dauert der Import zwar etwas länger, er sollte aber erfolgreich durchlaufen.

Um einen solchen Export zu erstellen, könnt ihr die WP-CLI verwenden und ein paar mysqldump Argumente anhängen:

wp db export --extended-insert=FALSE --complete-insert=TRUE

Dies sollte eine solche Export-Datei erstellen. Jetzt solltet ihr diese hoffentlich importieren können

Erhöhen des Werts von „max_allowed_packet“

Wenn ihr noch immer das gleiche Problem habt, dann solltet ihr versuchen den Wert von „max_allowed_packet“ zu erhöhen. Der Standardwert kann vermutlich nur mit Administrationsrechten auf dem Server angepasst werden. Ihr könnt den Wert aber oft temporär ändern. Hierzu vebindet ihr euch zum MySQL-Server, erhöht den Wert und importiert dann die Datei. Ich verwende hierfür in meisten den wp db cli Befehl. Damit bin ich dann mit der richtigen Datenbank zu meiner WordPress-Installation verbunden. Anschließend führe ich dann die folgenden Befehle aus:

SET GLOBAL max_allowed_packet=1*1024*1024*1024;
SOURCE db_dump_filename.sql;

Der erste Befehl erhöht die Variable global auf 1 GB. Im Gegensatz zur MySQL-Konfigurationsdatei könnt iher hier als Wert nicht „1G“ im Statement verwenden, ihr müsst stattdessen den Wert in Bytes angeben. Ihr könnt aber eine Berechnung verwenden, daher habe ich diese Notation verwendet, mit der man besser erkennen kann, auf welchen Byte-Wert man die Variable setzt.

Fazit

Das Importieren von großen Datenbanken kann ein echtes Problem darstellen. Hoffentlich könnt ihr das Problem mit diesen Tipps selbst lösen. Falls nicht, fragt am besten jemanden mit Administrationsrechten, denn sie haben vermutlich entsprechenden Tools, um euch beim Import zu helfen. Und falls ihr selbst Daten in die Datenbank schreibt, dann achtet darauf, dass Zeilen nicht zu lang werden.