Websupporter

Vor allem über WordPress.

Was und Wie: WordPress Embeds

Mit Version 4.4 kam ein Thema wieder auf, welches in WordPress eigentlich schon eine ganze Zeit schlummerte, aber – wenigstens von mir – wenig Beachtung geschenkt wurde. Ich fand es zwar immer super, dass man einfach nur eine Youtube-URL einfügen musste, um das Video zu erhalten, aber mit der dahinter stehenden Technologie hatte ich mich nie näher beschäftigt. Mit WordPress 4.4 wurde es plötzlich möglich, das Gleiche auch mit WordPress Beiträgen zu tun. Die URL eines Beitrags transformierte sich in einen Iframe und in WordPress 4.5 schließlich konnte man das Aussehen dieses Iframes super einfach im Theme ändern. Für mich hieß das: Zeit, sich näher mit Embeds und oEmbeds in WordPress zu beschäfitgen.

In diesem Blogbeitrag werde ich zunächst ganz allgemein auf oEmbeds in WordPress eingehen und wie man dieses System nutzt. Danach sehe ich mir an, wie man sich als Entwickler in diese API einklinken kann. Zunächst werde ich dabei erörtern, welche Schnittstellen WordPress als (o)Embed-Client bereithält, um danach auf die Schnittstellen des oEmbed Providers WordPress einzugehen.

Inhalt

  1. Was sind (o)Embeds?
  2. Mit der (o)Embeds API entwickeln
    1. WordPress als Embed-Client
      1. Entferne die automatische Einbettung von URLs mit remove_filter()
      2. URLs einbinden mit wp_embed_register_handler()
      3. Einen neuen oEmbed Provider registrieren: wp_oembed_add_provider()
      4. Zwei unterschiedliche Wege, den Output zu kontrollieren
      5. Den Embedded-Output zu einer URL erhalten: wp_oembed_get()
    2. WordPress als oEmbed Provider
      1. Das JSON Objekt
      2. Die Inhalte des IFrames

Zum Inhalt

Was sind (o)Embeds?

Möchte man ein Youtube-Video in einem Beitrag einfügen, so kann man dies ganz einfach, indem man schlicht die URL in den Beitrag einfügt. Das gleiche gilt für einen Twitter Status. Doch die dahinter liegende Technik ist weniger bekannt. Dabei wird von WordPress auf das oEmbed-Format zurückgegriffen. Dieses Format ermöglicht den schnellen Austausch von HTML und weiteren Informationen zwischen zwei Webseiten; konkret: damit werden Inhalte ausgetauscht. Was passiert also, wenn man die URL von Youtube in den Editor einfügt?

Nehmen wir dazu folgendes Beispielvideo:

Youtube

Click the button below to load the content from Youtube.

Im Wesentlichen erkennt WordPress automatisch, dass es sich hierbei um eine URL von Youtube handelt und fragt deshalb an der oEmbed-Schnittstelle von Youtube nach den Informationen über das Video:
https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=8ucCxtgN6sc

Youtube antwortet nun mit dem durch oEmbed definierten Datenformat folgendermaßen:

{
	"author_url": "https:\/\/www.youtube.com\/user\/TheFineBros", 
	"thumbnail_url": "https:\/\/i.ytimg.com\/vi\/8ucCxtgN6sc\/hqdefault.jpg", 
	"version": "1.0", 
	"author_name": "Fine Brothers Entertainment", 
	"height": 270, 
	"html": "\u003ciframe width=\"480\" height=\"270\" src=\"https:\/\/www.youtube.com\/embed\/8ucCxtgN6sc?feature=oembed\" frameborder=\"0\" allowfullscreen\u003e\u003c\/iframe\u003e", 
	"provider_url": "https:\/\/www.youtube.com\/", 
	"width": 480, 
	"thumbnail_height": 360, 
	"type": "video", 
	"title": "TEENS REACT TO WINDOWS 95", 
	"thumbnail_width": 480, 
	"provider_name": "YouTube"
}

Oder, um das ganze ein bißchen leserlicher zu machen:

{
	"author_url": "https://www.youtube.com/user/TheFineBros", 
	"thumbnail_url": "https://i.ytimg.com/vi/8ucCxtgN6sc/hqdefault.jpg", 
	"version": "1.0", 
	"author_name": "Fine Brothers Entertainment", 
	"height": 270, 
	"html": "<iframe width="480" height="270" src="https://www.youtube.com/embed/8ucCxtgN6sc?feature=oembed" frameborder="0" allowfullscreen></iframe>", 
	"provider_url": "https://www.youtube.com/", 
	"width": 480, 
	"thumbnail_height": 360, 
	"type": "video", 
	"title": "TEENS REACT TO WINDOWS 95", 
	"thumbnail_width": 480, 
	"provider_name": "YouTube"
}

Man erhält also die URL zum Autoren zurück, eine Thumbnail URL, den Namen des Autoren, ein Höhen- und Breitenattribut sowie ein HTML-Attribut in welchem ein Iframe enthalten ist. Und genau diesen Iframe bindet WordPress nun anstelle der ursprünglichen URL, welche wir angegeben haben, ein.

