]> git.mxchange.org Git - friendica.git/blobdiff - src/Content/Text/HTML.php
Add Trending Tags widget + template
[friendica.git] / src / Content / Text / HTML.php
index 6e31697e07fa6fca3c28107e22edeb4cd827e81c..c127973b13b8493837844b770c8762694f28785f 100644 (file)
@@ -7,15 +7,15 @@ namespace Friendica\Content\Text;
 
 use DOMDocument;
 use DOMXPath;
-use Friendica\Core\Addon;
+use Friendica\Content\Widget\ContactBlock;
+use Friendica\Core\Hook;
 use Friendica\Core\L10n;
 use Friendica\Core\Config;
-use Friendica\Core\PConfig;
 use Friendica\Core\Renderer;
-use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Util\Network;
 use Friendica\Util\Proxy as ProxyUtils;
+use Friendica\Util\Strings;
 use Friendica\Util\XML;
 use League\HTMLToMarkdown\HtmlConverter;
 
@@ -56,6 +56,7 @@ class HTML
 
                $xpath = new DOMXPath($doc);
 
+               /** @var \DOMNode[] $list */
                $list = $xpath->query("//" . $tag);
                foreach ($list as $node) {
                        $attr = [];
@@ -98,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);
+                                               }
                                        }
                                }
 
@@ -115,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 = '')
        {
@@ -177,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();
@@ -287,6 +294,7 @@ class HTML
                self::tagToBBCode($doc, 'a', ['href' => '/mailto:(.+)/'], '[mail=$1]', '[/mail]');
                self::tagToBBCode($doc, 'a', ['href' => '/(.+)/'], '[url=$1]', '[/url]');
 
+               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]');
 
@@ -310,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);
 
@@ -556,6 +564,8 @@ class HTML
                                $ignore = false;
                        }
 
+                       $ignore = $ignore || strpos($treffer[1], '#') === 0;
+
                        if (!$ignore) {
                                $urls[$treffer[1]] = $treffer[1];
                        }
@@ -564,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);
 
