Blöcke für bestimme Post-Types deaktivieren

In dem Projekt, in dem ich WordPress als Backend für eine native App einsetze, habe ich zwei Custom-Post-Type implementiert. Für beide soll nur eine eingeschränkte Auswahl an Blöcken verfügbar sein. Das hat einfach damit zu tun, dass nicht alle Arten von Inhalten in einer nativen App angezeigt/verwendet werden können.

Erlaubt Blöcke filtern

Wir können den Filter allowed_block_types nutzen, um die verwendbaren Blöcke einzuschränken oder um alle Blöcke zu erlauben/verbieten. Wenn ihr also für einen spezifischen Post-Type erlauben möchtet, könnt ihr den Filter wie folgt verwenden:

function cpt_allowed_blocks( $allowed_block_types, $post ) {
	if ( 'my_post_type' === $post->post_type ) {
		return array(
			'core/paragraph',
			'core/list',
			'core/quote',
			'core/code',
			'core/table',
			'core/image',
			'core/video',
			'core/audio',
		);
	}

	return $allowed_block_types;

}
add_action( 'allowed_block_types', 'cpt_allowed_blocks', 10, 2 );

In der Callback-Funktion überprüfen wir zuerst, ob gerade unser Custom-Post-Type bearbeitet wird. Wenn dem so ist, dann liefern wir ein Array mit genau den Blöcken zurück, die verwendet werden dürfen. Ansonsten liefern wir Originalwert unverändert zurück.

Der Originalwert kann entweder ein Array oder ein boolscher Wert sein. Wenn hier true zurück geliefert wird, sind alle Blöcke erlaubt, bei false sind keine Blöcke erlaubt.

Bonus: Ein Block-Template für den Post-Type setzen

Wenn ihr die Auswahl der Blöcke einschränkt, dann möchtet ihr damit vermutlich ein bestimmtes Layout in eurem Post-Type erreichen. Ihr könnt das vereinfachen, indem ich ein paar Blöcke standardmäßig hinzufügt, wenn ein neuer Eintrag im Post-Type erstellt wird. Das Template wird in der Funktion zur Registrierung eines Post-Types angegeben:

function cpt_register_post_type() {
	register_post_type(
		'my_post_type',
		array(
			'label'                 => __( 'My Post Type', 'text_domain' ),
			'supports'              => array( 'title', 'editor' ),
			'public'                => true,
			'show_ui'               => true,
			// ... other settings
			'has_archive'           => true,
			'show_in_rest'          => true,
			'template'              => array(
				array(
					'core/paragraph',
					array(
						'placeholder' => __( 'Lorem ...', 'text_domain' ),
					),
				),
				array(
					'core/image',
				),
			),
		)
	);
}
add_action( 'init', 'cpt_register_post_type' );

Mit dem template Parameter könnt ihr mehrere Blöcke definieren, die bei jedem neuen Eintrag eingefügt werden. Jeder Block wird in einem eigenen Array definiert. Für viele Blöcke können darin zusätzlich Eigenschaften gesetzt werden. In diesem Beispiel wird für den Absatz-Block ein Platzhaltertext (nicht der Inhalt) gesetzt. Der zweite Block ist ein Bild-Block, ohne Eigenschaften. Er sieht genau so aus, wie direkt nach dem Hinzufügen der Block, er befindet sich also im „Bearbeitungsmodus“:

Fazit

Wenn ihr mit Custom-Post-Types arbeitet, dann könnt ihr recht einfach einschränken, welche Blöcke verwendet werden dürfen. In Kombination mit einem Block-Template für neue Einträge könnt ihr die Erstellung von einheitlichen Einträgen erheblich vereinfachen.

Ändern der Standardwerte für Absender-Name und E-Mail-Adresse

In WordPress werden an vielen verschiedenen Orten E-Mails versendet. Meistens werden sie von Plugins versendet. In den meisten davon kann man den Absender-Name („From Name“) und die E-Mail-Adresse („From Mail“) selbst festlegen. Einige E-Mails werden aber auch vom WordPress Core verwendet. Eine solche E-Mail wäre etwa die zum Zurücksetzen des Passworts. Aber auch andere Plugins schicken Mails an Nutzer oder Administratoren.

Ändern der Absender-Standardwerte

Wenn eine E-Mail versendet wird und dabei der Name und die Adresse nicht explizit gesetzt werden, dann wird immer „WordPress“ für den Namen und „wordpress@deine-domain.tld“ verwendet. Leider gibt es im Dashboard keine Einstellung um diese Werte zu ändern.