Wenn man die Höhe des eingebundenen Inhalts selbst bestimmen möchte, kann man auch den Shortcode [\embed]1 nutzen: Mit [\embed width="123" height="456"]http://hier-eine-url-zu-youtube/[/embed] würde also das Video 123 Pixel breit und 456 Pixel hoch angezeigt werden.

Generell unterscheidet man zwischen einem oEmbed Provider (wie Youtube einer ist) und dem oEmbed Client; in unserem Fall die WordPress Seite, welche die Informationen anfragt. Eine Liste von Providern findet sich auf der oEmbed Seite von WordPress. Doch seit der Version 4.4 ist WordPress nun nicht mehr einfach nur ein oEmbed Client sondern auch selbst zu einem oEmbed Provider geworden! Auch WordPress hat nun eine solche Schnittstelle, über welche die Informationen zu Beiträgen abgefragt werden können. Diese lautet für diesen Blog beispielsweise

https://websupporter.net/wp-json/oembed/1.0/embed?url=

Übergeben wir an diese URL die Webadresse eines meiner Blogbeiträge erhalten wir erneut ein JSON-Objekt zurück:

{
	"version":"1.0",
	"provider_name":"Websupporter",
	"provider_url":"https://websupporter.net",
	"author_name":"David Remer",
	"author_url":"https://websupporter.net/author/websupporter/",
	"title":"BuddyPress Desktop Notification 0.7 Release",
	"type":"rich",
	"width":600,
	"height":338,
	"html":"<blockquote class="wp-embedded-content"><a href="https://websupporter.net/buddypress-desktop-notification-0-7-release/">BuddyPress Desktop Notification 0.7 Release</a></blockquote>n<script type='text/javascript'>n<!--//--><![CDATA[//><!--ntt!function(a,b){"use strict";function c(){if(!e){e=!0;var a,c,d,f,g=-1!==navigator.appVersion.indexOf("MSIE 10"),h=!!navigator.userAgent.match(/Trident.*rv:11./),i=b.querySelectorAll("iframe.wp-embedded-content"),j=b.querySelectorAll("blockquote.wp-embedded-content");for(c=0;c<j.length;c++)j[c].style.display="none";for(c=0;c<i.length;c++)if(d=i[c],d.style.display="",!d.getAttribute("data-secret")){if(f=Math.random().toString(36).substr(2,10),d.src+="#?secret="+f,d.setAttribute("data-secret",f),g||h)a=d.cloneNode(!0),a.removeAttribute("security"),d.parentNode.replaceChild(a,d)}else;}}var d=!1,e=!1;if(b.querySelector)if(a.addEventListener)d=!0;if(a.wp=a.wp||{},!a.wp.receiveEmbedMessage)if(a.wp.receiveEmbedMessage=function(c){var d=c.data;if(d.secret||d.message||d.value)if(!/[^a-zA-Z0-9]/.test(d.secret)){var e,f,g,h,i,j=b.querySelectorAll('iframe[data-secret="'+d.secret+'"]'),k=b.querySelectorAll('blockquote[data-secret="'+d.secret+'"]');for(e=0;e<k.length;e++)k[e].style.display="none";for(e=0;e<j.length;e++)if(f=j[e],c.source===f.contentWindow){if(f.style.display="","height"===d.message){if(g=parseInt(d.value,10),g>1e3)g=1e3;else if(200>~~g)g=200;f.height=g}if("link"===d.message)if(h=b.createElement("a"),i=b.createElement("a"),h.href=f.getAttribute("src"),i.href=d.value,i.host===h.host)if(b.activeElement===f)a.top.location.href=d.value}else;}},d)a.addEventListener("message",a.wp.receiveEmbedMessage,!1),b.addEventListener("DOMContentLoaded",c,!1),a.addEventListener("load",c,!1)}(window,document);n//--><!]]>n</script><iframe sandbox="allow-scripts" security="restricted" src="https://websupporter.net/buddypress-desktop-notification-0-7-release/embed/" width="600" height="338" title="Embedded WordPress Post" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe>",
	"thumbnail_url":
	"https://websupporter.net/wp-content/uploads/2015/05/banner-772x250.png",
	"thumbnail_width":600,
	"thumbnail_height":194
}

Wenn man sich ein wenig durchwühlt sieht man auch hier, dass im HTML-Attribut ein Iframe übergeben wird, nämlich mit der URL https://websupporter.net/buddypress-desktop-notification-0-7-release/embed/ (Leider funktioniert die oEmbed-Funktion gerade nicht mit meinen deutschsprachigen Beiträgen, das muss ich auch noch irgendwann beheben, wahrscheinlich indem ich endlich mal auf Polylang umsattele, hmm – wobei ich nochmal prüfen muss, ob das mit Polylang dann überhaupt besser ginge, aber irgendwie geh ich grad einfach davon aus). Wenn also jemand diesen Beitrag einbetten möchte, so kann er einfach die URL zu diesem Beitrag in den Editor kopieren (merke: ohne ihn zu verlinken!) und die URL würde ersetzt mit diesem Iframe.

Pascal Birchler
Pascal Birchler
Maßgeblich verantwortlich für die (Weiter-)Entwicklung der gesamten oEmbed-Schnittstelle in WordPress ist dabei der Schweizer Entwickler Pascal Birchler (@swissspidy). Wie viele andere Entwicklungen im Core hat auch diese zunächst als „Feature Plugin“ angefangen. Das bedeutet, dass eine wesentliche Erweiterung des Cores zunächst als Plugin ausgetestet wird und wenn sich dieses als funktional und erfolgreich herausstellt wird es schließlich in den Core aufgenommen.

