]> git.mxchange.org Git - friendica.git/blobdiff - src/Content/Text/BBCode.php
Merge pull request #6223 from annando/ap-dba-error
[friendica.git] / src / Content / Text / BBCode.php
index be2054067b25b68a9a898e9f732a524390cd11db..e304f47637eeaf1b0b2d9cb10a0734d35340685c 100644 (file)
@@ -15,7 +15,9 @@ use Friendica\Core\Addon;
 use Friendica\Core\Cache;
 use Friendica\Core\Config;
 use Friendica\Core\L10n;
+use Friendica\Core\Logger;
 use Friendica\Core\Protocol;
+use Friendica\Core\Renderer;
 use Friendica\Core\System;
 use Friendica\Model\Contact;
 use Friendica\Model\Event;
@@ -25,6 +27,8 @@ use Friendica\Util\Map;
 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
 {
@@ -126,12 +130,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]);
                }
 
@@ -149,12 +153,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];
                }
 
@@ -164,12 +168,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];
                }
 
@@ -182,12 +186,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];
                }
 
@@ -197,12 +201,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];
                }
 
@@ -230,7 +234,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.
@@ -379,7 +383,7 @@ class BBCode extends BaseObject
                $c = preg_match_all('/\[img.*?\](.*?)\[\/img\]/ism', $s, $matches, PREG_SET_ORDER);
                if ($c) {
                        foreach ($matches as $mtch) {
-                               logger('scale_external_image: ' . $mtch[1]);
+                               Logger::log('scale_external_image: ' . $mtch[1]);
 
                                $hostname = str_replace('www.', '', substr(System::baseUrl(), strpos(System::baseUrl(), '://') + 3));
                                if (stristr($mtch[1], $hostname)) {
@@ -414,7 +418,7 @@ class BBCode extends BaseObject
                                                        $Image->scaleDown(640);
                                                        $new_width = $Image->getWidth();
                                                        $new_height = $Image->getHeight();
-                                                       logger('scale_external_images: ' . $orig_width . '->' . $new_width . 'w ' . $orig_height . '->' . $new_height . 'h' . ' match: ' . $mtch[0], LOGGER_DEBUG);
+                                                       Logger::log('scale_external_images: ' . $orig_width . '->' . $new_width . 'w ' . $orig_height . '->' . $new_height . 'h' . ' match: ' . $mtch[0], Logger::DEBUG);
                                                        $s = str_replace(
                                                                $mtch[0],
                                                                '[img=' . $new_width . 'x' . $new_height. ']' . $scaled . '[/img]'
@@ -423,7 +427,7 @@ class BBCode extends BaseObject
                                                                        : ''),
                                                                $s
                                                        );
-                                                       logger('scale_external_images: new string: ' . $s, LOGGER_DEBUG);
+                                                       Logger::log('scale_external_images: new string: ' . $s, Logger::DEBUG);
                                                }
                                        }
                                }
