PageRenderTime 56ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/functions.general.php

https://github.com/Ramir1/esoTalk
PHP | 1091 lines | 508 code | 156 blank | 427 comment | 109 complexity | 505f9f6db4f1a73ab5d4b8314863bc89 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. // Copyright 2011 Toby Zerner, Simon Zerner
  3. // This file is part of esoTalk. Please see the included license file for usage information.
  4. if (!defined("IN_ESOTALK")) exit;
  5. /**
  6. * General functions. Contains utility functions that are used throughout the application, such as sanitation
  7. * functions, color functions, JSON encoding, URL construction, and array manipluation.
  8. *
  9. * @package esoTalk
  10. */
  11. // Define E_USER_DEPRECATED for PHP < 5.3.
  12. if (!defined("E_USER_DEPRECATED")) define('E_USER_DEPRECATED', E_USER_WARNING);
  13. /**
  14. * Throw a deprecation error.
  15. *
  16. * @param string $oldFunction The name of the deprecated function.
  17. * @param string $newFunction The name of a function that should be used instead.
  18. * @return void
  19. *
  20. * @package esoTalk
  21. */
  22. function deprecated($oldFunction, $newFunction = false)
  23. {
  24. $message = "$oldFunction is deprecated.";
  25. if ($newFunction) $message .= " Use $newFunction instead.";
  26. trigger_error($message, E_USER_DEPRECATED);
  27. }
  28. /**
  29. * Shortcut function for ET::translate().
  30. *
  31. * @see ET::translate()
  32. *
  33. * @package esoTalk
  34. */
  35. function T($string, $default = false)
  36. {
  37. return ET::translate($string, $default);
  38. }
  39. /**
  40. * Translate a string to its normal form or its plurular form, depending on an amount.
  41. *
  42. * @param string $string The string to translate (singular).
  43. * @param string $pluralString The string to translate (plurular).
  44. * @param int $amount The amount.
  45. *
  46. * @package esoTalk
  47. */
  48. function Ts($string, $pluralString, $amount)
  49. {
  50. return sprintf(T($amount == 1 ? $string : $pluralString), $amount);
  51. }
  52. /**
  53. * Shortcut function for ET::config().
  54. *
  55. * @see ET::config()
  56. *
  57. * @package esoTalk
  58. */
  59. function C($string, $default = false)
  60. {
  61. return ET::config($string, $default);
  62. }
  63. /**
  64. * Get a request input value, falling back to a default value if it is not set. POST will be searched first,
  65. * then GET, and then the fallback will be used.
  66. *
  67. * @param string $key The request input key.
  68. * @param mixed $default The fallback value.
  69. * @return mixed
  70. *
  71. * @package esoTalk
  72. */
  73. function R($key, $default = "")
  74. {
  75. if (!empty($_POST[$key])) return $_POST[$key];
  76. elseif (isset($_GET[$key])) return $_GET[$key];
  77. else return $default;
  78. }
  79. /**
  80. * Remove a directory recursively.
  81. *
  82. * @param string $dir The path to the directory.
  83. * @return bool Whether or not the remove succeeded.
  84. *
  85. * @package esoTalk
  86. */
  87. function rrmdir($dir)
  88. {
  89. if (is_dir($dir)) {
  90. $objects = scandir($dir);
  91. foreach ($objects as $object) {
  92. if ($object != "." and $object != "..") {
  93. if (filetype($dir."/".$object) == "dir") rrmdir($dir."/".$object);
  94. else unlink($dir."/".$object);
  95. }
  96. }
  97. return rmdir($dir);
  98. }
  99. return false;
  100. }
  101. /**
  102. * Write contents to a file, attempting to create the directory that the file is in if it does not exist.
  103. *
  104. * @param string $file The filepath to write to.
  105. * @param string $contents The contents to write.
  106. * @return int
  107. *
  108. * @package esoTalk
  109. */
  110. function file_force_contents($file, $contents){
  111. $parts = explode("/", $file);
  112. $file = array_pop($parts);
  113. $dir = "";
  114. foreach($parts as $part)
  115. if (!is_dir($dir .= "$part/")) mkdir($dir);
  116. return file_put_contents("$dir$file", $contents);
  117. }
  118. /**
  119. * Converts the php.ini notiation for numbers (like '2M') to an integer of bytes.
  120. *
  121. * @param string $value The value of the php.ini directive.
  122. * @return int The equivalent number of bytes.
  123. *
  124. * @package esoTalk
  125. */
  126. function iniToBytes($value)
  127. {
  128. $l = substr($value, -1);
  129. $ret = substr($value, 0, -1);
  130. switch(strtoupper($l)){
  131. case "P":
  132. $ret *= 1024;
  133. case "T":
  134. $ret *= 1024;
  135. case "G":
  136. $ret *= 1024;
  137. case "M":
  138. $ret *= 1024;
  139. case "K":
  140. $ret *= 1024;
  141. break;
  142. }
  143. return $ret;
  144. }
  145. /**
  146. * Minify a CSS string by removing comments and whitespace.
  147. *
  148. * @param string $css The CSS to minify.
  149. * @return string The minified result.
  150. *
  151. * @package esoTalk
  152. */
  153. function minifyCSS($css)
  154. {
  155. // Compress whitespace.
  156. $css = preg_replace('/\s+/', ' ', $css);
  157. // Remove comments.
  158. $css = preg_replace('/\/\*.*?\*\//', '', $css);
  159. return trim($css);
  160. }
  161. /**
  162. * Minify a JavaScript string using JSMin.
  163. *
  164. * @param string $js The JavaScript to minify.
  165. * @return string The minified result.
  166. *
  167. * @package esoTalk
  168. */
  169. function minifyJS($js)
  170. {
  171. require_once PATH_LIBRARY."/vendor/jsmin.php";
  172. return JSMin::minify($js);
  173. }
  174. /**
  175. * Send an email with proper headers.
  176. *
  177. * @param string $to The address to send the email to.
  178. * @param string $subject The subject of the email.
  179. * @param string $body The body of the email.
  180. * @return bool Whether or not the mailing succeeded.
  181. *
  182. * @package esoTalk
  183. */
  184. function sendEmail($to, $subject, $body)
  185. {
  186. $phpmailer = PATH_LIBRARY.'/vendor/class.phpmailer.php';
  187. require_once($phpmailer);
  188. $mail = new PHPMailer(true);
  189. if (($return = ET::trigger("sendEmailBefore", array($mail, &$to, &$subject, &$body))) and !empty($return))
  190. return reset($return);
  191. $mail->IsHTML(true);
  192. $mail->AddAddress($to);
  193. $mail->SetFrom(C("esoTalk.emailFrom"), sanitizeForHTTP(C("esoTalk.forumTitle")));
  194. $mail->Subject = sanitizeForHTTP($subject);
  195. $mail->Body = $body;
  196. return $mail->Send();
  197. }
  198. /**
  199. * Parse an array of request parts (eg. $_GET["p"] exploded by "/"), work out what controller to set up,
  200. * instantiate it, and work out the method + arguments to dispatch to it.
  201. *
  202. * @param array $parts An array of parts of the request.
  203. * @param array $controllers An array of available controllers, with the keys as the controller names and the
  204. * values as the factory names.
  205. * @return array An array of information about the response:
  206. * 0 => the controller name
  207. * 1 => the controller instance
  208. * 2 => the method to dispatch
  209. * 3 => the arguments to pass when dispatching
  210. * 4 => the response type to use
  211. *
  212. * @package esoTalk
  213. */
  214. function parseRequest($parts, $controllers)
  215. {
  216. $c = strtolower(@$parts[0]);
  217. $method = "index";
  218. $type = RESPONSE_TYPE_DEFAULT;
  219. // If the specified controller doesn't exist, 404.
  220. if (!isset($controllers[$c])) ET::notFound();
  221. // Make an instance of the controller.
  222. $controller = ETFactory::make($controllers[$c]);
  223. // Determine the controller method and response type to use. Default to index.
  224. $arguments = array_slice($parts, 2);
  225. if (!empty($parts[1])) {
  226. $method = strtolower($parts[1]);
  227. // If there's a period in the method string, use the first half as the method and the second half as the response type.
  228. if (strpos($method, ".") !== false) {
  229. list($method, $suffix) = explode(".", $method, 2);
  230. if (in_array($suffix, array(RESPONSE_TYPE_VIEW, RESPONSE_TYPE_JSON, RESPONSE_TYPE_AJAX, RESPONSE_TYPE_ATOM))) $type = $suffix;
  231. }
  232. // Get all of the immediately public methods in the controller class.
  233. $methods = get_class_methods($controller);
  234. $parentMethods = get_class_methods(get_parent_class($controller));
  235. $methods = array_diff($methods, $parentMethods);
  236. foreach ($methods as $k => $v) $methods[$k] = strtolower($v);
  237. // If the method we want to use doesn't exist in the controller...
  238. if (!$method or !in_array($method, $methods)) {
  239. // Search for a plugin with this method. If found, use that.
  240. $found = false;
  241. foreach (ET::$plugins as $plugin) {
  242. if (method_exists($plugin, $c."Controller_".$method)) {
  243. $found = true;
  244. break;
  245. }
  246. }
  247. // If one wasn't found, default to the "index" method.
  248. if (!$found) {
  249. $method = "index";
  250. $arguments = array_slice($parts, 1);
  251. }
  252. }
  253. }
  254. return array($c, $controller, $method, $arguments, $type);
  255. }
  256. /**
  257. * Sanitize a string for outputting in a HTML context.
  258. *
  259. * @param string $string The string to sanitize.
  260. * @return string The sanitized string.
  261. *
  262. * @package esoTalk
  263. */
  264. function sanitizeHTML($value)
  265. {
  266. return htmlentities($value, ENT_QUOTES, "UTF-8");
  267. }
  268. /**
  269. * Sanitize HTTP header-sensitive characters (CR and LF.)
  270. *
  271. * @param string $string The string to sanitize.
  272. * @return string The sanitized string.
  273. *
  274. * @package esoTalk
  275. */
  276. function sanitizeForHTTP($value)
  277. {
  278. return str_replace(array("\r", "\n", "%0a", "%0d", "%0A", "%0D"), "", $value);
  279. }
  280. /**
  281. * Sanitize file-system sensitive characters.
  282. *
  283. * @param string $string The string to sanitize.
  284. * @return string The sanitized string.
  285. *
  286. * @package esoTalk
  287. */
  288. function sanitizeFileName($value)
  289. {
  290. return preg_replace("/(?:[\/:\\\]|\.{2,}|\\x00)/", "", $value);
  291. }
  292. /**
  293. * Sort an array by values in its second dimension.
  294. *
  295. * @param array $array The array to sort.
  296. * @param mixed $index The key of the second dimension to sort the array by.
  297. * @param string $order The direction (asc or desc).
  298. * @param bool $natSort Whether or not to use the natsort function.
  299. * @param bool $caseSensitive Whether or not to use case-sensitive sort functions.
  300. * @return array The sorted array.
  301. *
  302. * @package esoTalk
  303. */
  304. function sort2d($array, $index, $order = "asc", $natSort = false, $caseSensitive = false)
  305. {
  306. if (is_array($array) and count($array) > 0) {
  307. $temp = array();
  308. foreach (array_keys($array) as $key) {
  309. $temp[$key] = $array[$key][$index];
  310. if (!$natSort) ($order == "asc") ? asort($temp) : arsort($temp);
  311. else {
  312. ($caseSensitive) ? natsort($temp) : natcasesort($temp);
  313. if ($order != "asc") $temp = array_reverse($temp, true);
  314. }
  315. }
  316. foreach (array_keys($temp) as $key) $sorted[$key] = $array[$key];
  317. return $sorted;
  318. }
  319. return $array;
  320. }
  321. /**
  322. * Returns whether or not the user is using a mobile device.
  323. *
  324. * @return bool
  325. *
  326. * @package esoTalk
  327. */
  328. function isMobileBrowser()
  329. {
  330. static $isMobileBrowser = null;
  331. if (is_null($isMobileBrowser)) {
  332. // This code is from http://detectmobilebrowser.com/ by Chad Smith. Thanks Chad!
  333. $userAgent = $_SERVER["HTTP_USER_AGENT"];
  334. $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)));
  335. }
  336. return $isMobileBrowser;
  337. }
  338. /**
  339. * Create a slug for use in URLs from a given string. Any non-alphanumeric characters will be converted to "-".
  340. *
  341. * @param string $string The string to convert.
  342. * @return string The slug.
  343. *
  344. * @package esoTalk
  345. */
  346. function slug($string)
  347. {
  348. // Convert special latin letters and other characters to HTML entities.
  349. $slug = htmlentities($string, ENT_NOQUOTES, "UTF-8");
  350. // With those HTML entities, either convert them back to a normal letter, or remove them.
  351. $slug = preg_replace(array("/&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);/i", "/&[^;]{2,6};/"), array("$1", " "), $slug);
  352. // Now replace non-alphanumeric characters with a hyphen, and remove multiple hyphens.
  353. $slug = strtolower(trim(preg_replace(array("/[^0-9a-z]/i", "/-+/"), "-", $slug), "-"));
  354. return substr($slug, 0, 63);
  355. }
  356. /**
  357. * Generate a salt of $numOfChars characters long containing random letters, numbers, and symbols.
  358. *
  359. * @param int $numOfChars The length of the random string.
  360. * @return string The random string.
  361. *
  362. * @package esoTalk
  363. */
  364. function generateRandomString($numOfChars)
  365. {
  366. $possibleChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~!@#%^&*()_+=-{}[]:;<,>.?/`";
  367. $salt = "";
  368. for ($i = 0; $i < $numOfChars; $i++) $salt .= $possibleChars[rand(0, strlen($possibleChars) - 1)];
  369. return $salt;
  370. }
  371. /**
  372. * For bad server configs. Pretty much just performing stripslashes on an array here.
  373. *
  374. * @param mixed $value The value to undo magic quotes on.
  375. * @return mixed
  376. *
  377. * @package esoTalk
  378. */
  379. function undoMagicQuotes($value)
  380. {
  381. if (!is_array($value)) return stripslashes($value);
  382. else array_map("undoMagicQuotes", $value);
  383. return $value;
  384. }
  385. /**
  386. * For bad server configs as well. Unset all input variables added to the global namespace if register_globals
  387. * is on.
  388. *
  389. * @return void
  390. *
  391. * @package esoTalk
  392. */
  393. function undoRegisterGlobals()
  394. {
  395. if (ini_get("register_globals")) {
  396. $array = array("_REQUEST", "_SESSION", "_SERVER", "_ENV", "_FILES");
  397. foreach ($array as $value) {
  398. foreach ((array)$GLOBALS[$value] as $key => $var) {
  399. if (isset($GLOBALS[$key]) and $var === $GLOBALS[$key]) unset($GLOBALS[$key]);
  400. }
  401. }
  402. }
  403. }
  404. /**
  405. * Convert an RGB triplet to an HSL triplet.
  406. *
  407. * @param array $rgb The RGB triplet.
  408. * @return array The HSL triplet.
  409. *
  410. * @package esoTalk
  411. */
  412. function rgb2hsl($rgb)
  413. {
  414. $r = $rgb[0];
  415. $g = $rgb[1];
  416. $b = $rgb[2];
  417. $min = min($r, min($g, $b));
  418. $max = max($r, max($g, $b));
  419. $delta = $max - $min;
  420. $l = ($min + $max) / 2;
  421. $s = 0;
  422. if ($l > 0 && $l < 1) {
  423. $s = $delta / ($l < 0.5 ? (2 * $l) : (2 - 2 * $l));
  424. }
  425. $h = 0;
  426. if ($delta > 0) {
  427. if ($max == $r && $max != $g) {
  428. $h += ($g - $b) / $delta;
  429. }
  430. if ($max == $g && $max != $b) {
  431. $h += (2 + ($b - $r) / $delta);
  432. }
  433. if ($max == $b && $max != $r) {
  434. $h += (4 + ($r - $g) / $delta);
  435. }
  436. $h /= 6;
  437. }
  438. return array($h, $s, $l);
  439. }
  440. /**
  441. * Convert an HSL triplet to an RGB triplet.
  442. *
  443. * @param array $hsl The HSL triplet.
  444. * @return array The RGB triplet.
  445. *
  446. * @package esoTalk
  447. */
  448. function hsl2rgb($hsl)
  449. {
  450. $h = $hsl[0];
  451. $s = $hsl[1];
  452. $l = $hsl[2];
  453. $m2 = ($l <= 0.5) ? $l * ($s + 1) : $l + $s - $l*$s;
  454. $m1 = $l * 2 - $m2;
  455. return array(hue2rgb($m1, $m2, $h + 0.33333),
  456. hue2rgb($m1, $m2, $h),
  457. hue2rgb($m1, $m2, $h - 0.33333));
  458. }
  459. /**
  460. * Helper function for hsl2rgb().
  461. *
  462. * @package esoTalk
  463. */
  464. function hue2rgb($m1, $m2, $h)
  465. {
  466. $h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h);
  467. if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
  468. if ($h * 2 < 1) return $m2;
  469. if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (0.66666 - $h) * 6;
  470. return $m1;
  471. }
  472. /**
  473. * Convert a hex color into an RGB triplet.
  474. *
  475. * @param string $hex The hex color value (with a leading #).
  476. * @param bool $normalize Whether or not the values of the RGB triplet should be 0-255 or 0-1.
  477. * @return array The RGB triplet.
  478. *
  479. * @package esoTalk
  480. */
  481. function colorUnpack($hex, $normalize = false)
  482. {
  483. if (strlen($hex) == 4) {
  484. $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
  485. }
  486. $c = hexdec($hex);
  487. for ($i = 16; $i >= 0; $i -= 8) {
  488. $out[] = (($c >> $i) & 0xFF) / ($normalize ? 255 : 1);
  489. }
  490. return $out;
  491. }
  492. /**
  493. * Convert an RGB triplet to a hex color.
  494. *
  495. * @param array $rgb The RGB triplet.
  496. * @param bool $normalize Whether or not the values of the RGB triplet are 0-255 or 0-1.
  497. * @return The hex color, with a leading #.
  498. *
  499. * @package esoTalk
  500. */
  501. function colorPack($rgb, $normalize = false)
  502. {
  503. $out = null;
  504. foreach ($rgb as $k => $v) {
  505. $out |= (($v * ($normalize ? 255 : 1)) << (16 - $k * 8));
  506. }
  507. return "#".str_pad(dechex($out), 6, 0, STR_PAD_LEFT);
  508. }
  509. // json_encode for PHP < 5.2.0.
  510. if (!function_exists("json_encode")) {
  511. function json_encode($a = false)
  512. {
  513. if (is_null($a)) return "null";
  514. if ($a === false) return "false";
  515. if ($a === true) return "true";
  516. if (is_scalar($a)) {
  517. if (is_float($a)) return floatval(str_replace(",", ".", strval($a)));
  518. if (is_string($a)) {
  519. static $jsonReplaces = array(array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"'), array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'));
  520. return '"'.str_replace($jsonReplaces[0], $jsonReplaces[1], $a).'"';
  521. } else return $a;
  522. }
  523. $isList = true;
  524. for ($i = 0, reset($a), $count = count($a); $i < $count; $i++, next($a)) {
  525. if (key($a) !== $i) {
  526. $isList = false;
  527. break;
  528. }
  529. }
  530. $result = array();
  531. if ($isList) {
  532. foreach ($a as $v) $result[] = json_encode($v);
  533. return '['.implode(',', $result).']';
  534. } else {
  535. foreach ($a as $k => $v) $result[] = '"'.($k).'":'.json_encode($v);
  536. return '{'.implode(',', $result).'}';
  537. }
  538. }
  539. }
  540. // json_decode for PHP < 5.2.0
  541. if (!function_exists("json_decode")) {
  542. function json_decode($json)
  543. {
  544. $json = str_replace(array("\\\\", "\\\""), array("&#92;", "&#34;"), $json);
  545. $parts = preg_split("@(\"[^\"]*\")|([\[\]\{\},:])|\s@is", $json, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
  546. foreach ($parts as $index => $part) {
  547. if (strlen($part) == 1) {
  548. switch ($part) {
  549. case "[":
  550. case "{":
  551. $parts[$index] = "array(";
  552. break;
  553. case "]":
  554. case "}":
  555. $parts[$index] = ")";
  556. break;
  557. case ":":
  558. $parts[$index] = "=>";
  559. break;
  560. case ",":
  561. break;
  562. default:
  563. return null;
  564. }
  565. }
  566. else {
  567. if ((substr($part, 0, 1) != "\"") || (substr($part, -1, 1) != "\""))
  568. return null;
  569. }
  570. }
  571. $json = str_replace(array("&#92;", "&#34;", "$"), array("\\\\", "\\\"", "\\$"), implode("", $parts));
  572. return eval("return $json;");
  573. }
  574. }
  575. /**
  576. * Construct a URL, given a request path.
  577. *
  578. * Constructs a relative or absolute URL which can be used to link
  579. * to a page in esoTalk, according to the format specified by C("esoTalk.urls").
  580. *
  581. * @param string $url The request path (eg. conversations/all). May include a query string/hash.
  582. * @param bool $absolute Whether or not to return an absolute URL.
  583. * @return string
  584. *
  585. * @package esoTalk
  586. */
  587. function URL($url = "", $absolute = false)
  588. {
  589. if (strpos($url, "http://") === 0) return $url;
  590. // Strip off the hash.
  591. $hash = strstr($url, "#");
  592. if ($hash) $url = substr($url, 0, -strlen($hash));
  593. // Strip off the query string.
  594. $query = strstr($url, "?");
  595. if ($query) $url = substr($url, 0, -strlen($query));
  596. // If we don't have nice urls, use ?p=controller/method/argument instead.
  597. if (!C("esoTalk.urls.friendly") and $url) {
  598. $link = "?p=".$url;
  599. if ($query) $query[0] = "&";
  600. }
  601. else $link = $url;
  602. // Re-add the query string and has to the URL.
  603. $link .= $query . $hash;
  604. // If we're not using mod_rewrite, we need to prepend "index.php/" to the link.
  605. if (C("esoTalk.urls.friendly") and !C("esoTalk.urls.rewrite")) $link = "index.php/$link";
  606. return $absolute ? rtrim(C("esoTalk.baseURL"), "/")."/".$link : getWebPath($link);
  607. }
  608. /**
  609. * Remove the absolute path to the root esoTalk directory from a file path.
  610. *
  611. * @param string $path The file path.
  612. * @return string The path relative to the esoTalk root directory.
  613. *
  614. * @package esoTalk
  615. */
  616. function getRelativePath($path)
  617. {
  618. if (strpos($path, PATH_ROOT) === 0) $path = substr($path, strlen(PATH_ROOT) + 1);
  619. return $path;
  620. }
  621. /**
  622. * Get the "web path" (the path relative to the domain root) for a file.
  623. *
  624. * @param string $path The path to convert.
  625. * @return string The web path for the specified path.
  626. *
  627. * @package esoTalk
  628. */
  629. function getWebPath($path)
  630. {
  631. if (strpos($path, "://") === false) {
  632. // Remove the absolute path to the root esoTalk directory.
  633. $path = getRelativePath($path);
  634. // Prepend the web path.
  635. $path = ET::$webPath."/".ltrim($path, "/");
  636. }
  637. return $path;
  638. }
  639. /**
  640. * Get the relative path or URL to a resource (a web-accessible file stored in plugins, skins, languages, or js)
  641. * depending on the state of the resourceURL config setting.
  642. *
  643. * @param string $path The absolute path to the resource.
  644. * @return string The relative path or URL to the given resource.
  645. *
  646. * @package esoTalk
  647. */
  648. function getResource($path, $absolute = false)
  649. {
  650. if (strpos($path, "://") === false) {
  651. // Remove the absolute path to the root esoTalk directory.
  652. $path = getRelativePath($path);
  653. // Prepend the web path.
  654. $path = ltrim($path, "/");
  655. if ($c = C("esoTalk.resourceURL")) $path = $c.$path;
  656. else $path = $absolute ? rtrim(C("esoTalk.baseURL"), "/")."/".$path : ET::$webPath."/".$path;
  657. }
  658. return $path;
  659. }
  660. /**
  661. * Construct a URL to a conversation, given its ID and title.
  662. *
  663. * @param int $conversationId The ID of the conversation.
  664. * @param string $title The title of the conversation.
  665. * @return string The URL to the conversation (to be used in the URL function.)
  666. *
  667. * @package esoTalk
  668. */
  669. function conversationURL($conversationId, $title = "")
  670. {
  671. return $conversationId.(($title = slug($title)) ? "-$title" : "");
  672. }
  673. /**
  674. * Construct a URL to a member, given their ID and username, and the profile pane to go to.
  675. *
  676. * @param int $memberId The ID of the member.
  677. * @param string $username The member's username.
  678. * @param string $pane The profile pane to go to.
  679. * @return string The URL to the member's profile (to be used in the URL function.)
  680. *
  681. * @package esoTalk
  682. */
  683. function memberURL($memberId, $username = "", $pane = "")
  684. {
  685. return "member/".($pane ? "$pane/" : "").$memberId.(($username = slug($username)) ? "-$username" : "");
  686. }
  687. /**
  688. * Construct a URL to a post, given its ID.
  689. *
  690. * @param int $postId The ID of the post.
  691. * @return string The URL to the post (to be used in the URL function.)
  692. *
  693. * @package esoTalk
  694. */
  695. function postURL($postId)
  696. {
  697. return "conversation/post/".$postId;
  698. }
  699. /**
  700. * Construct a URL to a search results page, given a search string.
  701. *
  702. * @param string $search The search string.
  703. * @param string $channel The channel slug ('all' if not specified.)
  704. * @return string The URL to the search page (to be used in the URL function.)
  705. *
  706. * @package esoTalk
  707. */
  708. function searchURL($search, $channel = "all")
  709. {
  710. return "conversations/$channel/".($search ? "?search=".urlencode($search) : "");
  711. }
  712. /**
  713. * Send a HTTP Location header to redirect to a specific page.
  714. *
  715. * @param string $destination The location to redirect to.
  716. * @param int $code The HTTP code to send with the redirection.
  717. * @return void
  718. *
  719. * @package esoTalk
  720. */
  721. function redirect($destination, $code = 302)
  722. {
  723. // Close the database connection.
  724. if (ET::$database) ET::$database->close();
  725. // Clear the output buffer, and send the location header.
  726. @ob_end_clean();
  727. header("Location: ".sanitizeForHTTP($destination), true, $code);
  728. exit;
  729. }
  730. /**
  731. * Get a human-friendly string (eg. 1 hour ago) for how much time has passed since a given time.
  732. *
  733. * @param int $then UNIX timestamp of the time to work out how much time has passed since.
  734. * @param bool $precise Whether or not to return "x minutes/seconds", or just "a few minutes".
  735. * @return string A human-friendly time string.
  736. *
  737. * @package esoTalk
  738. */
  739. function relativeTime($then, $precise = false)
  740. {
  741. // If there is no $then, we can only assume that whatever it is never happened...
  742. if (!$then) return T("never");
  743. // Work out how many seconds it has been since $then.
  744. $ago = time() - $then;
  745. // If $then happened less than 1 second ago (or is yet to happen,) say "Just now".
  746. if ($ago < 1) return T("just now");
  747. // If this happened over a year ago, return "x years ago".
  748. if ($ago >= ($period = 60 * 60 * 24 * 365.25)) {
  749. $years = floor($ago / $period);
  750. return Ts("%d year ago", "%d years ago", $years);
  751. }
  752. // If this happened over two months ago, return "x months ago".
  753. elseif ($ago >= ($period = 60 * 60 * 24 * (365.25 / 12)) * 2) {
  754. $months = floor($ago / $period);
  755. return Ts("%d month ago", "%d months ago", $months);
  756. }
  757. // If this happend over a week ago, return "x weeks ago".
  758. elseif ($ago >= ($period = 60 * 60 * 24 * 7)) {
  759. $weeks = floor($ago / $period);
  760. return Ts("%d week ago", "%d weeks ago", $weeks);
  761. }
  762. // If this happened over a day ago, return "x days ago".
  763. elseif ($ago >= ($period = 60 * 60 * 24)) {
  764. $days = floor($ago / $period);
  765. return Ts("%d day ago", "%d days ago", $days);
  766. }
  767. // If this happened over an hour ago, return "x hours ago".
  768. elseif ($ago >= ($period = 60 * 60)) {
  769. $hours = floor($ago / $period);
  770. return Ts("%d hour ago", "%d hours ago", $hours);
  771. }
  772. // If we're going for a precise value, go on to test at the minute/second level.
  773. if ($precise) {
  774. // If this happened over a minute ago, return "x minutes ago".
  775. if ($ago >= ($period = 60)) {
  776. $minutes = floor($ago / $period);
  777. return Ts("%d minute ago", "%d minutes ago", $minutes);
  778. }
  779. // Return "x seconds ago".
  780. elseif ($ago >= 1) return Ts("%d second ago", "%d seconds ago", $ago);
  781. }
  782. // Otherwise, just return "Just now".
  783. return T("just now");
  784. }
  785. /**
  786. * Extract the contents of a ZIP file, and return a list of files it contains and their contents.
  787. *
  788. * @param string $filename The filepath to the ZIP file.
  789. * @return array An array of files and their details/contents.
  790. *
  791. * @package esoTalk
  792. */
  793. function unzip($filename)
  794. {
  795. $files = array();
  796. $handle = fopen($filename, "rb");
  797. // Seek to the end of central directory record.
  798. $size = filesize($filename);
  799. @fseek($handle, $size - 22);
  800. // Error checking.
  801. if (ftell($handle) != $size - 22) return false; // Can't seek to end of central directory?
  802. // Check end of central directory signature.
  803. $data = unpack("Vid", fread($handle, 4));
  804. if ($data["id"] != 0x06054b50) return false;
  805. // Extract the central directory information.
  806. $centralDir = unpack("vdisk/vdiskStart/vdiskEntries/ventries/Vsize/Voffset/vcommentSize", fread($handle, 18));
  807. $pos = $centralDir["offset"];
  808. // Loop through each entry in the zip file.
  809. for ($i = 0; $i < $centralDir["entries"]; $i++) {
  810. // Read next central directory structure header.
  811. @rewind($handle);
  812. @fseek($handle, $pos + 4);
  813. $header = unpack("vversion/vversionExtracted/vflag/vcompression/vmtime/vmdate/Vcrc/VcompressedSize/Vsize/vfilenameLen/vextraLen/vcommentLen/vdisk/vinternal/Vexternal/Voffset", fread($handle, 42));
  814. // Get the filename.
  815. $header["filename"] = $header["filenameLen"] ? fread($handle, $header["filenameLen"]) : "";
  816. // Save the position.
  817. $pos = ftell($handle) + $header["extraLen"] + $header["commentLen"];
  818. // Go to the position of the file.
  819. @rewind($handle);
  820. @fseek($handle, $header["offset"] + 4);
  821. // Read the local file header to get the filename length.
  822. $localHeader = unpack("vversion/vflag/vcompression/vmtime/vmdate/Vcrc/VcompressedSize/Vsize/vfilenameLen/vextraLen", fread($handle, 26));
  823. // Get the filename.
  824. $localHeader["filename"] = fread($handle, $localHeader["filenameLen"]);
  825. // Skip the extra bit.
  826. if ($localHeader["extraLen"] > 0) fread($handle, $localHeader["extraLen"]);
  827. // Extract the file (if it's not a folder.)
  828. $directory = substr($header["filename"], -1) == "/";
  829. if (!$directory and $header["compressedSize"] > 0) {
  830. if ($header["compression"] == 0) $content = fread($handle, $header["compressedSize"]);
  831. else $content = gzinflate(fread($handle, $header["compressedSize"]));
  832. } else $content = "";
  833. // Add to the files array.
  834. $files[] = array(
  835. "name" => $header["filename"],
  836. "size" => $header["size"],
  837. "directory" => $directory,
  838. "content" => !$directory ? $content : false
  839. );
  840. }
  841. fclose($handle);
  842. // Return an array of files that were extracted.
  843. return $files;
  844. }
  845. /**
  846. * Add an element to an indexed array after a specified position.
  847. *
  848. * @param array $array The array to add to.
  849. * @param mixed $add The element to add.
  850. * @param int $position The index to add the element at.
  851. * @return void
  852. *
  853. * @package esoTalk
  854. */
  855. function addToArray(&$array, $add, $position = false)
  856. {
  857. // If no position is specified, add it to the end and return the key
  858. if ($position === false) {
  859. $array[] = $add;
  860. end($array);
  861. ksort($array);
  862. return key($array);
  863. }
  864. // Else, until we can get ahold of a position (starting from the specified one), keep on going!
  865. // Consider replacing this with array_splice, and adding the element AT the specified position (not after?)
  866. do {
  867. if (isset($array[$position])) {
  868. $position++;
  869. continue;
  870. }
  871. $array[$position] = $add;
  872. ksort($array);
  873. return $position;
  874. } while (true);
  875. }
  876. /**
  877. * Add an element to an keyed array after/before a certain key, or at a certain index.
  878. *
  879. * @param array $array The array to add to.
  880. * @param string $key The key to add to the array.
  881. * @param mixed $value The value to add to the array.
  882. * @param mixed $position The position to add the element at. If this is an integer, the element will be added
  883. * at that index. If this is an array with the first key as "before" or "after", the element will be added
  884. * before or after the specified key.
  885. * @return void
  886. *
  887. * @package esoTalk
  888. */
  889. function addToArrayString(&$array, $key, $value, $position = false)
  890. {
  891. // If we're intending to add it to the end of the array, that's easy.
  892. if ($position === false) {
  893. $array[$key] = $value;
  894. return;
  895. }
  896. // Otherwise, split the array into keys and values.
  897. $keys = array_keys($array);
  898. $values = array_values($array);
  899. // If the position is "before" or "after" a certain key, find that key in the array and record the index.
  900. // If the key doesn't exist, then we'll add the element to the end of the array.
  901. if (is_array($position)) {
  902. $index = array_search(reset($position), $keys, true);
  903. if ($index === false) $index = count($array);
  904. if (key($position) == "after") $index++;
  905. }
  906. // If the position is just an integer, then we already have the index.
  907. else $index = (int)$position;
  908. // Add the key/value to their respective arrays at the appropriate index.
  909. array_splice($keys, $index, 0, $key);
  910. array_splice($values, $index, 0, array($value));
  911. // Combine the new keys/values!
  912. $array = array_combine($keys, $values);
  913. }
  914. if (function_exists("lcfirst") === false) {
  915. /**
  916. * Make a string's first character lowercase.
  917. *
  918. * NOTE: Is included in PHP 5 >= 5.3.0
  919. *
  920. * @param string $str The input string.
  921. * @return string
  922. *
  923. * @package esoTalk
  924. */
  925. function lcfirst($str)
  926. {
  927. $str[0] = strtolower($str[0]);
  928. return $str;
  929. }
  930. }