This model allows a feature to be built, tested, refined, and polished before it is considered as a merge candidate.
wordpress.org

Dies war mit WordPress 4.4 der Fall. Es konnte nun auch in den Core aufgenommen werden, da der erste Teil der WP Rest API ebenfalls in den Core integriert wurde und die oEmbed API, welche WordPress zu einem Provider macht, auf Funktionalitäten der Rest API aufsetzt. Am 18. Juli 2015 gab Pascal in einem Blogbeitrag of wordpress.org schließlich bekannt, dass mit der Versino 4.4 WordPress zu einem oEmbed Provider würde:

Embed from make.wordpress.org

Click the button below to load the content from make.wordpress.org.

Über die nächsten Monate arbeitete Birchler und viele andere nun daran, das Feature für den Core vorzubereiten. Schließlich war es soweit und WordPress wurde zu einem oEmbed Provider.

Zum Inhalt

Mit der (o)Embeds API entwickeln

Doch genug mit dem Vorgeplänkel. Wie kann man die oEmbed API nun als Entwickler für sich nutzbar machen? Welche Funktionen, Filter und Actions gibt es, in welche man sich einhaken kann? Gehen wir dafür in zwei Schritten vor. Im ersten Teil sehen wir uns an, wie WordPress als oEmbed Client agiert, im zweiten schließlich, wie die Provider-Schnittstelle aufgebaut ist.

Zum Inhalt

WordPress als Embed-Client

Entferne die automatische Einbettung von URLs mit remove_filter()

Die entscheidende Klasse, welche die oEmbed Client Funktion für WordPress bereithält ist WP_Embed (wp-includes/class-wp-embed.php), sowie WP_oEmbed (wp-includes/class-wp-oembed.php). WP_Embed kümmert sich generell um die Einbettung von Inhalten, denn es können auch Inhalte eingebettet werden, welche nicht von einem oEmbed Provider stammen (s.u.). WP_oEmbed kümmert sich nur um die Einbettung von Inhalten, welche von oEmbed Providern stammen. Sehen wir uns zunächst die WP_Embed näher an: Diese hakt sich in den Filter 'the_content' ein und wendet hier die Funktion auto_embed() an. Im Wesentlichen wird hier nach alleinstehenden URLs gesucht und diese schließlich so behandelt als wären sie mit dem Shortcode [\embed] umgeben. Wenn man also die automatische Umwandlung von alleinstehenden URLs in eingebettete Inhalte verhindern möchte, so kann man dies, da die Klasse WP_Embed in der Variablen $GLOBALS['wp_embed'] abgelegt wird folgendermaßen erreichen:

Embed from gist.github.com

Click the button below to load the content from gist.github.com.

Zum Inhalt

URLs einbinden mit wp_embed_register_handler()

Dieses kleine Codebeispiel wird übrigens auf Github gespeichert und mit Hilfe der Embed API eingebettet. Doch, wenn man auf der Liste der offiziell unterstützten oEmbed Provider von WordPress nachsieht, sieht man, dass Github offiziell nicht unterstützt wird. Wir müssen also Github zunächst registrieren, damit das Script eingebunden werden kann. Dazu gibt es die Funktion wp_embed_register_handler(). Den Großteil des Codes für dieses kleine Script habe ich von hier, musste es allerdings noch ein wenig abwandeln:

Embed from gist.github.com

Click the button below to load the content from gist.github.com.

Da Github selbst eigentlich kein oEmbed Provider ist müssen wir es quasi selbst zu einem machen. Deshalb registrieren wir auch einen Embed Handler und keinen Embed Provider, doch dazu gleich mehr. Wie funktioniert also wp_embed_register_handler()? Zunächst übergeben wir eine ID für den neuen Handler, in unserem Falle 'gist'. Diese können wir frei wählen, müssen aber sicherstellen, dass diese einzigartig ist. Danach übergeben wir einen regulären Ausdruck. Trifft dieser auf eine URL in einem Blogbeitrag zu wird schließlich die in Parameter drei übergebene Callback-Funktion aufgerufen. Schließlich könnten wir im letzten Parameter optional noch die Priorität für diesen Handler übergeben.

Im unserem regulären Ausdruck haben wir zwei Platzhalter übergeben. Eine Gist-URL sieht normalerweise folgendermaßen aus:
https://gist.github.com/websupporter/36d1108d03c3cdab5d3d

Gist ermöglicht es, diese Dateien mittels eines Javascripts einzubetten. Die URL zu diesem Script sieht normalerweise so aus:
https://gist.github.com/websupporter/36d1108d03c3cdab5d3d.js

Unsere Platzhalter nehmen also den Benutzernamen und die Gist-ID entgegen und übergebn diese an die Callback-Funktion zusammen mit weiteren Parametern. So können wir aus diesen Parametern unsere Script-URL formen und zurückgeben. Schon hat man einen neuen Embed Handler registriert. Wenn Du nun die Gist-URL über den Shortcode [\embed] einpflegen würdest, so würden die Attribute des Shortcodes über $attr übergeben. Die eigentliche URL wird im dritten Parameter an die Callback-URL übergeben und $rawattr enthält nocheinmal die Attribute, aber „unbehandelt“, für gewöhnlich wirst Du diese Variable nicht benötigen.

