PageRenderTime 56ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/system/expressionengine/libraries/Functions.php

https://bitbucket.org/tdevonshire/hoolux
PHP | 2996 lines | 1909 code | 430 blank | 657 comment | 380 complexity | cd7db8c4a9364fdf434e3b3da1c42de8 MD5 | raw file
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * ExpressionEngine - by EllisLab
  4. *
  5. * @package ExpressionEngine
  6. * @author EllisLab Dev Team
  7. * @copyright Copyright (c) 2003 - 2012, EllisLab, Inc.
  8. * @license http://ellislab.com/expressionengine/user-guide/license.html
  9. * @link http://ellislab.com
  10. * @since Version 2.0
  11. * @filesource
  12. */
  13. // ------------------------------------------------------------------------
  14. /**
  15. * ExpressionEngine Core Functions Class
  16. *
  17. * @package ExpressionEngine
  18. * @subpackage Core
  19. * @category Core
  20. * @author EllisLab Dev Team
  21. * @link http://ellislab.com
  22. */
  23. class EE_Functions {
  24. var $seed = FALSE; // Whether we've seeded our rand() function. We only seed once per script execution
  25. var $cached_url = array();
  26. var $cached_path = array();
  27. var $cached_index = array();
  28. var $cached_captcha = '';
  29. var $template_map = array();
  30. var $template_type = '';
  31. var $action_ids = array();
  32. var $file_paths = array();
  33. var $conditional_debug = FALSE;
  34. var $catfields = array();
  35. /**
  36. * Constructor
  37. */
  38. function __construct()
  39. {
  40. // Make a local reference to the ExpressionEngine super object
  41. $this->EE =& get_instance();
  42. }
  43. // --------------------------------------------------------------------
  44. /**
  45. * Fetch base site index
  46. *
  47. * @access public
  48. * @param bool
  49. * @param bool
  50. * @return string
  51. */
  52. function fetch_site_index($add_slash = FALSE, $sess_id = TRUE)
  53. {
  54. if (isset($this->cached_index[$add_slash.$sess_id.$this->template_type]))
  55. {
  56. return $this->cached_index[$add_slash.$sess_id.$this->template_type];
  57. }
  58. $url = $this->EE->config->slash_item('site_url');
  59. $url .= $this->EE->config->item('site_index');
  60. if ($this->EE->config->item('force_query_string') == 'y')
  61. {
  62. $url .= '?';
  63. }
  64. if (is_object($this->EE->session) && $this->EE->session->userdata('session_id') != '' && REQ != 'CP' && $sess_id == TRUE &&
  65. $this->EE->config->item('user_session_type') != 'c' && $this->template_type == 'webpage')
  66. {
  67. $url .= "/S=".$this->EE->session->userdata('session_id')."/";
  68. }
  69. if ($add_slash == TRUE)
  70. {
  71. if (substr($url, -1) != '/')
  72. {
  73. $url .= "/";
  74. }
  75. }
  76. $this->cached_index[$add_slash.$sess_id.$this->template_type] = $url;
  77. return $url;
  78. }
  79. // --------------------------------------------------------------------
  80. /**
  81. * Create a custom URL
  82. *
  83. * The input to this function is parsed and added to the
  84. * full site URL to create a full URL/URI
  85. *
  86. * @access public
  87. * @param string
  88. * @param bool
  89. * @return string
  90. */
  91. function create_url($segment, $sess_id = TRUE)
  92. {
  93. // Since this function can be used via a callback
  94. // we'll fetch the segment if it's an array
  95. if (is_array($segment))
  96. {
  97. $segment = $segment[1];
  98. }
  99. if (isset($this->cached_url[$segment]))
  100. {
  101. return $this->cached_url[$segment];
  102. }
  103. $full_segment = $segment;
  104. $segment = str_replace(array("'", '"'), '', $segment);
  105. $segment = preg_replace("/(.+?(\/))index(\/)(.*?)/", "\\1\\2", $segment);
  106. $segment = preg_replace("/(.+?(\/))index$/", "\\1", $segment);
  107. // These are exceptions to the normal path rules
  108. if ($segment == '' OR strtolower($segment) == 'site_index')
  109. {
  110. return $this->fetch_site_index();
  111. }
  112. if (strtolower($segment) == 'logout')
  113. {
  114. $qs = ($this->EE->config->item('force_query_string') == 'y') ? '' : '?';
  115. return $this->fetch_site_index(0, 0).$qs.'ACT='.$this->fetch_action_id('Member', 'member_logout');
  116. }
  117. // END Specials
  118. // Load the string helper
  119. $this->EE->load->helper('string');
  120. $base = $this->fetch_site_index(0, $sess_id).'/'.trim_slashes($segment);
  121. $out = $this->remove_double_slashes($base);
  122. $this->cached_url[$full_segment] = $out;
  123. return $out;
  124. }
  125. // --------------------------------------------------------------------
  126. /**
  127. * Creates a url for Pages links
  128. *
  129. * @access public
  130. * @return string
  131. */
  132. function create_page_url($base_url, $segment, $trailing_slash = FALSE)
  133. {
  134. // Load the string helper
  135. $this->EE->load->helper('string');
  136. if ($this->EE->config->item('force_query_string') == 'y')
  137. {
  138. if (strpos($base_url, $this->EE->config->item('index_page') . '/') !== FALSE)
  139. {
  140. $base_url = rtrim($base_url, '/');
  141. }
  142. $base_url .= '?';
  143. }
  144. $base = $base_url.'/'.trim_slashes($segment);
  145. if (substr($base, -1) != '/' && $trailing_slash == TRUE)
  146. {
  147. $base .= '/';
  148. }
  149. $out = $this->remove_double_slashes($base);
  150. return $out;
  151. }
  152. // --------------------------------------------------------------------
  153. /**
  154. * Fetch site index with URI query string
  155. *
  156. * @access public
  157. * @return string
  158. */
  159. function fetch_current_uri()
  160. {
  161. return rtrim($this->remove_double_slashes($this->fetch_site_index(1).$this->EE->uri->uri_string), '/');
  162. }
  163. // --------------------------------------------------------------------
  164. /**
  165. * Prep Query String
  166. *
  167. * This function checks to see if "Force Query Strings" is on.
  168. * If so it adds a question mark to the URL if needed
  169. *
  170. * @access public
  171. * @param string
  172. * @return string
  173. */
  174. function prep_query_string($str)
  175. {
  176. if (stristr($str, '.php') && substr($str, -7) == '/index/')
  177. {
  178. $str = substr($str, 0, -6);
  179. }
  180. if (strpos($str, '?') === FALSE && $this->EE->config->item('force_query_string') == 'y')
  181. {
  182. if (stristr($str, '.php'))
  183. {
  184. $str = preg_replace("#(.+?)\.php(.*?)#", "\\1.php?\\2", $str);
  185. }
  186. else
  187. {
  188. $str .= "?";
  189. }
  190. }
  191. return $str;
  192. }
  193. // --------------------------------------------------------------------
  194. /**
  195. * Convert EE Tags to Entities
  196. *
  197. * @access public
  198. * @param string
  199. * @param bool
  200. * @return string
  201. */
  202. function encode_ee_tags($str, $convert_curly = FALSE)
  203. {
  204. if ($str != '' && strpos($str, '{') !== FALSE)
  205. {
  206. if ($convert_curly === TRUE)
  207. {
  208. $str = str_replace(array('{', '}'), array('&#123;', '&#125;'), $str);
  209. }
  210. else
  211. {
  212. $str = preg_replace("/\{(\/){0,1}exp:(.+?)\}/", "&#123;\\1exp:\\2&#125;", $str);
  213. $str = str_replace(array('{exp:', '{/exp'), array('&#123;exp:', '&#123;\exp'), $str);
  214. $str = preg_replace("/\{embed=(.+?)\}/", "&#123;embed=\\1&#125;", $str);
  215. $str = preg_replace("/\{path:(.+?)\}/", "&#123;path:\\1&#125;", $str);
  216. $str = preg_replace("/\{redirect=(.+?)\}/", "&#123;redirect=\\1&#125;", $str);
  217. }
  218. }
  219. return $str;
  220. }
  221. // --------------------------------------------------------------------
  222. /**
  223. * Remove duplicate slashes from URL
  224. *
  225. * With all the URL/URI parsing/building, there is the potential
  226. * to end up with double slashes. This is a clean-up function.
  227. *
  228. * Will likely be deprecated in 2.6, use string helper instead
  229. *
  230. * @access public
  231. * @param string
  232. * @return string
  233. */
  234. function remove_double_slashes($str)
  235. {
  236. $this->EE->load->helper('string_helper');
  237. return reduce_double_slashes($str);
  238. }
  239. // --------------------------------------------------------------------
  240. /**
  241. * Extract path info
  242. *
  243. * We use this to extract the template group/template name
  244. * from path variables, like {some_var path="channel/index"}
  245. *
  246. * @access public
  247. * @param string
  248. * @return string
  249. */
  250. function extract_path($str)
  251. {
  252. if (preg_match("#=(.*)#", $str, $match))
  253. {
  254. $match[1] = trim($match[1], '}');
  255. if (isset($this->cached_path[$match[1]]))
  256. {
  257. return $this->cached_path[$match[1]];
  258. }
  259. // Load the string helper
  260. $this->EE->load->helper('string');
  261. $path = trim_slashes(str_replace(array("'",'"'), "", $match[1]));
  262. if (substr($path, -6) == 'index/')
  263. {
  264. $path = str_replace('/index', '', $path);
  265. }
  266. if (substr($path, -5) == 'index')
  267. {
  268. $path = str_replace('/index', '', $path);
  269. }
  270. $this->cached_path[$match[1]] = $path;
  271. return $path;
  272. }
  273. else
  274. {
  275. return 'SITE_INDEX';
  276. }
  277. }
  278. // --------------------------------------------------------------------
  279. /**
  280. * Replace variables
  281. *
  282. * @access public
  283. * @param string
  284. * @param string
  285. * @return string
  286. */
  287. function var_swap($str, $data)
  288. {
  289. if ( ! is_array($data))
  290. {
  291. return FALSE;
  292. }
  293. foreach ($data as $key => $val)
  294. {
  295. $str = str_replace('{'.$key.'}', $val, $str);
  296. }
  297. return $str;
  298. }
  299. // --------------------------------------------------------------------
  300. /**
  301. * Redirect
  302. *
  303. * @access public
  304. * @param string
  305. * @return void
  306. */
  307. function redirect($location, $method = FALSE)
  308. {
  309. // Remove hard line breaks and carriage returns
  310. $location = str_replace(array("\n", "\r"), '', $location);
  311. // Remove any and all line breaks
  312. while (stripos($location, '%0d') !== FALSE OR stripos($location, '%0a') !== FALSE)
  313. {
  314. $location = str_ireplace(array('%0d', '%0a'), '', $location);
  315. }
  316. $location = str_replace('&amp;', '&', $this->insert_action_ids($location));
  317. if (count($this->EE->session->flashdata))
  318. {
  319. // Ajax requests don't redirect - serve the flashdata
  320. if ($this->EE->input->is_ajax_request())
  321. {
  322. // We want the data that would be available for the next request
  323. $this->EE->session->_age_flashdata();
  324. $this->EE->load->library('javascript');
  325. die($this->EE->javascript->generate_json(
  326. $this->EE->session->flashdata));
  327. }
  328. }
  329. if ($method === FALSE)
  330. {
  331. $method = $this->EE->config->item('redirect_method');
  332. }
  333. switch($method)
  334. {
  335. case 'refresh' : header("Refresh: 0;url=$location");
  336. break;
  337. default : header("Location: $location");
  338. break;
  339. }
  340. exit;
  341. }
  342. // --------------------------------------------------------------------
  343. /**
  344. * Convert a string into an encrypted hash
  345. * DEPRECATED 2.0
  346. *
  347. * @access public
  348. * @param string
  349. * @return string
  350. */
  351. function hash($str)
  352. {
  353. $this->EE->load->library('logger');
  354. $this->EE->logger->deprecated('2.0', 'Security_helper::do_hash');
  355. $this->EE->load->helper('security');
  356. return do_hash($str);
  357. }
  358. // --------------------------------------------------------------------
  359. /**
  360. * Random number/password generator
  361. *
  362. * @access public
  363. * @param string
  364. * @param int
  365. * @return string
  366. */
  367. function random($type = 'encrypt', $len = 8)
  368. {
  369. $this->EE->load->helper('string');
  370. return random_string($type, $len);
  371. }
  372. // --------------------------------------------------------------------
  373. /**
  374. * Form declaration
  375. *
  376. * This function is used by modules when they need to create forms
  377. *
  378. * @access public
  379. * @param string
  380. * @return string
  381. */
  382. function form_declaration($data)
  383. {
  384. // Load the form helper
  385. $this->EE->load->helper('form');
  386. $deft = array(
  387. 'hidden_fields' => array(),
  388. 'action' => '',
  389. 'id' => '',
  390. 'class' => '',
  391. 'secure' => TRUE,
  392. 'enctype' => '',
  393. 'onsubmit' => '',
  394. );
  395. foreach ($deft as $key => $val)
  396. {
  397. if ( ! isset($data[$key]))
  398. {
  399. $data[$key] = $val;
  400. }
  401. }
  402. if (is_array($data['hidden_fields']) && ! isset($data['hidden_fields']['site_id']))
  403. {
  404. $data['hidden_fields']['site_id'] = $this->EE->config->item('site_id');
  405. }
  406. // Add the CSRF Protection Hash
  407. if ($this->EE->config->item('csrf_protection') == TRUE )
  408. {
  409. $data['hidden_fields'][$this->EE->security->get_csrf_token_name()] = $this->EE->security->get_csrf_hash();
  410. }
  411. // -------------------------------------------
  412. // 'form_declaration_modify_data' hook.
  413. // - Modify the $data parameters before they are processed
  414. // - Added EE 1.4.0
  415. //
  416. if ($this->EE->extensions->active_hook('form_declaration_modify_data') === TRUE)
  417. {
  418. $data = $this->EE->extensions->call('form_declaration_modify_data', $data);
  419. }
  420. //
  421. // -------------------------------------------
  422. // -------------------------------------------
  423. // 'form_declaration_return' hook.
  424. // - Take control of the form_declaration function
  425. // - Added EE 1.4.0
  426. //
  427. if ($this->EE->extensions->active_hook('form_declaration_return') === TRUE)
  428. {
  429. $form = $this->EE->extensions->call('form_declaration_return', $data);
  430. if ($this->EE->extensions->end_script === TRUE) return $form;
  431. }
  432. //
  433. // -------------------------------------------
  434. if ($data['action'] == '')
  435. {
  436. $data['action'] = $this->fetch_site_index();
  437. }
  438. if ($data['onsubmit'] != '')
  439. {
  440. $data['onsubmit'] = 'onsubmit="'.trim($data['onsubmit']).'"';
  441. }
  442. if (substr($data['action'], -1) == '?')
  443. {
  444. $data['action'] = substr($data['action'], 0, -1);
  445. }
  446. $data['name'] = (isset($data['name']) && $data['name'] != '') ? 'name="'.$data['name'].'" ' : '';
  447. $data['id'] = ($data['id'] != '') ? 'id="'.$data['id'].'" ' : '';
  448. $data['class'] = ($data['class'] != '') ? 'class="'.$data['class'].'" ' : '';
  449. if ($data['enctype'] == 'multi' OR strtolower($data['enctype']) == 'multipart/form-data')
  450. {
  451. $data['enctype'] = 'enctype="multipart/form-data" ';
  452. }
  453. $form = '<form '.$data['id'].$data['class'].$data['name'].'method="post" action="'.$data['action'].'" '.$data['onsubmit'].' '.$data['enctype'].">\n";
  454. if ($data['secure'] == TRUE)
  455. {
  456. if ($this->EE->config->item('secure_forms') == 'y')
  457. {
  458. if ( ! isset($data['hidden_fields']['XID']))
  459. {
  460. $data['hidden_fields'] = array_merge(array('XID' => '{XID_HASH}'), $data['hidden_fields']);
  461. }
  462. elseif ($data['hidden_fields']['XID'] == '')
  463. {
  464. $data['hidden_fields']['XID'] = '{XID_HASH}';
  465. }
  466. }
  467. }
  468. if (is_array($data['hidden_fields']))
  469. {
  470. $form .= "<div class='hiddenFields'>\n";
  471. foreach ($data['hidden_fields'] as $key => $val)
  472. {
  473. $form .= '<input type="hidden" name="'.$key.'" value="'.form_prep($val).'" />'."\n";
  474. }
  475. $form .= "</div>\n\n";
  476. }
  477. return $form;
  478. }
  479. // --------------------------------------------------------------------
  480. /**
  481. * Form backtrack
  482. *
  483. * This function lets us return a user to a previously
  484. * visited page after submitting a form. The page
  485. * is determined by the offset that the admin
  486. * places in each form
  487. *
  488. * @access public
  489. * @param string
  490. * @return string
  491. */
  492. function form_backtrack($offset = '')
  493. {
  494. $ret = $this->fetch_site_index();
  495. if ($offset != '')
  496. {
  497. if (isset($this->EE->session->tracker[$offset]))
  498. {
  499. if ($this->EE->session->tracker[$offset] != 'index')
  500. {
  501. return $this->remove_double_slashes($this->fetch_site_index().'/'.$this->EE->session->tracker[$offset]);
  502. }
  503. }
  504. }
  505. if (isset($_POST['RET']))
  506. {
  507. if (strncmp($_POST['RET'], '-', 1) == 0)
  508. {
  509. $return = str_replace("-", "", $_POST['RET']);
  510. if (isset($this->EE->session->tracker[$return]))
  511. {
  512. if ($this->EE->session->tracker[$return] != 'index')
  513. {
  514. $ret = $this->fetch_site_index().'/'.$this->EE->session->tracker[$return];
  515. }
  516. }
  517. }
  518. else
  519. {
  520. if (strpos($_POST['RET'], '/') !== FALSE)
  521. {
  522. if (strncasecmp($_POST['RET'], 'http://', 7) == 0 OR
  523. strncasecmp($_POST['RET'], 'https://', 8) == 0 OR
  524. strncasecmp($_POST['RET'], 'www.', 4) == 0)
  525. {
  526. $ret = $_POST['RET'];
  527. }
  528. else
  529. {
  530. $ret = $this->create_url($_POST['RET']);
  531. }
  532. }
  533. else
  534. {
  535. $ret = $_POST['RET'];
  536. }
  537. }
  538. // We need to slug in the session ID if the admin is running
  539. // their site using sessions only. Normally the $this->EE->functions->fetch_site_index()
  540. // function adds the session ID automatically, except in cases when the
  541. // $_POST['RET'] variable is set. Since the login routine relies on the RET
  542. // info to know where to redirect back to we need to sandwich in the session ID.
  543. if ($this->EE->config->item('user_session_type') != 'c')
  544. {
  545. if ($this->EE->session->userdata['session_id'] != '' && ! stristr($ret, $this->EE->session->userdata['session_id']))
  546. {
  547. $url = $this->EE->config->slash_item('site_url');
  548. $url .= $this->EE->config->item('site_index');
  549. if ($this->EE->config->item('force_query_string') == 'y')
  550. {
  551. $url .= '?';
  552. }
  553. $sess_id = "/S=".$this->EE->session->userdata['session_id']."/";
  554. $ret = str_replace($url, $url.$sess_id, $ret);
  555. }
  556. }
  557. }
  558. return $this->remove_double_slashes($ret);
  559. }
  560. // --------------------------------------------------------------------
  561. /**
  562. * eval()
  563. *
  564. * Evaluates a string as PHP
  565. *
  566. * @access public
  567. * @param string
  568. * @return mixed
  569. */
  570. function evaluate($str)
  571. {
  572. return eval('?'.'>'.$str.'<?php ');
  573. }
  574. // --------------------------------------------------------------------
  575. /**
  576. * Encode email from template callback
  577. *
  578. * @access public
  579. * @param string
  580. * @return string
  581. */
  582. function encode_email($str)
  583. {
  584. if (isset($this->EE->session->cache['functions']['emails'][$str]))
  585. {
  586. return preg_replace("/(eeEncEmail_)\w+/", '\\1'.$this->EE->functions->random('alpha', 10), $this->EE->session->cache['functions']['emails'][$str]);
  587. }
  588. $email = (is_array($str)) ? trim($str[1]) : trim($str);
  589. $title = '';
  590. $email = str_replace(array('"', "'"), '', $email);
  591. if ($p = strpos($email, "title="))
  592. {
  593. $title = substr($email, $p + 6);
  594. $email = trim(substr($email, 0, $p));
  595. }
  596. $this->EE->load->library('typography');
  597. $this->EE->typography->initialize();
  598. $encoded = $this->EE->typography->encode_email($email, $title, TRUE);
  599. $this->EE->session->cache['functions']['emails'][$str] = $encoded;
  600. return $encoded;
  601. }
  602. // --------------------------------------------------------------------
  603. /**
  604. * Delete spam prevention hashes
  605. *
  606. * @access public
  607. * @return void
  608. */
  609. function clear_spam_hashes()
  610. {
  611. if ($this->EE->config->item('secure_forms') == 'y')
  612. {
  613. $this->EE->security->garbage_collect_xids();
  614. }
  615. }
  616. // --------------------------------------------------------------------
  617. /**
  618. * Set Cookie
  619. *
  620. * @access public
  621. * @param string
  622. * @param string
  623. * @param string
  624. * @return void
  625. */
  626. function set_cookie($name = '', $value = '', $expire = '')
  627. {
  628. $data['name'] = $name;
  629. if ( ! is_numeric($expire))
  630. {
  631. $data['expire'] = time() - 86500;
  632. }
  633. else
  634. {
  635. if ($expire > 0)
  636. {
  637. $data['expire'] = time() + $expire;
  638. }
  639. else
  640. {
  641. $data['expire'] = 0;
  642. }
  643. }
  644. $data['prefix'] = ( ! $this->EE->config->item('cookie_prefix')) ? 'exp_' : $this->EE->config->item('cookie_prefix').'_';
  645. $data['path'] = ( ! $this->EE->config->item('cookie_path')) ? '/' : $this->EE->config->item('cookie_path');
  646. if (REQ == 'CP' && $this->EE->config->item('multiple_sites_enabled') == 'y')
  647. {
  648. $data['domain'] = $this->EE->config->cp_cookie_domain;
  649. }
  650. else
  651. {
  652. $data['domain'] = ( ! $this->EE->config->item('cookie_domain')) ? '' : $this->EE->config->item('cookie_domain');
  653. }
  654. $data['value'] = stripslashes($value);
  655. $data['secure_cookie'] = ($this->EE->config->item('cookie_secure') === TRUE) ? 1 : 0;
  656. if ($data['secure_cookie'])
  657. {
  658. $req = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : FALSE;
  659. if ( ! $req OR $req == 'off')
  660. {
  661. return FALSE;
  662. }
  663. }
  664. /* -------------------------------------------
  665. /* 'set_cookie_end' hook.
  666. /* - Take control of Cookie setting routine
  667. /* - Added EE 2.5.0
  668. */
  669. $this->EE->extensions->call('set_cookie_end', $data);
  670. if ($this->EE->extensions->end_script === TRUE) return;
  671. /*
  672. /* -------------------------------------------*/
  673. setcookie($data['prefix'].$data['name'], $data['value'], $data['expire'],
  674. $data['path'], $data['domain'], $data['secure_cookie']);
  675. }
  676. // --------------------------------------------------------------------
  677. /**
  678. * Character limiter
  679. *
  680. * @access public
  681. * @param string
  682. * @return string
  683. */
  684. function char_limiter($str, $num = 500)
  685. {
  686. if (strlen($str) < $num)
  687. {
  688. return $str;
  689. }
  690. $str = str_replace("\n", " ", $str);
  691. $str = preg_replace("/\s+/", " ", $str);
  692. if (strlen($str) <= $num)
  693. {
  694. return $str;
  695. }
  696. $str = trim($str);
  697. $out = "";
  698. foreach (explode(" ", trim($str)) as $val)
  699. {
  700. $out .= $val;
  701. if (strlen($out) >= $num)
  702. {
  703. return (strlen($out) == strlen($str)) ? $out : $out.'&#8230;';
  704. }
  705. $out .= ' ';
  706. }
  707. }
  708. // --------------------------------------------------------------------
  709. /**
  710. * Word limiter
  711. *
  712. * @access public
  713. * @param string
  714. * @return string
  715. */
  716. function word_limiter($str, $num = 100)
  717. {
  718. if (strlen($str) < $num)
  719. {
  720. return $str;
  721. }
  722. // allows the split to work properly with multi-byte Unicode characters
  723. if (is_php('4.3.2') === TRUE)
  724. {
  725. $word = preg_split('/\s/u', $str, -1, PREG_SPLIT_NO_EMPTY);
  726. }
  727. else
  728. {
  729. $word = preg_split('/\s/', $str, -1, PREG_SPLIT_NO_EMPTY);
  730. }
  731. if (count($word) <= $num)
  732. {
  733. return $str;
  734. }
  735. $str = "";
  736. for ($i = 0; $i < $num; $i++)
  737. {
  738. $str .= $word[$i]." ";
  739. }
  740. return trim($str).'&#8230;';
  741. }
  742. // --------------------------------------------------------------------
  743. /**
  744. * Fetch Email Template
  745. *
  746. * @access public
  747. * @param string
  748. * @return string
  749. */
  750. function fetch_email_template($name)
  751. {
  752. $query = $this->EE->db->query("SELECT template_name, data_title, template_data, enable_template FROM exp_specialty_templates WHERE site_id = '".$this->EE->db->escape_str($this->EE->config->item('site_id'))."' AND template_name = '".$this->EE->db->escape_str($name)."'");
  753. // Unlikely that this is necessary but it's possible a bad template request could
  754. // happen if a user hasn't run the update script.
  755. if ($query->num_rows() == 0)
  756. {
  757. return array('title' => '', 'data' => '');
  758. }
  759. if ($query->row('enable_template') == 'y')
  760. {
  761. return array('title' => $query->row('data_title') , 'data' => $query->row('template_data') );
  762. }
  763. if ($this->EE->session->userdata['language'] != '')
  764. {
  765. $user_lang = $this->EE->session->userdata['language'];
  766. }
  767. else
  768. {
  769. if ($this->EE->input->cookie('language'))
  770. {
  771. $user_lang = $this->EE->input->cookie('language');
  772. }
  773. elseif ($this->EE->config->item('deft_lang') != '')
  774. {
  775. $user_lang = $this->EE->config->item('deft_lang');
  776. }
  777. else
  778. {
  779. $user_lang = 'english';
  780. }
  781. }
  782. $user_lang = $this->EE->security->sanitize_filename($user_lang);
  783. if ( function_exists($name))
  784. {
  785. $title = $name.'_title';
  786. return array('title' => $title(), 'data' => $name());
  787. }
  788. else
  789. {
  790. if ( ! @include(APPPATH.'language/'.$user_lang.'/email_data.php'))
  791. {
  792. return array('title' => $query->row('data_title') , 'data' => $query->row('template_data') );
  793. }
  794. if (function_exists($name))
  795. {
  796. $title = $name.'_title';
  797. return array('title' => $title(), 'data' => $name());
  798. }
  799. else
  800. {
  801. return array('title' => $query->row('data_title') , 'data' => $query->row('template_data') );
  802. }
  803. }
  804. }
  805. // --------------------------------------------------------------------
  806. /**
  807. * Create character encoding menu
  808. *
  809. * DEPRECATED IN 2.0
  810. *
  811. * @access public
  812. * @param string
  813. * @param string
  814. * @param string
  815. * @return string
  816. */
  817. function encoding_menu($name, $selected = '')
  818. {
  819. $this->EE->load->library('logger');
  820. $this->EE->logger->deprecated('2.0');
  821. $file = APPPATH.'config/languages.php';
  822. if ( ! file_exists($file))
  823. {
  824. return FALSE;
  825. }
  826. require_once $file;
  827. $languages = array_flip($languages);
  828. $this->EE->load->helper('form');
  829. return form_dropdown($name, $languages, $selected);
  830. }
  831. // --------------------------------------------------------------------
  832. /**
  833. * Create Directory Map
  834. *
  835. * DEPRECATED IN 2.2
  836. *
  837. * @access public
  838. * @param string
  839. * @param bool
  840. * @return array
  841. */
  842. function create_directory_map($source_dir, $top_level_only = FALSE)
  843. {
  844. $this->EE->load->library('logger');
  845. $this->EE->logger->deprecated('2.2');
  846. if ( ! isset($filedata))
  847. $filedata = array();
  848. if ($fp = @opendir($source_dir))
  849. {
  850. while (FALSE !== ($file = readdir($fp)))
  851. {
  852. if (@is_dir($source_dir.$file) && substr($file, 0, 1) != '.' AND $top_level_only == FALSE)
  853. {
  854. $temp_array = array();
  855. $temp_array = $this->create_directory_map($source_dir.$file."/");
  856. $filedata[$file] = $temp_array;
  857. }
  858. elseif (substr($file, 0, 1) != "." && $file != 'index.html')
  859. {
  860. $filedata[] = $file;
  861. }
  862. }
  863. return $filedata;
  864. }
  865. }
  866. // --------------------------------------------------------------------
  867. /**
  868. * Create pull-down optios from dirctory map
  869. *
  870. * @access public
  871. * @param array
  872. * @param string
  873. * @return string
  874. */
  875. function render_map_as_select_options($zarray, $array_name = '')
  876. {
  877. foreach ($zarray as $key => $val)
  878. {
  879. if ( is_array($val))
  880. {
  881. if ($array_name != '')
  882. {
  883. $key = $array_name.'/'.$key;
  884. }
  885. $this->render_map_as_select_options($val, $key);
  886. }
  887. else
  888. {
  889. if ($array_name != '')
  890. {
  891. $val = $array_name.'/'.$val;
  892. }
  893. if (substr($val, -4) == '.php')
  894. {
  895. if ($val != 'theme_master.php')
  896. {
  897. $this->template_map[] = $val;
  898. }
  899. }
  900. }
  901. }
  902. }
  903. // --------------------------------------------------------------------
  904. /**
  905. * Fetch names of installed language packs
  906. *
  907. * DEPRECATED IN 2.0
  908. *
  909. * @access public
  910. * @param string
  911. * @return string
  912. */
  913. function language_pack_names($default)
  914. {
  915. $source_dir = APPPATH.'language/';
  916. $dirs = array();
  917. if ($fp = @opendir($source_dir))
  918. {
  919. while (FALSE !== ($file = readdir($fp)))
  920. {
  921. if (is_dir($source_dir.$file) && substr($file, 0, 1) != ".")
  922. {
  923. $dirs[] = $file;
  924. }
  925. }
  926. closedir($fp);
  927. }
  928. sort($dirs);
  929. $r = "<div class='default'>";
  930. $r .= "<select name='deft_lang' class='select'>\n";
  931. foreach ($dirs as $dir)
  932. {
  933. $selected = ($dir == $default) ? " selected='selected'" : '';
  934. $r .= "<option value='{$dir}'{$selected}>".ucfirst($dir)."</option>\n";
  935. }
  936. $r .= "</select>";
  937. $r .= "</div>";
  938. return $r;
  939. }
  940. // --------------------------------------------------------------------
  941. /**
  942. * Delete cache files
  943. *
  944. * @access public
  945. * @param string
  946. * @return string
  947. */
  948. function clear_caching($which, $sub_dir = '', $relationships=FALSE)
  949. {
  950. $actions = array('page', 'tag', 'db', 'sql', 'relationships', 'all');
  951. if ( ! in_array($which, $actions))
  952. {
  953. return;
  954. }
  955. /* -------------------------------------
  956. /* Disable Tag Caching
  957. /*
  958. /* All for you, Nevin! Disables tag caching, which if used unwisely
  959. /* on a high traffic site can lead to disastrous disk i/o
  960. /* This setting allows quick thinking admins to temporarily disable
  961. /* it without hacking or modifying folder permissions
  962. /*
  963. /* Hidden Configuration Variable
  964. /* - disable_tag_caching => Disable tag caching? (y/n)
  965. /* -------------------------------------*/
  966. if ($which == 'tag' && $this->EE->config->item('disable_tag_caching') == 'y')
  967. {
  968. return;
  969. }
  970. $db_path = '';
  971. if ($sub_dir != '')
  972. {
  973. if ($which == 'all' OR $which == 'db')
  974. {
  975. $segs = explode('/', str_replace($this->fetch_site_index(), '', $sub_dir));
  976. $segment_one = (isset($segs['0'])) ? $segs['0'] : 'default';
  977. $segment_two = (isset($segs['1'])) ? $segs['1'] : 'index';
  978. $db_path = '/'.$segment_one.'+'.$segment_two.'/';
  979. }
  980. $sub_dir = '/'.md5($sub_dir).'/';
  981. }
  982. switch ($which)
  983. {
  984. case 'page' : $this->delete_directory(APPPATH.'cache/page_cache'.$sub_dir);
  985. break;
  986. case 'db' : $this->delete_directory(APPPATH.'cache/db_cache_'.$this->EE->config->item('site_id').$db_path);
  987. break;
  988. case 'tag' : $this->delete_directory(APPPATH.'cache/tag_cache'.$sub_dir);
  989. break;
  990. case 'sql' : $this->delete_directory(APPPATH.'cache/sql_cache'.$sub_dir);
  991. break;
  992. case 'relationships' : $this->EE->db->query("UPDATE exp_relationships SET rel_data = '', reverse_rel_data = ''");
  993. break;
  994. case 'all' :
  995. $this->delete_directory(APPPATH.'cache/page_cache'.$sub_dir);
  996. $this->delete_directory(APPPATH.'cache/db_cache_'.$this->EE->config->item('site_id').$db_path);
  997. $this->delete_directory(APPPATH.'cache/sql_cache'.$sub_dir);
  998. if ($this->EE->config->item('disable_tag_caching') != 'y')
  999. {
  1000. $this->delete_directory(APPPATH.'cache/tag_cache'.$sub_dir);
  1001. }
  1002. if ($relationships === TRUE)
  1003. {
  1004. $this->EE->db->query("UPDATE exp_relationships SET rel_data = '', reverse_rel_data = ''");
  1005. }
  1006. break;
  1007. }
  1008. }
  1009. // --------------------------------------------------------------------
  1010. /**
  1011. * Delete Direcories
  1012. *
  1013. * @access public
  1014. * @param string
  1015. * @param bool
  1016. * @return void
  1017. */
  1018. function delete_directory($path, $del_root = FALSE)
  1019. {
  1020. $path = rtrim($path, '/');
  1021. $path_delete = $path.'_delete';
  1022. if ( ! is_dir($path))
  1023. {
  1024. return FALSE;
  1025. }
  1026. // Delete temporary directory if it happens to exist from a previous attempt
  1027. if (is_dir($path_delete))
  1028. {
  1029. @exec("rm -r -f {$path_delete}");
  1030. }
  1031. // let's try this the sane way first
  1032. @exec("mv {$path} {$path_delete}", $out, $ret);
  1033. if (isset($ret) && $ret == 0)
  1034. {
  1035. if ($del_root === FALSE)
  1036. {
  1037. @mkdir($path, 0777);
  1038. if ($fp = @fopen($path.'/index.html', FOPEN_WRITE_CREATE_DESTRUCTIVE))
  1039. {
  1040. fclose($fp);
  1041. }
  1042. }
  1043. @exec("rm -r -f {$path_delete}");
  1044. }
  1045. else
  1046. {
  1047. if ( ! $current_dir = @opendir($path))
  1048. {
  1049. return;
  1050. }
  1051. while($filename = @readdir($current_dir))
  1052. {
  1053. if (@is_dir($path.'/'.$filename) and ($filename != "." and $filename != ".."))
  1054. {
  1055. $this->delete_directory($path.'/'.$filename, TRUE);
  1056. }
  1057. elseif($filename != "." and $filename != "..")
  1058. {
  1059. @unlink($path.'/'.$filename);
  1060. }
  1061. }
  1062. @closedir($current_dir);
  1063. if (substr($path, -6) == '_cache' && $fp = @fopen($path.'/index.html', FOPEN_WRITE_CREATE_DESTRUCTIVE))
  1064. {
  1065. fclose($fp);
  1066. }
  1067. if ($del_root == TRUE)
  1068. {
  1069. @rmdir($path);
  1070. }
  1071. }
  1072. }
  1073. // --------------------------------------------------------------------
  1074. /**
  1075. * Fetch allowed channels
  1076. *
  1077. * This function fetches the ID numbers of the
  1078. * channels assigned to the currently logged in user.
  1079. *
  1080. * @access public
  1081. * @param bool
  1082. * @return array
  1083. */
  1084. function fetch_assigned_channels($all_sites = FALSE)
  1085. {
  1086. $allowed_channels = array();
  1087. if (REQ == 'CP' AND isset($this->EE->session->userdata['assigned_channels']) && $all_sites === FALSE)
  1088. {
  1089. $allowed_channels = array_keys($this->EE->session->userdata['assigned_channels']);
  1090. }
  1091. elseif ($this->EE->session->userdata['group_id'] == 1)
  1092. {
  1093. if ($all_sites === TRUE)
  1094. {
  1095. $this->EE->db->select('channel_id');
  1096. $query = $this->EE->db->get('channels');
  1097. }
  1098. else
  1099. {
  1100. $this->EE->db->select('channel_id');
  1101. $this->EE->db->where('site_id', $this->EE->config->item('site_id'));
  1102. $query = $this->EE->db->get('channels');
  1103. }
  1104. if ($query->num_rows() > 0)
  1105. {
  1106. foreach ($query->result_array() as $row)
  1107. {
  1108. $allowed_channels[] = $row['channel_id'];
  1109. }
  1110. }
  1111. }
  1112. else
  1113. {
  1114. if ($all_sites === TRUE)
  1115. {
  1116. $result = $this->EE->db->query("SELECT exp_channel_member_groups.channel_id FROM exp_channel_member_groups
  1117. WHERE exp_channel_member_groups.group_id = '".$this->EE->db->escape_str($this->EE->session->userdata['group_id'])."'");
  1118. }
  1119. else
  1120. {
  1121. $result = $this->EE->db->query("SELECT exp_channels.channel_id FROM exp_channels, exp_channel_member_groups
  1122. WHERE exp_channels.channel_id = exp_channel_member_groups.channel_id
  1123. AND exp_channels.site_id = '".$this->EE->db->escape_str($this->EE->config->item('site_id'))."'
  1124. AND exp_channel_member_groups.group_id = '".$this->EE->db->escape_str($this->EE->session->userdata['group_id'])."'");
  1125. }
  1126. if ($result->num_rows() > 0)
  1127. {
  1128. foreach ($result->result_array() as $row)
  1129. {
  1130. $allowed_channels[] = $row['channel_id'];
  1131. }
  1132. }
  1133. }
  1134. return array_values($allowed_channels);
  1135. }
  1136. // --------------------------------------------------------------------
  1137. /**
  1138. * Log Search terms
  1139. *
  1140. * @access public
  1141. * @param string
  1142. * @param string
  1143. * @return void
  1144. */
  1145. function log_search_terms($terms = '', $type = 'site')
  1146. {
  1147. if ($terms == '' OR $this->EE->db->table_exists('exp_search_log') === FALSE)
  1148. return;
  1149. if ($this->EE->config->item('enable_search_log') == 'n')
  1150. return;
  1151. $this->EE->load->helper('xml');
  1152. $search_log = array(
  1153. 'member_id' => $this->EE->session->userdata('member_id'),
  1154. 'screen_name' => $this->EE->session->userdata('screen_name'),
  1155. 'ip_address' => $this->EE->input->ip_address(),
  1156. 'search_date' => $this->EE->localize->now,
  1157. 'search_type' => $type,
  1158. 'search_terms' => xml_convert($this->EE->functions->encode_ee_tags($this->EE->security->xss_clean($terms), TRUE)),
  1159. 'site_id' => $this->EE->config->item('site_id')
  1160. );
  1161. $this->EE->db->query($this->EE->db->insert_string('exp_search_log', $search_log));
  1162. // Prune Database
  1163. srand(time());
  1164. if ((rand() % 100) < 5)
  1165. {
  1166. $max = ( ! is_numeric($this->EE->config->item('max_logged_searches'))) ? 500 : $this->EE->config->item('max_logged_searches');
  1167. $query = $this->EE->db->query("SELECT MAX(id) as search_id FROM exp_search_log WHERE site_id = '".$this->EE->db->escape_str($this->EE->config->item('site_id'))."'");
  1168. $row = $query->row_array();
  1169. if (isset($row['search_id'] ) && $row['search_id'] > $max)
  1170. {
  1171. $this->EE->db->query("DELETE FROM exp_search_log WHERE site_id = '".$this->EE->db->escape_str($this->EE->config->item('site_id'))."' AND id < ".($row['search_id'] -$max)."");
  1172. }
  1173. }
  1174. }
  1175. // --------------------------------------------------------------------
  1176. /**
  1177. * Fetch Action ID
  1178. *
  1179. * @access public
  1180. * @param string
  1181. * @param string
  1182. * @return string
  1183. */
  1184. function fetch_action_id($class, $method)
  1185. {
  1186. if ($class == '' OR $method == '')
  1187. {
  1188. return FALSE;
  1189. }
  1190. $this->action_ids[ucfirst($class)][$method] = $method;
  1191. return LD.'AID:'.ucfirst($class).':'.$method.RD;
  1192. }
  1193. // --------------------------------------------------------------------
  1194. /**
  1195. * Insert Action IDs
  1196. *
  1197. * @access public
  1198. * @param string
  1199. * @return string
  1200. */
  1201. function insert_action_ids($str)
  1202. {
  1203. if (count($this->action_ids) == 0)
  1204. {
  1205. return $str;
  1206. }
  1207. $sql = "SELECT action_id, class, method FROM exp_actions WHERE";
  1208. foreach($this->action_ids as $key => $value)
  1209. {
  1210. foreach($value as $k => $v)
  1211. {
  1212. $sql .= " (class= '".$this->EE->db->escape_str($key)."' AND method = '".$this->EE->db->escape_str($v)."') OR";
  1213. }
  1214. }
  1215. $query = $this->EE->db->query(substr($sql, 0, -3));
  1216. if ($query->num_rows() > 0)
  1217. {
  1218. foreach($query->result_array() as $row)
  1219. {
  1220. $str = str_replace(LD.'AID:'.$row['class'].':'.$row['method'].RD, $row['action_id'], $str);
  1221. }
  1222. }
  1223. return $str;
  1224. }
  1225. // --------------------------------------------------------------------
  1226. /**
  1227. * Compile and cache relationship data
  1228. *
  1229. * This is used when submitting new channel entries or gallery posts.
  1230. * It serializes the related entry data. The reason it's in this
  1231. * file is because it gets called from the publish class and the
  1232. * gallery class so we need it somewhere that is accessible to both.
  1233. *
  1234. * @access public
  1235. * @param string
  1236. * @param bool
  1237. * @param bool
  1238. * @return void
  1239. */
  1240. function compile_relationship($data, $parent_entry = TRUE, $reverse = FALSE)
  1241. {
  1242. if ($data['type'] == 'channel' OR ($reverse === TRUE && $parent_entry === FALSE))
  1243. {
  1244. $sql = "SELECT t.entry_id, t.channel_id, t.forum_topic_id, t.author_id, t.ip_address, t.title, t.url_title, t.status, t.dst_enabled, t.view_count_one, t.view_count_two, t.view_count_three, t.view_count_four, t.allow_comments, t.comment_expiration_date, t.sticky, t.entry_date, t.year, t.month, t.day, t.entry_date, t.edit_date, t.expiration_date, t.recent_comment_date, t.comment_total, t.site_id as entry_site_id,
  1245. w.channel_title, w.channel_name, w.channel_url, w.comment_url, w.comment_moderate, w.channel_html_formatting, w.channel_allow_img_urls, w.channel_auto_link_urls,
  1246. m.username, m.email, m.url, m.screen_name, m.location, m.occupation, m.interests, m.aol_im, m.yahoo_im, m.msn_im, m.icq, m.signature, m.sig_img_filename, m.sig_img_width, m.sig_img_height, m.avatar_filename, m.avatar_width, m.avatar_height, m.photo_filename, m.photo_width, m.photo_height, m.group_id, m.member_id, m.bday_d, m.bday_m, m.bday_y, m.bio,
  1247. md.*,
  1248. wd.*
  1249. FROM exp_channel_titles AS t
  1250. LEFT JOIN exp_channels AS w ON t.channel_id = w.channel_id
  1251. LEFT JOIN exp_channel_data AS wd ON t.entry_id = wd.entry_id
  1252. LEFT JOIN exp_members AS m ON m.member_id = t.author_id
  1253. LEFT JOIN exp_member_data AS md ON md.member_id = m.member_id
  1254. WHERE t.entry_id = '".(($reverse === TRUE && $parent_entry === FALSE) ? $data['parent_id'] : $data['child_id'])."'";
  1255. $entry_query = $this->EE->db->query($sql);
  1256. // Is there a category group associated with this channel?
  1257. $query = $this->EE->db->query("SELECT cat_group FROM exp_channels WHERE channel_id = '".$entry_query->row('channel_id') ."'");
  1258. $cat_group = (trim($query->row('cat_group')) == '') ? FALSE : $query->row('cat_group');
  1259. $this->cat_array = array();
  1260. $cat_array = array();
  1261. if ($cat_group !== FALSE)
  1262. {
  1263. $this->get_categories($cat_group, ($reverse === TRUE && $parent_entry === FALSE) ? $data['parent_id'] : $data['child_id']);
  1264. }
  1265. $cat_array = $this->cat_array;
  1266. if ($parent_entry == TRUE)
  1267. {
  1268. $this->EE->db->query("INSERT INTO exp_relationships (rel_parent_id, rel_child_id, rel_type, rel_data, reverse_rel_data)
  1269. VALUES ('".$data['parent_id']."', '".$data['child_id']."', '".$data['type']."',
  1270. '".addslashes(serialize(array('query' => $entry_query, 'cats_fixed' => '1', 'categories' => $cat_array)))."', '')");
  1271. return $this->EE->db->insert_id();
  1272. }
  1273. else
  1274. {
  1275. if ($reverse === TRUE)
  1276. {
  1277. $this->EE->db->query("UPDATE exp_relationships
  1278. SET reverse_rel_data = '".addslashes(serialize(array('query' => $entry_query, 'cats_fixed' => '1', 'categories' => $cat_array)))."'
  1279. WHERE rel_type = '".$this->EE->db->escape_str($data['type'])."' AND rel_parent_id = '".$data['parent_id']."'");
  1280. }
  1281. else
  1282. {
  1283. $this->EE->db->query("UPDATE exp_relationships
  1284. SET rel_data = '".addslashes(serialize(array('query' => $entry_query, 'cats_fixed' => '1', 'categories' => $cat_array)))."'
  1285. WHERE rel_type = 'channel' AND rel_child_id = '".$data['child_id']."'");
  1286. }
  1287. }
  1288. }
  1289. }
  1290. // --------------------------------------------------------------------
  1291. /**
  1292. * Get Categories for Channel Entry/Entries
  1293. *
  1294. * @access public
  1295. * @param string
  1296. * @param string
  1297. * @return array
  1298. */
  1299. function get_categories($cat_group, $entry_id)
  1300. {
  1301. // fetch the custom category fields
  1302. $field_sqla = '';
  1303. $field_sqlb = '';
  1304. $query = $this->EE->db->query("SELECT field_id, field_name FROM exp_category_fields WHERE group_id IN ('".str_replace('|', "','", $this->EE->db->escape_str($cat_group))."')");
  1305. if ($query->num_rows() > 0)
  1306. {
  1307. foreach ($query->result_array() as $row)
  1308. {
  1309. $this->catfields[] = array('field_name' => $row['field_name'], 'field_id' => $row['field_id']);
  1310. }
  1311. $field_sqla = ", cg.field_html_formatting, fd.* ";
  1312. $field_sqlb = " LEFT JOIN exp_category_field_data AS fd ON fd.cat_id = c.cat_id
  1313. LEFT JOIN exp_category_groups AS cg ON cg.group_id = c.group_id";
  1314. }
  1315. $sql = "SELECT c.cat_name, c.cat_url_title, c.cat_id, c.cat_image, p.cat_id, c.parent_id, c.cat_description, c.group_id
  1316. {$field_sqla}
  1317. FROM (exp_categories AS c, exp_category_posts AS p)
  1318. {$field_sqlb}
  1319. WHERE c.group_id IN ('".str_replace('|', "','", $this->EE->db->escape_str($cat_group))."')
  1320. AND p.entry_id = '".$entry_id."'
  1321. AND c.cat_id = p.cat_id
  1322. ORDER BY c.parent_id, c.cat_order";
  1323. $sql = str_replace("\t", " ", $sql);
  1324. $query = $this->EE->db->query($sql);
  1325. $this->cat_array = array();
  1326. $parents = array();
  1327. if ($query->num_rows() > 0)
  1328. {
  1329. $this->temp_array = array();
  1330. foreach ($query->result_array() as $row)
  1331. {
  1332. $this->temp_array[$row['cat_id']] = array($row['cat_id'], $row['parent_id'], $row['cat_name'], $row['cat_image'], $row['cat_description'], $row['group_id'], $row['cat_url_title']);
  1333. if ($field_sqla != '')
  1334. {
  1335. foreach ($row as $k => $v)
  1336. {
  1337. if (strpos($k, 'field') !== FALSE)
  1338. {
  1339. $this->temp_array[$row['cat_id']][$k] = $v;
  1340. }
  1341. }
  1342. }
  1343. if ($row['parent_id'] > 0 && ! isset($this->temp_array[$row['parent_id']])) $parents[$row['parent_id']] = '';
  1344. unset($parents[$row['cat_id']]);
  1345. }
  1346. foreach($this->temp_array as $k => $v)
  1347. {
  1348. if (isset($parents[$v[1]])) $v[1] = 0;
  1349. if (0 == $v[1])
  1350. {
  1351. $this->cat_array[] = $v;
  1352. $this->process_subcategories($k);
  1353. }
  1354. }
  1355. unset($this->temp_array);
  1356. }
  1357. }
  1358. // --------------------------------------------------------------------
  1359. /**
  1360. * Process Subcategories
  1361. *
  1362. * @access public
  1363. * @param string
  1364. * @return void
  1365. */
  1366. function process_subcategories($parent_id)
  1367. {
  1368. foreach($this->temp_array as $key => $val)
  1369. {
  1370. if ($parent_id == $val[1])
  1371. {
  1372. $this->cat_array[] = $val;
  1373. $this->process_subcategories($key);
  1374. }
  1375. }
  1376. }
  1377. // --------------------------------------------------------------------
  1378. /**
  1379. * Add security hashes to forms
  1380. *
  1381. * @access public
  1382. * @param string
  1383. * @return string
  1384. */
  1385. function add_form_security_hash($str)
  1386. {
  1387. if ($this->EE->config->item('secure_forms') == 'y')
  1388. {
  1389. if (preg_match_all("/({XID_HASH})/", $str, $matches))
  1390. {
  1391. $db_reset = FALSE;
  1392. // Disable DB caching if it's currently set
  1393. if ($this->EE->db->cache_on == TRUE)
  1394. {
  1395. $this->EE->db->cache_off();
  1396. $db_reset = TRUE;
  1397. }
  1398. // Add security hashes
  1399. $hashes = $this->EE->security->generate_xid(count($matches[1]), TRUE);
  1400. foreach ($hashes as $hash)
  1401. {
  1402. $str = preg_replace("/{XID_HASH}/", $hash, $str, 1);
  1403. }
  1404. // Re-enable DB caching
  1405. if ($db_reset == TRUE)
  1406. {
  1407. $this->EE->db->cache_on();
  1408. }
  1409. }
  1410. }
  1411. return $str;
  1412. }
  1413. // --------------------------------------------------------------------
  1414. /**
  1415. * Generate CAPTCHA
  1416. *
  1417. * @access public
  1418. * @param string
  1419. * @return string
  1420. */
  1421. function create_captcha($old_word = '')
  1422. {
  1423. if ($this->EE->config->item('captcha_require_members') == 'n' AND $this->EE->session->userdata['member_id'] != 0)
  1424. {
  1425. return '';
  1426. }
  1427. // -------------------------------------------
  1428. // 'create_captcha_start' hook.
  1429. // - Allows rewrite of how CAPTCHAs are created
  1430. //
  1431. if ($this->EE->extensions->active_hook('create_captcha_start') === TRUE)
  1432. {
  1433. $edata = $this->EE->extensions->call('create_captcha_start', $old_word);
  1434. if ($this->EE->extensions->end_script === TRUE) return $edata;
  1435. }
  1436. // -------------------------------------------
  1437. $img_path = $this->EE->config->slash_item('captcha_path', 1);
  1438. $img_url = $this->EE->config->slash_item('captcha_url');
  1439. $use_font = ($this->EE->config->item('captcha_font') == 'y') ? TRUE : FALSE;
  1440. $font_face = "texb.ttf";
  1441. $font_size = 16;
  1442. $expiration = 60*60*2; // 2 hours
  1443. $img_width = 140; // Image width
  1444. $img_height = 30; // Image height
  1445. if ($img_path == '' OR $img_url == '')
  1446. {
  1447. return FALSE;
  1448. }
  1449. if ( ! @is_dir($img_path))
  1450. {
  1451. return FALSE;
  1452. }
  1453. if ( ! is_really_writable($img_path))
  1454. {
  1455. return FALSE;
  1456. }
  1457. if ( ! file_exists(APPPATH.'config/captcha.php'))
  1458. {
  1459. return FALSE;
  1460. }
  1461. if ( ! extension_loaded('gd'))
  1462. {
  1463. return FALSE;
  1464. }
  1465. if (substr($img_url, -1) != '/') $img_url .= '/';
  1466. // Disable DB caching if it's currently set
  1467. $db_reset = FALSE;
  1468. if ($this->EE->db->cache_on == TRUE)
  1469. {
  1470. $this->EE->db->cache_off();
  1471. $db_reset = TRUE;
  1472. }
  1473. // Remove old images - add a bit of randomness so we aren't doing this every page access
  1474. list($usec, $sec) = explode(" ", microtime());
  1475. $now = ((float)$usec + (float)$sec);
  1476. if ((mt_rand() % 100) < $this->EE->session->gc_probability)
  1477. {
  1478. $old = time() - $expiration;
  1479. $this->EE->db->query("DELETE FROM exp_captcha WHERE date < ".$old);
  1480. $current_dir = @opendir($img_path);
  1481. while($filename = @readdir($current_dir))
  1482. {
  1483. if ($filename != "." and $filename != ".." and $filename != "index.html")
  1484. {
  1485. $name = str_replace(".jpg", "", $filename);
  1486. if (($name + $expiration) < $now)
  1487. {
  1488. @unlink($img_path.$filename);
  1489. }
  1490. }
  1491. }
  1492. @closedir($current_dir);
  1493. }
  1494. // Fetch and insert word
  1495. if ($old_word == '')
  1496. {
  1497. require APPPATH.'config/captcha.php';
  1498. $word = $words[array_rand($words)];
  1499. if ($this->EE->config->item('captcha_rand') == 'y')
  1500. {
  1501. $word .= $this->random('nozero', 2);
  1502. }
  1503. $this->EE->db->query("INSERT INTO exp_captcha (date, ip_address, word) VALUES (UNIX_TIMESTAMP(), '".$this->EE->input->ip_address()."', '".$this->EE->db->escape_str($word)."')");
  1504. }
  1505. else
  1506. {
  1507. $word = $old_word;
  1508. }
  1509. $this->cached_captcha = $word;
  1510. // Determine angle and position
  1511. $length = strlen($word);
  1512. $angle = ($length >= 6) ? rand(-($length-6), ($length-6)) : 0;
  1513. $x_axis = rand(6, (360/$length)-16);
  1514. $y_axis = ($angle >= 0 ) ? rand($img_height, $img_width) : rand(6, $img_height);
  1515. // Create image
  1516. $im = ImageCreate($img_width, $img_height);
  1517. // Assign colors
  1518. $bg_color = ImageColorAllocate($im, 255, 255, 255);
  1519. $border_color = ImageColorAllocate($im, 153, 102, 102);
  1520. $text_color = ImageColorAllocate($im, 204, 153, 153);
  1521. $grid_color = imagecolorallocate($im, 255, 182, 182);
  1522. $shadow_color = imagecolorallocate($im, 255, 240, 240);
  1523. // Create the rectangle
  1524. ImageFilledRectangle($im, 0, 0, $img_width, $img_height, $bg_color);
  1525. // Create the spiral pattern
  1526. $theta = 1;
  1527. $thetac = 6;
  1528. $radius = 12;
  1529. $circles = 20;
  1530. $points = 36;
  1531. for ($i = 0; $i < ($circles * $points) - 1; $i++)
  1532. {
  1533. $theta = $theta + $thetac;
  1534. $rad = $radius * ($i / $points );
  1535. $x = ($rad * cos($theta)) + $x_axis;
  1536. $y = ($rad * sin($theta)) + $y_axis;
  1537. $theta = $theta + $thetac;
  1538. $rad1 = $radius * (($i + 1) / $points);
  1539. $x1 = ($rad1 * cos($theta)) + $x_axis;
  1540. $y1 = ($rad1 * sin($theta )) + $y_axis;
  1541. imageline($im, $x, $y, $x1, $y1, $grid_color);
  1542. $theta = $theta - $thetac;
  1543. }
  1544. //imageline($im, $img_width, $img_height, 0, 0, $grid_color);
  1545. // Write the text
  1546. $font_path = APPPATH.'fonts/'.$font_face;
  1547. if ($use_font == TRUE)
  1548. {
  1549. if ( ! file_exists($font_path))
  1550. {
  1551. $use_font = FALSE;
  1552. }
  1553. }
  1554. if ($use_font == FALSE OR ! function_exists('imagettftext'))
  1555. {
  1556. $font_size = 5;
  1557. ImageString($im, $font_size, $x_axis, $img_height/3.8, $word, $text_color);
  1558. }
  1559. else
  1560. {
  1561. imagettftext($im, $font_size, $angle, $x_axis, $img_height/1.5, $text_color, $font_path, $word);
  1562. }
  1563. // Create the border
  1564. imagerectangle($im, 0, 0, $img_width-1, $img_height-1, $border_color);
  1565. // Generate the image
  1566. $img_name = $now.'.jpg';
  1567. ImageJPEG($im, $img_path.$img_name);
  1568. $img = "<img src=\"$img_url$img_name\" width=\"$img_width\" height=\"$img_height\" style=\"border:0;\" alt=\" \" />";
  1569. ImageDestroy($im);
  1570. // Re-enable DB caching
  1571. if ($db_reset == TRUE)
  1572. {
  1573. $this->EE->db->cache_on();
  1574. }
  1575. return $img;
  1576. }
  1577. // --------------------------------------------------------------------
  1578. /**
  1579. * SQL "AND" or "OR" string for conditional tag parameters
  1580. *
  1581. * This function lets us build a specific type of query
  1582. * needed when tags have conditional parameters:
  1583. *
  1584. * {exp:some_tag param="value1|value2|value3"}
  1585. *
  1586. * Or the parameter can contain "not":
  1587. *
  1588. * {exp:some_tag param="not value1|value2|value3"}
  1589. *
  1590. * This function explodes the pipes and constructs a series of AND
  1591. * conditions or OR conditions
  1592. *
  1593. * We should probably put this in the DB class but it's not
  1594. * something that is typically used
  1595. *
  1596. * @access public
  1597. * @param string
  1598. * @param string
  1599. * @param string
  1600. * @param bool
  1601. * @return string
  1602. */
  1603. function sql_andor_string($str, $field, $prefix = '', $null=FALSE)
  1604. {
  1605. if ($str == "" OR $field == "")
  1606. {
  1607. return '';
  1608. }
  1609. $str = trim($str);
  1610. $sql = '';
  1611. $not = '';
  1612. if ($prefix != '')
  1613. {
  1614. $prefix .= '.';
  1615. }
  1616. if (strpos($str, '|') !== FALSE)
  1617. {
  1618. $parts = preg_split('/\|/', $str, -1, PREG_SPLIT_NO_EMPTY);
  1619. $parts = array_map('trim', array_map(array($this->EE->db, 'escape_str'), $parts));
  1620. if (count($parts) > 0)
  1621. {
  1622. if (strncasecmp($parts[0], 'not ', 4) == 0)
  1623. {
  1624. $parts[0] = substr($parts[0], 4);
  1625. $not = 'NOT';
  1626. }
  1627. if ($null === TRUE)
  1628. {
  1629. $sql .= "AND ({$prefix}{$field} {$not} IN ('".implode("','", $parts)."') OR {$prefix}{$field} IS NULL)";
  1630. }
  1631. else
  1632. {
  1633. $sql .= "AND {$prefix}{$field} {$not} IN ('".implode("','", $parts)."')";
  1634. }
  1635. }
  1636. }
  1637. else
  1638. {
  1639. if (strncasecmp($str, 'not ', 4) == 0)
  1640. {
  1641. $str = trim(substr($str, 3));
  1642. $not = '!';
  1643. }
  1644. if ($null === TRUE)
  1645. {
  1646. $sql .= "AND ({$prefix}{$field} {$not}= '".$this->EE->db->escape_str($str)."' OR {$prefix}{$field} IS NULL)";
  1647. }
  1648. else
  1649. {
  1650. $sql .= "AND {$prefix}{$field} {$not}= '".$this->EE->db->escape_str($str)."'";
  1651. }
  1652. }
  1653. return $sql;
  1654. }
  1655. // --------------------------------------------------------------------
  1656. /**
  1657. * AR "AND" or "OR" string for conditional tag parameters
  1658. *
  1659. * This function lets us build a specific type of query
  1660. * needed when tags have conditional parameters:
  1661. *
  1662. * {exp:some_tag param="value1|value2|value3"}
  1663. *
  1664. * Or the parameter can contain "not":
  1665. *
  1666. * {exp:some_tag param="not value1|value2|value3"}
  1667. *
  1668. * This function explodes the pipes and builds an AR query.
  1669. *
  1670. * We should probably put this in the DB class but it's not
  1671. * something that is typically used
  1672. *
  1673. * @access public
  1674. * @param string
  1675. * @param string
  1676. * @param string
  1677. * @param bool
  1678. */
  1679. function ar_andor_string($str, $field, $prefix = '', $null=FALSE)
  1680. {
  1681. if ($str == "" OR $field == "")
  1682. {
  1683. return '';
  1684. }
  1685. $str = trim($str);
  1686. if ($prefix != '')
  1687. {
  1688. $prefix .= '.';
  1689. }
  1690. if (strpos($str, '|') !== FALSE)
  1691. {
  1692. $parts = preg_split('/\|/', $str, -1, PREG_SPLIT_NO_EMPTY);
  1693. $parts = array_map('trim', array_map(array($this->EE->db, 'escape_str'), $parts));
  1694. if (count($parts) > 0)
  1695. {
  1696. if ($null === TRUE)
  1697. {
  1698. // MySQL Only
  1699. if (strncasecmp($parts[0], 'not ', 4) == 0)
  1700. {
  1701. $parts[0] = substr($parts[0], 4);
  1702. $sql = "({$prefix}{$field} NOT IN ('".implode("','", $parts)."') OR {$prefix}{$field} IS NULL)";
  1703. }
  1704. else
  1705. {
  1706. $sql = "({$prefix}{$field} IN ('".implode("','", $parts)."') OR {$prefix}{$field} IS NULL)";
  1707. }
  1708. $this->EE->db->where($sql);
  1709. // END MySQL Only
  1710. }
  1711. else
  1712. {
  1713. if (strncasecmp($parts[0], 'not ', 4) == 0)
  1714. {
  1715. $parts[0] = substr($parts[0], 4);
  1716. $this->EE->db->where_not_in($prefix.$field, $parts);
  1717. }
  1718. else
  1719. {
  1720. $this->EE->db->where_in($prefix.$field, $parts);
  1721. }
  1722. }
  1723. }
  1724. }
  1725. else
  1726. {
  1727. if ($null === TRUE)
  1728. {
  1729. // MySQL Only
  1730. if (strncasecmp($str, 'not ', 4) == 0)
  1731. {
  1732. $str = trim(substr($str, 3));
  1733. $sql = "({$prefix}{$field} != '".$this->EE->db->escape_str($str)."' OR {$prefix}{$field} IS NULL)";
  1734. }
  1735. else
  1736. {
  1737. $sql = "({$prefix}{$field} = '".$this->EE->db->escape_str($str)."' OR {$prefix}{$field} IS NULL)";
  1738. }
  1739. $this->EE->db->where($sql);
  1740. // END MySQL Only
  1741. }
  1742. else
  1743. {
  1744. if (strncasecmp($str, 'not ', 4) == 0)
  1745. {
  1746. $str = trim(substr($str, 3));
  1747. $this->EE->db->where($prefix.$field.' !=', $str);
  1748. }
  1749. else
  1750. {
  1751. $this->EE->db->where($prefix.$field, $str);
  1752. }
  1753. }
  1754. }
  1755. }
  1756. // --------------------------------------------------------------------
  1757. /**
  1758. * Assign Conditional Variables
  1759. *
  1760. * @access public
  1761. * @param string
  1762. * @param string
  1763. * @param string
  1764. * @param string
  1765. * @return array
  1766. */
  1767. function assign_conditional_variables($str, $slash = '/', $LD = '{', $RD = '}')
  1768. {
  1769. // The first half of this function simply gathers the openging "if" tags
  1770. // and a numeric value that corresponds to the depth of nesting.
  1771. // The second half parses out the chunks
  1772. $conds = array();
  1773. $var_cond = array();
  1774. $modified_str = $str; // Not an alias!
  1775. // Find the conditionals.
  1776. // Added a \s in there to make sure it does not match {if:elseif} or {if:else} would would give
  1777. // us a bad array and cause havoc.
  1778. if ( ! preg_match_all("/".$LD."if(\s.*?)".$RD."/s", $modified_str, $eek))
  1779. {
  1780. return $var_cond;
  1781. }
  1782. $total_conditionals = count($eek[0]);
  1783. // Mark all opening conditionals, sequentially.
  1784. if (count($modified_str) > 0)
  1785. {
  1786. for ($i = 0; $i < $total_conditionals; $i++)
  1787. {
  1788. // Embedded variable fix
  1789. if ($ld_location = strpos($eek[1][$i],$LD))
  1790. {
  1791. if (preg_match_all("|".preg_quote($eek[0][$i])."(.*?)".$RD."|s", $modified_str, $fix_eek))
  1792. {
  1793. if (count($fix_eek) > 0)
  1794. {
  1795. $eek[0][$i] = $fix_eek[0][0];
  1796. $eek[1][$i] .= $RD.$fix_eek[1][0];
  1797. }
  1798. }
  1799. }
  1800. $modified_string_length = strlen($eek[1][$i]);
  1801. $replace_value[$i] = $LD.'if'.$i;
  1802. $p1 = strpos($modified_str,$eek[0][$i]);
  1803. $p2 = $p1+strlen($replace_value[$i].$eek[1][$i])-strlen($i);
  1804. $p3 = strlen($modified_str);
  1805. $modified_str = substr($modified_str,0,$p1).$replace_value[$i].$eek[1][$i].substr($modified_str,$p2, $p3);
  1806. }
  1807. }
  1808. // Mark all closing conditions.
  1809. $closed_position = array();
  1810. for ($t=$i-1; $t >= 0; $t--)
  1811. {
  1812. // Find the conditional's start
  1813. $coordinate = strpos($modified_str, $LD.'if'.$t);
  1814. // Find the shortned string.
  1815. $shortened = substr($modified_str, $coordinate);
  1816. // Find the conditional's end. Should be first closing tag.
  1817. $closed_position = strpos($shortened,$LD.$slash.'if'.$RD);
  1818. // Location of the next closing tag in main content var
  1819. $p1 = $coordinate + $closed_position;
  1820. $p2 = $p1 + strlen($LD.$slash.'if'.$t.$RD) - 1;
  1821. $modified_str = substr($modified_str,0,$p1).$LD.$slash.'if'.$t.$RD.substr($modified_str,$p2);
  1822. }
  1823. // Create Rick's array
  1824. for ($i = 0; $i < $total_conditionals; $i++)
  1825. {
  1826. $p1 = strpos($modified_str, $LD.'if'.$i.' ');
  1827. $p2 = strpos($modified_str, $LD.$slash.'if'.$i.$RD);
  1828. $length = $p2-$p1;
  1829. $text_range = substr($modified_str,$p1,$length);
  1830. // We use \d here because we want to look for one of the 'marked' conditionals, but
  1831. // not an Advanced Conditional, which would have a colon
  1832. if (preg_match_all("/".$LD."if(\d.*?)".$RD."/", $text_range, $depth_check))
  1833. {
  1834. // Depth is minus one, since it counts itself
  1835. $conds[] = array($LD.'if'.$eek[1][$i].$RD, count($depth_check[0]));
  1836. }
  1837. }
  1838. // Create detailed conditional array
  1839. $float = $str;
  1840. $CE = $LD.$slash.'if'.$RD;
  1841. $offset = strlen($CE);
  1842. $start = 1;
  1843. $duplicates = array();
  1844. foreach ($conds as $key => $val)
  1845. {
  1846. if ($val[1] > $start) $start = $val[1];
  1847. $open_tag = strpos($float, $val[0]);
  1848. $float = substr($float, $open_tag);
  1849. $temp = $float;
  1850. $len = 0;
  1851. $duplicates = array();
  1852. $i = 1;
  1853. while (FALSE !== ($in_point = strpos($temp, $CE)))
  1854. {
  1855. $temp = substr($temp, $in_point + $offset);
  1856. $len += $in_point + $offset;
  1857. if ($i === $val[1])
  1858. {
  1859. $tag = str_replace($LD, '', $val[0]);
  1860. $tag = str_replace($RD, '', $tag);
  1861. $outer = substr($float, 0, $len);
  1862. if (isset($duplicates[$val[1]]) && in_array($outer, $duplicates[$val[1]]))
  1863. {
  1864. break;
  1865. }
  1866. $duplicates[$val[1]][] = $outer;
  1867. $inner = substr($outer, strlen($val[0]), -$offset);
  1868. $tag = str_replace("|", "\|", $tag);
  1869. $tagb = preg_replace("/^if/", "", $tag);
  1870. $field = ( ! preg_match("#(\S+?)\s*(\!=|==|<|>|<=|>=|<>)#s", $tag, $match)) ? trim($tagb) : $match[1];
  1871. // Array prototype:
  1872. // offset 0: the full opening tag sans delimiters: if extended
  1873. // offset 1: the complete conditional chunk
  1874. // offset 2: the inner conditional chunk
  1875. // offset 3: the field name
  1876. $var_cond[$val[1]][] = array($tag, $outer, $inner, $field);
  1877. $float = substr($float, strlen($val[0]));
  1878. break;
  1879. }
  1880. $i++;
  1881. }
  1882. }
  1883. // Parse Order
  1884. $final_conds = array();
  1885. for ($i=$start; $i > 0; --$i)
  1886. {
  1887. if (isset($var_cond[$i])) $final_conds = array_merge($final_conds, $var_cond[$i]);
  1888. }
  1889. return $final_conds;
  1890. }
  1891. // --------------------------------------------------------------------
  1892. /**
  1893. * Assign Tag Variables
  1894. *
  1895. * This function extracts the variables contained within the current tag
  1896. * being parsed and assigns them to one of three arrays.
  1897. *
  1898. * There are three types of variables:
  1899. *
  1900. * Simple variables: {some_variable}
  1901. *
  1902. * Paired variables: {variable} stuff... {/variable}
  1903. *
  1904. * Contidionals: {if something != 'val'} stuff... {if something}
  1905. *
  1906. * Each of the three variables is parsed slightly different and appears in its own array
  1907. *
  1908. * @access public
  1909. * @param string
  1910. * @param string
  1911. * @return array
  1912. */
  1913. function assign_variables($str = '', $slash = '/')
  1914. {
  1915. $return['var_single'] = array();
  1916. $return['var_pair'] = array();
  1917. if ($str == '')
  1918. {
  1919. return $return;
  1920. }
  1921. // No variables? No reason to continue...
  1922. if (strpos($str, '{') === FALSE OR ! preg_match_all("/".LD."(.+?)".RD."/", $str, $matches))
  1923. {
  1924. return $return;
  1925. }
  1926. $temp_close = array();
  1927. $temp_misc = array();
  1928. $slash_length = strlen($slash);
  1929. foreach($matches[1] as $key => $val)
  1930. {
  1931. if (strncmp($val, 'if ', 3) !== 0 &&
  1932. strncmp($val, 'if:', 3) !== 0 &&
  1933. substr($val, 0, $slash_length+2) != $slash."if")
  1934. {
  1935. if (strpos($val, '{') !== FALSE)
  1936. {
  1937. if (preg_match("/(.+?)".LD."(.*)/s", $val, $matches2))
  1938. {
  1939. $temp_misc[$key] = $matches2[2];
  1940. }
  1941. }
  1942. elseif (strncmp($val, $slash, $slash_length) === 0)
  1943. {
  1944. $temp_close[$key] = str_replace($slash, '', $val);
  1945. }
  1946. else
  1947. {
  1948. $temp_misc[$key] = $val;
  1949. }
  1950. }
  1951. elseif (strpos($val, '{') !== FALSE) // Variable in conditional. ::sigh::
  1952. {
  1953. $full_conditional = substr($this->full_tag($matches[0][$key], $str), 1, -1);
  1954. // We only need the first match here, all others will get caught by our
  1955. // previous code as they won't start with if.
  1956. if (preg_match("/".LD."(.*?)".RD."/s", $full_conditional, $cond_vars))
  1957. {
  1958. $temp_misc[$key] = $cond_vars[1];
  1959. }
  1960. }
  1961. }
  1962. // $temp_misc contains all (opening) tags
  1963. // $temp_close contains all closing tags
  1964. // In 1.x we assumed that a closing tag meant that the variable was
  1965. // a tag pair. We now have variables that output as pairs and single tags
  1966. // so we need to properly match the pairs.
  1967. // In order to find proper pairs, we need to find equivalent opening and
  1968. // closing tags that are closest together (no nesting).
  1969. // The easiest way to go about this is to find all opening tags up to a
  1970. // closing tag - and then just take the last one.
  1971. $temp_pair = array();
  1972. $temp_single = array();
  1973. $open_stack = array();
  1974. foreach($temp_misc as $key => $item)
  1975. {
  1976. foreach($temp_close as $idx => $row)
  1977. {
  1978. // Find the closest (potential) closing tag following it
  1979. if (($idx > $key) && substr($item, 0, strlen($row)) == $row)
  1980. {
  1981. // There could be another opening tag between these
  1982. // so we create a stack of opening tag values
  1983. $open_stack[$idx][] = $key;
  1984. continue;
  1985. }
  1986. }
  1987. }
  1988. // Pop the last item off each stack of opening tags - these are pairs
  1989. foreach($open_stack as $potential_openings)
  1990. {
  1991. $open_tag_key = array_pop($potential_openings);
  1992. if (isset($temp_misc[$open_tag_key]))
  1993. {
  1994. $temp_pair[] = $temp_misc[$open_tag_key];
  1995. unset($temp_misc[$open_tag_key]);
  1996. }
  1997. }
  1998. // The rest of them are single tags
  1999. $temp_single = array_values($temp_misc);
  2000. // Weed out the duplicatess
  2001. $temp_single = array_unique($temp_single);
  2002. $temp_pair = array_unique($temp_pair);
  2003. // Assign Single Variables
  2004. $var_single = array();
  2005. foreach($temp_single as $val)
  2006. {
  2007. // simple conditionals
  2008. if (stristr($val, '\|') && substr($val, 0, 6) != 'switch' && substr($val, 0, 11) != 'multi_field')
  2009. {
  2010. $var_single[$val] = $this->fetch_simple_conditions($val);
  2011. }
  2012. // date variables
  2013. elseif (strpos($val, 'format') !== FALSE && preg_match("/.+?\s+?format/", $val))
  2014. {
  2015. $var_single[$val] = $this->fetch_date_variables($val);
  2016. }
  2017. else // single variables
  2018. {
  2019. $var_single[$val] = $val;
  2020. }
  2021. }
  2022. // Assign Variable Pairs
  2023. $var_pair = array();
  2024. foreach($temp_pair as $val)
  2025. {
  2026. $var_pair[$val] = $this->assign_parameters($val);
  2027. }
  2028. $return['var_single'] = $var_single;
  2029. $return['var_pair'] = $var_pair;
  2030. return $return;
  2031. }
  2032. // --------------------------------------------------------------------
  2033. /**
  2034. * Find the Full Opening Tag
  2035. *
  2036. * @access public
  2037. * @param string
  2038. * @param string
  2039. * @param string
  2040. * @param string
  2041. * @return string
  2042. */
  2043. function full_tag($str, $chunk='', $open='', $close='')
  2044. {
  2045. if ($chunk == '') $chunk = (isset($this->EE->TMPL) && is_object($this->EE->TMPL)) ? $this->EE->TMPL->fl_tmpl : '';
  2046. if ($open == '') $open = LD;
  2047. if ($close == '') $close = RD;
  2048. // Warning: preg_match() Compilation failed: regular expression is too large at offset #
  2049. // This error will occur if someone tries to stick over 30k-ish strings as tag parameters that also happen to include curley brackets.
  2050. // Instead of preventing the error, we let it take place, so the user will hopefully visit the forums seeking assistance
  2051. if ( ! preg_match("/".preg_quote($str, '/')."(.*?)".$close."/s", $chunk, $matches))
  2052. {
  2053. return $str;
  2054. }
  2055. if (isset($matches[1]) && $matches[1] != '' && stristr($matches[1], $open) !== false)
  2056. {
  2057. $matches[0] = $this->full_tag($matches[0], $chunk, $open, $close);
  2058. }
  2059. return $matches[0];
  2060. }
  2061. // --------------------------------------------------------------------
  2062. /**
  2063. * Fetch simple conditionals
  2064. *
  2065. * @access public
  2066. * @param string
  2067. * @return string
  2068. */
  2069. function fetch_simple_conditions($str)
  2070. {
  2071. if ($str == '')
  2072. {
  2073. return;
  2074. }
  2075. $str = str_replace(' ', '', trim($str, '|'));
  2076. return explode('|', $str);
  2077. }
  2078. // --------------------------------------------------------------------
  2079. /**
  2080. * Fetch date variables
  2081. *
  2082. * This function looks for a variable that has this prototype:
  2083. *
  2084. * {date format="%Y %m %d"}
  2085. *
  2086. * If found, returns only the datecodes: %Y %m %d
  2087. *
  2088. * @access public
  2089. * @param string
  2090. * @return string
  2091. */
  2092. function fetch_date_variables($datestr)
  2093. {
  2094. if ($datestr == '')
  2095. return;
  2096. if ( ! preg_match("/format\s*=\s*[\'|\"](.*?)[\'|\"]/s", $datestr, $match))
  2097. return FALSE;
  2098. return $match[1];
  2099. }
  2100. // --------------------------------------------------------------------
  2101. /**
  2102. * Return parameters as an array
  2103. *
  2104. * Creates an associative array from a string
  2105. * of parameters: sort="asc" limit="2" etc.
  2106. *
  2107. * @access public
  2108. * @param string
  2109. * @return bool
  2110. */
  2111. function assign_parameters($str)
  2112. {
  2113. if ($str == "")
  2114. return FALSE;
  2115. // \047 - Single quote octal
  2116. // \042 - Double quote octal
  2117. // I don't know for sure, but I suspect using octals is more reliable than ASCII.
  2118. // I ran into a situation where a quote wasn't being matched until I switched to octal.
  2119. // I have no idea why, so just to be safe I used them here. - Rick
  2120. // matches[0] => attribute and value
  2121. // matches[1] => attribute name
  2122. // matches[2] => single or double quote
  2123. // matches[3] => attribute value
  2124. preg_match_all("/(\S+?)\s*=\s*(\042|\047)([^\\2]*?)\\2/is", $str, $matches, PREG_SET_ORDER);
  2125. if (count($matches) > 0)
  2126. {
  2127. $result = array();
  2128. foreach($matches as $match)
  2129. {
  2130. $result[$match[1]] = (trim($match[3]) == '') ? $match[3] : trim($match[3]);
  2131. }
  2132. return $result;
  2133. }
  2134. return FALSE;
  2135. }
  2136. // --------------------------------------------------------------------
  2137. /**
  2138. * Prep conditional
  2139. *
  2140. * This function lets us do a little prepping before
  2141. * running any conditionals through eval()
  2142. *
  2143. * @access public
  2144. * @param string
  2145. * @return string
  2146. */
  2147. function prep_conditional($cond = '')
  2148. {
  2149. $cond = preg_replace("/^if/", "", $cond);
  2150. if (preg_match("/(\S+)\s*(\!=|==|<=|>=|<>|<|>)\s*(.+)/", $cond, $match))
  2151. {
  2152. $cond = trim($match[1]).' '.trim($match[2]).' '.trim($match[3]);
  2153. }
  2154. $rcond = substr($cond, strpos($cond, ' '));
  2155. $cond = str_replace($rcond, $rcond, $cond);
  2156. // Since we allow the following shorthand condition: {if username}
  2157. // but it's not legal PHP, we'll correct it by adding: != ''
  2158. if ( ! preg_match("/(\!=|==|<|>|<=|>=|<>)/", $cond))
  2159. {
  2160. $cond .= ' != "" ';
  2161. }
  2162. return trim($cond);
  2163. }
  2164. // --------------------------------------------------------------------
  2165. /**
  2166. * Reverse Key Sort
  2167. *
  2168. * @access public
  2169. * @param string
  2170. * @param string
  2171. * @return string
  2172. */
  2173. function reverse_key_sort($a, $b) {return strlen($b) > strlen($a);}
  2174. // --------------------------------------------------------------------
  2175. /**
  2176. * Prep conditionals
  2177. *
  2178. * @access public
  2179. * @param string
  2180. * @param string
  2181. * @param string
  2182. * @param string
  2183. * @return array
  2184. */
  2185. function prep_conditionals($str, $vars, $safety='n', $prefix='')
  2186. {
  2187. if (isset($this->EE->TMPL->embed_vars))
  2188. {
  2189. // If this is being called from a module tag, embedded variables
  2190. // aren't going to be available yet. So this is a quick workaround
  2191. // to ensure advanced conditionals using embedded variables can do
  2192. // their thing in mod tags.
  2193. $vars = array_merge($vars, $this->EE->TMPL->embed_vars);
  2194. }
  2195. if (count($vars) == 0) return $str;
  2196. $switch = array();
  2197. $protect = array();
  2198. $prep_id = $this->random('alpha', 3);
  2199. $embedded_tags = (stristr($str, LD.'exp:')) ? TRUE : FALSE;
  2200. $valid = array('!=','==','<=','>=','<','>','<>',
  2201. 'AND', 'XOR', 'OR','&&','||',
  2202. ')','(',
  2203. 'TRUE', 'FALSE');
  2204. $str = str_replace(LD.'if:else'.RD, unique_marker('if_else_safety'), $str);
  2205. // The ((else)*if) is actually faster than (elseif|if) in PHP 5.0.4,
  2206. // but only by a half a thousandth of a second. However, why not be
  2207. // as efficient as possible? It also gives me a chance to catch some
  2208. // user error mistakes.
  2209. if (preg_match_all("/".preg_quote(LD)."((if:else)*if)\s+(.*?)".preg_quote(RD)."/s", $str, $matches))
  2210. {
  2211. // PROTECT QUOTED TEXT
  2212. // That which is in quotes should be protected and ignored as it will screw
  2213. // up the parsing if the variable is found within a string
  2214. if (preg_match_all('/([\"\'])([^\\1]*?)\\1/s', implode(' ', $matches[3]), $quote_matches))
  2215. {
  2216. foreach($quote_matches[0] as $ii => $quote_match)
  2217. {
  2218. $md5_key = (string) hexdec($prep_id.md5($quote_match));
  2219. $protect[$quote_match] = $md5_key;
  2220. // To better protect quotes inside conditional quotes, we need to
  2221. // determine which kind of quote to surround the newly-encoded string
  2222. $surrounding_quote = surrounding_character($quote_match);
  2223. if (($surrounding_quote != '"' AND $surrounding_quote != "'")
  2224. OR $surrounding_quote === FALSE)
  2225. {
  2226. $surrounding_quote = '"';
  2227. }
  2228. // We do these conversions on variables below, so we need
  2229. // to also do them on the hardcoded values to make sure
  2230. // the conditionals resolve as expected.
  2231. // e.g. {if location == "pony's house"}
  2232. $quote_match = $surrounding_quote.
  2233. str_replace(
  2234. array("'", '"', '(', ')', '$', '{', '}', "\n", "\r", '\\'),
  2235. array('&#39;', '&#34;', '&#40;', '&#41;', '&#36;', '', '', '', '', '&#92;'),
  2236. $quote_matches[2][$ii]
  2237. ).
  2238. $surrounding_quote;
  2239. $switch[$md5_key] = $quote_match;
  2240. }
  2241. $matches[3] = str_replace(array_keys($protect), array_values($protect), $matches[3]);
  2242. // Remove quoted values altogether to find variables...
  2243. $matches['t'] = str_replace($valid, ' ', str_replace(array_values($protect), '', $matches[3]));
  2244. }
  2245. else
  2246. {
  2247. $matches['t'] = str_replace($valid, ' ', $matches[3]);
  2248. }
  2249. // Find what we need, nothing more!!
  2250. $data = array();
  2251. foreach($matches['t'] as $cond)
  2252. {
  2253. if (trim($cond) == '') continue;
  2254. $x = preg_split("/\s+/", trim($cond)); $i=0;
  2255. do
  2256. {
  2257. if (array_key_exists($x[$i], $vars))
  2258. {
  2259. $data[$x[$i]] = trim($vars[$x[$i]]);
  2260. }
  2261. elseif($embedded_tags === TRUE && ! is_numeric($x[$i]))
  2262. {
  2263. $data[$x[$i]] = $x[$i];
  2264. }
  2265. elseif(strncmp($x[$i], 'embed:', 6) == 0)
  2266. {
  2267. $data[$x[$i]] = '';
  2268. }
  2269. if ($i > 500) break; ++$i;
  2270. }
  2271. while(isset($x[$i]));
  2272. }
  2273. // This should prevent, for example, the variable 'comment' from
  2274. // overwriting the variable 'comments'.
  2275. uksort($data, array($this, 'reverse_key_sort'));
  2276. if ($safety == 'y')
  2277. {
  2278. // Make sure we have the same amount of opening conditional tags
  2279. // as closing conditional tags.
  2280. $tstr = preg_replace("/<script.*?".">.*?<\/script>/is", '', $str);
  2281. $opening = substr_count($tstr, LD.'if') - substr_count($tstr, LD.'if:elseif');
  2282. $closing = substr_count($tstr, LD.'/if'.RD);
  2283. if ($opening > $closing)
  2284. {
  2285. $str .= str_repeat(LD.'/if'.RD, $opening-$closing);
  2286. }
  2287. }
  2288. // Prep the data array to remove characters we do not want
  2289. // And also just add the quotes around the value for good measure.
  2290. while (list($key) = each($data))
  2291. {
  2292. if ( is_array($data[$key])) continue;
  2293. // TRUE AND FALSE values are for short hand conditionals,
  2294. // like {if logged_in} and so we have no need to remove
  2295. // unwanted characters and we do not quote it.
  2296. if ($data[$key] != 'TRUE' && $data[$key] != 'FALSE' && ($key != $data[$key] OR $embedded_tags !== TRUE))
  2297. {
  2298. $data[$key] = '"'.
  2299. str_replace(array("'", '"', '(', ')', '$', '{', '}', "\n", "\r", '\\'),
  2300. array('&#39;', '&#34;', '&#40;', '&#41;', '&#36;', '', '', '', '', '&#92;'),
  2301. (strlen($data[$key]) > 100) ? substr(htmlspecialchars($data[$key]), 0, 100) : $data[$key]
  2302. ).
  2303. '"';
  2304. }
  2305. $md5_key = (string) hexdec($prep_id.md5($key));
  2306. $protect[$key] = $md5_key;
  2307. $switch[$md5_key] = $data[$key];
  2308. if ($prefix != '')
  2309. {
  2310. $md5_key = (string) hexdec($prep_id.md5($prefix.$key));
  2311. $protect[$prefix.$key] = $md5_key;
  2312. $switch[$md5_key] = $data[$key];
  2313. }
  2314. }
  2315. // Example:
  2316. //
  2317. // {if entry_date < current_time}FUTURE{/if}
  2318. // {if "{entry_date format='%Y%m%d'}" == "{current_time format='%Y%m%d'}"}Today{/if}
  2319. //
  2320. // The above used to fail because the second conditional would turn into something like:
  2321. //
  2322. // {if "{"1343930801" format='%Y%m%d'}
  2323. //
  2324. // So here, we make sure the value we're replacing doesn't ALSO happen to appear in the
  2325. // middle of something that looks like a date field with a format parameter
  2326. foreach ($matches[3] as &$match)
  2327. {
  2328. foreach ($protect as $key => $value)
  2329. {
  2330. // Make sure $key doesn't appear as "{$key "
  2331. if ( ! strstr($match, LD.$key.' '))
  2332. {
  2333. $match = str_replace($key, $value, $match);
  2334. }
  2335. }
  2336. }
  2337. if ($safety == 'y')
  2338. {
  2339. $matches['s'] = str_replace($protect, '^', $matches[3]);
  2340. $matches['s'] = preg_replace('/"(.*?)"/s', '^', $matches['s']);
  2341. $matches['s'] = preg_replace("/'(.*?)'/s", '^', $matches['s']);
  2342. $matches['s'] = str_replace($valid, ' ', $matches['s']);
  2343. $matches['s'] = preg_replace("/(^|\s+)[0-9]+(\s|$)/", ' ', $matches['s']); // Remove unquoted numbers
  2344. $done = array();
  2345. }
  2346. for($i=0, $s = count($matches[0]); $i < $s; ++$i)
  2347. {
  2348. if ($safety == 'y' && ! in_array($matches[0][$i], $done))
  2349. {
  2350. $done[] = $matches[0][$i];
  2351. // Make sure someone did put in an {if:else conditional}
  2352. // when they likely meant to have an {if:elseif conditional}
  2353. if ($matches[2][$i] == '' &&
  2354. substr($matches[3][$i], 0, 5) == ':else' &&
  2355. $matches[1][$i] == 'if')
  2356. {
  2357. $matches[3][$i] = substr($matches[3][$i], 5);
  2358. $matches[2][$i] == 'elseif';
  2359. trigger_error('Invalid Conditional, Assumed ElseIf : '.str_replace(' :else',
  2360. ':else',
  2361. $matches[0][$i]),
  2362. E_USER_WARNING);
  2363. }
  2364. // If there are parentheses, then we
  2365. // try to make sure they match up correctly.
  2366. $left = substr_count($matches[3][$i], '(');
  2367. $right = substr_count($matches[3][$i], ')');
  2368. if ($left > $right)
  2369. {
  2370. $matches[3][$i] .= str_repeat(')', $left-$right);
  2371. }
  2372. elseif ($right > $left)
  2373. {
  2374. $matches[3][$i] = str_repeat('(', $right-$left).$matches[3][$i];
  2375. }
  2376. // Check for unparsed variables
  2377. if (trim($matches['s'][$i]) != '' && trim($matches['s'][$i]) != '^')
  2378. {
  2379. $x = preg_split("/\s+/", trim($matches['s'][$i]));
  2380. for($j=0, $sj=count($x); $j < $sj; ++$j)
  2381. {
  2382. if ($x[$j] == '^') continue;
  2383. if (substr($x[$j], 0, 1) != '^')
  2384. {
  2385. // We have an unset variable in the conditional.
  2386. // Set the unparsed variable to FALSE
  2387. $matches[3][$i] = str_replace($x[$j], 'FALSE', $matches[3][$i]);
  2388. if ($this->conditional_debug === TRUE)
  2389. {
  2390. trigger_error('Unset EE Conditional Variable ('.$x[$j].') : '.$matches[0][$i],
  2391. E_USER_WARNING);
  2392. }
  2393. }
  2394. else
  2395. {
  2396. // There is a partial variable match being done
  2397. // because they are doing something like segment_11
  2398. // when there is no such variable but there is a segment_1
  2399. // echo $x[$j]."\n<br />\n";
  2400. trigger_error('Invalid EE Conditional Variable: '.
  2401. $matches[0][$i],
  2402. E_USER_WARNING);
  2403. // Set entire conditional to FALSE since it fails
  2404. $matches[3][$i] = 'FALSE';
  2405. }
  2406. }
  2407. }
  2408. }
  2409. $matches[3][$i] = LD.$matches[1][$i].' '.trim($matches[3][$i]).RD;
  2410. }
  2411. $str = str_replace($matches[0], $matches[3], $str);
  2412. $str = str_replace(array_keys($switch), array_values($switch), $str);
  2413. }
  2414. unset($data);
  2415. unset($switch);
  2416. unset($matches);
  2417. unset($protect);
  2418. $str = str_replace(unique_marker('if_else_safety'),LD.'if:else'.RD, $str);
  2419. return $str;
  2420. }
  2421. // --------------------------------------------------------------------
  2422. /**
  2423. * Fetch file upload paths
  2424. *
  2425. * @access public
  2426. * @return array
  2427. */
  2428. function fetch_file_paths()
  2429. {
  2430. if ( ! empty($this->file_paths))
  2431. {
  2432. return $this->file_paths;
  2433. }
  2434. // if $this->file_paths === FALSE,
  2435. // we've queried and have nuttin
  2436. if ($this->file_paths === FALSE)
  2437. {
  2438. return array();
  2439. }
  2440. $this->EE->load->model('file_upload_preferences_model');
  2441. $upload_prefs = $this->EE->file_upload_preferences_model->get_file_upload_preferences(NULL, NULL, TRUE);
  2442. if (count($upload_prefs) == 0)
  2443. {
  2444. // Set $this->file_paths to FALSE so we check for it
  2445. // the next time through a coupla lines up.
  2446. // by default it's array().
  2447. $this->file_paths = FALSE;
  2448. return array();
  2449. }
  2450. foreach ($upload_prefs as $row)
  2451. {
  2452. $this->file_paths[$row['id']] = $row['url'];
  2453. }
  2454. return $this->file_paths;
  2455. }
  2456. // --------------------------------------------------------------------
  2457. /**
  2458. * Clones an Object
  2459. *
  2460. * This is required because of the way PHP 5 handles the passing of objects
  2461. * @php4
  2462. *
  2463. * @deprecated as of EE 2.1.2
  2464. * @param object
  2465. * @return object
  2466. */
  2467. function clone_object($object)
  2468. {
  2469. $this->EE->load->library('logger');
  2470. $this->EE->logger->deprecated('2.1.2');
  2471. return clone $object;
  2472. }
  2473. // --------------------------------------------------------------------
  2474. /**
  2475. * bookmarklet qstr decode
  2476. *
  2477. * @param string
  2478. */
  2479. function bm_qstr_decode($str)
  2480. {
  2481. $str = str_replace("%20", " ", $str);
  2482. $str = str_replace("%uFFA5", "&#8226;", $str);
  2483. $str = str_replace("%uFFCA", " ", $str);
  2484. $str = str_replace("%uFFC1", "-", $str);
  2485. $str = str_replace("%uFFC9", "...", $str);
  2486. $str = str_replace("%uFFD0", "-", $str);
  2487. $str = str_replace("%uFFD1", "-", $str);
  2488. $str = str_replace("%uFFD2", "\"", $str);
  2489. $str = str_replace("%uFFD3", "\"", $str);
  2490. $str = str_replace("%uFFD4", "\'", $str);
  2491. $str = str_replace("%uFFD5", "\'", $str);
  2492. $str = preg_replace("/\%u([0-9A-F]{4,4})/e","'&#'.base_convert('\\1',16,10).';'", $str);
  2493. $str = $this->security->xss_clean(stripslashes(urldecode($str)));
  2494. return $str;
  2495. }
  2496. // --------------------------------------------------------------------
  2497. }
  2498. // END CLASS
  2499. /* End of file Functions.php */
  2500. /* Location: ./system/expressionengine/libraries/Functions.php */