Merge pull request #8729 from MrPetovan/bug/8726-mention-parsing
Add tag escaping to BBCode::setTags
This commit is contained in:
commit
ad47ff50a9
|
@ -613,15 +613,26 @@ On Mastodon this field is used for the content warning.
|
|||
<th>Result</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>If you need to put literal bbcode in a message, [noparse], [nobb] or [pre] are used to escape bbcode:
|
||||
<td>If you need to put literal BBCode in a message, [noparse], [nobb] or [pre] blocks prevent BBCode conversion:
|
||||
<ul>
|
||||
<li>[noparse][b]bold[/b][/noparse]</li>
|
||||
<li>[nobb][b]bold[/b][/nobb]</li>
|
||||
<li>[pre][b]bold[/b][/pre]</li>
|
||||
</ul>
|
||||
Note: [code] has priority over [noparse], [nobb] and [pre] which makes them display as BBCode tags in code blocks instead of being removed.
|
||||
[code] blocks inside [noparse] will still be converted to a code block.
|
||||
</td>
|
||||
<td>[b]bold[/b]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Additionally, [noparse] and [pre] blocks prevent mention and hashtag conversion to links:
|
||||
<ul>
|
||||
<li>[noparse]@user@domain.tld #hashtag[/noparse]</li>
|
||||
<li>[pre]@user@domain.tld #hashtag[/pre]</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>@user@domain.tld #hashtag</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[nosmile] is used to disable smilies on a post by post basis<br>
|
||||
<br>
|
||||
|
|
|
@ -624,7 +624,7 @@ function api_get_user(App $a, $contact_id = null)
|
|||
'name' => $contact["name"],
|
||||
'screen_name' => (($contact['nick']) ? $contact['nick'] : $contact['name']),
|
||||
'location' => ($contact["location"] != "") ? $contact["location"] : ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol']),
|
||||
'description' => BBCode::toPlaintext($contact["about"]),
|
||||
'description' => BBCode::toPlaintext($contact["about"] ?? ''),
|
||||
'profile_image_url' => $contact["micro"],
|
||||
'profile_image_url_https' => $contact["micro"],
|
||||
'profile_image_url_profile_size' => $contact["thumb"],
|
||||
|
@ -698,7 +698,7 @@ function api_get_user(App $a, $contact_id = null)
|
|||
'name' => (($uinfo[0]['name']) ? $uinfo[0]['name'] : $uinfo[0]['nick']),
|
||||
'screen_name' => (($uinfo[0]['nick']) ? $uinfo[0]['nick'] : $uinfo[0]['name']),
|
||||
'location' => $location,
|
||||
'description' => BBCode::toPlaintext($description),
|
||||
'description' => BBCode::toPlaintext($description ?? ''),
|
||||
'profile_image_url' => $uinfo[0]['micro'],
|
||||
'profile_image_url_https' => $uinfo[0]['micro'],
|
||||
'profile_image_url_profile_size' => $uinfo[0]["thumb"],
|
||||
|
|
|
@ -465,7 +465,7 @@ function notification($params)
|
|||
if ($show_in_notification_page) {
|
||||
$notification = DI::notify()->insert([
|
||||
'name' => $params['source_name'] ?? '',
|
||||
'name_cache' => substr(strip_tags(BBCode::convert($params['source_name'] ?? '')), 0, 255),
|
||||
'name_cache' => substr(strip_tags(BBCode::convert($params['source_name'])), 0, 255),
|
||||
'url' => $params['source_link'] ?? '',
|
||||
'photo' => $params['source_photo'] ?? '',
|
||||
'link' => $itemlink ?? '',
|
||||
|
|
|
@ -78,7 +78,7 @@ function cal_init(App $a)
|
|||
'$photo' => $profile['photo'],
|
||||
'$addr' => $profile['addr'] ?: '',
|
||||
'$account_type' => $account_type,
|
||||
'$about' => BBCode::convert($profile['about'] ?: ''),
|
||||
'$about' => BBCode::convert($profile['about']),
|
||||
]);
|
||||
|
||||
$cal_widget = Widget\CalendarExport::getHTML();
|
||||
|
|
35
mod/item.php
35
mod/item.php
|
@ -369,16 +369,16 @@ function item_post(App $a) {
|
|||
|
||||
// Look for any tags and linkify them
|
||||
$inform = '';
|
||||
|
||||
$tags = BBCode::getTags($body);
|
||||
|
||||
$tagged = [];
|
||||
|
||||
$private_forum = false;
|
||||
$private_id = null;
|
||||
$only_to_forum = false;
|
||||
$forum_contact = [];
|
||||
|
||||
if (count($tags)) {
|
||||
BBCode::performWithEscapedTags($body, ['noparse', 'pre', 'code'], function ($body) use ($profile_uid, $network, $str_contact_allow, &$inform, &$private_forum, &$private_id, &$only_to_forum, &$forum_contact) {
|
||||
$tags = BBCode::getTags($body);
|
||||
|
||||
$tagged = [];
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$tag_type = substr($tag, 0, 1);
|
||||
|
||||
|
@ -386,41 +386,36 @@ function item_post(App $a) {
|
|||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we already tagged 'Robert Johnson', don't try and tag 'Robert'.
|
||||
/* If we already tagged 'Robert Johnson', don't try and tag 'Robert'.
|
||||
* Robert Johnson should be first in the $tags array
|
||||
*/
|
||||
$fullnametagged = false;
|
||||
/// @TODO $tagged is initialized above if () block and is not filled, maybe old-lost code?
|
||||
foreach ($tagged as $nextTag) {
|
||||
if (stristr($nextTag, $tag . ' ')) {
|
||||
$fullnametagged = true;
|
||||
break;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
if ($fullnametagged) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$success = handle_tag($body, $inform, local_user() ? local_user() : $profile_uid, $tag, $network);
|
||||
if ($success['replaced']) {
|
||||
$tagged[] = $tag;
|
||||
}
|
||||
// When the forum is private or the forum is addressed with a "!" make the post private
|
||||
if (is_array($success['contact']) && (!empty($success['contact']['prv']) || ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]))) {
|
||||
if (!empty($success['contact']['prv']) || ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) {
|
||||
$private_forum = $success['contact']['prv'];
|
||||
$only_to_forum = ($tag_type == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]);
|
||||
$private_id = $success['contact']['id'];
|
||||
$forum_contact = $success['contact'];
|
||||
} elseif (is_array($success['contact']) && !empty($success['contact']['forum']) &&
|
||||
($str_contact_allow == '<' . $success['contact']['id'] . '>')) {
|
||||
} elseif (!empty($success['contact']['forum']) && ($str_contact_allow == '<' . $success['contact']['id'] . '>')) {
|
||||
$private_forum = false;
|
||||
$only_to_forum = true;
|
||||
$private_id = $success['contact']['id'];
|
||||
$forum_contact = $success['contact'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
|
||||
$original_contact_id = $contact_id;
|
||||
|
||||
|
@ -642,7 +637,7 @@ function item_post(App $a) {
|
|||
|
||||
// Check for hashtags in the body and repair or add hashtag links
|
||||
if ($preview || $orig_post) {
|
||||
Item::setHashtags($datarray);
|
||||
$datarray['body'] = Item::setHashtags($datarray['body']);
|
||||
}
|
||||
|
||||
// preview mode - prepare the body for display and send it via json
|
||||
|
|
|
@ -82,7 +82,7 @@ function photos_init(App $a) {
|
|||
'$photo' => $profile['photo'],
|
||||
'$addr' => $profile['addr'] ?? '',
|
||||
'$account_type' => $account_type,
|
||||
'$about' => BBCode::convert($profile['about'] ?? ''),
|
||||
'$about' => BBCode::convert($profile['about']),
|
||||
]);
|
||||
|
||||
$albums = Photo::getAlbums($a->data['user']['uid']);
|
||||
|
|
264
mod/poco.php
264
mod/poco.php
|
@ -204,142 +204,142 @@ function poco_init(App $a) {
|
|||
}
|
||||
}
|
||||
|
||||
if (is_array($contacts)) {
|
||||
if (DBA::isResult($contacts)) {
|
||||
foreach ($contacts as $contact) {
|
||||
if (!isset($contact['updated'])) {
|
||||
$contact['updated'] = '';
|
||||
}
|
||||
if (!is_array($contacts)) {
|
||||
throw new \Friendica\Network\HTTPException\InternalServerErrorException();
|
||||
}
|
||||
|
||||
if (! isset($contact['generation'])) {
|
||||
if ($global) {
|
||||
$contact['generation'] = 3;
|
||||
} elseif ($system_mode) {
|
||||
$contact['generation'] = 1;
|
||||
} else {
|
||||
$contact['generation'] = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (($contact['keywords'] == "") && isset($contact['pub_keywords'])) {
|
||||
$contact['keywords'] = $contact['pub_keywords'];
|
||||
}
|
||||
if (isset($contact['account-type'])) {
|
||||
$contact['contact-type'] = $contact['account-type'];
|
||||
}
|
||||
$about = DI::cache()->get("about:" . $contact['updated'] . ":" . $contact['nurl']);
|
||||
if (is_null($about)) {
|
||||
$about = BBCode::convert($contact['about'], false);
|
||||
DI::cache()->set("about:" . $contact['updated'] . ":" . $contact['nurl'], $about);
|
||||
}
|
||||
|
||||
// Non connected persons can only see the keywords of a Diaspora account
|
||||
if ($contact['network'] == Protocol::DIASPORA) {
|
||||
$contact['location'] = "";
|
||||
$about = "";
|
||||
}
|
||||
|
||||
$entry = [];
|
||||
if ($fields_ret['id']) {
|
||||
$entry['id'] = (int)$contact['id'];
|
||||
}
|
||||
if ($fields_ret['displayName']) {
|
||||
$entry['displayName'] = $contact['name'];
|
||||
}
|
||||
if ($fields_ret['aboutMe']) {
|
||||
$entry['aboutMe'] = $about;
|
||||
}
|
||||
if ($fields_ret['currentLocation']) {
|
||||
$entry['currentLocation'] = $contact['location'];
|
||||
}
|
||||
if ($fields_ret['generation']) {
|
||||
$entry['generation'] = (int)$contact['generation'];
|
||||
}
|
||||
if ($fields_ret['urls']) {
|
||||
$entry['urls'] = [['value' => $contact['url'], 'type' => 'profile']];
|
||||
if ($contact['addr'] && ($contact['network'] !== Protocol::MAIL)) {
|
||||
$entry['urls'][] = ['value' => 'acct:' . $contact['addr'], 'type' => 'webfinger'];
|
||||
}
|
||||
}
|
||||
if ($fields_ret['preferredUsername']) {
|
||||
$entry['preferredUsername'] = $contact['nick'];
|
||||
}
|
||||
if ($fields_ret['updated']) {
|
||||
if (! $global) {
|
||||
$entry['updated'] = $contact['success_update'];
|
||||
|
||||
if ($contact['name-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['name-date'];
|
||||
}
|
||||
if ($contact['uri-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['uri-date'];
|
||||
}
|
||||
if ($contact['avatar-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['avatar-date'];
|
||||
}
|
||||
} else {
|
||||
$entry['updated'] = $contact['updated'];
|
||||
}
|
||||
$entry['updated'] = date("c", strtotime($entry['updated']));
|
||||
}
|
||||
if ($fields_ret['photos']) {
|
||||
$entry['photos'] = [['value' => $contact['photo'], 'type' => 'profile']];
|
||||
}
|
||||
if ($fields_ret['network']) {
|
||||
$entry['network'] = $contact['network'];
|
||||
if ($entry['network'] == Protocol::STATUSNET) {
|
||||
$entry['network'] = Protocol::OSTATUS;
|
||||
}
|
||||
if (($entry['network'] == "") && ($contact['self'])) {
|
||||
$entry['network'] = Protocol::DFRN;
|
||||
}
|
||||
}
|
||||
if ($fields_ret['tags']) {
|
||||
$tags = str_replace(",", " ", $contact['keywords']);
|
||||
$tags = explode(" ", $tags);
|
||||
|
||||
$cleaned = [];
|
||||
foreach ($tags as $tag) {
|
||||
$tag = trim(strtolower($tag));
|
||||
if ($tag != "") {
|
||||
$cleaned[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
$entry['tags'] = [$cleaned];
|
||||
}
|
||||
if ($fields_ret['address']) {
|
||||
$entry['address'] = [];
|
||||
|
||||
// Deactivated. It just reveals too much data. (Although its from the default profile)
|
||||
//if (isset($rr['address']))
|
||||
// $entry['address']['streetAddress'] = $rr['address'];
|
||||
|
||||
if (isset($contact['locality'])) {
|
||||
$entry['address']['locality'] = $contact['locality'];
|
||||
}
|
||||
if (isset($contact['region'])) {
|
||||
$entry['address']['region'] = $contact['region'];
|
||||
}
|
||||
// See above
|
||||
//if (isset($rr['postal-code']))
|
||||
// $entry['address']['postalCode'] = $rr['postal-code'];
|
||||
|
||||
if (isset($contact['country'])) {
|
||||
$entry['address']['country'] = $contact['country'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($fields_ret['contactType']) {
|
||||
$entry['contactType'] = intval($contact['contact-type']);
|
||||
}
|
||||
$ret['entry'][] = $entry;
|
||||
if (DBA::isResult($contacts)) {
|
||||
foreach ($contacts as $contact) {
|
||||
if (!isset($contact['updated'])) {
|
||||
$contact['updated'] = '';
|
||||
}
|
||||
} else {
|
||||
$ret['entry'][] = [];
|
||||
|
||||
if (! isset($contact['generation'])) {
|
||||
if ($global) {
|
||||
$contact['generation'] = 3;
|
||||
} elseif ($system_mode) {
|
||||
$contact['generation'] = 1;
|
||||
} else {
|
||||
$contact['generation'] = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (($contact['keywords'] == "") && isset($contact['pub_keywords'])) {
|
||||
$contact['keywords'] = $contact['pub_keywords'];
|
||||
}
|
||||
if (isset($contact['account-type'])) {
|
||||
$contact['contact-type'] = $contact['account-type'];
|
||||
}
|
||||
$about = DI::cache()->get("about:" . $contact['updated'] . ":" . $contact['nurl']);
|
||||
if (is_null($about)) {
|
||||
$about = BBCode::convert($contact['about'], false);
|
||||
DI::cache()->set("about:" . $contact['updated'] . ":" . $contact['nurl'], $about);
|
||||
}
|
||||
|
||||
// Non connected persons can only see the keywords of a Diaspora account
|
||||
if ($contact['network'] == Protocol::DIASPORA) {
|
||||
$contact['location'] = "";
|
||||
$about = "";
|
||||
}
|
||||
|
||||
$entry = [];
|
||||
if ($fields_ret['id']) {
|
||||
$entry['id'] = (int)$contact['id'];
|
||||
}
|
||||
if ($fields_ret['displayName']) {
|
||||
$entry['displayName'] = $contact['name'];
|
||||
}
|
||||
if ($fields_ret['aboutMe']) {
|
||||
$entry['aboutMe'] = $about;
|
||||
}
|
||||
if ($fields_ret['currentLocation']) {
|
||||
$entry['currentLocation'] = $contact['location'];
|
||||
}
|
||||
if ($fields_ret['generation']) {
|
||||
$entry['generation'] = (int)$contact['generation'];
|
||||
}
|
||||
if ($fields_ret['urls']) {
|
||||
$entry['urls'] = [['value' => $contact['url'], 'type' => 'profile']];
|
||||
if ($contact['addr'] && ($contact['network'] !== Protocol::MAIL)) {
|
||||
$entry['urls'][] = ['value' => 'acct:' . $contact['addr'], 'type' => 'webfinger'];
|
||||
}
|
||||
}
|
||||
if ($fields_ret['preferredUsername']) {
|
||||
$entry['preferredUsername'] = $contact['nick'];
|
||||
}
|
||||
if ($fields_ret['updated']) {
|
||||
if (! $global) {
|
||||
$entry['updated'] = $contact['success_update'];
|
||||
|
||||
if ($contact['name-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['name-date'];
|
||||
}
|
||||
if ($contact['uri-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['uri-date'];
|
||||
}
|
||||
if ($contact['avatar-date'] > $entry['updated']) {
|
||||
$entry['updated'] = $contact['avatar-date'];
|
||||
}
|
||||
} else {
|
||||
$entry['updated'] = $contact['updated'];
|
||||
}
|
||||
$entry['updated'] = date("c", strtotime($entry['updated']));
|
||||
}
|
||||
if ($fields_ret['photos']) {
|
||||
$entry['photos'] = [['value' => $contact['photo'], 'type' => 'profile']];
|
||||
}
|
||||
if ($fields_ret['network']) {
|
||||
$entry['network'] = $contact['network'];
|
||||
if ($entry['network'] == Protocol::STATUSNET) {
|
||||
$entry['network'] = Protocol::OSTATUS;
|
||||
}
|
||||
if (($entry['network'] == "") && ($contact['self'])) {
|
||||
$entry['network'] = Protocol::DFRN;
|
||||
}
|
||||
}
|
||||
if ($fields_ret['tags']) {
|
||||
$tags = str_replace(",", " ", $contact['keywords']);
|
||||
$tags = explode(" ", $tags);
|
||||
|
||||
$cleaned = [];
|
||||
foreach ($tags as $tag) {
|
||||
$tag = trim(strtolower($tag));
|
||||
if ($tag != "") {
|
||||
$cleaned[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
$entry['tags'] = [$cleaned];
|
||||
}
|
||||
if ($fields_ret['address']) {
|
||||
$entry['address'] = [];
|
||||
|
||||
// Deactivated. It just reveals too much data. (Although its from the default profile)
|
||||
//if (isset($rr['address']))
|
||||
// $entry['address']['streetAddress'] = $rr['address'];
|
||||
|
||||
if (isset($contact['locality'])) {
|
||||
$entry['address']['locality'] = $contact['locality'];
|
||||
}
|
||||
if (isset($contact['region'])) {
|
||||
$entry['address']['region'] = $contact['region'];
|
||||
}
|
||||
// See above
|
||||
//if (isset($rr['postal-code']))
|
||||
// $entry['address']['postalCode'] = $rr['postal-code'];
|
||||
|
||||
if (isset($contact['country'])) {
|
||||
$entry['address']['country'] = $contact['country'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($fields_ret['contactType']) {
|
||||
$entry['contactType'] = intval($contact['contact-type']);
|
||||
}
|
||||
$ret['entry'][] = $entry;
|
||||
}
|
||||
} else {
|
||||
throw new \Friendica\Network\HTTPException\InternalServerErrorException();
|
||||
$ret['entry'][] = [];
|
||||
}
|
||||
|
||||
Logger::log("End of poco", Logger::DEBUG);
|
||||
|
|
|
@ -67,7 +67,7 @@ function videos_init(App $a)
|
|||
'$photo' => $profile['photo'],
|
||||
'$addr' => $profile['addr'] ?? '',
|
||||
'$account_type' => $account_type,
|
||||
'$about' => BBCode::convert($profile['about'] ?? ''),
|
||||
'$about' => BBCode::convert($profile['about']),
|
||||
]);
|
||||
|
||||
// If not there, create 'aside' empty
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -167,252 +167,238 @@ class HTML
|
|||
{
|
||||
$message = str_replace("\r", "", $message);
|
||||
|
||||
// Removing code blocks before the whitespace removal processing below
|
||||
$codeblocks = [];
|
||||
$message = Strings::performWithEscapedBlocks($message, '#<pre><code.*</code></pre>#iUs', function ($message) {
|
||||
$message = str_replace(
|
||||
[
|
||||
"<li><p>",
|
||||
"</p></li>",
|
||||
],
|
||||
[
|
||||
"<li>",
|
||||
"</li>",
|
||||
],
|
||||
$message
|
||||
);
|
||||
|
||||
// remove namespaces
|
||||
$message = preg_replace('=<(\w+):(.+?)>=', '<removeme>', $message);
|
||||
$message = preg_replace('=</(\w+):(.+?)>=', '</removeme>', $message);
|
||||
|
||||
$doc = new DOMDocument();
|
||||
$doc->preserveWhiteSpace = false;
|
||||
|
||||
$message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8");
|
||||
|
||||
@$doc->loadHTML($message, LIBXML_HTML_NODEFDTD);
|
||||
|
||||
XML::deleteNode($doc, 'style');
|
||||
XML::deleteNode($doc, 'head');
|
||||
XML::deleteNode($doc, 'title');
|
||||
XML::deleteNode($doc, 'meta');
|
||||
XML::deleteNode($doc, 'xml');
|
||||
XML::deleteNode($doc, 'removeme');
|
||||
|
||||
$xpath = new DomXPath($doc);
|
||||
$list = $xpath->query("//pre");
|
||||
foreach ($list as $node) {
|
||||
// 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();
|
||||
$message = str_replace(["\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"], ["<", ">", "<br />", " ", ""], $message);
|
||||
$message = preg_replace('= [\s]*=i', " ", $message);
|
||||
|
||||
@$doc->loadHTML($message, LIBXML_HTML_NODEFDTD);
|
||||
|
||||
self::tagToBBCode($doc, 'html', [], "", "");
|
||||
self::tagToBBCode($doc, 'body', [], "", "");
|
||||
|
||||
// Outlook-Quote - Variant 1
|
||||
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]'
|
||||
);
|
||||
|
||||
// MyBB-Stuff
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'text-decoration: underline;'], '[u]', '[/u]');
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'font-style: italic;'], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'font-weight: bold;'], '[b]', '[/b]');
|
||||
|
||||
/* self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[font=$1][size=$2][color=$3]', '[/color][/size][/font]');
|
||||
self::node2BBCode($doc, 'font', array('size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[size=$1][color=$2]', '[/color][/size]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(.+)/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'color'=>'/(.+)/'), '[font=$1][color=$3]', '[/color][/font]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/'), '[font=$1]', '[/font]');
|
||||
self::node2BBCode($doc, 'font', array('size'=>'/(\d+)/'), '[size=$1]', '[/size]');
|
||||
self::node2BBCode($doc, 'font', array('color'=>'/(.+)/'), '[color=$1]', '[/color]');
|
||||
*/
|
||||
// Untested
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*font-family:\s*(.+?)[,;].*color:\s*(.+?)[,;].*/'), '[size=$1][font=$2][color=$3]', '[/color][/font][/size]');
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(\d+)[,;].*/'), '[size=$1]', '[/size]');
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*/'), '[size=$1]', '[/size]');
|
||||
|
||||
self::tagToBBCode($doc, 'span', ['style' => '/.*color:\s*(.+?)[,;].*/'], '[color="$1"]', '[/color]');
|
||||
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)pt.*/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)px.*/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]');
|
||||
// Importing the classes - interesting for importing of posts from third party networks that were exported from friendica
|
||||
// Test
|
||||
//self::node2BBCode($doc, 'span', array('class'=>'/([\w ]+)/'), '[class=$1]', '[/class]');
|
||||
self::tagToBBCode($doc, 'span', ['class' => 'type-link'], '[class=type-link]', '[/class]');
|
||||
self::tagToBBCode($doc, 'span', ['class' => 'type-video'], '[class=type-video]', '[/class]');
|
||||
|
||||
self::tagToBBCode($doc, 'strong', [], '[b]', '[/b]');
|
||||
self::tagToBBCode($doc, 'em', [], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'b', [], '[b]', '[/b]');
|
||||
self::tagToBBCode($doc, 'i', [], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'u', [], '[u]', '[/u]');
|
||||
self::tagToBBCode($doc, 's', [], '[s]', '[/s]');
|
||||
self::tagToBBCode($doc, 'del', [], '[s]', '[/s]');
|
||||
self::tagToBBCode($doc, 'strike', [], '[s]', '[/s]');
|
||||
|
||||
self::tagToBBCode($doc, 'big', [], "[size=large]", "[/size]");
|
||||
self::tagToBBCode($doc, 'small', [], "[size=small]", "[/size]");
|
||||
|
||||
self::tagToBBCode($doc, 'blockquote', [], '[quote]', '[/quote]');
|
||||
|
||||
self::tagToBBCode($doc, 'br', [], "\n", '');
|
||||
|
||||
self::tagToBBCode($doc, 'p', ['class' => 'MsoNormal'], "\n", "");
|
||||
self::tagToBBCode($doc, 'div', ['class' => 'MsoNormal'], "\r", "");
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
self::tagToBBCode($doc, 'pre', [], "", "");
|
||||
|
||||
self::tagToBBCode($doc, 'div', [], "\r", "\r");
|
||||
self::tagToBBCode($doc, 'p', [], "\n", "\n");
|
||||
|
||||
self::tagToBBCode($doc, 'ul', [], "[list]", "[/list]");
|
||||
self::tagToBBCode($doc, 'ol', [], "[list=1]", "[/list]");
|
||||
self::tagToBBCode($doc, 'li', [], "[*]", "");
|
||||
|
||||
self::tagToBBCode($doc, 'hr', [], "[hr]", "");
|
||||
|
||||
self::tagToBBCode($doc, 'table', [], "[table]", "[/table]");
|
||||
self::tagToBBCode($doc, 'th', [], "[th]", "[/th]");
|
||||
self::tagToBBCode($doc, 'tr', [], "[tr]", "[/tr]");
|
||||
self::tagToBBCode($doc, 'td', [], "[td]", "[/td]");
|
||||
|
||||
self::tagToBBCode($doc, 'h1', [], "[h1]", "[/h1]");
|
||||
self::tagToBBCode($doc, 'h2', [], "[h2]", "[/h2]");
|
||||
self::tagToBBCode($doc, 'h3', [], "[h3]", "[/h3]");
|
||||
self::tagToBBCode($doc, 'h4', [], "[h4]", "[/h4]");
|
||||
self::tagToBBCode($doc, 'h5', [], "[h5]", "[/h5]");
|
||||
self::tagToBBCode($doc, 'h6', [], "[h6]", "[/h6]");
|
||||
|
||||
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]', true);
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'width' => '/(\d+)/', 'height' => '/(\d+)/'], '[img=$2x$3]$1', '[/img]', true);
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], '[img]$1', '[/img]', true);
|
||||
|
||||
|
||||
self::tagToBBCode($doc, 'video', ['src' => '/(.+)/'], '[video]$1', '[/video]', true);
|
||||
self::tagToBBCode($doc, 'audio', ['src' => '/(.+)/'], '[audio]$1', '[/audio]', true);
|
||||
self::tagToBBCode($doc, 'iframe', ['src' => '/(.+)/'], '[iframe]$1', '[/iframe]', true);
|
||||
|
||||
self::tagToBBCode($doc, 'key', [], '[code]', '[/code]');
|
||||
self::tagToBBCode($doc, 'code', [], '[code]', '[/code]');
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
|
||||
// I'm removing something really disturbing
|
||||
// Don't know exactly what it is
|
||||
$message = str_replace(chr(194) . chr(160), ' ', $message);
|
||||
|
||||
$message = str_replace(" ", " ", $message);
|
||||
|
||||
// removing multiple DIVs
|
||||
$message = preg_replace('=\r *\r=i', "\n", $message);
|
||||
$message = str_replace("\r", "\n", $message);
|
||||
|
||||
Hook::callAll('html2bbcode', $message);
|
||||
|
||||
$message = strip_tags($message);
|
||||
|
||||
$message = html_entity_decode($message, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// remove quotes if they don't make sense
|
||||
$message = preg_replace('=\[/quote\][\s]*\[quote\]=i', "\n", $message);
|
||||
|
||||
$message = preg_replace('=\[quote\]\s*=i', "[quote]", $message);
|
||||
$message = preg_replace('=\s*\[/quote\]=i', "[/quote]", $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n \n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n\n\n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace(
|
||||
[
|
||||
"[/size]\n\n",
|
||||
"\n[hr]",
|
||||
"[hr]\n",
|
||||
"\n[list",
|
||||
"[/list]\n",
|
||||
"\n[/",
|
||||
"[list]\n",
|
||||
"[list=1]\n",
|
||||
"\n[*]"],
|
||||
[
|
||||
"[/size]\n",
|
||||
"[hr]",
|
||||
"[hr]",
|
||||
"[list",
|
||||
"[/list]",
|
||||
"[/",
|
||||
"[list]",
|
||||
"[list=1]",
|
||||
"[*]"],
|
||||
$message
|
||||
);
|
||||
} while ($message != $oldmessage);
|
||||
|
||||
$message = str_replace(
|
||||
['[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'],
|
||||
['[b]', '[/b]', '[i]', '[/i]'],
|
||||
$message
|
||||
);
|
||||
|
||||
// Handling Yahoo style of mails
|
||||
$message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message);
|
||||
|
||||
return $message;
|
||||
});
|
||||
|
||||
$message = preg_replace_callback(
|
||||
'#<pre><code(?: class="language-([^"]*)")?>(.*)</code></pre>#iUs',
|
||||
function ($matches) use (&$codeblocks) {
|
||||
$return = '[codeblock-' . count($codeblocks) . ']';
|
||||
|
||||
function ($matches) {
|
||||
$prefix = '[code]';
|
||||
if ($matches[1] != '') {
|
||||
$prefix = '[code=' . $matches[1] . ']';
|
||||
}
|
||||
|
||||
$codeblocks[] = $prefix . PHP_EOL . trim($matches[2]) . PHP_EOL . '[/code]';
|
||||
return $return;
|
||||
},
|
||||
$message
|
||||
);
|
||||
|
||||
$message = str_replace(
|
||||
[
|
||||
"<li><p>",
|
||||
"</p></li>",
|
||||
],
|
||||
[
|
||||
"<li>",
|
||||
"</li>",
|
||||
],
|
||||
$message
|
||||
);
|
||||
|
||||
// remove namespaces
|
||||
$message = preg_replace('=<(\w+):(.+?)>=', '<removeme>', $message);
|
||||
$message = preg_replace('=</(\w+):(.+?)>=', '</removeme>', $message);
|
||||
|
||||
$doc = new DOMDocument();
|
||||
$doc->preserveWhiteSpace = false;
|
||||
|
||||
$message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8");
|
||||
|
||||
@$doc->loadHTML($message, LIBXML_HTML_NODEFDTD);
|
||||
|
||||
XML::deleteNode($doc, 'style');
|
||||
XML::deleteNode($doc, 'head');
|
||||
XML::deleteNode($doc, 'title');
|
||||
XML::deleteNode($doc, 'meta');
|
||||
XML::deleteNode($doc, 'xml');
|
||||
XML::deleteNode($doc, 'removeme');
|
||||
|
||||
$xpath = new DomXPath($doc);
|
||||
$list = $xpath->query("//pre");
|
||||
foreach ($list as $node) {
|
||||
// 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();
|
||||
$message = str_replace(["\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"], ["<", ">", "<br />", " ", ""], $message);
|
||||
$message = preg_replace('= [\s]*=i', " ", $message);
|
||||
|
||||
@$doc->loadHTML($message, LIBXML_HTML_NODEFDTD);
|
||||
|
||||
self::tagToBBCode($doc, 'html', [], "", "");
|
||||
self::tagToBBCode($doc, 'body', [], "", "");
|
||||
|
||||
// Outlook-Quote - Variant 1
|
||||
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]'
|
||||
);
|
||||
|
||||
// MyBB-Stuff
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'text-decoration: underline;'], '[u]', '[/u]');
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'font-style: italic;'], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'font-weight: bold;'], '[b]', '[/b]');
|
||||
|
||||
/* self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[font=$1][size=$2][color=$3]', '[/color][/size][/font]');
|
||||
self::node2BBCode($doc, 'font', array('size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[size=$1][color=$2]', '[/color][/size]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(.+)/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'color'=>'/(.+)/'), '[font=$1][color=$3]', '[/color][/font]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/'), '[font=$1]', '[/font]');
|
||||
self::node2BBCode($doc, 'font', array('size'=>'/(\d+)/'), '[size=$1]', '[/size]');
|
||||
self::node2BBCode($doc, 'font', array('color'=>'/(.+)/'), '[color=$1]', '[/color]');
|
||||
*/
|
||||
// Untested
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*font-family:\s*(.+?)[,;].*color:\s*(.+?)[,;].*/'), '[size=$1][font=$2][color=$3]', '[/color][/font][/size]');
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(\d+)[,;].*/'), '[size=$1]', '[/size]');
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*/'), '[size=$1]', '[/size]');
|
||||
|
||||
self::tagToBBCode($doc, 'span', ['style' => '/.*color:\s*(.+?)[,;].*/'], '[color="$1"]', '[/color]');
|
||||
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)pt.*/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)px.*/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]');
|
||||
// Importing the classes - interesting for importing of posts from third party networks that were exported from friendica
|
||||
// Test
|
||||
//self::node2BBCode($doc, 'span', array('class'=>'/([\w ]+)/'), '[class=$1]', '[/class]');
|
||||
self::tagToBBCode($doc, 'span', ['class' => 'type-link'], '[class=type-link]', '[/class]');
|
||||
self::tagToBBCode($doc, 'span', ['class' => 'type-video'], '[class=type-video]', '[/class]');
|
||||
|
||||
self::tagToBBCode($doc, 'strong', [], '[b]', '[/b]');
|
||||
self::tagToBBCode($doc, 'em', [], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'b', [], '[b]', '[/b]');
|
||||
self::tagToBBCode($doc, 'i', [], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'u', [], '[u]', '[/u]');
|
||||
self::tagToBBCode($doc, 's', [], '[s]', '[/s]');
|
||||
self::tagToBBCode($doc, 'del', [], '[s]', '[/s]');
|
||||
self::tagToBBCode($doc, 'strike', [], '[s]', '[/s]');
|
||||
|
||||
self::tagToBBCode($doc, 'big', [], "[size=large]", "[/size]");
|
||||
self::tagToBBCode($doc, 'small', [], "[size=small]", "[/size]");
|
||||
|
||||
self::tagToBBCode($doc, 'blockquote', [], '[quote]', '[/quote]');
|
||||
|
||||
self::tagToBBCode($doc, 'br', [], "\n", '');
|
||||
|
||||
self::tagToBBCode($doc, 'p', ['class' => 'MsoNormal'], "\n", "");
|
||||
self::tagToBBCode($doc, 'div', ['class' => 'MsoNormal'], "\r", "");
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
self::tagToBBCode($doc, 'pre', [], "", "");
|
||||
|
||||
self::tagToBBCode($doc, 'div', [], "\r", "\r");
|
||||
self::tagToBBCode($doc, 'p', [], "\n", "\n");
|
||||
|
||||
self::tagToBBCode($doc, 'ul', [], "[list]", "[/list]");
|
||||
self::tagToBBCode($doc, 'ol', [], "[list=1]", "[/list]");
|
||||
self::tagToBBCode($doc, 'li', [], "[*]", "");
|
||||
|
||||
self::tagToBBCode($doc, 'hr', [], "[hr]", "");
|
||||
|
||||
self::tagToBBCode($doc, 'table', [], "[table]", "[/table]");
|
||||
self::tagToBBCode($doc, 'th', [], "[th]", "[/th]");
|
||||
self::tagToBBCode($doc, 'tr', [], "[tr]", "[/tr]");
|
||||
self::tagToBBCode($doc, 'td', [], "[td]", "[/td]");
|
||||
|
||||
self::tagToBBCode($doc, 'h1', [], "[h1]", "[/h1]");
|
||||
self::tagToBBCode($doc, 'h2', [], "[h2]", "[/h2]");
|
||||
self::tagToBBCode($doc, 'h3', [], "[h3]", "[/h3]");
|
||||
self::tagToBBCode($doc, 'h4', [], "[h4]", "[/h4]");
|
||||
self::tagToBBCode($doc, 'h5', [], "[h5]", "[/h5]");
|
||||
self::tagToBBCode($doc, 'h6', [], "[h6]", "[/h6]");
|
||||
|
||||
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]', true);
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'width' => '/(\d+)/', 'height' => '/(\d+)/'], '[img=$2x$3]$1', '[/img]', true);
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], '[img]$1', '[/img]', true);
|
||||
|
||||
|
||||
self::tagToBBCode($doc, 'video', ['src' => '/(.+)/'], '[video]$1', '[/video]', true);
|
||||
self::tagToBBCode($doc, 'audio', ['src' => '/(.+)/'], '[audio]$1', '[/audio]', true);
|
||||
self::tagToBBCode($doc, 'iframe', ['src' => '/(.+)/'], '[iframe]$1', '[/iframe]', true);
|
||||
|
||||
self::tagToBBCode($doc, 'key', [], '[code]', '[/code]');
|
||||
self::tagToBBCode($doc, 'code', [], '[code]', '[/code]');
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
|
||||
// I'm removing something really disturbing
|
||||
// Don't know exactly what it is
|
||||
$message = str_replace(chr(194) . chr(160), ' ', $message);
|
||||
|
||||
$message = str_replace(" ", " ", $message);
|
||||
|
||||
// removing multiple DIVs
|
||||
$message = preg_replace('=\r *\r=i', "\n", $message);
|
||||
$message = str_replace("\r", "\n", $message);
|
||||
|
||||
Hook::callAll('html2bbcode', $message);
|
||||
|
||||
$message = strip_tags($message);
|
||||
|
||||
$message = html_entity_decode($message, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// remove quotes if they don't make sense
|
||||
$message = preg_replace('=\[/quote\][\s]*\[quote\]=i', "\n", $message);
|
||||
|
||||
$message = preg_replace('=\[quote\]\s*=i', "[quote]", $message);
|
||||
$message = preg_replace('=\s*\[/quote\]=i', "[/quote]", $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n \n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n\n\n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace(
|
||||
[
|
||||
"[/size]\n\n",
|
||||
"\n[hr]",
|
||||
"[hr]\n",
|
||||
"\n[list",
|
||||
"[/list]\n",
|
||||
"\n[/",
|
||||
"[list]\n",
|
||||
"[list=1]\n",
|
||||
"\n[*]"],
|
||||
[
|
||||
"[/size]\n",
|
||||
"[hr]",
|
||||
"[hr]",
|
||||
"[list",
|
||||
"[/list]",
|
||||
"[/",
|
||||
"[list]",
|
||||
"[list=1]",
|
||||
"[*]"],
|
||||
$message
|
||||
);
|
||||
} while ($message != $oldmessage);
|
||||
|
||||
$message = str_replace(
|
||||
['[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'],
|
||||
['[b]', '[/b]', '[i]', '[/i]'],
|
||||
$message
|
||||
);
|
||||
|
||||
// Handling Yahoo style of mails
|
||||
$message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message);
|
||||
|
||||
// Restore code blocks
|
||||
$message = preg_replace_callback(
|
||||
'#\[codeblock-([0-9]+)\]#iU',
|
||||
function ($matches) use ($codeblocks) {
|
||||
$return = '';
|
||||
if (isset($codeblocks[intval($matches[1])])) {
|
||||
$return = $codeblocks[$matches[1]];
|
||||
}
|
||||
return $return;
|
||||
return $prefix . PHP_EOL . trim($matches[2]) . PHP_EOL . '[/code]';
|
||||
},
|
||||
$message
|
||||
);
|
||||
|
|
|
@ -1780,7 +1780,7 @@ class Item
|
|||
|
||||
|
||||
// Check for hashtags in the body and repair or add hashtag links
|
||||
self::setHashtags($item);
|
||||
$item['body'] = self::setHashtags($item['body']);
|
||||
|
||||
// Fill the cache field
|
||||
self::putInCache($item);
|
||||
|
@ -2424,84 +2424,69 @@ class Item
|
|||
}
|
||||
}
|
||||
|
||||
public static function setHashtags(&$item)
|
||||
public static function setHashtags($body)
|
||||
{
|
||||
$tags = BBCode::getTags($item["body"]);
|
||||
$body = BBCode::performWithEscapedTags($body, ['noparse', 'pre', 'code'], function ($body) {
|
||||
$tags = BBCode::getTags($body);
|
||||
|
||||
// No hashtags?
|
||||
if (!count($tags)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// What happens in [code], stays in [code]!
|
||||
// escape the # and the [
|
||||
// hint: we will also get in trouble with #tags, when we want markdown in posts -> ### Headline 3
|
||||
$item["body"] = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism",
|
||||
function ($match) {
|
||||
// we truly ESCape all # and [ to prevent gettin weird tags in [code] blocks
|
||||
$find = ['#', '['];
|
||||
$replace = [chr(27).'sharp', chr(27).'leftsquarebracket'];
|
||||
return ("[code" . $match[1] . "]" . str_replace($find, $replace, $match[2]) . "[/code]");
|
||||
}, $item["body"]);
|
||||
|
||||
// This sorting is important when there are hashtags that are part of other hashtags
|
||||
// Otherwise there could be problems with hashtags like #test and #test2
|
||||
// Because of this we are sorting from the longest to the shortest tag.
|
||||
usort($tags, function($a, $b) {
|
||||
return strlen($b) <=> strlen($a);
|
||||
});
|
||||
|
||||
$URLSearchString = "^\[\]";
|
||||
|
||||
// All hashtags should point to the home server if "local_tags" is activated
|
||||
if (DI::config()->get('system', 'local_tags')) {
|
||||
$item["body"] = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
"#[url=".DI::baseUrl()."/search?tag=$2]$2[/url]", $item["body"]);
|
||||
}
|
||||
|
||||
// mask hashtags inside of url, bookmarks and attachments to avoid urls in urls
|
||||
$item["body"] = preg_replace_callback("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
function ($match) {
|
||||
return ("[url=" . str_replace("#", "#", $match[1]) . "]" . str_replace("#", "#", $match[2]) . "[/url]");
|
||||
}, $item["body"]);
|
||||
|
||||
$item["body"] = preg_replace_callback("/\[bookmark\=([$URLSearchString]*)\](.*?)\[\/bookmark\]/ism",
|
||||
function ($match) {
|
||||
return ("[bookmark=" . str_replace("#", "#", $match[1]) . "]" . str_replace("#", "#", $match[2]) . "[/bookmark]");
|
||||
}, $item["body"]);
|
||||
|
||||
$item["body"] = preg_replace_callback("/\[attachment (.*)\](.*?)\[\/attachment\]/ism",
|
||||
function ($match) {
|
||||
return ("[attachment " . str_replace("#", "#", $match[1]) . "]" . $match[2] . "[/attachment]");
|
||||
}, $item["body"]);
|
||||
|
||||
// Repair recursive urls
|
||||
$item["body"] = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
"#$2", $item["body"]);
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=') || strlen($tag) < 2 || $tag[1] == '#') {
|
||||
continue;
|
||||
// No hashtags?
|
||||
if (!count($tags)) {
|
||||
return $body;
|
||||
}
|
||||
|
||||
$basetag = str_replace('_',' ',substr($tag,1));
|
||||
$newtag = '#[url=' . DI::baseUrl() . '/search?tag=' . $basetag . ']' . $basetag . '[/url]';
|
||||
// This sorting is important when there are hashtags that are part of other hashtags
|
||||
// Otherwise there could be problems with hashtags like #test and #test2
|
||||
// Because of this we are sorting from the longest to the shortest tag.
|
||||
usort($tags, function ($a, $b) {
|
||||
return strlen($b) <=> strlen($a);
|
||||
});
|
||||
|
||||
$item["body"] = str_replace($tag, $newtag, $item["body"]);
|
||||
}
|
||||
$URLSearchString = "^\[\]";
|
||||
|
||||
// Convert back the masked hashtags
|
||||
$item["body"] = str_replace("#", "#", $item["body"]);
|
||||
// All hashtags should point to the home server if "local_tags" is activated
|
||||
if (DI::config()->get('system', 'local_tags')) {
|
||||
$body = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
"#[url=" . DI::baseUrl() . "/search?tag=$2]$2[/url]", $body);
|
||||
}
|
||||
|
||||
// Remember! What happens in [code], stays in [code]
|
||||
// roleback the # and [
|
||||
$item["body"] = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism",
|
||||
function ($match) {
|
||||
// we truly unESCape all sharp and leftsquarebracket
|
||||
$find = [chr(27).'sharp', chr(27).'leftsquarebracket'];
|
||||
$replace = ['#', '['];
|
||||
return ("[code" . $match[1] . "]" . str_replace($find, $replace, $match[2]) . "[/code]");
|
||||
}, $item["body"]);
|
||||
// mask hashtags inside of url, bookmarks and attachments to avoid urls in urls
|
||||
$body = preg_replace_callback("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
function ($match) {
|
||||
return ("[url=" . str_replace("#", "#", $match[1]) . "]" . str_replace("#", "#", $match[2]) . "[/url]");
|
||||
}, $body);
|
||||
|
||||
$body = preg_replace_callback("/\[bookmark\=([$URLSearchString]*)\](.*?)\[\/bookmark\]/ism",
|
||||
function ($match) {
|
||||
return ("[bookmark=" . str_replace("#", "#", $match[1]) . "]" . str_replace("#", "#", $match[2]) . "[/bookmark]");
|
||||
}, $body);
|
||||
|
||||
$body = preg_replace_callback("/\[attachment (.*)\](.*?)\[\/attachment\]/ism",
|
||||
function ($match) {
|
||||
return ("[attachment " . str_replace("#", "#", $match[1]) . "]" . $match[2] . "[/attachment]");
|
||||
}, $body);
|
||||
|
||||
// Repair recursive urls
|
||||
$body = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
"#$2", $body);
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=') || strlen($tag) < 2 || $tag[1] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$basetag = str_replace('_', ' ', substr($tag, 1));
|
||||
$newtag = '#[url=' . DI::baseUrl() . '/search?tag=' . $basetag . ']' . $basetag . '[/url]';
|
||||
|
||||
$body = str_replace($tag, $newtag, $body);
|
||||
}
|
||||
|
||||
// Convert back the masked hashtags
|
||||
$body = str_replace("#", "#", $body);
|
||||
|
||||
return $body;
|
||||
});
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -70,7 +70,7 @@ class Notify extends BaseModel
|
|||
private function setNameCache()
|
||||
{
|
||||
try {
|
||||
$this->name_cache = strip_tags(BBCode::convert($this->source_name ?? ''));
|
||||
$this->name_cache = strip_tags(BBCode::convert($this->source_name));
|
||||
} catch (InternalServerErrorException $e) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,14 +102,12 @@ class Babel extends BaseModule
|
|||
'content' => visible_whitespace($bbcode4)
|
||||
];
|
||||
|
||||
$item = ['body' => $bbcode];
|
||||
|
||||
$tags = Text\BBCode::getTags($bbcode);
|
||||
|
||||
Item::setHashtags($item);
|
||||
$body = Item::setHashtags($bbcode);
|
||||
$results[] = [
|
||||
'title' => DI::l10n()->t('Item Body'),
|
||||
'content' => visible_whitespace($item['body'])
|
||||
'content' => visible_whitespace($body)
|
||||
];
|
||||
$results[] = [
|
||||
'title' => DI::l10n()->t('Item Tags'),
|
||||
|
@ -125,9 +123,7 @@ class Babel extends BaseModule
|
|||
|
||||
$markdown = XML::unescape($diaspora);
|
||||
case 'markdown':
|
||||
if (!isset($markdown)) {
|
||||
$markdown = trim($_REQUEST['text']);
|
||||
}
|
||||
$markdown = $markdown ?? trim($_REQUEST['text']);
|
||||
|
||||
$results[] = [
|
||||
'title' => DI::l10n()->t('Source input (Markdown)'),
|
||||
|
|
|
@ -472,4 +472,52 @@ class Strings
|
|||
|
||||
return mb_substr($string, 0, $start) . $replacement . mb_substr($string, $start + $length, $string_length - $start - $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a custom function on a text after having escaped blocks matched by the provided regular expressions.
|
||||
* Only full matches are used, capturing group are ignored.
|
||||
*
|
||||
* To change the provided text, the callback function needs to return it and this function will return the modified
|
||||
* version as well after having restored the escaped blocks.
|
||||
*
|
||||
* @param string $text
|
||||
* @param string $regex
|
||||
* @param callable $callback
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function performWithEscapedBlocks(string $text, string $regex, callable $callback)
|
||||
{
|
||||
// Enables nested use
|
||||
$executionId = random_int(PHP_INT_MAX / 10, PHP_INT_MAX);
|
||||
|
||||
$blocks = [];
|
||||
|
||||
$text = preg_replace_callback($regex,
|
||||
function ($matches) use ($executionId, &$blocks) {
|
||||
$return = '«block-' . $executionId . '-' . count($blocks) . '»';
|
||||
|
||||
$blocks[] = $matches[0];
|
||||
|
||||
return $return;
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
$text = $callback($text) ?? '';
|
||||
|
||||
// Restore code blocks
|
||||
$text = preg_replace_callback('/«block-' . $executionId . '-([0-9]+)»/iU',
|
||||
function ($matches) use ($blocks) {
|
||||
$return = $matches[0];
|
||||
if (isset($blocks[intval($matches[1])])) {
|
||||
$return = $blocks[$matches[1]];
|
||||
}
|
||||
return $return;
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3852,7 +3852,7 @@ class ApiTest extends DatabaseTest
|
|||
$assertXml=<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<notes>
|
||||
<note id="1" hash="" type="8" name="Reply to" url="http://localhost/display/1" photo="http://localhost/" date="2020-01-01 12:12:02" msg="A test reply from an item" uid="42" uri-id="" link="http://localhost/notification/1" iid="4" parent="0" parent-uri-id="" seen="0" verb="" otype="item" name_cache="" msg_cache="A test reply from an item" timestamp="1577880722" date_rel="{$dateRel}" msg_html="A test reply from an item" msg_plain="A test reply from an item"/>
|
||||
<note id="1" hash="" type="8" name="Reply to" url="http://localhost/display/1" photo="http://localhost/" date="2020-01-01 12:12:02" msg="A test reply from an item" uid="42" uri-id="" link="http://localhost/notification/1" iid="4" parent="0" parent-uri-id="" seen="0" verb="" otype="item" name_cache="Reply to" msg_cache="A test reply from an item" timestamp="1577880722" date_rel="{$dateRel}" msg_html="A test reply from an item" msg_plain="A test reply from an item"/>
|
||||
</notes>
|
||||
XML;
|
||||
$this->assertXmlStringEqualsXmlString($assertXml, $result);
|
||||
|
|
|
@ -194,4 +194,30 @@ class StringsTest extends TestCase
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testPerformWithEscapedBlocks()
|
||||
{
|
||||
$originalText = '[noparse][/noparse][nobb]nobb[/nobb][noparse]noparse[/noparse]';
|
||||
|
||||
$text = Strings::performWithEscapedBlocks($originalText, '#[(?:noparse|nobb)].*?\[/(?:noparse|nobb)]#is', function ($text) {
|
||||
return $text;
|
||||
});
|
||||
|
||||
$this->assertEquals($originalText, $text);
|
||||
}
|
||||
|
||||
public function testPerformWithEscapedBlocksNested()
|
||||
{
|
||||
$originalText = '[noparse][/noparse][nobb]nobb[/nobb][noparse]noparse[/noparse]';
|
||||
|
||||
$text = Strings::performWithEscapedBlocks($originalText, '#[nobb].*?\[/nobb]#is', function ($text) {
|
||||
$text = Strings::performWithEscapedBlocks($text, '#[noparse].*?\[/noparse]#is', function ($text) {
|
||||
return $text;
|
||||
});
|
||||
|
||||
return $text;
|
||||
});
|
||||
|
||||
$this->assertEquals($originalText, $text);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user