Überschreiben der Werte mit einem Filter

Es gibt zwei Filter, mit denen die Standardwerte für den Absendernamen und die E-Mail-Adresse geändert werden können. Fügt einfach folgendes in ein (MU-)Plugin ein und aktiviert es:

// Change the "from name"
function wp_email_set_from_name( $from_name ) {
	return 'Dein Name';
}
add_filter( 'wp_mail_from_name', 'wp_email_set_from_name' );
// Change the "from email"
function wp_email_set_from_email( $from_email ) {
	return 'dein-name@deine-domain.tld';
}
add_filter( 'wp_mail_from', 'wp_email_set_from_email' );

Das überschreibt die beiden Werte. Aber es überschreibt sie für alle E-Mails. Vermutlich nicht das, was ihr wollt. Leider werden die beiden Filter erst dann aufgerufen, wenn die Standardwerte schon gesetzt wurden. Man kann also nicht einfach prüfen, ob explizite Werte zum Senden der E-Mail verwendet wurden. Daher prüfen wir stattdessen, ob die Werte noch immer der Standardwerten entsprechen und ändern sie nur in diesem Fall:

// Change the default "from name"
function wp_email_set_from_name( $from_name ) {
	if ( 'WordPress' === $from_name ) {
		return 'Dein Name';
	}

	return $from_name;
}
add_filter( 'wp_mail_from_name', 'wp_email_set_from_name' );
// Change the default "from email"
function wp_email_set_from_email( $from_email ) {
	if ( false !== strpos( $from_email, 'wordpress@' ) ) {
		return 'dein-name@deine-domain.tld';
	}

	return $from_email;
}
add_filter( 'wp_mail_from', 'wp_email_set_from_email' );

Dies sollte also nur dann neue Werte setzen, wenn sie noch immer den Standardwerten entsprechen. Ihr könnt aber weiterhin einen anderen Namen oder eine andere E-Mail-Adressen, zum Beispiel in einem Formular-Plugin.

Fazit

Das Überschreiben der Standardwerte für den Absender-Namen und die E-Mail-Adresse kann mit Filtern umgesetzt werden. Ich solltet dabei aber aufpassen, dass ihr es nur in den Fällen tut, in denen die Werte nicht zuvor auf einen anderen Wert schon gesetzt wurden.

Benutzer in WordPress mit der REST API erstellen

In einem aktuellen Projekt verwenden wir WordPress als Backend für eine native Mobile App. Die App selbst wird von einer anderen Agentur entwickelt. Ich bin für den WordPress-Teil zuständig. Der Inhalt der App wird über mehrere Custom-Post-Types und Custom-Taxonomies sowie einiger spezieller Blocks erstellt, für eine komfortable Verwaltung. Die App kann ohne Registrierung verwendet werden. Für erweiterte Funktionen, wie etwas die Synchronisation der Ergebnisse mit mehreren Geräten oder der Vergleich mit anderen, wird eine Registrierung benötigt.

Der Server für den Empfang von Requests vorbereiten

Um Anfragen von der App an den Server machen zu können, müssen einige Response-Header geschickt werden, damit diese akzeptiert werden. Die gilt besonders für die iOS-Version der App. Ich habe zuerst versucht die Header über die Server-Konfiguration umzusetzen, nur um dann festzustellen, dass einige davon zusätzlich noch von WordPress gesetzt wurden. Das Resultat waren dann doppelte Header mit gleichen Namen aber unterschiedlichen Werten. Nach ein wenig Debugging konnte ich ein paar Hooks finden, die ich verwenden konnte:

function app_rest_headers_allowed_cors_headers( $allow_headers ) {
	return array(
		'Origin',
		'Content-Type',
		'X-Auth-Token',
		'Accept',
		'Authorization',
		'X-Request-With',
		'Access-Control-Request-Method',
		'Access-Control-Request-Headers',
	);
}
add_filter( 'rest_allowed_cors_headers', 'app_rest_headers_allowed_cors_headers' );

Bei einem anderen Hook gab es leider keinen Filter, daher musste ich hier direkt die header() Funktion aufrufen:

function app_rest_headers_pre_serve_request( $value ) {
	header( 'Access-Control-Allow-Origin: *', true );

	return $value;
}
add_filter( 'rest_pre_serve_request', 'app_rest_headers_pre_serve_request', 11 );