Mit wp_embed_unregister_handler() kann man einen Handler wieder entfernen.

Zum Inhalt

Einen neuen oEmbed Provider registrieren: wp_oembed_add_provider()

Doch wp_embed_register_handler() ist eigentlich etwas ab vom Schuss für unser Thema oEmbeds, denn schließlich bettet man hier Inhalte ein, welche gerade nicht von einem oEmbed Provider kommen. Doch was, wenn es einen oEmbed Provider gibt, welcher bisher von WordPress offiziell nicht unterstützt wird, den wir aber unterstützen möchten. Angenommen, wir sind ein solcher Provider und möchten nun ein WordPress Plugin zur Verfügung stellen, welches uns als oEmbed Provider registriert: wp_oembed_add_provider().

Nehmen wir also kurz an, wir würden gfycat.com betreiben, eine Seite, welche GIFs bereitstellt. Oder – wir mögen Katzen-GIFs einfach so gerne, dass wir die so schnell wie möglich einbinden möchten können. Eine große Schande, dass auch mit WordPress 4.5 gfycat.com noch immer nicht zu den offiziellen oEmbed Providern von WordPress zählt. Immer wieder wird WordPress um fancy Funktionen erweitert, doch die Wesentlichen stets aufgeschoben. Wie das so ist, ein Snippet muss her:

Embed from gist.github.com

Click the button below to load the content from gist.github.com.

Doch zunächst einmal:

Embed from gfycat.com

Click the button below to load the content from gfycat.com.


Always allow gfycat.com

It works! Im ersten Parameter übergeben wir einen regulären Ausdruck und im zweiten die oEmbed-Schnittstelle des Providers. Trifft eine URL auf den regulären Ausdruck zu, so wird sie an die Schnittstelle übergeben, um die Daten zu erhalten. Ob es sich beim ersten Parameter tatsächlich um einen regulären Ausdruck handelt oder nur mit einem Wildcard String gearbeitet wird kann man im letzten Parameter festlegen. true bedeutet, es handelt sich um einen regulären Ausdruck. Der Standard ist dabei false.

Mit wp_oembed_remove_provider() kann man einen Provider wieder entfernen.

Zum Inhalt

Zwei unterschiedliche Wege, den Output zu kontrollieren

Einleitend habe ich schon darauf hingewiesen, dass es die Klasse WP_Embed sowie WP_oEmbed gibt. Je nachdem, ob der Inhalt über die oEmbed-Schnittstelle oder einen Handler eingebettet wird, kann man den Output an unterschiedlichen Stellen filtern.

Den Embed Handler Output filtern
Der Output eines Embed-Handlers kann mit 'embed_handler_html' gefiltert werden. Sagen wir, wir möchten nicht das Javascript von Gist verwenden, sondern den Raw Output, welchen wir als Iframe einbinden möchten. Aus irgendeinem Grund können wir das nicht schon in dem obigen Codesnippet bewerkstelligen. Dann hätten wir immer noch den Filter 'embed_handler_html':

Embed from gist.github.com

Click the button below to load the content from gist.github.com.

Einzig: Git erlaubt das Einbetten via Iframe nicht 😞. Aber – das Prinzip wird deutlich: Über den Filter erhalten wir

  1. den vorgesehenen HTML Output
  2. die ursprüngliche URL
  3. die Shortcode-Attribute falls über [\embed] eingebunden
  4. sowie – aber nicht immer – die aktuelle Post ID

In unserem Script prüfen wir also, ob die URL von Github stammt. Sollte das nicht der Fall sein geben wir einfach den Output zurück. Ansonsten haben wir uns auch schon wieder unsere Platzhalter in $matches gezogen, kennen also den Benutzernamen und die Gist-ID und setzen diese nun in die entsprechende URL des Iframes ein. Was wir dann sehen: Nichts, weil Github es nicht zulässt die RAW-Daten als Iframe einzubinden. Aber: Das Prinzip funktioniert und ich suche jetzt nicht nochmal nach einem anderen Beispiel XD.

Den oEmbed Output filtern
Stattdessen wende mich lieber dem oEmbed Filter zu. Einige Zeitgenossen meckern ja bei der ganzen Embed Kiste über Ladezeiten. Was? Warum? Dieser Blogpost braucht doch nur 30 Sekunden bis er geladen ist. Doch tatsächlich, diese Youtube-Videos brauchen ja ewig zu laden, da wird einiges in dem Iframe reingehauen. Google, right? Die ganze Zeit sagen, „Ey! Du musst das Web schneller machen“ und dann an jeder Stelle selber die Handbremse ziehen. Anyway, erklären wir Google und Youtube mal, wie man das macht. Versuchen wir doch mal den Output von Youtube dahingehend zu verändern, dass statt des Iframes zunächst einmal nur das Thumbnail dargestellt wird und sobald man darauf klickt das Video geladen wird. Erste Station: Der Filter 'oembed_result'. Übergeben wird uns hier der HTML-String zum Filtern, die ursprüngliche URL sowie eventuell weitere Argumente des Shortcodes.

