]> git.mxchange.org Git - friendica.git/commitdiff
Add Module\Oembed and Content\OEmbed
authorHypolite Petovan <mrpetovan@gmail.com>
Mon, 1 Jan 2018 01:58:09 +0000 (20:58 -0500)
committerHypolite Petovan <mrpetovan@gmail.com>
Mon, 1 Jan 2018 01:58:09 +0000 (20:58 -0500)
include/bbcode.php
src/Content/OEmbed.php [new file with mode: 0644]
src/Module/Oembed.php [new file with mode: 0644]
src/ParseUrl.php
src/Protocol/DFRN.php

index be59c180726860a0292f2e10ef0b9e574afbd56f..e672001937677848650fe8421fc72b439e022e44 100644 (file)
@@ -2,6 +2,7 @@
 
 use Friendica\App;
 use Friendica\Content\Smilies;
+use Friendica\Content\OEmbed;
 use Friendica\Core\Cache;
 use Friendica\Core\System;
 use Friendica\Core\Config;
@@ -232,7 +233,7 @@ function tryoembed($match) {
        $url = str_replace(array("http://www.youtube.com/", "http://player.vimeo.com/"),
                                array("https://www.youtube.com/", "https://player.vimeo.com/"), $url);
 
-       $o = oembed_fetch_url($url);
+       $o = OEmbed::fetchURL($url);
 
        if (!is_object($o)) {
                return $match[0];
@@ -246,7 +247,7 @@ function tryoembed($match) {
                return $match[0];
        }
 
-       $html = oembed_format_object($o);
+       $html = OEmbed::formatObject($o);
 
        return $html;
 }
@@ -1263,7 +1264,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $simplehtml = fa
 //     $Text = preg_replace("/\[youtube\](.*?)\[\/youtube\]/", '<object width="425" height="350" type="application/x-shockwave-flash" data="http://www.youtube.com/v/$1" ><param name="movie" value="http://www.youtube.com/v/$1"></param><!--[if IE]><embed src="http://www.youtube.com/v/$1" type="application/x-shockwave-flash" width="425" height="350" /><![endif]--></object>', $Text);
 
        // oembed tag
-       $Text = oembed_bbcode2html($Text);
+       $Text = OEmbed::BBCode2HTML($Text);
 
        // Avoid triple linefeeds through oembed
        $Text = str_replace("<br style='clear:left'></span><br /><br />", "<br style='clear:left'></span><br />", $Text);
diff --git a/src/Content/OEmbed.php b/src/Content/OEmbed.php
new file mode 100644 (file)
index 0000000..db2c130
--- /dev/null
@@ -0,0 +1,352 @@
+<?php\r
+\r
+/*\r
+ * To change this license header, choose License Headers in Project Properties.\r
+ * To change this template file, choose Tools | Templates\r
+ * and open the template in the editor.\r
+ */\r
+\r
+namespace Friendica\Content;\r
+\r
+use Friendica\Core\Cache;\r
+use Friendica\Core\System;\r
+use Friendica\ParseUrl;\r
+use Friendica\Core\Config;\r
+use Friendica\Database\DBM;\r
+use dba;\r
+use DOMDocument;\r
+use DOMXPath;\r
+use DOMNode;\r
+\r
+require_once 'include/dba.php';\r
+require_once 'mod/proxy.php';\r
+\r
+/**\r
+ * Description of OEmbed\r
+ *\r
+ * @author benlo\r
+ */\r
+class OEmbed\r
+{\r
+       public static function replaceCallback($matches)\r
+       {\r
+               $embedurl = $matches[1];\r
+               $j = OEmbed::fetchURL($embedurl);\r
+               $s = OEmbed::formatObject($j);\r
+\r
+               return $s;\r
+       }\r
+\r
+       /**\r
+        * @brief Get data from an URL to embed its content.\r
+        *\r
+        * @param string $embedurl The URL from which the data should be fetched.\r
+        * @param bool $no_rich_type If set to true rich type content won't be fetched.\r
+        *\r
+        * @return bool|object Returns object with embed content or false if no embedable\r
+        *       content exists\r
+        */\r
+       public static function fetchURL($embedurl, $no_rich_type = false)\r
+       {\r
+               $embedurl = trim($embedurl, "'");\r
+               $embedurl = trim($embedurl, '"');\r
+\r
+               $a = get_app();\r
+\r
+               $condition = array('url' => normalise_link($embedurl));\r
+               $r = dba::select('oembed', array('content'), $condition, array('limit' => 1));\r
+\r
+               if (DBM::is_result($r)) {\r
+                       $txt = $r["content"];\r
+               } else {\r
+                       $txt = Cache::get($a->videowidth . $embedurl);\r
+               }\r
+               // These media files should now be caught in bbcode.php\r
+               // left here as a fallback in case this is called from another source\r
+\r
+               $noexts = array("mp3", "mp4", "ogg", "ogv", "oga", "ogm", "webm");\r
+               $ext = pathinfo(strtolower($embedurl), PATHINFO_EXTENSION);\r
+\r
+\r
+               if (is_null($txt)) {\r
+                       $txt = "";\r
+\r
+                       if (!in_array($ext, $noexts)) {\r
+                               // try oembed autodiscovery\r
+                               $redirects = 0;\r
+                               $html_text = fetch_url($embedurl, false, $redirects, 15, "text/*");\r
+                               if ($html_text) {\r
+                                       $dom = @DOMDocument::loadHTML($html_text);\r
+                                       if ($dom) {\r
+                                               $xpath = new DOMXPath($dom);\r
+                                               $entries = $xpath->query("//link[@type='application/json+oembed']");\r
+                                               foreach ($entries as $e) {\r
+                                                       $href = $e->getAttributeNode("href")->nodeValue;\r
+                                                       $txt = fetch_url($href . '&maxwidth=' . $a->videowidth);\r
+                                                       break;\r
+                                               }\r
+                                               $entries = $xpath->query("//link[@type='text/json+oembed']");\r
+                                               foreach ($entries as $e) {\r
+                                                       $href = $e->getAttributeNode("href")->nodeValue;\r
+                                                       $txt = fetch_url($href . '&maxwidth=' . $a->videowidth);\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       $txt = trim($txt);\r
+\r
+                       if ($txt[0] != "{") {\r
+                               $txt = '{"type":"error"}';\r
+                       } else { //save in cache\r
+                               $j = json_decode($txt);\r
+                               if ($j->type != "error") {\r
+                                       dba::insert('oembed', array('url' => normalise_link($embedurl),\r
+                                               'content' => $txt, 'created' => datetime_convert()), true);\r
+                               }\r
+\r
+                               Cache::set($a->videowidth . $embedurl, $txt, CACHE_DAY);\r
+                       }\r
+               }\r
+\r
+               $j = json_decode($txt);\r
+\r
+               if (!is_object($j)) {\r
+                       return false;\r
+               }\r
+\r
+               // Always embed the SSL version\r
+               if (isset($j->html)) {\r
+                       $j->html = str_replace(array("http://www.youtube.com/", "http://player.vimeo.com/"), array("https://www.youtube.com/", "https://player.vimeo.com/"), $j->html);\r
+               }\r
+\r
+               $j->embedurl = $embedurl;\r
+\r
+               // If fetching information doesn't work, then improve via internal functions\r
+               if (($j->type == "error") || ($no_rich_type && ($j->type == "rich"))) {\r
+                       $data = ParseUrl::getSiteinfoCached($embedurl, true, false);\r
+                       $j->type = $data["type"];\r
+\r
+                       if ($j->type == "photo") {\r
+                               $j->url = $data["url"];\r
+                               //$j->width = $data["images"][0]["width"];\r
+                               //$j->height = $data["images"][0]["height"];\r
+                       }\r
+\r
+                       if (isset($data["title"])) {\r
+                               $j->title = $data["title"];\r
+                       }\r
+\r
+                       if (isset($data["text"])) {\r
+                               $j->description = $data["text"];\r
+                       }\r
+\r
+                       if (is_array($data["images"])) {\r
+                               $j->thumbnail_url = $data["images"][0]["src"];\r
+                               $j->thumbnail_width = $data["images"][0]["width"];\r
+                               $j->thumbnail_height = $data["images"][0]["height"];\r
+                       }\r
+               }\r
+\r
+               call_hooks('oembed_fetch_url', $embedurl, $j);\r
+\r
+               return $j;\r
+       }\r
+\r
+       public static function formatObject($j)\r
+       {\r
+               $embedurl = $j->embedurl;\r
+               $jhtml = OEmbed::iframe($j->embedurl, (isset($j->width) ? $j->width : null), (isset($j->height) ? $j->height : null));\r
+               $ret = "<span class='oembed " . $j->type . "'>";\r
+               switch ($j->type) {\r
+                       case "video":\r
+                               if (isset($j->thumbnail_url)) {\r
+                                       $tw = (isset($j->thumbnail_width) && intval($j->thumbnail_width)) ? $j->thumbnail_width : 200;\r
+                                       $th = (isset($j->thumbnail_height) && intval($j->thumbnail_height)) ? $j->thumbnail_height : 180;\r
+                                       // make sure we don't attempt divide by zero, fallback is a 1:1 ratio\r
+                                       $tr = (($th) ? $tw / $th : 1);\r
+\r
+                                       $th = 120;\r
+                                       $tw = $th * $tr;\r
+                                       $tpl = get_markup_template('oembed_video.tpl');\r
+                                       $ret.=replace_macros($tpl, array(\r
+                                               '$baseurl' => System::baseUrl(),\r
+                                               '$embedurl' => $embedurl,\r
+                                               '$escapedhtml' => base64_encode($jhtml),\r
+                                               '$tw' => $tw,\r
+                                               '$th' => $th,\r
+                                               '$turl' => $j->thumbnail_url,\r
+                                       ));\r
+                               } else {\r
+                                       $ret = $jhtml;\r
+                               }\r
+                               //$ret.="<br>";\r
+                               break;\r
+                       case "photo":\r
+                               $ret.= "<img width='" . $j->width . "' src='" . proxy_url($j->url) . "'>";\r
+                               break;\r
+                       case "link":\r
+                               break;\r
+                       case "rich":\r
+                               // not so safe..\r
+                               if (!Config::get("system", "no_oembed_rich_content")) {\r
+                                       $ret.= proxy_parse_html($jhtml);\r
+                               }\r
+                               break;\r
+               }\r
+\r
+               // add link to source if not present in "rich" type\r
+               if ($j->type != 'rich' || !strpos($j->html, $embedurl)) {\r
+                       $ret .= "<h4>";\r
+                       if (isset($j->title)) {\r
+                               if (isset($j->provider_name)) {\r
+                                       $ret .= $j->provider_name . ": ";\r
+                               }\r
+\r
+                               $embedlink = (isset($j->title)) ? $j->title : $embedurl;\r
+                               $ret .= "<a href='$embedurl' rel='oembed'>$embedlink</a>";\r
+                               if (isset($j->author_name)) {\r
+                                       $ret.=" (" . $j->author_name . ")";\r
+                               }\r
+                       } elseif (isset($j->provider_name) || isset($j->author_name)) {\r
+                               $embedlink = "";\r
+                               if (isset($j->provider_name)) {\r
+                                       $embedlink .= $j->provider_name;\r
+                               }\r
+\r
+                               if (isset($j->author_name)) {\r
+                                       if ($embedlink != "") {\r
+                                               $embedlink .= ": ";\r
+                                       }\r
+\r
+                                       $embedlink .= $j->author_name;\r
+                               }\r
+                               if (trim($embedlink) == "") {\r
+                                       $embedlink = $embedurl;\r
+                               }\r
+\r
+                               $ret .= "<a href='$embedurl' rel='oembed'>$embedlink</a>";\r
+                       }\r
+                       //if (isset($j->author_name)) $ret.=" by ".$j->author_name;\r
+                       //if (isset($j->provider_name)) $ret.=" on ".$j->provider_name;\r
+                       $ret .= "</h4>";\r
+               } else {\r
+                       // add <a> for html2bbcode conversion\r
+                       $ret .= "<a href='$embedurl' rel='oembed'>$embedurl</a>";\r
+               }\r
+               $ret.="</span>";\r
+               $ret = str_replace("\n", "", $ret);\r
+               return mb_convert_encoding($ret, 'HTML-ENTITIES', mb_detect_encoding($ret));\r
+       }\r
+\r
+       public static function BBCode2HTML($text)\r
+       {\r
+               $stopoembed = Config::get("system", "no_oembed");\r
+               if ($stopoembed == true) {\r
+                       return preg_replace("/\[embed\](.+?)\[\/embed\]/is", "<!-- oembed $1 --><i>" . t('Embedding disabled') . " : $1</i><!-- /oembed $1 -->", $text);\r
+               }\r
+               return preg_replace_callback("/\[embed\](.+?)\[\/embed\]/is", ['self', 'replaceCallback'], $text);\r
+       }\r
+\r
+       /**\r
+        * Find <span class='oembed'>..<a href='url' rel='oembed'>..</a></span>\r
+        * and replace it with [embed]url[/embed]\r
+        */\r
+       public static function HTML2BBCode($text)\r
+       {\r
+               // start parser only if 'oembed' is in text\r
+               if (strpos($text, "oembed")) {\r
+\r
+                       // convert non ascii chars to html entities\r
+                       $html_text = mb_convert_encoding($text, 'HTML-ENTITIES', mb_detect_encoding($text));\r
+\r
+                       // If it doesn't parse at all, just return the text.\r
+                       $dom = @DOMDocument::loadHTML($html_text);\r
+                       if (!$dom) {\r
+                               return $text;\r
+                       }\r
+                       $xpath = new DOMXPath($dom);\r
+\r
+                       $xattr = OEmbed::buildXPath("class", "oembed");\r
+                       $entries = $xpath->query("//span[$xattr]");\r
+\r
+                       $xattr = "@rel='oembed'"; //oe_build_xpath("rel","oembed");\r
+                       foreach ($entries as $e) {\r
+                               $href = $xpath->evaluate("a[$xattr]/@href", $e)->item(0)->nodeValue;\r
+                               if (!is_null($href)) {\r
+                                       $e->parentNode->replaceChild(new DOMText("[embed]" . $href . "[/embed]"), $e);\r
+                               }\r
+                       }\r
+                       return OEmbed::getInnerHTML($dom->getElementsByTagName("body")->item(0));\r
+               } else {\r
+                       return $text;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * @brief Generates the iframe HTML for an oembed attachment.\r
+        *\r
+        * Width and height are given by the remote, and are regularly too small for\r
+        * the generated iframe.\r
+        *\r
+        * The width is entirely discarded for the actual width of the post, while fixed\r
+        * height is used as a starting point before the inevitable resizing.\r
+        *\r
+        * Since the iframe is automatically resized on load, there are no need for ugly\r
+        * and impractical scrollbars.\r
+        *\r
+        * @param string $src Original remote URL to embed\r
+        * @param string $width\r
+        * @param string $height\r
+        * @return string formatted HTML\r
+        *\r
+        * @see oembed_format_object()\r
+        */\r
+       private static function iframe($src, $width, $height)\r
+       {\r
+               $a = get_app();\r
+\r
+               if (!$height || strstr($height, '%')) {\r
+                       $height = '200';\r
+               }\r
+               $width = '100%';\r
+\r
+               $s = System::baseUrl() . '/oembed/' . base64url_encode($src);\r
+               return '<iframe onload="resizeIframe(this);" class="embed_rich" height="' . $height . '" width="' . $width . '" src="' . $s . '" allowfullscreen scrolling="no" frameborder="no">' . t('Embedded content') . '</iframe>';\r
+       }\r
+\r
+       /**\r
+        * Generates an XPath query to select elements whose provided attribute contains\r
+        * the provided value in a space-separated list.\r
+        *\r
+        * @brief Generates attribute search XPath string\r
+        *\r
+        * @param string $attr Name of the attribute to seach\r
+        * @param string $value Value to search in a space-separated list\r
+        * @return string\r
+        */\r
+       private static function buildXPath($attr, $value)\r
+       {\r
+               // https://www.westhoffswelt.de/blog/2009/6/9/select-html-elements-with-more-than-one-css-class-using-xpath\r
+               return "contains( normalize-space( @$attr ), ' $value ' ) or substring( normalize-space( @$attr ), 1, string-length( '$value' ) + 1 ) = '$value ' or substring( normalize-space( @$attr ), string-length( @$attr ) - string-length( '$value' ) ) = ' $value' or @$attr = '$value'";\r
+       }\r
+\r
+       /**\r
+        * Returns the inner XML string of a provided DOMNode\r
+        *\r
+        * @brief Returns the inner XML string of a provided DOMNode\r
+        *\r
+        * @param DOMNode $node\r
+        * @return string\r
+        */\r
+       private static function getInnerHTML(DOMNode $node)\r
+       {\r
+               $innerHTML = '';\r
+               $children = $node->childNodes;\r
+               foreach ($children as $child) {\r
+                       $innerHTML .= $child->ownerDocument->saveXML($child);\r
+               }\r
+               return $innerHTML;\r
+       }\r
+}\r
diff --git a/src/Module/Oembed.php b/src/Module/Oembed.php
new file mode 100644 (file)
index 0000000..f30fb86
--- /dev/null
@@ -0,0 +1,53 @@
+<?php\r
+\r
+namespace Friendica\Module;\r
+\r
+use Friendica\BaseModule;\r
+use Friendica\Content;\r
+\r
+/**\r
+ * Oembed module\r
+ *\r
+ * Displays stored embed content based on a base64 hash of a remote URL\r
+ *\r
+ * Example: /oembed/aHR0cHM6Ly9...\r
+ *\r
+ * @author Hypolite Petovan <mrpetovan@gmail.com>\r
+ */\r
+class Oembed extends BaseModule\r
+{\r
+       public static function content()\r
+       {\r
+               $a = self::getApp();\r
+\r
+               // Unused form: /oembed/b2h?url=...\r
+               if ($a->argv[1] == 'b2h') {\r
+                       $url = array("", trim(hex2bin($_REQUEST['url'])));\r
+                       echo Content\OEmbed::replaceCallback($url);\r
+                       killme();\r
+               }\r
+\r
+               // Unused form: /oembed/h2b?text=...\r
+               if ($a->argv[1] == 'h2b') {\r
+                       $text = trim(hex2bin($_REQUEST['text']));\r
+                       echo Content\OEmbed::HTML2BBCode($text);\r
+                       killme();\r
+               }\r
+\r
+               if ($a->argc == 2) {\r
+                       echo '<html><body>';\r
+                       $url = base64url_decode($a->argv[1]);\r
+                       $j = Content\OEmbed::fetchURL($url);\r
+\r
+                       // workaround for media.ccc.de (and any other endpoint that return size 0)\r
+                       if (substr($j->html, 0, 7) == "<iframe" && strstr($j->html, 'width="0"')) {\r
+                               $j->html = '<style>html,body{margin:0;padding:0;} iframe{width:100%;height:100%;}</style>' . $j->html;\r
+                               $j->html = str_replace('width="0"', '', $j->html);\r
+                               $j->html = str_replace('height="0"', '', $j->html);\r
+                       }\r
+                       echo $j->html;\r
+                       echo '</body></html>';\r
+               }\r
+               killme();\r
+       }\r
+}\r
index 9e46281ec91a73ec6d159be72dc84068c91aaeb1..6252e0b68a7b8d9ccc074ec60165357043e48b5d 100644 (file)
@@ -5,7 +5,7 @@
  */
 namespace Friendica;
 
-use Friendica\Core\Config;
+use Friendica\Content\OEmbed;
 use Friendica\Object\Image;
 use Friendica\Util\XML;
 
@@ -164,7 +164,7 @@ class ParseUrl
                $body = $data["body"];
 
                if ($do_oembed) {
-                       $oembed_data = oembed_fetch_url($url);
+                       $oembed_data = OEmbed::fetchURL($url);
 
                        if (!in_array($oembed_data->type, array("error", "rich", ""))) {
                                $siteinfo["type"] = $oembed_data->type;
index 5e9c91645bb6d567623786beffbf2be5bb3e31a3..1ec0c792e015850e24ed6565b1b594465c3a926d 100644 (file)
@@ -8,6 +8,7 @@
  */
 namespace Friendica\Protocol;
 
+use Friendica\Content\OEmbed;
 use Friendica\Core\Config;
 use Friendica\Core\System;
 use Friendica\Core\Worker;
@@ -2502,7 +2503,7 @@ class DFRN
 
                        $item['body'] = html2bb_video($item['body']);
 
-                       $item['body'] = oembed_html2bbcode($item['body']);
+                       $item['body'] = OEmbed::HTML2BBCode($item['body']);
 
                        $config = \HTMLPurifier_Config::createDefault();
                        $config->set('Cache.DefinitionImpl', null);