Split text.php to HTML class

move functions from text.php to src/Content/Text/HTML
This commit is contained in:
Adam Magness 2018-11-05 20:27:22 -05:00
parent e8891e9ba4
commit e31dde6084
2 changed files with 354 additions and 321 deletions

View File

@ -163,19 +163,6 @@ function autoname($len) {
return $word;
}
/**
* Loader for infinite scrolling
* @return string html for loader
*/
function scroll_loader() {
$tpl = Renderer::getMarkupTemplate("scroll_loader.tpl");
return Renderer::replaceMacros($tpl, [
'wait' => L10n::t('Loading more entries...'),
'end' => L10n::t('The end')
]);
}
/**
* Turn user/group ACLs stored as angle bracketed text into arrays
*
@ -349,188 +336,6 @@ function qp($s) {
return str_replace("%", "=", rawurlencode($s));
}
/**
* Get html for contact block.
*
* @template contact_block.tpl
* @hook contact_block_end (contacts=>array, output=>string)
* @return string
*/
function contact_block() {
$o = '';
$a = get_app();
$shown = PConfig::get($a->profile['uid'], 'system', 'display_friend_count', 24);
if ($shown == 0) {
return;
}
if (!is_array($a->profile) || $a->profile['hide-friends']) {
return $o;
}
$r = q("SELECT COUNT(*) AS `total` FROM `contact`
WHERE `uid` = %d AND NOT `self` AND NOT `blocked`
AND NOT `pending` AND NOT `hidden` AND NOT `archive`
AND `network` IN ('%s', '%s', '%s')",
intval($a->profile['uid']),
DBA::escape(Protocol::DFRN),
DBA::escape(Protocol::OSTATUS),
DBA::escape(Protocol::DIASPORA)
);
if (DBA::isResult($r)) {
$total = intval($r[0]['total']);
}
if (!$total) {
$contacts = L10n::t('No contacts');
$micropro = null;
} else {
// Splitting the query in two parts makes it much faster
$r = q("SELECT `id` FROM `contact`
WHERE `uid` = %d AND NOT `self` AND NOT `blocked`
AND NOT `pending` AND NOT `hidden` AND NOT `archive`
AND `network` IN ('%s', '%s', '%s')
ORDER BY RAND() LIMIT %d",
intval($a->profile['uid']),
DBA::escape(Protocol::DFRN),
DBA::escape(Protocol::OSTATUS),
DBA::escape(Protocol::DIASPORA),
intval($shown)
);
if (DBA::isResult($r)) {
$contacts = [];
foreach ($r AS $contact) {
$contacts[] = $contact["id"];
}
$r = q("SELECT `id`, `uid`, `addr`, `url`, `name`, `thumb`, `network` FROM `contact` WHERE `id` IN (%s)",
DBA::escape(implode(",", $contacts)));
if (DBA::isResult($r)) {
$contacts = L10n::tt('%d Contact', '%d Contacts', $total);
$micropro = [];
foreach ($r as $rr) {
$micropro[] = micropro($rr, true, 'mpfriend');
}
}
}
}
$tpl = Renderer::getMarkupTemplate('contact_block.tpl');
$o = Renderer::replaceMacros($tpl, [
'$contacts' => $contacts,
'$nickname' => $a->profile['nickname'],
'$viewcontacts' => L10n::t('View Contacts'),
'$micropro' => $micropro,
]);
$arr = ['contacts' => $r, 'output' => $o];
Addon::callHooks('contact_block_end', $arr);
return $o;
}
/**
* @brief Format contacts as picture links or as texxt links
*
* @param array $contact Array with contacts which contains an array with
* int 'id' => The ID of the contact
* int 'uid' => The user ID of the user who owns this data
* string 'name' => The name of the contact
* string 'url' => The url to the profile page of the contact
* string 'addr' => The webbie of the contact (e.g.) username@friendica.com
* string 'network' => The network to which the contact belongs to
* string 'thumb' => The contact picture
* string 'click' => js code which is performed when clicking on the contact
* @param boolean $redirect If true try to use the redir url if it's possible
* @param string $class CSS class for the
* @param boolean $textmode If true display the contacts as text links
* if false display the contacts as picture links
* @return string Formatted html
*/
function micropro($contact, $redirect = false, $class = '', $textmode = false) {
// Use the contact URL if no address is available
if (!x($contact, "addr")) {
$contact["addr"] = $contact["url"];
}
$url = $contact['url'];
$sparkle = '';
$redir = false;
if ($redirect) {
$url = Contact::magicLink($contact['url']);
if (strpos($url, 'redir/') === 0) {
$sparkle = ' sparkle';
}
}
// If there is some js available we don't need the url
if (x($contact, 'click')) {
$url = '';
}
return Renderer::replaceMacros(Renderer::getMarkupTemplate(($textmode)?'micropro_txt.tpl':'micropro_img.tpl'),[
'$click' => defaults($contact, 'click', ''),
'$class' => $class,
'$url' => $url,
'$photo' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB),
'$name' => $contact['name'],
'title' => $contact['name'] . ' [' . $contact['addr'] . ']',
'$parkle' => $sparkle,
'$redir' => $redir,
]);
}
/**
* Search box.
*
* @param string $s Search query.
* @param string $id HTML id
* @param string $url Search url.
* @param bool $save Show save search button.
* @param bool $aside Display the search widgit aside.
*
* @return string Formatted HTML.
*/
function search($s, $id = 'search-box', $url = 'search', $save = false, $aside = true)
{
$mode = 'text';
if (strpos($s, '#') === 0) {
$mode = 'tag';
}
$save_label = $mode === 'text' ? L10n::t('Save') : L10n::t('Follow');
$values = [
'$s' => htmlspecialchars($s),
'$id' => $id,
'$action_url' => $url,
'$search_label' => L10n::t('Search'),
'$save_label' => $save_label,
'$savedsearch' => local_user() && Feature::isEnabled(local_user(),'savedsearch'),
'$search_hint' => L10n::t('@name, !forum, #tags, content'),
'$mode' => $mode
];
if (!$aside) {
$values['$searchoption'] = [
L10n::t("Full Text"),
L10n::t("Tags"),
L10n::t("Contacts")];
if (Config::get('system','poco_local_search')) {
$values['$searchoption'][] = L10n::t("Forums");
}
}
return Renderer::replaceMacros(Renderer::getMarkupTemplate('searchbox.tpl'), $values);
}
/**
* @brief Check for a valid email string
*
@ -542,19 +347,6 @@ function valid_email($email_address)
return preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/', $email_address);
}
/**
* Replace naked text hyperlink with HTML formatted hyperlink
*
* @param string $s
*/
function linkify($s) {
$s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' <a href="$1" target="_blank">$1</a>', $s);
$s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$s);
return $s;
}
/**
* Load poke verbs
*
@ -888,7 +680,7 @@ function prepare_body(array &$item, $attach = false, $is_preview = false)
$s = preg_replace('|(<img[^>]+src="[^"]+/photo/[0-9a-f]+)-[0-9]|', "$1-" . $ps, $s);
}
$s = apply_content_filter($s, $filter_reasons);
$s = HTML::applyContentFilter($s, $filter_reasons);
$hook_data = ['item' => $item, 'html' => $s];
Addon::callHooks('prepare_body_final', $hook_data);
@ -896,30 +688,6 @@ function prepare_body(array &$item, $attach = false, $is_preview = false)
return $hook_data['html'];
}
/**
* Given a HTML text and a set of filtering reasons, adds a content hiding header with the provided reasons
*
* Reasons are expected to have been translated already.
*
* @param string $html
* @param array $reasons
* @return string
*/
function apply_content_filter($html, array $reasons)
{
if (count($reasons)) {
$tpl = Renderer::getMarkupTemplate('wall/content_filter.tpl');
$html = Renderer::replaceMacros($tpl, [
'$reasons' => $reasons,
'$rnd' => random_string(8),
'$openclose' => L10n::t('Click to open/close'),
'$html' => $html
]);
}
return $html;
}
/**
* @brief Given a text string, convert from bbcode to html and add smilie icons.
*
@ -1049,17 +817,6 @@ function get_plink($item) {
return $ret;
}
/**
* replace html amp entity with amp char
* @param string $s
* @return string
*/
function unamp($s) {
return str_replace('&amp;', '&', $s);
}
/**
* return number of bytes in size (K, M, G)
* @param string $size_str
@ -1121,16 +878,6 @@ function base64url_decode($s) {
}
/**
* return div element with class 'clear'
* @return string
* @deprecated
*/
function cleardiv() {
return '<div class="clear"></div>';
}
function bb_translate_video($s) {
$matches = null;
@ -1147,59 +894,6 @@ function bb_translate_video($s) {
return $s;
}
function html2bb_video($s) {
$s = preg_replace('#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism',
'[youtube]$2[/youtube]', $s);
$s = preg_replace('#<iframe[^>](.*?)https?://www.youtube.com/embed/([A-Za-z0-9\-_=]+)(.*?)</iframe>#ism',
'[youtube]$2[/youtube]', $s);
$s = preg_replace('#<iframe[^>](.*?)https?://player.vimeo.com/video/([0-9]+)(.*?)</iframe>#ism',
'[vimeo]$2[/vimeo]', $s);
return $s;
}
/**
* transform link href and img src from relative to absolute
*
* @param string $text
* @param string $base base url
* @return string
*/
function reltoabs($text, $base) {
if (empty($base)) {
return $text;
}
$base = rtrim($base,'/');
$base2 = $base . "/";
// Replace links
$pattern = "/<a([^>]*) href=\"(?!http|https|\/)([^\"]*)\"/";
$replace = "<a\${1} href=\"" . $base2 . "\${2}\"";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<a([^>]*) href=\"(?!http|https)([^\"]*)\"/";
$replace = "<a\${1} href=\"" . $base . "\${2}\"";
$text = preg_replace($pattern, $replace, $text);
// Replace images
$pattern = "/<img([^>]*) src=\"(?!http|https|\/)([^\"]*)\"/";
$replace = "<img\${1} src=\"" . $base2 . "\${2}\"";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<img([^>]*) src=\"(?!http|https)([^\"]*)\"/";
$replace = "<img\${1} src=\"" . $base . "\${2}\"";
$text = preg_replace($pattern, $replace, $text);
// Done
return $text;
}
/**
* get translated item type
*

View File

@ -1,5 +1,4 @@
<?php
/**
* @file src/Content/Text/HTML.php
*/
@ -9,7 +8,14 @@ namespace Friendica\Content\Text;
use DOMDocument;
use DOMXPath;
use Friendica\Core\Addon;
use Friendica\Core\L10n;
use Friendica\Core\Config;
use Friendica\Core\PConfig;
use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Util\Network;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\XML;
use League\HTMLToMarkdown\HtmlConverter;
@ -140,12 +146,14 @@ class HTML
$message = str_replace(
[
"<li><p>",
"</p></li>",
], [
"<li>",
"</li>",
], $message
"<li><p>",
"</p></li>",
],
[
"<li>",
"</li>",
],
message
);
// remove namespaces
@ -184,8 +192,13 @@ class HTML
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]');
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]');
@ -274,8 +287,7 @@ class HTML
self::tagToBBCode($doc, 'a', ['href' => '/mailto:(.+)/'], '[mail=$1]', '[/mail]');
self::tagToBBCode($doc, 'a', ['href' => '/(.+)/'], '[url=$1]', '[/url]');
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'width' => '/(\d+)/', 'height' => '/(\d+)/'], '[img=$2x$3]$1',
'[/img]');
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'width' => '/(\d+)/', 'height' => '/(\d+)/'], '[img=$2x$3]$1', '[/img]');
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], '[img]$1', '[/img]');
@ -344,12 +356,15 @@ class HTML
"[/",
"[list]",
"[list=1]",
"[*]"], $message
"[*]"],
$message
);
} while ($message != $oldmessage);
$message = str_replace(
['[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'], ['[b]', '[/b]', '[i]', '[/i]'], $message
['[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'],
['[b]', '[/b]', '[i]', '[/i]'],
$message
);
// Handling Yahoo style of mails
@ -424,7 +439,8 @@ class HTML
foreach ($matches as $match) {
$body = preg_replace_callback(
$match, function ($match) use ($basepath) {
$match,
function ($match) use ($basepath) {
return self::qualifyURLsSub($match, $basepath);
},
$body
@ -688,4 +704,327 @@ class HTML
return $markdown;
}
/**
* @brief Convert video HTML to BBCode tags
*
* @param string $s
*/
public static function htmlToBBVideo($s)
{
$s = preg_replace(
'#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism',
'[youtube]$2[/youtube]',
$s
);
$s = preg_replace(
'#<iframe[^>](.*?)https?://www.youtube.com/embed/([A-Za-z0-9\-_=]+)(.*?)</iframe>#ism',
'[youtube]$2[/youtube]',
$s
);
$s = preg_replace(
'#<iframe[^>](.*?)https?://player.vimeo.com/video/([0-9]+)(.*?)</iframe>#ism',
'[vimeo]$2[/vimeo]',
$s
);
return $s;
}
/**
* transform link href and img src from relative to absolute
*
* @param string $text
* @param string $base base url
* @return string
*/
public static function relToAbs($text, $base)
{
if (empty($base)) {
return $text;
}
$base = rtrim($base, '/');
$base2 = $base . "/";
// Replace links
$pattern = "/<a([^>]*) href=\"(?!http|https|\/)([^\"]*)\"/";
$replace = "<a\${1} href=\"" . $base2 . "\${2}\"";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<a([^>]*) href=\"(?!http|https)([^\"]*)\"/";
$replace = "<a\${1} href=\"" . $base . "\${2}\"";
$text = preg_replace($pattern, $replace, $text);
// Replace images
$pattern = "/<img([^>]*) src=\"(?!http|https|\/)([^\"]*)\"/";
$replace = "<img\${1} src=\"" . $base2 . "\${2}\"";
$text = preg_replace($pattern, $replace, $text);
$pattern = "/<img([^>]*) src=\"(?!http|https)([^\"]*)\"/";
$replace = "<img\${1} src=\"" . $base . "\${2}\"";
$text = preg_replace($pattern, $replace, $text);
// Done
return $text;
}
/**
* return div element with class 'clear'
* @return string
* @deprecated
*/
public static function clearDiv()
{
return '<div class="clear"></div>';
}
/**
* Loader for infinite scrolling
* @return string html for loader
*/
public static function scrollLoader()
{
$tpl = Renderer::getMarkupTemplate("scroll_loader.tpl");
return Renderer::replaceMacros($tpl, [
'wait' => L10n::t('Loading more entries...'),
'end' => L10n::t('The end')
]);
}
/**
* Get html for contact block.
*
* @template contact_block.tpl
* @hook contact_block_end (contacts=>array, output=>string)
* @return string
*/
public static function contactBlock()
{
$o = '';
$a = get_app();
$shown = PConfig::get($a->profile['uid'], 'system', 'display_friend_count', 24);
if ($shown == 0) {
return;
}
if (!is_array($a->profile) || $a->profile['hide-friends']) {
return $o;
}
$r = q("SELECT COUNT(*) AS `total` FROM `contact`
WHERE `uid` = %d AND NOT `self` AND NOT `blocked`
AND NOT `pending` AND NOT `hidden` AND NOT `archive`
AND `network` IN ('%s', '%s', '%s')",
intval($a->profile['uid']),
DBA::escape(Protocol::DFRN),
DBA::escape(Protocol::OSTATUS),
DBA::escape(Protocol::DIASPORA)
);
if (DBA::isResult($r)) {
$total = intval($r[0]['total']);
}
if (!$total) {
$contacts = L10n::t('No contacts');
$micropro = null;
} else {
// Splitting the query in two parts makes it much faster
$r = q("SELECT `id` FROM `contact`
WHERE `uid` = %d AND NOT `self` AND NOT `blocked`
AND NOT `pending` AND NOT `hidden` AND NOT `archive`
AND `network` IN ('%s', '%s', '%s')
ORDER BY RAND() LIMIT %d",
intval($a->profile['uid']),
DBA::escape(Protocol::DFRN),
DBA::escape(Protocol::OSTATUS),
DBA::escape(Protocol::DIASPORA),
intval($shown)
);
if (DBA::isResult($r)) {
$contacts = [];
foreach ($r as $contact) {
$contacts[] = $contact["id"];
}
$r = q("SELECT `id`, `uid`, `addr`, `url`, `name`, `thumb`, `network` FROM `contact` WHERE `id` IN (%s)",
DBA::escape(implode(",", $contacts))
);
if (DBA::isResult($r)) {
$contacts = L10n::tt('%d Contact', '%d Contacts', $total);
$micropro = [];
foreach ($r as $rr) {
$micropro[] = micropro($rr, true, 'mpfriend');
}
}
}
}
$tpl = Renderer::getMarkupTemplate('contact_block.tpl');
$o = Renderer::replaceMacros($tpl, [
'$contacts' => $contacts,
'$nickname' => $a->profile['nickname'],
'$viewcontacts' => L10n::t('View Contacts'),
'$micropro' => $micropro,
]);
$arr = ['contacts' => $r, 'output' => $o];
Addon::callHooks('contact_block_end', $arr);
return $o;
}
/**
* @brief Format contacts as picture links or as texxt links
*
* @param array $contact Array with contacts which contains an array with
* int 'id' => The ID of the contact
* int 'uid' => The user ID of the user who owns this data
* string 'name' => The name of the contact
* string 'url' => The url to the profile page of the contact
* string 'addr' => The webbie of the contact (e.g.) username@friendica.com
* string 'network' => The network to which the contact belongs to
* string 'thumb' => The contact picture
* string 'click' => js code which is performed when clicking on the contact
* @param boolean $redirect If true try to use the redir url if it's possible
* @param string $class CSS class for the
* @param boolean $textmode If true display the contacts as text links
* if false display the contacts as picture links
* @return string Formatted html
*/
public static function micropro($contact, $redirect = false, $class = '', $textmode = false)
{
// Use the contact URL if no address is available
if (!x($contact, "addr")) {
$contact["addr"] = $contact["url"];
}
$url = $contact['url'];
$sparkle = '';
$redir = false;
if ($redirect) {
$url = Contact::magicLink($contact['url']);
if (strpos($url, 'redir/') === 0) {
$sparkle = ' sparkle';
}
}
// If there is some js available we don't need the url
if (x($contact, 'click')) {
$url = '';
}
return Renderer::replaceMacros(Renderer::getMarkupTemplate(($textmode)?'micropro_txt.tpl':'micropro_img.tpl'), [
'$click' => defaults($contact, 'click', ''),
'$class' => $class,
'$url' => $url,
'$photo' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB),
'$name' => $contact['name'],
'title' => $contact['name'] . ' [' . $contact['addr'] . ']',
'$parkle' => $sparkle,
'$redir' => $redir
]);
}
/**
* Search box.
*
* @param string $s Search query.
* @param string $id HTML id
* @param string $url Search url.
* @param bool $save Show save search button.
* @param bool $aside Display the search widgit aside.
*
* @return string Formatted HTML.
*/
public static function search($s, $id = 'search-box', $url = 'search', $save = false, $aside = true)
{
$mode = 'text';
if (strpos($s, '#') === 0) {
$mode = 'tag';
}
$save_label = $mode === 'text' ? L10n::t('Save') : L10n::t('Follow');
$values = [
'$s' => htmlspecialchars($s),
'$id' => $id,
'$action_url' => $url,
'$search_label' => L10n::t('Search'),
'$save_label' => $save_label,
'$savedsearch' => local_user() && Feature::isEnabled(local_user(), 'savedsearch'),
'$search_hint' => L10n::t('@name, !forum, #tags, content'),
'$mode' => $mode
];
if (!$aside) {
$values['$searchoption'] = [
L10n::t("Full Text"),
L10n::t("Tags"),
L10n::t("Contacts")];
if (Config::get('system', 'poco_local_search')) {
$values['$searchoption'][] = L10n::t("Forums");
}
}
return Renderer::replaceMacros(Renderer::getMarkupTemplate('searchbox.tpl'), $values);
}
/**
* Replace naked text hyperlink with HTML formatted hyperlink
*
* @param string $s
*/
public static function toLink($s)
{
$s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' <a href="$1" target="_blank">$1</a>', $s);
$s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism", '<$1$2=$3&$4>', $s);
return $s;
}
/**
* Given a HTML text and a set of filtering reasons, adds a content hiding header with the provided reasons
*
* Reasons are expected to have been translated already.
*
* @param string $html
* @param array $reasons
* @return string
*/
public static function applyContentFilter($html, array $reasons)
{
if (count($reasons)) {
$tpl = Renderer::getMarkupTemplate('wall/content_filter.tpl');
$html = Renderer::replaceMacros($tpl, [
'$reasons' => $reasons,
'$rnd' => random_string(8),
'$openclose' => L10n::t('Click to open/close'),
'$html' => $html
]);
}
return $html;
}
/**
* replace html amp entity with amp char
* @param string $s
* @return string
*/
public static function unamp($s)
{
return str_replace('&amp;', '&', $s);
}
}