Embed from gist.github.com

Click the button below to load the content from gist.github.com.

In unserem Script haken wir uns in den Filter ein und überprüfen zunächst, ob es sich um eine Youtube-URL handelt. Sollte dem nicht so sein geben wir den HTML-Output einfach zurück. Sollte es sich allerdings um eine Youtube-URL handeln ziehen wir uns die Youtube-ID, verpacken das ganze in ein schönes DIV in welchem wir das Thumbnail verpacken und geben dieses zurück. Zusätzlich laden wir ein Javascript, welches das Handling für uns übernimmt:

Embed from gist.github.com

Click the button below to load the content from gist.github.com.

Ein bißchen hacky das ganze, aber es funktioniert 🙂 Eine vernünftige Umsetzung sähe allerdings anders aus (Insbesondere, wenn man sich über Ladezeiten beschwert, dann ist dieser Script-Enqueue natürlich ätzend). Es stellt sich auch die Frage, ob man sich nicht schon vorher einhaken kann. Youtube übergibt in seinen oEmbed Informationen ja schon das Thumbnail. Wäre es nicht schöner, sich schon während dieser Kommunikation einzuhaken?

Einhaken, während die Daten ermittelt werden
Sieht man sich die Funktion get_html() aus der oEmbed-Klasse näher an, sieht man, dass das HTML-Resultat eigentlich schon in der Funktion data2html() ermittelt wird. Hier wird der HTML-String mit dem Filter 'oembed_dataparse' zurückgegeben und in diesem haben wir neben dem HTML-Output noch das Datenobjekt, welches die Informationen des oEmbed Providers enthält, sowie die URL.

Wir könnten also auch wie folgt vorgehen:

Embed from gist.github.com

Click the button below to load the content from gist.github.com.

Soweit zum Prinzip. Richtig schön wird das Script hier allerdings nicht mehr 😛

Eine letzte Anmerkung, wenn man mit diesen Filtern arbeitet. Damit nicht bei jedem Aufruf WordPress zunächst beim oEmbed Provider nachfragen muss, was denn nun wie gerendert werden soll wird der HTML-Output sowie ein Timestamp als benutzerdefinierte Felder in der Datenbank abgelegt. Arbeitet man also mit diesen Filtern sollte man parallel in phpMyadmin diese benutzerdefinierten Felder immer wieder löschen. Zur Erstellung dieses Beitrags habe ich deshalb immer auf folgendes SQL Statement zurückgegriffen:

delete from `wp_postmeta` where `post_id` = 772 AND `meta_key` like '_oembed%'

Zum Inhalt

Den Embedded-Output zu einer URL erhalten: wp_oembed_get()

Nur kurz, weil es doch den ein oder anderen interessieren könnte. Mit Hilfe der Funktion wp_oembed_get() kann man das oEmbed Resultat zu einer URL erhalten.

$embed_code = wp_oembed_get('http://www.youtube.com/watch?v=AbcDeFg123');

würde das Youtube-Video zurück geben. Auch hier gilt, dass man sich mit den oben besprochenen Filtern in diese Ausgabe einhaken kann. Neben der URL können im zweiten Parameter weitere Argumente übergeben werden.

Zum Inhalt

WordPress als oEmbed Provider

Wie ausführlich beschrieben ist WordPress seit Version 4.4 eben nicht nur Client sondern auch Provider! Zunächst, wem das nicht gefällt, dem hilft das Plugin „Disable Embeds“, die Provider-Funktion abzustellen.

Zwei Bereiche der oEmbed Provider API möchte ich mir hier genauer ansehen: Zum einen natürlich die Embeds themeselves, also die HTML-Inhalte, welche via Iframe eingbunden werden. Zum anderen aber auch das JSON-Objekt welches generiert wird.

Zum Inhalt

Das JSON Objekt

Schaut man sich den HTML-Quelltext eines Beitrags in WordPress an, so wird man dort folgende Zeilen im <head>-Bereich finden:

<link rel="alternate" type="application/json+oembed" href="http://url-zum-blog/wp-json/oembed/1.0/embed?url=url-zum-aktuellen-beitrag" />
<link rel="alternate" type="text/xml+oembed" href="http://url-zum-blog/wp-json/oembed/1.0/embed?url=url-zum-aktuellen-beitrag&format=xml" />

Hierbei handelt es sich um die Adresse zum JSON-, beziehungsweise XML-Objekt des jeweiligen Beitrags. Wie man an dem /wp-json/-Teil sieht, handelt es sich dabei um einen REST-Api Endpoint. Übergeben werden die Informationen: Version, welche standardmäßig 1.0 ist, den Namen des oEmbed Providers, standardmäßig der Name des Blogs, die URL zum oEmbed Provider, also die Adresse zum Blog, den Namen des Autoren und die URL zu seinen Beiträgen auf der Seite, der Titel des Beitrags sowie ein „type“, standardmäßig ist das „link“. Diese Informationen können über den Filter 'oembed_response_data' gefiltert werden. Neben den angegebenen Daten, welche als Array übergeben werden erhält man das Post-Objekt im zweiten Parameter, sowie die vorgesehene Breite und Höhe des Embeds.

