Auf dem CloudFest Hackathon im März, hab‘ ich mich ein wenig mit Alain Schlesser über WordPress und Plugin-Entwicklung unterhalten. Alain meinte, dass er, wenn es das Plugin hergibt und es zeitlich möglich ist, immer zuerst eine generische Library bauen und diese dann über ein WordPress-Plugin implementieren würde.
Vor ein paar Tagen hab ich mich an unser Gespräch erinnert, weil ich mich (aus Gründen) wieder intensiver mit dem ActivityPub-Plugin beschäftigt habe. Wir hatten schon vor einem Jahr den Plan, eine ActivityPub-Bibliothek zu verwenden, um uns nicht mit dem Protokoll beschäftigen zu müssen, und uns so voll und ganz auf die WordPress Integration konzentrieren könn(t)en. Wir haben die Idee aber erstmal nicht weiter verfolgt, da die Bibliothek eine Reihe von schwergewichtigen Third-Party-Libs wie Guzzle (HTTP-Client), Monolog (Logger) oder Symfony Cache (Caching) mit sich bringt. Für all diese Funktionen hat WordPress eigene Lösungen und wegen der Interoperabilität mit anderen Plugins, sollte man diese auch nutzen.
Bei der erneuten Evaluierung der ActivityPub-Bibliothek ist mir aufgefallen, dass sich Monolog, durch einen beliebigen PSR-3 kompatiblen Logger ersetzen lässt.
Eine PHP Standard Recommendation (PSR) ist eine PHP-Spezifikation, welche durch die PHP Framework Interop Group veröffentlicht wird. Ähnlich einem Java Specification Request in Java dient sie der Standardisierung von Programmierkonzepten. Ziel ist es die Interoperabilität von Komponenten zu ermöglichen und eine gemeinsame technische Basis zu schaffen oder bewährte Konzepte für einen guten Programmierstil sowie eine gute Testbarkeit von Komponenten umzusetzen. Verschiedene Frameworks wie z. B. die der TYPO3-Community, Symfony oder Zend implementieren hierbei PSR-Spezifikationen in einem selbst gewählten Umfang.
PSR-3 definiert ein standardisiertes Logger-Interface, welches (in unserem Beispiel) durch Monolog implementiert wird. Beim initialisieren des ActivityPub-Servers, lässt sich Monolog, durch einen alternativen Logger ersetzen, solange dieser auch PSR-3 konform ist.
useActivityPhp\Server;
// Create a server instance with no log output
$server = new Server([
'logger' => [
'driver' => '\Psr\Log\NullLogger'
],
]);
PSR definiert passenderweise auch Interfaces für HTTP-Clients, Request-/Response-Objekte und Caching. Für die Request-Library die in WordPress benutzt wird, gibt es sogar schon ein passendes Issue: https://github.com/WordPress/Requests/issues/320
Leider sind Caching und der HTTP-Client in der ActivityPub-Implementierung noch nicht austauschbar, obwohl beide benutzten Libraries PSR kompatibel sind. Das heißt ich muss zuerst evaluieren, wie viel Arbeit es ist, die ActivityPub-Lib anzupassen, um dann diverse Wrapper für das WordPress Caching und die Request-/Response-Classes zu schreiben…
Bzw. muss ich das wahrscheinlich nicht einmal selbst implementieren, da ich bei weitem nicht der Erste war, der diese Idee hatte:
WordPress tut sich immernoch schwer mit modernem PHP-Dependency-/Plugin-Management. Ersteres ist praktisch nicht vorhanden (wird aber zumindest nicht verhindert) und letzteres basiert immer noch auf einem Prozess in dem SVN eine tragende Rolle spielt. Aber mit PSR(-Wrappern) kann man das Problem schon ganz gut kompensieren 🙂
Viele der Plugins schreibe ich in erster Linie für mich selbst (eat your own dogfood), weshalb ich in den wenigesten Fällen über die Plugins spreche oder sie bewerbe. Das, in Verbindung mit meinen eher spärlichen Beschreibungen, sorgt oft für eher zweistellige, maximal dreistellige Download-Zahlen. Wo die Zahlen höher sind, habe ich das Plugin meistens von Anderen übernommen (um die Weiterentwicklung zu gewährleisten) oder ich bin einfach „nur“ Contributor.
Aber Schluss mit der falschen Bescheidenheit!
Selbst wenn ich die Plugins für mich baue, ist die Motivation natürlich größer, wenn sie auch von anderen benutzt werden. Also möchte ich euch hier ein paar meiner Plugins vorstellen.
ActivityPub
ActivityPub ist ein, vom W3C veröffentlichtes, offenes, dezentrales Protokoll für soziale Netzwerke.
The ActivityPub protocol is a decentralized social networking protocol based upon the [ActivityStreams] 2.0 data format. It provides a client to server API for creating, updating and deleting content, as well as a federated server to server API for delivering notifications and content.
https://www.w3.org/TR/activitypub/
Es ermöglicht das dezentrale kommunizieren über Text, Bild, Video und Audio über ein simples Inbox/Outbox Prinzip.
WebFinger Plugin
WebFinger ist kein fester Bestandteil von ActivityPub, wird aber von allen großen Netzwerken unterstützt und von Mastodon sogar verlangt. WebFinger ist eine Art Meta-Data System für alle möglichen URIs. Der gängige Identifier im Fediverse ist @username@domain.tld, das Plugin erlaubt aber auch die Author URL oder die Instant-Messaging Accounts eines Users, wenn diese unter der gleichen Domain erreichbar sind.
NodeInfo (2) ist auch kein fester Bestandteil von ActivityPub, wird aber auch von den Meisten Netzwerken unterstützt. NodeInfo stellt, wie der Name schon sagt, Infos über einen „Node“ (Server) bereit. Dank NodeInfo gibt es eine ganze Reihe an Statistik-Seiten wie the-federation.info, die bei der Auswahl der richtigen Plattform bzw. des richtigen Servers helfen.
Das eigentliche ActivityPub Plugin macht WordPress zu einem (kleinen) Teil des Fediverse. User von Mastodon, Pleroma, Friendi.ca oder Pixelfed können dem Blog „folgen“ und sehen ab dann alle neuen Blog-Posts in ihrer Timeline und können diese kommentieren. Das Plugin ist immernoch in einem frühen Stadium und bekommt sicherlich noch das ein oder andere Feature, im Fokus soll aber das Bloggen stehen. Wer ein vollwertiges, dezentrales, soziales Netzwerk möchte, sollte sich erstmal für eine der oben genannten Plattformen entscheiden.
Mehr zum IndieWeb findet ihr hier oder unter dem Tag „indieweb“ hier im Blog.
IndieWeb Plugin
Das IndieWeb Plugin hat nahezu keine Funktionalität, es ist vielmehr eine Art Installer um die IndieWeb Plugins über eine zentrale Stelle verwalten zu können.
Es gibt immer wieder Kritik am Aufbau des Plugins, bzw. kommt immer wieder die Frage auf, warum das Plugin nicht einfach die komplette Funktionalität der einzelnen Plugins beinhaltet. Meine Antwort darauf: Das IndieWeb ist mehr eine Idee als eine Spezifikation und es gibt verschiedene Möglichkeiten diese Idee mit WordPress umzusetzen. Für einen Usecase gibt es also oft verschiedene Lösungen, die von verschiedenen Personen entwickelt werden. Ein IndieWeb Plugin im Stil von ActivityPub ist in meinen Augen nicht möglich. Ich lasse mich aber gerne eines besseren belehren 😉
Webmentions sind eine moderne Alternative zu Pingbacks und Trackbacks. Im Gegensatz zu der eher unglücklichen Darstellung von Pingbacks ([...] super, wie war nochmal der kontext, oder [...]) versucht das IndieWeb (über Webmentions und Microformats), den Sinn und die Art einer Verlinkung heraus zu bekommen um die Reaktion dann als Like, Bookmark oder vollwertiges Kommentar anzuzeigen.
Das Webmention Plugin implementiert aktuell nur den Kommunikations-Teil, für das Interpretieren der Websemantiken benötigt ihr zusätzlich das „Semantic Linkbacks“ Plugin.
Mehr über Webmentions hier oder unter dem „webmention“ Tag hier im Blog.
Wie oben beschrieben sorgt das Semantic Linkbacks Plugin für die hübsche Darstellung der Webmentions, Pingbacks und Trackbacks. Wir sind gerade dabei, die Funktionalität in das Webmention Plugin zu übertragen, deshalb hat das Plugin aber nur noch temporär Bedeutung.
WebSub (formerly known as: PubSubHubbub) ist ein simples PubSub Protokoll für das Web. Es wurde ursprünglich entwickelt um updates von RSS und Atom Feeds in „echtzeit“ zu konsumieren. Push statt pull. Die Restriktion auf RSS und Atom, wurde mit der aktuellen Version aufgehoben.
WebSub provides a common mechanism for communication between publishers of any kind of Web content and their subscribers, based on HTTP web hooks. Subscription requests are relayed through hubs, which validate and verify the request. Hubs then distribute new and updated content to subscribers when it becomes available. WebSub was previously known as PubSubHubbub.
https://www.w3.org/TR/websub/
Über das WebSub Plugin (ursprünglich entwickelt von Josh Fraser) kann man die Standard-Feeds von WordPress abonnieren. Das Plugin kann aber auch über andere Plugins und Themes erweitert werden.
Das IndieWeb setzt im, Gegensatz zum Fediverse, nicht auf APIs, sondern auf Semantisches HTML:
The idea is rather than publishing something twice (repeating yourself) with (x)HTML for browsers and XML for aggregators – you simply publish once using (x)HTML and allow the tools to take care of the rest.
Ich habe viel herum experimentiert um Themes über ein Plugin mit den nötigen Semantiken zu erweitern, was aber, durch Output Escaping, zu komischen Nebeneffekten geführt hat (das alles aber nur der Vollständigkeit halber, das Thema ist eigentlich einen ganzen Artikel wert).
Letztendlich haben wir für WordPress ein Plugin gebaut, das einen Feed bereit stellt, der genau dem JSON Format entspricht, welches auch die Microformats Parser ausspucken. Das Webmention Plugin sucht also erst den pre-parsed Feed und versucht erst im zweiten Schritt, die Seite selbst zu parsen.
Ihr versteht die Ironie? Microformats(2) sind geschaffen worden um XML/JSON APIs abzulösen und weil das bei WordPress nicht wirklich dolle funktioniert bieten wir die Infos als JSON API an! 😀
Ich bin ein Freund von kleinen Plugins die nur einen spezifischen Anwendungsfall abdecken und im besten Fall auch vollkommen ohne Settings aus kommen. Frei nach dem Motto von WordPress:
When making decisions these are the users we consider first. A great example of this consideration is software options. Every time you give a user an option, you are asking them to make a decision. When a user doesn’t care or understand the option this ultimately leads to frustration.
The Open Graph protocol enables any web page to become a rich object in a social graph. For instance, this is used on Facebook to allow any web page to have the same functionality as any other object on Facebook.
Es wird aktuell von fast jedem großen Netzwerk oder fast jeder Messaging App benutzt und sorgt dafür, dass ihr die kleinen hübschen Vorschausnippets seht, wenn ihr einen Link mit euren Freunden teilt.
Das OpenGraph Plugin wurde ursprünglich von Will Norris geschrieben und generiert alle notwendigen Meta-Tag Header. Keine Settings, keine Entscheidungen, aber mit wohl definierten Filtern zum erweitern.
Hum generiert schöne, semantische Short-URLs für WordPress Posts und Pages. Das Plugin ist ursprünglich auch von Will Norris, integriert sich in die WordPress Core-Funktionen und kommt auch komplett ohne Settings aus.
Das Plugin wurde ursprünglich von johnnoone Entwickelt, es stellt eine XML Beschreibung der Suche und einen Endpunkt für Such-Vorschläge basierend auf Tags bereit.
Natürlich gibt es noch mehr, das würde aber den Rahmen sprengen. Ich nutze WordPress gerne um neue Specs und Ideen auszuprobieren und daraus entstehen meist kleine Plugins, die es oft nicht wert sind, auf WordPress.org veröffentlichen zu werden.
Ich schreibe gerade einen Artikel für das t3n Magazin über aktuelle Sign-In-Mechanismen und hab mir in dem Zuge BrowserID mal etwas genauer angeschaut. Ich bin wirklich extrem überrascht mit wie wenig Arbeit es sich in z.B. WordPress einbauen lässt.
BrowserID besteht eigentlich nur aus einem JS-File,ein paar Zeilen JS-Code:
<scriptsrc="https://browserid.org/include.js"type="text/javascript"></script><scripttype="text/javascript">
navigator.id.get(function(assertion){
if (assertion) {
// This code will be invoked once the user has successfully// selected an email address they control to sign in with.
} else {
// something went wrong! the user isn't logged in.
}
});
</script>
Code-Sprache:HTML, XML(xml)
und dem anschließenden Verifizieren der assertion:
Den ausführlichen Ablauf der Authentifizierung findet ihr auf Github.
Um BrowserID in WordPress zu integrieren lädt man also zuerst den JS-Code in den Login Header:
// add the BrowserID javascript-code to the header
add_action('login_head', 'bi_add_js_header');
functionbi_add_js_header(){
echo'<script src="https://browserid.org/include.js" type="text/javascript"></script>';
echo'<script type="text/javascript">'."\n";
echo'function browser_id_login() {
navigator.id.get(function(assertion) {
if (assertion) {
window.location="' . get_site_url(null, '/') .'?browser_id_assertion=" + assertion;
} else {
// do nothing!
}
})
};'."\n";
echo'</script>';
}
Code-Sprache:PHP(php)
und platziert den BrowserID-Button auf der Login-Seite:
// add the login button
add_action('login_form', 'bi_add_button');
function bi_add_button() {
echo '<p><ahref="#"onclick="return browser_id_login();"><imgsrc="https://browserid.org/i/sign_in_blue.png"style="border: 0;" /></a></p>';
}
Code-Sprache:HTML, XML(xml)
Nach dem klick auf den Button öffnet sich das Autorisierungs-Fenster von BrowserID und nach dem erfolgreichen Sign-In wird die gerade implementierte Methode navigator.id.get(function(assertion) {} aufgerufen.
Im nächsten Schritt muß man die erhaltene assertion über BrowserID.org verifizieren. Da ich den notwendigen POST nicht über JavaScript absetzen will, leite ich einfach auf eine Seite weiter und übergebe die erhaltene assertion als GET-Paramater.
Jetzt kann der POST bequem über WordPress abgesetzt werden.
// the verification code
add_action('parse_request', 'bi_verify_id');
functionbi_verify_id(){
global $wp_query, $wp, $user;
if( array_key_exists('browser_id_assertion', $wp->query_vars) ) {
// some settings for the post request
$args = array(
'method' => 'POST',
'timeout' => 30,
'redirection' => 0,
'httpversion' => '1.0',
'blocking' => true,
'headers' => array(),
'body' => array(
'assertion' => $wp->query_vars['browser_id_assertion'], // the assertion number we get from the js'audience' => "http://".$_SERVER['HTTP_HOST'] // the server host
),
'cookies' => array(),
'sslverify' => 0
);
// check the response
$response = wp_remote_post("https://browserid.org/verify", $args);
if (!is_wp_error($response)) {
$bi_response = json_decode($response['body'], true);
// if everything is ok, check if there is a user with this email addressif ($bi_response['status'] == 'okay') {
$userdata = get_user_by('email', $bi_response['email']);
if ($userdata) {
$user = new WP_User($userdata->ID);
wp_set_current_user($userdata->ID, $userdata->user_login);
wp_set_auth_cookie($userdata->ID, $rememberme);
do_action('wp_login', $userdata->user_login);
wp_redirect(home_url());
exit;
} else {
// show error when there is no matching userecho"no user with email address '" . $bi_response['email'] . "'";
exit;
}
}
}
// show error if something didn't work wellecho"error logging in";
exit;
}
}
Code-Sprache:PHP(php)
Gibt es einen User mit der entsprechenden E-Mail – Adresse wird er eingeloggt, falls nicht, wird ein Fehler ausgegeben.
Bei der Demo hab ich mir aus Zeitgründen ein wenig Code bei Marcel Bokhorst geliehen, dessen BrowserID-Plugin wesentlich ausgereifter und vollständiger ist als der kleine Demo-Code den ich hier zusammengestückelt habe.
Wenn euch das zu schnell ging und ich auf einige Details nicht genügend eingegangen bin, könnt ihr gerne fragen 🙂
Ich habe den kompletten Code übrigens auch auf Github hochgeladen… das ist einfacher als sich alles zusammen zu kopieren.
Dass HTML5 ein paar neue input-types definiert, habe ich durch die hcard-input-brainstorming so am Rande auf geschnappt, mir aber nichts weiter dabei gedacht… Durch Zufall bin ich heute aber über folgenden Tweet von Sylvia Egger gestoßen:
Just implemented native #HTML5 form validation on #wp comments form – it' quite simple & should be in #wp default theme
und habe bissle recherchiert… Mit den neuen Input-Types ist es doch tatsächlich möglich Input-Felder über den Browser validieren zu lassen… Ich bin begeistert! 🙂
Trägt man beispielsweise eine Nicht-Email-Adresse in folgendes Feld…
<inputtype="email" />
Code-Sprache:HTML, XML(xml)
bekommt man…
Schön wenn man sich noch über solche Kleinigkeiten freuen kann oder 😉
Lange rede kurzer Sinn: Da WordPress alle Formulare an zentraler Stelle definiert, ist es ziemlich einfach sie mit ein paar neuen Input-Types zu versehen. Mit dem folgenden Code wird das Kommentar-Formular mit den Typen "email" und "url" und das Suchformular mit dem Typ "search" (funktioniert nur in den WebKit-Browsern) erweitert:
Code-Update: Eric Eggert hat mich in den Kommentaren darauf hingewiesen, dass man mit <input required /> auch noch die Pflichtfelder validieren kann. Danke!
Code-Update 2: Dank maxe werden jetzt auch die WordPress Settings berücksichtigt (Comment author must fill out name and e-mail) und das "Comment"-Feld ist natürlich auch required
Die Informationsflut im Internet nimmt immer mehr zu und FeedReader bieten bisher keine wirkliche Möglichkeit diese Informationen sinnvoll zu filtern und da man nicht wirklich (zeitnah) Einfluss auf die Weiterentwicklung von NetNewsWire, Google Reader & Co. hat, bleibt nur noch eins: Erst filtern, dann abonnieren!
NoisePress erlaubt Seitenbesucher, einen RSS/ATOM-Feed mit Hilfe von APML vorzufiltern.
(Zum ausprobieren braucht man ein APML-Profil. Wer keines hat, sollte sich entweder das WordPress Plugin installieren oder heimlich Carstens Profil benutzen 😉 )
Warum mit APML filtern?
Man könnte natürlich auch mit WordPress-Bordmitteln eine Menge Rauschen ausfiltern, und wirklich nur das abonnieren was gerade wichtig ist:
Das Problem: Ändert sich dieses Interesse, müssen alle Feeds mühsam aussortiert (und neue gesammelt) werden. Außerdem besteht die Gefahr, dass einige spannende Themen, die nicht genau die abonnierte Kategorie/den abonnierten Tag besitzen, durch das Raster fallen können.
Das Prinzip von NoisePress: APML ist eine Art semantische Tag-Clound die das Interesse einer Person widerspiegelt. Das Interessens-Profil wird in der Regel automatisch generiert und sollte sich somit auch den diversen Interessensveränderungen anpassen.
Am Beispiel WordPress Plugin: Das Plugin erstellt ein APML-File anhand der Häufigkeit der verwendeten Tags und Kategorien. Schreibt jemand viel über OpenID, kann man davon ausgehen, dass er das Thema für wichtig hält. Ändert sich der Fokus des Blogs, wird OpenID auch im APML-Feed immer irrelevanter.
Hört sich nach Geek-Zeugs an?
Richtig! 🙂 …aber NoisePress ist auch erst einmal nur ein Test ob meine Idee überhaupt funktioniert! Im besten Fall soll der User von all der Technik gar nichts mitbekommen. Ich hoffe dass sich Firefox‘ Account Manager oder XAuth schnell weiter entwickeln und ich eine dieser Techniken für NoisePress missbrauchen könnte.
Ich würde mich übrigens sehr über ein bisschen Feedback freuen!
Ich habe mal ein kleines Plugin geschrieben welches den WordPress-Atom-Feed mit der ActivityStream-Syntax erweitert.
<entry><id>https://notiz.blog/?p=1775</id><author><name>Matthias Pfefferle</name><uri>https://notiz.blog</uri></author><...><activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb><activity:object><activity:object-type>http://activitystrea.ms/schema/1.0/blog-entry</activity:object-type><activity:object-type>http://activitystrea.ms/schema/1.0/article</activity:object-type><id>tag:notiz.blog,2009-07-13:/post/1775</id><titletype="html"><![CDATA[Matthias Pfefferle posted a new blog-entry]]></title><linkrel="alternate"type="text/html"href="https://notiz.blog/2009/07/14/webstandards-kolumne/" /></activity:object></entry>
Code-Sprache:HTML, XML(xml)
Die Syntax ist dazu gedacht, dem Feed-Parser/Feed-Reader zu erklären um was für einen Eintrag es sich handelt. Bei WordPress sind die <entry />s ausschließlich Blogposts/Artikel…
Und für die Dienste wie NoseRub, die die Aktivität gerne in einen Satz packen, gibt’s das ganze auch noch in Prosa.
<titletype="html"><![CDATA[Matthias Pfefferle posted a new blog-entry]]></title>
Code-Sprache:HTML, XML(xml)
Das ActivityStream Schema definiert übrigens noch ’ne ganze Reihe an weiteren Objekten und Verben, die auf alle möglichen Aktionen im Netz passen. Falls ihr also noch welche findet, die zu WordPress passen könnten… lasst es mich wissen 😉
Es gibt leider aber auch ein paar Probleme mit der Syntax und diversen Feed-Readern, die das zweite <title /> im <activity-object /> mit interpretieren und dann beide Titel ausgeben… aber da ja auch MySpace und Facebook die ActivityStream-Syntax einsetzen ist dieser Fehler sicherlich bald bei jedem Feed-Reader behoben 😉
Operator – Deciding what to do with Operator is difficult. […] That being said, I’m going to do a few fixes for Operator, call it 1.0 and then stop development.
The biggest news I have is that I have resumed work on Operator. In particular, I’m fixing bugs, adding a few usability enhancements and adding support for new microformat stuff like the value class/pattern for dates. I’m also considering completely removing the “Actions” toolbar and switching to interacting only with the data. I’m definitely looking for feedback on that one.
Wer Ideen zu neuen Features oder Funktionen hat, kann diese gerne auf Michaels Weblog loswerden. Mein Wunsch: Microdata support 🙂
Ich lese in letzter Zeit wieder immer häufiger von Microformats-Implementierungen im Internet Explorer 8. Geht es da „nur“ um die schon angekündigten Webslices? Weiß jemand mehr?