Nach dieser kleinen Vorbereitung können wir versuchen über den Users-Endpoint zu erstellen. Das wird aber nicht funktionieren. Wieso nicht? Nun, ganz einfach aus dem Grund, dass man nicht einfach über die WordPress REST API einen Benutzer erstellen kann, ohne sich vorher zu autorisieren. Wenn das möglich wäre könnte ja sonst jemand auf einer beliebigen WordPress-Seite Benutzer erstellen.

Einen Nutzer mit der REST API authentifizieren

Wie müssen uns also an der REST API authentifizieren, aber wie machen wir das? Es gibt hier verschiedene Möglichkeiten. Eine neue Möglichkeit, die als erstes im Core langen wird und kurz vor der Aufnahme steht sind die Application Password.

Es gibt schon einen Vorschlag für die Integration des Feature-Projects in WordPress 5.6. Wenn ihr aber jetzt schon diese Möglichkeit testen wollt, könnt ihr einfach das Plugin Application Passwords installieren.

Einen Benutzer für die Benutzerverwaltung erstellen

Normalerweise können nur Administratoren die Benutzer verwalten. Aber ihr möchtet vermutlich nicht unbedingt einer App sämtliche Berechtigungen geben, die ein Admin auf einer WordPress-Seite hat. Daher macht es Sinn, einen speziellen Benutzer zu erstellen, der nur die Berechtigungen hat, die für die Benutzerverwaltung benötigt werden. Ihr könnt das mit einem Plugin wie etwa Members machen, oder aber ihr erstellt die Rolle und den Benutzer mit PHP Code oder ihr verwendet die WP-CLI:

wp role create app App --clone=subscriber
wp cap add app create_users
wp user create app-rest-user app-rest-user@example.com --role=app

Der Benutzer braucht mindestens die Berechtigung create_users, um Benutzer anlegen zu können. Wenn ihr zusätzlich die read Berechtigung setzt, könnt ihr euch auch mit diesem Benutzer anmelden und das Application Password setzen (daher wird in dem Beispiel oben auch die Rolle subscriber geklont). Alternativ könnt ihr als Admin das Application Password für einen Benutzer setzen.

Das Application Passwort für den neuen Benutzer erstellen

Nachdem ihr den Nutzer erstellt habt, könnr ihr euch mit diesem einloggen und auf die Profileinstellungen gehen (oder eben das Profil des Nutzers mit dem Admin bearbeiten). Hier findet ihr ein neues Feld, in dem ihr Application Passwords erstellen könnt. Wählt hier einen Namen aus und klickt auf „Add new“ (zum Zeitpunkt der Veröffentlichung des Beitrags gab es noch keine Übersetzung für das Plugin bzw. die Core-Funktionalität):

Daher öffnet sich ein Modal mit dem generierten Application Password. Ihr müsst dieses Passwort hier kopieren, da es nach dem Schließen des Modal nicht mehr als Klartext angezeigt werden kann.

Damit ist jetzt alles vorbereitet um einen Benutzer über einen Request an die REST API zu erstellen.

Deinen ersten Benutzer erstellen

Je nachdem welches System ihr verwendet, um Requests an die WordPress REST API zu schicken, sieht der Workflow hier anders aus. Daher zeige ich es heir am Beispiel eines curl Requests über die Kommandozeile:

curl --user "app-rest-user:younfj4FX6nnDGuv9EwRkDrK" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"username":"jane","password":"secret","email":"jane@example.com"}' \
     http://example.com/wp-json/wp/v2/users

Wenn ihr zuvor alles richtig eingerichtet habt, dann solltet ihr eine JSON-Response mit den Daten zum neuen Benutzer erhalten.

Fazit

Die Verwaltung von Benutzern ist nach etwas Vorbereitung und (aktuell) noch externer Plugins möglich. Es wird durch das neue Applications Passwords Core-Feature aber um einiges leichter. In gleicher Weise könnt ihr dann natürlich auch andere REST API Requests machen, die einen autorisierten Benutzer erfordern. Für eine erhöhte Sicherheit solltet ihr hierzu aber immer einen speziellen Benutzer erstellen (es sei denn, ihr wollte Inhalte für einen bestehenden Benutzer erstellen und zuweisen).

Disclaimer

Seit fast genau vier Jahren versuche ich all meine Beiträge geschlechtsneutral zu schreiben. Das ist mir bisher auch recht gut gelungen (auch wenn ich sicher doch mal aus Versehen etwas übersehen habe).