Bei diesem Filter ist ein wenig Vorsicht geboten, denn tatsächlich hakt sich WordPress selbst in diesen Filter standardmäßig ein und ändert den in get_oembed_response_data() erzeugten Array in der Funktion get_oembed_response_data_rich() (Diese Funktion ändert unter anderem das type-Attribut auf rich.) Hakt man sich also zu früh ein, könnten die eigenen Änderungen wieder überschrieben werden.

Aber, warum sollten wir überhaupt das JSON-Objekt (beziehungsweise das XML-Objekt) ändern wollen? oEmbed ist ein Standard, der dem oEmbed Client erklärt, wie er mit einzubettenden Inhalten umgehen soll. So gibt es beispielsweise verschiedene Typen von Einbettungsdaten: Bilder (photo), Videos (video), HTML (rich), Audio (audio) oder Link (link).

Sagen wir, wir betreiben einen Fotoblog und möchten diese Fotos einbettbar machen. Folgt man der oEmbed Spezifikationsseite böte es sich an, den type auf photo zu ändern und zusätzlich den Datenarray um den Parameter url zu erweitern, welcher die URL zu beispielsweise dem Thumbnail des Beitrags enthält. Zusätzlich sollte man dann auch mit width und height die Maße des Bildes übergeben. Ein oEmbed Client würde nun verstehen, dass es sich bei dem Inhalt um eine einzubettende Grafikdatei handeln würde. Unser kleiner Beispielcode könnte also folgendermaßen aussehen:

Embed from gist.github.com

Click the button below to load the content from gist.github.com.

Hat der Beitrag ein Beitragsbild, so wird nun der oEmbed-Typ auf photo umgestellt und das Beitragsbild übergeben. Ein oEmbed Client, beispielsweise eine andere WordPress-Seite, würde nun das in url übergebene Bild rendern und – im Falle von WordPress – dabei auf die im Editor hinterlegte URL verlinken.

Zum Inhalt

Der oEmbed Iframe

Standardmäßig übergibt WordPress allerdings einen Iframe, in welchem man einen Ausschnitt des verlinkten Beitrags sieht. So wird das Beitragsbild, der Titel und das Exzerpt in diesem Iframe dargestellt. Das Aussehen dieser HTML-Seite, welche sich hinter http://url-zum-blog/blogbeitrag/embed/ befindet, war bisher etwas umständlich mit Hilfe von Filtern zu ändern. Mit Version 4.5 (ab 12. April 2016) ändert sich dies nun!

Hallo an alle Themedesigner, das ist für Euch! Wenn Ihr was auf Eure User gebt, dann solltet Ihr diese Template-Dateien berücksichtigen. Schließlich werden Eure Blogger eingebunden und das sollte doch in dem einzigartigen Style erfolgen, der den Blog – aus Designersicht – so erfolgreich macht. Ihr sagt ja immer, es geht nicht nur um Inhalte sondern auch, wie diese verpackt sind. Also los: Eure Blogger werden embeddet, euer Layout sollte es auch 🙂

Das Iframe wird nun mit Hilfe von Templates modifizierbar. Diese Templates sind nun Bestandteil der WordPress Template Hierarchie, wobei der letzte Fallback nicht wie üblich index.php, sondern wp-includes/theme-compat/embed.php ist.

Die Embed Template Hierarchie
Die Embed Template Hierarchie

Wie bei anderen Templates auch kann man hier die Template Tags wie the_title(), the_excerpt() und so weiter einsetzen.

Doch sehen wir uns zunächst das Standard-Template an:

Embed from gist.github.com

Click the button below to load the content from gist.github.com.

Zunächst wird mit get_header( 'embed' ) eine header-embed.php aufgerufen. Wenn das Theme über eine solche verfügt, wird diese genommen, ansonsten wp-includes/theme-compat/header-embed.php. Am Ende wird mit get_footer( 'embed' ) das gleiche mit footer-embed.php versucht. Das bedeutet, dass Themedesigner ganz ähnlich wie für das normale Themelayout auch für Embeds Header und Footer anlegen können. Allerdings sollte man hier ein wenig aufpassen.

  1. Der <title>-Tag wird hier via
    <title><?php echo wp_get_document_title(); ?></title>
    generiert und nicht über Themesupport geregelt!
  2. statt der Aktion 'wp_head' wird die Aktion 'embed_head' ausgeführt!

Interessant ist auch, dass via header() ein X-WP-embed:true übergeben wird:

an X-WP-embed:true header will be sent when that template is used, so you can easily target embedded posts in your application.
wordpress.org

Und auch für den Footer gilt: Es wird nicht die Aktion 'wp_footer' sondern 'embed_footer' ausgeführt.

Desweiteren fallen die zwei Template-Parts auf: embed-content.php, sowie embed-404.php. Auch hier gilt, zunächst werden diese Template-Teile im Themeordner gesucht und – falls dort nicht vorhanden – wird auf die Teile im theme-compat-Ordner zurückgegriffen.

Verbleiben wir kurz bei der embed-404.php. Diese wird aufgerufen, sollte kein Blogbeitrag gefunden worden sein. Wenn ich eine URL zu einem Beitrag einbette und hinter dieser steht kein Beitrag, so gibt die JSON-Abfrage normalerweise folgendes zurück:
{"code":"oembed_invalid_url","message":"Not Found","data":{"status":404}}

