PageRenderTime 45ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/system/expressionengine/third_party/freeform/addon_builder/addon_builder.php

https://bitbucket.org/studiobreakfast/sync
PHP | 3658 lines | 1978 code | 657 blank | 1023 comment | 314 complexity | 4d86a6c53174d9e28ba4d142a9a303b1 MD5 | raw file
  1. <?php if ( ! defined('EXT')) exit('No direct script access allowed');
  2. /**
  3. * Solspace - Add-On Builder Framework
  4. *
  5. * @package Add-On Builder Framework
  6. * @author Solspace DevTeam
  7. * @copyright Copyright (c) 2008-2011, Solspace, Inc.
  8. * @link http://solspace.com/docs/
  9. * @version 1.2.4
  10. */
  11. /**
  12. * Add-On Builder - Base Class
  13. *
  14. * A class that helps with the building of ExpressionEngine Add-Ons by allowing the automating of certain
  15. * tasks.
  16. *
  17. * @package Add-On Builder Framework
  18. * @subpackage Solspace:Add-On Builder
  19. * @author Solspace DevTeam
  20. * @link http://solspace.com/docs/
  21. */
  22. //--------------------------------------------
  23. // Alias to get_instance()
  24. //--------------------------------------------
  25. if ( ! function_exists('ee') )
  26. {
  27. function ee()
  28. {
  29. return get_instance();
  30. }
  31. }
  32. //--------------------------------------------
  33. // need the bridge adaptor in 1.x
  34. //--------------------------------------------
  35. if (APP_VER < 2.0)
  36. {
  37. require_once PATH . "bridge/codeigniter/ci_bridge_adaptor.php";
  38. }
  39. class Addon_builder_freeform {
  40. static $bridge_version = '1.2.4';
  41. public $cache = array(); // Internal cache
  42. public $ob_level = 0;
  43. public $cached_vars = array();
  44. public $switches = array();
  45. // The general class name (ucfirst with underscores), used in database and class instantiation
  46. public $class_name = '';
  47. // The lowercased class name, used for referencing module files and in URLs
  48. public $lower_name = '';
  49. // The name that we put into the Extensions DB table, different for 2.x and 1.x
  50. public $extension_name = '';
  51. // Module disabled? Typically used when an update is in progress.
  52. public $disabled = FALSE;
  53. public $addon_path = '';
  54. public $theme = 'default';
  55. public $version = '';
  56. public $crumbs = array();
  57. public $document = FALSE;
  58. public $data = FALSE;
  59. public $actions = FALSE;
  60. public $module_preferences = array();
  61. public $remote_data = ''; // For remote file retrieving and storage
  62. public $sc;
  63. //this will house items that might not always be set when called.
  64. public $constants;
  65. //holder for the json object if ever
  66. public $json;
  67. public $json_array;
  68. //for upper right link building
  69. public $right_links = array();
  70. // Member Fields array
  71. public $mfields = array();
  72. public $updater;
  73. public $aob_path = '';
  74. public $auto_paginate = FALSE;
  75. // --------------------------------------------------------------------
  76. /**
  77. * Constructor
  78. *
  79. * @access public
  80. * @return null
  81. */
  82. public function Addon_builder_freeform ($name='')
  83. {
  84. //path to this folder
  85. $this->aob_path = rtrim(realpath(dirname(__FILE__)), '/') . '/';
  86. $this->EE =& get_instance();
  87. if ( APP_VER < 2.0)
  88. {
  89. ee()->localize = $GLOBALS['LOC'];
  90. ee()->stats = ( ! isset($GLOBALS['STAT'])) ? FALSE : $GLOBALS['STAT'];
  91. //need a symbolic link to extension->last_call and end_script
  92. if ( isset($GLOBALS['EXT']) AND is_object($GLOBALS['EXT']))
  93. {
  94. ee()->extensions->last_call =& $GLOBALS['EXT']->last_call;
  95. ee()->extensions->end_script =& $GLOBALS['EXT']->end_script;
  96. }
  97. }
  98. // --------------------------------------------
  99. // Session Global
  100. // - Add-On Builder might be called for an Extension using the 'session_' hooks,
  101. // - so we need to check for that object first.
  102. // --------------------------------------------
  103. if ( ! isset(ee()->session) OR ! is_object(ee()->session))
  104. {
  105. if ( APP_VER < 2.0)
  106. {
  107. //Have to check for ->userdata too because a REAL session instance has it always
  108. //Some other addon devs are creating $SESS->cache even when $SESS is null
  109. //That autocreates the session object before the real one clobbers it,
  110. //that in turn fools addons into thinking that $SESSION has already fired :/
  111. //assume its still not there
  112. ee()->session = FALSE;
  113. //if it is, lets grab it changed to pass by reference
  114. if ( isset($GLOBALS['SESS']) AND isset($GLOBALS['SESS']->userdata))
  115. {
  116. ee()->session =& $GLOBALS['SESS'];
  117. }
  118. }
  119. elseif (file_exists(APPPATH.'libraries/Session.php'))
  120. {
  121. ee()->load->library('session');
  122. }
  123. }
  124. // --------------------------------------------
  125. // PAGE Request? Check for $TMPL global
  126. // --------------------------------------------
  127. if (APP_VER < 2.0 AND
  128. ( ! isset(ee()->TMPL) OR ! is_object(ee()->TMPL)) AND
  129. isset($GLOBALS['TMPL']) AND
  130. is_object($GLOBALS['TMPL']))
  131. {
  132. ee()->TMPL =& $GLOBALS['TMPL'];
  133. }
  134. //--------------------------------------------
  135. // CP Request? Check for $DSP global
  136. //--------------------------------------------
  137. if (APP_VER < 2.0 AND
  138. REQ == 'CP' AND
  139. isset($GLOBALS['DSP']) AND
  140. is_object($GLOBALS['DSP']))
  141. {
  142. ee()->cp =& $GLOBALS['DSP'];
  143. }
  144. //--------------------------------------------
  145. // Required CONSTANTs
  146. //--------------------------------------------
  147. if ( ! defined('QUERY_MARKER'))
  148. {
  149. define('QUERY_MARKER', (ee()->config->item('force_query_string') == 'y') ? '' : '?');
  150. }
  151. if ( ! defined('SLASH'))
  152. {
  153. define('SLASH', '&#47;'); // Seems this constant is the same for both EE 1.x and EE 2.x
  154. }
  155. if ( ! defined('T_SLASH')) // Template Parsing Slash
  156. {
  157. define('T_SLASH', (APP_VER < '2.0') ? '&#47;' : "/");
  158. }
  159. if ( ! defined('NL'))
  160. {
  161. define('NL', "\n");
  162. }
  163. if (APP_VER < 2.0 AND ! defined('PATH_THIRD') AND defined('PATH_MOD'))
  164. {
  165. define('PATH_THIRD', PATH_MOD);
  166. }
  167. if ( ! defined('PATH_CP_IMG') AND defined('PATH_CP_GBL_IMG'))
  168. {
  169. define('PATH_CP_IMG', PATH_CP_GBL_IMG);
  170. }
  171. //just in case we need them early
  172. if ( ! defined('AMP'))
  173. {
  174. define('AMP', '&amp;');
  175. }
  176. if ( ! defined('BR'))
  177. {
  178. define('BR', '<br />');
  179. }
  180. if ( ! defined('NBS'))
  181. {
  182. define('NBS', "&nbsp;");
  183. }
  184. // EE 1.x does not have this constant,
  185. // but it adds it to every form automatically.
  186. // EE 2.x sets it all the time now.
  187. $constants = array(
  188. 'XID_SECURE_HASH' => (APP_VER < 2.0 OR ! defined('XID_SECURE_HASH')) ?
  189. '' : XID_SECURE_HASH,
  190. );
  191. $this->constants = (object) $constants;
  192. //--------------------------------------------
  193. // Auto-Detect Name
  194. //--------------------------------------------
  195. if ($name == '')
  196. {
  197. $name = get_class($this);
  198. $ends = array(
  199. '_cp_base',
  200. '_mcp',
  201. '_CP',
  202. '_ext',
  203. '_extension',
  204. '_extension_base',
  205. '_updater_base',
  206. '_updater',
  207. '_upd',
  208. '_actions',
  209. '_data',
  210. '_ft',
  211. '_acc'
  212. );
  213. foreach($ends as $remove)
  214. {
  215. if (substr($name, -strlen($remove)) == $remove)
  216. {
  217. $name = substr($name, 0, -strlen($remove));
  218. break;
  219. }
  220. }
  221. }
  222. //--------------------------------------------
  223. // Important Class Vars
  224. //--------------------------------------------
  225. //this should always be loaded after EE 2.1.4
  226. if ( ! isset(ee()->security) OR
  227. ! is_object(ee()->security))
  228. {
  229. ee()->load->library('security');
  230. }
  231. $this->lower_name = strtolower(ee()->security->sanitize_filename($name));
  232. $this->class_name = ucfirst($this->lower_name);
  233. $this->extension_name = $this->class_name . ((APP_VER < 2.0) ? '_extension' : '_ext');
  234. // -------------------------------------
  235. // set short cuts (must be done after lowername)
  236. // -------------------------------------
  237. $this->sc = $this->generate_shortcuts();
  238. //--------------------------------------------
  239. // Prepare Caching
  240. //--------------------------------------------
  241. //no sessions? lets use global until we get here again
  242. if ( ! isset(ee()->session) OR ! is_object(ee()->session))
  243. {
  244. if ( ! isset($GLOBALS['solspace']['cache']['addon_builder']['addon'][$this->lower_name]))
  245. {
  246. $GLOBALS['solspace']['cache']['addon_builder']['addon'][$this->lower_name] = array();
  247. }
  248. $this->cache =& $GLOBALS['solspace']['cache']['addon_builder']['addon'][$this->lower_name];
  249. if ( ! isset($GLOBALS['solspace']['cache']['addon_builder']['global']) )
  250. {
  251. $GLOBALS['solspace']['cache']['addon_builder']['global'] = array();
  252. }
  253. $this->global_cache =& $GLOBALS['solspace']['cache']['addon_builder']['global'];
  254. }
  255. //sessions?
  256. else
  257. {
  258. //been here before?
  259. if ( ! isset(ee()->session->cache['solspace']['addon_builder']['addon'][$this->lower_name]))
  260. {
  261. //grab pre-session globals, and only unset the ones for this addon
  262. if ( isset($GLOBALS['solspace']['cache']['addon_builder']['addon'][$this->lower_name]))
  263. {
  264. ee()->session->cache['solspace']['addon_builder']['addon'][$this->lower_name] = $GLOBALS['solspace']['cache']['addon_builder']['addon'][$this->lower_name];
  265. //cleanup, isle 5
  266. unset($GLOBALS['solspace']['cache']['addon_builder']['addon'][$this->lower_name]);
  267. }
  268. else
  269. {
  270. ee()->session->cache['solspace']['addon_builder']['addon'][$this->lower_name] = array();
  271. }
  272. }
  273. //check for solspace-wide globals
  274. if ( ! isset(ee()->session->cache['solspace']['addon_builder']['global']) )
  275. {
  276. if (isset($GLOBALS['solspace']['cache']['addon_builder']['global']))
  277. {
  278. ee()->session->cache['solspace']['addon_builder']['global'] = $GLOBALS['solspace']['cache']['addon_builder']['global'];
  279. unset($GLOBALS['solspace']['cache']['addon_builder']['global']);
  280. }
  281. else
  282. {
  283. ee()->session->cache['solspace']['addon_builder']['global'] = array();
  284. }
  285. }
  286. $this->global_cache =& ee()->session->cache['solspace']['addon_builder']['global'];
  287. $this->cache =& ee()->session->cache['solspace']['addon_builder']['addon'][$this->lower_name];
  288. }
  289. //--------------------------------------------
  290. // Add-On Path
  291. //--------------------------------------------
  292. if (APP_VER < 2.0)
  293. {
  294. // Because of Bridge Magic with eval() and parents, we might have to go one or two levels up
  295. $parent_class = get_parent_class($this);
  296. $super_parent_class = get_parent_class($parent_class);
  297. if (($parent_class == 'Extension_builder_freeform' OR
  298. $super_parent_class == 'Extension_builder_freeform') AND
  299. is_dir(PATH_EXT.$this->lower_name.'/'))
  300. {
  301. $this->extension_name = $this->class_name;
  302. $this->addon_path = PATH_EXT . $this->lower_name.'/';
  303. }
  304. else
  305. {
  306. $this->addon_path = PATH_MOD . $this->lower_name . '/';
  307. }
  308. }
  309. else
  310. {
  311. $this->addon_path = PATH_THIRD . $this->lower_name . '/';
  312. }
  313. //--------------------------------------------
  314. // Language Override
  315. //--------------------------------------------
  316. if (isset(ee()->lang) AND is_object(ee()->lang))
  317. {
  318. ee()->lang->loadfile($this->lower_name);
  319. }
  320. //--------------------------------------------
  321. // Module Constants
  322. //--------------------------------------------
  323. if ( defined(strtoupper($this->lower_name).'_VERSION') == FALSE AND
  324. file_exists($this->addon_path.'constants.'.$this->lower_name.'.php'))
  325. {
  326. require_once $this->addon_path.'constants.'.$this->lower_name.'.php';
  327. }
  328. if (defined(strtoupper($this->lower_name).'_VERSION') !== FALSE)
  329. {
  330. $this->version = constant(strtoupper($this->lower_name).'_VERSION');
  331. }
  332. //--------------------------------------------
  333. // Data Object - Used Cached Version, if Available
  334. //--------------------------------------------
  335. if ( isset($this->cache['objects']['data']) AND
  336. is_object($this->cache['objects']['data']))
  337. {
  338. $this->data =& $this->cache['objects']['data'];
  339. }
  340. else
  341. {
  342. if ( file_exists($this->addon_path . 'data.' . $this->lower_name.'.php'))
  343. {
  344. $name = $this->class_name . '_data';
  345. if ( ! class_exists($name))
  346. {
  347. require_once $this->addon_path . 'data.' . $this->lower_name.'.php';
  348. }
  349. $this->data = new $name($this);
  350. $this->data->sc = $this->sc;
  351. }
  352. else
  353. {
  354. if ( ! class_exists('Addon_builder_data_freeform'))
  355. {
  356. require_once $this->aob_path . 'data.addon_builder.php';
  357. }
  358. $this->data = new Addon_builder_data_freeform($this);
  359. }
  360. $this->cache['objects']['data'] =& $this->data;
  361. }
  362. $this->data->parent_aob_instance =& $this;
  363. //--------------------------------------------
  364. // documentDOM_freeform instantiated, might move this.
  365. //--------------------------------------------
  366. if (REQ == 'CP' AND file_exists($this->aob_path . 'document_dom.php'))
  367. {
  368. if ( ! class_exists('documentDOM_freeform'))
  369. {
  370. require_once $this->aob_path . 'document_dom.php';
  371. }
  372. $this->document = new documentDOM_freeform();
  373. }
  374. //--------------------------------------------
  375. // Important Cached Vars - Used in Both Extensions and Modules
  376. //--------------------------------------------
  377. $this->cached_vars['XID_SECURE_HASH'] = $this->constants->XID_SECURE_HASH;
  378. $this->cached_vars['page_crumb'] = '';
  379. $this->cached_vars['page_title'] = '';
  380. $this->cached_vars['text_direction'] = 'ltr';
  381. $this->cached_vars['onload_events'] = '';
  382. $this->cached_vars['message'] = '';
  383. $this->cached_vars['caller'] =& $this;
  384. $this->cached_vars['theme_url'] = $this->sc->addon_theme_url;
  385. $this->cached_vars['addon_theme_url'] = $this->sc->addon_theme_url;
  386. //--------------------------------------------
  387. // Determine View Path for Add-On
  388. //--------------------------------------------
  389. if ( isset($this->cache['view_path']))
  390. {
  391. $this->view_path = $this->cache['view_path'];
  392. }
  393. else
  394. {
  395. $possible_paths = array();
  396. $this->theme = ee()->security->sanitize_filename($this->theme);
  397. if (APP_VER < 2.0)
  398. {
  399. if (trim($this->theme, '/') != '')
  400. {
  401. $possible_paths[] = $this->addon_path.'views/1.x/'.trim($this->theme, '/').'/';
  402. }
  403. $possible_paths[] = $this->addon_path.'views/1.x/default/';
  404. $possible_paths[] = $this->addon_path.'views/1.x/';
  405. }
  406. else
  407. {
  408. if (trim($this->theme, '/') != '')
  409. {
  410. $possible_paths[] = $this->addon_path.'views/2.x/'.trim($this->theme, '/').'/';
  411. }
  412. $possible_paths[] = $this->addon_path.'views/2.x/default/';
  413. $possible_paths[] = $this->addon_path.'views/2.x/';
  414. }
  415. if (trim($this->theme, '/') != '')
  416. {
  417. $possible_paths[] = $this->addon_path.'views/'.trim($this->theme, '/').'/';
  418. }
  419. $possible_paths[] = $this->addon_path.'views/default/';
  420. $possible_paths[] = $this->addon_path.'views/';
  421. foreach(array_unique($possible_paths) as $path)
  422. {
  423. if ( is_dir($path))
  424. {
  425. $this->view_path = $path;
  426. break;
  427. }
  428. }
  429. }
  430. }
  431. // END Addon_builder_freeform()
  432. // --------------------------------------------------------------------
  433. /**
  434. * Creates shortcuts for common changed items between versions.
  435. *
  436. * @access public
  437. * @return object
  438. */
  439. public function generate_shortcuts ()
  440. {
  441. $is2 = ! (APP_VER < 2.0);
  442. if (defined('URL_THIRD_THEMES'))
  443. {
  444. $theme_url = URL_THIRD_THEMES;
  445. }
  446. else
  447. {
  448. $theme_url = (
  449. rtrim(ee()->config->item('theme_folder_url'), '/') .
  450. '/' . ($is2 ? 'third_party/' : '')
  451. );
  452. }
  453. if (defined('PATH_THIRD_THEMES'))
  454. {
  455. $theme_path = PATH_THIRD_THEMES;
  456. }
  457. else
  458. {
  459. $theme_path = (
  460. rtrim(ee()->config->item('theme_folder_path'), '/') .
  461. '/' . ($is2 ? 'third_party/' : '')
  462. );
  463. }
  464. return (object) array(
  465. 'db' => (object) array(
  466. 'channel_name' => $is2 ? 'channel_name' : 'blog_name',
  467. 'channel_url' => $is2 ? 'channel_url' : 'blog_url',
  468. 'channel_title' => $is2 ? 'channel_title' : 'blog_title',
  469. 'channels' => $is2 ? 'exp_channels' : 'exp_weblogs',
  470. 'data' => $is2 ? 'exp_channel_data' : 'exp_weblog_data',
  471. 'channel_data' => $is2 ? 'exp_channel_data' : 'exp_weblog_data',
  472. 'fields' => $is2 ? 'exp_channel_fields' : 'exp_weblog_fields',
  473. 'channel_fields' => $is2 ? 'exp_channel_fields' : 'exp_weblog_fields',
  474. 'id' => $is2 ? 'channel_id' : 'weblog_id',
  475. 'channel_id' => $is2 ? 'channel_id' : 'weblog_id',
  476. 'member_groups' => $is2 ? 'exp_channel_member_groups' : 'exp_weblog_member_groups',
  477. 'channel_member_groups' => $is2 ? 'exp_channel_member_groups' : 'exp_weblog_member_groups',
  478. 'titles' => $is2 ? 'exp_channel_titles' : 'exp_weblog_titles',
  479. 'channel_titles' => $is2 ? 'exp_channel_titles' : 'exp_weblog_titles'
  480. ),
  481. 'channel' => $is2 ? 'channel' : 'weblog',
  482. 'channels' => $is2 ? 'channels' : 'weblogs',
  483. 'theme_url' => $theme_url,
  484. 'theme_path' => $theme_path,
  485. 'addon_theme_url' => $theme_url . $this->lower_name . '/',
  486. 'addon_theme_path' => $theme_path . $this->lower_name . '/',
  487. );
  488. }
  489. /* END generate_shortcuts() */
  490. // --------------------------------------------------------------------
  491. /**
  492. * Instantiates an Object and Returns It
  493. *
  494. * Tired of having the same code duplicate everywhere for calling Typography, Email, Et Cetera.
  495. *
  496. * @access public
  497. * @return object|NULL
  498. */
  499. public function instantiate ( $name , $variables = array())
  500. {
  501. $lower_name = strtolower($name);
  502. $class_name = ucfirst($lower_name);
  503. // I am embarrassed by this exception -Paul
  504. if ($lower_name == 'email')
  505. {
  506. $class_name == 'EEmail';
  507. }
  508. if ( ! class_exists($class_name))
  509. {
  510. // We only load classes from the CP or CORE directories
  511. if (file_exists(PATH_CP.'core.'.$lower_name.EXT))
  512. {
  513. $location = PATH_CP.'core.'.$lower_name.EXT;
  514. }
  515. elseif (file_exists(PATH_CORE.'cp.'.$lower_name.EXT))
  516. {
  517. $location = PATH_CORE.'cp.'.$lower_name.EXT;
  518. }
  519. else
  520. {
  521. return NULL;
  522. }
  523. require_once $location;
  524. }
  525. $NEW = new $class_name();
  526. foreach($variables AS $key => $value)
  527. {
  528. $NEW->$key = $value;
  529. }
  530. return $NEW;
  531. }
  532. /* END instantiate() */
  533. // --------------------------------------------------------------------
  534. /**
  535. * Module's Action Object
  536. *
  537. * intantiates the actions object and sticks it to $this->actions
  538. *
  539. * @access public
  540. * @return object
  541. */
  542. public function actions ()
  543. {
  544. if ( ! is_object($this->actions))
  545. {
  546. $name = $this->class_name.'_actions';
  547. if ( ! class_exists($name))
  548. {
  549. require_once $this->addon_path . 'act.'.$this->lower_name.'.php';
  550. }
  551. $this->actions = new $name();
  552. $this->actions->data =& $this->data;
  553. }
  554. return $this->actions;
  555. }
  556. // END actions()
  557. // --------------------------------------------------------------------
  558. /**
  559. * Database Version
  560. *
  561. * Returns the version of the module in the database
  562. *
  563. * @access public
  564. * @param bool ignore all caches and get version from database
  565. * @return string
  566. */
  567. public function database_version ($ignore_cache = FALSE)
  568. {
  569. if ( ! $ignore_cache AND
  570. isset($this->cache['database_version']))
  571. {
  572. return $this->cache['database_version'];
  573. }
  574. // ----------------------------------------
  575. // Use Template object variable, if available
  576. // ----------------------------------------
  577. //EE1
  578. if ( ! $ignore_cache AND
  579. APP_VER < 2.0 AND
  580. isset($GLOBALS['TMPL']) AND
  581. is_object($GLOBALS['TMPL']) AND
  582. count($GLOBALS['TMPL']->module_data) > 0)
  583. {
  584. if ( ! isset($GLOBALS['TMPL']->module_data[$this->class_name]))
  585. {
  586. $this->cache['database_version'] = FALSE;
  587. }
  588. else
  589. {
  590. $this->cache['database_version'] = $GLOBALS['TMPL']->module_data[$this->class_name]['version'];
  591. }
  592. }
  593. //EE2
  594. elseif ( ! $ignore_cache AND
  595. APP_VER >= 2.0 AND
  596. isset(ee()->TMPL) AND
  597. is_object(ee()->TMPL) AND
  598. count(ee()->TMPL->module_data) > 0)
  599. {
  600. if ( ! isset(ee()->TMPL->module_data[$this->class_name]))
  601. {
  602. $this->cache['database_version'] = FALSE;
  603. }
  604. else
  605. {
  606. $this->cache['database_version'] = ee()->TMPL->module_data[$this->class_name]['version'];
  607. }
  608. }
  609. //global cache
  610. elseif ( ! $ignore_cache AND
  611. isset($this->global_cache['module_data']) AND
  612. isset($this->global_cache['module_data'][$this->lower_name]['database_version']))
  613. {
  614. $this->cache['database_version'] = $this->global_cache['module_data'][$this->lower_name]['database_version'];
  615. }
  616. //fill global with last resort
  617. else
  618. {
  619. // ----------------------------------------
  620. // Retrieve all Module Versions from the Database
  621. // - By retrieving all of them at once,
  622. // we can limit it to a max of one query per
  623. // page load for all Bridge Add-Ons
  624. // ----------------------------------------
  625. $query = $this->cacheless_query(
  626. "SELECT module_version, module_name
  627. FROM exp_modules"
  628. );
  629. foreach($query->result_array() as $row)
  630. {
  631. if ( isset(ee()->session) AND is_object(ee()->session))
  632. {
  633. $this->global_cache['module_data'][strtolower($row['module_name'])]['database_version'] = $row['module_version'];
  634. }
  635. if ($this->class_name == $row['module_name'])
  636. {
  637. $this->cache['database_version'] = $row['module_version'];
  638. }
  639. }
  640. }
  641. //did get anything?
  642. return isset($this->cache['database_version']) ? $this->cache['database_version'] : FALSE;
  643. }
  644. // END database_version()
  645. // --------------------------------------------------------------------
  646. /**
  647. * Find and return preference
  648. *
  649. * Any number of possible arguments, although typically I expect there will be only one or two
  650. *
  651. * @access public
  652. * @param string Preference to retrieve
  653. * @return null|string If preference does not exist, NULL is returned, else the value
  654. */
  655. public function preference ()
  656. {
  657. $s = func_num_args();
  658. if ($s == 0)
  659. {
  660. return NULL;
  661. }
  662. //--------------------------------------------
  663. // Fetch Module Preferences
  664. //--------------------------------------------
  665. if (count($this->module_preferences) == 0 AND $this->database_version() !== FALSE)
  666. {
  667. if ( method_exists($this->actions(), 'module_preferences'))
  668. {
  669. $this->module_preferences = $this->actions()->module_preferences();
  670. }
  671. elseif ( method_exists($this->data, 'get_module_preferences'))
  672. {
  673. $this->module_preferences = $this->data->get_module_preferences();
  674. }
  675. else
  676. {
  677. return NULL;
  678. }
  679. }
  680. //--------------------------------------------
  681. // Find Our Value, If It Exists
  682. //--------------------------------------------
  683. $value = (isset($this->module_preferences[func_get_arg(0)])) ?
  684. $this->module_preferences[func_get_arg(0)] : NULL;
  685. for($i = 1; $i < $s; ++$i)
  686. {
  687. if ( ! isset($value[func_get_arg($i)]))
  688. {
  689. return NULL;
  690. }
  691. $value = $value[func_get_arg($i)];
  692. }
  693. return $value;
  694. }
  695. // END preference()
  696. // --------------------------------------------------------------------
  697. /**
  698. * Checks to see if extensions are allowed
  699. *
  700. *
  701. * @access public
  702. * @return bool Whether the extensions are allowed
  703. */
  704. public function extensions_allowed ()
  705. {
  706. return $this->check_yes(ee()->config->item('allow_extensions'));
  707. }
  708. //END extensions_allowed
  709. // --------------------------------------------------------------------
  710. /**
  711. * Homegrown Version of Version Compare
  712. *
  713. * Compared two versions in the form of 1.1.1.d12 <= 1.2.3.f0
  714. *
  715. * @access public
  716. * @param string First Version
  717. * @param string Operator for Comparison
  718. * @param string Second Version
  719. * @return bool Whether the comparison is TRUE or FALSE
  720. */
  721. public function version_compare ($v1, $operator, $v2)
  722. {
  723. // Allowed operators
  724. if ( ! in_array($operator, array('>', '<', '>=', '<=', '==', '!=')))
  725. {
  726. trigger_error("Invalid Operator in Add-On Library - Version Compare", E_USER_WARNING);
  727. return FALSE;
  728. }
  729. // Normalize and Fix Invalid Values
  730. foreach(array('v1', 'v2') as $var)
  731. {
  732. $x = array_slice(preg_split("/\./", trim($$var), -1, PREG_SPLIT_NO_EMPTY), 0, 4);
  733. for($i=0; $i < 4; $i++)
  734. {
  735. if ( ! isset($x[$i]))
  736. {
  737. $x[$i] = ($i == 3) ? 'f0' : '0';
  738. }
  739. elseif ($i < 3 AND ctype_digit($x[$i]) == FALSE)
  740. {
  741. $x[$i] = '0';
  742. }
  743. elseif($i == 3 AND ! preg_match("/^[abdf]{1}[0-9]+$/", $x[$i]))
  744. {
  745. $x[$i] = 'f0';
  746. }
  747. // Set up for PHP's version_compare
  748. if ($i == 3)
  749. {
  750. $letter = substr($x[3], 0, 1);
  751. $sans_letter = substr($x[3], 1);
  752. if ($letter == 'd')
  753. {
  754. $letter = 'dev';
  755. }
  756. elseif($letter == 'f')
  757. {
  758. $letter = 'RC';
  759. }
  760. $x[3] = $letter.'.'.$sans_letter;
  761. }
  762. }
  763. $$var = implode('.', $x);
  764. }
  765. // echo $v1.' - '.$v2;
  766. //this is a php built in function,
  767. //self::version_compare is just prep work
  768. return version_compare($v1, $v2, $operator);
  769. }
  770. // END version_compare()
  771. // --------------------------------------------------------------------
  772. /**
  773. * ExpressionEngine CP View Request
  774. *
  775. * Just like a typical view request but we do a few EE CP related things too
  776. *
  777. * @access public
  778. * @param array
  779. * @return void
  780. */
  781. public function ee_cp_view ($view)
  782. {
  783. //--------------------------------------------
  784. // Build Crumbs!
  785. //--------------------------------------------
  786. $this->build_crumbs();
  787. $this->build_right_links();
  788. //--------------------------------------------
  789. // EE 1.x Code for Calling Certain CP Hooks
  790. //--------------------------------------------
  791. if (APP_VER < 2.0)
  792. {
  793. // -------------------------------------------
  794. // 'show_full_control_panel_start' hook.
  795. // - Full Control over CP
  796. // - Modify any $DSP class variable (JS, headers, etc.)
  797. // - Override any $DSP method and use their own
  798. //
  799. $edata = ee()->extensions->call('show_full_control_panel_start');
  800. if (ee()->extensions->end_script === TRUE) return;
  801. //
  802. // -------------------------------------------
  803. }
  804. //--------------------------------------------
  805. // Load View Path, Call View File
  806. //--------------------------------------------
  807. $output = $this->view($view, array(), TRUE);
  808. //--------------------------------------------
  809. // EE 1.x Code for Calling Certain CP Hooks
  810. //--------------------------------------------
  811. if (APP_VER < 2.0)
  812. {
  813. // -------------------------------------------
  814. // 'show_full_control_panel_end' hook.
  815. // - Rewrite CP's HTML
  816. // - Find/Replace Stuff, etc.
  817. //
  818. if (ee()->extensions->active_hook('show_full_control_panel_end') === TRUE)
  819. {
  820. $output = ee()->extensions->call('show_full_control_panel_end', $output);
  821. if (ee()->extensions->end_script === TRUE) return;
  822. }
  823. //
  824. // -------------------------------------------
  825. }
  826. //--------------------------------------------
  827. // EE 1.x, We Add Secure Form Hashes and Output Content to Browser
  828. //--------------------------------------------
  829. if (APP_VER < 2.0)
  830. {
  831. if (stristr($output, '{XID_HASH}'))
  832. {
  833. $output = ee()->functions->add_form_security_hash($output);
  834. }
  835. ee()->output->_display(ee()->cp->secure_hash($output));
  836. exit;
  837. }
  838. //--------------------------------------------
  839. // In EE 2.x, we return the Output and Let EE Continue Building the CP
  840. //--------------------------------------------
  841. return $output;
  842. }
  843. // END ee_cp_view()
  844. // --------------------------------------------------------------------
  845. /**
  846. * Javascript/CSS File View Request
  847. *
  848. * Outputs a View file as if it were a Javascript file
  849. *
  850. * @access public
  851. * @param array
  852. * @return void
  853. */
  854. public function file_view ($view, $modification_time = '')
  855. {
  856. //--------------------------------------------
  857. // Auto-detect the Type
  858. //--------------------------------------------
  859. if (preg_match("/\.([cjs]{2,3})$/i", $view, $match) AND
  860. in_array($match[1], array('css', 'js')))
  861. {
  862. switch($match[1])
  863. {
  864. case 'css' :
  865. $type = 'css';
  866. break;
  867. case 'js' :
  868. $type = 'javascript';
  869. break;
  870. }
  871. }
  872. else
  873. {
  874. exit;
  875. }
  876. //--------------------------------------------
  877. // Load View Path, Call View File
  878. //--------------------------------------------
  879. $output = $this->view($view, array(), TRUE);
  880. //--------------------------------------------
  881. // EE 1.x, We Add Secure Form Hashes and Output Content to Browser
  882. //--------------------------------------------
  883. if ($type == 'javascript' AND stristr($output, '{XID_SECURE_HASH}'))
  884. {
  885. $output = str_replace('{XID_SECURE_HASH}', '{XID_HASH}', $output);
  886. }
  887. if ($type == 'javascript')
  888. {
  889. $output = ee()->functions->add_form_security_hash($output);
  890. }
  891. //----------------------------------------
  892. // Generate HTTP headers
  893. //----------------------------------------
  894. if (ee()->config->item('send_headers') == 'y')
  895. {
  896. $ext = pathinfo($view, PATHINFO_EXTENSION);
  897. $file = ($ext == '') ? $view.EXT : $view;
  898. $path = $this->view_path.$file;
  899. $max_age = 5184000;
  900. $modification_time = ($modification_time != '') ? $modification_time : filemtime($path);
  901. $modified_since = ee()->input->server('HTTP_IF_MODIFIED_SINCE');
  902. if ( ! ctype_digit($modification_time))
  903. {
  904. $modification_time = filemtime($path);
  905. }
  906. // Remove anything after the semicolon
  907. if ($pos = strrpos($modified_since, ';') !== FALSE)
  908. {
  909. $modified_since = substr($modified_since, 0, $pos);
  910. }
  911. // Send a custom ETag to maintain a useful cache in
  912. // load-balanced environments
  913. header("ETag: ".md5($modification_time.$path));
  914. // If the file is in the client cache, we'll
  915. // send a 304 and be done with it.
  916. if ($modified_since AND (strtotime($modified_since) == $modification_time))
  917. {
  918. ee()->output->set_status_header(304);
  919. exit;
  920. }
  921. ee()->output->set_status_header(200);
  922. @header("Cache-Control: max-age={$max_age}, must-revalidate");
  923. @header('Vary: Accept-Encoding');
  924. @header('Last-Modified: '.gmdate('D, d M Y H:i:s', $modification_time).' GMT');
  925. @header('Expires: '.gmdate('D, d M Y H:i:s', time() + $max_age).' GMT');
  926. @header('Content-Length: '.strlen($output));
  927. }
  928. //----------------------------------------
  929. // Send JavaScript/CSS Header and Output
  930. //----------------------------------------
  931. @header("Content-type: text/".$type);
  932. exit($output);
  933. }
  934. // END ee_cp_view()
  935. // --------------------------------------------------------------------
  936. /**
  937. * View File Loader
  938. *
  939. * Takes a file from the filesystem and loads it so that we can parse PHP within it just
  940. *
  941. *
  942. * @access public
  943. * @param string $view - The view file to be located
  944. * @param array $vars - Array of data variables to be parsed in the file system
  945. * @param bool $return - Return file as string or put into buffer
  946. * @param string $path - Override path for the file rather than using $this->view_path
  947. * @return string
  948. */
  949. public function view ($view, $vars = array(), $return = FALSE, $path='')
  950. {
  951. //have to keep this for legacy footers
  952. global $DSP, $LANG, $PREFS;
  953. //--------------------------------------------
  954. // Determine File Name and Extension for Requested File
  955. //--------------------------------------------
  956. if ($path == '')
  957. {
  958. $ext = pathinfo($view, PATHINFO_EXTENSION);
  959. $file = ($ext == '') ? $view.EXT : $view;
  960. $path = $this->view_path.$file;
  961. }
  962. else
  963. {
  964. $x = explode('/', $path);
  965. $file = end($x);
  966. }
  967. //--------------------------------------------
  968. // Make Sure the File Actually Exists
  969. //--------------------------------------------
  970. if ( ! file_exists($path))
  971. {
  972. trigger_error("Invalid View File Request of '".$path."'");
  973. return FALSE;
  974. }
  975. // All variables sent to the function are cached, which allows us to use them
  976. // within embedded view files within this file.
  977. if (is_array($vars))
  978. {
  979. $this->cached_vars = array_merge($this->cached_vars, $vars);
  980. }
  981. extract($this->cached_vars, EXTR_PREFIX_SAME, 'var_');
  982. //print_r($this->cached_vars);
  983. //--------------------------------------------
  984. // Buffer Output
  985. // - Increases Speed
  986. // - Allows Views to be Nested Within Views
  987. //--------------------------------------------
  988. ob_start();
  989. //--------------------------------------------
  990. // Load File and Rewrite Short Tags
  991. //--------------------------------------------
  992. $rewrite_short_tags = TRUE; // Hard coded setting for now...
  993. if ((bool) @ini_get('short_open_tag') === FALSE AND $rewrite_short_tags == TRUE)
  994. {
  995. echo eval('?'.'>'.preg_replace("/;*\s*\?".">/", "; ?".">",
  996. str_replace('<'.'?=', '<?php echo ',
  997. file_get_contents($path))).'<'.'?php ');
  998. }
  999. else
  1000. {
  1001. include($path);
  1002. }
  1003. //--------------------------------------------
  1004. // Return Parsed File as String
  1005. //--------------------------------------------
  1006. if ($return === TRUE)
  1007. {
  1008. $buffer = ob_get_contents();
  1009. @ob_end_clean();
  1010. return $buffer;
  1011. }
  1012. //--------------------------------------------
  1013. // Flush Buffer
  1014. //--------------------------------------------
  1015. if (ob_get_level() > $this->ob_level + 1)
  1016. {
  1017. ob_end_flush();
  1018. }
  1019. else
  1020. {
  1021. $buffer = ob_get_contents();
  1022. @ob_end_clean();
  1023. return $buffer;
  1024. }
  1025. }
  1026. // END view()
  1027. // --------------------------------------------------------------------
  1028. /**
  1029. * Fetch the CP Stylesheet
  1030. *
  1031. * Had to build this because it was not abstracted well enough for us to simply call EE methods
  1032. *
  1033. * @access public
  1034. * @param array An array of find/replace values to perform in the stylesheet
  1035. * @return string
  1036. */
  1037. public function fetch_stylesheet ()
  1038. {
  1039. // Change CSS on the click so it works like the hover until they unclick?
  1040. $ptb = ee()->config->item('publish_tab_behavior');
  1041. $stb = ee()->config->item('sites_tab_behavior');
  1042. $tab_behaviors = array(
  1043. 'publish_tab_selector' => ($ptb == 'hover') ? 'hover' : 'active',
  1044. 'publish_tab_display' => ($ptb == 'none') ? '' : 'display:block; visibility: visible;',
  1045. 'publish_tab_ul_display' => ($ptb == 'none') ? '' : 'display:none;',
  1046. 'sites_tab_selector' => ($stb == 'hover') ? 'hover' : 'active',
  1047. 'sites_tab_display' => ($stb == 'none') ? '' : 'display:block; visibility: visible;',
  1048. 'sites_tab_ul_display' => ($stb == 'none') ? '' : 'display:none;'
  1049. );
  1050. $stylesheet = $GLOBALS['DSP']->fetch_stylesheet();
  1051. foreach ($tab_behaviors as $key => $val)
  1052. {
  1053. $stylesheet = str_replace(LD.$key.RD, $val, $stylesheet);
  1054. }
  1055. return $stylesheet;
  1056. }
  1057. // END fetch_stylesheet()
  1058. // --------------------------------------------------------------------
  1059. /**
  1060. * Add Array of Breadcrumbs for a Page
  1061. *
  1062. * @access public
  1063. * @param array
  1064. * @return null
  1065. */
  1066. public function add_crumbs ($array)
  1067. {
  1068. if ( is_array($array))
  1069. {
  1070. foreach($array as $value)
  1071. {
  1072. if ( is_array($value))
  1073. {
  1074. $this->add_crumb($value[0], $value[1]);
  1075. }
  1076. else
  1077. {
  1078. $this->add_crumb($value);
  1079. }
  1080. }
  1081. }
  1082. }
  1083. /* END add_crumbs */
  1084. // --------------------------------------------------------------------
  1085. /**
  1086. * Add Single Crumb to List of Breadcrumbs
  1087. *
  1088. * @access public
  1089. * @param string Text of breacrumb
  1090. * @param string Link, if any for breadcrumb
  1091. * @return null
  1092. */
  1093. public function add_crumb ($text, $link='')
  1094. {
  1095. $this->crumbs[] = ($link == '') ? array($text) : array($text, $link);
  1096. }
  1097. /* END add_crumb() */
  1098. // --------------------------------------------------------------------
  1099. /**
  1100. * Takes Our Crumbs and Builds them into the Breadcrumb List
  1101. *
  1102. * @access public
  1103. * @return null
  1104. */
  1105. public function build_crumbs ()
  1106. {
  1107. global $DSP, $OUT;
  1108. if ( is_string($this->crumbs))
  1109. {
  1110. if (APP_VER < 2.0) $DSP->title = $this->crumbs;
  1111. $this->cached_vars['page_crumb'] = $this->crumbs;
  1112. $this->cached_vars['page_title'] = $this->crumbs;
  1113. return;
  1114. }
  1115. if (APP_VER < 2.0) $DSP->crumb = '';
  1116. $this->cached_vars['page_crumb'] = '';
  1117. $this->cached_vars['page_title'] = '';
  1118. $item = (count($this->crumbs) == 1) ? TRUE : FALSE;
  1119. ee()->load->helper('url');
  1120. foreach($this->crumbs as $key => $value)
  1121. {
  1122. if (is_array($value))
  1123. {
  1124. $name = $value[0];
  1125. if (isset($value[1]))
  1126. {
  1127. $name = "<a href='{$value[1]}'>{$value[0]}</a>";
  1128. }
  1129. $this->cached_vars['page_title'] = $value[0];
  1130. }
  1131. else
  1132. {
  1133. $name = $value;
  1134. $this->cached_vars['page_title'] = $value;
  1135. }
  1136. if (APP_VER < 2.0)
  1137. {
  1138. if ($item === FALSE)
  1139. {
  1140. $this->cached_vars['page_crumb'] .= $name;
  1141. $item = TRUE;
  1142. }
  1143. else
  1144. {
  1145. $this->cached_vars['page_crumb'] .= $DSP->crumb_item($name);
  1146. }
  1147. }
  1148. else
  1149. {
  1150. if (is_array($value) AND isset($value[1]))
  1151. {
  1152. ee()->cp->set_breadcrumb($value[1], $value[0]);
  1153. }
  1154. }
  1155. }
  1156. /** --------------------------------------------
  1157. /** 2.0 Specific Code
  1158. /** --------------------------------------------*/
  1159. $this->cached_vars['cp_page_title'] = $this->cached_vars['page_title'];
  1160. if (APP_VER >= 2.0)
  1161. {
  1162. ee()->cp->set_variable('cp_page_title', $this->cached_vars['cp_page_title'] );
  1163. }
  1164. /** --------------------------------------------
  1165. /** 1.x Breadcrumb View Variable
  1166. /** --------------------------------------------*/
  1167. if (APP_VER < 2.0) $DSP->crumb = $this->cached_vars['page_crumb'];
  1168. }
  1169. /* END build_crumbs() */
  1170. // --------------------------------------------------------------------
  1171. /**
  1172. * Field Output Prep for arrays and strings
  1173. *
  1174. *
  1175. * @access public
  1176. * @param string|array The item that needs to be prepped for output
  1177. * @return string|array
  1178. */
  1179. function output ($item)
  1180. {
  1181. if (is_array($item))
  1182. {
  1183. $array = array();
  1184. foreach($item as $key => $value)
  1185. {
  1186. $array[$this->output($key)] = $this->output($value);
  1187. }
  1188. return $array;
  1189. }
  1190. elseif(is_string($item))
  1191. {
  1192. return htmlspecialchars($item, ENT_QUOTES);
  1193. }
  1194. else
  1195. {
  1196. return $item;
  1197. }
  1198. }
  1199. /* END output() */
  1200. // --------------------------------------------------------------------
  1201. /**
  1202. * Cycles Between Values
  1203. *
  1204. * Takes a list of arguments and cycles through them on each call
  1205. *
  1206. * @access public
  1207. * @param string|array The items that need to be cycled through
  1208. * @return string|array
  1209. */
  1210. function cycle ($items)
  1211. {
  1212. if ( ! is_array($items))
  1213. {
  1214. $items = func_get_args();
  1215. }
  1216. $hash = md5(implode('|', $items));
  1217. if ( ! isset($this->switches[$hash]) OR ! isset($items[$this->switches[$hash] + 1]))
  1218. {
  1219. $this->switches[$hash] = 0;
  1220. }
  1221. else
  1222. {
  1223. $this->switches[$hash]++;
  1224. }
  1225. return $items[$this->switches[$hash]];
  1226. }
  1227. /* END cycle() */
  1228. // --------------------------------------------------------------------
  1229. /**
  1230. * Order Array
  1231. *
  1232. * Takes an array and reorders it based on the value of a key
  1233. *
  1234. * @access public
  1235. * @param array $array The array needing to be reordered
  1236. * @param string $key The key being used to reorder
  1237. * @param string $order The order for the values asc/desc
  1238. * @return array
  1239. */
  1240. function order_array ($array, $key, $order = 'desc')
  1241. {
  1242. // http://us2.php.net/manual/en/function.array-multisort.php
  1243. }
  1244. /* END order_array() */
  1245. // --------------------------------------------------------------------
  1246. /**
  1247. * Column Exists in DB Table
  1248. *
  1249. * @access public
  1250. * @param string $column The column whose existence we are looking for
  1251. * @param string $table In which table?
  1252. * @return array
  1253. */
  1254. public function column_exists ( $column, $table, $cache = TRUE )
  1255. {
  1256. if ($cache === TRUE AND isset($this->cache['column_exists'][$table][$column]))
  1257. {
  1258. return $this->cache['column_exists'][$table][$column];
  1259. }
  1260. /** ----------------------------------------
  1261. /** Check for columns in tags table
  1262. /** ----------------------------------------*/
  1263. $query = ee()->db->query( "DESCRIBE `".ee()->db->escape_str( $table )."` `".ee()->db->escape_str( $column )."`" );
  1264. if ( $query->num_rows > 0 )
  1265. {
  1266. return $this->cache['column_exists'][$table][$column] = TRUE;
  1267. }
  1268. return $this->cache['column_exists'][$table][$column] = FALSE;
  1269. }
  1270. /* END column_exists() */
  1271. // --------------------------------------------------------------------
  1272. /**
  1273. * Retrieve Remote File and Cache It
  1274. *
  1275. * @access public
  1276. * @param string $url - URL to be retrieved
  1277. * @param integer $cache_length - How long to cache the result, if successful retrieval
  1278. * @return bool Success or failure. Data result stored in $this->remote_data
  1279. */
  1280. public function retrieve_remote_file ($url, $cache_length = 24, $path='', $file='')
  1281. {
  1282. global $FNS;
  1283. $path = ($path == '') ? PATH_CACHE.'addon_builder/' : rtrim($path, '/').'/';
  1284. $file = ($file == '') ? md5($url).'.txt' : $file;
  1285. $file_path = $path.$file;
  1286. /** --------------------------------------------
  1287. /** Check for Cached File
  1288. /** --------------------------------------------*/
  1289. if ( ! file_exists($file_path) OR (time() - filemtime($file_path)) > (60 * 60 * round($cache_length)))
  1290. {
  1291. @unlink($file_path);
  1292. }
  1293. elseif (($this->remote_data = file_get_contents($file_path)) === FALSE)
  1294. {
  1295. @unlink($file_path);
  1296. }
  1297. else
  1298. {
  1299. return TRUE;
  1300. }
  1301. /** --------------------------------------------
  1302. /** Validate and Create Cache Directory
  1303. /** --------------------------------------------*/
  1304. if ( ! is_dir($path))
  1305. {
  1306. $dirs = explode('/', trim(ee()->functions->remove_double_slashes($path), '/'));
  1307. $path = '/';
  1308. foreach ($dirs as $dir)
  1309. {
  1310. if ( ! @is_dir($path.$dir))
  1311. {
  1312. if ( ! @mkdir($path.$dir, 0777))
  1313. {
  1314. $this->errors[] = 'Unable to Create Directory: '.$path.$dir;
  1315. return;
  1316. }
  1317. @chmod($path.$dir, 0777);
  1318. }
  1319. $path .= $dir.'/';
  1320. }
  1321. }
  1322. if ($this->is_really_writable($path) === FALSE)
  1323. {
  1324. $this->errors[] = 'Cache Directory is Not Writable: '.$path;
  1325. return FALSE;
  1326. }
  1327. /** --------------------------------------------
  1328. /** Retrieve Our URL
  1329. /** --------------------------------------------*/
  1330. $this->remote_data = $this->fetch_url($url);
  1331. if ($this->remote_data == '')
  1332. {
  1333. $this->errors[] = 'Unable to Retrieve URL: '.$url;
  1334. return FALSE;
  1335. }
  1336. /** --------------------------------------------
  1337. /** Write Cache File
  1338. /** --------------------------------------------*/
  1339. if ( ! $this->write_file($file_path, $this->remote_data))
  1340. {
  1341. $this->errors[] = 'Unable to Write File to Cache';
  1342. return FALSE;
  1343. }
  1344. return TRUE;
  1345. }
  1346. /* END retrieve_remote_file() */
  1347. // --------------------------------------------------------------------
  1348. /**
  1349. * Fetch the Data for a URL
  1350. *
  1351. * @access public
  1352. * @param string $url - The URI that we are fetching
  1353. * @param array $post - The POST array we are sending
  1354. * @param string|bool $username - Possible username required
  1355. * @param string|bool $password - Password to go with the username
  1356. * @return string
  1357. */
  1358. public function fetch_url ($url, $post = array(), $username = FALSE, $password = FALSE)
  1359. {
  1360. $data = '';
  1361. $user_agent = ini_get('user_agent');
  1362. if ( empty($user_agent))
  1363. {
  1364. $user_agent = $this->class_name.'/1.0';
  1365. }
  1366. /** --------------------------------------------
  1367. /** file_get_contents()
  1368. /** --------------------------------------------*/
  1369. if ((bool) @ini_get('allow_url_fopen') !== FALSE && empty($post) && $username == FALSE)
  1370. {
  1371. $opts = array('http' => array('header' => "User-Agent:".$user_agent."\r\n"),
  1372. 'https' => array('header' => "User-Agent:".$user_agent."\r\n"));
  1373. $context = stream_context_create($opts);
  1374. if ($data = @file_get_contents($url, FALSE, $context))
  1375. {
  1376. return $data;
  1377. }
  1378. }
  1379. /** --------------------------------------------
  1380. /** cURL
  1381. /** --------------------------------------------*/
  1382. if (function_exists('curl_init') === TRUE AND ($ch = @curl_init()) !== FALSE)
  1383. {
  1384. curl_setopt($ch, CURLOPT_URL, $url);
  1385. curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  1386. curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
  1387. // prevent a PHP warning on certain servers
  1388. if (! ini_get('safe_mode') AND ! ini_get('open_basedir'))
  1389. {
  1390. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
  1391. }
  1392. // Are we posting?
  1393. if ( ! empty( $post ) )
  1394. {
  1395. $str = '';
  1396. foreach ( $post as $key => $val )
  1397. {
  1398. $str .= urlencode( $key ) . "=" . urlencode( $val ) . "&";
  1399. }
  1400. $str = substr( $str, 0, -1 );
  1401. curl_setopt( $ch, CURLOPT_POST, TRUE );
  1402. curl_setopt( $ch, CURLOPT_POSTFIELDS, $str );
  1403. }
  1404. curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
  1405. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
  1406. curl_setopt($ch, CURLOPT_TIMEOUT, 15);
  1407. curl_setopt($ch, CURLOPT_HEADER, FALSE);
  1408. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
  1409. if ($username != FALSE)
  1410. {
  1411. curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
  1412. if (defined('CURLOPT_HTTPAUTH')) curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST);
  1413. }
  1414. $data = curl_exec($ch);
  1415. curl_close($ch);
  1416. if ($data !== FALSE)
  1417. {
  1418. return $data;
  1419. }
  1420. }
  1421. // --------------------------------------------
  1422. // fsockopen() - Last but only slightly least...
  1423. // --------------------------------------------
  1424. $parts = parse_url($url);
  1425. $host = $parts['host'];
  1426. $path = (!isset($parts['path'])) ? '/' : $parts['path'];
  1427. $port = ($parts['scheme'] == "https") ? '443' : '80';
  1428. $ssl = ($parts['scheme'] == "https") ? 'ssl://' : '';
  1429. if (isset($parts['query']) AND $parts['query'] != '')
  1430. {
  1431. $path .= '?'.$parts['query'];
  1432. }
  1433. $data = '';
  1434. $fp = @fsockopen($ssl.$host, $port, $error_num, $error_str, 7);
  1435. if (is_resource($fp))
  1436. {
  1437. $getpost = ( ! empty( $post ) ) ? 'POST ': 'GET ';
  1438. fputs($fp, $getpost.$path." HTTP/1.0\r\n" );
  1439. fputs($fp, "Host: ".$host . "\r\n" );
  1440. if ( ! empty( $post ) )
  1441. {
  1442. $str = '';
  1443. foreach ( $post as $key => $val )
  1444. {
  1445. $str .= urlencode( $key ) . "=" . urlencode( $val ) . "&";
  1446. }
  1447. $str = substr( $str, 0, -1 );
  1448. fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
  1449. fputs($fp, "Content-length: " . strlen( $str ) . "\r\n");
  1450. }
  1451. fputs($fp, "User-Agent: ".$user_agent."r\n");
  1452. if ($username != FALSE)
  1453. {
  1454. fputs ($fp, "Authorization: Basic ".base64_encode($username.':'.$password)."\r\n");
  1455. }
  1456. fputs($fp, "Connection: close\r\n\r\n");
  1457. if ( ! empty( $post ) )
  1458. {
  1459. fputs($fp, $str . "\r\n\r\n");
  1460. }
  1461. /* ------------------------------
  1462. /* This error suppression has to do with a PHP bug involving
  1463. /* SSL connections: http://bugs.php.net/bug.php?id=23220
  1464. /* ------------------------------*/
  1465. $old_level = error_reporting(0);
  1466. $headers = '';
  1467. while ( ! feof($fp))
  1468. {
  1469. $bit = fgets($fp, 128);
  1470. $headers .= $bit;
  1471. if(preg_match("/^\r?\n$/", $bit)) break;
  1472. }
  1473. while ( ! feof($fp))
  1474. {
  1475. $data .= fgets($fp, 128);
  1476. }
  1477. error_reporting($old_level);
  1478. fclose($fp);
  1479. }
  1480. return trim($data);
  1481. }
  1482. /* END fetch_url() */
  1483. // --------------------------------------------------------------------
  1484. /**
  1485. * Write File
  1486. *
  1487. * @access public
  1488. * @param $file Full location of final file
  1489. * @param $data Data to put into file
  1490. * @return bool
  1491. */
  1492. function write_file ($file, $data)
  1493. {
  1494. $temp_file = $file.'.tmp';
  1495. if ( ! file_exists($temp_file))
  1496. {
  1497. // Remove old cache file, prevents rename problem on Windows
  1498. // http://bugs.php.net/bug.php?id=44805
  1499. @unlink($file);
  1500. if (file_exists($file))
  1501. {
  1502. $this->errors[] = "Unable to Delete Old Cache File: ".$file;
  1503. return FALSE;
  1504. }
  1505. if ( ! $fp = @fopen($temp_file, 'wb'))
  1506. {
  1507. $this->errors[] = "Unable to Write Temporary Cache File: ".$temp_file;
  1508. return FALSE;
  1509. }
  1510. if ( ! flock($fp, LOCK_EX | LOCK_NB))
  1511. {
  1512. $this->errors[] = "Locking Error when Writing Cache File";
  1513. return FALSE;
  1514. }
  1515. fwrite($fp, $data);
  1516. flock($fp, LOCK_UN);
  1517. fclose($fp);
  1518. // Write, then rename...
  1519. @rename($temp_file, $file);
  1520. // Double check permissions
  1521. @chmod($file, 0777);
  1522. // Just in case the rename did not work
  1523. @unlink($temp_file);
  1524. }
  1525. return TRUE;
  1526. }
  1527. /* END write_file() */
  1528. // --------------------------------------------------------------------
  1529. /**
  1530. * Check that File is Really Writable, Even on Windows
  1531. *
  1532. * is_writable() returns TRUE on Windows servers when you really cannot write to the file
  1533. * as the OS reports to PHP as FALSE only if the read-only attribute is marked. Ugh!
  1534. *
  1535. * Oh, and there is some silly thing with
  1536. *
  1537. * @access public
  1538. * @param string $path - Path to be written to.
  1539. * @param bool $remove - If writing a file, remove it after testing?
  1540. * @return bool
  1541. */
  1542. public function is_really_writable ($file, $remove = FALSE)
  1543. {
  1544. // is_writable() returns TRUE on Windows servers
  1545. // when you really can't write to the file
  1546. // as the OS reports to PHP as FALSE only if the
  1547. // read-only attribute is marked. Ugh?
  1548. if (substr($file, -1) == '/' OR is_dir($file))
  1549. {
  1550. return self::is_really_writable(rtrim($file, '/').'/'.uniqid(mt_rand()), TRUE);
  1551. }
  1552. if (($fp = @fopen($file, 'ab')) === FALSE)
  1553. {
  1554. return FALSE;
  1555. }
  1556. else
  1557. {
  1558. if ($remove === TRUE)
  1559. {
  1560. @unlink($file);
  1561. }
  1562. fclose($fp);
  1563. return TRUE;
  1564. }
  1565. }
  1566. /* END is_really_writable() */
  1567. // --------------------------------------------------------------------
  1568. /**
  1569. * Check Captcha
  1570. *
  1571. * If Captcha is required by a module, we simply do all the work
  1572. *
  1573. * @access public
  1574. * @return bool
  1575. */
  1576. public function check_captcha ()
  1577. {
  1578. if ( ee()->config->item('captcha_require_members') == 'y' OR
  1579. (ee()->config->item('captcha_require_members') == 'n' AND
  1580. ee()->session->userdata['member_id'] == 0))
  1581. {
  1582. if ( empty($_POST['captcha']))
  1583. {
  1584. return FALSE;
  1585. }
  1586. else
  1587. {
  1588. $res = ee()->db->query(
  1589. "SELECT COUNT(*) AS count
  1590. FROM exp_captcha
  1591. WHERE word = '" . ee()->db->escape_str($_POST['captcha']) . "'
  1592. AND ip_address = '" . ee()->db->escape_str(ee()->input->ip_address()) . "'
  1593. AND date > UNIX_TIMESTAMP()-7200"
  1594. );
  1595. if ($res->row('count') == 0)
  1596. {
  1597. return FALSE;
  1598. }
  1599. ee()->db->query(
  1600. "DELETE FROM exp_captcha
  1601. WHERE (
  1602. word = '" . ee()->db->escape_str($_POST['captcha']) . "'
  1603. AND ip_address = '" . ee()->db->escape_str(ee()->input->ip_address()) . "'
  1604. )
  1605. OR date < UNIX_TIMESTAMP()-7200"
  1606. );
  1607. }
  1608. }
  1609. return TRUE;
  1610. }
  1611. // END check_captcha()
  1612. // --------------------------------------------------------------------
  1613. /**
  1614. * Check Secure Forms
  1615. *
  1616. * Checks to see if Secure Forms is enabled, and if so sees if the submitted hash is valid
  1617. *
  1618. * @access public
  1619. * @return bool
  1620. */
  1621. public function check_secure_forms ()
  1622. {
  1623. // ----------------------------------------
  1624. // Secure forms?
  1625. // ----------------------------------------
  1626. if ( ee()->config->item('secure_forms') == 'y' )
  1627. {
  1628. if ( ! isset($_POST['XID']) AND ! isset($_GET['XID']))
  1629. {
  1630. return FALSE;
  1631. }
  1632. $hash = (isset($_POST['XID'])) ? $_POST['XID'] : $_GET['XID'];
  1633. $query = ee()->db->query(
  1634. "SELECT COUNT(*) AS count
  1635. FROM exp_security_hashes
  1636. WHERE hash = '" . ee()->db->escape_str($hash) . "'
  1637. AND ip_address = '" . ee()->db->escape_str(ee()->input->ip_address()) . "'
  1638. AND date > UNIX_TIMESTAMP()-7200"
  1639. );
  1640. if ($query->row('count') == 0)
  1641. {
  1642. return FALSE;
  1643. }
  1644. ee()->db->query(
  1645. "DELETE FROM exp_security_hashes
  1646. WHERE (
  1647. hash = '" . ee()->db->escape_str($hash) . "'
  1648. AND ip_address = '" . ee()->db->escape_str(ee()->input->ip_address()) . "'
  1649. )
  1650. OR date < UNIX_TIMESTAMP()-7200"
  1651. );
  1652. }
  1653. // ----------------------------------------
  1654. // Return
  1655. // ----------------------------------------
  1656. return TRUE;
  1657. }
  1658. // END check_secure_forms()
  1659. // --------------------------------------------------------------------
  1660. //depricated. please instead include in your view headers.
  1661. //uncompressed is available in svn
  1662. //this is due to be moved into addon view folders
  1663. /**
  1664. * A Slightly More Flexible Magic Checkbox
  1665. *
  1666. * Toggles the checkbox based on clicking anywhere in the table row that contains the checkbox
  1667. * Also allows multiple master toggle checkboxes at the top and bottom of a table to de/select all checkboxes
  1668. * - give them a name="toggle_all_checkboxes" attribute
  1669. * - No longer need to add onclick="toggle(this);" attribute
  1670. * No longer do you have to give your <form> tag an id="target" attrbiute, you can specify your own ID:
  1671. * - <script type="text/javascript">create_magic_checkboxes('delete_cached_uris_form');</script>
  1672. * - Or, if you specify no ID, it will find every <table> in the document with a class of
  1673. * 'magic_checkbox_table' and create the magic checkboxes automatically
  1674. * Also, it fixes that annoying problem where it was very difficult to easily select text in a row.
  1675. *
  1676. *
  1677. * @access public
  1678. * @return string
  1679. */
  1680. function js_magic_checkboxes ()
  1681. {
  1682. return <<< EOT
  1683. <script type="text/javascript">
  1684. var lastCheckedBox="";
  1685. function create_magic_checkboxes(d){if(typeof d=="undefined"){var k=document.getElementsByTagName("table");for(d=0;d<k.length;d++)if(k[d].className.indexOf("magic_checkbox_table")>-1||k[d].className.indexOf("magicCheckboxTable")>-1)create_magic_checkboxes(k[d])}else{if(typeof d=="object")var l=d;else if(typeof d=="string"){if(!document.getElementById(d))return;l=document.getElementById(d)}else return;k=l.getElementsByTagName("tr");for(d=0;d<k.length;d++)for(var c=0;c<2;c++)for(var g=c==1?"th":"td",
  1686. h=k[d].getElementsByTagName(g),m=0;m<h.length;m++)h[m].onclick=function(e){e=e?e:window.event?window.event:"";var a=e.target||e.srcElement,i=a.tagName?a.tagName.toLowerCase():null;if(i==null){a=a.parentNode;i=a.tagName?a.tagName.toLowerCase():null}if(i!="a"&&i!=null){for(;a.tagName.toLowerCase()!="tr";){a=a.parentNode;if(a.tagName.toLowerCase()=="a")return}for(var f=a.getElementsByTagName(g),b=a.getElementsByTagName("input"),n=false,o=false,j=0;j<b.length;j++)if(b[j].type=="checkbox"){if(b[j].name==
  1687. "toggle_all_checkboxes")o=true;else n=b[j].id;break}if(!(n==false&&o==false))if(o==true){if(i=="input"){selectAllVal=b[j].checked?true:false;e=l.getElementsByTagName("tr");b=l.getElementsByTagName("input");for(j=0;j<b.length;j++)if(b[j].type=="checkbox")b[j].checked=selectAllVal;for(a=1;a<e.length;a++){f=e[a].getElementsByTagName(g);for(b=0;b<f.length;b++)f[b].className=selectAllVal==true?f[b].className.indexOf("tableCellOne")>-1?"tableCellOneHover":"tableCellTwoHover":f[b].className.indexOf("tableCellTwo")>
  1688. -1?"tableCellTwo":"tableCellOne"}}}else{if(i!="input")document.getElementById(n).checked=document.getElementById(n).checked?false:true;if(window.getSelection||document.selection&&document.selection.createRange){b=window.getSelection?window.getSelection().toString():document.selection.createRange().text;if(b!=""&&b.replace(/<\/?[^>]+(>|$)/g,"").replace(/\s*/g,"")=="")if(document.getSelection)window.getSelection().removeAllRanges();else document.selection?document.selection.empty():document.getElementById(n).focus()}for(b=
  1689. 0;b<f.length;b++)f[b].className=document.getElementById(n).checked==true?f[b].className.indexOf("tableCellTwo")>-1?"tableCellTwoHover":"tableCellOneHover":f[b].className.indexOf("tableCellOne")>-1?"tableCellOne":"tableCellTwo";e.shiftKey&&lastCheckedBox!=""&&shift_magic_checkbox(document.getElementById(n).checked,lastCheckedBox,a);lastCheckedBox=a}}}}}
  1690. function shift_magic_checkbox(d,k,l){var c=l.parentNode,g=c.tagName?c.tagName.toLowerCase():null;if(g==null){c=c.parentNode;g=c.tagName?c.tagName.toLowerCase():null}if(g!=null){for(;c.tagName.toLowerCase()!="table";)c=c.parentNode;c=c.getElementsByTagName("tr");g=false;for(var h=1;h<c.length;h++)if(!(g==false&&c[h]!=k&&c[h]!=l))for(var m=0;m<2;m++){var e=m==1?"th":"td";e=c[h].getElementsByTagName(e);for(var a=c[h].getElementsByTagName("input"),i=false,f=0;f<a.length;f++)if(a[f].type=="checkbox")i=
  1691. a[f].id;if(i==false||i=="")return;document.getElementById(i).checked=d;for(a=0;a<e.length;a++)e[a].className=d==true?e[a].className.indexOf("tableCellTwo")>-1?"tableCellTwoHover":"tableCellOneHover":e[a].className.indexOf("tableCellOne")>-1?"tableCellOne":"tableCellTwo";if(c[h]==k||c[h]==l){if(g==true)break;if(g==false)g=true}}}};
  1692. </script>
  1693. EOT;
  1694. }
  1695. /* END js_magic_checkboxes() */
  1696. // --------------------------------------------------------------------
  1697. /**
  1698. * Balance a URI
  1699. *
  1700. * @access public
  1701. * @param string $uri
  1702. * @return array
  1703. */
  1704. public function balance_uri ( $uri )
  1705. {
  1706. $uri = '/'.trim($uri, '/').'/';
  1707. if ($uri == '//' OR $uri == '')
  1708. {
  1709. $uri = '/';
  1710. }
  1711. return $uri;
  1712. }
  1713. /* END balance_uri() */
  1714. // --------------------------------------------------------------------
  1715. /**
  1716. * Fetch Themes for a path
  1717. *
  1718. * @access public
  1719. * @param string $path - Absolute server path to theme directory
  1720. * @return array
  1721. */
  1722. public function fetch_themes ($path)
  1723. {
  1724. $themes = array();
  1725. if ($fp = @opendir($path))
  1726. {
  1727. while (false !== ($file = readdir($fp)))
  1728. {
  1729. if (is_dir($path.$file) AND substr($file, 0, 1) != '.')
  1730. {
  1731. $themes[] = $file;
  1732. }
  1733. }
  1734. closedir($fp);
  1735. }
  1736. sort($themes);
  1737. return $themes;
  1738. }
  1739. /* END fetch_themes() */
  1740. // --------------------------------------------------------------------
  1741. /**
  1742. * Allowed Group
  1743. *
  1744. * Member access validation
  1745. *
  1746. * @access public
  1747. * @param string
  1748. * @return bool
  1749. */
  1750. public function allowed_group ($which = '')
  1751. {
  1752. if ( is_object(ee()->cp))
  1753. {
  1754. return ee()->cp->allowed_group($which);
  1755. }
  1756. else
  1757. {
  1758. return ee()->display->allowed_group($which);
  1759. }
  1760. }
  1761. /* END allowed_group() */
  1762. // --------------------------------------------------------------------
  1763. /**
  1764. * Global Error Message Routine
  1765. *
  1766. * @access public
  1767. * @param string
  1768. * @return bool
  1769. */
  1770. public function show_error ($which = '')
  1771. {
  1772. if ( function_exists('show_error'))
  1773. {
  1774. show_error($which);
  1775. }
  1776. else
  1777. {
  1778. ee()->display->error_message($which);
  1779. }
  1780. }
  1781. /* END error_message() */
  1782. // --------------------------------------------------------------------
  1783. /**
  1784. * Check if Submitted String is a Yes value
  1785. *
  1786. * If the value is 'y', 'yes', 'true', or 'on', then returns TRUE, otherwise FALSE
  1787. *
  1788. * @access public
  1789. * @param string
  1790. * @return bool
  1791. */
  1792. function check_yes ($which)
  1793. {
  1794. if (is_string($which))
  1795. {
  1796. $which = strtolower(trim($which));
  1797. }
  1798. return in_array($which, array('yes', 'y', 'true', 'on'), TRUE);
  1799. }
  1800. /* END check_yes() */
  1801. // --------------------------------------------------------------------
  1802. /**
  1803. * Check if Submitted String is a No value
  1804. *
  1805. * If the value is 'n', 'no', 'false', or 'off', then returns TRUE, otherwise FALSE
  1806. *
  1807. * @access public
  1808. * @param string
  1809. * @return bool
  1810. */
  1811. function check_no ($which)
  1812. {
  1813. if (is_string($which))
  1814. {
  1815. $which = strtolower(trim($which));
  1816. }
  1817. return in_array($which, array('no', 'n', 'false', 'off'), TRUE);
  1818. }
  1819. /* END check_yes() */
  1820. // --------------------------------------------------------------------
  1821. /**
  1822. * json_encode
  1823. *
  1824. * @access public
  1825. * @param object
  1826. * @return string
  1827. */
  1828. public function json_encode ($data)
  1829. {
  1830. if (function_exists('json_encode'))
  1831. {
  1832. return json_encode($data);
  1833. }
  1834. if ( ! class_exists('Services_JSON'))
  1835. {
  1836. require_once $this->aob_path . 'json.php';
  1837. }
  1838. if ( ! is_object($this->json))
  1839. {
  1840. $this->json = new Services_JSON();
  1841. }
  1842. return $this->json->encode($data);
  1843. }
  1844. /* END json_encode() */
  1845. // --------------------------------------------------------------------
  1846. /**
  1847. * json_decode
  1848. *
  1849. * @access public
  1850. * @param string
  1851. * @param bool $associative - By default JSON decode returns object, this forces an array
  1852. * @return object
  1853. */
  1854. public function json_decode ($data, $associative = FALSE)
  1855. {
  1856. if (function_exists('json_decode'))
  1857. {
  1858. return json_decode($data, $associative);
  1859. }
  1860. if ( ! class_exists('Services_JSON'))
  1861. {
  1862. require_once $this->aob_path . 'json.php';
  1863. }
  1864. if ( $associative == TRUE)
  1865. {
  1866. if ( ! is_object($this->json_array))
  1867. {
  1868. $this->json_array = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
  1869. }
  1870. return $this->json_array->decode($data);
  1871. }
  1872. else
  1873. {
  1874. if ( ! is_object($this->json))
  1875. {
  1876. $this->json = new Services_JSON();
  1877. }
  1878. return $this->json->decode($data);
  1879. }
  1880. }
  1881. // END json_decode()
  1882. // --------------------------------------------------------------------
  1883. /**
  1884. * Pagination for all versions front-end and back
  1885. *
  1886. * * = optional
  1887. * $input_data = array(
  1888. * 'sql' => '',
  1889. * 'total_results' => '',
  1890. * *'url_suffix' => '',
  1891. * 'tagdata' => ee()->TMPL->tagdata,
  1892. * 'limit' => '',
  1893. * *'offset' => ee()->TMPL->fetch_param('offset'),
  1894. * *'query_string_segment' => 'P',
  1895. * 'uri_string' => ee()->uri->uri_string,
  1896. * *'current_page' => 0
  1897. * *'pagination_config' => array()
  1898. * );
  1899. *
  1900. * @access public
  1901. * @param array
  1902. * @return array
  1903. */
  1904. public function universal_pagination ( $input_data )
  1905. {
  1906. // -------------------------------------
  1907. // prep input data
  1908. // -------------------------------------
  1909. //set defaults for optional items
  1910. $input_defaults = array(
  1911. 'url_suffix' => '',
  1912. 'query_string_segment' => 'P',
  1913. 'offset' => 0,
  1914. 'pagination_page' => 0,
  1915. 'pagination_config' => array(),
  1916. 'sql' => '',
  1917. 'tagdata' => '',
  1918. 'uri_string' => '',
  1919. 'paginate_prefix' => '',
  1920. 'prefix' => '',
  1921. 'total_results' => 0,
  1922. 'request' => REQ,
  1923. 'auto_paginate' => FALSE
  1924. );
  1925. //array2 overwrites any duplicate key from array1
  1926. $input_data = array_merge($input_defaults, $input_data);
  1927. // -------------------------------------
  1928. // using the prefix? well, lets use it like the old. Stupid legacy
  1929. // -------------------------------------
  1930. if (trim($input_data['prefix']) !== '')
  1931. {
  1932. //allowing ':' in a prefix
  1933. if (substr($input_data['prefix'], -1, 1) !== ':')
  1934. {
  1935. $input_data['prefix'] = rtrim($input_data['prefix'], '_') . '_';
  1936. }
  1937. $input_data['paginate_prefix'] = $input_data['prefix'];
  1938. }
  1939. //using query strings?
  1940. //technically, ACT is the same here, but ACT is not for templates :p
  1941. $use_query_strings = (
  1942. REQ == 'CP' OR
  1943. $input_data['request'] == 'CP' OR
  1944. ee()->config->item('enable_query_strings')
  1945. );
  1946. //make sure there is are surrounding slashes.
  1947. $input_data['uri_string'] = '/' . trim($input_data['uri_string'], '/') . '/';
  1948. //shortcuts
  1949. $config = $input_data['pagination_config'];
  1950. $p = $input_data['query_string_segment'];
  1951. $config['query_string_segment'] = $input_data['query_string_segment'];
  1952. $config['page_query_string'] = $use_query_strings;
  1953. //need the prefix so our segments are like /segment/segment/P10
  1954. //instead of like /segment/segment/10
  1955. //this only works in EE 2.x because CI 1.x didn't have the prefix
  1956. //a hack later in the code makes this work for EE 1.x
  1957. if (REQ == 'PAGE')
  1958. {
  1959. $config['prefix'] = $config['query_string_segment'];
  1960. }
  1961. //current page
  1962. if ( ! $use_query_strings AND preg_match("/$p(\d+)/s", $input_data['uri_string'], $match) )
  1963. {
  1964. if ( $input_data['pagination_page'] == 0 AND is_numeric($match[1]) )
  1965. {
  1966. $input_data['pagination_page'] = $match[1];
  1967. //remove page from uri string, query_string, and uri_segments
  1968. $input_data['uri_string'] = ee()->functions->remove_double_slashes(
  1969. str_replace($p . $match[1] , '', $input_data['uri_string'] )
  1970. );
  1971. }
  1972. }
  1973. else if ( $use_query_strings === FALSE)
  1974. {
  1975. if ( ! is_numeric($input_data['pagination_page']) )
  1976. {
  1977. $input_data['pagination_page'] = 0;
  1978. }
  1979. }
  1980. else if ( ! in_array(
  1981. ee()->input->get_post($input_data['query_string_segment']),
  1982. array(FALSE, '')
  1983. ))
  1984. {
  1985. $input_data['pagination_page'] = ee()->input->get_post($input_data['query_string_segment']);
  1986. }
  1987. // --------------------------------------------
  1988. // Automatic Total Results
  1989. // --------------------------------------------
  1990. if ( empty($input_data['total_results']) AND
  1991. ! empty($input_data['sql'])
  1992. )
  1993. {
  1994. $query = ee()->db->query(
  1995. preg_replace(
  1996. "/SELECT(.*?)\s+FROM\s+/is",
  1997. 'SELECT COUNT(*) AS count FROM ',
  1998. $input_data['sql'],
  1999. 1
  2000. )
  2001. );
  2002. $input_data['total_results'] = $query->row('count');
  2003. }
  2004. //this prevents the CI pagination class from
  2005. //trying to find the number itself...
  2006. $config['uri_segment'] = 0;
  2007. // -------------------------------------
  2008. // prep return data
  2009. // -------------------------------------
  2010. $return_data = array(
  2011. 'paginate' => FALSE,
  2012. 'paginate_tagpair_data' => '',
  2013. 'current_page' => 0,
  2014. 'total_pages' => 0,
  2015. 'total_results' => $input_data['total_results'],
  2016. 'page_count' => '',
  2017. 'pagination_links' => '',
  2018. 'pagination_array' => '', //2.3.0+
  2019. 'base_url' => '',
  2020. 'page_next' => '',
  2021. 'page_previous' => '',
  2022. 'pagination_page' => $input_data['pagination_page'],
  2023. 'tagdata' => $input_data['tagdata'],
  2024. 'sql' => $input_data['sql'],
  2025. );
  2026. // -------------------------------------
  2027. // Begin pagination check
  2028. // -------------------------------------
  2029. if (REQ == 'CP' OR
  2030. $input_data['request'] == 'CP' OR
  2031. (
  2032. strpos(
  2033. $return_data['tagdata'],
  2034. LD . $input_data['paginate_prefix'] . 'paginate'
  2035. ) !== FALSE
  2036. OR
  2037. strpos(
  2038. $return_data['tagdata'],
  2039. LD . 'paginate'
  2040. ) !== FALSE
  2041. )
  2042. )
  2043. {
  2044. $return_data['paginate'] = TRUE;
  2045. // -------------------------------------
  2046. // If we have prefixed pagination tags,
  2047. // lets do those first
  2048. // -------------------------------------
  2049. if ($input_data['paginate_prefix'] != '' AND preg_match(
  2050. "/" . LD . $input_data['paginate_prefix'] . "paginate" . RD .
  2051. "(.+?)" .
  2052. LD . preg_quote(T_SLASH, '/') .
  2053. $input_data['paginate_prefix'] . "paginate" .
  2054. RD . "/s",
  2055. $return_data['tagdata'],
  2056. $match
  2057. ))
  2058. {
  2059. $return_data['paginate_tagpair_data'] = $match[1];
  2060. $return_data['tagdata'] = str_replace(
  2061. $match[0],
  2062. '',
  2063. $return_data['tagdata']
  2064. );
  2065. }
  2066. //else lets check for normal pagination tags
  2067. else if (preg_match(
  2068. "/" . LD . "paginate" . RD .
  2069. "(.+?)" .
  2070. LD . preg_quote(T_SLASH, '/') . "paginate" . RD . "/s",
  2071. $return_data['tagdata'],
  2072. $match
  2073. ))
  2074. {
  2075. $return_data['paginate_tagpair_data'] = $match[1];
  2076. $return_data['tagdata'] = str_replace(
  2077. $match[0],
  2078. '',
  2079. $return_data['tagdata']
  2080. );
  2081. }
  2082. // ----------------------------------------
  2083. // Calculate total number of pages
  2084. // ----------------------------------------
  2085. $return_data['current_page'] = floor(
  2086. $input_data['pagination_page'] / $input_data['limit']
  2087. ) + 1;
  2088. $return_data['total_pages'] = ceil(
  2089. ($input_data['total_results'] - $input_data['offset']) / $input_data['limit']
  2090. );
  2091. $return_data['page_count'] = lang('page') . ' ' .
  2092. $return_data['current_page'] . ' ' .
  2093. lang('of') . ' ' .
  2094. $return_data['total_pages'];
  2095. // ----------------------------------------
  2096. // Do we need pagination?
  2097. // ----------------------------------------
  2098. if ( ($input_data['total_results'] - $input_data['offset']) > $input_data['limit'] )
  2099. {
  2100. if ( ! isset( $config['base_url'] ) )
  2101. {
  2102. $config['base_url'] = ee()->functions->create_url(
  2103. $input_data['uri_string'] . $input_data['url_suffix'],
  2104. FALSE,
  2105. 0
  2106. );
  2107. }
  2108. $config['total_rows'] = ($input_data['total_results'] - $input_data['offset']);
  2109. $config['per_page'] = $input_data['limit'];
  2110. $config['cur_page'] = $input_data['pagination_page'];
  2111. if (APP_VER >= '2.3.0')
  2112. {
  2113. $config['first_link'] = lang('pag_first_link');
  2114. $config['last_link'] = lang('pag_last_link');
  2115. }
  2116. ee()->load->library('pagination');
  2117. ee()->pagination->initialize($config);
  2118. $return_data['pagination_links'] = ee()->pagination->create_links();
  2119. //2.3.0+ supports pagination array
  2120. //create_link_array must be called second after create_links
  2121. //or weird things happen
  2122. if (APP_VER >= '2.3.0')
  2123. {
  2124. $return_data['pagination_array'] = ee()->pagination->create_link_array();
  2125. }
  2126. $return_data['base_url'] = ee()->pagination->base_url;
  2127. //CI 1.x pagination does not have the
  2128. //prefix variable so we have to use this hack
  2129. //to turn /segment/segment/10/ into /segment/segment/P10/
  2130. //where P is $p
  2131. if (APP_VER < 2.0 AND ! $use_query_strings )
  2132. {
  2133. $return_data['pagination_links'] = preg_replace(
  2134. "/" . preg_quote($return_data['base_url'], '/') .
  2135. "([0-9]+)(?:" . preg_quote(T_SLASH, '/') . ")?/s",
  2136. rtrim( $return_data['base_url'] . $p . "$1", '/') . '/',
  2137. $return_data['pagination_links']
  2138. );
  2139. }
  2140. // ----------------------------------------
  2141. // Prepare next_page and previous_page variables
  2142. // ----------------------------------------
  2143. //next page?
  2144. if ( (($return_data['total_pages'] * $input_data['limit']) - $input_data['limit']) >
  2145. $return_data['pagination_page'])
  2146. {
  2147. $return_data['page_next'] = $return_data['base_url'] .
  2148. ($use_query_strings ? '' : $p) .
  2149. ($input_data['pagination_page'] + $input_data['limit']) . '/';
  2150. }
  2151. //previous page?
  2152. if (($return_data['pagination_page'] - $input_data['limit'] ) >= 0)
  2153. {
  2154. $return_data['page_previous'] = $return_data['base_url'] .
  2155. ($use_query_strings ? '' : $p) .
  2156. ($input_data['pagination_page'] - $input_data['limit']) . '/';
  2157. }
  2158. }
  2159. }
  2160. //move current page to offset
  2161. //$return_data['current_page'] += $input_data['offset'];
  2162. //add limit to passed in sql
  2163. $return_data['sql'] .= ' LIMIT ' .
  2164. ($return_data['pagination_page'] + $input_data['offset']) .
  2165. ', ' . $input_data['limit'];
  2166. //if we are automatically making magic, lets add all of the class vars
  2167. if ($input_data['auto_paginate'] === TRUE)
  2168. {
  2169. $this->auto_paginate = TRUE;
  2170. $this->paginate = $return_data['paginate'];
  2171. $this->page_next = $return_data['page_next'];
  2172. $this->page_previous = $return_data['page_previous'];
  2173. $this->p_page = $return_data['pagination_page'];
  2174. $this->current_page = $return_data['current_page'];
  2175. $this->pagination_links = $return_data['pagination_links'];
  2176. $this->pagination_array = $return_data['pagination_array'];
  2177. $this->basepath = $return_data['base_url'];
  2178. $this->total_pages = $return_data['total_pages'];
  2179. $this->paginate_data = $return_data['paginate_tagpair_data'];
  2180. $this->page_count = $return_data['page_count'];
  2181. //ee()->TMPL->tagdata = $return_data['tagdata'];
  2182. }
  2183. return $return_data;
  2184. }
  2185. // End universal_pagination
  2186. // --------------------------------------------------------------------
  2187. /**
  2188. * Universal Parse Pagination
  2189. *
  2190. * This creates a new XID hash in the DB for usage.
  2191. *
  2192. * @access public
  2193. * @param array
  2194. * @return tagdata
  2195. */
  2196. public function parse_pagination ($options = array())
  2197. {
  2198. // -------------------------------------
  2199. // prep input data
  2200. // -------------------------------------
  2201. //set defaults for optional items
  2202. $defaults = array(
  2203. 'prefix' => '',
  2204. 'tagdata' => ((isset(ee()->TMPL) and is_object(ee()->TMPL)) ?
  2205. ee()->TMPL->tagdata : ''),
  2206. 'paginate' => FALSE,
  2207. 'page_next' => '',
  2208. 'page_previous' => '',
  2209. 'p_page' => 0,
  2210. 'current_page' => 0,
  2211. 'pagination_links' => '',
  2212. 'pagination_array' => '',
  2213. 'basepath' => '',
  2214. 'total_pages' => '',
  2215. 'paginate_data' => '',
  2216. 'page_count' => '',
  2217. 'auto_paginate' => $this->auto_paginate
  2218. );
  2219. //array2 overwrites any duplicate key from array1
  2220. $options = array_merge($defaults, $options);
  2221. // -------------------------------------
  2222. // auto paginate?
  2223. // -------------------------------------
  2224. if ($options['auto_paginate'])
  2225. {
  2226. $options = array_merge($options, array(
  2227. 'paginate' => $this->paginate,
  2228. 'page_next' => $this->page_next,
  2229. 'page_previous' => $this->page_previous,
  2230. 'p_page' => $this->p_page,
  2231. 'current_page' => $this->current_page,
  2232. 'pagination_links' => $this->pagination_links,
  2233. 'pagination_array' => $this->pagination_array,
  2234. 'basepath' => $this->basepath,
  2235. 'total_pages' => $this->total_pages,
  2236. 'paginate_data' => $this->paginate_data,
  2237. 'page_count' => $this->page_count,
  2238. ));
  2239. }
  2240. // -------------------------------------
  2241. // prefixed items?
  2242. // -------------------------------------
  2243. $prefix = '';
  2244. if (trim($options['prefix']) != '')
  2245. {
  2246. //allowing ':' in a prefix
  2247. if (substr($options['prefix'], -1, 1) !== ':')
  2248. {
  2249. $options['prefix'] = rtrim($options['prefix'], '_') . '_';
  2250. }
  2251. $prefix = $options['prefix'];
  2252. }
  2253. $tag_paginate = $prefix . 'paginate';
  2254. $tag_pagination_links = $prefix . 'pagination_links';
  2255. $tag_current_page = $prefix . 'current_page';
  2256. $tag_total_pages = $prefix . 'total_pages';
  2257. $tag_page_count = $prefix . 'page_count';
  2258. $tag_previous_page = $prefix . 'previous_page';
  2259. $tag_next_page = $prefix . 'next_page';
  2260. $tag_auto_path = $prefix . 'auto_path';
  2261. $tag_path = $prefix . 'path';
  2262. // -------------------------------------
  2263. // TO VARIABLES!
  2264. // -------------------------------------
  2265. extract($options);
  2266. // ----------------------------------------
  2267. // no paginate? :(
  2268. // ----------------------------------------
  2269. if ( $paginate === FALSE )
  2270. {
  2271. return ee()->functions->prep_conditionals(
  2272. $tagdata,
  2273. array($tag_paginate => FALSE)
  2274. );
  2275. }
  2276. // -------------------------------------
  2277. // replace {if (prefix_)paginate} blocks
  2278. // -------------------------------------
  2279. $tagdata = ee()->functions->prep_conditionals(
  2280. $tagdata,
  2281. array($tag_paginate => TRUE)
  2282. );
  2283. // -------------------------------------
  2284. // count and link conditionals
  2285. // -------------------------------------
  2286. $pagination_items = array(
  2287. $tag_pagination_links => $pagination_links,
  2288. $tag_current_page => $current_page,
  2289. $tag_total_pages => $total_pages,
  2290. $tag_page_count => $page_count
  2291. );
  2292. // -------------------------------------
  2293. // ee 2.3 pagination array?
  2294. // -------------------------------------
  2295. if (APP_VER >= '2.3.0' AND $pagination_array)
  2296. {
  2297. // Check to see if pagination_links is being used as a single
  2298. // variable or as a variable pair
  2299. if (preg_match_all(
  2300. "/" . LD . $tag_pagination_links . RD .
  2301. "(.+?)" .
  2302. LD . '\/' . $tag_pagination_links . RD . "/s",
  2303. $paginate_data,
  2304. $matches
  2305. ))
  2306. {
  2307. // Parse current_page and total_pages
  2308. $paginate_data = ee()->TMPL->parse_variables(
  2309. $paginate_data,
  2310. array(array($tag_pagination_links => array($pagination_array)))
  2311. );
  2312. }
  2313. }
  2314. // -------------------------------------
  2315. // parse everything left
  2316. // -------------------------------------
  2317. $paginate_data = ee()->functions->prep_conditionals(
  2318. $paginate_data,
  2319. $pagination_items
  2320. );
  2321. // -------------------------------------
  2322. // if this is EE 2.3+, we need to parse the pagination
  2323. // tag pair before we str_replace
  2324. // -------------------------------------
  2325. foreach ( $pagination_items as $key => $val )
  2326. {
  2327. $paginate_data = str_replace(
  2328. LD . $key . RD,
  2329. $val,
  2330. $paginate_data
  2331. );
  2332. }
  2333. // ----------------------------------------
  2334. // Previous link
  2335. // ----------------------------------------
  2336. if (preg_match(
  2337. "/" . LD . "if " . $tag_previous_page . RD .
  2338. "(.+?)" .
  2339. LD . preg_quote(T_SLASH, '/') . "if" . RD . "/s",
  2340. $paginate_data,
  2341. $match
  2342. ))
  2343. {
  2344. if ($page_previous == '')
  2345. {
  2346. $paginate_data = preg_replace(
  2347. "/" . LD . "if " . $tag_previous_page . RD .
  2348. ".+?" .
  2349. LD . preg_quote(T_SLASH, '/') . "if" . RD . "/s",
  2350. '',
  2351. $paginate_data
  2352. );
  2353. }
  2354. else
  2355. {
  2356. $match['1'] = preg_replace(
  2357. "/" . LD . $tag_path . '.*?' . RD . "/",
  2358. $page_previous,
  2359. $match['1']
  2360. );
  2361. $match['1'] = preg_replace(
  2362. "/" . LD . $tag_auto_path . RD . "/",
  2363. $page_previous,
  2364. $match['1']
  2365. );
  2366. $paginate_data = str_replace(
  2367. $match['0'],
  2368. $match['1'],
  2369. $paginate_data
  2370. );
  2371. }
  2372. }
  2373. // ----------------------------------------
  2374. // Next link
  2375. // ----------------------------------------
  2376. if (preg_match(
  2377. "/" . LD . "if " . $tag_next_page . RD .
  2378. "(.+?)" .
  2379. LD . preg_quote(T_SLASH, '/') . "if" . RD . "/s",
  2380. $paginate_data,
  2381. $match
  2382. ))
  2383. {
  2384. if ($page_next == '')
  2385. {
  2386. $paginate_data = preg_replace(
  2387. "/" . LD . "if " . $tag_next_page . RD .
  2388. ".+?" .
  2389. LD . preg_quote(T_SLASH, '/') . "if" . RD . "/s",
  2390. '',
  2391. $paginate_data
  2392. );
  2393. }
  2394. else
  2395. {
  2396. $match['1'] = preg_replace(
  2397. "/" . LD . $tag_path . '.*?' . RD . "/",
  2398. $page_next,
  2399. $match['1']
  2400. );
  2401. $match['1'] = preg_replace(
  2402. "/" . LD . $tag_auto_path . RD . "/",
  2403. $page_next,
  2404. $match['1']
  2405. );
  2406. $paginate_data = str_replace(
  2407. $match['0'],
  2408. $match['1'],
  2409. $paginate_data
  2410. );
  2411. }
  2412. }
  2413. // ----------------------------------------
  2414. // Add pagination
  2415. // ----------------------------------------
  2416. if ( ee()->TMPL->fetch_param('paginate') == 'both' )
  2417. {
  2418. $tagdata = $paginate_data . $tagdata . $paginate_data;
  2419. }
  2420. elseif ( ee()->TMPL->fetch_param('paginate') == 'top' )
  2421. {
  2422. $tagdata = $paginate_data . $tagdata;
  2423. }
  2424. else
  2425. {
  2426. $tagdata = $tagdata . $paginate_data;
  2427. }
  2428. // ----------------------------------------
  2429. // Return
  2430. // ----------------------------------------
  2431. return $tagdata;
  2432. }
  2433. //END parse_pagination
  2434. // --------------------------------------------------------------------
  2435. /**
  2436. * pagination_prefix_replace
  2437. * gets the tag group id from a number of places and sets it to the
  2438. * instance default param
  2439. *
  2440. * @access public
  2441. * @param string prefix for tag
  2442. * @param string tagdata
  2443. * @param bool reverse, are we removing the preixes we did before?
  2444. * @return string tag data with prefix changed out
  2445. */
  2446. public function pagination_prefix_replace ($prefix = '', $tagdata = '', $reverse = FALSE)
  2447. {
  2448. if ($prefix == '')
  2449. {
  2450. return $tagdata;
  2451. }
  2452. //allowing ':' in a prefix
  2453. if (substr($prefix, -1, 1) !== ':')
  2454. {
  2455. $prefix = rtrim($prefix, '_') . '_';
  2456. }
  2457. //if there is nothing prefixed, we don't want to do anything datastardly
  2458. if ( ! $reverse AND
  2459. strpos($tagdata, LD.$prefix . 'paginate'.RD) === FALSE)
  2460. {
  2461. return $tagdata;
  2462. }
  2463. $hash = 'e2c518d61874f2d4a14bbfb9087a7c2d';
  2464. $items = array(
  2465. 'paginate',
  2466. 'pagination_links',
  2467. 'current_page',
  2468. 'total_pages',
  2469. 'page_count',
  2470. 'previous_page',
  2471. 'next_page',
  2472. 'auto_path',
  2473. 'path'
  2474. );
  2475. $find = array();
  2476. $hash_replace = array();
  2477. $prefix_replace = array();
  2478. $length = count($items);
  2479. foreach ($items as $key => $item)
  2480. {
  2481. $nkey = $key + $length;
  2482. //this is terse, but it ensures that we
  2483. //find any an all tag pairs if they occur
  2484. $find[$key] = LD . $item . RD;
  2485. $find[$nkey] = LD . T_SLASH . $item . RD;
  2486. $hash_replace[$key] = LD . $hash . $item . RD;
  2487. $hash_replace[$nkey] = LD . T_SLASH . $hash . $item . RD;
  2488. $prefix_replace[$key] = LD . $prefix . $item . RD;
  2489. $prefix_replace[$nkey] = LD . T_SLASH . $prefix . $item . RD;
  2490. }
  2491. //prefix standard and replace prefixs
  2492. if ( ! $reverse)
  2493. {
  2494. $tagdata = str_replace($find, $hash_replace, $tagdata);
  2495. $tagdata = str_replace($prefix_replace, $find, $tagdata);
  2496. }
  2497. //we are on the return, fix the hashed ones
  2498. else
  2499. {
  2500. $tagdata = str_replace($hash_replace, $find, $tagdata);
  2501. }
  2502. return $tagdata;
  2503. }
  2504. //END pagination_prefix_replace
  2505. // --------------------------------------------------------------------
  2506. /**
  2507. * Create XID
  2508. *
  2509. * This creates a new XID hash in the DB for usage.
  2510. *
  2511. * @access public
  2512. * @return string
  2513. */
  2514. public function create_xid ()
  2515. {
  2516. $sql = "INSERT INTO exp_security_hashes (date, ip_address, hash) VALUES";
  2517. $hash = ee()->functions->random('encrypt');
  2518. $sql .= "(UNIX_TIMESTAMP(), '". ee()->input->ip_address() . "', '" . $hash . "')";
  2519. $this->cacheless_query($sql);
  2520. return $hash;
  2521. }
  2522. // END Create XID
  2523. // --------------------------------------------------------------------
  2524. /**
  2525. * cacheless_query
  2526. *
  2527. * this sends a query to the db non-cached
  2528. *
  2529. * @access public
  2530. * @param string sql to query
  2531. * @return object query object
  2532. */
  2533. public function cacheless_query ($sql)
  2534. {
  2535. $reset = FALSE;
  2536. // Disable DB caching if it's currently set
  2537. if (ee()->db->cache_on == TRUE)
  2538. {
  2539. ee()->db->cache_off();
  2540. $reset = TRUE;
  2541. }
  2542. $query = ee()->db->query($sql);
  2543. // Re-enable DB caching
  2544. if ($reset == TRUE)
  2545. {
  2546. ee()->db->cache_on();
  2547. }
  2548. return $query;
  2549. }
  2550. // END cacheless_query
  2551. // --------------------------------------------------------------------
  2552. /**
  2553. * Implodes an Array and Hashes It
  2554. *
  2555. * @access public
  2556. * @return string
  2557. */
  2558. public function _imploder ($arguments)
  2559. {
  2560. return md5(serialize($arguments));
  2561. }
  2562. // END
  2563. // --------------------------------------------------------------------
  2564. /**
  2565. * Prepare keyed result
  2566. *
  2567. * Take a query object and return an associative array. If $val is empty,
  2568. * the entire row per record will become the value attached to the indicated key.
  2569. *
  2570. * For example, if you do a query on exp_channel_titles and exp_channel_data
  2571. * you can use this to quickly create an associative array of channel entry
  2572. * data keyed to entry id.
  2573. *
  2574. * @access public
  2575. * @return mixed
  2576. */
  2577. public function prepare_keyed_result ( $query, $key = '', $val = '' )
  2578. {
  2579. if ( ! is_object( $query ) OR $key == '' ) return FALSE;
  2580. // --------------------------------------------
  2581. // Loop through query
  2582. // --------------------------------------------
  2583. $data = array();
  2584. foreach ( $query->result_array() as $row )
  2585. {
  2586. if ( isset( $row[$key] ) === FALSE ) continue;
  2587. $data[ $row[$key] ] = ( $val != '' AND isset( $row[$val] ) ) ? $row[$val]: $row;
  2588. }
  2589. return ( empty( $data ) ) ? FALSE : $data;
  2590. }
  2591. // END prepare_keyed_result
  2592. // --------------------------------------------------------------------
  2593. /**
  2594. * returns the truthy or last arg i
  2595. *
  2596. * @access public
  2597. * @param array args to be checked against
  2598. * @param mixed bool or array of items to check against
  2599. * @return mixed
  2600. */
  2601. public function either_or_base ($args = array(), $test = FALSE)
  2602. {
  2603. foreach ($args as $arg)
  2604. {
  2605. //do we have an array of nots?
  2606. //if so, we need to be test for type
  2607. if ( is_array($test))
  2608. {
  2609. if ( ! in_array($arg, $test, TRUE) ) return $arg;
  2610. }
  2611. //is it implicit false?
  2612. elseif ($test)
  2613. {
  2614. if ($arg !== FALSE) return $arg;
  2615. }
  2616. //else just test for falsy
  2617. else
  2618. {
  2619. if ($arg) return $arg;
  2620. }
  2621. }
  2622. return end($args);
  2623. }
  2624. //END either_or_base
  2625. // --------------------------------------------------------------------
  2626. /**
  2627. * returns the truthy or last arg
  2628. *
  2629. * @access public
  2630. * @param mixed any number of arguments consisting of variables to be returned false
  2631. * @return mixed
  2632. */
  2633. public function either_or ()
  2634. {
  2635. $args = func_get_args();
  2636. return $this->either_or_base($args);
  2637. }
  2638. //END either_or
  2639. // --------------------------------------------------------------------
  2640. /**
  2641. * returns the non exact bool FALSE or last arg
  2642. *
  2643. * @access public
  2644. * @param mixed any number of arguments consisting of variables to be returned false
  2645. * @return mixed
  2646. */
  2647. public function either_or_strict ()
  2648. {
  2649. $args = func_get_args();
  2650. return $this->either_or_base($args, TRUE);
  2651. }
  2652. // END either_or_strict
  2653. //---------------------------------------------------------------------
  2654. /**
  2655. * add_right_link
  2656. * @access public
  2657. * @param (string) string of link name
  2658. * @param (string) html link for right link
  2659. * @return (null)
  2660. */
  2661. public function add_right_link ($text, $link)
  2662. {
  2663. //no funny business
  2664. if (REQ != 'CP') return;
  2665. $this->right_links[$text] = $link;
  2666. }
  2667. //end add_right_link
  2668. //---------------------------------------------------------------------
  2669. /**
  2670. * build_right_links
  2671. * @access public
  2672. * @return (null)
  2673. */
  2674. public function build_right_links ()
  2675. {
  2676. //no funny business
  2677. if (REQ != 'CP' OR empty($this->right_links)) return;
  2678. if (APP_VER < 2.0)
  2679. {
  2680. $this->cached_vars['right_links'] = $this->right_links;
  2681. }
  2682. else
  2683. {
  2684. ee()->cp->set_right_nav($this->right_links);
  2685. }
  2686. }
  2687. //end build_right_links
  2688. // --------------------------------------------------------------------
  2689. /**
  2690. * Fetch List of Member Fields and Data
  2691. *
  2692. * @access public
  2693. * @return string
  2694. */
  2695. public function mfields ()
  2696. {
  2697. return $this->mfields = $this->data->get_member_fields();
  2698. }
  2699. // END mfields()
  2700. // --------------------------------------------------------------------
  2701. /**
  2702. * do we have any hooks?
  2703. *
  2704. *
  2705. * @access public
  2706. * @return bool Whether the extensions are allowed
  2707. */
  2708. public function has_hooks ()
  2709. {
  2710. //is it there? is it array? is it empty? Such are life's unanswerable questions, until now.
  2711. if ( ! $this->updater() OR
  2712. ((! isset($this->updater()->hooks) OR
  2713. ! is_array($this->updater->hooks)) AND
  2714. (! isset($this->hooks) OR
  2715. ! is_array($this->hooks))) OR
  2716. (empty($this->hooks) AND empty($this->updater->hooks))
  2717. )
  2718. {
  2719. return FALSE;
  2720. }
  2721. return TRUE;
  2722. }
  2723. //end has hooks
  2724. // --------------------------------------------------------------------
  2725. /**
  2726. * loads updater object and sets it to $this->upd and returns it
  2727. *
  2728. *
  2729. * @access public
  2730. * @return obj updater object for module
  2731. */
  2732. public function updater ()
  2733. {
  2734. if ( ! is_object($this->updater) )
  2735. {
  2736. //why not use the app_ver constant here?
  2737. //well its not available while in wizard
  2738. $ee1_class = $this->class_name . '_updater';
  2739. $ee2_class = $this->class_name . '_upd';
  2740. $update_file = $this->addon_path . 'upd.' . $this->lower_name . '.php';
  2741. if ( ! class_exists($ee1_class) AND
  2742. ! class_exists($ee2_class))
  2743. {
  2744. if (is_file($update_file))
  2745. {
  2746. require_once $update_file;
  2747. }
  2748. else
  2749. {
  2750. //techincally, this is false, but we dont want to halt something else because the
  2751. //file cannot be found that we need here. Needs to be a better solution
  2752. return FALSE;
  2753. }
  2754. }
  2755. $class = class_exists($ee2_class) ? $ee2_class : $ee1_class;
  2756. $this->updater = new $class();
  2757. }
  2758. return $this->updater;
  2759. }
  2760. //end updater
  2761. // --------------------------------------------------------------------
  2762. /**
  2763. * Checks to see if extensions are enabled for this module
  2764. *
  2765. *
  2766. * @access public
  2767. * @param bool match exact number of hooks
  2768. * @return bool Whether the extensions are enabled if need be
  2769. */
  2770. public function extensions_enabled ( $check_all_enabled = FALSE )
  2771. {
  2772. if ( ! $this->has_hooks() ) return TRUE;
  2773. //we don't want to end on this as it would confuse users
  2774. if ( $this->updater() === FALSE ) return TRUE;
  2775. $num_enabled = 0;
  2776. foreach ($this->updater()->hooks as $hook_data)
  2777. {
  2778. if (isset(ee()->extensions->extensions[$hook_data['hook']]))
  2779. {
  2780. foreach(ee()->extensions->extensions[$hook_data['hook']] as $priority => $hook_array)
  2781. {
  2782. if (isset($hook_array[$this->extension_name]))
  2783. {
  2784. $num_enabled++;
  2785. }
  2786. }
  2787. }
  2788. }
  2789. //we arent going to look for all of the hooks
  2790. //because some could be turned off manually for testing
  2791. return (($check_all_enabled) ?
  2792. ($num_enabled == count($this->updater()->hooks) ) :
  2793. ($num_enabled > 0) );
  2794. }
  2795. //END extensions_enabled
  2796. // --------------------------------------------------------------------
  2797. /**
  2798. * AJAX Request
  2799. *
  2800. * Tests via headers or GET/POST parameter whether the incoming
  2801. * request is AJAX in nature
  2802. * Useful when we want to change the output of a method.
  2803. *
  2804. * @access public
  2805. * @return boolean
  2806. */
  2807. public function is_ajax_request ()
  2808. {
  2809. // --------------------------------------------
  2810. // Headers indicate this is an AJAX Request
  2811. // - They can disable via a parameter or GET/POST
  2812. // - If not, TRUE
  2813. // --------------------------------------------
  2814. if (ee()->input->server('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest')
  2815. {
  2816. // Check for parameter
  2817. if (isset(ee()->TMPL) AND is_object(ee()->TMPL))
  2818. {
  2819. if (ee()->TMPL->fetch_param('ajax_request') !== FALSE &&
  2820. $this->check_no(ee()->TMPL->fetch_param('ajax_request')))
  2821. {
  2822. return FALSE;
  2823. }
  2824. }
  2825. // Check for GET/POST variable
  2826. if (ee()->input->get_post('ajax_request') !== FALSE &&
  2827. $this->check_no(ee()->input->get_post('ajax_request')))
  2828. {
  2829. return FALSE;
  2830. }
  2831. // Not disabled
  2832. return TRUE;
  2833. }
  2834. // --------------------------------------------
  2835. // Headers do NOT indicate it is an AJAX Request
  2836. // - They can force with a parameter OR GET/POST variable
  2837. // - If not, FALSE
  2838. // --------------------------------------------
  2839. if (isset(ee()->TMPL) AND is_object(ee()->TMPL))
  2840. {
  2841. if ($this->check_yes(ee()->TMPL->fetch_param('ajax_request')))
  2842. {
  2843. return TRUE;
  2844. }
  2845. }
  2846. if ($this->check_yes(ee()->input->get_post('ajax_request')))
  2847. {
  2848. return TRUE;
  2849. }
  2850. return FALSE;
  2851. }
  2852. // END is_ajax_request()
  2853. // --------------------------------------------------------------------
  2854. /**
  2855. * Send AJAX response
  2856. *
  2857. * Outputs and exit either an HTML string or a
  2858. * JSON array with the Profile disabled and correct
  2859. * headers sent.
  2860. *
  2861. * @access public
  2862. * @param string|array String is sent as HTML, Array is sent as JSON
  2863. * @param bool Is this an error message?
  2864. * @param bool bust cache for JSON?
  2865. * @return void
  2866. */
  2867. public function send_ajax_response ($msg, $error = FALSE, $cache_bust = TRUE)
  2868. {
  2869. ee()->output->enable_profiler(FALSE);
  2870. if ($error === TRUE)
  2871. {
  2872. //ee()->output->set_status_header(500);
  2873. }
  2874. $send_headers = (ee()->config->item('send_headers') == 'y');
  2875. //if this is an array or object, output json
  2876. if (is_array($msg) OR is_object($msg))
  2877. {
  2878. if ($send_headers)
  2879. {
  2880. if ($cache_bust)
  2881. {
  2882. //cache bust
  2883. @header('Cache-Control: no-cache, must-revalidate');
  2884. @header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
  2885. }
  2886. @header('Content-Type: application/json');
  2887. }
  2888. echo $this->json_encode($msg);
  2889. }
  2890. else
  2891. {
  2892. if ($send_headers)
  2893. {
  2894. @header('Content-Type: text/html; charset=UTF-8');
  2895. }
  2896. echo (string) $msg;
  2897. }
  2898. exit();
  2899. }
  2900. //END send_ajax_response()
  2901. // --------------------------------------------------------------------
  2902. /**
  2903. * Validate Emails
  2904. *
  2905. * Validates an array or parses a string of emails and then validates
  2906. *
  2907. * @access public
  2908. * @param string|array
  2909. * @return array $vars - Contains two keys good/bad of, what else, good and bad emails
  2910. */
  2911. public function validate_emails ($emails)
  2912. {
  2913. ee()->load->helper('email');
  2914. if ( is_string($emails))
  2915. {
  2916. // Remove all white space and replace with commas
  2917. $email = trim(preg_replace("/\s*(\S+)\s*/s", "\\1,", trim($emails)), ',');
  2918. // Remove duplicate commas
  2919. $email = str_replace(',,', ',', $email);
  2920. // Explode and make unique
  2921. $emails = array_unique(explode(",", $email));
  2922. }
  2923. $vars['good'] = array();
  2924. $vars['bad'] = array();
  2925. foreach($emails as $addr)
  2926. {
  2927. if (preg_match('/<(.*)>/', $addr, $match))
  2928. {
  2929. $addr = $match[1];
  2930. }
  2931. if ( ! valid_email($addr))
  2932. {
  2933. $vars['bad'][] = $addr;
  2934. continue;
  2935. }
  2936. $vars['good'][] = $addr;
  2937. }
  2938. return $vars;
  2939. }
  2940. // END validate_emails();
  2941. // --------------------------------------------------------------------
  2942. /**
  2943. * Get Action URL
  2944. *
  2945. * returns a full URL for an action
  2946. *
  2947. * @access public
  2948. * @param string method name
  2949. * @return string url for action
  2950. */
  2951. public function get_action_url ($method_name)
  2952. {
  2953. $action_id = ee()->db->where(
  2954. array(
  2955. 'class' => $this->class_name,
  2956. 'method' => $method_name
  2957. )
  2958. )->get('actions')->row('action_id');
  2959. return ee()->functions->fetch_site_index(0, 0) . QUERY_MARKER . 'ACT=' . $action_id;
  2960. }
  2961. //END get_action_url
  2962. // --------------------------------------------------------------------
  2963. /**
  2964. * is_positive_intlike
  2965. *
  2966. * return
  2967. *
  2968. * (is_positive_entlike would have taken forever)
  2969. *
  2970. * @access public
  2971. * @param mixed num number/int/string to check for numeric
  2972. * @param int threshold lowest number acceptable (default 1)
  2973. * @return bool
  2974. */
  2975. public function is_positive_intlike ($num, $threshold = 1)
  2976. {
  2977. //without is_numeric, bools return positive
  2978. //because preg_match auto converts to string
  2979. return (
  2980. is_numeric($num) AND
  2981. preg_match("/^[0-9]+$/", $num) AND
  2982. $num >= $threshold
  2983. );
  2984. }
  2985. //END is_positive_intlike
  2986. // --------------------------------------------------------------------
  2987. /**
  2988. * get_post_or_zero
  2989. *
  2990. * @access public
  2991. * @param string name of GET/POST var to check
  2992. * @return int returns 0 if the get/post is not present or numeric or above 0
  2993. */
  2994. public function get_post_or_zero ($name)
  2995. {
  2996. $name = ee()->input->get_post($name);
  2997. return ($this->is_positive_intlike($name) ? $name : 0);
  2998. }
  2999. //END get_post_or_zero
  3000. }
  3001. // END Addon_builder Class