<?php
// Copyright 2011 Toby Zerner, Simon Zerner
// This file is part of esoTalk. Please see the included license file for usage information.
if (!defined("IN_ESOTALK")) exit;
/**
* General functions. Contains utility functions that are used throughout the application, such as sanitation
* functions, color functions, JSON encoding, URL construction, and array manipluation.
*
* @package esoTalk
*/
// Define E_USER_DEPRECATED for PHP < 5.3.
if (!defined("E_USER_DEPRECATED")) define('E_USER_DEPRECATED', E_USER_WARNING);
/**
* Throw a deprecation error.
*
* @param string $oldFunction The name of the deprecated function.
* @param string $newFunction The name of a function that should be used instead.
* @return void
*
* @package esoTalk
*/
function deprecated($oldFunction, $newFunction = false)
{
$message = "$oldFunction is deprecated.";
if ($newFunction) $message .= " Use $newFunction instead.";
trigger_error($message, E_USER_DEPRECATED);
}
/**
* Shortcut function for ET::translate().
*
* @see ET::translate()
*
* @package esoTalk
*/
function T($string, $default = false)
{
return ET::translate($string, $default);
}
/**
* Translate a string to its normal form or its plurular form, depending on an amount.
*
* @param string $string The string to translate (singular).
* @param string $pluralString The string to translate (plurular).
* @param int $amount The amount.
*
* @package esoTalk
*/
function Ts($string, $pluralString, $amount)
{
$number = (float)str_replace(",", "", $amount);
return sprintf(T($number == 1 ? $string : $pluralString), $amount);
}
/**
* Shortcut function for ET::config().
*
* @see ET::config()
*
* @package esoTalk
*/
function C($string, $default = false)
{
return ET::config($string, $default);
}
/**
* Get a request input value, falling back to a default value if it is not set. POST will be searched first,
* then GET, and then the fallback will be used.
*
* @param string $key The request input key.
* @param mixed $default The fallback value.
* @return mixed
*
* @package esoTalk
*/
function R($key, $default = "")
{
if (!empty($_POST[$key])) return $_POST[$key];
elseif (isset($_GET[$key])) return $_GET[$key];
else return $default;
}
/**
* Remove a directory recursively.
*
* @param string $dir The path to the directory.
* @return bool Whether or not the remove succeeded.
*
* @package esoTalk
*/
function rrmdir($dir)
{
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." and $object != "..") {
if (filetype($dir."/".$object) == "dir") rrmdir($dir."/".$object);
else unlink($dir."/".$object);
}
}
return rmdir($dir);
}
return false;
}
/**
* Write contents to a file, attempting to create the directory that the file is in if it does not exist.
*
* @param string $file The filepath to write to.
* @param string $contents The contents to write.
* @return int
*
* @package esoTalk
*/
function file_force_contents($file, $contents){
$parts = explode("/", $file);
$file = array_pop($parts);
$dir = "";
foreach($parts as $part)
if (!is_dir($dir .= "$part/")) mkdir($dir);
return file_put_contents("$dir$file", $contents);
}
/**
* Converts the php.ini notiation for numbers (like '2M') to an integer of bytes.
*
* @param string $value The value of the php.ini directive.
* @return int The equivalent number of bytes.
*
* @package esoTalk
*/
function iniToBytes($value)
{
$l = substr($value, -1);
$ret = substr($value, 0, -1);
switch(strtoupper($l)){
case "P":
$ret *= 1024;
case "T":
$ret *= 1024;
case "G":
$ret *= 1024;
case "M":
$ret *= 1024;
case "K":
$ret *= 1024;
break;
}
return $ret;
}
/**
* Minify a CSS string by removing comments and whitespace.
*
* @param string $css The CSS to minify.
* @return string The minified result.
*
* @package esoTalk
*/
function minifyCSS($css)
{
// Compress whitespace.
$css = preg_replace('/\s+/', ' ', $css);
// Remove comments.
$css = preg_replace('/\/\*.*?\*\//', '', $css);
return trim($css);
}
/**
* Minify a JavaScript string using JSMin.
*
* @param string $js The JavaScript to minify.
* @return string The minified result.
*
* @package esoTalk
*/
function minifyJS($js)
{
require_once PATH_LIBRARY."/vendor/jsmin.php";
return JSMin::minify($js);
}
/**
* Send an email with proper headers.
*
* @param string $to The address to send the email to.
* @param string $subject The subject of the email.
* @param string $body The body of the email.
* @return bool Whether or not the mailing succeeded.
*
* @package esoTalk
*/
function sendEmail($to, $subject, $body)
{
$phpmailer = PATH_LIBRARY.'/vendor/class.phpmailer.php';
require_once($phpmailer);
$mail = new PHPMailer(true);
if ($return = ET::first("sendEmailBefore", array($mail, &$to, &$subject, &$body))) return $return;
$mail->CharSet = 'UTF-8';
$mail->IsHTML(true);
$mail->AddAddress($to);
$mail->SetFrom(C("esoTalk.emailFrom"), sanitizeForHTTP(C("esoTalk.forumTitle")));
$mail->Subject = sanitizeForHTTP($subject);
$mail->Body = $body;
return $mail->Send();
}
/**
* Parse an array of request parts (eg. $_GET["p"] exploded by "/"), work out what controller to set up,
* instantiate it, and work out the method + arguments to dispatch to it.
*
* @param array $parts An array of parts of the request.
* @param array $controllers An array of available controllers, with the keys as the controller names and the
* values as the factory names.
* @return array An array of information about the response:
* 0 => the controller name
* 1 => the controller instance
* 2 => the method to dispatch
* 3 => the arguments to pass when dispatching
* 4 => the response type to use
*
* @package esoTalk
*/
function parseRequest($parts, $controllers)
{
$c = strtolower(@$parts[0]);
$method = "index";
$type = RESPONSE_TYPE_DEFAULT;
// If the specified controller doesn't exist, 404.
if (!isset($controllers[$c])) ET::notFound();
// Make an instance of the controller.
$controller = ETFactory::make($controllers[$c]);
// Determine the controller method and response type to use. Default to index.
$arguments = array_slice($parts, 2);
if (!empty($parts[1])) {
$method = strtolower($parts[1]);
// If there's a period in the method string, use the first half as the method and the second half as the response type.
if (strpos($method, ".") !== false) {
list($method, $suffix) = explode(".", $method, 2);
if (in_array($suffix, array(RESPONSE_TYPE_VIEW, RESPONSE_TYPE_JSON, RESPONSE_TYPE_AJAX, RESPONSE_TYPE_ATOM))) $type = $suffix;
}
// Get all of the action methods in the controller class.
$methods = get_class_methods($controller);
foreach ($methods as $k => $v) {
if (strpos($v = strtolower($v), "action_") !== 0) {
unset($methods[$k]);
continue;
}
$methods[$k] = substr($v, 7);
}
// If the method we want to use doesn't exist in the controller...
if (!$method or !in_array($method, $methods)) {
// Search for a plugin with this method. If found, use that.
$found = false;
foreach (ET::$plugins as $plugin) {
if (method_exists($plugin, "action_".$c."Controller_".$method)) {
$found = true;
break;
}
}
// If one wasn't found, default to the "action_index" method.
if (!$found) {
$method = "index";
$arguments = array_slice($parts, 1);
}
}
}
return array($c, $controller, $method, $arguments, $type);
}
/**
* Sanitize a string for outputting in a HTML context.
*
* @param string $string The string to sanitize.
* @return string The sanitized string.
*
* @package esoTalk
*/
function sanitizeHTML($value)
{
return htmlentities($value, ENT_QUOTES, "UTF-8");
}
/**
* Sanitize HTTP header-sensitive characters (CR and LF.)
*
* @param string $string The string to sanitize.
* @return string The sanitized string.
*
* @package esoTalk
*/
function sanitizeForHTTP($value)
{
return str_replace(array("\r", "\n", "%0a", "%0d", "%0A", "%0D"), "", $value);
}
/**
* Sanitize file-system sensitive characters.
*
* @param string $string The string to sanitize.
* @return string The sanitized string.
*
* @package esoTalk
*/
function sanitizeFileName($value)
{
return preg_replace("/(?:[\/:\\\]|\.{2,}|\\x00)/", "", $value);
}
/**
* Sort an array by values in its second dimension.
*
* @param array $array The array to sort.
* @param mixed $index The key of the second dimension to sort the array by.
* @param string $order The direction (asc or desc).
* @param bool $natSort Whether or not to use the natsort function.
* @param bool $caseSensitive Whether or not to use case-sensitive sort functions.
* @return array The sorted array.
*
* @package esoTalk
*/
function sort2d($array, $index, $order = "asc", $natSort = false, $caseSensitive = false)
{
if (is_array($array) and count($array) > 0) {
$temp = array();
foreach (array_keys($array) as $key) {
$temp[$key] = $array[$key][$index];
if (!$natSort) ($order == "asc") ? asort($temp) : arsort($temp);
else {
($caseSensitive) ? natsort($temp) : natcasesort($temp);
if ($order != "asc") $temp = array_reverse($temp, true);
}
}
foreach (array_keys($temp) as $key) $sorted[$key] = $array[$key];
return $sorted;
}
return $array;
}
/**
* Returns whether or not the user is using a mobile device.
*
* @return bool
*
* @package esoTalk
*/
function isMobileBrowser()
{
static $isMobileBrowser = null;
if (is_null($isMobileBrowser)) {
// This code is from http://detectmobilebrowser.com/ by Chad Smith. Thanks Chad!
$userAgent = $_SERVER["HTTP_USER_AGENT"];
$isMobileBrowser = (preg_match("/android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i", $userAgent) || preg_match("/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i", substr($userAgent, 0, 4)));
}
return $isMobileBrowser;
}
/**
* Create a slug for use in URLs from a given string. Any non-alphanumeric characters will be converted to "-".
*
* @param string $string The string to convert.
* @return string The slug.
*
* @package esoTalk
*/
function slug($string)
{
// If there are any characters other than basic alphanumeric, space, punctuation, then we need to attempt transliteration.
if (preg_match("/[^\x20-\x7f]/", $string)) {
// Thanks to krakos for this code! http://esotalk.org/forum/582-unicode-in-usernames-and-url-s
if (function_exists('transliterator_transliterate')) {
// Unicode decomposition rules states that these cannot be decomposed, hence
// we have to deal with them manually. Note: even though “scharfes s” is commonly
// transliterated as “sz”, in this context “ss” is preferred, as it's the most popular
// method among German speakers.
$src = array('œ', 'æ', 'đ', 'ø', 'ł', 'ß', 'Œ', 'Æ', 'Đ', 'Ø', 'Ł');
$dst = array('oe','ae','d', 'o', 'l', 'ss', 'OE', 'AE', 'D', 'O', 'L');
$string = str_replace($src, $dst, $string);
// Using transliterator to get rid of accents and convert non-Latin to Latin
$string = transliterator_transliterate("Any-Latin; NFD; [:Nonspacing Mark:] Remove; NFC; [:Punctuation:] Remove; Lower();", $string);
}
elseif (function_exists('iconv')) {
// IConv won't deal nicely with the following, hence we have to deal with them
// manually. Note: even though “scharfes s” is commonly transliterated as “sz”,
// in this context “ss” is preferred, as it's the most popular method among German
// speakers.
$src = array('đ', 'ø', 'ß', 'Đ', 'Ø');
$dst = array('d', 'o', 'ss', 'D', 'O');
$string = str_replace($src, $dst, $string);
// Using IConv to get rid of accents. Non-Latin letters are unaffected
$string = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $string);
}
else {
// A fallback to old method.
// Convert special Latin letters and other characters to HTML entities.
$string = htmlentities($string, ENT_NOQUOTES, "UTF-8");
// With those HTML entities, either convert them back to a normal letter, or remove them.
$string = preg_replace(array("/&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml|caron);/i", "/&[^;]{2,6};/"), array("$1", " "), $string);
}
}
// Allow plugins to alter the slug.
ET::trigger("slug", array(&$string));
// Now replace non-alphanumeric characters with a hyphen, and remove multiple hyphens.
$slug = strtolower(trim(preg_replace(array("/[^0-9a-z]/i", "/-+/"), "-", $string), "-"));
return substr($slug, 0, 63);
}
/**
* Generate a salt of $numOfChars characters long containing random letters, numbers, and symbols.
*
* @param int $numOfChars The length of the random string.
* @return string The random string.
*
* @package esoTalk
*/
function generateRandomString($numOfChars, $possibleChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~!@#%^&*()_+=-{}[]:;<,>.?/`")
{
$salt = "";
for ($i = 0; $i < $numOfChars; $i++) $salt .= $possibleChars[rand(0, strlen($possibleChars) - 1)];
return $salt;
}
/**
* For bad server configs. Pretty much just performing stripslashes on an array here.
*
* @param mixed $value The value to undo magic quotes on.
* @return mixed
*
* @package esoTalk
*/
function undoMagicQuotes($value)
{
if (!is_array($value)) return stripslashes($value);
else array_map("undoMagicQuotes", $value);
return $value;
}
/**
* For bad server configs as well. Unset all input variables added to the global namespace if register_globals
* is on.
*
* @return void
*
* @package esoTalk
*/
function undoRegisterGlobals()
{
if (ini_get("register_globals")) {
$array = array("_REQUEST", "_SESSION", "_SERVER", "_ENV", "_FILES");
foreach ($array as $value) {
foreach ((array)$GLOBALS[$value] as $key => $var) {
if (isset($GLOBALS[$key]) and $var === $GLOBALS[$key]) unset($GLOBALS[$key]);
}
}
}
}
/**
* Convert an RGB triplet to an HSL triplet.
*
* @param array $rgb The RGB triplet.
* @return array The HSL triplet.
*
* @package esoTalk
*/
function rgb2hsl($rgb)
{
$r = $rgb[0];
$g = $rgb[1];
$b = $rgb[2];
$min = min($r, min($g, $b));
$max = max($r, max($g, $b));
$delta = $max - $min;
$l = ($min + $max) / 2;
$s = 0;
if ($l > 0 && $l < 1) {
$s = $delta / ($l < 0.5 ? (2 * $l) : (2 - 2 * $l));
}
$h = 0;
if ($delta > 0) {
if ($max == $r && $max != $g) {
$h += ($g - $b) / $delta;
}
if ($max == $g && $max != $b) {
$h += (2 + ($b - $r) / $delta);
}
if ($max == $b && $max != $r) {
$h += (4 + ($r - $g) / $delta);
}
$h /= 6;
}
return array($h, $s, $l);
}
/**
* Convert an HSL triplet to an RGB triplet.
*
* @param array $hsl The HSL triplet.
* @return array The RGB triplet.
*
* @package esoTalk
*/
function hsl2rgb($hsl)
{
$h = $hsl[0];
$s = $hsl[1];
$l = $hsl[2];
$m2 = ($l <= 0.5) ? $l * ($s + 1) : $l + $s - $l*$s;
$m1 = $l * 2 - $m2;
return array(hue2rgb($m1, $m2, $h + 0.33333),
hue2rgb($m1, $m2, $h),
hue2rgb($m1, $m2, $h - 0.33333));
}
/**
* Helper function for hsl2rgb().
*
* @package esoTalk
*/
function hue2rgb($m1, $m2, $h)
{
$h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h);
if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
if ($h * 2 < 1) return $m2;
if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (0.66666 - $h) * 6;
return $m1;
}
/**
* Convert a hex color into an RGB triplet.
*
* @param string $hex The hex color value (with a leading #).
* @param bool $normalize Whether or not the values of the RGB triplet should be 0-255 or 0-1.
* @return array The RGB triplet.
*
* @package esoTalk
*/
function colorUnpack($hex, $normalize = false)
{
if (strlen($hex) == 4) {
$hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
}
$c = hexdec($hex);
for ($i = 16; $i >= 0; $i -= 8) {
$out[] = (($c >> $i) & 0xFF) / ($normalize ? 255 : 1);
}
return $out;
}
/**
* Convert an RGB triplet to a hex color.
*
* @param array $rgb The RGB triplet.
* @param bool $normalize Whether or not the values of the RGB triplet are 0-255 or 0-1.
* @return The hex color, with a leading #.
*
* @package esoTalk
*/
function colorPack($rgb, $normalize = false)
{
$out = null;
foreach ($rgb as $k => $v) {
$out |= (($v * ($normalize ? 255 : 1)) << (16 - $k * 8));
}
return "#".str_pad(dechex($out), 6, 0, STR_PAD_LEFT);
}
// json_encode for PHP < 5.2.0.
if (!function_exists("json_encode")) {
function json_encode($a = false)
{
if (is_null($a)) return "null";
if ($a === false) return "false";
if ($a === true) return "true";
if (is_scalar($a)) {
if (is_float($a)) return floatval(str_replace(",", ".", strval($a)));
if (is_string($a)) {
static $jsonReplaces = array(array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"'), array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'));
return '"'.str_replace($jsonReplaces[0], $jsonReplaces[1], $a).'"';
} else return $a;
}
$isList = true;
for ($i = 0, reset($a), $count = count($a); $i < $count; $i++, next($a)) {
if (key($a) !== $i) {
$isList = false;
break;
}
}
$result = array();
if ($isList) {
foreach ($a as $v) $result[] = json_encode($v);
return '['.implode(',', $result).']';
} else {
foreach ($a as $k => $v) $result[] = '"'.($k).'":'.json_encode($v);
return '{'.implode(',', $result).'}';
}
}
}
// json_decode for PHP < 5.2.0
if (!function_exists("json_decode")) {
function json_decode($json)
{
$json = str_replace(array("\\\\", "\\\""), array("\", """), $json);
$parts = preg_split("@(\"[^\"]*\")|([\[\]\{\},:])|\s@is", $json, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
foreach ($parts as $index => $part) {
if (strlen($part) == 1) {
switch ($part) {
case "[":
case "{":
$parts[$index] = "array(";
break;
case "]":
case "}":
$parts[$index] = ")";
break;
case ":":
$parts[$index] = "=>";
break;
case ",":
break;
default:
return null;
}
}
else {
if ((substr($part, 0, 1) != "\"") || (substr($part, -1, 1) != "\""))
return null;
}
}
$json = str_replace(array("\", """, "$"), array("\\\\", "\\\"", "\\$"), implode("", $parts));
return eval("return $json;");
}
}
/**
* Construct a URL, given a request path.
*
* Constructs a relative or absolute URL which can be used to link
* to a page in esoTalk, according to the format specified by C("esoTalk.urls").
*
* @param string $url The request path (eg. conversations/all). May include a query string/hash.
* @param bool $absolute Whether or not to return an absolute URL.
* @return string
*
* @package esoTalk
*/
function URL($url = "", $absolute = false)
{
if (preg_match('/^(https?\:)?\/\//', $url)) return $url;
// Strip off the hash.
$hash = strstr($url, "#");
if ($hash) $url = substr($url, 0, -strlen($hash));
// Strip off the query string.
$query = strstr($url, "?");
if ($query) $url = substr($url, 0, -strlen($query));
// If we don't have nice urls, use ?p=controller/method/argument instead.
if (!C("esoTalk.urls.friendly") and $url) {
$link = "?p=".$url;
if ($query) $query[0] = "&";
}
else $link = $url;
// Re-add the query string and has to the URL.
$link .= $query . $hash;
// If we're not using mod_rewrite, we need to prepend "index.php/" to the link.
if (C("esoTalk.urls.friendly") and !C("esoTalk.urls.rewrite")) $link = "index.php/$link";
return $absolute ? rtrim(C("esoTalk.baseURL"), "/")."/".$link : getWebPath($link);
}
/**
* Remove the absolute path to the root esoTalk directory from a file path.
*
* @param string $path The file path.
* @return string The path relative to the esoTalk root directory.
*
* @package esoTalk
*/
function getRelativePath($path)
{
if (strpos($path, PATH_ROOT) === 0) $path = substr($path, strlen(PATH_ROOT) + 1);
return $path;
}
/**
* Get the "web path" (the path relative to the domain root) for a file.
*
* @param string $path The path to convert.
* @return string The web path for the specified path.
*
* @package esoTalk
*/
function getWebPath($path)
{
if (strpos($path, "://") === false) {
// Remove the absolute path to the root esoTalk directory.
$path = getRelativePath($path);
// Prepend the web path.
$path = ET::$webPath."/".ltrim($path, "/");
}
return $path;
}
/**
* Get the relative path or URL to a resource (a web-accessible file stored in plugins, skins, languages, or js)
* depending on the state of the resourceURL config setting.
*
* @param string $path The absolute path to the resource.
* @return string The relative path or URL to the given resource.
*
* @package esoTalk
*/
function getResource($path, $absolute = false)
{
if (strpos($path, "://") === false) {
// Remove the absolute path to the root esoTalk directory.
$path = getRelativePath($path);
// Prepend the web path.
$path = ltrim($path, "/");
if ($c = C("esoTalk.resourceURL")) $path = $c.$path;
else $path = $absolute ? rtrim(C("esoTalk.baseURL"), "/")."/".$path : ET::$webPath."/".$path;
}
return $path;
}
/**
* Send a HTTP Location header to redirect to a specific page.
*
* @param string $destination The location to redirect to.
* @param int $code The HTTP code to send with the redirection.
* @return void
*
* @package esoTalk
*/
function redirect($destination, $code = 302)
{
// Close the database connection.
if (ET::$database) ET::$database->close();
// Clear the output buffer, and send the location header.
@ob_end_clean();
header("Location: ".sanitizeForHTTP($destination), true, $code);
exit;
}
/**
* Get a human-friendly string (eg. 1 hour ago) for how much time has passed since a given time.
*
* @param int $then UNIX timestamp of the time to work out how much time has passed since.
* @param bool $precise Whether or not to return "x minutes/seconds", or just "a few minutes".
* @return string A human-friendly time string.
*
* @package esoTalk
*/
function relativeTime($then, $precise = false)
{
// If there is no $then, we can only assume that whatever it is never happened...
if (!$then) return T("never");
// Work out how many seconds it has been since $then.
$ago = time() - $then;
// If $then happened less than 1 second ago, say "Just now".
if ($ago < 1) return T("just now");
// If this happened over a year ago, return "x years ago".
if ($ago >= ($period = 60 * 60 * 24 * 365.25)) {
$years = floor($ago / $period);
return Ts("%d year ago", "%d years ago", $years);
}
// If this happened over two months ago, return "x months ago".
elseif ($ago >= ($period = 60 * 60 * 24 * (365.25 / 12)) * 2) {
$months = floor($ago / $period);
return Ts("%d month ago", "%d months ago", $months);
}
// If this happend over a week ago, return "x weeks ago".
elseif ($ago >= ($period = 60 * 60 * 24 * 7)) {
$weeks = floor($ago / $period);
return Ts("%d week ago", "%d weeks ago", $weeks);
}
// If this happened over a day ago, return "x days ago".
elseif ($ago >= ($period = 60 * 60 * 24)) {
$days = floor($ago / $period);
return Ts("%d day ago", "%d days ago", $days);
}
// If this happened over an hour ago, return "x hours ago".
elseif ($ago >= ($period = 60 * 60)) {
$hours = floor($ago / $period);
return Ts("%d hour ago", "%d hours ago", $hours);
}
// If we're going for a precise value, go on to test at the minute/second level.
if ($precise) {
// If this happened over a minute ago, return "x minutes ago".
if ($ago >= ($period = 60)) {
$minutes = floor($ago / $period);
return Ts("%d minute ago", "%d minutes ago", $minutes);
}
// Return "x seconds ago".
elseif ($ago >= 1) return Ts("%d second ago", "%d seconds ago", $ago);
}
// Otherwise, just return "Just now".
return T("just now");
}
/**
* Get a smart human-friendly string for a date.
*
* @param int $then UNIX timestamp of the time to work out how much time has passed since.
* @param bool $precise Whether or not to return "x minutes/seconds", or just "a few minutes".
* @return string A human-friendly time string.
*
* @package esoTalk
*/
function smartTime($then, $precise = false)
{
// Work out how many seconds it has been since $then.
$ago = time() - $then;
// If the time was within the last 48 hours, show a relative time (eg. 2 hours ago.)
if ($ago >= 0 and $ago < 48 * 60 * 60) return relativeTime($then, $precise);
// If the time is within the last half a year or the next half a year, show just a month and a day.
elseif ($ago < 180 * 24 * 60 * 60) return strftime("%b %e", $then);
// Otherwise, show the month, day, and year.
else return strftime(($precise ? "%e " : "")."%b %Y", $then);
}
/**
* Extract the contents of a ZIP file, and return a list of files it contains and their contents.
*
* @param string $filename The filepath to the ZIP file.
* @return array An array of files and their details/contents.
*
* @package esoTalk
*/
function unzip($filename)
{
$files = array();
$handle = fopen($filename, "rb");
// Seek to the end of central directory record.
$size = filesize($filename);
@fseek($handle, $size - 22);
// Error checking.
if (ftell($handle) != $size - 22) return false; // Can't seek to end of central directory?
// Check end of central directory signature.
$data = unpack("Vid", fread($handle, 4));
if ($data["id"] != 0x06054b50) return false;
// Extract the central directory information.
$centralDir = unpack("vdisk/vdiskStart/vdiskEntries/ventries/Vsize/Voffset/vcommentSize", fread($handle, 18));
$pos = $centralDir["offset"];
// Loop through each entry in the zip file.
for ($i = 0; $i < $centralDir["entries"]; $i++) {
// Read next central directory structure header.
@rewind($handle);
@fseek($handle, $pos + 4);
$header = unpack("vversion/vversionExtracted/vflag/vcompression/vmtime/vmdate/Vcrc/VcompressedSize/Vsize/vfilenameLen/vextraLen/vcommentLen/vdisk/vinternal/Vexternal/Voffset", fread($handle, 42));
// Get the filename.
$header["filename"] = $header["filenameLen"] ? fread($handle, $header["filenameLen"]) : "";
// Save the position.
$pos = ftell($handle) + $header["extraLen"] + $header["commentLen"];
// Go to the position of the file.
@rewind($handle);
@fseek($handle, $header["offset"] + 4);
// Read the local file header to get the filename length.
$localHeader = unpack("vversion/vflag/vcompression/vmtime/vmdate/Vcrc/VcompressedSize/Vsize/vfilenameLen/vextraLen", fread($handle, 26));
// Get the filename.
$localHeader["filename"] = fread($handle, $localHeader["filenameLen"]);
// Skip the extra bit.
if ($localHeader["extraLen"] > 0) fread($handle, $localHeader["extraLen"]);
// Extract the file (if it's not a folder.)
$directory = substr($header["filename"], -1) == "/";
if (!$directory and $header["compressedSize"] > 0) {
if ($header["compression"] == 0) $content = fread($handle, $header["compressedSize"]);
else $content = gzinflate(fread($handle, $header["compressedSize"]));
} else $content = "";
// Add to the files array.
$files[] = array(
"name" => $header["filename"],
"size" => $header["size"],
"directory" => $directory,
"content" => !$directory ? $content : false
);
}
fclose($handle);
// Return an array of files that were extracted.
return $files;
}
/**
* Add an element to an indexed array after a specified position.
*
* @param array $array The array to add to.
* @param mixed $add The element to add.
* @param int $position The index to add the element at.
* @return void
*
* @package esoTalk
*/
function addToArray(&$array, $add, $position = false)
{
// If no position is specified, add it to the end and return the key
if ($position === false) {
$array[] = $add;
end($array);
ksort($array);
return key($array);
}
// Else, add the element at the specified position.
array_splice($array, $position, 0, array($add));
return $position;
}
/**
* Add an element to an keyed array after/before a certain key, or at a certain index.
*
* @param array $array The array to add to.
* @param string $key The key to add to the array.
* @param mixed $value The value to add to the array.
* @param mixed $position The position to add the element at. If this is an integer, the element will be added
* at that index. If this is an array with the first key as "before" or "after", the element will be added
* before or after the specified key.
* @return void
*
* @package esoTalk
*/
function addToArrayString(&$array, $key, $value, $position = false)
{
// If we're intending to add it to the end of the array, that's easy.
if ($position === false) {
$array[$key] = $value;
return;
}
// Otherwise, split the array into keys and values.
$keys = array_keys($array);
$values = array_values($array);
// If the position is "before" or "after" a certain key, find that key in the array and record the index.
// If the key doesn't exist, then we'll add the element to the end of the array.
if (is_array($position)) {
$index = array_search(reset($position), $keys, true);
if ($index === false) $index = count($array);
if (key($position) == "after") $index++;
}
// If the position is just an integer, then we already have the index.
else $index = (int)$position;
// Add the key/value to their respective arrays at the appropriate index.
array_splice($keys, $index, 0, $key);
array_splice($values, $index, 0, array($value));
// Combine the new keys/values!
$array = array_combine($keys, $values);
}
if (function_exists("lcfirst") === false) {
/**
* Make a string's first character lowercase.
*
* NOTE: Is included in PHP 5 >= 5.3.0
*
* @param string $str The input string.
* @return string
*
* @package esoTalk
*/
function lcfirst($str)
{
$str[0] = strtolower($str[0]);
return $str;
}
}