In diesem Fall würde WordPress die URL also nicht weiter einzubetten versuchen. Nun kann es aber sein, dass ein Beitrag einmal existierte und dann gelöscht wurde. Da der oEmbed Client WordPress die HTML-Ausgabe und das heißt den Iframe allerdings für einen Tag cacht, würde maximal einen Tag lang weiterhin versucht, den Beitrag darzustellen. Für diese Zeitspanne würde dann die embed-404.php ausgegeben. Danach würde erneut das JSON-Objekt abgefragt, festgestellt, dass die URL nicht mehr gültig ist und die Einbettung des Iframes aufgehoben.

Interessanter ist natürlich die embed-content.php:

Embed from gist.github.com

Click the button below to load the content from gist.github.com.

Wie man sieht unterscheidet sich auch dieses Template geringfügig von normalen Templates. So wird die post_class() um .wp-embed erweitert und einige Berechungen zum Beitragsbild durchgeführt, welche aber auf das entsprechende Layout zurückzuführen sind und deshalb auch erst einmal vernachlässigt werden können (wobei man schon einiges in Sachen „Embed“-Layout lernen kann). Interessant ist, dass das Exzerpt nicht mit Hilfe von the_excerpt() sondern mit the_excerpt_embed() dargestellt wird. Der einzige Unterschied zwischen diesen beiden Methoden besteht darin, dass hierbei ein zusätzlicher Filter eingebaut ist, durch welchen das Exzerpt gefiltert wird: 'the_excerpt_embed'. Anschließend wird die Aktion 'embed_content' ausgeführt. Im Footer-Bereich findet sich die Funktion the_embed_site_title(). Diese ist für die Ausgabe des Seitentitels und des Seitenicons zuständig.

Die folgende Grafik veranschaulicht, welche Funktionen wo im Standard-Template zum Einsatz kommen:

Funktionen in der embed-content.php
Funktionen in der embed-content.php

the_embed_site_title()
Diese Funktion gibt also den HTML-Code für den Seitentitel und das Icon aus. Bleiben wir kurz beim Icon stehen. Seit Version 4.3 unterstützt WordPress Siteicons. Diese werden letztlich in der Option 'site_icon' abgelegt. Sollte kein Siteicon angegeben sein, so wird ein WordPress-Logo aus dem wp-includes/images-Ordner genommen. Mit Hilfe des Filters get_site_icon_url kann man sich allerdings in die URL-Ausgabe einhaken und selbst ein Fallback-Icon festlegen (siehe dazu die Funktion get_site_icon_url() in wp-includes/general-template.php). Auch the_embed_site_title() filtert die HTML-Ausgabe mit Hilfe des Filters 'embed_site_title_html'.

print_embed_comments_button() und print_embed_sharing_button()
Im Quelltext der embed-content.php findet sich nach dieser Funktion die Aktion 'embed_content_meta'. In diese Aktion haken sich nun die zwei Befehle ein, welche man in der oberen Grafik sieht: print_embed_comments_button() und print_embed_sharing_button().

Beide Funktionen geben dabei tatsächlich nur den HTML-Code aus und können nicht weiter modifiziert werden. Wenn man also weiter mit dem Aktion-Hook arbeiten möchte, allerdings auf eine dieser Ausgaben verzichten möchte, so muss man diese Aktionen deregistrieren.

get_post_embed_html()
Mit Hilfe dieser Templates kann man die Ausgabe des Iframe-Inhalts also komplett steuern. Doch, es wird mehr als nur ein Iframe übergeben. Tatsächlich wird zunächst ein <blockquote> und jede Menge Javascript übergeben, bevor der eigentliche Iframe übermittelt wird. Diese Ausgabe wird in get_post_embed_html() vorbereitet und kann mit Hilfe von 'embed_html' komplett gefiltert werden.

Standardmäßig sieht man zunächst den Titel des Blogbeitrags als Zitat. Dieses Zitat wird ausgeblendet, sobald das Iframe geladen wurde. So sieht der Besucher zumindest immer etwas, auch wenn der iFrame noch nicht geladen ist. An den Filter 'embed_html' wird nun der komplette HTML-Output (nicht das Embed, sondern das <iframe>-Element mit dem Embed als Quelle) übergeben, das aktuelle Post-Objekt, sowie die Höhe und Breite des Embeds.

Höhe und Breite eines Embeds
Die maximal erlaubte Breite eines Embeds ist standardmäßig 600 Pixel und kann mit dem Filter 'oembed_default_width' abgeändert werden. WordPress legt es bei der Ausgabe eines Embeds auf das 16:9-Format an. Die Höhe berechnet sich dabei aus der angegebenen Breite, wobei 200 Pixel nicht unterschritten werden soll. Auch für die Breite des Embeds gilt, dass es nicht kleiner als 200 Pixel sein soll. Möchte man dies ändern, so kann man auf den Filter 'oembed_min_max_width' zurückgreifen, welcher einen Array mit der Mindest- und Maximalbreite des Embeds zurückgibt.

Möchte man die Standardbreite des Embeds über 600 Pixel ausdehnen langt es auch nicht nur mit dem Filter'oembed_default_width' zu arbeiten. Darüber hinaus muss auch die Maximalbreite in 'oembed_min_max_width' angepasst werden.

