Wenn du individuelle Plugins außerhalb von WordPress.org anbietest, heißt das auch, Dinge wie Plugin-Informationen manuell zu handhaben. Deshalb suchte ich hier eine etwas automatisiertere Lösung für meine Änderungsprotokolle, sodass ich sie nicht mehr mit dem Plugin selbst mitliefern muss, sondern sie von woanders automatisch laden kann.

Dieser Ansatz könnte noch erweitert werde, um alle möglichen Plugin-Informationen zu laden, wenn der „Details anzeigen“-Link auf der Seite der Plugin-Liste im Backend aufgerufen wird. In diesem Beitrag fokussiere ich mich jedoch einzig auf das Änderungsprotokolle.

Voraussetzung: Der Update-Server

Bei seinem vorherigen Stand wurden bereits einige Plugin-Informationen zur Verfügung gestellt, wie etwa Version, Autor, unterstützte WordPress- und PHP-Versionen etc. Inhaltsweise war jedoch alles direkt Teil des Plugins. Das hiße ebenfalls, dass beispielsweise Version 1.1.0 des Plugins immer nur den Inhalt von genau dieser Version anzeigte. Wann immer eine neue Version verfügbar war, zeigten die Plugin-Informationen immer noch nur die Informationen bis Version 1.1.0 an. Dabei ist es insbesondere für das Änderungsprotokoll wichtig, immer möglichst aktuell zu sein, sodass man vor der Aktualisierung die Änderungen nachvollziehen kann.

Für diese allgemeinen Informationen implementierte ich den WP Update Server von Yahnis Elsts mit einem Fork, der einige spezifische Änderungen für meine Umgebung beinhaltet.

Damit hatte ich praktisch bereits eine Methode, um Daten aus dem Update-Server in meinem Plugin zu bekommen. Danach hatte ich die Daten des Änderungsprotokolls zu diesem Datensatz manuell im Plugin selbst hinzugefügt.

Single source of truth

Das das heißt, dass ich, neben der Tatsache, dass ältere Versionen des Plugins nicht das aktuellste Änderungsprotokoll beinhalten, selbigen an mehrfacher Stelle pflegen muss, nämlich ebenfalls in der Dokumentation auf meiner Website, wollte ich das ändern.

Meine ursprüngliche Idee war eine textbasierte Version mit zusätzlichen Textdateien, die irgendwo im Update-Server liegen und dann zu den Plugin-Informationen des Plugins gesendet werden. Das würde zwar die Lösung für veraltete Änderungsprotokolle in älteren Plugin-Versionen lösen, nicht aber das Problem, dass ich sie auch auf der Website pflegen müsste.

Deshalb habe ich den Ansatz verändert, sodass der eigentliche Inhalt meiner Website vom Update-Server geladen und zum Plugin zurück gesendet werden, wo sie dann im Tab der Plugin-Informationen angezeigt werden.

Serverseitige Implementierung

Der größte Teil war nun die serverseitige Implementierung. Sie muss die Seite – in der richtigen Sprache – herunterladen und das Änderungsprotokoll davon extrahieren. Da die Plugin-Informationen zumindest eine spezifische Anzahl an HTML-Tags erlaubt, musste ich glücklicherweise nicht viel am extrahierten HTML ändern und die Darstellung passt direkt.

Um nur das Änderungsprotokoll zu extrahieren, verwendete ich einen Gruppenblock, um alle meine Einträge in einem Bereich zu gruppieren (da die Seite auch weitere Dokumentation enthält). Dann habe ich einen Anker zu dieser Gruppe hinzugefügt, sodass ich nur diese Gruppe später einfacher über PHPs DOMDocument auswählen konnte.

Da ich die Inhalte sowohl auf Deutsch als auch Englisch veröffentliche, habe ich weiterhin eine Prüfung für die deutsche Sprache hinzugefügt (ebenso die Varianten für schweizerische und österreichische WordPress-Instanzen). Die Sprache der anfragenden Website wird bereits übermittelt. Dann kann ich entweder die deutsche Version für diese spezifischen oder die englische Version für alle anderen Sprachen verwenden.

Der gesamte Code sieht dann so aus:

/**
 * Get the changelog.
 * 
 * @param	array	$meta Current metadata
 * @param	string	$locale The specified locale
 * @return	array Updated metadata
 */