@@ -451,7 +455,7 @@ class BBCode extends BaseObject
                // than the maximum, then don't waste time looking for the images
                if ($maxlen && (strlen($body) > $maxlen)) {
 
-                       logger('the total body length exceeds the limit', LOGGER_DEBUG);
+                       Logger::log('the total body length exceeds the limit', Logger::DEBUG);
 
                        $orig_body = $body;
                        $new_body = '';
@@ -471,7 +475,7 @@ class BBCode extends BaseObject
 
                                        if (($textlen + $img_start) > $maxlen) {
                                                if ($textlen < $maxlen) {
-                                                       logger('the limit happens before an embedded image', LOGGER_DEBUG);
+                                                       Logger::log('the limit happens before an embedded image', Logger::DEBUG);
                                                        $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
                                                        $textlen = $maxlen;
                                                }
@@ -485,7 +489,7 @@ class BBCode extends BaseObject
 
                                        if (($textlen + $img_end) > $maxlen) {
                                                if ($textlen < $maxlen) {
-                                                       logger('the limit happens before the end of a non-embedded image', LOGGER_DEBUG);
+                                                       Logger::log('the limit happens before the end of a non-embedded image', Logger::DEBUG);
                                                        $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
                                                        $textlen = $maxlen;
                                                }
@@ -508,11 +512,11 @@ class BBCode extends BaseObject
 
                        if (($textlen + strlen($orig_body)) > $maxlen) {
                                if ($textlen < $maxlen) {
-                                       logger('the limit happens after the end of the last image', LOGGER_DEBUG);
+                                       Logger::log('the limit happens after the end of the last image', Logger::DEBUG);
                                        $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
                                }
                        } else {
-                               logger('the text size with embedded images extracted did not violate the limit', LOGGER_DEBUG);
+                               Logger::log('the text size with embedded images extracted did not violate the limit', Logger::DEBUG);
                                $new_body = $new_body . $orig_body;
                        }
 
@@ -941,7 +945,7 @@ class BBCode extends BaseObject
                        case 3: // Diaspora
                                $headline = '<p><b>' . html_entity_decode('&#x2672; ', ENT_QUOTES, 'UTF-8') . $mention . ':</b></p>' . "\n";
 
-                               if (stripos(normalise_link($attributes['link']), 'http://twitter.com/') === 0) {
+                               if (stripos(Strings::normaliseLink($attributes['link']), 'http://twitter.com/') === 0) {
                                        $text = ($is_quote_share? '<hr />' : '') . '<p><a href="' . $attributes['link'] . '">' . $attributes['link'] . '</a></p>' . "\n";
                                } else {
                                        $text = ($is_quote_share? '<hr />' : '') . $headline . '<blockquote>' . trim($content) . '</blockquote>' . "\n";
@@ -976,7 +980,7 @@ class BBCode extends BaseObject
                                break;
                        default:
                                // Transforms quoted tweets in rich attachments to avoid nested tweets
-                               if (stripos(normalise_link($attributes['link']), 'http://twitter.com/') === 0 && OEmbed::isAllowedURL($attributes['link'])) {
+                               if (stripos(Strings::normaliseLink($attributes['link']), 'http://twitter.com/') === 0 && OEmbed::isAllowedURL($attributes['link'])) {
                                        try {
                                                $text = ($is_quote_share? '<br />' : '') . OEmbed::getHTML($attributes['link']);
                                        } catch (Exception $e) {
@@ -985,8 +989,8 @@ class BBCode extends BaseObject
                                } else {
                                        $text = ($is_quote_share? "\n" : '');
 
-                                       $tpl = get_markup_template('shared_content.tpl');
-                                       $text .= replace_macros($tpl, [
+                                       $tpl = Renderer::getMarkupTemplate('shared_content.tpl');
+                                       $text .= Renderer::replaceMacros($tpl, [
                                                '$profile' => $attributes['profile'],
                                                '$avatar'  => $attributes['avatar'],
                                                '$author'  => $attributes['author'],
@@ -1337,15 +1341,21 @@ 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);
 
                $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);
@@ -1361,7 +1371,7 @@ class BBCode extends BaseObject
                $text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1">$2</a>', $text);
 
                // leave open the posibility of [map=something]
-               // this is replaced in prepare_body() which has knowledge of the item location
+               // this is replaced in Item::prepareBody() which has knowledge of the item location
 
                if (strpos($text, '[/map]') !== false) {
                        $text = preg_replace_callback(
@@ -1472,7 +1482,7 @@ class BBCode extends BaseObject
 
                $text = str_replace('[hr]', '<hr />', $text);
 
-               // This is actually executed in prepare_body()
+               // This is actually executed in Item::prepareBody()
 
                $text = str_replace('[nosmile]', '', $text);
 
@@ -1652,7 +1662,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);
@@ -1908,4 +1918,78 @@ class BBCode extends BaseObject
 
                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;
+    }
 }