]> git.mxchange.org Git - friendica.git/blobdiff - src/Content/OEmbed.php
Fixes for OEmbed class (#5392)
[friendica.git] / src / Content / OEmbed.php
index 0095d2b3cb33db58d50220322a3f5b63a34996ee..390f3ea868435a3c035d9f9f7e9deceab24f5fec 100644 (file)
-<?php\r
-\r
-/**\r
- * @file src/Content/OEmbed.php\r
- */\r
-\r
-namespace Friendica\Content;\r
-\r
-use Friendica\Core\Cache;\r
-use Friendica\Core\System;\r
-use Friendica\Core\Config;\r
-use Friendica\Database\DBM;\r
-use Friendica\Util\ParseUrl;\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
- * Handles all OEmbed content fetching and replacement\r
- *\r
- * OEmbed is a standard used to allow an embedded representation of a URL on\r
- * third party sites\r
- *\r
- * @see https://oembed.com\r
- *\r
- * @author Hypolite Petovan <mrpetovan@gmail.com>\r
- */\r
-class OEmbed\r
-{\r
-       public static function replaceCallback($matches)\r
-       {\r
-               $embedurl = $matches[1];\r
-               $j = self::fetchURL($embedurl);\r
-               $s = self::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 || $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 = $j->html;\r
-               $ret = '<div 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
-                               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
-                               if (self::isAllowedURL($embedurl)) {\r
-                                       $ret .= proxy_parse_html($jhtml);\r
-                               }\r
-                               break;\r
-               }\r
-\r
-               $ret .= '</div>';\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
-                       $ret .= "</h4>";\r
-               } elseif (!strpos($j->html, $embedurl)) {\r
-                       // add <a> for html2bbcode conversion\r
-                       $ret .= '<a href="' . $embedurl . '" rel="oembed">' . $j->title . '</a>';\r
-               }\r
-\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 = self::buildXPath("class", "oembed");\r
-                       $entries = $xpath->query("//div[$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 self::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
-        * @todo This function is currently unused until someoneā„¢ adds support for a separate OEmbed domain\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
-               $src = System::baseUrl() . '/oembed/' . base64url_encode($src);\r
-               return '<iframe onload="resizeIframe(this);" class="embed_rich" height="' . $height . '" width="' . $width . '" src="' . $src . '" 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
-       /**\r
-        * Determines if rich content OEmbed is allowed for the provided URL\r
-        *\r
-        * @brief Determines if rich content OEmbed is allowed for the provided URL\r
-        * @param string $url\r
-        * @return boolean\r
-        */\r
-       private static function isAllowedURL($url)\r
-       {\r
-               if (!Config::get('system', 'no_oembed_rich_content')) {\r
-                       return true;\r
-               }\r
-\r
-               $domain = parse_url($url, PHP_URL_HOST);\r
-\r
-               $str_allowed = Config::get('system', 'allowed_oembed', '');\r
-               $allowed = explode(',', $str_allowed);\r
-\r
-               return allowed_domain($domain, $allowed, true);\r
-       }\r
-}\r
+<?php
+
+/**
+ * @file src/Content/OEmbed.php
+ */
+namespace Friendica\Content;
+
+use Friendica\Core\Addon;
+use Friendica\Core\Cache;
+use Friendica\Core\Config;
+use Friendica\Core\L10n;
+use Friendica\Core\System;
+use Friendica\Database\DBM;
+use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Network;
+use Friendica\Util\ParseUrl;
+use dba;
+use DOMDocument;
+use DOMNode;
+use DOMText;
+use DOMXPath;
+use Exception;
+use stdClass;
+
+require_once 'include/dba.php';
+require_once 'mod/proxy.php';
+
+/**
+ * Handles all OEmbed content fetching and replacement
+ *
+ * OEmbed is a standard used to allow an embedded representation of a URL on
+ * third party sites
+ *
+ * @see https://oembed.com
+ *
+ * @author Hypolite Petovan <mrpetovan@gmail.com>
+ */
+class OEmbed
+{
+       public static function replaceCallback($matches)
+       {
+               $embedurl = $matches[1];
+               $j = self::fetchURL($embedurl, !self::isAllowedURL($embedurl));
+               $s = self::formatObject($j);
+
+               return $s;
+       }
+
+       /**
+        * @brief Get data from an URL to embed its content.
+        *
+        * @param string $embedurl The URL from which the data should be fetched.
+        * @param bool $no_rich_type If set to true rich type content won't be fetched.
+        *
+        * @return bool|object Returns object with embed content or false if no embeddable
+        *                         content exists
+        */
+       public static function fetchURL($embedurl, $no_rich_type = false)
+       {
+               $embedurl = trim($embedurl, "'");
+               $embedurl = trim($embedurl, '"');
+
+               $a = get_app();
+
+               $condition = ['url' => normalise_link($embedurl), 'maxwidth' => $a->videowidth];
+               $oembed = dba::selectFirst('oembed', ['content'], $condition);
+               if (DBM::is_result($oembed)) {
+                       $txt = $oembed["content"];
+               } else {
+                       $txt = Cache::get($a->videowidth . $embedurl);
+               }
+               // These media files should now be caught in bbcode.php
+               // left here as a fallback in case this is called from another source
+
+               $noexts = ["mp3", "mp4", "ogg", "ogv", "oga", "ogm", "webm"];
+               $ext = pathinfo(strtolower($embedurl), PATHINFO_EXTENSION);
+
+
+               if (is_null($txt)) {
+                       $txt = "";
+
+                       if (!in_array($ext, $noexts)) {
+                               // try oembed autodiscovery
+                               $redirects = 0;
+                               $html_text = Network::fetchUrl($embedurl, false, $redirects, 15, "text/*");
+                               if ($html_text) {
+                                       $dom = @DOMDocument::loadHTML($html_text);
+                                       if ($dom) {
+                                               $xpath = new DOMXPath($dom);
+                                               $entries = $xpath->query("//link[@type='application/json+oembed']");
+                                               foreach ($entries as $e) {
+                                                       $href = $e->getAttributeNode("href")->nodeValue;
+                                                       $txt = Network::fetchUrl($href . '&maxwidth=' . $a->videowidth);
+                                                       break;
+                                               }
+                                               $entries = $xpath->query("//link[@type='text/json+oembed']");
+                                               foreach ($entries as $e) {
+                                                       $href = $e->getAttributeNode("href")->nodeValue;
+                                                       $txt = Network::fetchUrl($href . '&maxwidth=' . $a->videowidth);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+
+                       $txt = trim($txt);
+
+                       if (!$txt || $txt[0] != "{") {
+                               $txt = '{"type":"error"}';
+                       } else { //save in cache
+                               $j = json_decode($txt);
+                               if (!empty($j->type) && $j->type != "error") {
+                                       dba::insert('oembed', [
+                                               'url' => normalise_link($embedurl),
+                                               'maxwidth' => $a->videowidth,
+                                               'content' => $txt,
+                                               'created' => DateTimeFormat::utcNow()
+                                       ], true);
+                               }
+
+                               Cache::set($a->videowidth . $embedurl, $txt, CACHE_DAY);
+                       }
+               }
+
+               $j = json_decode($txt);
+
+               if (!is_object($j)) {
+                       return false;
+               }
+
+               // Always embed the SSL version
+               if (isset($j->html)) {
+                       $j->html = str_replace(["http://www.youtube.com/", "http://player.vimeo.com/"], ["https://www.youtube.com/", "https://player.vimeo.com/"], $j->html);
+               }
+
+               $j->embedurl = $embedurl;
+
+               // If fetching information doesn't work, then improve via internal functions
+               if ($no_rich_type && ($j->type == "rich")) {
+                       $data = ParseUrl::getSiteinfoCached($embedurl, true, false);
+                       $j->type = $data["type"];
+
+                       if ($j->type == "photo") {
+                               $j->url = $data["url"];
+                       }
+
+                       if (isset($data["title"])) {
+                               $j->title = $data["title"];
+                       }
+
+                       if (isset($data["text"])) {
+                               $j->description = $data["text"];
+                       }
+
+                       if (is_array($data["images"])) {
+                               $j->thumbnail_url = $data["images"][0]["src"];
+                               $j->thumbnail_width = $data["images"][0]["width"];
+                               $j->thumbnail_height = $data["images"][0]["height"];
+                       }
+               }
+
+               Addon::callHooks('oembed_fetch_url', $embedurl, $j);
+
+               return $j;
+       }
+
+       private static function formatObject(stdClass $j)
+       {
+               $embedurl = $j->embedurl;
+               $jhtml = $j->html;
+               $ret = '<div class="oembed ' . $j->type . '">';
+
+               switch ($j->type) {
+                       case "video":
+                               if (isset($j->thumbnail_url)) {
+                                       $tw = (isset($j->thumbnail_width) && intval($j->thumbnail_width)) ? $j->thumbnail_width : 200;
+                                       $th = (isset($j->thumbnail_height) && intval($j->thumbnail_height)) ? $j->thumbnail_height : 180;
+                                       // make sure we don't attempt divide by zero, fallback is a 1:1 ratio
+                                       $tr = (($th) ? $tw / $th : 1);
+
+                                       $th = 120;
+                                       $tw = $th * $tr;
+                                       $tpl = get_markup_template('oembed_video.tpl');
+                                       $ret .= replace_macros($tpl, [
+                                               '$baseurl' => System::baseUrl(),
+                                               '$embedurl' => $embedurl,
+                                               '$escapedhtml' => base64_encode($jhtml),
+                                               '$tw' => $tw,
+                                               '$th' => $th,
+                                               '$turl' => $j->thumbnail_url,
+                                       ]);
+                               } else {
+                                       $ret = $jhtml;
+                               }
+                               break;
+                       case "photo":
+                               $ret .= '<img width="' . $j->width . '" src="' . proxy_url($j->url) . '">';
+                               break;
+                       case "link":
+                               break;
+                       case "rich":
+                               $ret .= proxy_parse_html($jhtml);
+                               break;
+               }
+
+               // add link to source if not present in "rich" type
+               if ($j->type != 'rich' || !strpos($j->html, $embedurl)) {
+                       $ret .= '<h4>';
+                       if (!empty($j->title)) {
+                               if (!empty($j->provider_name)) {
+                                       $ret .= $j->provider_name . ": ";
+                               }
+
+                               $ret .= '<a href="' . $embedurl . '" rel="oembed">' . $j->title . '</a>';
+                               if (!empty($j->author_name)) {
+                                       $ret .= ' (' . $j->author_name . ')';
+                               }
+                       } elseif (!empty($j->provider_name) || !empty($j->author_name)) {
+                               $embedlink = "";
+                               if (!empty($j->provider_name)) {
+                                       $embedlink .= $j->provider_name;
+                               }
+
+                               if (!empty($j->author_name)) {
+                                       if ($embedlink != "") {
+                                               $embedlink .= ": ";
+                                       }
+
+                                       $embedlink .= $j->author_name;
+                               }
+                               if (trim($embedlink) == "") {
+                                       $embedlink = $embedurl;
+                               }
+
+                               $ret .= '<a href="' . $embedurl . '" rel="oembed">' . $embedlink . '</a>';
+                       } else {
+                               $ret .= '<a href="' . $embedurl . '" rel="oembed">' . $embedurl . '</a>';
+                       }
+                       $ret .= "</h4>";
+               } elseif (!strpos($j->html, $embedurl)) {
+                       // add <a> for html2bbcode conversion
+                       $ret .= '<a href="' . $embedurl . '" rel="oembed">' . $j->title . '</a>';
+               }
+
+               $ret .= '</div>';
+
+               $ret = str_replace("\n", "", $ret);
+               return mb_convert_encoding($ret, 'HTML-ENTITIES', mb_detect_encoding($ret));
+       }
+
+       public static function BBCode2HTML($text)
+       {
+               $stopoembed = Config::get("system", "no_oembed");
+               if ($stopoembed == true) {
+                       return preg_replace("/\[embed\](.+?)\[\/embed\]/is", "<!-- oembed $1 --><i>" . L10n::t('Embedding disabled') . " : $1</i><!-- /oembed $1 -->", $text);
+               }
+               return preg_replace_callback("/\[embed\](.+?)\[\/embed\]/is", ['self', 'replaceCallback'], $text);
+       }
+
+       /**
+        * Find <span class='oembed'>..<a href='url' rel='oembed'>..</a></span>
+        * and replace it with [embed]url[/embed]
+        */
+       public static function HTML2BBCode($text)
+       {
+               // start parser only if 'oembed' is in text
+               if (strpos($text, "oembed")) {
+
+                       // convert non ascii chars to html entities
+                       $html_text = mb_convert_encoding($text, 'HTML-ENTITIES', mb_detect_encoding($text));
+
+                       // If it doesn't parse at all, just return the text.
+                       $dom = @DOMDocument::loadHTML($html_text);
+                       if (!$dom) {
+                               return $text;
+                       }
+                       $xpath = new DOMXPath($dom);
+
+                       $xattr = self::buildXPath("class", "oembed");
+                       $entries = $xpath->query("//div[$xattr]");
+
+                       $xattr = "@rel='oembed'"; //oe_build_xpath("rel","oembed");
+                       foreach ($entries as $e) {
+                               $href = $xpath->evaluate("a[$xattr]/@href", $e)->item(0)->nodeValue;
+                               if (!is_null($href)) {
+                                       $e->parentNode->replaceChild(new DOMText("[embed]" . $href . "[/embed]"), $e);
+                               }
+                       }
+                       return self::getInnerHTML($dom->getElementsByTagName("body")->item(0));
+               } else {
+                       return $text;
+               }
+       }
+
+       /**
+        * Determines if rich content OEmbed is allowed for the provided URL
+        *
+        * @brief Determines if rich content OEmbed is allowed for the provided URL
+        * @param string $url
+        * @return boolean
+        */
+       public static function isAllowedURL($url)
+       {
+               if (!Config::get('system', 'no_oembed_rich_content')) {
+                       return true;
+               }
+
+               $domain = parse_url($url, PHP_URL_HOST);
+               if (!x($domain)) {
+                       return false;
+               }
+
+               $str_allowed = Config::get('system', 'allowed_oembed', '');
+               if (!x($str_allowed)) {
+                       return false;
+               }
+
+               $allowed = explode(',', $str_allowed);
+
+               return Network::isDomainAllowed($domain, $allowed);
+       }
+
+       public static function getHTML($url, $title = null)
+       {
+               // Always embed the SSL version
+               $url = str_replace(["http://www.youtube.com/", "http://player.vimeo.com/"],
+                                       ["https://www.youtube.com/", "https://player.vimeo.com/"], $url);
+
+               $o = self::fetchURL($url, !self::isAllowedURL($url));
+
+               if (!is_object($o) || property_exists($o, 'type') && $o->type == 'error') {
+                       throw new Exception('OEmbed failed for URL: ' . $url);
+               }
+
+               if (x($title)) {
+                       $o->title = $title;
+               }
+
+               $html = self::formatObject($o);
+
+               return $html;
+       }
+
+       /**
+        * @brief Generates the iframe HTML for an oembed attachment.
+        *
+        * Width and height are given by the remote, and are regularly too small for
+        * the generated iframe.
+        *
+        * The width is entirely discarded for the actual width of the post, while fixed
+        * height is used as a starting point before the inevitable resizing.
+        *
+        * Since the iframe is automatically resized on load, there are no need for ugly
+        * and impractical scrollbars.
+        *
+        * @todo This function is currently unused until someoneā„¢ adds support for a separate OEmbed domain
+        *
+        * @param string $src Original remote URL to embed
+        * @param string $width
+        * @param string $height
+        * @return string formatted HTML
+        *
+        * @see oembed_format_object()
+        */
+       private static function iframe($src, $width, $height)
+       {
+               $a = get_app();
+
+               if (!$height || strstr($height, '%')) {
+                       $height = '200';
+               }
+               $width = '100%';
+
+               $src = System::baseUrl() . '/oembed/' . base64url_encode($src);
+               return '<iframe onload="resizeIframe(this);" class="embed_rich" height="' . $height . '" width="' . $width . '" src="' . $src . '" allowfullscreen scrolling="no" frameborder="no">' . L10n::t('Embedded content') . '</iframe>';
+       }
+
+       /**
+        * Generates an XPath query to select elements whose provided attribute contains
+        * the provided value in a space-separated list.
+        *
+        * @brief Generates attribute search XPath string
+        *
+        * @param string $attr Name of the attribute to seach
+        * @param string $value Value to search in a space-separated list
+        * @return string
+        */
+       private static function buildXPath($attr, $value)
+       {
+               // https://www.westhoffswelt.de/blog/2009/6/9/select-html-elements-with-more-than-one-css-class-using-xpath
+               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'";
+       }
+
+       /**
+        * Returns the inner XML string of a provided DOMNode
+        *
+        * @brief Returns the inner XML string of a provided DOMNode
+        *
+        * @param DOMNode $node
+        * @return string
+        */
+       private static function getInnerHTML(DOMNode $node)
+       {
+               $innerHTML = '';
+               $children = $node->childNodes;
+               foreach ($children as $child) {
+                       $innerHTML .= $child->ownerDocument->saveXML($child);
+               }
+               return $innerHTML;
+       }
+
+}