PageRenderTime 31ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/textpattern/lib/txplib_misc.php

http://textpattern.googlecode.com/
PHP | 6665 lines | 4403 code | 728 blank | 1534 comment | 368 complexity | dae7c70e15316972451dd92cbcf576e7 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * Textpattern Content Management System
  4. * http://textpattern.com
  5. *
  6. * Copyright (C) 2014 The Textpattern Development Team
  7. *
  8. * This file is part of Textpattern.
  9. *
  10. * Textpattern is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation, version 2.
  13. *
  14. * Textpattern is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with Textpattern. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. /**
  23. * Collection of miscellaneous tools.
  24. *
  25. * @package Misc
  26. */
  27. /**
  28. * Strips NULL bytes.
  29. *
  30. * @param string|array $in The input value
  31. * @return mixed
  32. */
  33. function deNull($in)
  34. {
  35. return is_array($in) ? doArray($in, 'deNull') : strtr($in, array("\0" => ''));
  36. }
  37. /**
  38. * Strips carriage returns and linefeeds.
  39. *
  40. * @param string|array $in The input value
  41. * @return mixed
  42. */
  43. function deCRLF($in)
  44. {
  45. return is_array($in) ? doArray($in, 'deCRLF') : strtr($in, array("\n" => '', "\r" => ''));
  46. }
  47. /**
  48. * Applies a callback to a given string or an array.
  49. *
  50. * @param string|array $in An array or a string to run through the callback function
  51. * @param callback $function The callback function
  52. * @return mixed
  53. * @example
  54. * echo doArray(array('value1', 'value2'), 'intval');
  55. */
  56. function doArray($in, $function)
  57. {
  58. if (is_array($in)) {
  59. return array_map($function, $in);
  60. }
  61. if (is_array($function)) {
  62. return call_user_func($function, $in);
  63. }
  64. return $function($in);
  65. }
  66. /**
  67. * Un-quotes a quoted string or an array of values.
  68. *
  69. * @param string|array $in The input value
  70. * @return mixed
  71. */
  72. function doStrip($in)
  73. {
  74. return is_array($in) ? doArray($in, 'doStrip') : doArray($in, 'stripslashes');
  75. }
  76. /**
  77. * Strips HTML and PHP tags from a string or an array.
  78. *
  79. * @param string|array $in The input value
  80. * @return mixed
  81. * @example
  82. * echo doStripTags('<p>Hello world!</p>');
  83. */
  84. function doStripTags($in)
  85. {
  86. return is_array($in) ? doArray($in, 'doStripTags') : doArray($in, 'strip_tags');
  87. }
  88. /**
  89. * Converts entity escaped brackets back to characters.
  90. *
  91. * @param string|array $in The input value
  92. * @return mixed
  93. */
  94. function doDeEnt($in)
  95. {
  96. return doArray($in, 'deEntBrackets');
  97. }
  98. /**
  99. * Converts entity escaped brackets back to characters.
  100. *
  101. * @param string $in The input value
  102. * @return string
  103. */
  104. function deEntBrackets($in)
  105. {
  106. $array = array(
  107. '&#60;' => '<',
  108. '&lt;' => '<',
  109. '&#x3C;' => '<',
  110. '&#62;' => '>',
  111. '&gt;' => '>',
  112. '&#x3E;' => '>',
  113. );
  114. foreach ($array as $k => $v) {
  115. $in = preg_replace("/".preg_quote($k)."/i", $v, $in);
  116. }
  117. return $in;
  118. }
  119. /**
  120. * Escapes special characters for use in an SQL statement.
  121. *
  122. * Always use this function when dealing with user-defined values
  123. * in SQL statements. If this function is not used to escape
  124. * user-defined data in a statement, the query is vulnerable to
  125. * SQL injection attacks.
  126. *
  127. * @param string|array $in The input value
  128. * @return mixed An array of escaped values or a string depending on $in
  129. * @package DB
  130. * @example
  131. * echo safe_field('column', 'table', "color='" . doSlash(gps('color')) . "'");
  132. */
  133. function doSlash($in)
  134. {
  135. return doArray($in, 'safe_escape');
  136. }
  137. /**
  138. * Escape SQL LIKE pattern's wildcards for use in an SQL statement.
  139. *
  140. * @param string|array $in The input value
  141. * @return mixed An array of escaped values or a string depending on $in
  142. * @since 4.6.0
  143. * @package DB
  144. * @example
  145. * echo safe_field('column', 'table', "color LIKE '" . doLike(gps('color')) . "'");
  146. */
  147. function doLike($in)
  148. {
  149. return doArray($in, 'safe_escape_like');
  150. }
  151. /**
  152. * A shell for htmlspecialchars() with $flags defaulting to ENT_QUOTES.
  153. *
  154. * @param string $string The string being converted
  155. * @param int $flags A bitmask of one or more flags. The default is ENT_QUOTES
  156. * @param string $encoding Defines encoding used in conversion. The default is UTF-8
  157. * @param bool $double_encode When double_encode is turned off PHP will not encode existing HTML entities, the default is to convert everything
  158. * @return string
  159. * @see http://www.php.net/manual/function.htmlspecialchars.php
  160. * @since 4.5.0
  161. * @package Filter
  162. */
  163. function txpspecialchars($string, $flags = ENT_QUOTES, $encoding = 'UTF-8', $double_encode = true)
  164. {
  165. // Ignore ENT_HTML5 and ENT_XHTML for now.
  166. // ENT_HTML5 and ENT_XHTML are defined in PHP 5.4+ but we consistently encode single quotes as &#039; in any doctype.
  167. // global $prefs;
  168. // static $h5 = null;
  169. //
  170. // if (defined(ENT_HTML5)) {
  171. // if ($h5 === null) {
  172. // $h5 = ($prefs['doctype'] == 'html5' && txpinterface == 'public');
  173. // }
  174. //
  175. // if ($h5) {
  176. // $flags = ($flags | ENT_HTML5) & ~ENT_HTML401;
  177. // }
  178. // }
  179. //
  180. return htmlspecialchars($string, $flags, $encoding, $double_encode);
  181. }
  182. /**
  183. * Converts special characters to HTML entities.
  184. *
  185. * @param array|string $in The input value
  186. * @return mixed The array or string with HTML syntax characters escaped
  187. * @package Filter
  188. */
  189. function doSpecial($in)
  190. {
  191. return doArray($in, 'txpspecialchars');
  192. }
  193. /**
  194. * Converts the given value to NULL.
  195. *
  196. * @param mixed $a The input value
  197. * @return null
  198. * @package Filter
  199. * @access private
  200. */
  201. function _null($a)
  202. {
  203. return null;
  204. }
  205. /**
  206. * Converts an array of values to NULL.
  207. *
  208. * @param array $in The array
  209. * @return array
  210. * @package Filter
  211. */
  212. function array_null($in)
  213. {
  214. return array_map('_null', $in);
  215. }
  216. /**
  217. * Escapes a page title. Converts &lt;, &gt;, ', " characters to HTML entities.
  218. *
  219. * @param string $title The input string
  220. * @return string The string escaped
  221. * @package Filter
  222. */
  223. function escape_title($title)
  224. {
  225. return strtr($title, array(
  226. '<' => '&#60;',
  227. '>' => '&#62;',
  228. "'" => '&#39;',
  229. '"' => '&#34;',
  230. ));
  231. }
  232. /**
  233. * Sanitises a string for use in a JavaScript string.
  234. *
  235. * This function escapes \, \n, \r, " and ' characters. When
  236. * you need to pass a string from PHP to JavaScript, use this
  237. * function to sanitise the value to avoid XSS attempts.
  238. *
  239. * @param string $js JavaScript input
  240. * @return string Escaped JavaScript
  241. * @since 4.4.0
  242. * @package Filter
  243. */
  244. function escape_js($js)
  245. {
  246. return addcslashes($js, "\\\'\"\n\r");
  247. }
  248. /**
  249. * A shell for htmlspecialchars() with $flags defaulting to ENT_QUOTES.
  250. *
  251. * @param string $str The input string
  252. * @return string
  253. * @deprecated in 4.2.0
  254. * @see txpspecialchars()
  255. * @package Filter
  256. */
  257. function escape_output($str)
  258. {
  259. trigger_error(gTxt('deprecated_function_with', array('{name}' => __FUNCTION__, '{with}' => 'txpspecialchars')), E_USER_NOTICE);
  260. return txpspecialchars($str);
  261. }
  262. /**
  263. * Replaces &lt; and &gt; characters with entities.
  264. *
  265. * @param string $str The input string
  266. * @return string
  267. * @deprecated in 4.2.0
  268. * @see txpspecialchars()
  269. * @package Filter
  270. */
  271. function escape_tags($str)
  272. {
  273. trigger_error(gTxt('deprecated_function', array('{name}' => __FUNCTION__)), E_USER_NOTICE);
  274. return strtr($str, array(
  275. '<' => '&#60;',
  276. '>' => '&#62;',
  277. ));
  278. }
  279. /**
  280. * Escapes CDATA section for an XML document.
  281. *
  282. * @param string $str The string
  283. * @return string XML representation wrapped in CDATA tags
  284. * @package XML
  285. */
  286. function escape_cdata($str)
  287. {
  288. return '<![CDATA['.str_replace(']]>', ']]]><![CDATA[]>', $str).']]>';
  289. }
  290. /**
  291. * Returns a localisation string.
  292. *
  293. * @param string $var String name
  294. * @param array $atts Replacement pairs
  295. * @param string $escape Convert special characters to HTML entities. Either "html" or ""
  296. * @return string A localisation string
  297. * @package L10n
  298. */
  299. function gTxt($var, $atts = array(), $escape = 'html')
  300. {
  301. global $textarray;
  302. if (!is_array($atts)) {
  303. $atts = array();
  304. }
  305. if ($escape == 'html') {
  306. foreach ($atts as $key => $value) {
  307. $atts[$key] = txpspecialchars($value);
  308. }
  309. }
  310. $v = strtolower($var);
  311. if (isset($textarray[$v])) {
  312. $out = $textarray[$v];
  313. if ($out !== '') {
  314. return strtr($out, $atts);
  315. }
  316. }
  317. if ($atts) {
  318. return $var.': '.join(', ', $atts);
  319. }
  320. return $var;
  321. }
  322. /**
  323. * Loads client-side localisation scripts.
  324. *
  325. * This function passes localisation strings from the database
  326. * to JavaScript.
  327. *
  328. * Only works on the admin-side pages.
  329. *
  330. * @param string|array $var Scalar or array of string keys
  331. * @param array $atts Array or array of arrays of variable substitution pairs
  332. * @since 4.5.0
  333. * @package L10n
  334. * @example
  335. * gTxtScript(array('string1', 'string2', 'string3'));
  336. */
  337. function gTxtScript($var, $atts = array())
  338. {
  339. global $textarray_script;
  340. if (!is_array($textarray_script)) {
  341. $textarray_script = array();
  342. }
  343. $data = is_array($var) ? array_map('gTxt', $var, $atts) : (array) gTxt($var, $atts);
  344. $textarray_script = $textarray_script + array_combine((array) $var, $data);
  345. }
  346. /**
  347. * Returns given timestamp in a format of 01 Jan 2001 15:19:16.
  348. *
  349. * @param int $timestamp The UNIX timestamp
  350. * @return string A formatted date
  351. * @access private
  352. * @see safe_stftime()
  353. * @package DateTime
  354. * @example
  355. * echo gTime();
  356. */
  357. function gTime($timestamp = 0)
  358. {
  359. return safe_strftime('%d&#160;%b&#160;%Y %X', $timestamp);
  360. }
  361. /**
  362. * Cretes a dumpfile from a backtrace and outputs given parameters.
  363. *
  364. * @package Debug
  365. */
  366. function dmp()
  367. {
  368. static $f = false;
  369. if (defined('txpdmpfile')) {
  370. global $prefs;
  371. if (!$f) {
  372. $f = fopen($prefs['tempdir'].'/'.txpdmpfile, 'a');
  373. }
  374. $stack = get_caller();
  375. fwrite($f, "\n[".$stack[0].t.safe_strftime('iso8601')."]\n");
  376. }
  377. $a = func_get_args();
  378. if (!$f) {
  379. echo "<pre dir=\"auto\">".n;
  380. }
  381. foreach ($a as $thing) {
  382. $out = is_scalar($thing) ? strval($thing) : var_export($thing, true);
  383. if ($f) {
  384. fwrite($f, $out.n);
  385. } else {
  386. echo txpspecialchars($out).n;
  387. }
  388. }
  389. if (!$f) {
  390. echo "</pre>".n;
  391. }
  392. }
  393. /**
  394. * Gets the given language's strings from the database.
  395. *
  396. * This function gets the given language from the database
  397. * and returns the strings as an array.
  398. *
  399. * If no $events is specified, only appropriate strings for the
  400. * current context are returned. If 'txpinterface' constant equals 'admin' all
  401. * strings are returned. Otherwise, only strings from events 'common' and 'public'.
  402. *
  403. * If $events is FALSE, returns all strings.
  404. *
  405. * @param string $lang The language code
  406. * @param array|string|bool $events An array of loaded events
  407. * @return array
  408. * @package L10n
  409. * @see load_lang_event()
  410. * @example
  411. * print_r(
  412. * load_lang('en-gb', false)
  413. * );
  414. */
  415. function load_lang($lang, $events = null)
  416. {
  417. if ($events === null && txpinterface != 'admin') {
  418. $events = array('public', 'common');
  419. }
  420. $where = '';
  421. if ($events) {
  422. $where .= ' and event in('.join(',', quote_list((array) $events)).')';
  423. }
  424. foreach (array($lang, 'en-gb') as $lang_code) {
  425. $rs = safe_rows('name, data', 'txp_lang', "lang='".doSlash($lang_code)."'".$where);
  426. if (!empty($rs)) {
  427. break;
  428. }
  429. }
  430. $out = array();
  431. if (!empty($rs)) {
  432. foreach ($rs as $a) {
  433. if (!empty($a['name'])) {
  434. $out[$a['name']] = $a['data'];
  435. }
  436. }
  437. }
  438. return $out;
  439. }
  440. /**
  441. * Loads date definitions from a localisation file.
  442. *
  443. * @param string $lang The language
  444. * @package L10n
  445. * @deprecated in 4.6.0
  446. */
  447. function load_lang_dates($lang)
  448. {
  449. $filename = is_file(txpath.'/lang/'.$lang.'_dates.txt')?
  450. txpath.'/lang/'.$lang.'_dates.txt':
  451. txpath.'/lang/en-gb_dates.txt';
  452. $file = @file(txpath.'/lang/'.$lang.'_dates.txt', 'r');
  453. if (is_array($file)) {
  454. foreach ($file as $line) {
  455. if ($line[0] == '#' || strlen($line) < 2) {
  456. continue;
  457. }
  458. list($name, $val) = explode('=>', $line, 2);
  459. $out[trim($name)] = trim($val);
  460. }
  461. return $out;
  462. }
  463. return false;
  464. }
  465. /**
  466. * Gets language strings for the given event.
  467. *
  468. * If no $lang is specified, the strings are loaded
  469. * from the currently active language.
  470. *
  471. * @param string $event The event to get, e.g. "common", "admin", "public"
  472. * @param string $lang The language code
  473. * @return array|string Array of string on success, or an empty string when no strings were found
  474. * @package L10n
  475. * @see load_lang()
  476. * @example
  477. * print_r(
  478. * load_lang_event('common')
  479. * );
  480. */
  481. function load_lang_event($event, $lang = LANG)
  482. {
  483. $installed = (false !== safe_field('name', 'txp_lang',"lang='".doSlash($lang)."' limit 1"));
  484. $lang_code = ($installed) ? $lang : 'en-gb';
  485. $rs = safe_rows_start('name, data', 'txp_lang', "lang='".doSlash($lang_code)."' AND event='".doSlash($event)."'");
  486. $out = array();
  487. if ($rs && !empty($rs)) {
  488. while ($a = nextRow($rs)) {
  489. $out[$a['name']] = $a['data'];
  490. }
  491. }
  492. return ($out) ? $out : '';
  493. }
  494. /**
  495. * Requires privileges from a user.
  496. *
  497. * @deprecated in 4.3.0
  498. * @see require_privs()
  499. * @package User
  500. */
  501. function check_privs()
  502. {
  503. trigger_error(gTxt('deprecated_function_with', array('{name}' => __FUNCTION__, '{with}' => 'require_privs')), E_USER_NOTICE);
  504. global $txp_user;
  505. $privs = safe_field("privs", "txp_users", "name='".doSlash($txp_user)."'");
  506. $args = func_get_args();
  507. if (!in_array($privs,$args)) {
  508. exit(pageTop('Restricted').'<p style="margin-top:3em;text-align:center">'.
  509. gTxt('restricted_area').'</p>');
  510. }
  511. }
  512. /**
  513. * Grants privileges to user-groups.
  514. *
  515. * This function will not let you to override existing privs.
  516. *
  517. * @param string $res The resource
  518. * @param string $perm List of user-groups, e.g. '1,2,3'
  519. * @package User
  520. * @example
  521. * add_privs('my_admin_side_panel_event', '1,2,3,4,5');
  522. */
  523. function add_privs($res, $perm = '1')
  524. {
  525. global $txp_permissions;
  526. if (!isset($txp_permissions[$res])) {
  527. $perm = join(',', do_list($perm));
  528. $txp_permissions[$res] = $perm;
  529. }
  530. }
  531. /**
  532. * Checks if a user has privileges to the given resource.
  533. *
  534. * @param string $res The resource
  535. * @param string $user The user. If no user name is supplied, assume the current logged in user
  536. * @return bool
  537. * @package User
  538. * @example
  539. * add_privs('my_privilege_resource', '1,2,3');
  540. * if (has_privs('my_privilege_resource', 'username'))
  541. * {
  542. * echo "'username' has privileges to 'my_privilege_resource'.";
  543. * }
  544. */
  545. function has_privs($res, $user = '')
  546. {
  547. global $txp_user, $txp_permissions;
  548. static $privs;
  549. $user = (string) $user;
  550. if ($user === '') {
  551. $user = (string) $txp_user;
  552. }
  553. if ($user !== '') {
  554. if (!isset($privs[$user])) {
  555. $privs[$user] = safe_field("privs", "txp_users", "name = '".doSlash($user)."'");
  556. }
  557. if (isset($txp_permissions[$res]) && $privs[$user] && $txp_permissions[$res]) {
  558. return in_list($privs[$user], $txp_permissions[$res]);
  559. }
  560. }
  561. return false;
  562. }
  563. /**
  564. * Require privileges from a user to the given resource.
  565. *
  566. * Terminates the script if user doesn't have required privileges.
  567. *
  568. * @param string|null $res The resource, or NULL
  569. * @param string $user The user. If no user name is supplied, assume the current logged in user
  570. * @package User
  571. * @example
  572. * require_privs('article.edit');
  573. */
  574. function require_privs($res = null, $user = '')
  575. {
  576. if ($res === null || !has_privs($res, $user)) {
  577. pagetop(gTxt('restricted_area'));
  578. echo graf(gTxt('restricted_area'), array('class' => 'restricted-area'));
  579. end_page();
  580. exit;
  581. }
  582. }
  583. /**
  584. * Gets a list of users having access to a resource.
  585. *
  586. * @param string $res The resource, e.g. 'article.edit.published'
  587. * @return array A list of usernames
  588. * @since 4.5.0
  589. * @package User
  590. */
  591. function the_privileged($res)
  592. {
  593. global $txp_permissions;
  594. if (isset($txp_permissions[$res])) {
  595. return safe_column('name', 'txp_users', "FIND_IN_SET(privs, '{$txp_permissions[$res]}') order by name asc");
  596. } else {
  597. return array();
  598. }
  599. }
  600. /**
  601. * Gets a list of user groups.
  602. *
  603. * @return array
  604. * @package User
  605. * @example
  606. * print_r(
  607. * get_groups()
  608. * );
  609. */
  610. function get_groups()
  611. {
  612. global $txp_groups;
  613. return doArray($txp_groups, 'gTxt');
  614. }
  615. /**
  616. * Gets the dimensions of an image for a HTML &lt;img&gt; tag.
  617. *
  618. * @param string $name The filename
  619. * @return string|bool height="100" width="40", or FALSE on failure
  620. * @package Image
  621. * @example
  622. * if ($size = sizeImage('/path/to/image.png'))
  623. * {
  624. * echo "&lt;img src='image.png' {$size} /&gt;";
  625. * }
  626. */
  627. function sizeImage($name)
  628. {
  629. $size = @getimagesize($name);
  630. return is_array($size) ? $size[3] : false;
  631. }
  632. /**
  633. * Lists image types that can be safely uploaded.
  634. *
  635. * This function returns different results
  636. * based on the logged in user's privileges.
  637. *
  638. * @param int $type If set, validates the given value
  639. * @return mixed
  640. * @package Image
  641. * @since 4.6.0
  642. * @example
  643. * list($width, $height, $extension) = getimagesize('image');
  644. * if ($type = get_safe_image_types($extension))
  645. * {
  646. * echo "Valid image of {$type}.";
  647. * }
  648. */
  649. function get_safe_image_types($type = null)
  650. {
  651. if (!has_privs('image.create.trusted')) {
  652. $extensions = array(0, '.gif', '.jpg', '.png');
  653. } else {
  654. $extensions = array(0, '.gif', '.jpg', '.png', '.swf', 0, 0, 0, 0, 0, 0, 0, 0, '.swf');
  655. }
  656. if (func_num_args() > 0) {
  657. return !empty($extensions[$type]) ? $extensions[$type] : false;
  658. }
  659. return $extensions;
  660. }
  661. /**
  662. * Checks if GD supports the given image type.
  663. *
  664. * @param string $image_type Either '.gif', '.png', '.jpg'
  665. * @return bool TRUE if the type is supported
  666. * @package Image
  667. */
  668. function check_gd($image_type)
  669. {
  670. if (!function_exists('gd_info')) {
  671. return false;
  672. }
  673. $gd_info = gd_info();
  674. switch ($image_type) {
  675. case '.gif' :
  676. return ($gd_info['GIF Create Support'] == true);
  677. break;
  678. case '.png' :
  679. return ($gd_info['PNG Support'] == true);
  680. break;
  681. case '.jpg' :
  682. return (!empty($gd_info['JPEG Support']) || !empty($gd_info['JPG Support']));
  683. break;
  684. }
  685. return false;
  686. }
  687. /**
  688. * Uploads an image.
  689. *
  690. * This function can be used to upload a new image or replace an existing one.
  691. * If $id is specified, the image will be replaced. If $uploaded is set
  692. * FALSE, $file can take a local file instead of HTTP file upload variable.
  693. *
  694. * All uploaded files will included on the Images panel.
  695. *
  696. * @param array $file HTTP file upload variables
  697. * @param array $meta Image meta data, allowed keys 'caption', 'alt', 'category'
  698. * @param int $id Existing image's ID
  699. * @param bool $uploaded If FALSE, $file takes a filename instead of upload vars
  700. * @return array|string An array of array(message, id) on success, localized error string on error
  701. * @package Image
  702. * @example
  703. * print_r(image_data(
  704. * $_FILES['myfile'],
  705. * array(
  706. * 'caption' => '',
  707. * 'alt' => '',
  708. * 'category' => '',
  709. * )
  710. * ));
  711. */
  712. function image_data($file, $meta = array(), $id = 0, $uploaded = true)
  713. {
  714. global $txp_user, $event;
  715. $name = $file['name'];
  716. $error = $file['error'];
  717. $file = $file['tmp_name'];
  718. if ($uploaded) {
  719. $file = get_uploaded_file($file);
  720. if (get_pref('file_max_upload_size') < filesize($file)) {
  721. unlink($file);
  722. return upload_get_errormsg(UPLOAD_ERR_FORM_SIZE);
  723. }
  724. }
  725. if (empty($file)) {
  726. return upload_get_errormsg(UPLOAD_ERR_NO_FILE);
  727. }
  728. list($w, $h, $extension) = getimagesize($file);
  729. $ext = get_safe_image_types($extension);
  730. if (!$ext) {
  731. return gTxt('only_graphic_files_allowed');
  732. }
  733. $name = substr($name, 0, strrpos($name, '.')).$ext;
  734. $safename = doSlash($name);
  735. $meta = lAtts(array(
  736. 'category' => '',
  737. 'caption' => '',
  738. 'alt' => ''
  739. ), (array) $meta, false);
  740. extract(doSlash($meta));
  741. $q = "
  742. name = '$safename',
  743. ext = '$ext',
  744. w = $w,
  745. h = $h,
  746. alt = '$alt',
  747. caption = '$caption',
  748. category = '$category',
  749. date = now(),
  750. author = '".doSlash($txp_user)."'
  751. ";
  752. if (empty($id)) {
  753. $rs = safe_insert('txp_image', $q);
  754. if ($rs) {
  755. $id = $GLOBALS['ID'] = $rs;
  756. }
  757. $update = false;
  758. } else {
  759. $id = assert_int($id);
  760. $rs = safe_update('txp_image', $q, "id = $id");
  761. $update = true;
  762. }
  763. if (!$rs) {
  764. return gTxt('image_save_error');
  765. }
  766. $newpath = IMPATH.$id.$ext;
  767. if (shift_uploaded_file($file, $newpath) == false) {
  768. if (!$update) {
  769. safe_delete('txp_image', "id = $id");
  770. }
  771. unset($GLOBALS['ID']);
  772. return $newpath.sp.gTxt('upload_dir_perms');
  773. }
  774. @chmod($newpath, 0644);
  775. // GD is supported
  776. if (check_gd($ext)) {
  777. // Auto-generate a thumbnail using the last settings
  778. if (get_pref('thumb_w') > 0 || get_pref('thumb_h') > 0) {
  779. $t = new txp_thumb($id);
  780. $t->crop = (bool) get_pref('thumb_crop');
  781. $t->hint = '0';
  782. $t->width = (int) get_pref('thumb_w');
  783. $t->height = (int) get_pref('thumb_h');
  784. $t->write();
  785. }
  786. }
  787. $message = gTxt('image_uploaded', array('{name}' => $name));
  788. update_lastmod();
  789. // call post-upload plugins with new image's $id
  790. callback_event('image_uploaded', $event, false, $id);
  791. return array($message, $id);
  792. }
  793. /**
  794. * Gets an image as an array.
  795. *
  796. * @param string $where SQL where clause
  797. * @return array|bool An image data, or FALSE on failure
  798. * @package Image
  799. * @example
  800. * if ($image = fileDownloadFetchInfo('id = 1'))
  801. * {
  802. * print_r($image);
  803. * }
  804. */
  805. function imageFetchInfo($where)
  806. {
  807. $rs = safe_row('*', 'txp_image', $where);
  808. if ($rs) {
  809. return image_format_info($rs);
  810. }
  811. return false;
  812. }
  813. /**
  814. * Formats image info.
  815. *
  816. * This function takes an image data array generated
  817. * by imageFetchInfo() and formats the contents.
  818. *
  819. * @param array $image The image
  820. * @return array
  821. * @see imageFetchInfo()
  822. * @access private
  823. * @package Image
  824. */
  825. function image_format_info($image)
  826. {
  827. if (($unix_ts = @strtotime($image['date'])) > 0) {
  828. $image['date'] = $unix_ts;
  829. }
  830. return $image;
  831. }
  832. /**
  833. * Formats link info.
  834. *
  835. * @param array $link The link to format
  836. * @return array Formatted link data
  837. * @access private
  838. * @package Link
  839. */
  840. function link_format_info($link)
  841. {
  842. if (($unix_ts = @strtotime($link['date'])) > 0) {
  843. $link['date'] = $unix_ts;
  844. }
  845. return $link;
  846. }
  847. /**
  848. * Gets a HTTP GET or POST parameter.
  849. *
  850. * This function internally handles and normalises MAGIC_QUOTES_GPC,
  851. * strips CRLF from GET parameters and removes NULL bytes.
  852. *
  853. * @param string $thing The parameter to get
  854. * @return string|array The value of $thing, or an empty string
  855. * @package Network
  856. * @example
  857. * if (gps('sky') == 'blue' && gps('roses') == 'red')
  858. * {
  859. * echo 'Roses are red, sky is blue.';
  860. * }
  861. */
  862. function gps($thing)
  863. {
  864. $out = '';
  865. if (isset($_GET[$thing])) {
  866. if (MAGIC_QUOTES_GPC) {
  867. $out = doStrip($_GET[$thing]);
  868. } else {
  869. $out = $_GET[$thing];
  870. }
  871. $out = doArray($out, 'deCRLF');
  872. } elseif (isset($_POST[$thing])) {
  873. if (MAGIC_QUOTES_GPC) {
  874. $out = doStrip($_POST[$thing]);
  875. } else {
  876. $out = $_POST[$thing];
  877. }
  878. }
  879. $out = doArray($out, 'deNull');
  880. return $out;
  881. }
  882. /**
  883. * Gets an array of HTTP GET or POST parameters.
  884. *
  885. * @param array $array The parameters to extract
  886. * @return array
  887. * @package Network
  888. * @example
  889. * extract(gpsa(array('sky', 'roses'));
  890. * if ($sky == 'blue' && $roses == 'red')
  891. * {
  892. * echo 'Roses are red, sky is blue.';
  893. * }
  894. */
  895. function gpsa($array)
  896. {
  897. if (is_array($array)) {
  898. $out = array();
  899. foreach ($array as $a) {
  900. $out[$a] = gps($a);
  901. }
  902. return $out;
  903. }
  904. return false;
  905. }
  906. /**
  907. * Gets a HTTP POST parameter.
  908. *
  909. * This function internally handles and normalises MAGIC_QUOTES_GPC,
  910. * and removes NULL bytes.
  911. *
  912. * @param string $thing The parameter to get
  913. * @return string|array The value of $thing, or an empty string
  914. * @package Network
  915. * @example
  916. * if (ps('sky') == 'blue' && ps('roses') == 'red')
  917. * {
  918. * echo 'Roses are red, sky is blue.';
  919. * }
  920. */
  921. function ps($thing)
  922. {
  923. $out = '';
  924. if (isset($_POST[$thing])) {
  925. if (MAGIC_QUOTES_GPC) {
  926. $out = doStrip($_POST[$thing]);
  927. } else {
  928. $out = $_POST[$thing];
  929. }
  930. }
  931. $out = doArray($out, 'deNull');
  932. return $out;
  933. }
  934. /**
  935. * Gets an array of HTTP POST parameters.
  936. *
  937. * @param array $array The parameters to extract
  938. * @return array
  939. * @package Network
  940. * @example
  941. * extract(psa(array('sky', 'roses'));
  942. * if ($sky == 'blue' && $roses == 'red')
  943. * {
  944. * echo 'Roses are red, sky is blue.';
  945. * }
  946. */
  947. function psa($array)
  948. {
  949. foreach ($array as $a) {
  950. $out[$a] = ps($a);
  951. }
  952. return $out;
  953. }
  954. /**
  955. * Gets an array of HTTP POST parameters and strips HTML and PHP tags from values.
  956. *
  957. * @param array $array The parameters to extract
  958. * @return array
  959. * @package Network
  960. */
  961. function psas($array)
  962. {
  963. foreach ($array as $a) {
  964. $out[$a] = doStripTags(ps($a));
  965. }
  966. return $out;
  967. }
  968. /**
  969. * Gets all received HTTP POST parameters.
  970. *
  971. * @return array
  972. * @package Network
  973. */
  974. function stripPost()
  975. {
  976. if (isset($_POST)) {
  977. if (MAGIC_QUOTES_GPC) {
  978. return doStrip($_POST);
  979. } else {
  980. return $_POST;
  981. }
  982. }
  983. return '';
  984. }
  985. /**
  986. * Gets a variable from $_SERVER global array.
  987. *
  988. * @param mixed $thing The variable
  989. * @return mixed The variable, or an empty string on error
  990. * @package System
  991. * @example
  992. * echo serverSet('HTTP_USER_AGENT');
  993. */
  994. function serverSet($thing)
  995. {
  996. return (isset($_SERVER[$thing])) ? $_SERVER[$thing] : '';
  997. }
  998. /**
  999. * Gets the client's IP address.
  1000. *
  1001. * This function supports proxies and uses 'X_FORWARDED_FOR'
  1002. * HTTP header if deemed necessary.
  1003. *
  1004. * @return string
  1005. * @package Network
  1006. * @example
  1007. * if ($ip = remote_addr())
  1008. * {
  1009. * echo "Your IP address is: {$ip}.";
  1010. * }
  1011. */
  1012. function remote_addr()
  1013. {
  1014. $ip = serverSet('REMOTE_ADDR');
  1015. if (($ip == '127.0.0.1' || $ip == '::1' || $ip == '::ffff:127.0.0.1' || $ip == serverSet('SERVER_ADDR')) && serverSet('HTTP_X_FORWARDED_FOR')) {
  1016. $ips = explode(', ', serverSet('HTTP_X_FORWARDED_FOR'));
  1017. $ip = $ips[0];
  1018. }
  1019. return $ip;
  1020. }
  1021. /**
  1022. * Gets a variable from HTTP POST or a prefixed cookie.
  1023. *
  1024. * This function gets either a HTTP cookie of the given
  1025. * name prefixed with 'txp_', or a HTTP POST parameter
  1026. * without a prefix.
  1027. *
  1028. * @param string $thing The variable
  1029. * @return array|string The variable or an empty string
  1030. * @package Network
  1031. * @example
  1032. * if ($cs = psc('myVariable'))
  1033. * {
  1034. * echo "'txp_myVariable' cookie or 'myVariable' POST parameter contained: '{$cs}'.";
  1035. * }
  1036. */
  1037. function pcs($thing)
  1038. {
  1039. if (isset($_COOKIE["txp_".$thing])) {
  1040. if (MAGIC_QUOTES_GPC) {
  1041. return doStrip($_COOKIE["txp_".$thing]);
  1042. } else {
  1043. return $_COOKIE["txp_".$thing];
  1044. }
  1045. } elseif (isset($_POST[$thing])) {
  1046. if (MAGIC_QUOTES_GPC) {
  1047. return doStrip($_POST[$thing]);
  1048. } else {
  1049. return $_POST[$thing];
  1050. }
  1051. }
  1052. return '';
  1053. }
  1054. /**
  1055. * Gets a HTTP cookie.
  1056. *
  1057. * This function internally normalises MAGIC_QUOTES_GPC.
  1058. *
  1059. * @param string $thing The cookie
  1060. * @return string The cookie or an empty string
  1061. * @package Network
  1062. * @example
  1063. * if ($cs = cs('myVariable'))
  1064. * {
  1065. * echo "'myVariable' cookie contained: '{$cs}'.";
  1066. * }
  1067. */
  1068. function cs($thing)
  1069. {
  1070. if (isset($_COOKIE[$thing])) {
  1071. if (MAGIC_QUOTES_GPC) {
  1072. return doStrip($_COOKIE[$thing]);
  1073. } else {
  1074. return $_COOKIE[$thing];
  1075. }
  1076. }
  1077. return '';
  1078. }
  1079. /**
  1080. * Converts a boolean to a localised "Yes" or "No" string.
  1081. *
  1082. * @param bool $status The boolean. Ignores type and as such can also take a string or an integer
  1083. * @return string No if FALSE, Yes otherwise
  1084. * @package L10n
  1085. * @example
  1086. * echo yes_no(3 * 3 === 2);
  1087. */
  1088. function yes_no($status)
  1089. {
  1090. return ($status) ? gTxt('yes') : gTxt('no');
  1091. }
  1092. /**
  1093. * Gets UNIX timestamp with microseconds.
  1094. *
  1095. * @return float
  1096. * @package DateTime
  1097. * @example
  1098. * echo getmicrotime();
  1099. */
  1100. function getmicrotime()
  1101. {
  1102. list($usec, $sec) = explode(" ", microtime());
  1103. return ((float) $usec + (float) $sec);
  1104. }
  1105. /**
  1106. * Loads the given plugin or checks if it was loaded.
  1107. *
  1108. * @param string $name The plugin
  1109. * @param bool $force If TRUE loads the plugin even if it's disabled
  1110. * @return bool TRUE if the plugin is loaded
  1111. * @example
  1112. * if (load_plugin('abc_plugin'))
  1113. * {
  1114. * echo "'abc_plugin' is active.";
  1115. * }
  1116. */
  1117. function load_plugin($name, $force = false)
  1118. {
  1119. global $plugins, $plugins_ver, $prefs, $txp_current_plugin;
  1120. if (is_array($plugins) and in_array($name, $plugins)) {
  1121. return true;
  1122. }
  1123. if (!empty($prefs['plugin_cache_dir'])) {
  1124. $dir = rtrim($prefs['plugin_cache_dir'], '/') . '/';
  1125. // In case it's a relative path.
  1126. if (!is_dir($dir)) {
  1127. $dir = rtrim(realpath(txpath.'/'.$dir), '/') . '/';
  1128. }
  1129. if (is_file($dir . $name . '.php')) {
  1130. $plugins[] = $name;
  1131. set_error_handler("pluginErrorHandler");
  1132. if (isset($txp_current_plugin)) {
  1133. $txp_parent_plugin = $txp_current_plugin;
  1134. }
  1135. $txp_current_plugin = $name;
  1136. include($dir . $name . '.php');
  1137. $txp_current_plugin = isset($txp_parent_plugin) ? $txp_parent_plugin : null;
  1138. $plugins_ver[$name] = @$plugin['version'];
  1139. restore_error_handler();
  1140. return true;
  1141. }
  1142. }
  1143. $rs = safe_row("name, code, version", "txp_plugin", ($force ? '' : 'status = 1 AND '). "name='".doSlash($name)."'");
  1144. if ($rs) {
  1145. $plugins[] = $rs['name'];
  1146. $plugins_ver[$rs['name']] = $rs['version'];
  1147. set_error_handler("pluginErrorHandler");
  1148. if (isset($txp_current_plugin)) {
  1149. $txp_parent_plugin = $txp_current_plugin;
  1150. }
  1151. $txp_current_plugin = $rs['name'];
  1152. eval($rs['code']);
  1153. $txp_current_plugin = isset($txp_parent_plugin) ? $txp_parent_plugin : null;
  1154. restore_error_handler();
  1155. return true;
  1156. }
  1157. return false;
  1158. }
  1159. /**
  1160. * Loads a plugin.
  1161. *
  1162. * Identical to load_plugin() except upon failure it issues an E_USER_ERROR.
  1163. *
  1164. * @param string $name The plugin
  1165. * @return bool
  1166. * @see load_plugin()
  1167. */
  1168. function require_plugin($name)
  1169. {
  1170. if (!load_plugin($name)) {
  1171. trigger_error("Unable to include required plugin \"{$name}\"",E_USER_ERROR);
  1172. return false;
  1173. }
  1174. return true;
  1175. }
  1176. /**
  1177. * Loads a plugin.
  1178. *
  1179. * Identical to load_plugin() except upon failure it issues an E_USER_WARNING.
  1180. *
  1181. * @param string $name The plugin
  1182. * @return bool
  1183. * @see load_plugin()
  1184. */
  1185. function include_plugin($name)
  1186. {
  1187. if (!load_plugin($name)) {
  1188. trigger_error("Unable to include plugin \"{$name}\"",E_USER_WARNING);
  1189. return false;
  1190. }
  1191. return true;
  1192. }
  1193. /**
  1194. * Error handler for plugins.
  1195. *
  1196. * @param int $errno
  1197. * @param string $errstr
  1198. * @param string $errfile
  1199. * @param int $errline
  1200. * @access private
  1201. * @package Debug
  1202. */
  1203. function pluginErrorHandler($errno, $errstr, $errfile, $errline)
  1204. {
  1205. global $production_status, $txp_current_plugin;
  1206. $error = array();
  1207. if ($production_status == 'testing') {
  1208. $error = array(
  1209. E_WARNING => 'Warning',
  1210. E_RECOVERABLE_ERROR => 'Catchable fatal error',
  1211. E_USER_ERROR => 'User_Error',
  1212. E_USER_WARNING => 'User_Warning',
  1213. );
  1214. } elseif ($production_status == 'debug') {
  1215. $error = array(
  1216. E_WARNING => 'Warning',
  1217. E_NOTICE => 'Notice',
  1218. E_RECOVERABLE_ERROR => 'Catchable fatal error',
  1219. E_USER_ERROR => 'User_Error',
  1220. E_USER_WARNING => 'User_Warning',
  1221. E_USER_NOTICE => 'User_Notice',
  1222. );
  1223. if (!isset($error[$errno])) {
  1224. $error[$errno] = $errno;
  1225. }
  1226. }
  1227. if (!isset($error[$errno]) || !error_reporting()) {
  1228. return;
  1229. }
  1230. printf('<pre dir="auto">'.gTxt('plugin_load_error').' <b>%s</b> -> <b>%s: %s on line %s</b></pre>',
  1231. $txp_current_plugin, $error[$errno], $errstr, $errline);
  1232. if ($production_status == 'debug') {
  1233. print "\n<pre class=\"backtrace\" dir=\"ltr\"><code>".txpspecialchars(join("\n", get_caller(10)))."</code></pre>";
  1234. }
  1235. }
  1236. /**
  1237. * Error handler for page templates.
  1238. *
  1239. * @param int $errno
  1240. * @param string $errstr
  1241. * @param string $errfile
  1242. * @param int $errline
  1243. * @access private
  1244. * @package Debug
  1245. */
  1246. function tagErrorHandler($errno, $errstr, $errfile, $errline)
  1247. {
  1248. global $production_status, $txp_current_tag, $txp_current_form, $pretext;
  1249. $error = array();
  1250. if ($production_status == 'testing') {
  1251. $error = array(
  1252. E_WARNING => 'Warning',
  1253. E_RECOVERABLE_ERROR => 'Textpattern Catchable fatal error',
  1254. E_USER_ERROR => 'Textpattern Error',
  1255. E_USER_WARNING => 'Textpattern Warning',
  1256. );
  1257. } elseif ($production_status == 'debug') {
  1258. $error = array(
  1259. E_WARNING => 'Warning',
  1260. E_NOTICE => 'Notice',
  1261. E_RECOVERABLE_ERROR => 'Textpattern Catchable fatal error',
  1262. E_USER_ERROR => 'Textpattern Error',
  1263. E_USER_WARNING => 'Textpattern Warning',
  1264. E_USER_NOTICE => 'Textpattern Notice',
  1265. );
  1266. if (!isset($error[$errno])) {
  1267. $error[$errno] = $errno;
  1268. }
  1269. }
  1270. if (!isset($error[$errno]) || !error_reporting()) {
  1271. return;
  1272. }
  1273. if (empty($pretext['page'])) {
  1274. $page = gTxt('none');
  1275. } else {
  1276. $page = $pretext['page'];
  1277. }
  1278. if (!isset($txp_current_form)) {
  1279. $txp_current_form = gTxt('none');
  1280. }
  1281. $locus = gTxt('while_parsing_page_form', array(
  1282. '{page}' => $page,
  1283. '{form}' => $txp_current_form,
  1284. ));
  1285. printf("<pre dir=\"auto\">".gTxt('tag_error').' <b>%s</b> -> <b> %s: %s %s</b></pre>',
  1286. txpspecialchars($txp_current_tag), $error[$errno], $errstr, $locus);
  1287. if ($production_status == 'debug') {
  1288. print "\n<pre class=\"backtrace\" dir=\"ltr\"><code>".txpspecialchars(join("\n", get_caller(10)))."</code></pre>";
  1289. $trace_msg = gTxt('tag_error').' '.$txp_current_tag.' -> '.$error[$errno].': '.$errstr.' '.$locus;
  1290. trace_add($trace_msg);
  1291. }
  1292. }
  1293. /**
  1294. * Error handler for XML feeds.
  1295. *
  1296. * @param int $errno
  1297. * @param string $errstr
  1298. * @param string $errfile
  1299. * @param int $errline
  1300. * @access private
  1301. * @package Debug
  1302. */
  1303. function feedErrorHandler($errno, $errstr, $errfile, $errline)
  1304. {
  1305. global $production_status;
  1306. if ($production_status != 'debug') {
  1307. return;
  1308. }
  1309. return tagErrorHandler($errno, $errstr, $errfile, $errline);
  1310. }
  1311. /**
  1312. * Error handler for admin-side pages.
  1313. *
  1314. * @param int $errno
  1315. * @param string $errstr
  1316. * @param string $errfile
  1317. * @param int $errline
  1318. * @access private
  1319. * @package Debug
  1320. */
  1321. function adminErrorHandler($errno, $errstr, $errfile, $errline)
  1322. {
  1323. global $production_status, $theme, $event, $step;
  1324. $error = array();
  1325. if ($production_status == 'testing') {
  1326. $error = array(
  1327. E_WARNING => 'Warning',
  1328. E_RECOVERABLE_ERROR => 'Catchable fatal error',
  1329. E_USER_ERROR => 'User_Error',
  1330. E_USER_WARNING => 'User_Warning',
  1331. );
  1332. } elseif ($production_status == 'debug') {
  1333. $error = array(
  1334. E_WARNING => 'Warning',
  1335. E_NOTICE => 'Notice',
  1336. E_RECOVERABLE_ERROR => 'Catchable fatal error',
  1337. E_USER_ERROR => 'User_Error',
  1338. E_USER_WARNING => 'User_Warning',
  1339. E_USER_NOTICE => 'User_Notice',
  1340. );
  1341. if (!isset($error[$errno])) {
  1342. $error[$errno] = $errno;
  1343. }
  1344. }
  1345. if (!isset($error[$errno]) || !error_reporting()) {
  1346. return;
  1347. }
  1348. // When even a minimum environment is missing.
  1349. if (!isset($production_status)) {
  1350. echo '<pre dir="auto">'.gTxt('internal_error').' "'.$errstr.'"'.n."in $errfile at line $errline".'</pre>';
  1351. return;
  1352. }
  1353. $backtrace = '';
  1354. if (has_privs('debug.verbose')) {
  1355. $msg = $error[$errno].' "'.$errstr.'"';
  1356. } else {
  1357. $msg = gTxt('internal_error');
  1358. }
  1359. if ($production_status == 'debug' && has_privs('debug.backtrace')) {
  1360. $msg .= n."in $errfile at line $errline";
  1361. $backtrace = join(n, get_caller(5, 1));
  1362. }
  1363. if ($errno == E_ERROR || $errno == E_USER_ERROR) {
  1364. $httpstatus = 500;
  1365. } else {
  1366. $httpstatus = 200;
  1367. }
  1368. $out = "$msg.\n$backtrace";
  1369. if (http_accept_format('html')) {
  1370. if ($backtrace) {
  1371. echo "<pre dir=\"auto\">$msg.</pre>".
  1372. n.'<pre class="backtrace" dir="ltr"><code>'.
  1373. txpspecialchars($backtrace).'</code></pre>';
  1374. } elseif (is_object($theme)) {
  1375. echo $theme->announce(array($out, E_ERROR), true);
  1376. } else {
  1377. echo "<pre dir=\"auto\">$out</pre>";
  1378. }
  1379. } elseif (http_accept_format('js')) {
  1380. if (is_object($theme)) {
  1381. send_script_response($theme->announce_async(array($out, E_ERROR), true));
  1382. } else {
  1383. send_script_response('/* '. $out . '*/');
  1384. }
  1385. } elseif (http_accept_format('xml')) {
  1386. send_xml_response(array(
  1387. 'http-status' => $httpstatus,
  1388. 'internal_error' => "$out",
  1389. ));
  1390. } else {
  1391. txp_die($msg, 500);
  1392. }
  1393. }
  1394. /**
  1395. * Error handler for public-side.
  1396. *
  1397. * @param int $errno
  1398. * @param string $errstr
  1399. * @param string $errfile
  1400. * @param int $errline
  1401. * @access private
  1402. * @package Debug
  1403. */
  1404. function publicErrorHandler($errno, $errstr, $errfile, $errline)
  1405. {
  1406. global $production_status;
  1407. $error = array();
  1408. if ($production_status == 'testing') {
  1409. $error = array(
  1410. E_WARNING => 'Warning',
  1411. E_USER_ERROR => 'Textpattern Error',
  1412. E_USER_WARNING => 'Textpattern Warning',
  1413. );
  1414. } elseif ($production_status == 'debug') {
  1415. $error = array(
  1416. E_WARNING => 'Warning',
  1417. E_NOTICE => 'Notice',
  1418. E_USER_ERROR => 'Textpattern Error',
  1419. E_USER_WARNING => 'Textpattern Warning',
  1420. E_USER_NOTICE => 'Textpattern Notice',
  1421. );
  1422. if (!isset($error[$errno])) {
  1423. $error[$errno] = $errno;
  1424. }
  1425. }
  1426. if (!isset($error[$errno]) || !error_reporting()) {
  1427. return;
  1428. }
  1429. printf ("<pre dir=\"auto\">".gTxt('general_error').' <b>%s: %s on line %s</b></pre>',
  1430. $error[$errno], $errstr, $errline);
  1431. if ($production_status == 'debug') {
  1432. print "\n<pre class=\"backtrace\" dir=\"ltr\"><code>".txpspecialchars(join("\n", get_caller(10)))."</code></pre>";
  1433. }
  1434. }
  1435. /**
  1436. * Loads plugins.
  1437. *
  1438. * @param bool $type If TRUE loads admin-side plugins, otherwise public
  1439. */
  1440. function load_plugins($type = false)
  1441. {
  1442. global $prefs, $plugins, $plugins_ver, $app_mode;
  1443. if (!is_array($plugins)) {
  1444. $plugins = array();
  1445. }
  1446. if (!empty($prefs['plugin_cache_dir'])) {
  1447. $dir = rtrim($prefs['plugin_cache_dir'], '/') . '/';
  1448. // In case it's a relative path.
  1449. if (!is_dir($dir)) {
  1450. $dir = rtrim(realpath(txpath.'/'.$dir), '/') . '/';
  1451. }
  1452. $files = glob($dir.'*.php');
  1453. if ($files) {
  1454. natsort($files);
  1455. foreach ($files as $f) {
  1456. trace_add("[Loading plugin from cache dir '$f']");
  1457. load_plugin(basename($f, '.php'));
  1458. }
  1459. }
  1460. }
  1461. $admin = ($app_mode == 'async' ? '4,5' : '1,3,4,5');
  1462. $where = 'status = 1 AND type IN ('.($type ? $admin : '0,1,5').')';
  1463. $rs = safe_rows("name, code, version", "txp_plugin", $where.' order by load_order asc, name asc');
  1464. if ($rs) {
  1465. $old_error_handler = set_error_handler("pluginErrorHandler");
  1466. foreach ($rs as $a) {
  1467. if (!in_array($a['name'], $plugins)) {
  1468. $plugins[] = $a['name'];
  1469. $plugins_ver[$a['name']] = $a['version'];
  1470. $GLOBALS['txp_current_plugin'] = $a['name'];
  1471. trace_add("[Loading plugin '{$a['name']}' version '{$a['version']}']");
  1472. $eval_ok = eval($a['code']);
  1473. if ($eval_ok === false) {
  1474. echo gTxt('plugin_load_error_above').strong($a['name']).n.br;
  1475. }
  1476. unset($GLOBALS['txp_current_plugin']);
  1477. }
  1478. }
  1479. restore_error_handler();
  1480. }
  1481. }
  1482. /**
  1483. * Attachs a handler to a callback event.
  1484. *
  1485. * @param callback $func The callback function
  1486. * @param string $event The callback event
  1487. * @param string $step The callback step
  1488. * @param bool $pre Before or after. Works only with selected callback events
  1489. * @package Callback
  1490. * @example
  1491. * register_callback('my_callback_function', 'article.updated');
  1492. * function my_callback_function($event)
  1493. * {
  1494. * return "'$event' fired.";
  1495. * }
  1496. */
  1497. function register_callback($func, $event, $step = '', $pre = 0)
  1498. {
  1499. global $plugin_callback;
  1500. $plugin_callback[] = array(
  1501. 'function' => $func,
  1502. 'event' => $event,
  1503. 'step' => $step,
  1504. 'pre' => $pre,
  1505. );
  1506. }
  1507. /**
  1508. * Registers an admin-side extension page.
  1509. *
  1510. * For now this just does the same as register_callback().
  1511. *
  1512. * @param callback $func The callback function
  1513. * @param string $event The callback event
  1514. * @param string $step The callback step
  1515. * @param bool $top The top or the bottom of the page
  1516. * @access private
  1517. * @see register_callback()
  1518. * @package Callback
  1519. */
  1520. function register_page_extension($func, $event, $step = '', $top = 0)
  1521. {
  1522. register_callback($func, $event, $step, $top);
  1523. }
  1524. /**
  1525. * Call an event's callback.
  1526. *
  1527. * This function executes all callback handlers attached to the
  1528. * matched event and step.
  1529. *
  1530. * When this function is called, any event handlers attached with
  1531. * register_callback() to the matching event, step and pre will be called.
  1532. * The handlers, callback functions, will be executed in the same order they
  1533. * were registered.
  1534. *
  1535. * Any extra arguments will be passed to the callback handlers in the same
  1536. * argument position. This allows passing any type of data to the attached
  1537. * handlers. Callback handlers will also receive the event and the step.
  1538. *
  1539. * This function returns a compined value of all values returned by the callback
  1540. * handlers.
  1541. *
  1542. * @param string $event The callback event
  1543. * @param string $step Additional callback step
  1544. * @param bool $pre Allows two callbacks, a prepending and an appending, with same event and step
  1545. * @return mixed The value returned by the attached callback functions, or an empty string
  1546. * @package Callback
  1547. * @see register_callback()
  1548. * @example
  1549. * register_callback('my_callback_function', 'my_custom_event');
  1550. * function my_callback_function($event, $step, $extra)
  1551. * {
  1552. * return "Passed '$extra' on '$event'.";
  1553. * }
  1554. * echo callback_event('my_custom_event', '', 0, 'myExtraValue');
  1555. */
  1556. function callback_event($event, $step = '', $pre = 0)
  1557. {
  1558. global $plugin_callback, $production_status;
  1559. if (!is_array($plugin_callback)) {
  1560. return '';
  1561. }
  1562. // Any payload parameters?
  1563. $argv = func_get_args();
  1564. $argv = (count($argv) > 3) ? array_slice($argv, 3) : array();
  1565. foreach ($plugin_callback as $c) {
  1566. if ($c['event'] == $event && (empty($c['step']) || $c['step'] == $step) && $c['pre'] == $pre) {
  1567. if (is_callable($c['function'])) {
  1568. $return_value = call_user_func_array($c['function'], array('event' => $event, 'step' => $step) + $argv);
  1569. if (isset($out)) {
  1570. if (is_array($return_value) && is_array($out)) {
  1571. $out = array_merge($out, $return_value);
  1572. } elseif (is_bool($return_value) && is_bool($out)) {
  1573. $out = $return_value && $out;
  1574. } else {
  1575. $out .= $return_value;
  1576. }
  1577. } else {
  1578. $out = $return_value;
  1579. }
  1580. } elseif ($production_status == 'debug') {
  1581. trigger_error(gTxt('unknown_callback_function', array('{function}' => callback_tostring($c['function']))), E_USER_WARNING);
  1582. }
  1583. }
  1584. }
  1585. if (isset($out)) {
  1586. return $out;
  1587. }
  1588. return '';
  1589. }
  1590. /**
  1591. * Call an event's callback with two optional byref parameters.
  1592. *
  1593. * @param string $event The callback event
  1594. * @param string $step Optional callback step
  1595. * @param bool $pre Allows two callbacks, a prepending and an appending, with same event and step
  1596. * @param mixed $data Optional arguments for event handlers
  1597. * @param mixed $options Optional arguments for event handlers
  1598. * @return array Collection of return values from event handlers
  1599. * @since 4.5.0
  1600. * @package Callback
  1601. */
  1602. function callback_event_ref($event, $step = '', $pre = 0, &$data = null, &$options = null)
  1603. {
  1604. global $plugin_callback, $production_status;
  1605. if (!is_array($plugin_callback)) {
  1606. return array();
  1607. }
  1608. $return_value = array();
  1609. foreach ($plugin_callback as $c) {
  1610. if ($c['event'] == $event and (empty($c['step']) or $c['step'] == $step) and $c['pre'] == $pre) {
  1611. if (is_callable($c['function'])) {
  1612. // Cannot call event handler via call_user_func() as this would dereference all arguments.
  1613. // Side effect: callback handler *must* be ordinary function, *must not* be class method in PHP <5.4
  1614. // see https://bugs.php.net/bug.php?id=47160
  1615. $return_value[] = $c['function']($event, $step, $data, $options);
  1616. } elseif ($production_status == 'debug') {
  1617. trigger_error(gTxt('unknown_callback_function', array('{function}' => callback_tostring($c['function']))), E_USER_WARNING);
  1618. }
  1619. }
  1620. }
  1621. return $return_value;
  1622. }
  1623. /**
  1624. * Converts a callable to a string presentation.
  1625. *
  1626. * <code>
  1627. * echo callback_tostring(array('class', 'method'));
  1628. * </code>
  1629. *
  1630. * @param callback $callback The callback
  1631. * @return string The $callback as a human-readable string
  1632. * @since 4.5.0
  1633. * @package Callback
  1634. * @deprecated in 4.6.0
  1635. * @see Textpattern_Type_Callable::toString()
  1636. */
  1637. function callback_tostring($callback)
  1638. {
  1639. return Txp::get('Textpattern_Type_Callable', $callback)->toString();
  1640. }
  1641. /**
  1642. * Checks if a callback event has active handlers.
  1643. *
  1644. * @param string $event The callback event
  1645. * @param string $step The callback step
  1646. * @param bool $pre The position
  1647. * @return bool TRUE if the event is active, FALSE otherwise
  1648. * @since 4.6.0
  1649. * @package Callback
  1650. * @example
  1651. * if (has_handler('article_saved'))
  1652. * {
  1653. * echo "There are active handlers for 'article_saved' event.";
  1654. * }
  1655. */
  1656. function has_handler($event, $step = '', $pre = 0)
  1657. {
  1658. return (bool) callback_handlers($event, $step, $pre, false);
  1659. }
  1660. /**
  1661. * Lists handlers attached to an event.
  1662. *
  1663. * @param string $event The callback event
  1664. * @param string $step The callback step
  1665. * @param bool $pre The position
  1666. * @param bool $as_string Return callables in string representation
  1667. * @return array|bool An array of handlers, or FALSE
  1668. * @since 4.6.0
  1669. * @package Callback
  1670. * @example
  1671. * if ($handlers = callback_handlers('article_saved'))
  1672. * {
  1673. * print_r($handlers);
  1674. * }
  1675. */
  1676. function callback_handlers($event, $step = '', $pre = 0, $as_string = true)
  1677. {
  1678. global $plugin_callback;
  1679. $out = array();
  1680. foreach ((array) $plugin_callback as $c) {
  1681. if ($c['event'] == $event && (!$c['step'] || $c['step'] == $step) && $c['pre'] == $pre) {
  1682. if ($as_string) {
  1683. $out[] = callback_tostring($c['function']);
  1684. } else {
  1685. $out[] = $c['function'];
  1686. }
  1687. }
  1688. }
  1689. if ($out) {
  1690. return $out;
  1691. }
  1692. return false;
  1693. }
  1694. /**
  1695. * Registers a new admin-side panel and adds a navigation link to the menu.
  1696. *
  1697. * @param string $area The menu the panel appears in, e.g. "home", "content", "presentation", "admin", "extensions"
  1698. * @param string $panel The panel's event
  1699. * @param st…

Large files files are truncated, but you can click here to view the full file