{"id":1398,"date":"2010-05-07T23:21:52","date_gmt":"2010-05-07T21:21:52","guid":{"rendered":"http:\/\/planetozh.com\/blog\/?p=1398"},"modified":"2010-05-08T01:06:57","modified_gmt":"2010-05-07T23:06:57","slug":"how-to-embed-a-tweet-in-wordpress-a-complete-oembed-tutorial","status":"publish","type":"post","link":"https:\/\/planetozh.com\/blog\/2010\/05\/how-to-embed-a-tweet-in-wordpress-a-complete-oembed-tutorial\/","title":{"rendered":"How to Embed a Tweet in WordPress: a Complete oEmbed Tutorial"},"content":{"rendered":"<p>A few days ago, Twitter published a <a href=\"http:\/\/media.twitter.com\/blackbird-pie\/\">new tool<\/a> that lets you embed a tweet on your site, simplifying the old school way: take a screenshot, crop the picture, upload it, embed it.<\/p>\n<p>The problem is: this Twitter tool is way too lame. Basically you need to cut and paste a lengthy code snippet full of HTML and CSS. How ugly. Compared to the way you can embed a Youtube video in WordPress (just pasting a video URL, on its own line) this is seriously cumbersome.<\/p>\n<p>Hey, wouldn&#39;t it be cool if you can embed a tweet just as easily? You would just paste its URL and let WordPress transforms it into a nicely presented tweet with links and everything? OK, let&#39;s do a plugin for this. And guess what, it&#39;s going to be fairly easy, using WordPress&#39; oEmbed API implementation.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/planetozh.com\/blog\/wp-content\/uploads\/2010\/05\/embed-tweet.jpg\" alt=\"\" title=\"embed-tweet\" width=\"528\" height=\"257\" class=\"aligncenter size-full wp-image-1400\" srcset=\"https:\/\/planetozh.com\/blog\/wp-content\/uploads\/2010\/05\/embed-tweet.jpg 528w, https:\/\/planetozh.com\/blog\/wp-content\/uploads\/2010\/05\/embed-tweet-300x146.jpg 300w\" sizes=\"auto, (max-width: 528px) 100vw, 528px\" \/><br \/>\n<!--more--><\/p>\n<h2>What is oEmbed<\/h2>\n<p>When you paste a Youtube video URL, raw (not hyperlinked) and on its own line, WordPress transforms it into an embedded video. Why? Basically, there&#39;s something in WordPress that says &quot;every time you see a URL like <tt>http:\/\/www.youtube.com\/watch?v=*<\/tt>, ask <tt>http:\/\/www.youtube.com\/oembed<\/tt> what to do with it&quot;.<\/p>\n<p>In short, <a href=\"http:\/\/www.oembed.com\/\">oEmbed<\/a> is a simple API that lets you define a URL pattern (used by a <em>consumer<\/em>) and the URL of a script (the <em>provider<\/em>) that will generate a clean HTLM out of it.<\/p>\n<p>Twitter does not support (yet) oEmbed, so we will have to handle this on our own: transform a WordPress blog into a oEmbed provider.<\/p>\n<h2>Register your blog as an oEmbed provider<\/h2>\n<p>First, let&#39;s register our blog as an oEmbed provider, to replace all URLs like <tt>http:\/\/twitter.com\/ozh\/status\/13156561<\/tt> into a cool piece of HTML. If you can breathe and walk at the same time, you&#39;re qualified to do this: all we need is a single function call. The function to be used here is <tt>wp_oembed_add_provider( $pattern, $url_of_service)<\/tt>.<\/p>\n<div class=\"igsh-code-box\" id=\"ig-sh-1\"><pre class=\"language-php line-numbers\" data-no-optimize=\"1\" data-cfasync=\"false\"><code class=\"language-php\">\/\/ Return the internal URL that will handle the oEmbed requests (ie yourblog.com\/blog\/?ozh_tweet=1)\r\nfunction ozh_tweetembed_internal_oembed_url() {\r\n\treturn trailingslashit( home_url() ) . &#039;?ozh_tweet=1&#039;;\r\n}\r\n\r\n\/\/ Register this internal URL as an oEmbed provider for twitter URLs\r\nfunction ozh_tweetembed_add_oembed_provider() {\r\n\t\/\/ http:\/\/twitter.com\/ozh\/statuses\/1234567\r\n\twp_oembed_add_provider( &#039;http:\/\/twitter.com\/*\/status\/*&#039;, ozh_tweetembed_internal_oembed_url() );\r\n}\r\nadd_action( &#039;init&#039;, &#039;ozh_tweetembed_add_oembed_provider&#039; );<\/code><\/pre><\/div>\n<p>OK, so far, we&#39;re telling WP that everytime something looks like <tt>http:\/\/twitter.com\/*\/status\/*<\/tt>, it will have to ask <tt>yourblog.com\/blog\/?ozh_tweet=1<\/tt> what to do with it.<\/p>\n<h2>Catch URLs that will contain ?ozh_tweet=1<\/h2>\n<p>Since we&#39;re now expecting WP to do something particular when called with <tt>?ozh_tweet=1<\/tt>, we have to hijack this URL:<\/p>\n<div class=\"igsh-code-box\" id=\"ig-sh-2\"><pre class=\"language-php line-numbers\" data-no-optimize=\"1\" data-cfasync=\"false\"><code class=\"language-php\">\/\/ Register &#039;ozh_tweet&#039; as a query var so we can check if ?ozh_tweet=1\r\nfunction ozh_tweetembed_add_tweet_query_var( $query_vars ) {\r\n\t$query_vars[] = &#039;ozh_tweet&#039;;\r\n\treturn $query_vars;\r\n}\r\nadd_filter(&#039;query_vars&#039;, &#039;ozh_tweetembed_add_tweet_query_var&#039;);\r\n\r\n\/\/ As early as possible, check for query var &#039;ozh_tweet&#039; and maybe shortcut everything\r\nfunction ozh_tweetembed_get_tweet_query_var() {\r\n\t$tweet = get_query_var( &#039;ozh_tweet&#039; );\r\n\tif( $tweet ) {\r\n\t\t\/\/ Make something with the oEmbed request\r\n\t\tozh_tweetembed_handle_oembed_request();\r\n\t\t\/\/ Now die, we don&#039;t want a blog page\r\n\t\tdie();\r\n\t}\r\n}\r\nadd_action( &#039;template_redirect&#039;, &#039;ozh_tweetembed_get_tweet_query_var&#039; );<\/code><\/pre><\/div>\n<p>So, at this point, every time WP sees a line matching <tt>http:\/\/twitter.com\/*\/status\/*<\/tt>, it will ask <tt>yourblog.com\/blog\/?ozh_tweet=1&url=the_tweet_url&some_other_var=...<\/tt> what to do with it. So, what to do with it?<\/p>\n<h2>Handling the oEmbed request itself<\/h2>\n<p>Now for the fun part: the function that handles the oEmbed request and return the expected formatted output.<\/p>\n<p>Basically, we&#39;re going to:<\/p>\n<ol>\n<li>Guess the tweet id<\/li>\n<li>Poll the Twitter API for information about this tweet, get a JSON response<\/li>\n<li>Parse the JSON response, make something pretty with this it<\/li>\n<li>Return that pretty thing into a JSON response<\/li>\n<\/ol>\n<p>Vamos, muchachos:<\/p>\n<div class=\"igsh-code-box\" id=\"ig-sh-3\"><pre class=\"language-php line-numbers\" data-no-optimize=\"1\" data-cfasync=\"false\"><code class=\"language-php\">\/\/ Handle the oEmbed requests\r\n\/\/ The oEmbed request is something like:\r\n\/\/ yourblog.com\/blog\/?ozh_tweet=1&amp;url=http:\/\/twitter.com\/ozh\/statuses\/123456&amp;...\r\n\/\/\r\nfunction ozh_tweetembed_handle_oembed_request() {\r\n\t$url = esc_url( $_GET[&#039;url&#039;] );\r\n\tif( !$url )\r\n\t\tdie(&#039;URL missing&#039;);\r\n\t\r\n\tpreg_match( &#039;!http:\/\/twitter.com\/([^\/]+)\/status\/(\\d+)!&#039;, $url, $matches );\r\n\t$author  = $matches[1];\r\n\t$tweetid = $matches[2];\r\n\tunset( $matches );\r\n\t\r\n\t\/\/ From this point, we fetch content from Twitter and print a JSON string.\r\n\t\/\/ If something goes wrong (like, Twitter unreachable) then we&#039;ll simply print anything but JSON\r\n\t\/\/ and let WP handle the response.\r\n\t\r\n\t\/\/ fetch http:\/\/api.twitter.com\/1\/statuses\/show\/$tweet.json\r\n\t$apiurl = &#039;http:\/\/api.twitter.com\/1\/statuses\/show\/&#039;.$tweetid.&#039;.json&#039;;\r\n\tif ( !$result = wp_remote_retrieve_body( wp_remote_get( $apiurl, array(&#039;timeout&#039; =&gt; 15) ) ) )\r\n\t\tdie( &quot;could not fetch $apiurl&quot; );\r\n\r\n\t\/\/ Check that JSON is well formed\r\n\t$result = trim( $result );\r\n\tif ( !$data = json_decode($result) )\r\n\t\tdie( &quot;Data was not JSON&quot; );\r\n\t\r\n\t\/\/ Now extract a few variables from the $data object\r\n\t$text = $data-&gt;text;\r\n\t$created_at = date(&#039;d M Y g:i a&#039;, strtotime( $data-&gt;created_at ) );\r\n\tif( !empty($data-&gt;source) )\r\n\t\t$source = &#039;via &#039;.$data-&gt;source;\r\n\tif( !empty($data-&gt;in_reply_to_screen_name) ) {\r\n\t\t$in_reply_to_screen_name = $data-&gt;in_reply_to_screen_name;\r\n\t\t$in_reply_to_status_id   = $data-&gt;in_reply_to_status_id;\r\n\t\t$inreply = &quot;in reply to &lt;a href=&#039;http:\/\/twitter.com\/$in_reply_to_screen_name\/status\/$in_reply_to_status_id&#039;&gt;@$in_reply_to_screen_name&lt;\/a&gt;&quot;;\r\n\t}\r\n\t$name = $data-&gt;user-&gt;name;\r\n\t$screen_name = $data-&gt;user-&gt;screen_name;\r\n\t$profile_image = $data-&gt;user-&gt;profile_image_url;\r\n\t\r\n\t\/\/ Make up some HTML with the tweet info\r\n\t$html = &lt;&lt;&lt;HTML\r\n&lt;div class=&quot;tweet&quot;&gt;\r\n\t&lt;div class=&quot;text&quot;&gt;$text&lt;\/div&gt;\r\n\t&lt;div class=&quot;meta&quot;&gt;$created_at $source $inreply&lt;\/div&gt;\r\n\t&lt;hr\/&gt;\r\n\t&lt;div class=&quot;pic&quot;&gt;&lt;a href=&quot;http:\/\/twitter.com\/$screen_name&quot;&gt;&lt;img src=&quot;$profile_image&quot;&gt;$name&lt;\/a&gt;&lt;\/div&gt;\r\n&lt;\/div&gt;\r\nHTML;\r\n\r\n\t\/\/ Put everything into a regular array\r\n\t$json = array(\r\n\t\t&#039;version&#039; =&gt; &#039;1.0&#039;,\r\n\t\t&#039;type&#039;    =&gt; &#039;rich&#039;,\r\n\t\t&#039;width&#039;   =&gt; 1337, \/\/ this parameter is mandatory according to the oEmbed specs, but we&#039;re not using it\r\n\t\t&#039;height&#039;  =&gt; 1337, \/\/ same.\r\n\t\t&#039;html&#039;    =&gt; trim( $html ),\r\n\t);\r\n\t\r\n\t\/\/ Now output a JSON response\t\r\n\theader(&#039;Cache-Control: no-cache, must-revalidate&#039;);\r\n\theader(&#039;Expires: Mon, 26 Jul 1997 05:00:00 GMT&#039;);\r\n\theader(&#039;Content-type: application\/json&#039;);\r\n\theader(&#039;Content-Disposition: attachment; filename=&quot;oembed.json&quot;&#039;);\r\n\techo json_encode( $json );\r\n}<\/code><\/pre><\/div>\n<p>And&#8230; That&#39;s it. Now just paste a tweet URL and let WordPress do the job. For your convenience, get the whole plugin we&#39;ve just dissected in a single file<\/p>\n<div class=\"download\">\nDownload the plugin <a href=\"https:\/\/planetozh.com\/blog\/wp-content\/uploads\/2010\/05\/ozh-oembed-tweet.php_.txt\">ozh-oembed-tweet.php.txt<\/a><br \/>\n(save as .php then put in your plugins folder)\n<\/div>\n<h2>Further improvements and remarks<\/h2>\n<p>This simple &quot;proof of concept&quot; plugin returns very basic HTML, you&#39;d need to make it prettier (use the tweet background, CSS style it). Just modify the &quot;$html =&quot; starting at line 44 or, even cooler, add an interface to the plugin where you can define a template for the tweet output. If you want some inspiration, I do something like this in my plugin <a href=\"https:\/\/planetozh.com\/blog\/my-projects\/wordpress-plugin-better-feed-rss\/\">Better Feed<\/a> where the user can customize feed item footer with a simple templating system.<\/p>\n<p>When WordPress makes an oEmbed request, it does it once and stores its result in the postmeta table. Twitter has that feature we all know and don&#39;t care about any longer: unreliability. Notice the timeout parameter I&#39;ve added to the remote request (line 21), this is really needed. Still, if you blog cannot fetch the tweet from Twitter&#39;s API, it will store a dummy postmeta entry (<tt>{{unknown}}<\/tt>) and won&#39;t replace the tweet URL. A proper plugin should test for the postmeta value and re-poll the Twitter API if applicable.<\/p>\n<p>Twitter does not support oEmbed, but another site does it for them: <a href=\"http:\/\/oohembed.com\/\">Ooh Embed<\/a>. So, technically, a plugin to embed tweets using the oEmbed API would require just three lines, register oohembed.com as a producer. But that wouldn&#39;t be fun :) (and I don&#39;t know how reliable is oohembed.com)<\/p>\n<p>Technically again, you could use more efficiently function <tt>wp_embed_register_handler()<\/tt> to deal with the oEmbed request without having to sending an extra HTTP request to your own site. But that would probably make a confusing example of the general mechanism of the API, with a consumer sending a request to a producer.<\/p>\n<p>Any other comment or idea of something cool to do with Twitter and oEmbed? Use the comment form <span title=\"low pitched voice\">belllowwww<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A few days ago, Twitter published a new tool that lets you embed a tweet on your site, simplifying the old school way: take a screenshot, crop the picture, upload it, embed it. The problem is: this Twitter tool is way too lame. Basically you need to cut and paste a lengthy code snippet full of HTML and CSS. How\u2026<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21],"tags":[367,371,85,292,245],"class_list":["post-1398","post","type-post","status-publish","format-standard","hentry","category-published","tag-how-to","tag-oembed","tag-plugins","tag-twitter","tag-wordpress"],"_links":{"self":[{"href":"https:\/\/planetozh.com\/blog\/wp-json\/wp\/v2\/posts\/1398","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/planetozh.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/planetozh.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/planetozh.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/planetozh.com\/blog\/wp-json\/wp\/v2\/comments?post=1398"}],"version-history":[{"count":0,"href":"https:\/\/planetozh.com\/blog\/wp-json\/wp\/v2\/posts\/1398\/revisions"}],"wp:attachment":[{"href":"https:\/\/planetozh.com\/blog\/wp-json\/wp\/v2\/media?parent=1398"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/planetozh.com\/blog\/wp-json\/wp\/v2\/categories?post=1398"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/planetozh.com\/blog\/wp-json\/wp\/v2\/tags?post=1398"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}