Im Englischen ist es bei Personenbezeichnungen auch recht einfach, da es hier nur selten eine männliche und weibliche Form gibt. In der deutschen Übersetzung verwende ich einfach statt einer Personenbezeichnung einen andren Begriff. Dadurch ist der Satz meisten genauso einfach lesbar, manchmal sogar besser. Aber beim heutigen Beitrag habe ich mich aber für „Benutzer“ und „Benutzerverwaltung“ entschieden. Ich hätte zwar auch „Zugang“ und „Zugangsverwaltung“ verwenden können, aber damit hätte ich wohl einige von euch sehr verwirrt. Der Terminus im Core ist aktuell eben noch „Benutzer“ und daher musste ich heute mal diese Ausnahme machen. Ich hoffe meine treuen Leserinnen verzeihen es mir 🙂

Einzelne Block-Styles deaktivieren

In meinem letzten Beitrag habe ich euch gezeigt, wie man Seiten-Templates in einem Child-Theme deaktivieren kann. Diese Woche geht es um die Deaktivierung anderer Dinge, die über das Parent-Theme oder ein Plugin kommen können. Es geht um Block-Styles. Damit können verschiedene Stile für einen Block definiert werden, die entweder im Core, im Theme, aber manchmal auch in Plugins zu finden sind.

Deaktivierung über einen „Dequeue-Hook“

Block-Stile werden innerhalb von JavaScript-Dateien definiert. Diese Dateien müssen in die Seite eingebunden werden. Hier können wir ansetzen und die Datei wieder entfernen. Das könnte wie folgt aussehen:

function dibs_dequeue_assets() {
	wp_dequeue_script( 'editorskit-editor' );
}
add_action( 'enqueue_block_editor_assets', 'dibs_dequeue_assets', 11 );

In diesem Beispiel wird die Hauptdatei mit allen JavaScript-Funktionen für den Editor entfernt. Sie enthält also nicht nur Block-Styles, sondern den gesamten JavaScript-Code für das Plugin. Das ist also nur dann eine gute Option, wenn das Plugin mehrere Dateien verwendet und eine davon nur die Registrierung der Block-Styles übernimmt.

Deaktivierung ausgewählter Block-Stile

Wenn ihr also nur bestimmte Styles in backend deaktivieren möchtet, dann müsst ihr ein wenig eigenen JavaScript-Code verwenden. Hierzu laden wir zuerst die Datei mit diesem Code:

function dibs_enqueue_assets() {
	wp_enqueue_script(
		'dibs-remove-block-styles',
		plugins_url( 'remove-block-styles.js', __FILE__ ),
		array( 'wp-blocks', 'wp-dom-ready', 'wp-edit-post' ),
		filemtime( plugin_dir_path( __FILE__ ) . 'remove-block-styles.js' ),
		true
	);
}
add_action( 'enqueue_block_editor_assets', 'dibs_enqueue_assets', 11 );

Die hinzugefügt Datei lädt die Abhängigkeiten zu wp-blocks, wp-dom-ready und wp-edit-post um korrekt zu funktionieren. Nachdem die Datei hinzugefügt wurde, können wir die Block-Styles entfernen:

wp.domReady( function() {
	wp.blocks.unregisterBlockStyle(
		'core/image',
		'editorskit-diagonal'
	);
	wp.blocks.unregisterBlockStyle(
		'core/image',
		'editorskit-inverted-diagonal'
	);
	wp.blocks.unregisterBlockStyle(
		'core/cover',
		'editorskit-diagonal'
	);
	wp.blocks.unregisterBlockStyle(
		'core/cover',
		'editorskit-inverted-diagonal'
	);
} );

Nach dem Laden der Seite wird wp.blocks.unregisterBlockStyle() verwendet, deren erster Parameter den „Slug“ des Blocks angibt, für den wir einen Stil entfernen wollen. Der zweite Parameter ist der „Slug“ des Block-Style. In diesem Beispiel würden wir also zwei Block-Stile von den Blöcken core/image und core/cover entfernen.

Dieser Ansatz ist vermutlich derjenige, den ihr verwenden möchtet, da das Entfernen einer gesamten JavaScript-Datei wie im ersten Beispiel nur selten funktionieren wird. Aber auch, wenn ihr nur einzelne Stile entfernen möchtet, ist dies wohl die bessere Lösung.

Fazit

