X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FContent%2FText%2FBBCode.php;h=66f4190b287eca731fe8b465dea18da5504b0feb;hb=5a02e39a65f8f685440228cc1d36738cbe15f32b;hp=14f98202e7b27b1c8a9e72c545c5602bb680abba;hpb=342b9af73431aeda10b331f36e13bf5e69a2d58d;p=friendica.git
diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php
index 14f98202e7..66f4190b28 100644
--- a/src/Content/Text/BBCode.php
+++ b/src/Content/Text/BBCode.php
@@ -2,33 +2,31 @@
/**
* @file src/Content/Text/BBCode.php
*/
+
namespace Friendica\Content\Text;
use DOMDocument;
-use DomXPath;
+use DOMXPath;
use Exception;
+use Friendica\BaseObject;
use Friendica\Content\OEmbed;
use Friendica\Content\Smilies;
-use Friendica\Content\Text\Plaintext;
use Friendica\Core\Addon;
use Friendica\Core\Cache;
use Friendica\Core\Config;
use Friendica\Core\L10n;
use Friendica\Core\Protocol;
-use Friendica\Core\PConfig;
use Friendica\Core\System;
use Friendica\Model\Contact;
+use Friendica\Model\Event;
+use Friendica\Network\Probe;
use Friendica\Object\Image;
use Friendica\Util\Map;
use Friendica\Util\Network;
use Friendica\Util\ParseUrl;
+use Friendica\Util\Proxy as ProxyUtils;
-require_once "include/bbcode.php";
-require_once "include/event.php";
-require_once "include/html2plain.php";
-require_once "mod/proxy.php";
-
-class BBCode
+class BBCode extends BaseObject
{
/**
* @brief Fetches attachment data that were generated the old way
@@ -74,10 +72,12 @@ class BBCode
$picturedata = Image::getInfoFromURL($matches[1]);
- if (($picturedata[0] >= 500) && ($picturedata[0] >= $picturedata[1])) {
- $post["image"] = $matches[1];
- } else {
- $post["preview"] = $matches[1];
+ if ($picturedata) {
+ if (($picturedata[0] >= 500) && ($picturedata[0] >= $picturedata[1])) {
+ $post["image"] = $matches[1];
+ } else {
+ $post["preview"] = $matches[1];
+ }
}
}
@@ -85,7 +85,7 @@ class BBCode
$post["url"] = $matches[1];
$post["title"] = $matches[2];
}
- if (($post["url"] == "") && (in_array($post["type"], ["link", "video"]))
+ if (!empty($post["url"]) && (in_array($post["type"], ["link", "video"]))
&& preg_match("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", $attacheddata, $matches)) {
$post["url"] = $matches[1];
}
@@ -174,7 +174,7 @@ class BBCode
}
if ($title != "") {
- $title = bbcode(html_entity_decode($title, ENT_QUOTES, 'UTF-8'), false, false, true);
+ $title = self::convert(html_entity_decode($title, ENT_QUOTES, 'UTF-8'), false, true);
$title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
$title = str_replace(["[", "]"], ["[", "]"], $title);
$data["title"] = $title;
@@ -239,6 +239,9 @@ class BBCode
$body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body);
$URLSearchString = "^\[\]";
+
+ $body = preg_replace("/\[img\=([$URLSearchString]*)\](.*?)\[\/img\]/ism", '[img]$1[/img]', $body);
+
if (preg_match_all("(\[url=([$URLSearchString]*)\]\s*\[img\]([$URLSearchString]*)\[\/img\]\s*\[\/url\])ism", $body, $pictures, PREG_SET_ORDER)) {
if ((count($pictures) == 1) && !$has_title) {
// Checking, if the link goes to a picture
@@ -264,7 +267,7 @@ class BBCode
$post["text"] = str_replace($pictures[0][0], "", $body);
} else {
$imgdata = Image::getInfoFromURL($pictures[0][1]);
- if (substr($imgdata["mime"], 0, 6) == "image/") {
+ if ($imgdata && substr($imgdata["mime"], 0, 6) == "image/") {
$post["type"] = "photo";
$post["image"] = $pictures[0][1];
$post["preview"] = $pictures[0][2];
@@ -336,159 +339,30 @@ class BBCode
}
/**
- * @brief Convert a message into plaintext for connectors to other networks
+ * @brief Converts a BBCode text into plaintext
*
- * @param array $b The message array that is about to be posted
- * @param int $limit The maximum number of characters when posting to that network
- * @param bool $includedlinks Has an attached link to be included into the message?
- * @param int $htmlmode This triggers the behaviour of the bbcode conversion
- * @param string $target_network Name of the network where the post should go to.
+ * @param bool $keep_urls Whether to keep URLs in the resulting plaintext
*
- * @return string The converted message
+ * @return string
*/
- public static function toPlaintext($b, $limit = 0, $includedlinks = false, $htmlmode = 2, $target_network = "")
+ public static function toPlaintext($text, $keep_urls = true)
{
- // Remove the hash tags
- $URLSearchString = "^\[\]";
- $body = preg_replace("/([#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$1$3', $b["body"]);
-
- // Add an URL element if the text contains a raw link
- $body = preg_replace("/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url]$2[/url]', $body);
-
- // Remove the abstract
- $body = self::stripAbstract($body);
-
- // At first look at data that is attached via "type-..." stuff
- // This will hopefully replaced with a dedicated bbcode later
- //$post = self::getAttachedData($b["body"]);
- $post = self::getAttachedData($body, $b);
-
- if (($b["title"] != "") && ($post["text"] != "")) {
- $post["text"] = trim($b["title"]."\n\n".$post["text"]);
- } elseif ($b["title"] != "") {
- $post["text"] = trim($b["title"]);
+ $naked_text = preg_replace('/\[(.+?)\]\s*/','', $text);
+ if (!$keep_urls) {
+ $naked_text = preg_replace('#https?\://[^\s<]+[^\s\.\)]#i', '', $naked_text);
}
- $abstract = "";
-
- // Fetch the abstract from the given target network
- if ($target_network != "") {
- $default_abstract = self::getAbstract($b["body"]);
- $abstract = self::getAbstract($b["body"], $target_network);
-
- // If we post to a network with no limit we only fetch
- // an abstract exactly for this network
- if (($limit == 0) && ($abstract == $default_abstract)) {
- $abstract = "";
- }
- } else {// Try to guess the correct target network
- switch ($htmlmode) {
- case 8:
- $abstract = self::getAbstract($b["body"], NETWORK_TWITTER);
- break;
- case 7:
- $abstract = self::getAbstract($b["body"], NETWORK_STATUSNET);
- break;
- case 6:
- $abstract = self::getAbstract($b["body"], NETWORK_APPNET);
- break;
- default: // We don't know the exact target.
- // We fetch an abstract since there is a posting limit.
- if ($limit > 0) {
- $abstract = self::getAbstract($b["body"]);
- }
- }
- }
-
- if ($abstract != "") {
- $post["text"] = $abstract;
-
- if ($post["type"] == "text") {
- $post["type"] = "link";
- $post["url"] = $b["plink"];
- }
- }
-
- $html = bbcode($post["text"].$post["after"], false, false, $htmlmode);
- $msg = html2plain($html, 0, true);
- $msg = trim(html_entity_decode($msg, ENT_QUOTES, 'UTF-8'));
-
- $link = "";
- if ($includedlinks) {
- if ($post["type"] == "link") {
- $link = $post["url"];
- } elseif ($post["type"] == "text") {
- $link = $post["url"];
- } elseif ($post["type"] == "video") {
- $link = $post["url"];
- } elseif ($post["type"] == "photo") {
- $link = $post["image"];
- }
-
- if (($msg == "") && isset($post["title"])) {
- $msg = trim($post["title"]);
- }
-
- if (($msg == "") && isset($post["description"])) {
- $msg = trim($post["description"]);
- }
-
- // If the link is already contained in the post, then it neeedn't to be added again
- // But: if the link is beyond the limit, then it has to be added.
- if (($link != "") && strstr($msg, $link)) {
- $pos = strpos($msg, $link);
-
- // Will the text be shortened in the link?
- // Or is the link the last item in the post?
- if (($limit > 0) && ($pos < $limit) && (($pos + 23 > $limit) || ($pos + strlen($link) == strlen($msg)))) {
- $msg = trim(str_replace($link, "", $msg));
- } elseif (($limit == 0) || ($pos < $limit)) {
- // The limit has to be increased since it will be shortened - but not now
- // Only do it with Twitter (htmlmode = 8)
- if (($limit > 0) && (strlen($link) > 23) && ($htmlmode == 8)) {
- $limit = $limit - 23 + strlen($link);
- }
-
- $link = "";
-
- if ($post["type"] == "text") {
- unset($post["url"]);
- }
- }
- }
- }
-
- if ($limit > 0) {
- // Reduce multiple spaces
- // When posted to a network with limited space, we try to gain space where possible
- while (strpos($msg, " ") !== false) {
- $msg = str_replace(" ", " ", $msg);
- }
-
- // Twitter is using its own limiter, so we always assume that shortened links will have this length
- if (iconv_strlen($link, "UTF-8") > 0) {
- $limit = $limit - 23;
- }
+ return $naked_text;
+ }
- if (iconv_strlen($msg, "UTF-8") > $limit) {
- if (($post["type"] == "text") && isset($post["url"])) {
- $post["url"] = $b["plink"];
- } elseif (!isset($post["url"])) {
- $limit = $limit - 23;
- $post["url"] = $b["plink"];
- // Which purpose has this line? It is now uncommented, but left as a reminder
- //} elseif (strpos($b["body"], "[share") !== false) {
- // $post["url"] = $b["plink"];
- } elseif (PConfig::get($b["uid"], "system", "no_intelligent_shortening")) {
- $post["url"] = $b["plink"];
- }
- $msg = Plaintext::shorten($msg, $limit);
- }
+ private static function proxyUrl($image, $simplehtml = false)
+ {
+ // Only send proxied pictures to API and for internal display
+ if (in_array($simplehtml, [false, 2])) {
+ return ProxyUtils::proxifyUrl($image);
+ } else {
+ return $image;
}
-
- $post["text"] = trim($msg);
-
- return($post);
}
public static function scaleExternalImages($srctext, $include_link = true, $scale_replace = false)
@@ -571,7 +445,7 @@ class BBCode
*/
public static function limitBodySize($body)
{
- $maxlen = get_max_import_size();
+ $maxlen = Config::get('config', 'max_import_size', 0);
// If the length of the body, including the embedded images, is smaller
// than the maximum, then don't waste time looking for the images
@@ -662,23 +536,25 @@ class BBCode
private static function convertAttachment($return, $simplehtml = false, $tryoembed = true)
{
$data = self::getAttachmentData($return);
- if (!$data) {
+ if (empty($data) || empty($data["url"])) {
return $return;
}
if (isset($data["title"])) {
$data["title"] = strip_tags($data["title"]);
$data["title"] = str_replace(["http://", "https://"], "", $data["title"]);
+ } else {
+ $data["title"] = null;
}
- if (((strpos($data["text"], "[img=") !== false) || (strpos($data["text"], "[img]") !== false) || Config::get('system', 'always_show_preview')) && ($data["image"] != "")) {
+ if (((strpos($data["text"], "[img=") !== false) || (strpos($data["text"], "[img]") !== false) || Config::get('system', 'always_show_preview')) && !empty($data["image"])) {
$data["preview"] = $data["image"];
$data["image"] = "";
}
$return = '';
if ($simplehtml == 7) {
- $return = self::convertUrlForMastodon($data["url"]);
+ $return = self::convertUrlForOStatus($data["url"]);
} elseif (($simplehtml != 4) && ($simplehtml != 0)) {
$return = sprintf('%s
', $data["url"], $data["title"]);
} else {
@@ -689,27 +565,32 @@ class BBCode
throw new Exception('OEmbed is disabled for this attachment.');
}
} catch (Exception $e) {
+ $data["title"] = defaults($data, 'title', $data['url']);
+
if ($simplehtml != 4) {
$return = sprintf('
%s', trim(bbcode($data["description"]))); + if (!empty($data["description"]) && $data["description"] != $data["title"]) { + // Sanitize the HTML by converting it to BBCode + $bbcode = HTML::toBBCode($data["description"]); + $return .= sprintf('
%s', trim(self::convert($bbcode))); } - if ($data["type"] == "link") { + if (!empty($data['url'])) { $return .= sprintf('%s', $data['url'], parse_url($data['url'], PHP_URL_HOST)); } @@ -719,7 +600,7 @@ class BBCode } } - return trim($data["text"] . ' ' . $return . ' ' . $data["after"]); + return trim(defaults($data, 'text', '') . ' ' . $return . ' ' . defaults($data, 'after', '')); } public static function removeShareInformation($Text, $plaintext = false, $nolink = false) @@ -729,10 +610,10 @@ class BBCode if (!$data) { return $Text; } elseif ($nolink) { - return $data["text"] . $data["after"]; + return $data["text"] . defaults($data, 'after', ''); } - $title = htmlentities($data["title"], ENT_QUOTES, 'UTF-8', false); + $title = htmlentities(defaults($data, 'title', ''), ENT_QUOTES, 'UTF-8', false); $text = htmlentities($data["text"], ENT_QUOTES, 'UTF-8', false); if ($plaintext || (($title != "") && strstr($text, $title))) { $data["title"] = $data["url"]; @@ -746,42 +627,21 @@ class BBCode } // If the link already is included in the post, don't add it again - if (($data["url"] != "") && strpos($data["text"], $data["url"])) { + if (!empty($data["url"]) && strpos($data["text"], $data["url"])) { return $data["text"] . $data["after"]; } $text = $data["text"]; - if (($data["url"] != "") && ($data["title"] != "")) { + if (!empty($data["url"]) && !empty($data["title"])) { $text .= "\n[url=" . $data["url"] . "]" . $data["title"] . "[/url]"; - } elseif (($data["url"] != "")) { - $text .= "\n" . $data["url"]; + } elseif (!empty($data["url"])) { + $text .= "\n[url]" . $data["url"] . "[/url]"; } return $text . "\n" . $data["after"]; } - private static function cleanCss($input) - { - $cleaned = ""; - - $input = strtolower($input); - - for ($i = 0; $i < strlen($input); $i++) { - $char = substr($input, $i, 1); - - if (($char >= "a") && ($char <= "z")) { - $cleaned .= $char; - } - - if (!(strpos(" #;:0123456789-_.%", $char) === false)) { - $cleaned .= $char; - } - } - - return $cleaned; - } - /** * Converts [url] BBCodes in a format that looks fine on Mastodon. (callback function) * @@ -789,7 +649,7 @@ class BBCode * @param array $match Array with the matching values * @return string reformatted link including HTML codes */ - private static function convertUrlForMastodonCallback($match) + private static function convertUrlForOStatusCallback($match) { $url = $match[1]; @@ -802,34 +662,27 @@ class BBCode return $match[0]; } - return self::convertUrlForMastodon($url); + return self::convertUrlForOStatus($url); } /** - * @brief Converts [url] BBCodes in a format that looks fine on Mastodon and GNU Social. + * @brief Converts [url] BBCodes in a format that looks fine on OStatus systems. * @param string $url URL that is about to be reformatted * @return string reformatted link including HTML codes */ - private static function convertUrlForMastodon($url) + private static function convertUrlForOStatus($url) { $parts = parse_url($url); $scheme = $parts['scheme'] . '://'; $styled_url = str_replace($scheme, '', $url); - $html = '' . - '%s'; - if (strlen($styled_url) > 30) { - $html .= '%s' . - '%s'; - - $ellipsis = substr($styled_url, 0, 30); - $rest = substr($styled_url, 30); - return sprintf($html, $url, $scheme, $ellipsis, $rest); - } else { - $html .= '%s'; - return sprintf($html, $url, $scheme, $styled_url); + $styled_url = substr($styled_url, 0, 30) . "â¦"; } + + $html = '%s'; + + return sprintf($html, $url, $styled_url); } /* @@ -998,7 +851,7 @@ class BBCode // it loops over the array starting from the first element and going sequentially // to the last element $newbody = str_replace('[$#saved_image' . $cnt . '#$]', - '', $newbody); + '', $newbody); $cnt++; } @@ -1078,7 +931,7 @@ class BBCode // We only call this so that a previously unknown contact can be added. // This is important for the function "Model\Contact::getDetailsByURL()". // This function then can fetch an entry from the contact table. - Contact::getIdForURL($profile, 0); + Contact::getIdForURL($profile, 0, true); $data = Contact::getDetailsByURL($profile); @@ -1124,18 +977,18 @@ class BBCode } if (stripos(normalise_link($link), 'http://twitter.com/') === 0) { + $text .= '
' . trim($share[3]) . "
' . trim($matches[2], "\n\r") . '
';
+ }
+ return $return;
+ },
+ $text
+ );
+
// Hide all [noparse] contained bbtags by spacefying them
// POSSIBLE BUG --> Will the 'preg' functions crash if there's an embedded image?
@@ -1399,7 +1253,7 @@ class BBCode
// After we're finished processing the bbcode we'll
// replace all of the event code with a reformatted version.
- $ev = bbtoevent($text);
+ $ev = Event::fromBBCode($text);
// Replace any html brackets with HTML Entities to prevent executing HTML or script
// Don't use strip_tags here because it breaks [url] search by replacing & with amp
@@ -1419,11 +1273,6 @@ class BBCode
$text = preg_replace("/\[share(.*?)avatar\s?=\s?'.*?'\s?(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "\n[share$1$2]$3[/share]", $text);
}
- // Check for [code] text here, before the linefeeds are messed with.
- // The highlighter will unescape and re-escape the content.
- if (strpos($text, '[code=') !== false) {
- $text = preg_replace_callback("/\[code=(.*?)\](.*?)\[\/code\]/ism", 'self::textHighlightCallback', $text);
- }
// Convert new line chars to html ' . Map::byLocation($match[1], $simple_html) . '
', $match[0]); }, $text ); @@ -1575,16 +1425,14 @@ class BBCode if (strpos($text, '[map=') !== false) { $text = preg_replace_callback( "/\[map=(.*?)\]/ism", - function ($match) { - // the extra space in the following line is intentional - // Whyyy? - @MrPetovan - return str_replace($match[0], '' . Map::byCoordinates(str_replace('/', ' ', $match[1]), $simple_html) . '
', $match[0]); }, $text ); } if (strpos($text, '[map]') !== false) { - $text = preg_replace("/\[map\]/", '', $text); + $text = preg_replace("/\[map\]/", '', $text); } // Check for headers @@ -1608,7 +1456,7 @@ class BBCode $text = preg_replace("(\[u\](.*?)\[\/u\])ism", '$1', $text); // Check for strike-through text - $text = preg_replace("(\[s\](.*?)\[\/s\])ism", '$2", + "
" . $t_wrote . "
$2", $text); } @@ -1735,12 +1583,12 @@ class BBCode // [img=widthxheight]image source[/img] $text = preg_replace_callback( "/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", - function ($matches) { + function ($matches) use ($simple_html) { if (strpos($matches[3], "data:image/") === 0) { return $matches[0]; } - $matches[3] = proxy_url($matches[3]); + $matches[3] = self::proxyUrl($matches[3], $simple_html); return "[img=" . $matches[1] . "x" . $matches[2] . "]" . $matches[3] . "[/img]"; }, $text @@ -1749,16 +1597,24 @@ class BBCode $text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '', $text); $text = preg_replace("/\[zmg\=([0-9]*)x([0-9]*)\](.*?)\[\/zmg\]/ism", '', $text); + $text = preg_replace_callback("/\[img\=([$URLSearchString]*)\](.*?)\[\/img\]/ism", + function ($matches) use ($simple_html) { + $matches[1] = self::proxyUrl($matches[1], $simple_html); + $matches[2] = htmlspecialchars($matches[2], ENT_COMPAT); + return ''; + }, + $text); + // Images // [img]pathtoimage[/img] $text = preg_replace_callback( "/\[img\](.*?)\[\/img\]/ism", - function ($matches) { + function ($matches) use ($simple_html) { if (strpos($matches[1], "data:image/") === 0) { return $matches[0]; } - $matches[1] = proxy_url($matches[1]); + $matches[1] = self::proxyUrl($matches[1], $simple_html); return "[img]" . $matches[1] . "[/img]"; }, $text @@ -1779,15 +1635,15 @@ class BBCode // Try to Oembed if ($try_oembed) { - $text = preg_replace("/\[video\](.*?\.(ogg|ogv|oga|ogm|webm|mp4))\[\/video\]/ism", '', $text); - $text = preg_replace("/\[audio\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mp3))\[\/audio\]/ism", '', $text); + $text = preg_replace("/\[video\](.*?\.(ogg|ogv|oga|ogm|webm|mp4).*?)\[\/video\]/ism", '', $text); + $text = preg_replace("/\[audio\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mp3).*?)\[\/audio\]/ism", '', $text); $text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", $try_oembed_callback, $text); $text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", $try_oembed_callback, $text); } else { - $text = preg_replace("/\[video\](.*?)\[\/video\]/", + $text = preg_replace("/\[video\](.*?)\[\/video\]/ism", '$1', $text); - $text = preg_replace("/\[audio\](.*?)\[\/audio\]/", + $text = preg_replace("/\[audio\](.*?)\[\/audio\]/ism", '$1', $text); } @@ -1845,7 +1701,7 @@ class BBCode // start which is always required). Allow desc with a missing summary for compatibility. if ((x($ev, 'desc') || x($ev, 'summary')) && x($ev, 'start')) { - $sub = format_event_html($ev, $simple_html); + $sub = Event::getHTML($ev, $simple_html); $text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism", '', $text); $text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism", '', $text); @@ -1880,10 +1736,12 @@ class BBCode $text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'self::unescapeNoparseCallback', $text); $text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'self::unescapeNoparseCallback', $text); - + /// @todo What is the meaning of these lines? $text = preg_replace('/\[\&\;([#a-z0-9]+)\;\]/', '&$1;', $text); $text = preg_replace('/\&\#039\;/', '\'', $text); - $text = preg_replace('/\"\;/', '"', $text); + + // Currently deactivated, it made problems with " inside of alt texts. + //$text = preg_replace('/\"\;/', '"', $text); // fix any escaped ampersands that may have been converted into links $text = preg_replace('/\<([^>]*?)(src|href)=(.*?)\&\;(.*?)\>/ism', '<$1$2=$3&$4>', $text); @@ -1908,6 +1766,18 @@ class BBCode $text = self::interpolateSavedImagesIntoItemBody($text, $saved_image); } + // Restore code blocks + $text = preg_replace_callback('/#codeblock-([0-9]+)#/iU', + function ($matches) use ($codeblocks) { + $return = $matches[0]; + if (isset($codeblocks[intval($matches[1])])) { + $return = $codeblocks[$matches[1]]; + } + return $return; + }, + $text + ); + // Clean up the HTML by loading and saving the HTML with the DOM. // Bad structured html can break a whole page. // For performance reasons do it only with ativated item cache or at export. @@ -1962,7 +1832,7 @@ class BBCode * @param string $addon The addon for which the abstract is meant for * @return string The abstract */ - private static function getAbstract($text, $addon = "") + public static function getAbstract($text, $addon = "") { $abstract = ""; $abstracts = []; @@ -1984,4 +1854,118 @@ class BBCode return $abstract; } + + /** + * @brief Callback function to replace a Friendica style mention in a mention for Diaspora + * + * @param array $match Matching values for the callback + * @return string Replaced mention + */ + private static function bbCodeMention2DiasporaCallback($match) + { + $contact = Contact::getDetailsByURL($match[3]); + + if (empty($contact['addr'])) { + $contact = Probe::uri($match[3]); + } + + if (empty($contact['addr'])) { + return $match[0]; + } + + $mention = '@{' . $match[2] . '; ' . $contact['addr'] . '}'; + return $mention; + } + + /** + * @brief Converts a BBCode text into Markdown + * + * This function converts a BBCode item body to be sent to Markdown-enabled + * systems like Diaspora and Libertree + * + * @param string $text + * @param bool $for_diaspora Diaspora requires more changes than Libertree + * @return string + */ + public static function toMarkdown($text, $for_diaspora = true) + { + $a = self::getApp(); + + $original_text = $text; + + // Since Diaspora is creating a summary for links, this function removes them before posting + if ($for_diaspora) { + $text = self::removeShareInformation($text); + } + + /** + * Transform #tags, strip off the [url] and replace spaces with underscore + */ + $url_search_string = "^\[\]"; + $text = preg_replace_callback("/#\[url\=([$url_search_string]*)\](.*?)\[\/url\]/i", + function ($matches) { + return '#' . str_replace(' ', '_', $matches[2]); + }, + $text + ); + + // Converting images with size parameters to simple images. Markdown doesn't know it. + $text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $text); + + // Convert it to HTML - don't try oembed + if ($for_diaspora) { + $text = self::convert($text, false, 3); + + // Add all tags that maybe were removed + if (preg_match_all("/#\[url\=([$url_search_string]*)\](.*?)\[\/url\]/ism", $original_text, $tags)) { + $tagline = ""; + foreach ($tags[2] as $tag) { + $tag = html_entity_decode($tag, ENT_QUOTES, 'UTF-8'); + if (!strpos(html_entity_decode($text, ENT_QUOTES, 'UTF-8'), '#' . $tag)) { + $tagline .= '#' . $tag . ' '; + } + } + $text = $text . " " . $tagline; + } + } else { + $text = self::convert($text, false, 4); + } + + // mask some special HTML chars from conversation to markdown + $text = str_replace(['<', '>', '&'], ['&_lt_;', '&_gt_;', '&_amp_;'], $text); + + // If a link is followed by a quote then there should be a newline before it + // Maybe we should make this newline at every time before a quote. + $text = str_replace(["
"], [""], $text); + + $stamp1 = microtime(true); + + // Now convert HTML to Markdown + $text = HTML::toMarkdown($text); + + // unmask the special chars back to HTML + $text = str_replace(['&\_lt\_;', '&\_gt\_;', '&\_amp\_;'], ['<', '>', '&'], $text); + + $a->saveTimestamp($stamp1, "parser"); + + // Libertree has a problem with escaped hashtags. + $text = str_replace(['\#'], ['#'], $text); + + // Remove any leading or trailing whitespace, as this will mess up + // the Diaspora signature verification and cause the item to disappear + $text = trim($text); + + if ($for_diaspora) { + $url_search_string = "^\[\]"; + $text = preg_replace_callback( + "/([@]\[(.*?)\])\(([$url_search_string]*?)\)/ism", + ['self', 'bbCodeMention2DiasporaCallback'], + $text + ); + } + + Addon::callHooks('bb2diaspora', $text); + + return $text; + } }