// must be ing format (no space).
// By default htmlawed rewrites tags to this format.
// if PHP supported conditional negative lookbehinds we could use this:
// $r = preg_replace_callback('/(?"\'\!\(\),]+)/i',
//
// we can put , in the list of excluded char but need to keep . because of domain names.
// it is removed in the callback.
$r = preg_replace_callback('/(?"\'\!\(\),]+)/i',
create_function(
'$matches',
'
$url = $matches[1];
$period = \'\';
if (substr($url, -1, 1) == \'.\') {
$period = \'.\';
$url = trim($url, \'.\');
}
$urltext = str_replace("/", "/ ", $url);
return "$urltext $period";
'
), $text);
return $r;
}
/**
* Create paragraphs from text with line spacing
* Borrowed from Wordpress.
*
* @param string $pee The string
* @param bool $br Add BRs?
*
* @todo Rewrite
* @return string
**/
function autop($pee, $br = 1) {
$pee = $pee . "\n"; // just to make things a little easier, pad the end
$pee = preg_replace('| \s* |', "\n\n", $pee);
// Space things out a little
$allblocks = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)';
$pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee);
$pee = preg_replace('!(' . $allblocks . '>)!', "$1\n\n", $pee);
$pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines
if (strpos($pee, ']*)>\s*|', " ", $pee); // no pee inside object/embed
$pee = preg_replace('|\s*\s*|', '', $pee);
}
$pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates
$pee = preg_replace('/\n?(.+?)(?:\n\s*\n|\z)/s', "$1
\n", $pee); // make paragraphs, including one at the end
$pee = preg_replace('|\s*?
|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace
$pee = preg_replace('!([^<]+)\s*?((?:div|address|form)[^>]*>)!', "
$1
$2", $pee);
$pee = preg_replace( '||', "$1
", $pee );
$pee = preg_replace('!
\s*(?' . $allblocks . '[^>]*>)\s*
!', "$1", $pee); // don't pee all over a tag
$pee = preg_replace("|(
|", "$1", $pee); // problem with nested lists
$pee = preg_replace('|
]*)>|i', "", $pee);
$pee = str_replace('
', '', $pee);
$pee = preg_replace('!\s*(?' . $allblocks . '[^>]*>)!', "$1", $pee);
$pee = preg_replace('!(?' . $allblocks . '[^>]*>)\s*
!', "$1", $pee);
if ($br) {
$pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', create_function('$matches', 'return str_replace("\n", " ", $matches[0]);'), $pee);
$pee = preg_replace('|(?)\s*\n|', " \n", $pee); // optionally make line breaks
$pee = str_replace(' ', "\n", $pee);
}
$pee = preg_replace('!(?' . $allblocks . '[^>]*>)\s* !', "$1", $pee);
$pee = preg_replace('! (\s*?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
//if (strpos($pee, '. Only there because of the comment.
// $pee = preg_replace_callback('!()(.*?) !is', 'clean_pre', $pee );
//}
$pee = preg_replace( "|\n$|", '', $pee );
return $pee;
}
/**
* Returns an excerpt.
* Will return up to n chars stopping at the nearest space.
* If no spaces are found (like in Japanese) will crop off at the
* n char mark. Adds ... if any text was chopped.
*
* @param string $text The full text to excerpt
* @param int $num_chars Return a string up to $num_chars long
*
* @return string
* @since 1.7.2
*/
function elgg_get_excerpt($text, $num_chars = 250) {
$text = trim(elgg_strip_tags($text));
$string_length = elgg_strlen($text);
if ($string_length <= $num_chars) {
return $text;
}
// handle cases
$excerpt = elgg_substr($text, 0, $num_chars);
$space = elgg_strrpos($excerpt, ' ', 0);
// don't crop if can't find a space.
if ($space === FALSE) {
$space = $num_chars;
}
$excerpt = trim(elgg_substr($excerpt, 0, $space));
if ($string_length != elgg_strlen($excerpt)) {
$excerpt .= '...';
}
return $excerpt;
}
/**
* Handles formatting of ampersands in urls
*
* @param string $url The URL
*
* @return string
* @since 1.7.1
*/
function elgg_format_url($url) {
return preg_replace('/&(?!amp;)/', '&', $url);
}
/**
* Converts an associative array into a string of well-formed attributes
*
* @note usually for HTML, but could be useful for XML too...
*
* @param array $attrs An associative array of attr => val pairs
*
* @return string HTML attributes to be inserted into a tag (e.g., )
*/
function elgg_format_attributes(array $attrs) {
$attrs = elgg_clean_vars($attrs);
$attributes = array();
if (isset($attrs['js'])) {
//@todo deprecated notice?
if (!empty($attrs['js'])) {
$attributes[] = $attrs['js'];
}
unset($attrs['js']);
}
foreach ($attrs as $attr => $val) {
$attr = strtolower($attr);
if ($val === TRUE) {
$val = $attr; //e.g. checked => TRUE ==> checked="checked"
}
// ignore $vars['entity'] => ElggEntity stuff
if ($val !== NULL && $val !== false && (is_array($val) || !is_object($val))) {
// allow $vars['class'] => array('one', 'two');
// @todo what about $vars['style']? Needs to be semi-colon separated...
if (is_array($val)) {
$val = implode(' ', $val);
}
$val = htmlspecialchars($val, ENT_QUOTES, 'UTF-8', false);
$attributes[] = "$attr=\"$val\"";
}
}
return implode(' ', $attributes);
}
/**
* Preps an associative array for use in {@link elgg_format_attributes()}.
*
* Removes all the junk that {@link elgg_view()} puts into $vars.
* Maintains backward compatibility with attributes like 'internalname' and 'internalid'
*
* @note This function is called automatically by elgg_format_attributes(). No need to
* call it yourself before using elgg_format_attributes().
*
* @param array $vars The raw $vars array with all it's dirtiness (config, url, etc.)
*
* @return array The array, ready to be used in elgg_format_attributes().
* @access private
*/
function elgg_clean_vars(array $vars = array()) {
unset($vars['config']);
unset($vars['url']);
unset($vars['user']);
// backwards compatibility code
if (isset($vars['internalname'])) {
$vars['name'] = $vars['internalname'];
unset($vars['internalname']);
}
if (isset($vars['internalid'])) {
$vars['id'] = $vars['internalid'];
unset($vars['internalid']);
}
if (isset($vars['__ignoreInternalid'])) {
unset($vars['__ignoreInternalid']);
}
if (isset($vars['__ignoreInternalname'])) {
unset($vars['__ignoreInternalname']);
}
return $vars;
}
/**
* Converts shorthand urls to absolute urls.
*
* If the url is already absolute or protocol-relative, no change is made.
*
* @example
* elgg_normalize_url(''); // 'http://my.site.com/'
* elgg_normalize_url('dashboard'); // 'http://my.site.com/dashboard'
* elgg_normalize_url('http://google.com/'); // no change
* elgg_normalize_url('//google.com/'); // no change
*
* @param string $url The URL to normalize
*
* @return string The absolute url
*/
function elgg_normalize_url($url) {
// see https://bugs.php.net/bug.php?id=51192
// from the bookmarks save action.
$php_5_2_13_and_below = version_compare(PHP_VERSION, '5.2.14', '<');
$php_5_3_0_to_5_3_2 = version_compare(PHP_VERSION, '5.3.0', '>=') &&
version_compare(PHP_VERSION, '5.3.3', '<');
$validated = false;
if ($php_5_2_13_and_below || $php_5_3_0_to_5_3_2) {
$tmp_address = str_replace("-", "", $url);
$validated = filter_var($tmp_address, FILTER_VALIDATE_URL);
} else {
$validated = filter_var($url, FILTER_VALIDATE_URL);
}
// work around for handling absoluate IRIs (RFC 3987) - see #4190
if (!$validated && (strpos($url, 'http:') === 0) || (strpos($url, 'https:') === 0)) {
$validated = true;
}
if ($validated) {
// all normal URLs including mailto:
return $url;
} elseif (preg_match("#^(\#|\?|//)#i", $url)) {
// '//example.com' (Shortcut for protocol.)
// '?query=test', #target
return $url;
} elseif (stripos($url, 'javascript:') === 0) {
// 'javascript:'
// Not covered in FILTER_VALIDATE_URL
return $url;
} elseif (preg_match("#^[^/]*\.php(\?.*)?$#i", $url)) {
// 'install.php', 'install.php?step=step'
return elgg_get_site_url() . $url;
} elseif (preg_match("#^[^/]*\.#i", $url)) {
// 'example.com', 'example.com/subpage'
return "http://$url";
} else {
// 'page/handler', 'mod/plugin/file.php'
// trim off any leading / because the site URL is stored
// with a trailing /
return elgg_get_site_url() . ltrim($url, '/');
}
}
/**
* When given a title, returns a version suitable for inclusion in a URL
*
* @param string $title The title
*
* @return string The optimised title
* @since 1.7.2
*/
function elgg_get_friendly_title($title) {
// return a URL friendly title to short circuit normal title formatting
$params = array('title' => $title);
$result = elgg_trigger_plugin_hook('format', 'friendly:title', $params, NULL);
if ($result) {
return $result;
}
//$title = iconv('UTF-8', 'ASCII//TRANSLIT', $title);
// use A-Za-z0-9_ instead of \w because \w is locale sensitive
$title = preg_replace("/[^A-Za-z0-9_ ]/", "", $title);
$title = preg_replace("/[^\w ]/", "", $title);
$title = str_replace(" ", "-", $title);
$title = str_replace("--", "-", $title);
$title = trim($title);
$title = strtolower($title);
return $title;
}
/**
* Formats a UNIX timestamp in a friendly way (eg "less than a minute ago")
*
* @see elgg_view_friendly_time()
*
* @param int $time A UNIX epoch timestamp
*
* @return string The friendly time string
* @since 1.7.2
*/
function elgg_get_friendly_time($time) {
// return a time string to short circuit normal time formatting
$params = array('time' => $time);
$result = elgg_trigger_plugin_hook('format', 'friendly:time', $params, NULL);
if ($result) {
return $result;
}
$diff = time() - (int)$time;
$minute = 60;
$hour = $minute * 60;
$day = $hour * 24;
if ($diff < $minute) {
return elgg_echo("friendlytime:justnow");
} else if ($diff < $hour) {
$diff = round($diff / $minute);
if ($diff == 0) {
$diff = 1;
}
if ($diff > 1) {
return elgg_echo("friendlytime:minutes", array($diff));
} else {
return elgg_echo("friendlytime:minutes:singular", array($diff));
}
} else if ($diff < $day) {
$diff = round($diff / $hour);
if ($diff == 0) {
$diff = 1;
}
if ($diff > 1) {
return elgg_echo("friendlytime:hours", array($diff));
} else {
return elgg_echo("friendlytime:hours:singular", array($diff));
}
} else {
$diff = round($diff / $day);
if ($diff == 0) {
$diff = 1;
}
if ($diff > 1) {
return elgg_echo("friendlytime:days", array($diff));
} else {
return elgg_echo("friendlytime:days:singular", array($diff));
}
}
}
/**
* Strip tags and offer plugins the chance.
* Plugins register for output:strip_tags plugin hook.
* Original string included in $params['original_string']
*
* @param string $string Formatted string
*
* @return string String run through strip_tags() and any plugin hooks.
*/
function elgg_strip_tags($string) {
$params['original_string'] = $string;
$string = strip_tags($string);
$string = elgg_trigger_plugin_hook('format', 'strip_tags', $params, $string);
return $string;
}