]> git.mxchange.org Git - friendica.git/blobdiff - src/Content/Text/BBCode.php
BBCode - fixed code styling
[friendica.git] / src / Content / Text / BBCode.php
index 778d95cedb8685c30279f8944ef57ffc02d57a44..b710dc13ec3640e2c830325f57612ba4c1d9052d 100644 (file)
@@ -11,9 +11,9 @@ use Exception;
 use Friendica\BaseObject;
 use Friendica\Content\OEmbed;
 use Friendica\Content\Smilies;
-use Friendica\Core\Addon;
 use Friendica\Core\Cache;
 use Friendica\Core\Config;
+use Friendica\Core\Hook;
 use Friendica\Core\L10n;
 use Friendica\Core\Logger;
 use Friendica\Core\Protocol;
@@ -28,6 +28,7 @@ use Friendica\Util\Network;
 use Friendica\Util\ParseUrl;
 use Friendica\Util\Proxy as ProxyUtils;
 use Friendica\Util\Strings;
+use Friendica\Util\XML;
 
 class BBCode extends BaseObject
 {
@@ -36,13 +37,14 @@ class BBCode extends BaseObject
         *
         * @param string $body Message body
         * @return array
-        * 'type' -> Message type ("link", "video", "photo")
-        * 'text' -> Text before the shared message
-        * 'after' -> Text after the shared message
-        * 'image' -> Preview image of the message
-        * 'url' -> Url to the attached message
-        * 'title' -> Title of the attachment
-        * 'description' -> Description of the attachment
+        *                     'type' -> Message type ("link", "video", "photo")
+        *                     'text' -> Text before the shared message
+        *                     'after' -> Text after the shared message
+        *                     'image' -> Preview image of the message
+        *                     'url' -> Url to the attached message
+        *                     'title' -> Title of the attachment
+        *                     'description' -> Description of the attachment
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        private static function getOldAttachmentData($body)
        {
@@ -107,13 +109,14 @@ class BBCode extends BaseObject
         *
         * @param string $body Message body
         * @return array
-        * 'type' -> Message type ("link", "video", "photo")
-        * 'text' -> Text before the shared message
-        * 'after' -> Text after the shared message
-        * 'image' -> Preview image of the message
-        * 'url' -> Url to the attached message
-        * 'title' -> Title of the attachment
-        * 'description' -> Description of the attachment
+        *                     'type' -> Message type ("link", "video", "photo")
+        *                     'text' -> Text before the shared message
+        *                     'after' -> Text after the shared message
+        *                     'image' -> Preview image of the message
+        *                     'url' -> Url to the attached message
+        *                     'title' -> Title of the attachment
+        *                     'description' -> Description of the attachment
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function getAttachmentData($body)
        {
@@ -129,12 +132,12 @@ class BBCode extends BaseObject
 
                $type = "";
                preg_match("/type='(.*?)'/ism", $attributes, $matches);
-               if (x($matches, 1)) {
+               if (!empty($matches[1])) {
                        $type = strtolower($matches[1]);
                }
 
                preg_match('/type="(.*?)"/ism', $attributes, $matches);
-               if (x($matches, 1)) {
+               if (!empty($matches[1])) {
                        $type = strtolower($matches[1]);
                }
 
@@ -152,12 +155,12 @@ class BBCode extends BaseObject
 
                $url = "";
                preg_match("/url='(.*?)'/ism", $attributes, $matches);
-               if (x($matches, 1)) {
+               if (!empty($matches[1])) {
                        $url = $matches[1];
                }
 
                preg_match('/url="(.*?)"/ism', $attributes, $matches);
-               if (x($matches, 1)) {
+               if (!empty($matches[1])) {
                        $url = $matches[1];
                }
 
@@ -167,12 +170,12 @@ class BBCode extends BaseObject
 
                $title = "";
                preg_match("/title='(.*?)'/ism", $attributes, $matches);
-               if (x($matches, 1)) {
+               if (!empty($matches[1])) {
                        $title = $matches[1];
                }
 
                preg_match('/title="(.*?)"/ism', $attributes, $matches);
-               if (x($matches, 1)) {
+               if (!empty($matches[1])) {
                        $title = $matches[1];
                }
 
@@ -185,12 +188,12 @@ class BBCode extends BaseObject
 
                $image = "";
                preg_match("/image='(.*?)'/ism", $attributes, $matches);
-               if (x($matches, 1)) {
+               if (!empty($matches[1])) {
                        $image = $matches[1];
                }
 
                preg_match('/image="(.*?)"/ism', $attributes, $matches);
-               if (x($matches, 1)) {
+               if (!empty($matches[1])) {
                        $image = $matches[1];
                }
 
@@ -200,12 +203,12 @@ class BBCode extends BaseObject
 
                $preview = "";
                preg_match("/preview='(.*?)'/ism", $attributes, $matches);
-               if (x($matches, 1)) {
+               if (!empty($matches[1])) {
                        $preview = $matches[1];
                }
 
                preg_match('/preview="(.*?)"/ism', $attributes, $matches);
-               if (x($matches, 1)) {
+               if (!empty($matches[1])) {
                        $preview = $matches[1];
                }
 
@@ -233,7 +236,7 @@ class BBCode extends BaseObject
                */
 
                $has_title = !empty($item['title']);
-               $plink = (!empty($item['plink']) ? $item['plink'] : '');
+               $plink = defaults($item, 'plink', '');
                $post = self::getAttachmentData($body);
 
                // if nothing is found, it maybe having an image.
@@ -344,6 +347,7 @@ class BBCode extends BaseObject
        /**
         * @brief Converts a BBCode text into plaintext
         *
+        * @param      $text
         * @param bool $keep_urls Whether to keep URLs in the resulting plaintext
         *
         * @return string
@@ -445,6 +449,7 @@ class BBCode extends BaseObject
         * @brief Truncates imported message body string length to max_import_size
         * @param string $body
         * @return string
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function limitBodySize($body)
        {
@@ -531,10 +536,11 @@ class BBCode extends BaseObject
         * Note: Can produce a [bookmark] tag in the returned string
         *
         * @brief Processes [attachment] tags
-        * @param string $return
+        * @param string   $return
         * @param bool|int $simplehtml
-        * @param bool $tryoembed
+        * @param bool     $tryoembed
         * @return string
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        private static function convertAttachment($return, $simplehtml = false, $tryoembed = true)
        {
@@ -625,7 +631,7 @@ class BBCode extends BaseObject
                        $data["title"] = $data["url"];
                }
 
-               if (($data["text"] == "") && ($data["title"] != "") && ($data["url"] == "")) {
+               if (empty($data["text"]) && !empty($data["title"]) && empty($data["url"])) {
                        return $data["title"] . $data["after"];
                }
 
@@ -776,10 +782,10 @@ class BBCode extends BaseObject
        /**
         * Performs a preg_replace within the boundaries of all named BBCode tags in a text
         *
-        * @param type $pattern Preg pattern string
-        * @param type $replace Preg replace string
-        * @param type $name    BBCode tag name
-        * @param type $text    Text to search
+        * @param string $pattern Preg pattern string
+        * @param string $replace Preg replace string
+        * @param string $name    BBCode tag name
+        * @param string $text    Text to search
         * @return string
         */
        public static function pregReplaceInTag($pattern, $replace, $name, $text)
@@ -929,6 +935,7 @@ class BBCode extends BaseObject
         * @param boolean $is_quote_share Whether there is content before the [share] block
         * @param integer $simplehtml     Mysterious integer value depending on the target network/formatting style
         * @return string
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        private static function convertShareCallback(array $attributes, array $author_contact, $content, $is_quote_share, $simplehtml)
        {
@@ -1141,8 +1148,9 @@ class BBCode extends BaseObject
         * @param int    $simple_html
         * @param bool   $for_plaintext
         * @return string
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
-       public static function convert($text, $try_oembed = true, $simple_html = false, $for_plaintext = false)
+       public static function convert($text, $try_oembed = true, $simple_html = 0, $for_plaintext = false)
        {
                $a = self::getApp();
 
@@ -1340,15 +1348,30 @@ class BBCode extends BaseObject
                $expression = "=diaspora://.*?/post/([0-9A-Za-z\-_@.:]{15,254}[0-9A-Za-z])=ism";
                $text = preg_replace($expression, System::baseUrl()."/display/$1", $text);
 
-               $text = preg_replace("/([#])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
-                                       '$1<a href="' . System::baseUrl() . '/search?tag=$3" class="tag" title="$3">$3</a>', $text);
-
-               $text = preg_replace("/\[url\=([$URLSearchString]*)\]#(.*?)\[\/url\]/ism",
-                                       '#<a href="' . System::baseUrl() . '/search?tag=$2" class="tag" title="$2">$2</a>', $text);
+               /* Tag conversion
+                * Supports:
+                * - #[url=<anything>]<term>[/url]
+                * - [url=<anything>]#<term>[/url]
+                */
+               $text = preg_replace_callback("/(?:#\[url\=[$URLSearchString]*\]|\[url\=[$URLSearchString]*\]#)(.*?)\[\/url\]/ism", function($matches) {
+                       return '#<a href="'
+                               . System::baseUrl()     . '/search?tag=' . rawurlencode($matches[1])
+                               . '" class="tag" title="' . XML::escape($matches[1]) . '">'
+                               . XML::escape($matches[1])
+                               . '</a>';
+               }, $text);
+
+               // We need no target="_blank" for local links
+               // convert links start with System::baseUrl() as local link
+               $escapedBaseUrl = str_replace('://', '\:\/\/', System::baseUrl());
+               $text = preg_replace("/\[url\]($escapedBaseUrl)([$URLSearchString]*)\[\/url\]/ism", '<a href="$1$2">$1$2</a>', $text);
+               $text = preg_replace("/\[url\=($escapedBaseUrl)([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1$2">$3</a>', $text);             
+               // convert links that start with / as local link
+               $text = preg_replace("/\[url\](\/[$URLSearchString]*)\[\/url\]/ism", '<a href="'.System::baseUrl().'$1">$1</a>', $text);
+               $text = preg_replace("/\[url\=(\/[$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="'.System::baseUrl().'$1">$2</a>', $text);         
 
                $text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" target="_blank">$1</a>', $text);
                $text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" target="_blank">$2</a>', $text);
-               //$Text = preg_replace("/\[url\=([$URLSearchString]*)\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" target="_blank">$2</a>', $Text);
 
                // Red compatibility, though the link can't be authenticated on Friendica
                $text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a href="$1" target="_blank">$2</a>', $text);
@@ -1655,7 +1678,7 @@ class BBCode extends BaseObject
                // Summary (e.g. title) is required, earlier revisions only required description (in addition to
                // start which is always required). Allow desc with a missing summary for compatibility.
 
-               if ((x($ev, 'desc') || x($ev, 'summary')) && x($ev, 'start')) {
+               if ((!empty($ev['desc']) || !empty($ev['summary'])) && !empty($ev['start'])) {
                        $sub = Event::getHTML($ev, $simple_html);
 
                        $text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism", '', $text);
@@ -1669,7 +1692,7 @@ class BBCode extends BaseObject
 
                // Replace non graphical smilies for external posts
                if ($simple_html) {
-                       $text = Smilies::replace($text, false, true);
+                       $text = Smilies::replace($text);
                }
 
                // Unhide all [noparse] contained bbtags unspacefying them
@@ -1749,7 +1772,7 @@ class BBCode extends BaseObject
                //$Text = str_replace('<br /><li>', '<li>', $Text);
                //$Text = str_replace('<br /><ul', '<ul ', $Text);
 
-               Addon::callHooks('bbcode', $text);
+               Hook::callAll('bbcode', $text);
 
                return trim($text);
        }
@@ -1802,7 +1825,12 @@ class BBCode extends BaseObject
         * @brief Callback function to replace a Friendica style mention in a mention for Diaspora
         *
         * @param array $match Matching values for the callback
+        *                     [1] = Mention type (! or @)
+        *                     [2] = Name
+        *                     [3] = Address
         * @return string Replaced mention
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        * @throws \ImagickException
         */
        private static function bbCodeMention2DiasporaCallback($match)
        {
@@ -1816,7 +1844,7 @@ class BBCode extends BaseObject
                        return $match[0];
                }
 
-               $mention = '@{' . $match[2] . '; ' . $contact['addr'] . '}';
+               $mention = $match[1] . '{' . $match[2] . '; ' . $contact['addr'] . '}';
                return $mention;
        }
 
@@ -1829,6 +1857,7 @@ class BBCode extends BaseObject
         * @param string $text
         * @param bool   $for_diaspora Diaspora requires more changes than Libertree
         * @return string
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function toMarkdown($text, $for_diaspora = true)
        {
@@ -1901,14 +1930,88 @@ class BBCode extends BaseObject
                if ($for_diaspora) {
                        $url_search_string = "^\[\]";
                        $text = preg_replace_callback(
-                               "/([@]\[(.*?)\])\(([$url_search_string]*?)\)/ism",
+                               "/([@!])\[(.*?)\]\(([$url_search_string]*?)\)/ism",
                                ['self', 'bbCodeMention2DiasporaCallback'],
                                $text
                        );
                }
 
-               Addon::callHooks('bb2diaspora', $text);
+               Hook::callAll('bb2diaspora', $text);
 
                return $text;
        }
+
+       /**
+     * @brief Pull out all #hashtags and @person tags from $string.
+     *
+     * We also get @person@domain.com - which would make
+     * the regex quite complicated as tags can also
+     * end a sentence. So we'll run through our results
+     * and strip the period from any tags which end with one.
+     * Returns array of tags found, or empty array.
+     *
+     * @param string $string Post content
+     * 
+     * @return array List of tag and person names
+     */
+    public static function getTags($string)
+    {
+        $ret = [];
+
+        // Convert hashtag links to hashtags
+        $string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2', $string);
+
+        // ignore anything in a code block
+        $string = preg_replace('/\[code\](.*?)\[\/code\]/sm', '', $string);
+
+        // Force line feeds at bbtags
+        $string = str_replace(['[', ']'], ["\n[", "]\n"], $string);
+
+        // ignore anything in a bbtag
+        $string = preg_replace('/\[(.*?)\]/sm', '', $string);
+
+        // Match full names against @tags including the space between first and last
+        // We will look these up afterward to see if they are full names or not recognisable.
+
+        if (preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/', $string, $matches)) {
+            foreach ($matches[1] as $match) {
+                if (strstr($match, ']')) {
+                    // we might be inside a bbcode color tag - leave it alone
+                    continue;
+                }
+
+                if (substr($match, -1, 1) === '.') {
+                    $ret[] = substr($match, 0, -1);
+                } else {
+                    $ret[] = $match;
+                }
+            }
+        }
+
+        // Otherwise pull out single word tags. These can be @nickname, @first_last
+        // and #hash tags.
+
+        if (preg_match_all('/([!#@][^\^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/', $string, $matches)) {
+            foreach ($matches[1] as $match) {
+                if (strstr($match, ']')) {
+                    // we might be inside a bbcode color tag - leave it alone
+                    continue;
+                }
+                if (substr($match, -1, 1) === '.') {
+                    $match = substr($match,0,-1);
+                }
+                // ignore strictly numeric tags like #1
+                if ((strpos($match, '#') === 0) && ctype_digit(substr($match, 1))) {
+                    continue;
+                }
+                // try not to catch url fragments
+                if (strpos($string, $match) && preg_match('/[a-zA-z0-9\/]/', substr($string, strpos($string, $match) - 1, 1))) {
+                    continue;
+                }
+                $ret[] = $match;
+            }
+        }
+
+        return $ret;
+    }
 }