private function get_changelog( array $meta, string $locale ): array {
	$urls = [
		'impressum' => [
			'default' => 'https://impressum.plus/en/documentation/',
			'german' => 'https://impressum.plus/dokumentation/',
		],
		'form-block-pro' => [
			'default' => 'https://formblock.pro/en/documentation/',
			'german' => 'https://formblock.pro/dokumentation/',
		],
	];

	if ( empty( $urls[ $meta['slug'] ] ) ) {
		$meta['sections'] = [
			'changelog' => '',
		];

		return $meta;
	}

	switch ( $locale ) {
		case 'de_AT':
		case 'de_DE':
		case 'de_DE_formal':
		case 'de_CH':
		case 'de_CH_formal':
			$url = $urls[ $meta['slug'] ]['german'];
			break;
		default:
			$url = $urls[ $meta['slug'] ]['default'];
			break;
	}

	$html = $this->get_single_api_data( $url );
	$use_internal_errors = \libxml_use_internal_errors( true );
	$changelog = '';
	$dom = new DOMDocument();
	$dom->loadHTML( $html, \LIBXML_HTML_NOIMPLIED | \LIBXML_HTML_NODEFDTD );
	$group = $dom->getElementById( 'changelog-group' );

	if ( $group ) {
		/** @var \DOMNode $childNode */
		foreach ( $group->firstElementChild->childNodes as $childNode ) {
			if ( \method_exists( $childNode, 'removeAttribute' ) ) {
				$childNode->removeAttribute( 'id' );
			}

			$changelog .= $dom->saveHTML( $childNode );
		}
	}

	\libxml_use_internal_errors( $use_internal_errors );

	$meta['sections'] = [
		'changelog' => \trim( $changelog ),
	];

	return $meta;
}
Code-Sprache: PHP (php)

Ich habe eine Liste an URLs, aufgeteilt nach Plugin, das eine Anfrage an den Update-Server stellt, und diese zusätzlich nach Sprachen geteilt. Dann findet die oben erwähnte Prüfung der Sprache statt und wählt die richtige URL aus, deren Inhalt dann über eine andere Methode get_single_api_data heruntergeladen wird, die ich ebenfalls zu meinem individuellen Update-Server hinzugefügt habe, um entfernte Daten via cURL herunterladen zu können.

Danach wird das Änderungsprotokoll in DOMDocument über seine ID extrahiert. Da ich den Gruppen-<div> nicht auch ausgeben möchte, iteriere ich über alle Kind-Nodes dieser Gruppe und speichere nur diese, bevor ich sie zurückgebe (tatsächlich verwende ich die Kindelemente des ersten Kindelements der Gruppe, da in meiner Umgebung alle Gruppen noch den inneren Container von vor WordPress 5.8 beinhalten).

Außerdem entfernt es alle id-Attribute der Kindelemente. Nur um sicherzugehen, dass es hier später keine Interferenzen gibt.

Diese gesamte Methode wird innerhalb der filterMetadata-Methode meines individuellen Update-Servers aufgerufen und dann mit den anderen Metadaten zum Plugin geschickt.

Das Plugin anpassen

Da ich bereits das Änderungsprotokoll angezeigt hatte, hatte ich bereits eine Methode, die den Filter plugins_api_result verwendet, um die Metadaten dementsprechend richtig anzuzeigen, wenn man auf den „Details anzeigen“-Link in der Plugin-Liste klickt.

Statt demnach wie bisher ein statisches Änderungsprotokoll innerhalb des Plugins anzuzeigen, führe ich nun eine Anfrage an den Update-Server aus, um die Metadaten zu erhalten und diese auszugeben. Du erhältst dabei einen JSON-Code vom Update-Server, bei dem du sicherstellen musst, zuerst json_decode zu verwenden, um ein assoziatives Array zu erhalten, und dieses Array dann zu einem Objekt zu casten, um innerhalb des Objekts dann Strings oder Array zu haben. Denn dies ist das erwartete Format von WordPress an dieser Stelle.

Der gesamte Code kann dann so aussehen:

/**
 * Get some plugin information.
 * 
 * @param	object	$res The plugin information resource
 * @param	string	$action The current action
 * @param	object	$args Additional plugin arguments
 * @return	object The updated plugin information resource 
 */
function my_get_plugin_information( object $res, string $action, array $args ): object {
	if ( $action !== 'plugin_information' ) {
		return $res;
	}
	
	if ( empty( $args->slug ) || $args->slug !== IMPRESSUM_BASE ) {
		return $res;
	}
	
	$request = \wp_remote_post( $url, $metadata_args );
	
	if ( \is_wp_error( $request ) ) {
		return $res;
	}
	
	$response = \wp_remote_retrieve_body( $request );
	$update_server_res = (object) \json_decode( $response, true );
	
	return $update_server_res;
}

\add_filter( 'plugins_api_result', 'my_get_plugin_information', 10, 3 );
Code-Sprache: PHP (php)

Du musst mindestens die $url und das $metadata_args-Argument in der Anfrage in Zeile 18 anpassen.

Fazit

Jetzt wird immer, wenn man die Plugin-Informationen aufruft, um das Änderungsprotokoll anzuzeigen, der Update-Server für neue Metadaten angefragt, das dann wiederum die Dokumentationsseite anfragt, um das aktuelle Änderungsprotokoll zu erhalten. Alles, was ich noch tun muss, ist, selbiges innerhalb der Dokumentation zu aktualisieren, damit es überall aktuell ist.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert