X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FContent%2FText%2FHTML.php;h=c127973b13b8493837844b770c8762694f28785f;hb=a947bd0889cfb2eb58692f92ab19a875019d3b79;hp=d6e699ed7b90e0b894419a823c911cdd08de8bcf;hpb=d0780ccf7dd3efb5337912dcf293617bea495d2a;p=friendica.git diff --git a/src/Content/Text/HTML.php b/src/Content/Text/HTML.php index d6e699ed7b..c127973b13 100644 --- a/src/Content/Text/HTML.php +++ b/src/Content/Text/HTML.php @@ -1,5 +1,4 @@ query("//" . $tag); foreach ($list as $node) { $attr = []; @@ -91,9 +99,12 @@ class HTML $node->parentNode->insertBefore($StartCode, $node); if ($node->hasChildNodes()) { + /** @var \DOMNode $child */ foreach ($node->childNodes as $child) { - $newNode = $child->cloneNode(true); - $node->parentNode->insertBefore($newNode, $node); + if (trim($child->nodeValue)) { + $newNode = $child->cloneNode(true); + $node->parentNode->insertBefore($newNode, $node); + } } } @@ -108,12 +119,13 @@ class HTML /** * Made by: ike@piratenpartei.de * Originally made for the syncom project: http://wiki.piratenpartei.de/Syncom - * https://github.com/annando/Syncom + * https://github.com/annando/Syncom * * @brief Converter for HTML to BBCode * @param string $message * @param string $basepath * @return string + * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function toBBCode($message, $basepath = '') { @@ -122,7 +134,7 @@ class HTML // Removing code blocks before the whitespace removal processing below $codeblocks = []; $message = preg_replace_callback( - '#
(.*)
#iUs', + '#
(.*)
#iUs', function ($matches) use (&$codeblocks) { $return = '[codeblock-' . count($codeblocks) . ']'; @@ -131,7 +143,7 @@ class HTML $prefix = '[code=' . $matches[1] . ']'; } - $codeblocks[] = $prefix . trim($matches[2]) . '[/code]'; + $codeblocks[] = $prefix . PHP_EOL . trim($matches[2]) . PHP_EOL . '[/code]'; return $return; }, $message @@ -139,12 +151,14 @@ class HTML $message = str_replace( [ - "
  • ", - "

  • ", - ], [ - "
  • ", - "
  • ", - ], $message + "
  • ", + "

  • ", + ], + [ + "
  • ", + "
  • ", + ], + $message ); // remove namespaces @@ -168,7 +182,9 @@ class HTML $xpath = new DomXPath($doc); $list = $xpath->query("//pre"); foreach ($list as $node) { - $node->nodeValue = str_replace("\n", "\r", $node->nodeValue); + // Ensure to escape unescaped & - they will otherwise raise a warning + $safe_value = preg_replace('/&(?!\w+;)/', '&', $node->nodeValue); + $node->nodeValue = str_replace("\n", "\r", $safe_value); } $message = $doc->saveHTML(); @@ -183,8 +199,13 @@ class HTML self::tagToBBCode($doc, 'p', ['class' => 'MsoNormal', 'style' => 'margin-left:35.4pt'], '[quote]', '[/quote]'); // Outlook-Quote - Variant 2 - self::tagToBBCode($doc, 'div', ['style' => 'border:none;border-left:solid blue 1.5pt;padding:0cm 0cm 0cm 4.0pt'], - '[quote]', '[/quote]'); + self::tagToBBCode( + $doc, + 'div', + ['style' => 'border:none;border-left:solid blue 1.5pt;padding:0cm 0cm 0cm 4.0pt'], + '[quote]', + '[/quote]' + ); // MyBB-Stuff self::tagToBBCode($doc, 'span', ['style' => 'text-decoration: underline;'], '[u]', '[/u]'); @@ -273,8 +294,8 @@ class HTML self::tagToBBCode($doc, 'a', ['href' => '/mailto:(.+)/'], '[mail=$1]', '[/mail]'); self::tagToBBCode($doc, 'a', ['href' => '/(.+)/'], '[url=$1]', '[/url]'); - self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'width' => '/(\d+)/', 'height' => '/(\d+)/'], '[img=$2x$3]$1', - '[/img]'); + self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'alt' => '/(.+)/'], '[img=$1]$2', '[/img]'); + self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'width' => '/(\d+)/', 'height' => '/(\d+)/'], '[img=$2x$3]$1', '[/img]'); self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], '[img]$1', '[/img]'); @@ -297,7 +318,7 @@ class HTML $message = preg_replace('=\r *\r=i', "\n", $message); $message = str_replace("\r", "\n", $message); - Addon::callHooks('html2bbcode', $message); + Hook::callAll('html2bbcode', $message); $message = strip_tags($message); @@ -343,12 +364,15 @@ class HTML "[/", "[list]", "[list=1]", - "[*]"], $message + "[*]"], + $message ); } while ($message != $oldmessage); $message = str_replace( - ['[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'], ['[b]', '[/b]', '[i]', '[/i]'], $message + ['[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'], + ['[b]', '[/b]', '[i]', '[/i]'], + $message ); // Handling Yahoo style of mails @@ -423,7 +447,8 @@ class HTML foreach ($matches as $match) { $body = preg_replace_callback( - $match, function ($match) use ($basepath) { + $match, + function ($match) use ($basepath) { return self::qualifyURLsSub($match, $basepath); }, $body @@ -520,7 +545,7 @@ class HTML // A list of some links that should be ignored $list = ["/user/", "/tag/", "/group/", "/profile/", "/search?search=", "/search?tag=", "mailto:", "/u/", "/node/", - "//facebook.com/profile.php?id=", "//plus.google.com/", "//twitter.com/"]; + "//plus.google.com/", "//twitter.com/"]; foreach ($list as $listitem) { if (strpos($treffer[1], $listitem) !== false) { $ignore = true; @@ -539,6 +564,8 @@ class HTML $ignore = false; } + $ignore = $ignore || strpos($treffer[1], '#') === 0; + if (!$ignore) { $urls[$treffer[1]] = $treffer[1]; } @@ -547,7 +574,13 @@ class HTML return $urls; } - public static function toPlaintext($html, $wraplength = 75, $compact = false) + /** + * @param string $html + * @param int $wraplength Ensures individual lines aren't longer than this many characters. Doesn't break words. + * @param bool $compact True: Completely strips image tags; False: Keeps image URLs + * @return string + */ + public static function toPlaintext(string $html, $wraplength = 75, $compact = false) { $message = str_replace("\r", "", $html); @@ -556,38 +589,20 @@ class HTML $message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8"); - @$doc->loadHTML($message); - - $xpath = new DOMXPath($doc); - $list = $xpath->query("//pre"); - foreach ($list as $node) { - $node->nodeValue = str_replace("\n", "\r", $node->nodeValue); - } + @$doc->loadHTML($message, LIBXML_HTML_NODEFDTD); $message = $doc->saveHTML(); - $message = str_replace(["\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"], ["<", ">", "
    ", " ", ""], $message); - $message = preg_replace('= [\s]*=i', " ", $message); + // Remove eventual UTF-8 BOM + $message = str_replace("\xC3\x82\xC2\xA0", "", $message); // Collecting all links $urls = self::collectURLs($message); - @$doc->loadHTML($message); + @$doc->loadHTML($message, LIBXML_HTML_NODEFDTD); self::tagToBBCode($doc, 'html', [], '', ''); self::tagToBBCode($doc, 'body', [], '', ''); - // MyBB-Auszeichnungen - /* - self::node2BBCode($doc, 'span', array('style'=>'text-decoration: underline;'), '_', '_'); - self::node2BBCode($doc, 'span', array('style'=>'font-style: italic;'), '/', '/'); - self::node2BBCode($doc, 'span', array('style'=>'font-weight: bold;'), '*', '*'); - - self::node2BBCode($doc, 'strong', array(), '*', '*'); - self::node2BBCode($doc, 'b', array(), '*', '*'); - self::node2BBCode($doc, 'i', array(), '/', '/'); - self::node2BBCode($doc, 'u', array(), '_', '_'); - */ - if ($compact) { self::tagToBBCode($doc, 'blockquote', [], "»", "«"); } else { @@ -601,8 +616,6 @@ class HTML self::tagToBBCode($doc, 'div', [], "\r", "\r"); self::tagToBBCode($doc, 'p', [], "\n", "\n"); - //self::node2BBCode($doc, 'ul', array(), "\n[list]", "[/list]\n"); - //self::node2BBCode($doc, 'ol', array(), "\n[list=1]", "[/list]\n"); self::tagToBBCode($doc, 'li', [], "\n* ", "\n"); self::tagToBBCode($doc, 'hr', [], "\n" . str_repeat("-", 70) . "\n", ""); @@ -617,12 +630,6 @@ class HTML self::tagToBBCode($doc, 'h5', [], "\n\n*", "*\n"); self::tagToBBCode($doc, 'h6', [], "\n\n*", "*\n"); - // Problem: there is no reliable way to detect if it is a link to a tag or profile - //self::node2BBCode($doc, 'a', array('href'=>'/(.+)/'), ' $1 ', ' ', true); - //self::node2BBCode($doc, 'a', array('href'=>'/(.+)/', 'rel'=>'oembed'), ' $1 ', '', true); - //self::node2BBCode($doc, 'img', array('alt'=>'/(.+)/'), '$1', ''); - //self::node2BBCode($doc, 'img', array('title'=>'/(.+)/'), '$1', ''); - //self::node2BBCode($doc, 'img', array(), '', ''); if (!$compact) { self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], ' [img]$1', '[/img] '); } else { @@ -672,4 +679,277 @@ class HTML return trim($message); } + + /** + * Converts provided HTML code to Markdown. The hardwrap parameter maximizes + * compatibility with Diaspora in spite of the Markdown standards. + * + * @param string $html + * @return string + */ + public static function toMarkdown($html) + { + $converter = new HtmlConverter(['hard_break' => true]); + $markdown = $converter->convert($html); + + return $markdown; + } + + /** + * @brief Convert video HTML to BBCode tags + * + * @param string $s + * @return string + */ + public static function toBBCodeVideo($s) + { + $s = preg_replace( + '#]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)#ism', + '[youtube]$2[/youtube]', + $s + ); + + $s = preg_replace( + '#](.*?)https?://www.youtube.com/embed/([A-Za-z0-9\-_=]+)(.*?)#ism', + '[youtube]$2[/youtube]', + $s + ); + + $s = preg_replace( + '#](.*?)https?://player.vimeo.com/video/([0-9]+)(.*?)#ism', + '[vimeo]$2[/vimeo]', + $s + ); + + return $s; + } + + /** + * transform link href and img src from relative to absolute + * + * @param string $text + * @param string $base base url + * @return string + */ + public static function relToAbs($text, $base) + { + if (empty($base)) { + return $text; + } + + $base = rtrim($base, '/'); + + $base2 = $base . "/"; + + // Replace links + $pattern = "/]*) href=\"(?!http|https|\/)([^\"]*)\"/"; + $replace = "'; + } + + /** + * Loader for infinite scrolling + * + * @return string html for loader + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function scrollLoader() + { + $tpl = Renderer::getMarkupTemplate("scroll_loader.tpl"); + return Renderer::replaceMacros($tpl, [ + 'wait' => L10n::t('Loading more entries...'), + 'end' => L10n::t('The end') + ]); + } + + /** + * Get html for contact block. + * + * @deprecated since version 2019.03 + * @see ContactBlock::getHTML() + * @return string + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public static function contactBlock() + { + $a = \get_app(); + + return ContactBlock::getHTML($a->profile); + } + + /** + * @brief Format contacts as picture links or as text links + * + * @param array $contact Array with contacts which contains an array with + * int 'id' => The ID of the contact + * int 'uid' => The user ID of the user who owns this data + * string 'name' => The name of the contact + * string 'url' => The url to the profile page of the contact + * string 'addr' => The webbie of the contact (e.g.) username@friendica.com + * string 'network' => The network to which the contact belongs to + * string 'thumb' => The contact picture + * string 'click' => js code which is performed when clicking on the contact + * @param boolean $redirect If true try to use the redir url if it's possible + * @param string $class CSS class for the + * @param boolean $textmode If true display the contacts as text links + * if false display the contacts as picture links + * @return string Formatted html + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public static function micropro($contact, $redirect = false, $class = '', $textmode = false) + { + // Use the contact URL if no address is available + if (empty($contact['addr'])) { + $contact["addr"] = $contact["url"]; + } + + $url = $contact['url']; + $sparkle = ''; + $redir = false; + + if ($redirect) { + $url = Contact::magicLink($contact['url']); + if (strpos($url, 'redir/') === 0) { + $sparkle = ' sparkle'; + } + } + + // If there is some js available we don't need the url + if (!empty($contact['click'])) { + $url = ''; + } + + return Renderer::replaceMacros(Renderer::getMarkupTemplate(($textmode)?'micropro_txt.tpl':'micropro_img.tpl'), [ + '$click' => defaults($contact, 'click', ''), + '$class' => $class, + '$url' => $url, + '$photo' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB), + '$name' => $contact['name'], + 'title' => $contact['name'] . ' [' . $contact['addr'] . ']', + '$parkle' => $sparkle, + '$redir' => $redir + ]); + } + + /** + * Search box. + * + * @param string $s Search query. + * @param string $id HTML id + * @param string $url Search url. + * @param bool $aside Display the search widgit aside. + * + * @return string Formatted HTML. + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function search($s, $id = 'search-box', $url = 'search', $aside = true) + { + $mode = 'text'; + + if (strpos($s, '#') === 0) { + $mode = 'tag'; + } + $save_label = $mode === 'text' ? L10n::t('Save') : L10n::t('Follow'); + + $values = [ + '$s' => $s, + '$id' => $id, + '$action_url' => $url, + '$search_label' => L10n::t('Search'), + '$save_label' => $save_label, + '$savedsearch' => 'savedsearch', + '$search_hint' => L10n::t('@name, !forum, #tags, content'), + '$mode' => $mode + ]; + + if (!$aside) { + $values['$searchoption'] = [ + L10n::t("Full Text"), + L10n::t("Tags"), + L10n::t("Contacts")]; + + if (Config::get('system', 'poco_local_search')) { + $values['$searchoption'][] = L10n::t("Forums"); + } + } + + return Renderer::replaceMacros(Renderer::getMarkupTemplate('searchbox.tpl'), $values); + } + + /** + * Replace naked text hyperlink with HTML formatted hyperlink + * + * @param string $s + * @return string + */ + public static function toLink($s) + { + $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' $1', $s); + $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&\;(.*?)\>/ism", '<$1$2=$3&$4>', $s); + return $s; + } + + /** + * Given a HTML text and a set of filtering reasons, adds a content hiding header with the provided reasons + * + * Reasons are expected to have been translated already. + * + * @param string $html + * @param array $reasons + * @return string + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function applyContentFilter($html, array $reasons) + { + if (count($reasons)) { + $tpl = Renderer::getMarkupTemplate('wall/content_filter.tpl'); + $html = Renderer::replaceMacros($tpl, [ + '$reasons' => $reasons, + '$rnd' => Strings::getRandomHex(8), + '$openclose' => L10n::t('Click to open/close'), + '$html' => $html + ]); + } + + return $html; + } + + /** + * replace html amp entity with amp char + * @param string $s + * @return string + */ + public static function unamp($s) + { + return str_replace('&', '&', $s); + } }