@@ -573,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"], ["<", ">", "<br>", " ", ""], $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 {
@@ -618,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", "");
@@ -634,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 {
@@ -709,8 +699,9 @@ class HTML
         * @brief Convert video HTML to BBCode tags
         *
         * @param string $s
+        * @return string
         */
-       public static function htmlToBBVideo($s)
+       public static function toBBCodeVideo($s)
        {
                $s = preg_replace(
                        '#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism',
@@ -785,7 +776,9 @@ class HTML
 
        /**
         * Loader for infinite scrolling
+        *
         * @return string html for loader
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function scrollLoader()
        {
@@ -799,113 +792,43 @@ class HTML
        /**
         * Get html for contact block.
         *
-        * @template contact_block.tpl
-        * @hook contact_block_end (contacts=>array, output=>string)
+        * @deprecated since version 2019.03
+        * @see ContactBlock::getHTML()
         * @return string
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        * @throws \ImagickException
         */
        public static function contactBlock()
        {
-               $o = '';
-               $a = get_app();
-
-               $shown = PConfig::get($a->profile['uid'], 'system', 'display_friend_count', 24);
-               if ($shown == 0) {
-                       return;
-               }
-
-               if (!is_array($a->profile) || $a->profile['hide-friends']) {
-                       return $o;
-               }
-
-               $r = q("SELECT COUNT(*) AS `total` FROM `contact`
-                               WHERE `uid` = %d AND NOT `self` AND NOT `blocked`
-                                       AND NOT `pending` AND NOT `hidden` AND NOT `archive`
-                                       AND `network` IN ('%s', '%s', '%s')",
-                       intval($a->profile['uid']),
-                       DBA::escape(Protocol::DFRN),
-                       DBA::escape(Protocol::OSTATUS),
-                       DBA::escape(Protocol::DIASPORA)
-               );
-
-               if (DBA::isResult($r)) {
-                       $total = intval($r[0]['total']);
-               }
-
-               if (!$total) {
-                       $contacts = L10n::t('No contacts');
-                       $micropro = null;
-               } else {
-                       // Splitting the query in two parts makes it much faster
-                       $r = q("SELECT `id` FROM `contact`
-                                       WHERE `uid` = %d AND NOT `self` AND NOT `blocked`
-                                               AND NOT `pending` AND NOT `hidden` AND NOT `archive`
-                                               AND `network` IN ('%s', '%s', '%s')
-                                       ORDER BY RAND() LIMIT %d",
-                               intval($a->profile['uid']),
-                               DBA::escape(Protocol::DFRN),
-                               DBA::escape(Protocol::OSTATUS),
-                               DBA::escape(Protocol::DIASPORA),
-                               intval($shown)
-                       );
-
-                       if (DBA::isResult($r)) {
-                               $contacts = [];
-                               foreach ($r as $contact) {
-                                       $contacts[] = $contact["id"];
-                               }
-
-                               $r = q("SELECT `id`, `uid`, `addr`, `url`, `name`, `thumb`, `network` FROM `contact` WHERE `id` IN (%s)",
-                                       DBA::escape(implode(",", $contacts))
-                               );
+               $a = \get_app();
 
-                               if (DBA::isResult($r)) {
-                                       $contacts = L10n::tt('%d Contact', '%d Contacts', $total);
-                                       $micropro = [];
-                                       foreach ($r as $rr) {
-                                               $micropro[] = self::micropro($rr, true, 'mpfriend');
-                                       }
-                               }
-                       }
-               }
-
-               $tpl = Renderer::getMarkupTemplate('contact_block.tpl');
-               $o = Renderer::replaceMacros($tpl, [
-                       '$contacts' => $contacts,
-                       '$nickname' => $a->profile['nickname'],
-                       '$viewcontacts' => L10n::t('View Contacts'),
-                       '$micropro' => $micropro,
-               ]);
-
-               $arr = ['contacts' => $r, 'output' => $o];
-
-               Addon::callHooks('contact_block_end', $arr);
-
-               return $o;
+               return ContactBlock::getHTML($a->profile);
        }
 
        /**
-        * @brief Format contacts as picture links or as texxt links
+        * @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
-       */
+        * @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 (!x($contact, "addr")) {
+               if (empty($contact['addr'])) {
                        $contact["addr"] = $contact["url"];
                }
 
@@ -921,7 +844,7 @@ class HTML
                }
 
                // If there is some js available we don't need the url
-               if (x($contact, 'click')) {
+               if (!empty($contact['click'])) {
                        $url = '';
                }
 
@@ -943,12 +866,12 @@ class HTML
         * @param string $s     Search query.
         * @param string $id    HTML id
         * @param string $url   Search url.
-        * @param bool   $save  Show save search button.
         * @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', $save = false, $aside = true)
+       public static function search($s, $id = 'search-box', $url = 'search', $aside = true)
        {
                $mode = 'text';
 
@@ -958,12 +881,12 @@ class HTML
                $save_label = $mode === 'text' ? L10n::t('Save') : L10n::t('Follow');
 
                $values = [
-                               '$s' => htmlspecialchars($s),
+                               '$s' => $s,
                                '$id' => $id,
                                '$action_url' => $url,
                                '$search_label' => L10n::t('Search'),
                                '$save_label' => $save_label,
-                               '$savedsearch' => local_user() && Feature::isEnabled(local_user(), 'savedsearch'),
+                               '$savedsearch' => 'savedsearch',
                                '$search_hint' => L10n::t('@name, !forum, #tags, content'),
                                '$mode' => $mode
                        ];
@@ -986,6 +909,7 @@ class HTML
         * Replace naked text hyperlink with HTML formatted hyperlink
         *
         * @param string $s
+        * @return string
         */
        public static function toLink($s)
        {
@@ -1002,6 +926,7 @@ class HTML
         * @param string $html
         * @param array  $reasons
         * @return string
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function applyContentFilter($html, array $reasons)
        {
@@ -1009,7 +934,7 @@ class HTML
                        $tpl = Renderer::getMarkupTemplate('wall/content_filter.tpl');
                        $html = Renderer::replaceMacros($tpl, [
                                '$reasons'   => $reasons,
-                               '$rnd'       => random_string(8),
+                               '$rnd'       => Strings::getRandomHex(8),
                                '$openclose' => L10n::t('Click to open/close'),
                                '$html'      => $html
                        ]);