Falls ein Plugin neue Block-Styles mitbringt, die ihr nicht verwenden möchtet, dann reicht eine kleine JavaScript-Datei aus, um diese zu entfernen. Es ist aber zu beachten, dass dies nur die Auswahl dieser Stile im Backend entfernt. Bereits zuvor hinzugefügt Blöcke in Beiträgen und Seiten haben weiterhin den Stil in Form von CSS-Klassen. Sofern die CSS-Datei auch weiterhin die Anweisungen enthält, wird der Block-Stil weiterhin im Frontend angezeigt. Falls ihr das also ebenfalls deaktivieren wollt, müsst ihr entweder die CSS-Klassen von den alten Blöcken entfernen oder aber die CSS-Eigenschaften im (Child-)Theme entfernen oder überschreiben.

Seiten-Template im Child-Theme deaktivieren oder überschreiben

Vorletzte Woche hat ein Kollege an einem neuen Projekt gearbeitet, bei dem ein Seiten-Template im Parent-Theme defekt war. Es gab ein besseres und sehr ähnliches Template, das stattdessen verwendet werden konnte. Aber es besteht weiterhin die Gefahr, dass jemand das defekte Seiten-Template beim Erstellen einer Seite auswählen würde. Also habe ich mich mal auf die Suche nach einer Lösung begeben, mit der man ein Seiten-Template aus der Auswahl entfernen kann.

Deaktivieren eines Seiten-Templates

In einem Child-Theme kann man ja sehr einfach ein neues Seiten-Template hinzufügen oder ein bestehendes überschreiben, indem man es aus dem Parent-Theme kopiert. Aber wie kann man ein bestehendes Template aus dem Parent-Theme aus der Auswahl entfernen. Glücklicherweise gibt es fast immer einen passenden Hook. Wenn ihr also ein Seiten-Template deaktivieren wollt, dann verwendet diesen Filter:

function child_theme_disable_page_template( $post_templates, $theme, $post, $post_type ) {
	unset( $post_templates['page-templates/tpl-hero-light.php'] );

	return $post_templates;
}
add_filter( 'theme_templates', 'child_theme_disable_page_template', 10, 4 );

Der Filter theme_templates bekommt ein Array aller Templates übergeben mit den relativen Pfaden als Schlüssel und den (übersetzten) Namen als Werten. Es könnte in etwa wie folgt aussehen:

$post_templates = array(
	'page-templates/tpl-fullscreen-light.php'  => 'Fullscreen, light header',
	'page-templates/tpl-fullscreen.php'        => 'Fullscreen',
	'page-templates/tpl-fullwidth-notitle.php' => 'Full Width, no page title',
	'page-templates/tpl-fullwidth.php'         => 'Full Width',
	'page-templates/tpl-hero-light.php'        => 'Hero, light header',
	'page-templates/tpl-hero.php'              => 'Hero',
);

Das Array enthält sowohl die Seiten-Templates aus dem Parent-Theme, als auch die aus dem Child-Theme. Falls beide ein Template mit dem gleichen Pfad haben, ist dieses nur einmal im Array enthalten.

Ersetzen eines Seiten-Templates

Jetzt wisst ihr also, wie ihr verhindern könnt, dass jemand ein Seiten-Template verwendet, das ihr nicht haben möchtet. Aber was ist mir den Seiten, bei denen das falsche Template bereits eingestellt ist? Nun, eine Möglichkeit wäre es, in der Datenbank das „postmeta“ mit dem Schlüssel _wp_page_template mit dem neuen Wert zu ersetzen. Aber vielleicht möchtet ihr den aktuellen Wert in der Datenbank intakt lassen. Dann könnt ihr einen von mehreren Filtern verwenden, um den Pfad zum Template zu überschreiben. Dies ist einer der Filter, den ihr nutzen könnt:

function child_theme_overwrite_page_template( $template ) {
	if ( get_parent_theme_file_path( '/page-templates/tpl-hero-light.php' ) === $template ) {
		return get_theme_file_path( '/page-templates/tpl-hero.php' );
	}

	return $template;
}

add_filter( 'template_include', 'child_theme_overwrite_page_template' );

In diesem Beispiel würde also jede Seite, die das Template page-templates/tpl-hero-light.php aus dem Parent-Theme verwenden würde, stattdessen das Template page-templates/tpl-hero.php aus dem Child-Theme verwenden.

Fazit

Während es sehr einfach ist ein Seiten-Template in einem Child-Theme hinzuzufügen oder zu überschreiben ist es nicht so einfach eines zu deaktivieren. Aber da WordPress normalerweise für jede gewünschte Anpassung einen passenden Filter anbietet, ist es auch nicht wirklich schwer.