Zum Inhalt

Schluss

Es zeigt sich also, dass die (o)Embed-Schnittstelle von WordPress ziemlich umfangreich ist und man wie immer fast in sämtliche Prozesse mittels Plugins, Snippets und Templates eingreifen kann. Liest man tatsächlich die entsprechenden PHP-Dateien findet man schnell, dass es noch zahlreiche weitere Action- und Filterhooks gibt, in welche man sich einhaken kann. Fehlt Euch ein Filter oder habt Ihr Fragen freue ich mich immer über Kommentare.


Fußnoten

1.) Der Shortcode embed kann leider nicht escaped werden, indem man ihn mit einem weiteren Bracket umschließt. Hier erkläre ich, wie man normalerweise einen Shortcode escapen kann. Da dies mit embed nicht funktioniert verwende ich hier [\.

Über den Autoren

Seine erste Webseite hat David Remer 1998 in HTML verfasst. Wenig später war er fasziniert von DHTML und JavaScript. Nach jahrelanger Freelancerei arbeitete er zunächst für Inpsyde und ist heute Entwickler bei Automattic. Außerdem hat er das Buch "WordPress für Entwickler" verfasst.

Kommentare

  1. Vielen Dank für diese tolle Übersicht!

    Noch zur Höhe und Breite eines Embeds: Hier muss man erwähnen, dass die Höhe mittels JavaScript jeweils automatisch angepasst wird. Somit ist eigentlich nur die Breite entscheidend.

    Ps: Statt [\embed] kann man einfach [[embed]] schreiben (zwei eckige Klammern)

    1. Hi Pascal. Danke für Deinen Hinweis. Mit [[embed]] hatte ich es lokal probiert, das hatte bei mir nie geklappt. Ich vermute es hängt damit zusammen, dass der Shortcode ja etwas anders läuft, hatte mich da jetzt aber nicht genügend eingelesen:

      		// Hack to get the [embed] shortcode to run before wpautop()
      		add_filter( 'the_content', array( $this, 'run_shortcode' ), 8 );
      
      		// Shortcode placeholder for strip_shortcodes()
      		add_shortcode( 'embed', '__return_false' );
      
      		// Attempts to embed all URLs in a post
      		add_filter( 'the_content', array( $this, 'autoembed' ), 8 );
      
      	public function run_shortcode( $content ) {
      		global $shortcode_tags;
      
      		// Back up current registered shortcodes and clear them all out
      		$orig_shortcode_tags = $shortcode_tags;
      		remove_all_shortcodes();
      
      		add_shortcode( 'embed', array( $this, 'shortcode' ) );
      
      		// Do the shortcode (only the [embed] one is registered)
      		$content = do_shortcode( $content, true );
      
      		// Put the original shortcodes back
      		$shortcode_tags = $orig_shortcode_tags;
      
      		return $content;
      	}
      

      Aber da muss ich nochmal näher reinschauen. Vielleicht ist in meinem lokalen Install (wo ich meinen Beitrag geschrieben habe) irgendeines meiner Testscripts verantwortlich 🙂

  2. Hallo Pascal,

    … kann man diese Embeds aktiviern/deaktivieren?
    Denn ich wundere mich gerade darueber, das es in meiner Multisite-Installation, in einer (Unter)Seite funktioniert und in der anderen nicht!

    Beispiel:
    1. Ich kopieren mir einen Link (egal ob Yoetube, Bild o. Textlink) und fuege in einen Beitrag ein.
    2. Ich fuege diesen Link in den Beitrag meiner ERSTEN (Unter)Seite ein und es erscheint die Vorschau des Videos, Bildes, etc. (es funktioniert)
    3. Ich fuege diesen Link in den Beitrag meiner ZWEITEN (Unter)Seite ein und es passiert nichts (es funktioniert nicht)

    Danach habe ich andere einzlne WP-Installation geprueft.
    Da funktioniert es auch nicht.

    Wie, wo, was kann/muss man da machen/einstellen, um diese Funktion zu nutzen?
    Oder wenn man sie nicht nutzen will, wie stellt man sie ab?

    Vielen Dank fuer die schnelle Antwort.
    G, Mike

    1. … ok – habe gerade Dein Plugin zum Deaktivieren gefunden (https://wordpress.org/plugins/disable-embeds/).

      Aber das erklaert leider nicht den Ausgangszustand in meiner diversen Seiten.

      Die Frage ist immer noch: Warum funktionieren diese Embeds, bei der einen WP-Installation o. WP-Seite und bei der anderen nicht – ohne vorher ein Plugin (zum deaktivieren) installiert zu haben?

      Danke – nocheinmal!
      G, Mike

  3. Hi Mike,
    das Plugin zum Abstellen hast Du ja gefunden. Normalerweise sollten die Embeds auch in Multisite Installationen laufen. Ich sehe zumindest keinen Grund, weshalb nicht.

    Gibt es zwischen der Seite, in der es funktioniert und den Seiten, in denen es nicht funktioniert einen signifikanten Unterschied (andere Plugins aktiviert/deaktiviert, andere Themes aktiv)?

    Benutzt Du die aktuelle Version von WordPress?

    Beste Grüße,
    David.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.