Serie: Effizienteres Bloggen mit WordPress dank XML-RPC
- Teil 1: Vorstellung einer neuen Serie
- Teil 2: Das Grundgerüst des XML-RPC-Scripts
- Teil 3: Blogpost-Metadaten auslesen
- Teil 4: Bilder hochladen und problematischen Code ersetzen
- Teil 5: Blogpost absenden und ein notwendiger Hack
- Teil 6: Skript Multi-Blog fähig und konfigurierbar machen
Ok, was haben wir bisher? Eine index.php, die wie folgt aussieht:
<?php
require_once 'inc/EnnoAutoPost.php';
$htmlString = $_SERVER['KMVAR_temp'];
$obj = new EnnoAutoPost($htmlString);
Außerdem die eingebundene Klasse EnnoAutoPost
:
<?php
require_once 'IXR_Library.php';
class EnnoAutoPost
{
public function __construct($htmlString)
{
// do something
}
}
Und was haben wir vor? Wir wollen die Metadaten auslesen und entsprechend speichern. Welche Metadaten? Konkret: den Titel, den Slug (quasi der Permalink), Tags, Kategorien und den Excerpt/Auszug. Aber fangen wir zunächst mal mit dem Einfügen ein paar weniger Variablen an.
Variablen der EnnoAutoPost-Klasse
Zuerst legen wir Variablen für jede der genannten Meta-Informationen an. Außerdem speichern wir den Inhalt des Blogposts in HTML-Form in einer Variable ($_content
). Da wir im weiteren Verlauf des Skripts auch mehrfach eine Verbindung über die XML-RPC-Schnittstelle herstellen werden, ist es sinnvoll die dazugehörige Instanz in eine Variable zu speichern ($_client
). In diesem Zug können wir auch gleich die URL der Schnittstelle als erste Konfigurationsvariable speichern ($_url
). Zu guter letzte noch eine generische Variable $_postData
für das finale Sammeln der zu sendenden Blogpost-Daten. Über deren Verwendung lässt sich im Nachhinein sicherlich aus Redundanz-Gründen streiten, aber es ist erstmal übersichtlicher.
Da wir etwas mit regulären Ausdrücken arbeiten werden, habe ich mich außerdem für einen zentralen Begrenzer (DELIMITER
) entschieden. Damit umgehe ich etwas “Escaperei”, was die ohnehin schwer lesbaren Regex-Patterns noch etwas kryptischer machen würde. Der Kopf unserer Klasse dürfte also anschließend so aussehen:
<?php
require_once 'IXR_Library.php';
class EnnoAutoPost
{
/**
* config
*/
private $_url = 'http://www.ienno.de/xmlrpc.php';
/**
* constants
*/
const DELIMITER = '|';
/**
* internals
*/
private $_client;
private $_title;
private $_content;
private $_slug;
private $_tags;
private $_categories;
private $_excerpt;
private $_postData = array();
...
}
In der __construct
-Methode sorgen wir außerdem dafür, dass der bei der Instanzierung der Klasse übergebene HTML-String gespeichert wird. Außerdem instanzieren wir wie bereits angesprochen den XML-RPC-Client.
public function __construct($htmlString)
{
$this->_client = new IXR_Client($this->_url);
$this->_content = $htmlString;
}
Metadaten im Text notieren
In welcher Syntax halte ich die Metadaten in meinem Blogpost fest? Recht easy:
@slug: TODO
@tags: TODO
@categories: TODO
@excerpt: TODO
Das TODO
dient als Indikator, damit wir nicht vergessen, die Metadaten tatsächlich auch zu befüllen.
Es gibt zwar sowas wie Markdown Metadaten, allerdings kommen diese für dieses Skript nicht in Frage. Grund? Das Skript nimmt HTML entgegen, das durch Byword generiert wurde (siehe vorheriger Teil der Serie). Markdown Metadaten werden beim Generieren des HTML-Outputs jedoch nicht mit übernommen. Sie dienen lediglich zur Beschreibung des Markdown-Textes, nicht des HTML-Outputs. Darum also oben genannte Variante von mir mit der @-Syntax.
Metadaten auslesen
Das Auslesen erfolgt über Regex, also reguläre Ausdrücke. Dabei durchsuchen wir das HTML z.B. nach @slug, speichern den Wert in einer Variable und löschen anschließend die Medadaten aus dem HTML. Dort haben sie ja auch nichts weiter verloren.
Doch wie setzen wir das jetzt konkret um? Erstmal eine neue Methode namens setMetadata
hinzufügen. Diese rufen wir in der index.php direkt nach der Instanzierung auf:
$htmlString = $_SERVER['KMVAR_temp'];
$obj = new EnnoAutoPost($htmlString);
$obj->setMetadata();
In der Methode selbst legen wir ein Array an, das als Schlüssel die zu beschreibenden Variablennamen und als Werte die Regex-Patterns für die Metadaten enthält.
$data = array(
'_title' => '<h1 id=".*">(.*)</h1>', // for Byword versions < 1.5.2
'_slug' => "@slug: (.*)",
'_excerpt' => "@excerpt: (.*)",
'_tags' => "@tags: (.*)",
'_categories' => "@categories: (.*)"
);
$this->_extractPostMetadata($data);
UPDATE: seit Byword in Version 1.5.2 bekommen Überschriften nicht mehr automatisch IDs zugewiesen. Deswegen muss in $data
der reguläre Ausdruck im Schlüssel ‘_title’
geändert werden.
$data = array(
'_title' => '<h1>(.*)</h1>', // for Byword versions >= 1.5.2
'_slug' => "@slug: (.*)",
'_excerpt' => "@excerpt: (.*)",
'_tags' => "@tags: (.*)",
'_categories' => "@categories: (.*)"
);
Anschließend wird dieses Array an eine private Funktion (_extractPostMetadata
) übergeben, in der die Regex-Magie vollzogen wird. Was heißt das genau? Nichts weiter, als dass z.B. der Slug, der sich hinter @slug
versteckt, in die Variable $this->_slug gespeichert
wird.
Danach müssen wir sicherstellen, dass jede der Metadaten auch wirklich gesetzt ist bzw. etwas anderes als den Startwert (TODO
) enthält. Ist dem nicht so, wird das Skript mit einer entsprechenden Nachricht abgebrochen. Andernfalls wird der Wert dem Array $this->_postData
hinzugefügt.
if (is_null($this->_slug) && $this->_slug != 'TODO')
exit("slug missing");
else
$this->_postData['post_name'] = $this->_slug;
Die Namen der Schlüssel von $this->_postData
kommen dabei nicht von ungefähr, sondern beziehen sich auf die Felder in der entsprechenden Datenbank-Tabelle der WordPress-Installation. Mögliche Schlüssel sind hier unter content aufgelistet.
Eine Besonderheit gibt es bei den Tags und Kategorien zu beachten. Da es hier mitunter mehrere gibt, die im Kopf durch ein Komma und Leerzeichen getrennt notiert werden können (also @tags: Tag1, Tag2, Tag3
), müssen wir hier leicht abgewandelt vorgehen. Mithilfe von explode()
extrahieren wir die einzelnen Tags bzw. Kategorien und geben sie an die Funktion $this->_addTaxonomyItems
weiter. Dort werden die Taxonomy-Items, wie sie in WordPress heißen, hinzugefügt.
if (is_null($this->_tags) && $this->_tags != 'TODO')
exit("tags missing");
else {
$taxonomyName = 'post_tag';
$cats = explode(', ', $this->_tags);
$this->_addTaxonomyItems($taxonomyName, $cats);
}
Nochmal in Ruhe, bitte!
Ok, hier der gesammelte Code für die setMetadata()
-Methode:
public function setMetadata()
{
$data = array(
'_title' => '<h1 id=".*">(.*)</h1>',
'_slug' => "@slug: (.*)",
'_excerpt' => "@excerpt: (.*)",
'_tags' => "@tags: (.*)",
'_categories' => "@categories: (.*)"
);
$this->_extractPostMetadata($data);
// title
if (is_null($this->_title) && $this->_title != 'TODO')
exit("title missing");
else
$this->_postData['post_title'] = $this->_title;
// slug
if (is_null($this->_slug) && $this->_slug != 'TODO')
exit("slug missing");
else
$this->_postData['post_name'] = $this->_slug;
// excerpt
if (is_null($this->_excerpt) && $this->_excerpt != 'TODO')
exit("excerpt missing");
else
$this->_postData['post_excerpt'] = $this->_excerpt;
// tags
if (is_null($this->_tags) && $this->_tags != 'TODO')
exit("tags missing");
else {
$taxonomyName = 'post_tag';
$cats = explode(', ', $this->_tags);
$this->_addTaxonomyItems($taxonomyName, $cats);
}
// categories
if (is_null($this->_categories) && $this->_categories != 'TODO')
exit("categories missing");
else {
$taxonomyName = 'category';
$cats = explode(', ', $this->_categories);
$this->_addTaxonomyItems($taxonomyName, $cats);
}
}
Die private Methode _extractPostMetadata
ist ein bisschen komplizierter. Ich versuche mal mit kommentiertem Quellcode zu punkten:
private function _extractPostMetadata($data)
{
// go through each key-value-pair in array
foreach ($data as $prop => $pattern) {
// wrap pattern with delimiters
$pattern = self::DELIMITER . $pattern . self::DELIMITER;
// find matches for current pattern in html-string
preg_match($pattern, $this->_content, $matches);
// is there a match?
if (isset($matches[1])) {
// set variable (transmitted as key of array $data)
$this->$prop = $matches[1];
// now that we saved it, delete metadata from html-string
$this->_content = trim(preg_replace($pattern, '', $this->_content));
}
}
// clean whitespace and empty paragraphs
$pattern = "<p>\n+</p>\n*";
$this->_content = preg_replace(
self::DELIMITER . $pattern . self::DELIMITER,
'',
$this->_content
);
}
Relativ einfach hingegen ist die _addTaxonomyItems
. Dort wird einfach nur geschaut, ob das Ziel-Array schon vorhanden ist. Falls ja, wird ein Schlüssel-Werte-Paar hinzugefügt, falls nicht, wird es halt vorher noch angelegt.
private function _addTaxonomyItems($key, $data)
{
if (isset($this->_postData['terms_names']))
$this->_postData['terms_names'][$key] = $data;
else
$this->_postData['terms_names'] = array($key => $data);
}
Zugegeben, hier war ich etwas faul. Ich gehe fest davon aus, dass es hier noch eine bessere Variante gibt. Hinweise sind willkommen.
Zusammenfassung
Die ein oder andere Zeile Code mag nicht intuitiv von jedem verstanden werden. Ich hoffe jedoch, dass meine Erklärungen ausreichen, um euch ein Bild von den stattfindenden Prozessen zu vermitteln.
Falls ihr Fragen habt, haut sie einfach in die Kommentare 🙂 Im nächsten Teil geht es dann weiter mit dem automatischen Bild-Upload.