PageRenderTime 41ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/html/AppCode/expressionengine/libraries/Template.php

https://github.com/w3bg/www.hsifin.com
PHP | 4169 lines | 3936 code | 96 blank | 137 comment | 51 complexity | d7fe52d7b39c7ca8ab2fd9373f5c906e MD5 | raw file
Possible License(s): AGPL-3.0
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * ExpressionEngine - by EllisLab
  4. *
  5. * @package ExpressionEngine
  6. * @author ExpressionEngine Dev Team
  7. * @copyright Copyright (c) 2003 - 2010, EllisLab, Inc.
  8. * @license http://expressionengine.com/user_guide/license.html
  9. * @link http://expressionengine.com
  10. * @since Version 2.0
  11. * @filesource
  12. */
  13. // ------------------------------------------------------------------------
  14. /**
  15. * ExpressionEngine Template Parser Class
  16. *
  17. * @package ExpressionEngine
  18. * @subpackage Core
  19. * @category Core
  20. * @author ExpressionEngine Dev Team
  21. * @link http://expressionengine.com
  22. */
  23. class EE_Template {
  24. var $loop_count = 0; // Main loop counter.
  25. var $depth = 0; // Sub-template loop depth
  26. var $in_point = ''; // String position of matched opening tag
  27. var $template = ''; // The requested template (page)
  28. var $final_template = ''; // The finalized template
  29. var $fl_tmpl = ''; // 'Floating' copy of the template. Used as a temporary "work area".
  30. var $cache_hash = ''; // md5 checksum of the template name. Used as title of cache file.
  31. var $cache_status = ''; // Status of page cache (NO_CACHE, CURRENT, EXPIRED)
  32. var $tag_cache_status = ''; // Status of tag cache (NO_CACHE, CURRENT, EXPIRED)
  33. var $cache_timestamp = '';
  34. var $template_type = ''; // Type of template (webpage, rss)
  35. var $embed_type = ''; // Type of template for embedded template
  36. var $template_hits = 0;
  37. var $php_parse_location = 'output'; // Where in the chain the PHP gets parsed
  38. var $template_edit_date = ''; // Template edit date
  39. var $templates_sofar = ''; // Templates processed so far, subtemplate tracker
  40. var $encode_email = TRUE; // Whether to use the email encoder. This is set automatically
  41. var $hit_lock_override = FALSE; // Set to TRUE if you want hits tracked on sub-templates
  42. var $hit_lock = FALSE; // Lets us lock the hit counter if sub-templates are contained in a template
  43. var $parse_php = FALSE; // Whether to parse PHP or not
  44. var $protect_javascript = TRUE; // Protect javascript in conditionals
  45. var $tag_data = array(); // Data contained in tags
  46. var $modules = array(); // List of installed modules
  47. var $module_data = array(); // Data for modules from exp_channels
  48. var $plugins = array(); // List of installed plug-ins
  49. var $var_single = array(); // "Single" variables
  50. var $var_cond = array(); // "Conditional" variables
  51. var $var_pair = array(); // "Paired" variables
  52. var $global_vars = array(); // This array can be set via the path.php file
  53. var $embed_vars = array(); // This array can be set via the {embed} tag
  54. var $segment_vars = array(); // Array of segment variables
  55. var $tagparts = array(); // The parts of the tag: {exp:comment:form}
  56. var $tagdata = ''; // The chunk between tag pairs. This is what modules will utilize
  57. var $tagproper = ''; // The full opening tag
  58. var $no_results = ''; // The contents of the {if no_results}{/if} conditionals
  59. var $no_results_block = ''; // The {if no_results}{/if} chunk
  60. var $search_fields = array(); // Special array of tag parameters that begin with 'search:'
  61. var $date_vars = array(); // Date variables found in the tagdata (FALSE if date variables do not exist in tagdata)
  62. var $unfound_vars = array(); // These are variables that have not been found in the tagdata and can be ignored
  63. var $conditional_vars = array(); // Used by the template variable parser to prep conditionals
  64. var $TYPE = FALSE; // FALSE if Typography has not been instantiated, Typography Class object otherwise
  65. var $related_data = array(); // A multi-dimensional array containing any related tags
  66. var $related_id = ''; // Used temporarily for the related ID number
  67. var $related_markers = array(); // Used temporarily
  68. var $site_ids = array(); // Site IDs for the Sites Request for a Tag
  69. var $sites = array(); // Array of sites with site_id as key and site_name as value, used to determine site_ids for tag, above.
  70. var $site_prefs_cache = array(); // Array of cached site prefs, to allow fetching of another site's template files
  71. var $reverse_related_data = array(); // A multi-dimensional array containing any reverse related tags
  72. var $t_cache_path = 'tag_cache/'; // Location of the tag cache file
  73. var $p_cache_path = 'page_cache/'; // Location of the page cache file
  74. var $disable_caching = FALSE;
  75. var $debugging = FALSE; // Template parser debugging on?
  76. var $cease_processing = FALSE; // Used with no_results() method.
  77. var $log = array(); // Log of Template processing
  78. var $start_microtime = 0; // For Logging (= microtime())
  79. var $strict_urls = FALSE; // Whether to make URLs operate strictly or not. This is set via a template global pref
  80. var $realm = 'ExpressionEngine Template'; // Localize?
  81. var $marker = '0o93H7pQ09L8X1t49cHY01Z5j4TT91fGfr'; // Temporary marker used as a place-holder for template data
  82. var $form_id = ''; // Form Id
  83. var $form_class = ''; // Form Class
  84. // --------------------------------------------------------------------
  85. /**
  86. * Constructor
  87. *
  88. * @access public
  89. * @return void
  90. */
  91. function EE_Template()
  92. {
  93. // Make a local reference to the ExpressionEngine super object
  94. $this->EE =& get_instance();
  95. if ($this->EE->config->item('multiple_sites_enabled') != 'y')
  96. {
  97. $this->sites[$this->EE->config->item('site_id')] = $this->EE->config->item('site_short_name');
  98. }
  99. if ($this->EE->config->item('template_debugging') === 'y' && $this->EE->session->userdata['group_id'] == 1)
  100. {
  101. $this->debugging = TRUE;
  102. if (phpversion() < 5)
  103. {
  104. list($usec, $sec) = explode(" ", microtime());
  105. $this->start_microtime = ((float)$usec + (float)$sec);
  106. }
  107. else
  108. {
  109. $this->start_microtime = microtime(TRUE);
  110. }
  111. }
  112. }
  113. // --------------------------------------------------------------------
  114. /**
  115. * Run Template Engine
  116. *
  117. * Upon a Page or a Preview, it Runs the Processing of a Template baed on URI request or method arguments
  118. *
  119. * @access public
  120. * @param string
  121. * @param string
  122. * @return void
  123. */
  124. function run_template_engine($template_group = '', $template = '')
  125. {
  126. $this->log_item(" - Begin Template Processing - ");
  127. // Set the name of the cache folder for both tag and page caching
  128. if ($this->EE->uri->uri_string != '')
  129. {
  130. $this->t_cache_path .= md5($this->EE->functions->fetch_site_index().$this->EE->uri->uri_string).'/';
  131. $this->p_cache_path .= md5($this->EE->functions->fetch_site_index().$this->EE->uri->uri_string).'/';
  132. }
  133. else
  134. {
  135. $this->t_cache_path .= md5($this->EE->config->item('site_url').'index'.$this->EE->uri->query_string).'/';
  136. $this->p_cache_path .= md5($this->EE->config->item('site_url').'index'.$this->EE->uri->query_string).'/';
  137. }
  138. // We limit the total number of cache files in order to
  139. // keep some sanity with large sites or ones that get
  140. // hit by over-ambitious crawlers.
  141. if ($this->disable_caching == FALSE)
  142. {
  143. if ($dh = @opendir(APPPATH.'cache/page_cache'))
  144. {
  145. $i = 0;
  146. while (FALSE !== (readdir($dh)))
  147. {
  148. $i++;
  149. }
  150. $max = ( ! $this->EE->config->item('max_caches') OR ! is_numeric($this->EE->config->item('max_caches')) OR $this->EE->config->item('max_caches') > 1000) ? 1000 : $this->EE->config->item('max_caches');
  151. if ($i > $max)
  152. {
  153. $this->EE->functions->clear_caching('page');
  154. }
  155. }
  156. }
  157. $this->log_item("URI: ".$this->EE->uri->uri_string);
  158. $this->log_item("Path.php Template: {$template_group}/{$template}");
  159. $this->fetch_and_parse($template_group, $template, FALSE);
  160. $this->log_item(" - End Template Processing - ");
  161. $this->log_item("Parse Global Variables");
  162. if ($this->template_type == 'static')
  163. {
  164. $this->final_template = $this->restore_xml_declaration($this->final_template);
  165. }
  166. else
  167. {
  168. $this->final_template = $this->parse_globals($this->final_template);
  169. }
  170. $this->log_item("Template Parsing Finished");
  171. $this->EE->output->out_type = $this->template_type;
  172. $this->EE->output->set_output($this->final_template);
  173. }
  174. // --------------------------------------------------------------------
  175. /**
  176. * Fetch and Process Template
  177. *
  178. * Determines what template to process, fetches the template and its preferences, and then processes all of it
  179. *
  180. * @access public
  181. * @param string
  182. * @param string
  183. * @param bool
  184. * @param int
  185. * @return void
  186. */
  187. function fetch_and_parse($template_group = '', $template = '', $sub = FALSE, $site_id = '')
  188. {
  189. // add this template to our subtemplate tracker
  190. $this->templates_sofar = $this->templates_sofar.'|'.$site_id.':'.$template_group.'/'.$template.'|';
  191. /** -------------------------------------
  192. /** Fetch the requested template
  193. /** -------------------------------------*/
  194. // The template can either come from the DB or a cache file
  195. // Do not use a reference!
  196. $this->cache_status = 'NO_CACHE';
  197. $this->log_item("Retrieving Template");
  198. $this->template = ($template_group != '' AND $template != '') ? $this->fetch_template($template_group, $template, FALSE, $site_id) : $this->parse_template_uri();
  199. $this->log_item("Template Type: ".$this->template_type);
  200. $this->parse($this->template, $sub, $site_id);
  201. }
  202. // --------------------------------------------------------------------
  203. /**
  204. * Parse a string as a template
  205. *
  206. * @access public
  207. * @param string
  208. * @param string
  209. * @return void
  210. */
  211. function parse(&$str, $sub = FALSE, $site_id = '')
  212. {
  213. if ($str != '')
  214. {
  215. $this->template =& $str;
  216. }
  217. /** -------------------------------------
  218. /** Static Content, No Parsing
  219. /** -------------------------------------*/
  220. if ($this->template_type == 'static' OR $this->embed_type == 'static')
  221. {
  222. if ($sub == FALSE)
  223. {
  224. $this->final_template = $this->template;
  225. }
  226. return;
  227. }
  228. /* -------------------------------------
  229. /* "Smart" Static Parsing
  230. /*
  231. /* Performed on embedded webpage templates only that do not have
  232. /* ExpressionEngine tags or PHP in them.
  233. /*
  234. /* Hidden Configuration Variable
  235. /* - smart_static_parsing => Bypass parsing of templates that could be
  236. /* of the type 'static' but aren't? (y/n)
  237. /* -------------------------------------*/
  238. if ($this->EE->config->item('smart_static_parsing') !== 'n' && $this->embed_type == 'webpage' && ! stristr($this->template, LD) && ! stristr($this->template, '<?'))
  239. {
  240. $this->log_item("Smart Static Parsing Triggered");
  241. if ($sub == FALSE)
  242. {
  243. $this->final_template = $this->template;
  244. }
  245. return;
  246. }
  247. /** --------------------------------------------------
  248. /** Parse 'Site' variables
  249. /** --------------------------------------------------*/
  250. $this->log_item("Parsing Site Variables");
  251. // load site variables into the global_vars array
  252. foreach (array('site_id', 'site_label', 'site_short_name') as $site_var)
  253. {
  254. $this->EE->config->_global_vars[$site_var] = stripslashes($this->EE->config->item($site_var));
  255. }
  256. // Parse {last_segment} variable
  257. $seg_array = $this->EE->uri->segment_array();
  258. $this->EE->config->_global_vars['last_segment'] = end($seg_array);
  259. /** -------------------------------------
  260. /** Parse manual variables and Snippets
  261. /** -------------------------------------*/
  262. // These are variables that can be set in the path.php file
  263. if (count($this->EE->config->_global_vars) > 0)
  264. {
  265. $this->log_item("Snippets (Keys): ".implode('|', array_keys($this->EE->config->_global_vars)));
  266. $this->log_item("Snippets (Values): ".trim(implode('|', $this->EE->config->_global_vars)));
  267. foreach ($this->EE->config->_global_vars as $key => $val)
  268. {
  269. $this->template = str_replace(LD.$key.RD, $val, $this->template);
  270. }
  271. // in case any of these variables have EE comments of their own
  272. $this->template = $this->remove_ee_comments($this->template);
  273. }
  274. /** -------------------------------------
  275. /** Parse URI segments
  276. /** -------------------------------------*/
  277. // This code lets admins fetch URI segments which become
  278. // available as: {segment_1} {segment_2}
  279. for ($i = 1; $i < 10; $i++)
  280. {
  281. $this->template = str_replace(LD.'segment_'.$i.RD, $this->EE->uri->segment($i), $this->template);
  282. $this->segment_vars['segment_'.$i] = $this->EE->uri->segment($i);
  283. }
  284. /** -------------------------------------
  285. /** Parse {embed} tag variables
  286. /** -------------------------------------*/
  287. if ($sub === TRUE && count($this->embed_vars) > 0)
  288. {
  289. $this->log_item("Embed Variables (Keys): ".implode('|', array_keys($this->embed_vars)));
  290. $this->log_item("Embed Variables (Values): ".trim(implode('|', $this->embed_vars)));
  291. foreach ($this->embed_vars as $key => $val)
  292. {
  293. // add 'embed:' to the key for replacement and so these variables work in conditionals
  294. $this->embed_vars['embed:'.$key] = $val;
  295. unset($this->embed_vars[$key]);
  296. $this->template = str_replace(LD.'embed:'.$key.RD, $val, $this->template);
  297. }
  298. }
  299. // cleanup of leftover/undeclared embed variables
  300. // don't worry with undeclared embed: vars in conditionals as the conditionals processor will handle that adequately
  301. if (strpos($this->template, LD.'embed:') !== FALSE)
  302. {
  303. $this->template = preg_replace('/'.LD.'embed:(.+?)'.RD.'/', '', $this->template);
  304. }
  305. /** -------------------------------------
  306. /** Parse date format string "constants"
  307. /** -------------------------------------*/
  308. $date_constants = array('DATE_ATOM' => '%Y-%m-%dT%H:%i:%s%Q',
  309. 'DATE_COOKIE' => '%l, %d-%M-%y %H:%i:%s UTC',
  310. 'DATE_ISO8601' => '%Y-%m-%dT%H:%i:%s%O',
  311. 'DATE_RFC822' => '%D, %d %M %y %H:%i:%s %O',
  312. 'DATE_RFC850' => '%l, %d-%M-%y %H:%m:%i UTC',
  313. 'DATE_RFC1036' => '%D, %d %M %y %H:%i:%s %O',
  314. 'DATE_RFC1123' => '%D, %d %M %Y %H:%i:%s %O',
  315. 'DATE_RFC2822' => '%D, %d %M %Y %H:%i:%s %O',
  316. 'DATE_RSS' => '%D, %d %M %Y %H:%i:%s %O',
  317. 'DATE_W3C' => '%Y-%m-%dT%H:%i:%s%Q'
  318. );
  319. foreach ($date_constants as $key => $val)
  320. {
  321. $this->template = str_replace(LD.$key.RD, $val, $this->template);
  322. }
  323. $this->log_item("Parse Date Format String Constants");
  324. /** --------------------------------------------------
  325. /** Template's Last Edit time {template_edit_date format="%Y %m %d %H:%i:%s"}
  326. /** --------------------------------------------------*/
  327. if (strpos($this->template, LD.'template_edit_date') !== FALSE && preg_match_all("/".LD."template_edit_date\s+format=([\"\'])([^\\1]*?)\\1".RD."/", $this->template, $matches))
  328. {
  329. for ($j = 0; $j < count($matches[0]); $j++)
  330. {
  331. $this->template = str_replace($matches[0][$j], $this->EE->localize->decode_date($matches[2][$j], $this->template_edit_date), $this->template);
  332. }
  333. }
  334. /** --------------------------------------------------
  335. /** Current time {current_time format="%Y %m %d %H:%i:%s"}
  336. /** --------------------------------------------------*/
  337. if (strpos($this->template, LD.'current_time') !== FALSE && preg_match_all("/".LD."current_time\s+format=([\"\'])([^\\1]*?)\\1".RD."/", $this->template, $matches))
  338. {
  339. for ($j = 0; $j < count($matches[0]); $j++)
  340. {
  341. $this->template = str_replace($matches[0][$j], $this->EE->localize->decode_date($matches[2][$j], $this->EE->localize->now), $this->template);
  342. }
  343. }
  344. $this->template = str_replace(LD.'current_time'.RD, $this->EE->localize->now, $this->template);
  345. $this->log_item("Parse Current Time Variables");
  346. /** -------------------------------------
  347. /** Is the main template cached?
  348. /** -------------------------------------*/
  349. // If a cache file exists for the primary template
  350. // there is no reason to go further.
  351. // However we do need to fetch any subtemplates
  352. if ($this->cache_status == 'CURRENT' AND $sub == FALSE)
  353. {
  354. $this->log_item("Cached Template Used");
  355. $this->template = $this->parse_nocache($this->template);
  356. /** -------------------------------------
  357. /** Smite Our Enemies: Advanced Conditionals
  358. /** -------------------------------------*/
  359. if (stristr($this->template, LD.'if'))
  360. {
  361. $this->template = $this->advanced_conditionals($this->template);
  362. }
  363. $this->log_item("Conditionals Parsed, Processing Sub Templates");
  364. $this->final_template = $this->template;
  365. $this->process_sub_templates($this->template);
  366. return;
  367. }
  368. // Remove whitespace from variables.
  369. // This helps prevent errors, particularly if PHP is used in a template
  370. $this->template = preg_replace("/".LD."\s*(\S+)\s*".RD."/U", LD."\\1".RD, $this->template);
  371. /** -------------------------------------
  372. /** Parse Input Stage PHP
  373. /** -------------------------------------*/
  374. if ($this->parse_php == TRUE AND $this->php_parse_location == 'input' AND $this->cache_status != 'CURRENT')
  375. {
  376. $this->log_item("Parsing PHP on Input");
  377. $this->template = $this->parse_template_php($this->template);
  378. }
  379. /** -------------------------------------
  380. /** Smite Our Enemies: Conditionals
  381. /** -------------------------------------*/
  382. $this->log_item("Parsing Segment, Embed, and Global Vars Conditionals");
  383. $this->template = $this->parse_simple_segment_conditionals($this->template);
  384. $this->template = $this->simple_conditionals($this->template, $this->embed_vars);
  385. $this->template = $this->simple_conditionals($this->template, $this->EE->config->_global_vars);
  386. /** -------------------------------------
  387. /** Assign Variables
  388. /** -------------------------------------*/
  389. if (strpos($this->template, 'preload_replace') !== FALSE)
  390. {
  391. if (preg_match_all("/".LD."preload_replace:(.+?)=([\"\'])([^\\2]*?)\\2".RD."/i", $this->template, $matches))
  392. {
  393. $this->log_item("Processing Preload Text Replacements: ".trim(implode('|', $matches[1])));
  394. for ($j = 0; $j < count($matches[0]); $j++)
  395. {
  396. $this->template = str_replace($matches[0][$j], "", $this->template);
  397. $this->template = str_replace(LD.$matches[1][$j].RD, $matches[3][$j], $this->template);
  398. }
  399. }
  400. }
  401. /** -------------------------------------
  402. /** Parse Plugin and Module Tags
  403. /** -------------------------------------*/
  404. $this->tags();
  405. if ($this->cease_processing === TRUE)
  406. {
  407. return;
  408. }
  409. /** -------------------------------------
  410. /** Parse Output Stage PHP
  411. /** -------------------------------------*/
  412. if ($this->parse_php == TRUE AND $this->php_parse_location == 'output' AND $this->cache_status != 'CURRENT')
  413. {
  414. $this->log_item("Parsing PHP on Output");
  415. $this->template = $this->parse_template_php($this->template);
  416. }
  417. /** -------------------------------------
  418. /** Write the cache file if needed
  419. /** -------------------------------------*/
  420. if ($this->cache_status == 'EXPIRED')
  421. {
  422. $this->template = $this->EE->functions->insert_action_ids($this->template);
  423. $this->write_cache_file($this->cache_hash, $this->template, 'template');
  424. }
  425. /** -------------------------------------
  426. /** Parse Our Uncacheable Forms
  427. /** -------------------------------------*/
  428. $this->template = $this->parse_nocache($this->template);
  429. /** -------------------------------------
  430. /** Smite Our Enemies: Advanced Conditionals
  431. /** -------------------------------------*/
  432. if (strpos($this->template, LD.'if') !== FALSE)
  433. {
  434. $this->log_item("Processing Advanced Conditionals");
  435. $this->template = $this->advanced_conditionals($this->template);
  436. }
  437. /** -------------------------------------
  438. /** Build finalized template
  439. /** -------------------------------------*/
  440. // We only do this on the first pass.
  441. // The sub-template routine will insert embedded
  442. // templates into the master template
  443. if ($sub == FALSE)
  444. {
  445. $this->final_template = $this->template;
  446. $this->process_sub_templates($this->template);
  447. }
  448. }
  449. // --------------------------------------------------------------------
  450. /**
  451. * Processes Any Embedded Templates in String
  452. *
  453. * If any {embed=} tags are found, it processes those templates and does a replacement.
  454. *
  455. * @access public
  456. * @param string
  457. * @return void
  458. */
  459. function process_sub_templates($template)
  460. {
  461. /** -------------------------------------
  462. /** Match all {embed=bla/bla} tags
  463. /** -------------------------------------*/
  464. $matches = array();
  465. if ( ! preg_match_all("/(".LD."embed\s*=)(.*?)".RD."/s", $template, $matches))
  466. {
  467. return;
  468. }
  469. /** -------------------------------------
  470. /** Loop until we have parsed all sub-templates
  471. /** -------------------------------------*/
  472. // For each embedded tag we encounter we'll run the template parsing
  473. // function - AND - through the beauty of recursive functions we
  474. // will also call THIS function as well, allowing us to parse
  475. // infinitely nested sub-templates in one giant loop o' love
  476. $this->log_item(" - Processing Sub Templates (Depth: ".($this->depth+1).") - ");
  477. $i = 0;
  478. $this->depth++;
  479. $this->log_item("List of Embeds: ".str_replace(array('"', "'"), '', trim(implode(',', $matches[2]))));
  480. // re-match the full tag of each if necessary before we start processing
  481. // necessary evil in case template globals are used inside the embed tag,
  482. // doing this within the processing loop will result in leaving unparsed
  483. // embed tags e.g. {embed="foo/bar" var="{global_var}/{custom_field}"}
  484. $temp = $template;
  485. foreach ($matches[2] as $key => $val)
  486. {
  487. if (strpos($val, LD) !== FALSE)
  488. {
  489. $matches[0][$key] = $this->EE->functions->full_tag($matches[0][$key], $temp);
  490. $matches[2][$key] = substr(str_replace($matches[1][$key], '', $matches[0][$key]), 0, -1);
  491. $temp = str_replace($matches[0][$key], '', $temp);
  492. }
  493. }
  494. // Load the string helper
  495. $this->EE->load->helper('string');
  496. foreach($matches[2] as $key => $val)
  497. {
  498. $parts = preg_split("/\s+/", $val, 2);
  499. $this->embed_vars = (isset($parts[1])) ? $this->EE->functions->assign_parameters($parts[1]) : array();
  500. if ($this->embed_vars === FALSE)
  501. {
  502. $this->embed_vars = array();
  503. }
  504. $val = trim_slashes(strip_quotes($parts[0]));
  505. if (strpos($val, '/') === FALSE)
  506. {
  507. continue;
  508. }
  509. $ex = explode("/", trim($val));
  510. if (count($ex) != 2)
  511. {
  512. continue;
  513. }
  514. /** ----------------------------------
  515. /** Determine Site
  516. /** ----------------------------------*/
  517. $site_id = $this->EE->config->item('site_id');
  518. if (stristr($ex[0], ':'))
  519. {
  520. $name = substr($ex[0], 0, strpos($ex[0], ':'));
  521. if ($this->EE->config->item('multiple_sites_enabled') == 'y' && ! IS_FREELANCER)
  522. {
  523. if (count($this->sites) == 0)
  524. {
  525. // This should really be cached somewhere
  526. $this->EE->db->select('site_id, site_name');
  527. $sites_query = $this->EE->db->get('sites');
  528. foreach($sites_query->result_array() as $row)
  529. {
  530. $this->sites[$row['site_id']] = $row['site_name'];
  531. }
  532. }
  533. $site_id = array_search($name, $this->sites);
  534. if (empty($site_id))
  535. {
  536. $site_id = $this->EE->config->item('site_id');
  537. }
  538. }
  539. $ex[0] = str_replace($name.':', '', $ex[0]);
  540. }
  541. /** ----------------------------------
  542. /** Loop Prevention
  543. /** ----------------------------------*/
  544. /* -------------------------------------------
  545. /* Hidden Configuration Variable
  546. /* - template_loop_prevention => 'n'
  547. Whether or not loop prevention is enabled - y/n
  548. /* -------------------------------------------*/
  549. if (substr_count($this->templates_sofar, '|'.$site_id.':'.$ex['0'].'/'.$ex['1'].'|') > 1 && $this->EE->config->item('template_loop_prevention') != 'n')
  550. {
  551. $this->final_template = ($this->EE->config->item('debug') >= 1) ? str_replace('%s', $ex['0'].'/'.$ex['1'], $this->EE->lang->line('template_loop')) : "";
  552. return;
  553. }
  554. /** ----------------------------------
  555. /** Process Subtemplate
  556. /** ----------------------------------*/
  557. $this->log_item("Processing Sub Template: ".$ex[0]."/".$ex[1]);
  558. $this->fetch_and_parse($ex[0], $ex[1], TRUE, $site_id);
  559. $this->final_template = str_replace($matches[0][$key], $this->template, $this->final_template);
  560. $this->embed_type = '';
  561. // Here we go again! Wheeeeeee.....
  562. $this->process_sub_templates($this->template);
  563. // pull the subtemplate tracker back a level to the parent template
  564. $this->templates_sofar = substr($this->templates_sofar, 0, - strlen('|'.$site_id.':'.$ex[0].'/'.$ex[1].'|'));
  565. }
  566. $this->depth--;
  567. if ($this->depth == 0)
  568. {
  569. $this->templates_sofar = '';
  570. }
  571. }
  572. // --------------------------------------------------------------------
  573. /**
  574. * Finds Tags, Parses Them
  575. *
  576. * Goes Through the Template, Finds the Beginning and End of Tags, and Stores Tag Data in a Class Array
  577. *
  578. * @access public
  579. * @return void
  580. */
  581. function parse_tags()
  582. {
  583. while (TRUE)
  584. {
  585. // Make a "floating" copy of the template which we'll progressively slice into pieces with each loop
  586. $this->fl_tmpl = $this->template;
  587. // Identify the string position of the first occurence of a matched tag
  588. $this->in_point = strpos($this->fl_tmpl, LD.'exp:');
  589. // If the above variable returns FALSE we are done looking for tags
  590. // This single conditional keeps the template engine from spiraling
  591. // out of control in an infinite loop.
  592. if (FALSE === $this->in_point)
  593. {
  594. break;
  595. }
  596. else
  597. {
  598. /** ------------------------------------------
  599. /** Process the tag data
  600. /** ------------------------------------------*/
  601. // These REGEXs parse out the various components contained in any given tag.
  602. // Grab the opening portion of the tag: {exp:some:tag param="value" param="value"}
  603. if ( ! preg_match("/".LD.'exp:'.".*?".RD."/s", $this->fl_tmpl, $matches))
  604. {
  605. $this->template = preg_replace("/".LD.'exp:'.".*?$/", '', $this->template);
  606. break;
  607. }
  608. // Checking for variables/tags embedded within tags
  609. // {exp:channel:entries channel="{master_channel_name}"}
  610. if (stristr(substr($matches[0], 1), LD) !== FALSE)
  611. {
  612. $matches[0] = $this->EE->functions->full_tag($matches[0]);
  613. }
  614. $this->log_item("Tag: ".$matches[0]);
  615. $raw_tag = str_replace(array("\r\n", "\r", "\n", "\t"), " ", $matches[0]);
  616. $tag_length = strlen($raw_tag);
  617. $data_start = $this->in_point + $tag_length;
  618. $tag = trim(substr($raw_tag, 1, -1));
  619. $args = trim((preg_match("/\s+.*/", $tag, $matches))) ? $matches[0] : '';
  620. $tag = trim(str_replace($args, '', $tag));
  621. $cur_tag_close = LD.'/'.$tag.RD;
  622. // Deprecate "weblog" tags, but allow them to work until 2.1, then remove this.
  623. if (strpos($tag, ':weblog:') !== FALSE OR strpos($tag, ' weblog=') !== FALSE)
  624. {
  625. $tag = str_replace(array(':weblog:', ' weblog='), array(':channel:', ' channel='), $tag);
  626. $this->log_item("WARNING: Deprecated 'weblog' tag used, please change to 'channel'");
  627. }
  628. // -----------------------------------------
  629. // Grab the class name and method names contained in the tag
  630. $class = explode(':', substr($tag, strlen('exp') + 1));
  631. // Tags can either have one segment or two:
  632. // {exp:first_segment}
  633. // {exp:first_segment:second_segment}
  634. //
  635. // These two segments represent either a "class:constructor"
  636. // or a "class:method". We need to determine which one it is.
  637. if (count($class) == 1)
  638. {
  639. $class[1] = $class[0];
  640. }
  641. foreach($class as $key => $value)
  642. {
  643. $class[$key] = trim($value);
  644. }
  645. // -----------------------------------------
  646. // Assign parameters based on the arguments from the tag
  647. $args = $this->EE->functions->assign_parameters($args);
  648. // standardized mechanism for "search" type parameters get some extra lovin'
  649. $search_fields = array();
  650. if ($args !== FALSE)
  651. {
  652. foreach ($args as $key => $val)
  653. {
  654. if (strncmp($key, 'search:', 7) == 0)
  655. {
  656. $search_fields[substr($key, 7)] = $val;
  657. }
  658. }
  659. }
  660. // Trim the floating template, removing the tag we just parsed.
  661. $this->fl_tmpl = substr($this->fl_tmpl, $this->in_point + $tag_length);
  662. $out_point = strpos($this->fl_tmpl, $cur_tag_close);
  663. // Do we have a tag pair?
  664. if (FALSE !== $out_point)
  665. {
  666. // Assign the data contained between the opening/closing tag pair
  667. $this->log_item("Closing Tag Found");
  668. $block = substr($this->template, $data_start, $out_point);
  669. // Fetch the "no_results" data
  670. $no_results = '';
  671. $no_results_block = '';
  672. if (strpos($block, 'if no_results') !== FALSE && preg_match("/".LD."if no_results".RD."(.*?)".LD.'\/'."if".RD."/s", $block, $match))
  673. {
  674. // Match the entirety of the conditional, dude. Bad Rick!
  675. if (stristr($match[1], LD.'if'))
  676. {
  677. $match[0] = $this->EE->functions->full_tag($match[0], $block, LD.'if', LD.'\/'."if".RD);
  678. }
  679. $no_results = substr($match[0], strlen(LD."if no_results".RD), -strlen(LD.'/'."if".RD));
  680. $no_results_block = $match[0];
  681. }
  682. // Define the entire "chunk" - from the left edge of the opening tag
  683. // to the right edge of closing tag.
  684. $out_point = $out_point + $tag_length + strlen($cur_tag_close);
  685. $chunk = substr($this->template, $this->in_point, $out_point);
  686. }
  687. else
  688. {
  689. // Single tag...
  690. $this->log_item("No Closing Tag");
  691. $block = ''; // Single tags don't contain data blocks
  692. $no_results = '';
  693. $no_results_block = '';
  694. // Define the entire opening tag as a "chunk"
  695. $chunk = substr($this->template, $this->in_point, $tag_length);
  696. }
  697. // Strip the "chunk" from the template, replacing it with a unique marker.
  698. if (stristr($raw_tag, 'random'))
  699. {
  700. $this->template = preg_replace("|".preg_quote($chunk)."|s", 'M'.$this->loop_count.$this->marker, $this->template, 1);
  701. }
  702. else
  703. {
  704. $this->template = str_replace($chunk, 'M'.$this->loop_count.$this->marker, $this->template);
  705. }
  706. $cfile = md5($chunk); // This becomes the name of the cache file
  707. // Build a multi-dimensional array containing all of the tag data we've assembled
  708. $this->tag_data[$this->loop_count]['tag'] = $raw_tag;
  709. $this->tag_data[$this->loop_count]['class'] = $class[0];
  710. $this->tag_data[$this->loop_count]['method'] = $class[1];
  711. $this->tag_data[$this->loop_count]['tagparts'] = $class;
  712. $this->tag_data[$this->loop_count]['params'] = $args;
  713. $this->tag_data[$this->loop_count]['chunk'] = $chunk; // Matched data block - including opening/closing tags
  714. $this->tag_data[$this->loop_count]['block'] = $block; // Matched data block - no tags
  715. $this->tag_data[$this->loop_count]['cache'] = $args;
  716. $this->tag_data[$this->loop_count]['cfile'] = $cfile;
  717. $this->tag_data[$this->loop_count]['no_results'] = $no_results;
  718. $this->tag_data[$this->loop_count]['no_results_block'] = $no_results_block;
  719. $this->tag_data[$this->loop_count]['search_fields'] = $search_fields;
  720. } // END IF
  721. // Increment counter
  722. $this->loop_count++;
  723. } // END WHILE
  724. }
  725. // --------------------------------------------------------------------
  726. /**
  727. * Looks Through Template Looking for Tags
  728. *
  729. * Goes Through the Template, Finds the Beginning and End of Tags, and Stores Tag Data in a Class Array
  730. *
  731. * @access public
  732. * @return void
  733. */
  734. function tags()
  735. {
  736. // Fetch installed modules and plugins if needed
  737. if (count($this->modules) == 0)
  738. {
  739. $this->fetch_addons();
  740. }
  741. // Parse the template.
  742. $this->log_item(" - Beginning Tag Processing - ");
  743. while (is_int(strpos($this->template, LD.'exp:')))
  744. {
  745. // Initialize values between loops
  746. $this->tag_data = array();
  747. $this->var_single = array();
  748. $this->var_cond = array();
  749. $this->var_pair = array();
  750. $this->loop_count = 0;
  751. $this->log_item("Parsing Tags in Template");
  752. // Run the template parser
  753. $this->parse_tags();
  754. $this->log_item("Processing Tags");
  755. // Run the class/method handler
  756. $this->process_tags();
  757. if ($this->cease_processing === TRUE)
  758. {
  759. return;
  760. }
  761. }
  762. $this->log_item(" - End Tag Processing - ");
  763. }
  764. // --------------------------------------------------------------------
  765. /**
  766. * Process Tags
  767. *
  768. * Takes the Class Array Full of Tag Data and Processes the Tags One by One. Class class, feeds
  769. * data to class, takes results, and puts it back into the Template.
  770. *
  771. * @access public
  772. * @return void
  773. */
  774. function process_tags()
  775. {
  776. $plugins = array();
  777. $modules = array();
  778. // Fill an array with the names of all the classes that we previously extracted from the tags
  779. for ($i = 0, $ctd = count($this->tag_data); $i < $ctd; $i++)
  780. {
  781. // Check the tag cache file
  782. $cache_contents = $this->fetch_cache_file($this->tag_data[$i]['cfile'], 'tag', $this->tag_data[$i]['cache']);
  783. // Set cache status for final processing
  784. $this->tag_data[$i]['cache'] = $this->tag_cache_status;
  785. if ($this->tag_cache_status == 'CURRENT')
  786. {
  787. // If so, replace the marker in the tag with the cache data
  788. $this->log_item("Tag Cached and Cache is Current");
  789. $this->template = str_replace('M'.$i.$this->marker, $cache_contents, $this->template);
  790. }
  791. else
  792. {
  793. // Is a module or plug-in being requested?
  794. if ( ! in_array($this->tag_data[$i]['class'] , $this->modules))
  795. {
  796. if ( ! in_array($this->tag_data[$i]['class'] , $this->plugins))
  797. {
  798. $this->log_item("Invalid Tag");
  799. if ($this->EE->config->item('debug') >= 1)
  800. {
  801. if ($this->tag_data[$i]['tagparts'][0] == $this->tag_data[$i]['tagparts'][1] &&
  802. ! isset($this->tag_data[$i]['tagparts'][2]))
  803. {
  804. unset($this->tag_data[$i]['tagparts'][1]);
  805. }
  806. $error = $this->EE->lang->line('error_tag_syntax');
  807. $error .= '<br /><br />';
  808. $error .= htmlspecialchars(LD);
  809. $error .= 'exp:'.implode(':', $this->tag_data[$i]['tagparts']);
  810. $error .= htmlspecialchars(RD);
  811. $error .= '<br /><br />';
  812. $error .= $this->EE->lang->line('error_fix_syntax');
  813. $this->EE->output->fatal_error($error);
  814. }
  815. else
  816. return FALSE;
  817. }
  818. else
  819. {
  820. $plugins[] = $this->tag_data[$i]['class'];
  821. $this->log_item("Plugin Tag: ".ucfirst($this->tag_data[$i]['class']).'/'.$this->tag_data[$i]['method']);
  822. }
  823. }
  824. else
  825. {
  826. $modules[] = $this->tag_data[$i]['class'];
  827. $this->log_item("Module Tag: ".ucfirst($this->tag_data[$i]['class']).'/'.$this->tag_data[$i]['method']);
  828. }
  829. }
  830. }
  831. // Remove duplicate class names and re-order the array
  832. $plugins = array_values(array_unique($plugins));
  833. $modules = array_values(array_unique($modules));
  834. // Dynamically require the file that contains each class
  835. $this->log_item("Including Files for Plugins and Modules");
  836. foreach ($plugins as $plugin)
  837. {
  838. // make sure it's not already included just in case
  839. if ( ! class_exists($plugin))
  840. {
  841. if (in_array($plugin ,$this->EE->core->native_plugins))
  842. {
  843. require_once PATH_PI."pi.{$plugin}".EXT;
  844. }
  845. else
  846. {
  847. require_once PATH_THIRD."{$plugin}/pi.{$plugin}".EXT;
  848. }
  849. }
  850. }
  851. foreach ($modules as $module)
  852. {
  853. // make sure it's not already included just in case
  854. if ( ! class_exists($module))
  855. {
  856. if (in_array($module, $this->EE->core->native_modules))
  857. {
  858. require_once PATH_MOD."{$module}/mod.{$module}".EXT;
  859. }
  860. else
  861. {
  862. require_once PATH_THIRD."{$module}/mod.{$module}".EXT;
  863. }
  864. }
  865. }
  866. $this->log_item("Files for Plugins and Modules All Included");
  867. /** -----------------------------------
  868. /** Only Retrieve Data if Not Done Before and Modules Being Called
  869. /** -----------------------------------*/
  870. if (count($this->module_data) == 0 && count(array_intersect($this->modules, $modules)) > 0)
  871. {
  872. $this->EE->db->select('module_version, module_name');
  873. $query = $this->EE->db->get('modules');
  874. foreach($query->result_array() as $row)
  875. {
  876. $this->module_data[$row['module_name']] = array('version' => $row['module_version']);
  877. }
  878. }
  879. // Final data processing
  880. // Loop through the master array containing our extracted template data
  881. $this->log_item("Beginning Final Tag Data Processing");
  882. reset($this->tag_data);
  883. for ($i = 0; $i < count($this->tag_data); $i++)
  884. {
  885. if ($this->tag_data[$i]['cache'] != 'CURRENT')
  886. {
  887. $this->log_item("Calling Class/Method: ".ucfirst($this->tag_data[$i]['class'])."/".$this->tag_data[$i]['method']);
  888. /* ---------------------------------
  889. /* Plugin as Parameter
  890. /*
  891. /* - Example: channel="{exp:some_plugin}"
  892. /* - A bit of a hidden feature. Has been tested but not quite
  893. /* ready to say it is ready for prime time as I might want to
  894. /* move it to earlier in processing so that if there are
  895. /* multiple plugins being used as parameters it is only called
  896. /* once instead of for every single parameter. - Paul
  897. /* ---------------------------------*/
  898. if (substr_count($this->tag_data[$i]['tag'], LD.'exp') > 1 && isset($this->tag_data[$i]['params']['parse']) && $this->tag_data[$i]['params']['parse'] == 'inward')
  899. {
  900. foreach($this->tag_data[$i]['params'] as $name => $param)
  901. {
  902. if (stristr($this->tag_data[$i]['params'][$name], LD.'exp'))
  903. {
  904. $this->log_item("Plugin in Parameter, Processing Plugin First");
  905. $TMPL2 = $this->EE->functions->clone_object($this);
  906. while (is_int(strpos($TMPL2->tag_data[$i]['params'][$name], LD.'exp:')))
  907. {
  908. unset($this->EE->TMPL);
  909. $this->EE->TMPL = new EE_Template();
  910. $this->EE->TMPL->start_microtime = $this->start_microtime;
  911. $this->EE->TMPL->template = $TMPL2->tag_data[$i]['params'][$name];
  912. $this->EE->TMPL->tag_data = array();
  913. $this->EE->TMPL->var_single = array();
  914. $this->EE->TMPL->var_cond = array();
  915. $this->EE->TMPL->var_pair = array();
  916. $this->EE->TMPL->plugins = $TMPL2->plugins;
  917. $this->EE->TMPL->modules = $TMPL2->modules;
  918. $this->EE->TMPL->parse_tags();
  919. $this->EE->TMPL->process_tags();
  920. $this->EE->TMPL->loop_count = 0;
  921. $TMPL2->tag_data[$i]['params'][$name] = $this->EE->TMPL->template;
  922. $TMPL2->log = array_merge($TMPL2->log, $this->EE->TMPL->log);
  923. }
  924. foreach (get_object_vars($TMPL2) as $key => $value)
  925. {
  926. $this->$key = $value;
  927. }
  928. unset($TMPL2);
  929. $this->EE->TMPL = $this;
  930. }
  931. }
  932. }
  933. /** ---------------------------------
  934. /** Nested Plugins...
  935. /** ---------------------------------*/
  936. if (in_array($this->tag_data[$i]['class'] , $this->plugins) && strpos($this->tag_data[$i]['block'], LD.'exp:') !== FALSE)
  937. {
  938. if ( ! isset($this->tag_data[$i]['params']['parse']) OR $this->tag_data[$i]['params']['parse'] != 'inward')
  939. {
  940. $this->log_item("Nested Plugins in Tag, Parsing Outward First");
  941. $TMPL2 = $this->EE->functions->clone_object($this);
  942. while (is_int(strpos($TMPL2->tag_data[$i]['block'], LD.'exp:')))
  943. {
  944. unset($this->EE->TMPL);
  945. $this->EE->TMPL = new EE_Template();
  946. $this->EE->TMPL->start_microtime = $this->start_microtime;
  947. $this->EE->TMPL->template = $TMPL2->tag_data[$i]['block'];
  948. $this->EE->TMPL->tag_data = array();
  949. $this->EE->TMPL->var_single = array();
  950. $this->EE->TMPL->var_cond = array();
  951. $this->EE->TMPL->var_pair = array();
  952. $this->EE->TMPL->plugins = $TMPL2->plugins;
  953. $this->EE->TMPL->modules = $TMPL2->modules;
  954. $this->EE->TMPL->parse_tags();
  955. $this->EE->TMPL->process_tags();
  956. $this->EE->TMPL->loop_count = 0;
  957. $TMPL2->tag_data[$i]['block'] = $this->EE->TMPL->template;
  958. $TMPL2->log = array_merge($TMPL2->log, $this->EE->TMPL->log);
  959. }
  960. foreach (get_object_vars($TMPL2) as $key => $value)
  961. {
  962. $this->$key = $value;
  963. }
  964. unset($TMPL2);
  965. $this->EE->TMPL = $this;
  966. }
  967. }
  968. // Assign the data chunk, parameters
  969. // We moved the no_results_block here because of nested tags. The first
  970. // parsed tag has priority for that conditional.
  971. $this->tagdata = str_replace($this->tag_data[$i]['no_results_block'], '', $this->tag_data[$i]['block']);
  972. $this->tagparams = $this->tag_data[$i]['params'];
  973. $this->tagchunk = $this->tag_data[$i]['chunk'];
  974. $this->tagproper = $this->tag_data[$i]['tag'];
  975. $this->tagparts = $this->tag_data[$i]['tagparts'];
  976. $this->no_results = $this->tag_data[$i]['no_results'];
  977. $this->search_fields = $this->tag_data[$i]['search_fields'];
  978. /** -------------------------------------
  979. /** Assign Sites for Tag
  980. /** -------------------------------------*/
  981. $this->_fetch_site_ids();
  982. /** -------------------------------------
  983. /** Fetch Form Class/Id Attributes
  984. /** -------------------------------------*/
  985. $this->tag_data[$i] = $this->_assign_form_params($this->tag_data[$i]);
  986. /** -------------------------------------
  987. /** Relationship Data Pulled Out
  988. /** -------------------------------------*/
  989. // If the channel:entries tag or search:search_results is being called
  990. // we need to extract any relationship data that might be present.
  991. // Note: This needs to happen before extracting the variables
  992. // in the tag so it doesn't get confused as to which entry the
  993. // variables belong to.
  994. if (($this->tag_data[$i]['class'] == 'channel' AND $this->tag_data[$i]['method'] == 'entries')
  995. OR ($this->tag_data[$i]['class'] == 'search' AND $this->tag_data[$i]['method'] == 'search_results'))
  996. {
  997. $this->tagdata = $this->assign_relationship_data($this->tagdata);
  998. }
  999. // LEGACY CODE
  1000. // Fetch the variables for this particular tag
  1001. // Hopefully, with Jones' new parsing code we should be able to stop using the
  1002. // assign_variables and assign_conditional_variables() methods entirely. -Paul
  1003. $vars = $this->EE->functions->assign_variables($this->tag_data[$i]['block']);
  1004. if (count($this->related_markers) > 0)
  1005. {
  1006. foreach ($this->related_markers as $mkr)
  1007. {
  1008. if ( ! isset($vars['var_single'][$mkr]))
  1009. {
  1010. $vars['var_single'][$mkr] = $mkr;
  1011. }
  1012. }
  1013. }
  1014. $this->var_single = $vars['var_single'];
  1015. $this->var_pair = $vars['var_pair'];
  1016. if ($this->related_id != '')
  1017. {
  1018. $this->var_single[$this->related_id] = $this->related_id;
  1019. $this->related_id = '';
  1020. }
  1021. // Assign the class name and method name
  1022. $class_name = ucfirst($this->tag_data[$i]['class']);
  1023. $meth_name = $this->tag_data[$i]['method'];
  1024. // If it's a third party class or a first party module,
  1025. // add the root folder to the loader paths so we can use
  1026. // libraries, models, and helpers
  1027. $package_path = '';
  1028. if ( ! in_array($this->tag_data[$i]['class'], $this->EE->core->native_plugins))
  1029. {
  1030. $package_path = in_array($this->tag_data[$i]['class'], $this->EE->core->native_modules) ? PATH_MOD : PATH_THIRD;
  1031. $package_path .= strtolower($this->tag_data[$i]['class'].'/');
  1032. $this->EE->load->add_package_path($package_path);
  1033. }
  1034. // Dynamically instantiate the class.
  1035. // If module, only if it is installed...
  1036. if (in_array($this->tag_data[$i]['class'], $this->modules) && ! isset($this->module_data[$class_name]))
  1037. {
  1038. $this->log_item("Problem Processing Module: Module Not Installed");
  1039. }
  1040. else
  1041. {
  1042. $this->log_item(" -> Class Called: ".$class_name);
  1043. $EE = new $class_name();
  1044. }
  1045. /** ----------------------------------
  1046. /** Does method exist? Is This A Module and Is It Installed?
  1047. /** ----------------------------------*/
  1048. if ((in_array($this->tag_data[$i]['class'], $this->modules) && ! isset($this->module_data[$class_name])) OR ! method_exists($EE, $meth_name))
  1049. {
  1050. $this->log_item("Tag Not Processed: Method Inexistent or Module Not Installed");
  1051. if ($this->EE->config->item('debug') >= 1)
  1052. {
  1053. if ($this->tag_data[$i]['tagparts'][0] == $this->tag_data[$i]['tagparts'][1] &&
  1054. ! isset($this->tag_data[$i]['tagparts'][2]))
  1055. {
  1056. unset($this->tag_data[$i]['tagparts'][1]);
  1057. }
  1058. $error = $this->EE->lang->line('error_tag_module_processing');
  1059. $error .= '<br /><br />';
  1060. $error .= htmlspecialchars(LD);
  1061. $error .= 'exp:'.implode(':', $this->tag_data[$i]['tagparts']);
  1062. $error .= htmlspecialchars(RD);
  1063. $error .= '<br /><br />';
  1064. $error .= str_replace('%x', $this->tag_data[$i]['class'], str_replace('%y', $meth_name, $this->EE->lang->line('error_fix_module_processing')));
  1065. $this->EE->output->fatal_error($error);
  1066. }
  1067. else
  1068. {
  1069. return;
  1070. }
  1071. }
  1072. /*
  1073. OK, lets grab the data returned from the class.
  1074. First, however, lets determine if the tag has one or two segments.
  1075. If it only has one, we don't want to call the constructor again since
  1076. it was already called during instantiation.
  1077. Note: If it only has one segment, only the object constructor will be called.
  1078. Since constructors can't return a value just by initialializing the object
  1079. the output of the class must be assigned to a variable called $this->return_data
  1080. */
  1081. $this->log_item(" -> Method Called: ".$meth_name);
  1082. if (strtolower($class_name) == $meth_name)
  1083. {
  1084. $return_data = (isset($EE->return_data)) ? $EE->return_data : '';
  1085. }
  1086. else
  1087. {
  1088. $return_data = $EE->$meth_name();
  1089. }
  1090. // if it's a third party add-on or module, remove the temporarily added path for local libraries, models, etc.
  1091. // if a "no results" template is returned, $this->tag_data will be reset inside of the scope
  1092. // of the tag being processed. So let's use the locally scoped variable for the class name
  1093. if ($package_path)
  1094. {
  1095. $this->EE->load->remove_package_path();
  1096. }
  1097. /** ----------------------------------
  1098. /** 404 Page Triggered, Cease All Processing of Tags From Now On
  1099. /** ----------------------------------*/
  1100. if ($this->cease_processing === TRUE)
  1101. {
  1102. return;
  1103. }
  1104. $this->log_item(" -> Data Returned");
  1105. // Write cache file if needed
  1106. if ($this->tag_data[$i]['cache'] == 'EXPIRED')
  1107. {
  1108. $this->write_cache_file($this->tag_data[$i]['cfile'], $return_data);
  1109. }
  1110. // Replace the temporary markers we added earlier with the fully parsed data
  1111. $this->template = str_replace('M'.$i.$this->marker, $return_data, $this->template);
  1112. // Initialize data in case there are susequent loops
  1113. $this->var_single = array();
  1114. $this->var_cond = array();
  1115. $this->var_pair = array();
  1116. unset($return_data);
  1117. unset($class_name);
  1118. unset($meth_name);
  1119. unset($EE);
  1120. }
  1121. }
  1122. }
  1123. // --------------------------------------------------------------------
  1124. /**
  1125. * Process Tags
  1126. *
  1127. * Channel entries can have related entries embedded within them.
  1128. * We'll extract the related tag data, stash it away in an array, and
  1129. * replace it with a marker string so that the template parser
  1130. * doesn't see it. In the channel class we'll check to see if the
  1131. * $this->EE->TMPL->related_data array contains anything. If so, we'll celebrate
  1132. * wildly.
  1133. *
  1134. * @access public
  1135. * @param string
  1136. * @return string
  1137. */
  1138. function assign_relationship_data($chunk)
  1139. {
  1140. $this->related_markers = array();
  1141. if (preg_match_all("/".LD."related_entries\s+id\s*=\s*[\"\'](.+?)[\"\']".RD."(.+?)".LD.'\/'."related_entries".RD."/is", $chunk, $matches))
  1142. {
  1143. $this->log_item("Assigning Related Entry Data");
  1144. $no_rel_content = '';
  1145. for ($j = 0; $j < count($matches[0]); $j++)
  1146. {
  1147. $rand = $this->EE->functions->random('alnum', 8);
  1148. $marker = LD.'REL['.$matches[1][$j].']'.$rand.'REL'.RD;
  1149. if (preg_match("/".LD."if no_related_entries".RD."(.*?)".LD.'\/'."if".RD."/s", $matches[2][$j], $no_rel_match))
  1150. {
  1151. // Match the entirety of the conditional
  1152. if (stristr($no_rel_match[1], LD.'if'))
  1153. {
  1154. $match[0] = $this->EE->functions->full_tag($no_rel_match[0], $matches[2][$j], LD.'if', LD.'\/'."if".RD);
  1155. }
  1156. $no_rel_content = substr($no_rel_match[0], strlen(LD."if no_related_entries".RD), -strlen(LD.'/'."if".RD));
  1157. }
  1158. $this->related_markers[] = $matches[1][$j];
  1159. $vars = $this->EE->functions->assign_variables($matches[2][$j]);
  1160. $this->related_id = $matches[1][$j];
  1161. $this->related_data[$rand] = array(
  1162. 'marker' => $rand,
  1163. 'field_name' => $matches[1][$j],
  1164. 'tagdata' => $matches[2][$j],
  1165. 'var_single' => $vars['var_single'],
  1166. 'var_pair' => $vars['var_pair'],
  1167. 'var_cond' => $this->EE->functions->assign_conditional_variables($matches[2][$j], '\/', LD, RD),
  1168. 'no_rel_content' => $no_rel_content
  1169. );
  1170. $chunk = str_replace($matches[0][$j], $marker, $chunk);
  1171. }
  1172. }
  1173. if (preg_match_all("/".LD."reverse_related_entries\s*(.*?)".RD."(.+?)".LD.'\/'."reverse_related_entries".RD."/is", $chunk, $matches))
  1174. {
  1175. $this->log_item("Assigning Reverse Related Entry Data");
  1176. for ($j = 0; $j < count($matches[0]); $j++)
  1177. {
  1178. $rand = $this->EE->functions->random('alnum', 8);
  1179. $marker = LD.'REV_REL['.$rand.']REV_REL'.RD;
  1180. $vars = $this->EE->functions->assign_variables($matches[2][$j]);
  1181. $no_rev_content = '';
  1182. if (preg_match("/".LD."if no_reverse_related_entries".RD."(.*?)".LD.'\/'."if".RD."/s", $matches[2][$j], $no_rev_match))
  1183. {
  1184. // Match the entirety of the conditional
  1185. if (stristr($no_rev_match[1], LD.'if'))
  1186. {
  1187. $match[0] = $this->EE->functions->full_tag($no_rev_match[0], $matches[2][$j], LD.'if', LD.'\/'."if".RD);
  1188. }
  1189. $no_rev_content = substr($no_rev_match[0], strlen(LD."if no_reverse_related_entries".RD), -strlen(LD.'/'."if".RD));
  1190. }
  1191. $this->reverse_related_data[$rand] = array(
  1192. 'marker' => $rand,
  1193. 'tagdata' => $matches[2][$j],
  1194. 'var_single' => $vars['var_single'],
  1195. 'var_pair' => $vars['var_pair'],
  1196. 'var_cond' => $this->EE->functions->assign_conditional_variables($matches[2][$j], '\/', LD, RD),
  1197. 'params' => $this->EE->functions->assign_parameters($matches[1][$j]),
  1198. 'no_rev_content' => $no_rev_content
  1199. );
  1200. $chunk = str_replace($matches[0][$j], $marker, $chunk);
  1201. }
  1202. }
  1203. return $chunk;
  1204. }
  1205. // --------------------------------------------------------------------
  1206. /**
  1207. * Fetch Parameter for Tag
  1208. *
  1209. * Used by Modules to fetch a paramter for the tag currently be processed. We also have code
  1210. * in here to convert legacy values like 'y' and 'on' to their more respectable full values.
  1211. * Further, if one assigns the second argument, it will be returned as the value if a
  1212. * parameter of the $which name does not exist for this tag. Handy for default values!
  1213. *
  1214. * @access public
  1215. * @access string
  1216. * @access bool
  1217. * @return string
  1218. */
  1219. function fetch_param($which, $default = FALSE)
  1220. {
  1221. if ( ! isset($this->tagparams[$which]))
  1222. {
  1223. return $default;
  1224. }
  1225. else
  1226. {
  1227. // Making yes/no tag parameters consistent. No "y/n" or "on/off".
  1228. switch($this->tagparams[$which])
  1229. {
  1230. case 'y' :
  1231. case 'on' :
  1232. return 'yes';
  1233. break;
  1234. case 'n' :
  1235. case 'off' :
  1236. return 'no';
  1237. break;
  1238. default :
  1239. return $this->tagparams[$which];
  1240. break;
  1241. }
  1242. }
  1243. }
  1244. // --------------------------------------------------------------------
  1245. /**
  1246. * Replace a Single Variable with Its Value
  1247. *
  1248. * LEGACY!!!
  1249. *
  1250. * @access public
  1251. * @param string
  1252. * @param string
  1253. * @param string
  1254. * @return string
  1255. */
  1256. function swap_var_single($search, $replace, $source)
  1257. {
  1258. return str_replace(LD.$search.RD, $replace, $source);
  1259. }
  1260. // --------------------------------------------------------------------
  1261. /**
  1262. * Seems to Take a Variable Pair and Replace it With Its COntents
  1263. *
  1264. * LEGACY!!!
  1265. *
  1266. * @access public
  1267. * @param string
  1268. * @param string
  1269. * @param string
  1270. * @return string
  1271. */
  1272. function swap_var_pairs($open, $close, $source)
  1273. {
  1274. return preg_replace("/".LD.preg_quote($open).RD."(.*?)".LD.'\/'.$close.RD."/s", "\\1", $source);
  1275. }
  1276. // --------------------------------------------------------------------
  1277. /**
  1278. * Completely Removes a Variable Pair
  1279. *
  1280. * LEGACY!!!
  1281. *
  1282. * @access public
  1283. * @param string
  1284. * @param string
  1285. * @param string
  1286. * @return string
  1287. */
  1288. function delete_var_pairs($open, $close, $source)
  1289. {
  1290. return preg_replace("/".LD.preg_quote($open).RD."(.*?)".LD.'\/'.$close.RD."/s", "", $source);
  1291. }
  1292. // --------------------------------------------------------------------
  1293. /**
  1294. * Fetches Variable Pair's Content
  1295. *
  1296. * LEGACY!!!
  1297. *
  1298. * @access public
  1299. * @param string
  1300. * @param string
  1301. * @return string
  1302. */
  1303. function fetch_data_between_var_pairs($str, $variable)
  1304. {
  1305. if ($str == '' OR $variable == '')
  1306. return;
  1307. if ( ! preg_match("/".LD.$variable.".*?".RD."(.*?)".LD.'\/'.$variable.RD."/s", $str, $match))
  1308. return;
  1309. return $match[1];
  1310. }
  1311. // --------------------------------------------------------------------
  1312. /**
  1313. * Returns String with PHP Processed
  1314. *
  1315. * @access public
  1316. * @param string
  1317. * @return string
  1318. */
  1319. function parse_template_php($str)
  1320. {
  1321. ob_start();
  1322. echo $this->EE->functions->evaluate($str);
  1323. $str = ob_get_contents();
  1324. ob_end_clean();
  1325. $this->parse_php = FALSE;
  1326. return $str;
  1327. }
  1328. // --------------------------------------------------------------------
  1329. /**
  1330. * Get Cache File Data
  1331. *
  1332. * @access public
  1333. * @param string
  1334. * @param string
  1335. * @param mixed
  1336. * @return string
  1337. */
  1338. function fetch_cache_file($cfile, $cache_type = 'tag', $args = array())
  1339. {
  1340. // Which cache are we working on?
  1341. $status = ($cache_type == 'tag') ? 'tag_cache_status' : 'cache_status';
  1342. $status =& $this->$status;
  1343. if ( ! isset($args['cache']) OR $args['cache'] != 'yes')
  1344. {
  1345. $status = 'NO_CACHE';
  1346. return FALSE;
  1347. }
  1348. $cache_dir = ($cache_type == 'tag') ? APPPATH.'cache/'.$this->t_cache_path : $cache_dir = APPPATH.'cache/'.$this->p_cache_path;
  1349. $file = $cache_dir.$cfile;
  1350. if ( ! file_exists($file) OR ! ($fp = @fopen($file, FOPEN_READ)))
  1351. {
  1352. $status = 'EXPIRED';
  1353. return FALSE;
  1354. }
  1355. $cache = '';
  1356. $refresh = ( ! isset($args['refresh'])) ? 0 : $args['refresh'];
  1357. flock($fp, LOCK_SH);
  1358. // Read the first line (left a small buffer - just in case)
  1359. $timestamp = trim(fgets($fp, 30));
  1360. if ((strlen($timestamp) != 10) OR ($timestamp !== ((string)(int) $timestamp))) // Integer check
  1361. {
  1362. // Should never happen - so we'll log it
  1363. $this->log_item("Invalid Cache File Format: ".$file);
  1364. $status = 'EXPIRED';
  1365. }
  1366. elseif (time() > ($timestamp + ($refresh * 60)))
  1367. {
  1368. $status = 'EXPIRED';
  1369. }
  1370. else
  1371. {
  1372. // Timestamp valid - read rest of file
  1373. $status = 'CURRENT';
  1374. $cache = @fread($fp, filesize($file));
  1375. }
  1376. flock($fp, LOCK_UN);
  1377. fclose($fp);
  1378. return $cache;
  1379. }
  1380. // --------------------------------------------------------------------
  1381. /**
  1382. * Write Data to Cache File
  1383. *
  1384. * Stores the Tag and Page Cache Data
  1385. *
  1386. * @access public
  1387. * @param string
  1388. * @param string
  1389. * @param string
  1390. * @return string
  1391. */
  1392. function write_cache_file($cfile, $data, $cache_type = 'tag')
  1393. {
  1394. if ($this->disable_caching == TRUE)
  1395. {
  1396. return;
  1397. }
  1398. /* -------------------------------------
  1399. /* Disable Tag Caching
  1400. /*
  1401. /* All for you, Nevin! Disables tag caching, which if used unwisely
  1402. /* on a high traffic site can lead to disastrous disk i/o
  1403. /* This setting allows quick thinking admins to temporarily disable
  1404. /* it without hacking or modifying folder permissions
  1405. /*
  1406. /* Hidden Configuration Variable
  1407. /* - disable_tag_caching => Disable tag caching? (y/n)
  1408. /* -------------------------------------*/
  1409. if ($cache_type == 'tag' && $this->EE->config->item('disable_tag_caching') == 'y')
  1410. {
  1411. return;
  1412. }
  1413. $cache_dir = ($cache_type == 'tag') ? APPPATH.'cache/'.$this->t_cache_path : $cache_dir = APPPATH.'cache/'.$this->p_cache_path;
  1414. $cache_base = ($cache_type == 'tag') ? APPPATH.'cache/tag_cache' : APPPATH.'cache/page_cache';
  1415. $cache_file = $cache_dir.$cfile;
  1416. $dirs = array($cache_base, $cache_dir);
  1417. foreach ($dirs as $dir)
  1418. {
  1419. if ( ! @is_dir($dir))
  1420. {
  1421. if ( ! @mkdir($dir, DIR_WRITE_MODE))
  1422. {
  1423. return;
  1424. }
  1425. if ($dir == $cache_base && $fp = @fopen($dir.'/index.html', FOPEN_WRITE_CREATE_DESTRUCTIVE))
  1426. {
  1427. fclose($fp);
  1428. }
  1429. @chmod($dir, DIR_WRITE_MODE);
  1430. }
  1431. }
  1432. if ( ! $fp = @fopen($cache_file, FOPEN_WRITE_CREATE_DESTRUCTIVE))
  1433. {
  1434. $this->log_item("Could not create/write to cache file: ".$cache_file);
  1435. return;
  1436. }
  1437. flock($fp, LOCK_EX);
  1438. if (fwrite($fp, time()."\n".$data) === FALSE)
  1439. {
  1440. $this->log_item("Could not write to cache file: ".$cache_file);
  1441. }
  1442. flock($fp, LOCK_UN);
  1443. fclose($fp);
  1444. @chmod($cache_file, FILE_WRITE_MODE);
  1445. }
  1446. // --------------------------------------------------------------------
  1447. /**
  1448. * Parse Template URI
  1449. *
  1450. * Determines Which Template to Fetch Based on the Page's URI.
  1451. * If invalid Template, shows Template Group's index page
  1452. * If invalid Template Group, depending on sendings may show 404 or default Template Group
  1453. *
  1454. * @access public
  1455. * @return string
  1456. */
  1457. function parse_template_uri()
  1458. {
  1459. $this->log_item("Parsing Template URI");
  1460. // Does the first segment exist? No? Show the default template
  1461. if ($this->EE->uri->segment(1) === FALSE)
  1462. {
  1463. return $this->fetch_template('', 'index', TRUE);
  1464. }
  1465. // Is only the pagination showing in the URI?
  1466. elseif(count($this->EE->uri->segments) == 1 && preg_match("#^(P\d+)$#", $this->EE->uri->segment(1), $match))
  1467. {
  1468. $this->EE->uri->query_string = $match['1'];
  1469. return $this->fetch_template('', 'index', TRUE);
  1470. }
  1471. // Set the strict urls pref
  1472. if ($this->EE->config->item('strict_urls') !== FALSE)
  1473. {
  1474. $this->strict_urls = ($this->EE->config->item('strict_urls') == 'y') ? TRUE : FALSE;
  1475. }
  1476. // Load the string helper
  1477. $this->EE->load->helper('string');
  1478. // At this point we know that we have at least one segment in the URI, so
  1479. // let's try to determine what template group/template we should show
  1480. // Is the first segment the name of a template group?
  1481. $this->EE->db->select('group_id');
  1482. $this->EE->db->where('group_name', $this->EE->uri->segment(1));
  1483. $this->EE->db->where('site_id', $this->EE->config->item('site_id'));
  1484. $query = $this->EE->db->get('template_groups');
  1485. // Template group found!
  1486. if ($query->num_rows() == 1)
  1487. {
  1488. // Set the name of our template group
  1489. $template_group = $this->EE->uri->segment(1);
  1490. $this->log_item("Template Group Found: ".$template_group);
  1491. // Set the group_id so we can use it in the next query
  1492. $group_id = $query->row('group_id');
  1493. // Does the second segment of the URI exist? If so...
  1494. if ($this->EE->uri->segment(2) !== FALSE)
  1495. {
  1496. // Is the second segment the name of a valid template?
  1497. $this->EE->db->select('COUNT(*) as count');
  1498. $this->EE->db->where('group_id', $group_id);
  1499. $this->EE->db->where('template_name', $this->EE->uri->segment(2));
  1500. $query = $this->EE->db->get('templates');
  1501. // We have a template name!
  1502. if ($query->row('count') == 1)
  1503. {
  1504. // Assign the template name
  1505. $template = $this->EE->uri->segment(2);
  1506. // Re-assign the query string variable in the Input class so the various tags can show the correct data
  1507. $this->EE->uri->query_string = ( ! $this->EE->uri->segment(3) AND $this->EE->uri->segment(2) != 'index') ? '' : trim_slashes(substr($this->EE->uri->uri_string, strlen('/'.$this->EE->uri->segment(1).'/'.$this->EE->uri->segment(2))));
  1508. }
  1509. else // A valid template was not found
  1510. {
  1511. // is there a file we can automatically create this template from?
  1512. if ($this->EE->config->item('save_tmpl_files') == 'y' && $this->EE->config->item('tmpl_file_basepath') != '')
  1513. {
  1514. if ($this->_create_from_file($template_group, $this->EE->uri->segment(2)))
  1515. {
  1516. return $this->fetch_template($template_group, $this->EE->uri->segment(2), FALSE);
  1517. }
  1518. }
  1519. // Set the template to index
  1520. $template = 'index';
  1521. // Re-assign the query string variable in the Input class so the various tags can show the correct data
  1522. $this->EE->uri->query_string = ( ! $this->EE->uri->segment(3)) ? $this->EE->uri->segment(2) : trim_slashes(substr($this->EE->uri->uri_string, strlen('/'.$this->EE->uri->segment(1))));
  1523. }
  1524. }
  1525. // The second segment of the URL does not exist
  1526. else
  1527. {
  1528. // Set the template as "index"
  1529. $template = 'index';
  1530. }
  1531. }
  1532. // The first segment in the URL does NOT correlate to a valid template group. Oh my!
  1533. else
  1534. {
  1535. // If we are enforcing strict URLs we need to show a 404
  1536. if ($this->strict_urls == TRUE)
  1537. {
  1538. // is there a file we can automatically create this template from?
  1539. if ($this->EE->config->item('save_tmpl_files') == 'y' && $this->EE->config->item('tmpl_file_basepath') != '')
  1540. {
  1541. if ($this->_create_from_file($this->EE->uri->segment(1), $this->EE->uri->segment(2)))
  1542. {
  1543. return $this->fetch_template($this->EE->uri->segment(1), $this->EE->uri->segment(2), FALSE);
  1544. }
  1545. }
  1546. if ($this->EE->config->item('site_404'))
  1547. {
  1548. $this->log_item("Template group and template not found, showing 404 page");
  1549. return $this->fetch_template('', '', FALSE);
  1550. }
  1551. else
  1552. {
  1553. return $this->_404();
  1554. }
  1555. }
  1556. // We we are not enforcing strict URLs, so Let's fetch the the name of the default template group
  1557. $this->EE->db->select('group_name, group_id');
  1558. $this->EE->db->where('is_site_default', 'y');
  1559. $this->EE->db->where('site_id', $this->EE->config->item('site_id'));
  1560. $result = $this->EE->db->get('template_groups');
  1561. // No result? Bail out...
  1562. // There's really nothing else to do here. We don't have a valid template group in the URL
  1563. // and the admin doesn't have a template group defined as the site default.
  1564. if ($result->num_rows() == 0)
  1565. {
  1566. // Turn off caching
  1567. $this->disable_caching = TRUE;
  1568. // Show the user-specified 404
  1569. if ($this->EE->config->item('site_404'))
  1570. {
  1571. $this->log_item("Template group and template not found, showing 404 page");
  1572. return $this->fetch_template('', '', FALSE);
  1573. }
  1574. else
  1575. {
  1576. // Show the default 404
  1577. return $this->_404();
  1578. }
  1579. }
  1580. // Since the first URI segment isn't a template group name,
  1581. // could it be the name of a template in the default group?
  1582. $this->EE->db->select('COUNT(*) as count');
  1583. $this->EE->db->where('group_id', $result->row('group_id'));
  1584. $this->EE->db->where('template_name', $this->EE->uri->segment(1));
  1585. $query = $this->EE->db->get('templates');
  1586. // We found a valid template!
  1587. if ($query->row('count') == 1)
  1588. {
  1589. // Set the template group name from the prior query result (we use the default template group name)
  1590. $template_group = $result->row('group_name');
  1591. $this->log_item("Template Group Using Default: ".$template_group);
  1592. // Set the template name
  1593. $template = $this->EE->uri->segment(1);
  1594. // Re-assign the query string variable in the Input class so the various tags can show the correct data
  1595. if ($this->EE->uri->segment(2))
  1596. {
  1597. $this->EE->uri->query_string = trim_slashes(substr($this->EE->uri->uri_string, strlen('/'.$this->EE->uri->segment(1))));
  1598. }
  1599. }
  1600. // A valid template was not found. At this point we do not have either a valid template group or a valid template name in the URL
  1601. else
  1602. {
  1603. // is there a file we can automatically create this template from?
  1604. if ($this->EE->config->item('save_tmpl_files') == 'y' && $this->EE->config->item('tmpl_file_basepath') != '')
  1605. {
  1606. if ($this->_create_from_file($this->EE->uri->segment(1), $this->EE->uri->segment(2)))
  1607. {
  1608. return $this->fetch_template($this->EE->uri->segment(1), $this->EE->uri->segment(2), FALSE);
  1609. }
  1610. }
  1611. // Turn off caching
  1612. $this->disable_caching = TRUE;
  1613. // is 404 preference set, we wet our group/template names as blank.
  1614. // The fetch_template() function below will fetch the 404 and show it
  1615. if ($this->EE->config->item('site_404'))
  1616. {
  1617. $template_group = '';
  1618. $template = '';
  1619. $this->log_item("Template group and template not found, showing 404 page");
  1620. }
  1621. else
  1622. // No 404 preference is set so we will show the index template from the default template group
  1623. {
  1624. $this->EE->uri->query_string = trim_slashes($this->EE->uri->uri_string);
  1625. $template_group = $result->row('group_name');
  1626. $template = 'index';
  1627. $this->log_item("Showing index. Template not found: ".$this->EE->uri->segment(1));
  1628. }
  1629. }
  1630. }
  1631. // Fetch the template!
  1632. return $this->fetch_template($template_group, $template, FALSE);
  1633. }
  1634. // END
  1635. // --------------------------------------------------------------------
  1636. /**
  1637. * 404 Page
  1638. *
  1639. * If users do not have a 404 template specified this is what gets shown
  1640. *
  1641. * @access private
  1642. * @return string
  1643. */
  1644. function _404()
  1645. {
  1646. $this->log_item("404 Page Returned");
  1647. $this->EE->output->set_status_header(404);
  1648. echo '<html><head><title>404 Page Not Found</title></head><body><h1>Status: 404 Page Not Found</h1></body></html>';
  1649. exit;
  1650. }
  1651. // END
  1652. // --------------------------------------------------------------------
  1653. /**
  1654. * Fetch Template Data
  1655. *
  1656. * Takes a Template Group, Template, and Site ID and will retrieve the Template and its metadata
  1657. * from the database (or file)
  1658. *
  1659. * @access public
  1660. * @param string
  1661. * @param string
  1662. * @param bool
  1663. * @param int
  1664. * @return string
  1665. */
  1666. function fetch_template($template_group, $template, $show_default = TRUE, $site_id = '')
  1667. {
  1668. if ($site_id == '' OR ! is_numeric($site_id))
  1669. {
  1670. $site_id = $this->EE->config->item('site_id');
  1671. }
  1672. $this->log_item("Retrieving Template from Database: ".$template_group.'/'.$template);
  1673. $sql_404 = '';
  1674. $template_group_404 = '';
  1675. $template_404 = '';
  1676. /* -------------------------------------------
  1677. /* Hidden Configuration Variable
  1678. /* - hidden_template_indicator => '.'
  1679. The character(s) used to designate a template as "hidden"
  1680. /* -------------------------------------------*/
  1681. $hidden_indicator = ($this->EE->config->item('hidden_template_indicator') === FALSE) ? '.' : $this->EE->config->item('hidden_template_indicator');
  1682. if ($this->depth == 0 AND substr($template, 0, 1) == $hidden_indicator)
  1683. {
  1684. /* -------------------------------------------
  1685. /* Hidden Configuration Variable
  1686. /* - hidden_template_404 => y/n
  1687. If a hidden template is encountered, the default behavior is
  1688. to throw a 404. With this set to 'n', the template group's
  1689. index page will be shown instead
  1690. /* -------------------------------------------*/
  1691. if ($this->EE->config->item('hidden_template_404') !== 'n')
  1692. {
  1693. $x = explode("/", $this->EE->config->item('site_404'));
  1694. if (isset($x[0]) AND isset($x[1]))
  1695. {
  1696. $this->EE->output->out_type = '404';
  1697. $this->template_type = '404';
  1698. $template_group_404 = $this->EE->db->escape_str($x[0]);
  1699. $template_404 = $this->EE->db->escape_str($x[1]);
  1700. $sql_404 = " AND exp_template_groups.group_name='".$this->EE->db->escape_str($x[0])."' AND exp_templates.template_name='".$this->EE->db->escape_str($x[1])."'";
  1701. }
  1702. else
  1703. {
  1704. $template = 'index';
  1705. }
  1706. }
  1707. else
  1708. {
  1709. $template = 'index';
  1710. }
  1711. }
  1712. if ($template_group == '' && $show_default == FALSE && $this->EE->config->item('site_404') != '')
  1713. {
  1714. $treq = $this->EE->config->item('site_404');
  1715. $x = explode("/", $treq);
  1716. if (isset($x[0]) AND isset($x[1]))
  1717. {
  1718. $this->EE->output->out_type = '404';
  1719. $this->template_type = '404';
  1720. $template_group_404 = $this->EE->db->escape_str($x[0]);
  1721. $template_404 = $this->EE->db->escape_str($x[1]);
  1722. $sql_404 = " AND exp_template_groups.group_name='".$this->EE->db->escape_str($x[0])."' AND exp_templates.template_name='".$this->EE->db->escape_str($x[1])."'";
  1723. }
  1724. }
  1725. $sql = "SELECT exp_templates.template_name,
  1726. exp_templates.template_id,
  1727. exp_templates.template_data,
  1728. exp_templates.template_type,
  1729. exp_templates.edit_date,
  1730. exp_templates.save_template_file,
  1731. exp_templates.cache,
  1732. exp_templates.refresh,
  1733. exp_templates.no_auth_bounce,
  1734. exp_templates.enable_http_auth,
  1735. exp_templates.allow_php,
  1736. exp_templates.php_parse_location,
  1737. exp_templates.hits,
  1738. exp_template_groups.group_name
  1739. FROM exp_template_groups, exp_templates
  1740. WHERE exp_template_groups.group_id = exp_templates.group_id
  1741. AND exp_template_groups.site_id = '".$this->EE->db->escape_str($site_id)."' ";
  1742. if ($sql_404 != '')
  1743. {
  1744. $sql .= $sql_404;
  1745. }
  1746. else
  1747. {
  1748. if ($template != '')
  1749. {
  1750. $sql .= " AND exp_templates.template_name = '".$this->EE->db->escape_str($template)."' ";
  1751. }
  1752. if ($show_default == TRUE)
  1753. {
  1754. $sql .= "AND exp_template_groups.is_site_default = 'y'";
  1755. }
  1756. else
  1757. {
  1758. $sql .= "AND exp_template_groups.group_name = '".$this->EE->db->escape_str($template_group)."'";
  1759. }
  1760. }
  1761. $query = $this->EE->db->query($sql);
  1762. if ($query->num_rows() == 0)
  1763. {
  1764. // is there a file we can automatically create this template from?
  1765. if ($this->EE->config->item('save_tmpl_files') == 'y' && $this->EE->config->item('tmpl_file_basepath') != '')
  1766. {
  1767. $t_group = ($sql_404 != '') ? $template_group_404 : $template_group;
  1768. $t_template = ($sql_404 != '') ? $template_404 : $template;
  1769. if ($this->_create_from_file($t_group, $t_template, TRUE))
  1770. {
  1771. // run the query again, as we just successfully created it
  1772. $query = $this->EE->db->query($sql);
  1773. }
  1774. else
  1775. {
  1776. $this->log_item("Template Not Found");
  1777. return FALSE;
  1778. }
  1779. }
  1780. else
  1781. {
  1782. $this->log_item("Template Not Found");
  1783. return FALSE;
  1784. }
  1785. }
  1786. $this->log_item("Template Found");
  1787. /** ----------------------------------------------------
  1788. /** HTTP Authentication
  1789. /** ----------------------------------------------------*/
  1790. if ($query->row('enable_http_auth') == 'y')
  1791. {
  1792. $this->log_item("HTTP Authentication in Progress");
  1793. $this->EE->db->select('member_group');
  1794. $this->EE->db->where('template_id', $query->row('template_id'));
  1795. $results = $this->EE->db->get('template_no_access');
  1796. $not_allowed_groups = array('2', '3', '4');
  1797. if ($results->num_rows() > 0)
  1798. {
  1799. foreach($results->result_array() as $row)
  1800. {
  1801. $not_allowed_groups[] = $row['member_group'];
  1802. }
  1803. }
  1804. if ($this->template_authentication_check_basic($not_allowed_groups) !== TRUE)
  1805. {
  1806. $this->template_authentication_basic();
  1807. }
  1808. }
  1809. /** ----------------------------------------------------
  1810. /** Is the current user allowed to view this template?
  1811. /** ----------------------------------------------------*/
  1812. if ($query->row('enable_http_auth') != 'y' && $query->row('no_auth_bounce') != '')
  1813. {
  1814. $this->log_item("Determining Template Access Privileges");
  1815. $this->EE->db->select('COUNT(*) as count');
  1816. $this->EE->db->where('template_id', $query->row('template_id'));
  1817. $this->EE->db->where('member_group', $this->EE->session->userdata('group_id'));
  1818. $result = $this->EE->db->get('template_no_access');
  1819. if ($result->row('count') > 0)
  1820. {
  1821. if ($this->depth > 0)
  1822. {
  1823. return '';
  1824. }
  1825. $sql = "SELECT a.template_id, a.template_data, a.template_name, a.template_type, a.edit_date, a.save_template_file, a.cache, a.refresh, a.hits, a.allow_php, a.php_parse_location, b.group_name
  1826. FROM exp_templates a, exp_template_groups b
  1827. WHERE a.group_id = b.group_id
  1828. AND template_id = '".$this->EE->db->escape_str($query->row('no_auth_bounce') )."'";
  1829. $query = $this->EE->db->query($sql);
  1830. }
  1831. }
  1832. if ($query->num_rows() == 0)
  1833. {
  1834. return FALSE;
  1835. }
  1836. $row = $query->row_array();
  1837. /** -----------------------------------------
  1838. /** Is PHP allowed in this template?
  1839. /** -----------------------------------------*/
  1840. if ($row['allow_php'] == 'y' AND $this->EE->config->item('demo_date') == FALSE)
  1841. {
  1842. $this->parse_php = TRUE;
  1843. $this->php_parse_location = ($row['php_parse_location'] == 'i') ? 'input' : 'output';
  1844. }
  1845. /** -----------------------------------------
  1846. /** Increment hit counter
  1847. /** -----------------------------------------*/
  1848. if (($this->hit_lock == FALSE OR $this->hit_lock_override == TRUE) AND $this->EE->config->item('enable_hit_tracking') != 'n')
  1849. {
  1850. $this->template_hits = $row['hits'] + 1;
  1851. $this->hit_lock = TRUE;
  1852. $this->EE->db->update('templates', array('hits' => $this->template_hits),
  1853. array('template_id' => $row['template_id']));
  1854. }
  1855. /** -----------------------------------------
  1856. /** Set template edit date
  1857. /** -----------------------------------------*/
  1858. $this->template_edit_date = $row['edit_date'];
  1859. /** -----------------------------------------
  1860. /** Set template type for our page headers
  1861. /** -----------------------------------------*/
  1862. if ($this->template_type == '')
  1863. {
  1864. $this->template_type = $row['template_type'];
  1865. $this->EE->functions->template_type = $row['template_type'];
  1866. /** -----------------------------------------
  1867. /** If JS or CSS request, reset Tracker Cookie
  1868. /** -----------------------------------------*/
  1869. if ($this->template_type == 'js' OR $this->template_type == 'css')
  1870. {
  1871. if (count($this->EE->session->tracker) <= 1)
  1872. {
  1873. $this->EE->session->tracker = array();
  1874. }
  1875. else
  1876. {
  1877. $removed = array_shift($this->EE->session->tracker);
  1878. }
  1879. $this->EE->functions->set_cookie('tracker', serialize($this->EE->session->tracker), '0');
  1880. }
  1881. }
  1882. if ($this->depth > 0)
  1883. {
  1884. $this->embed_type = $row['template_type'];
  1885. }
  1886. /** -----------------------------------------
  1887. /** Cache Override
  1888. /** -----------------------------------------*/
  1889. // We can manually set certain things not to be cached, like the
  1890. // search template and the member directory after it's updated
  1891. // Note: I think search caching is OK.
  1892. // $cache_override = array('member' => 'U', 'search' => FALSE);
  1893. $cache_override = array('member');
  1894. foreach ($cache_override as $val)
  1895. {
  1896. if (strncmp($this->EE->uri->uri_string, "/{$val}/", strlen($val) + 2) == 0)
  1897. {
  1898. $row['cache'] = 'n';
  1899. }
  1900. }
  1901. /** -----------------------------------------
  1902. /** Retreive cache
  1903. /** -----------------------------------------*/
  1904. $this->cache_hash = md5($site_id.'-'.$template_group.'-'.$template);
  1905. if ($row['cache'] == 'y')
  1906. {
  1907. $cache_contents = $this->fetch_cache_file($this->cache_hash, 'template', array('cache' => 'yes', 'refresh' => $row['refresh']));
  1908. if ($this->cache_status == 'CURRENT')
  1909. {
  1910. return $this->convert_xml_declaration($cache_contents);
  1911. }
  1912. }
  1913. /** -----------------------------------------
  1914. /** Retrieve template file if necessary
  1915. /** -----------------------------------------*/
  1916. if ($row['save_template_file'] == 'y')
  1917. {
  1918. $site_switch = FALSE;
  1919. if ($this->EE->config->item('site_id') != $site_id)
  1920. {
  1921. $site_switch = $this->EE->config->config;
  1922. if (isset($this->site_prefs_cache[$site_id]))
  1923. {
  1924. $this->EE->config->config = $this->site_prefs_cache[$site_id];
  1925. }
  1926. else
  1927. {
  1928. $this->EE->config->site_prefs('', $site_id);
  1929. $this->site_prefs_cache[$site_id] = $this->EE->config->config;
  1930. }
  1931. }
  1932. if ($this->EE->config->item('save_tmpl_files') == 'y' AND $this->EE->config->item('tmpl_file_basepath') != '')
  1933. {
  1934. $this->log_item("Retrieving Template from File");
  1935. $this->EE->load->library('api');
  1936. $this->EE->api->instantiate('template_structure');
  1937. $basepath = rtrim($this->EE->config->item('tmpl_file_basepath'), '/').'/';
  1938. $basepath .= $this->EE->config->item('site_short_name').'/'.$row['group_name'].'.group/'.$row['template_name'].$this->EE->api_template_structure->file_extensions($row['template_type']);
  1939. if (file_exists($basepath))
  1940. {
  1941. $row['template_data'] = file_get_contents($basepath);
  1942. }
  1943. }
  1944. if ($site_switch !== FALSE)
  1945. {
  1946. $this->EE->config->config = $site_switch;
  1947. }
  1948. }
  1949. // standardize newlines
  1950. $row['template_data'] = str_replace(array("\r\n", "\r"), "\n", $row['template_data']);
  1951. return $this->convert_xml_declaration($this->remove_ee_comments($row['template_data']));
  1952. }
  1953. // --------------------------------------------------------------------
  1954. /**
  1955. * Create From File
  1956. *
  1957. * Attempts to create a template group / template from a file
  1958. *
  1959. * @access public
  1960. * @param string template group name
  1961. * @param string template name
  1962. * @return bool
  1963. */
  1964. function _create_from_file($template_group, $template, $db_check = FALSE)
  1965. {
  1966. if ($this->EE->config->item('save_tmpl_files') != 'y' OR $this->EE->config->item('tmpl_file_basepath') == '')
  1967. {
  1968. return FALSE;
  1969. }
  1970. $template = ($template == '') ? 'index' : $template;
  1971. if ($db_check)
  1972. {
  1973. $this->EE->db->from('templates');
  1974. $this->EE->db->join('template_groups', 'templates.group_id = template_groups.group_id', 'left');
  1975. $this->EE->db->where('group_name', $template_group);
  1976. $this->EE->db->where('template_name', $template);
  1977. $valid_count = $this->EE->db->count_all_results();
  1978. // We found a valid template! Er- could this loop? Better just return FALSE
  1979. if ($valid_count > 0)
  1980. {
  1981. return FALSE;
  1982. }
  1983. }
  1984. $this->EE->load->library('api');
  1985. $this->EE->api->instantiate('template_structure');
  1986. $this->EE->load->model('template_model');
  1987. $basepath = $this->EE->config->slash_item('tmpl_file_basepath').$this->EE->config->item('site_short_name').'/'.$template_group.'.group';
  1988. if ( ! is_dir($basepath))
  1989. {
  1990. return FALSE;
  1991. }
  1992. $filename = FALSE;
  1993. // Note- we should add the extension before checking.
  1994. foreach ($this->EE->api_template_structure->file_extensions as $type => $temp_ext)
  1995. {
  1996. if (file_exists($basepath.'/'.$template.$temp_ext))
  1997. {
  1998. // found it with an extension
  1999. $filename = $template.$temp_ext;
  2000. $ext = $temp_ext;
  2001. $template_type = $type;
  2002. break;
  2003. }
  2004. }
  2005. // did we find anything?
  2006. if ($filename === FALSE)
  2007. {
  2008. return FALSE;
  2009. }
  2010. if ( ! $this->EE->api->is_url_safe($template))
  2011. {
  2012. // bail out
  2013. return FALSE;
  2014. }
  2015. $this->EE->db->select('group_id');
  2016. $this->EE->db->where('group_name', $template_group);
  2017. $this->EE->db->where('site_id', $this->EE->config->item('site_id'));
  2018. $query = $this->EE->db->get('template_groups');
  2019. if ($query->num_rows() != 0)
  2020. {
  2021. $group_id = $query->row('group_id');
  2022. }
  2023. else
  2024. {
  2025. // we have a new group to create!
  2026. if ( ! $this->EE->api->is_url_safe($template_group))
  2027. {
  2028. // bail out
  2029. return FALSE;
  2030. }
  2031. if (in_array($template_group, $this->EE->api_template_structure->reserved_names))
  2032. {
  2033. // bail out
  2034. return FALSE;
  2035. }
  2036. $data = array(
  2037. 'group_name' => $template_group,
  2038. 'group_order' => $this->EE->db->count_all('template_groups') + 1,
  2039. 'is_site_default' => 'n',
  2040. 'site_id' => $this->EE->config->item('site_id')
  2041. );
  2042. $group_id = $this->EE->template_model->create_group($data);
  2043. }
  2044. $data = array(
  2045. 'group_id' => $group_id,
  2046. 'template_name' => $template,
  2047. 'template_type' => $template_type,
  2048. 'template_data' => file_get_contents($basepath.'/'.$filename),
  2049. 'edit_date' => $this->EE->localize->now,
  2050. 'save_template_file' => 'y',
  2051. 'last_author_id' => '1', // assume a super admin
  2052. 'site_id' => $this->EE->config->item('site_id')
  2053. );
  2054. $this->EE->template_model->create_template($data);
  2055. // Clear db cache or it will create a new template record each page load!
  2056. $this->EE->functions->clear_caching('db');
  2057. return TRUE;
  2058. }
  2059. // --------------------------------------------------------------------
  2060. /**
  2061. * No Results
  2062. *
  2063. * If a tag/class has no results to show, it can call this method. Any no_results variable in
  2064. * the tag will be followed. May be 404 page, content, or even a redirect.
  2065. *
  2066. * @access public
  2067. * @return void
  2068. */
  2069. function no_results()
  2070. {
  2071. if ( ! preg_match("/".LD."redirect\s*=\s*(\042|\047)([^\\1]*?)\\1".RD."/si", $this->no_results, $match))
  2072. {
  2073. $this->log_item("Returning No Results Content");
  2074. return $this->no_results;
  2075. }
  2076. else
  2077. {
  2078. $this->log_item("Processing No Results Redirect");
  2079. if ($match[2] == "404")
  2080. {
  2081. $template = explode('/', $this->EE->config->item('site_404'));
  2082. if (isset($template[1]))
  2083. {
  2084. $this->log_item('Processing "'.$template[0].'/'.$template[1].'" Template as 404 Page');
  2085. $this->output->out_type = "404";
  2086. $this->template_type = "404";
  2087. $this->fetch_and_parse($template[0], $template[1]);
  2088. $this->cease_processing = TRUE;
  2089. }
  2090. else
  2091. {
  2092. $this->log_item('404 redirect requested, but no 404 page is specified in the Global Template Preferences');
  2093. return $this->no_results;
  2094. }
  2095. }
  2096. else
  2097. {
  2098. return $this->EE->functions->redirect($this->EE->functions->create_url($this->EE->functions->extract_path("=".$match[2])));
  2099. }
  2100. }
  2101. }
  2102. // --------------------------------------------------------------------
  2103. /**
  2104. * Make XML Declaration Safe
  2105. *
  2106. * Takes any XML declaration in the string and makes sure it is not interpreted as PHP during
  2107. * the processing of the template.
  2108. *
  2109. * @access public
  2110. * @param string
  2111. * @return string
  2112. */
  2113. // This fixes a parsing error when PHP is used in RSS templates
  2114. function convert_xml_declaration($str)
  2115. {
  2116. if (strpos($str, '<?xml') === FALSE) return $str;
  2117. return preg_replace("/\<\?xml(.+?)\?\>/", "<XXML\\1/XXML>", $str);
  2118. }
  2119. // --------------------------------------------------------------------
  2120. /**
  2121. * Restore XML Declaration
  2122. *
  2123. * @access public
  2124. * @param string
  2125. * @return string
  2126. */
  2127. function restore_xml_declaration($str)
  2128. {
  2129. if (strpos($str, '<XXML') === FALSE) return $str;
  2130. return preg_replace("/\<XXML(.+?)\/XXML\>/", "<?xml\\1?".">", $str); // <?
  2131. }
  2132. // --------------------------------------------------------------------
  2133. /**
  2134. * Remove all EE Code Comment Strings
  2135. *
  2136. * EE Templates have a special EE Code Comments for site designer notes and are removed prior
  2137. * to Template processing.
  2138. *
  2139. * @access public
  2140. * @param string
  2141. * @return string
  2142. */
  2143. function remove_ee_comments($str)
  2144. {
  2145. if (strpos($str, '{!--') === FALSE) return $str;
  2146. return preg_replace("/\{!--.*?--\}/s", '', $str);
  2147. }
  2148. // --------------------------------------------------------------------
  2149. /**
  2150. * Fetch Add-ons
  2151. *
  2152. * Gathers available modules and plugins
  2153. *
  2154. * @access public
  2155. * @return void
  2156. */
  2157. function fetch_addons()
  2158. {
  2159. $this->EE->load->helper('file');
  2160. $this->EE->load->helper('directory');
  2161. $ext_len = strlen(EXT);
  2162. $pattern = 'bas'.'e'.'6'.'4_d'.'ecode';
  2163. // first get first party modules
  2164. if (($map = directory_map(PATH_MOD, TRUE)) !== FALSE)
  2165. {
  2166. foreach ($map as $file)
  2167. {
  2168. if (strpos($file, '.') === FALSE)
  2169. {
  2170. eval($pattern('dW5zZXQoJG1vZHVsZSk7aWYgKElTX0ZSRUVMQU5DRVIgJiYgaW5fYXJyYXkoJGZpbGUsIGFycmF5KCdtZW1iZXInLCAnZm9ydW0nLCAnd2lraScpKSl7JG1vZHVsZT1UUlVFO30='));
  2171. if (isset($module))
  2172. {
  2173. continue;
  2174. }
  2175. $this->modules[] = $file;
  2176. }
  2177. }
  2178. }
  2179. // now first party plugins
  2180. if (($map = directory_map(PATH_PI, TRUE)) !== FALSE)
  2181. {
  2182. foreach ($map as $file)
  2183. {
  2184. if (strncasecmp($file, 'pi.', 3) == 0 && substr($file, -$ext_len) == EXT && strlen($file) > strlen('pi.'.EXT)
  2185. && in_array(substr($file, 3, -$ext_len), $this->EE->core->native_plugins))
  2186. {
  2187. $this->plugins[] = substr($file, 3, -$ext_len);
  2188. }
  2189. }
  2190. }
  2191. // now third party add-ons, which are arranged in "packages"
  2192. // only catch files that match the package name, as other files are merely assets
  2193. if (($map = directory_map(PATH_THIRD, 2)) !== FALSE)
  2194. {
  2195. foreach ($map as $pkg_name => $files)
  2196. {
  2197. if ( ! is_array($files))
  2198. {
  2199. $files = array($files);
  2200. }
  2201. foreach ($files as $file)
  2202. {
  2203. if (is_array($file))
  2204. {
  2205. // we're only interested in the top level files for the addon
  2206. continue;
  2207. }
  2208. // we gots a module?
  2209. if (strncasecmp($file, 'mod.', 4) == 0 && substr($file, -$ext_len) == EXT && strlen($file) > strlen('mod.'.EXT))
  2210. {
  2211. $file = substr($file, 4, -$ext_len);
  2212. if ($file == $pkg_name)
  2213. {
  2214. $this->modules[] = $file;
  2215. }
  2216. }
  2217. // how abouts a plugin?
  2218. elseif (strncasecmp($file, 'pi.', 3) == 0 && substr($file, -$ext_len) == EXT && strlen($file) > strlen('pi.'.EXT))
  2219. {
  2220. $file = substr($file, 3, -$ext_len);
  2221. if ($file == $pkg_name)
  2222. {
  2223. $this->plugins[] = $file;
  2224. }
  2225. }
  2226. }
  2227. }
  2228. }
  2229. }
  2230. // --------------------------------------------------------------------
  2231. /**
  2232. * Parse Globals
  2233. *
  2234. * The syntax is generally: {global:variable_name}
  2235. *
  2236. * Parses global variables like the currently logged in member's information, system variables,
  2237. * paths, action IDs, CAPTCHAs. Typically stuff that should only be done after caching to prevent
  2238. * any manner of changes in the system or who is viewing the page to affect the display.
  2239. *
  2240. * @access public
  2241. * @param string
  2242. * @return string
  2243. */
  2244. function parse_globals($str)
  2245. {
  2246. $charset = '';
  2247. $lang = '';
  2248. $user_vars = array('member_id', 'group_id', 'group_description', 'group_title', 'member_group', 'username', 'screen_name', 'email', 'ip_address', 'location', 'total_entries', 'total_comments', 'private_messages', 'total_forum_posts', 'total_forum_topics', 'total_forum_replies');
  2249. /** --------------------------------------------------
  2250. /** Redirect - if we have one of these, no need to go further
  2251. /** --------------------------------------------------*/
  2252. if (strpos($str, LD.'redirect') !== FALSE)
  2253. {
  2254. if (preg_match("/".LD."redirect\s*=\s*(\042|\047)([^\\1]*?)\\1".RD."/si", $str, $match))
  2255. {
  2256. if ($match['2'] == "404")
  2257. {
  2258. $template = explode('/', $this->EE->config->item('site_404'));
  2259. if (isset($template['1']))
  2260. {
  2261. $this->log_item('Processing "'.$template['0'].'/'.$template['1'].'" Template as 404 Page');
  2262. $this->template_type = "404";
  2263. $this->fetch_and_parse($template['0'], $template['1']);
  2264. $this->cease_processing = TRUE;
  2265. // the resulting template will not have globals parsed unless we do this
  2266. return $this->parse_globals($this->final_template);
  2267. }
  2268. else
  2269. {
  2270. $this->log_item('404 redirect requested, but no 404 page is specified in the Global Template Preferences');
  2271. return $this->_404();
  2272. }
  2273. }
  2274. else
  2275. {
  2276. // Functions::redirect() exit;s on its own
  2277. $this->EE->functions->redirect($this->EE->functions->create_url($this->EE->functions->extract_path("=".$match['2'])));
  2278. }
  2279. }
  2280. }
  2281. /** --------------------------------------------------
  2282. /** Restore XML declaration if it was encoded
  2283. /** --------------------------------------------------*/
  2284. $str = $this->restore_xml_declaration($str);
  2285. /** --------------------------------------------------
  2286. /** Parse User-defined Global Variables first so that
  2287. /** they can use other standard globals
  2288. /** --------------------------------------------------*/
  2289. $this->EE->db->select('variable_name, variable_data');
  2290. $this->EE->db->where('site_id', $this->EE->config->item('site_id'));
  2291. $query = $this->EE->db->get('global_variables');
  2292. if ($query->num_rows() > 0)
  2293. {
  2294. foreach ($query->result_array() as $row)
  2295. {
  2296. $str = str_replace(LD.$row['variable_name'].RD, $row['variable_data'], $str);
  2297. }
  2298. }
  2299. /** --------------------------------------------------
  2300. /** {hits}
  2301. /** --------------------------------------------------*/
  2302. $str = str_replace(LD.'hits'.RD, $this->template_hits, $str);
  2303. /** --------------------------------------------------
  2304. /** {ip_address} and {ip_hostname}
  2305. /** --------------------------------------------------*/
  2306. $str = str_replace(LD.'ip_address'.RD, $this->EE->input->ip_address(), $str);
  2307. // Turns out gethostbyaddr() is WAY SLOW on many systems so I'm killing it.
  2308. // $str = str_replace(LD.'ip_hostname'.RD, @gethostbyaddr($this->EE->input->ip_address()), $str);
  2309. $str = str_replace(LD.'ip_hostname'.RD, $this->EE->input->ip_address(), $str);
  2310. /** --------------------------------------------------
  2311. /** {homepage}
  2312. /** --------------------------------------------------*/
  2313. $str = str_replace(LD.'homepage'.RD, $this->EE->functions->fetch_site_index(), $str);
  2314. /** --------------------------------------------------
  2315. /** {cp_url}
  2316. /** --------------------------------------------------*/
  2317. if ($this->EE->session->access_cp === TRUE)
  2318. {
  2319. $str = str_replace(LD.'cp_url'.RD, $this->EE->config->item('cp_url'), $str);
  2320. }
  2321. else
  2322. {
  2323. $str = str_replace(LD.'cp_url'.RD, '', $str);
  2324. }
  2325. /** --------------------------------------------------
  2326. /** {site_name} {site_url} {site_index}
  2327. /** --------------------------------------------------*/
  2328. $str = str_replace(LD.'site_name'.RD, stripslashes($this->EE->config->item('site_name')), $str);
  2329. $str = str_replace(LD.'site_url'.RD, stripslashes($this->EE->config->item('site_url')), $str);
  2330. $str = str_replace(LD.'site_index'.RD, stripslashes($this->EE->config->item('site_index')), $str);
  2331. $str = str_replace(LD.'webmaster_email'.RD, stripslashes($this->EE->config->item('webmaster_email')), $str);
  2332. /** --------------------------------------------------
  2333. /** Stylesheet variable: {stylesheet=group/template}
  2334. /** --------------------------------------------------*/
  2335. if (strpos($str, 'stylesheet=') !== FALSE && preg_match_all("/".LD."\s*stylesheet=[\042\047]?(.*?)[\042\047]?".RD."/", $str, $css_matches))
  2336. {
  2337. $css_versions = array();
  2338. if ($this->EE->config->item('send_headers') == 'y')
  2339. {
  2340. $sql = "SELECT t.template_name, tg.group_name, t.edit_date, t.save_template_file FROM exp_templates t, exp_template_groups tg
  2341. WHERE t.group_id = tg.group_id
  2342. AND t.template_type = 'css'
  2343. AND t.site_id = '".$this->EE->db->escape_str($this->EE->config->item('site_id'))."'";
  2344. foreach($css_matches[1] as $css_match)
  2345. {
  2346. $ex = explode('/', $css_match, 2);
  2347. if (isset($ex[1]))
  2348. {
  2349. $css_parts[] = "(t.template_name = '".$this->EE->db->escape_str($ex[1])."' AND tg.group_name = '".$this->EE->db->escape_str($ex[0])."')";
  2350. }
  2351. }
  2352. $css_query = ( ! isset($css_parts)) ? $this->EE->db->query($sql) : $this->EE->db->query($sql.' AND ('.implode(' OR ', $css_parts) .')');
  2353. if ($css_query->num_rows() > 0)
  2354. {
  2355. foreach($css_query->result_array() as $row)
  2356. {
  2357. $css_versions[$row['group_name'].'/'.$row['template_name']] = $row['edit_date'];
  2358. if ($this->EE->config->item('save_tmpl_files') == 'y' AND $this->EE->config->item('tmpl_file_basepath') != '' AND $row['save_template_file'] == 'y')
  2359. {
  2360. $basepath = $this->EE->config->slash_item('tmpl_file_basepath').$this->EE->config->item('site_short_name').'/';
  2361. $basepath .= $ex['0'].'.group/'.$row['template_name'].'.css';
  2362. if (is_file($basepath))
  2363. {
  2364. $css_versions[$row['group_name'].'/'.$row['template_name']] = filemtime($basepath);
  2365. }
  2366. }
  2367. }
  2368. }
  2369. }
  2370. for($ci=0, $cs=count($css_matches[0]); $ci < $cs; ++$ci)
  2371. {
  2372. $str = str_replace($css_matches[0][$ci], $this->EE->functions->fetch_site_index().QUERY_MARKER.'css='.$css_matches[1][$ci].(isset($css_versions[$css_matches[1][$ci]]) ? '.v.'.$css_versions[$css_matches[1][$ci]] : ''), $str);
  2373. }
  2374. unset($css_matches);
  2375. unset($css_versions);
  2376. }
  2377. /** --------------------------------------------------
  2378. /** Email encode: {encode="you@yoursite.com" title="click Me"}
  2379. /** --------------------------------------------------*/
  2380. if (strpos($str, LD.'encode=') !== FALSE)
  2381. {
  2382. if ($this->encode_email == TRUE)
  2383. {
  2384. if (preg_match_all("/".LD."encode=(.+?)".RD."/i", $str, $matches))
  2385. {
  2386. for ($j = 0; $j < count($matches[0]); $j++)
  2387. {
  2388. $str = preg_replace('/'.preg_quote($matches['0'][$j], '/').'/', $this->EE->functions->encode_email($matches[1][$j]), $str, 1);
  2389. }
  2390. }
  2391. }
  2392. else
  2393. {
  2394. /* -------------------------------------------
  2395. /* Hidden Configuration Variable
  2396. /* - encode_removed_text => Text to display if there is an {encode=""}
  2397. tag but emails are not to be encoded
  2398. /* -------------------------------------------*/
  2399. $str = preg_replace("/".LD."\s*encode=(.+?)".RD."/",
  2400. ($this->EE->config->item('encode_removed_text') !== FALSE) ? $this->EE->config->item('encode_removed_text') : '',
  2401. $str);
  2402. }
  2403. }
  2404. /** --------------------------------------------------
  2405. /** Path variable: {path=group/template}
  2406. /** --------------------------------------------------*/
  2407. if (strpos($str, 'path=') !== FALSE)
  2408. {
  2409. $str = preg_replace_callback("/".LD."\s*path=(.*?)".RD."/", array(&$this->EE->functions, 'create_url'), $str);
  2410. }
  2411. /** --------------------------------------------------
  2412. /** Debug mode: {debug_mode}
  2413. /** --------------------------------------------------*/
  2414. $str = str_replace(LD.'debug_mode'.RD, ($this->EE->config->item('debug') > 0) ? $this->EE->lang->line('on') : $this->EE->lang->line('off'), $str);
  2415. /** --------------------------------------------------
  2416. /** GZip mode: {gzip_mode}
  2417. /** --------------------------------------------------*/
  2418. $str = str_replace(LD.'gzip_mode'.RD, ($this->EE->config->item('gzip_output') == 'y') ? $this->EE->lang->line('enabled') : $this->EE->lang->line('disabled'), $str);
  2419. /** --------------------------------------------------
  2420. /** App version: {version}
  2421. /** --------------------------------------------------*/
  2422. $str = str_replace(LD.'app_version'.RD, APP_VER, $str);
  2423. $str = str_replace(LD.'version'.RD, APP_VER, $str);
  2424. /** --------------------------------------------------
  2425. /** App version: {build}
  2426. /** --------------------------------------------------*/
  2427. $str = str_replace(LD.'app_build'.RD, APP_BUILD, $str);
  2428. $str = str_replace(LD.'build'.RD, APP_BUILD, $str);
  2429. /** --------------------------------------------------
  2430. /** {charset} and {lang}
  2431. /** --------------------------------------------------*/
  2432. $str = str_replace(LD.'charset'.RD, $this->EE->config->item('output_charset'), $str);
  2433. $str = str_replace(LD.'lang'.RD, $this->EE->config->item('xml_lang'), $str);
  2434. /** --------------------------------------------------
  2435. /** {doc_url}
  2436. /** --------------------------------------------------*/
  2437. $str = str_replace(LD.'doc_url'.RD, $this->EE->config->item('doc_url'), $str);
  2438. /** --------------------------------------------------
  2439. /** {member_profile_link}
  2440. /** --------------------------------------------------*/
  2441. if ($this->EE->session->userdata('member_id') != 0)
  2442. {
  2443. $name = ($this->EE->session->userdata['screen_name'] == '') ? $this->EE->session->userdata['username'] : $this->EE->session->userdata['screen_name'];
  2444. $path = "<a href='".$this->EE->functions->create_url('/member/'.$this->EE->session->userdata('member_id'))."'>".$name."</a>";
  2445. $str = str_replace(LD.'member_profile_link'.RD, $path, $str);
  2446. }
  2447. else
  2448. {
  2449. $str = str_replace(LD.'member_profile_link'.RD, '', $str);
  2450. }
  2451. /** -----------------------------------
  2452. /** Fetch CAPTCHA
  2453. /** -----------------------------------*/
  2454. if (strpos($str, "{captcha}") !== FALSE)
  2455. {
  2456. $str = str_replace("{captcha}", $this->EE->functions->create_captcha(), $str);
  2457. }
  2458. /** -----------------------------------
  2459. /** Add security hashes to forms
  2460. /** -----------------------------------*/
  2461. // We do this here to keep the security hashes from being cached
  2462. $str = $this->EE->functions->add_form_security_hash($str);
  2463. /** -----------------------------------
  2464. /** Add Action IDs form forms and links
  2465. /** -----------------------------------*/
  2466. $str = $this->EE->functions->insert_action_ids($str);
  2467. /** -----------------------------------
  2468. /** Parse non-cachable variables
  2469. /** -----------------------------------*/
  2470. $this->EE->session->userdata['member_group'] = $this->EE->session->userdata['group_id'];
  2471. foreach ($user_vars as $val)
  2472. {
  2473. if (isset($this->EE->session->userdata[$val]) AND ($val == 'group_description' OR strval($this->EE->session->userdata[$val]) != ''))
  2474. {
  2475. $str = str_replace(LD.$val.RD, $this->EE->session->userdata[$val], $str);
  2476. $str = str_replace('{out_'.$val.'}', $this->EE->session->userdata[$val], $str);
  2477. $str = str_replace('{global->'.$val.'}', $this->EE->session->userdata[$val], $str);
  2478. $str = str_replace('{logged_in_'.$val.'}', $this->EE->session->userdata[$val], $str);
  2479. }
  2480. }
  2481. // and once again just in case global vars introduce EE comments
  2482. return $this->remove_ee_comments($str);
  2483. }
  2484. // --------------------------------------------------------------------
  2485. /**
  2486. * Parse Uncachaable Forms
  2487. *
  2488. * Parses and Process forms that cannot be stored in a cache file. Probably one of the most
  2489. * tedious parts of EE's Template parser for a while there in 2004...
  2490. *
  2491. * @access public
  2492. * @param string
  2493. * @return string
  2494. */
  2495. function parse_nocache($str)
  2496. {
  2497. if (strpos($str, '{NOCACHE') === FALSE)
  2498. {
  2499. return $str;
  2500. }
  2501. /** -----------------------------------
  2502. /** Generate Comment Form if needed
  2503. /** -----------------------------------*/
  2504. // In order for the comment form not to cache the "save info"
  2505. // data we need to generate dynamically if necessary
  2506. if (preg_match_all("#{NOCACHE_(\S+)_FORM=\"(.*?)\"}(.+?){/NOCACHE_FORM}#s", $str, $match))
  2507. {
  2508. $this->EE->load->library('security');
  2509. for($i=0, $s=count($match[0]); $i < $s; $i++)
  2510. {
  2511. $class = $this->EE->security->sanitize_filename(strtolower($match[1][$i]));
  2512. if ( ! class_exists($class))
  2513. {
  2514. require PATH_MOD.$class.'/mod.'.$class.EXT;
  2515. }
  2516. $this->tagdata = $match[3][$i];
  2517. $vars = $this->EE->functions->assign_variables($match[3][$i], '/');
  2518. $this->var_single = $vars['var_single'];
  2519. $this->var_pair = $vars['var_pair'];
  2520. $this->tagparams = $this->EE->functions->assign_parameters($match[2][$i]);
  2521. $this->var_cond = $this->EE->functions->assign_conditional_variables($match[3][$i], '/', LD, RD);
  2522. // Assign sites for the tag
  2523. $this->_fetch_site_ids();
  2524. // Assign Form ID/Classes
  2525. if (isset($this->tag_data[$i]))
  2526. {
  2527. $this->tag_data[$i] = $this->_assign_form_params($this->tag_data[$i]);
  2528. }
  2529. if ($class == 'comment')
  2530. {
  2531. $str = str_replace($match[0][$i], Comment::form(TRUE, $this->EE->functions->cached_captcha), $str);
  2532. }
  2533. $str = str_replace('{PREVIEW_TEMPLATE}', $match[2][$i], $str);
  2534. }
  2535. }
  2536. /** -----------------------------------
  2537. /** Generate Stand-alone Publish form
  2538. /** -----------------------------------*/
  2539. if (preg_match_all("#{{NOCACHE_CHANNEL_FORM(.*?)}}(.+?){{/NOCACHE_FORM}}#s", $str, $match))
  2540. {
  2541. for($i=0, $s=count($match[0]); $i < $s; $i++)
  2542. {
  2543. if ( ! class_exists('Channel'))
  2544. {
  2545. require PATH_MOD.'channel/mod.channel'.EXT;
  2546. }
  2547. $this->tagdata = $match[2][$i];
  2548. $vars = $this->EE->functions->assign_variables($match[2][$i], '/');
  2549. $this->var_single = $vars['var_single'];
  2550. $this->var_pair = $vars['var_pair'];
  2551. $this->tagparams = $this->EE->functions->assign_parameters($match[1][$i]);
  2552. // Assign sites for the tag
  2553. $this->_fetch_site_ids();
  2554. // Assign Form ID/Classes
  2555. if (isset($this->tag_data[$i]))
  2556. {
  2557. $this->tag_data[$i] = $this->_assign_form_params($this->tag_data[$i]);
  2558. }
  2559. $XX = new Channel();
  2560. $str = str_replace($match[0][$i], $XX->entry_form(TRUE, $this->EE->functions->cached_captcha), $str);
  2561. $str = str_replace('{PREVIEW_TEMPLATE}', (isset($_POST['PRV'])) ? $_POST['PRV'] : $this->fetch_param('preview'), $str);
  2562. }
  2563. }
  2564. return $str;
  2565. }
  2566. // --------------------------------------------------------------------
  2567. /**
  2568. * Process Advanced Conditionals
  2569. *
  2570. * The syntax is generally: {if whatever = ""}Dude{if:elseif something != ""}Yo{if:else}
  2571. *
  2572. * The final processing of Advanced Conditionals. Takes all of the member variables and uncachable
  2573. * variables and preps the conditionals with them. Then, it converts the conditionals to PHP so that
  2574. * PHP can do all of the really heavy lifting for us.
  2575. *
  2576. * @access public
  2577. * @param string
  2578. * @return string
  2579. */
  2580. function advanced_conditionals($str)
  2581. {
  2582. if (stristr($str, LD.'if') === FALSE)
  2583. {
  2584. return $str;
  2585. }
  2586. /* ---------------------------------
  2587. /* Hidden Configuration Variables
  2588. /* - protect_javascript => Prevents advanced conditional parser from processing anything in <script> tags
  2589. /* ---------------------------------*/
  2590. if ($this->EE->config->item('protect_javascript') == 'n')
  2591. {
  2592. $this->protect_javascript = FALSE;
  2593. }
  2594. $user_vars = array('member_id', 'group_id', 'group_description', 'group_title', 'username', 'screen_name',
  2595. 'email', 'ip_address', 'location', 'total_entries',
  2596. 'total_comments', 'private_messages', 'total_forum_posts', 'total_forum_topics', 'total_forum_replies');
  2597. for($i=0,$s=count($user_vars), $data = array(); $i < $s; ++$i)
  2598. {
  2599. $data[$user_vars[$i]] = $this->EE->session->userdata[$user_vars[$i]];
  2600. $data['logged_in_'.$user_vars[$i]] = $this->EE->session->userdata[$user_vars[$i]];
  2601. }
  2602. // Define an alternate variable for {group_id} since some tags use
  2603. // it natively, causing it to be unavailable as a global
  2604. $data['member_group'] = $data['logged_in_member_group'] = $this->EE->session->userdata['group_id'];
  2605. // Logged in and logged out variables
  2606. $data['logged_in'] = ($this->EE->session->userdata['member_id'] == 0) ? 'FALSE' : 'TRUE';
  2607. $data['logged_out'] = ($this->EE->session->userdata['member_id'] != 0) ? 'FALSE' : 'TRUE';
  2608. // current time
  2609. $data['current_time'] = $this->EE->localize->now;
  2610. /** ------------------------------------
  2611. /** Member Group in_group('1') function, Super Secret! Shhhhh!
  2612. /** ------------------------------------*/
  2613. if (preg_match_all("/in_group\(([^\)]+)\)/", $str, $matches))
  2614. {
  2615. $groups = (is_array($this->EE->session->userdata['group_id'])) ? $this->EE->session->userdata['group_id'] : array($this->EE->session->userdata['group_id']);
  2616. for($i=0, $s=count($matches[0]); $i < $s; ++$i)
  2617. {
  2618. $check = explode('|', str_replace(array('"', "'"), '', $matches[1][$i]));
  2619. $str = str_replace($matches[0][$i], (count(array_intersect($check, $groups)) > 0) ? 'TRUE' : 'FALSE', $str);
  2620. }
  2621. }
  2622. /** ------------------------------------
  2623. /** Final Prep, Safety On
  2624. /** ------------------------------------*/
  2625. $str = $this->EE->functions->prep_conditionals($str, array_merge($this->segment_vars, $this->embed_vars, $this->EE->config->_global_vars, $data), 'y');
  2626. /** ------------------------------------
  2627. /** Protect Already Existing Unparsed PHP
  2628. /** ------------------------------------*/
  2629. $opener = '90Parse89Me34Not18Open';
  2630. $closer = '90Parse89Me34Not18Close';
  2631. $str = str_replace(array('<?', '?'.'>'),
  2632. array($opener.'?', '?'.$closer),
  2633. $str);
  2634. /** ------------------------------------
  2635. /** Protect <script> tags
  2636. /** ------------------------------------*/
  2637. $protected = array();
  2638. $front_protect = '89Protect17';
  2639. $back_protect = '21Me01Please47';
  2640. if ($this->protect_javascript !== FALSE &&
  2641. stristr($str, '<script') &&
  2642. preg_match_all("/<script.*?".">.*?<\/script>/is", $str, $matches))
  2643. {
  2644. for($i=0, $s=count($matches[0]); $i < $s; ++$i)
  2645. {
  2646. $protected[$front_protect.$i.$back_protect] = $matches[0][$i];
  2647. }
  2648. $str = str_replace(array_values($protected), array_keys($protected), $str);
  2649. }
  2650. /** ------------------------------------
  2651. /** Convert EE Conditionals to PHP
  2652. /** ------------------------------------*/
  2653. $str = str_replace(array(LD.'/if'.RD, LD.'if:else'.RD), array('<?php endif; ?'.'>','<?php else : ?'.'>'), $str);
  2654. if (strpos($str, LD.'if') !== FALSE)
  2655. {
  2656. $str = preg_replace("/".preg_quote(LD)."((if:(else))*if)\s+(.*?)".preg_quote(RD)."/s", '<?php \\3if(\\4) : ?'.'>', $str);
  2657. }
  2658. $str = $this->parse_template_php($str);
  2659. /** ------------------------------------
  2660. /** Unprotect <script> tags
  2661. /** ------------------------------------*/
  2662. if (count($protected) > 0)
  2663. {
  2664. $str = str_replace(array_keys($protected), array_values($protected), $str);
  2665. }
  2666. /** ------------------------------------
  2667. /** Unprotect Already Existing Unparsed PHP
  2668. /** ------------------------------------*/
  2669. $str = str_replace(array($opener.'?', '?'.$closer),
  2670. array('<'.'?', '?'.'>'),
  2671. $str);
  2672. return $str;
  2673. }
  2674. // --------------------------------------------------------------------
  2675. /**
  2676. * Parse Simple Segment Conditionals
  2677. *
  2678. * Back before Advanced Conditionals many people put embedded templates and preload_replace=""
  2679. * variables in segment conditionals to control what subpages were included and the values of
  2680. * many tag parameters. Since Advanced Conditionals are processed far later in Template parsing
  2681. * than that usage was required, we kept some separate processing in existence for the processing
  2682. * of "simple" segment conditionals. Only one variable, no elseif or else.
  2683. *
  2684. * @access public
  2685. * @param string
  2686. * @return string
  2687. */
  2688. function parse_simple_segment_conditionals($str)
  2689. {
  2690. if ( ! preg_match("/".LD."if\s+segment_.+".RD."/", $str))
  2691. {
  2692. return $str;
  2693. }
  2694. $this->var_cond = $this->EE->functions->assign_conditional_variables($str);
  2695. foreach ($this->var_cond as $val)
  2696. {
  2697. // Make sure this is for a segment conditional
  2698. // And that this is not an advanced conditional
  2699. if ( ! preg_match('/^segment_\d+$/i', $val['3']) OR
  2700. strpos($val[2], 'if:else') !== FALSE OR
  2701. strpos($val[0], 'if:else') !== FALSE OR
  2702. count(preg_split("/(\!=|==|<=|>=|<>|<|>|AND|XOR|OR|&&|\|\|)/", $val[0])) > 2)
  2703. {
  2704. continue;
  2705. }
  2706. $cond = $this->EE->functions->prep_conditional($val[0]);
  2707. $lcond = substr($cond, 0, strpos($cond, ' '));
  2708. $rcond = substr($cond, strpos($cond, ' '));
  2709. if (strpos($rcond, '"') == FALSE && strpos($rcond, "'") === FALSE) continue;
  2710. $n = substr($val[3], 8);
  2711. $temp = (isset($this->EE->uri->segments[$n])) ? $this->EE->uri->segments[$n] : '';
  2712. $lcond = str_replace($val[3], "\$temp", $lcond);
  2713. if (stristr($rcond, '\|') !== FALSE OR stristr($rcond, '&') !== FALSE)
  2714. {
  2715. $rcond = trim($rcond);
  2716. $operator = trim(substr($rcond, 0, strpos($rcond, ' ')));
  2717. $check = trim(substr($rcond, strpos($rcond, ' ')));
  2718. $quote = substr($check, 0, 1);
  2719. if (stristr($rcond, '\|') !== FALSE)
  2720. {
  2721. $array = explode('\|', str_replace($quote, '', $check));
  2722. $break_operator = ' OR ';
  2723. }
  2724. else
  2725. {
  2726. $array = explode('&', str_replace($quote, '', $check));
  2727. $break_operator = ' && ';
  2728. }
  2729. $rcond = $operator.' '.$quote;
  2730. $rcond .= implode($quote.$break_operator.$lcond.' '.$operator.' '.$quote, $array).$quote;
  2731. }
  2732. $cond = $lcond.' '.$rcond;
  2733. $cond = str_replace("\|", "|", $cond);
  2734. eval("\$result = (".$cond.");");
  2735. if ($result)
  2736. {
  2737. $str = str_replace($val[1], $val[2], $str);
  2738. }
  2739. else
  2740. {
  2741. $str = str_replace($val[1], '', $str);
  2742. }
  2743. }
  2744. return $str;
  2745. }
  2746. // --------------------------------------------------------------------
  2747. /**
  2748. * Parse Simple Conditionals
  2749. *
  2750. * Used for processing global_vars and embed and segment conditionals that need to occur far
  2751. * sooner than Advanced Conditionals. These conditionals are only one variable and have no
  2752. * {if:elseif} or {if:else} control structures.
  2753. *
  2754. * @access public
  2755. * @param string
  2756. * @param array
  2757. * @return string
  2758. */
  2759. function simple_conditionals($str, $vars = array())
  2760. {
  2761. if (count($vars) == 0 OR ! stristr($str, LD.'if'))
  2762. {
  2763. return $str;
  2764. }
  2765. $this->var_cond = $this->EE->functions->assign_conditional_variables($str);
  2766. if (count($this->var_cond) == 0)
  2767. {
  2768. return $str;
  2769. }
  2770. foreach ($this->var_cond as $val)
  2771. {
  2772. // Make sure there is such a $global_var
  2773. // And that this is not an advanced conditional
  2774. if ( ! isset($vars[$val[3]]) OR
  2775. strpos($val[2], 'if:else') !== FALSE OR
  2776. strpos($val[0], 'if:else') !== FALSE OR
  2777. count(preg_split("/(\!=|==|<=|>=|<>|<|>|AND|XOR|OR|&&|\|\|)/", $val[0])) > 2)
  2778. {
  2779. continue;
  2780. }
  2781. $cond = $this->EE->functions->prep_conditional($val[0]);
  2782. $lcond = substr($cond, 0, strpos($cond, ' '));
  2783. $rcond = substr($cond, strpos($cond, ' '));
  2784. if (strpos($rcond, '"') == FALSE && strpos($rcond, "'") === FALSE) continue;
  2785. $temp = $vars[$val[3]];
  2786. $lcond = str_replace($val[3], "\$temp", $lcond);
  2787. if (stristr($rcond, '\|') !== FALSE OR stristr($rcond, '&') !== FALSE)
  2788. {
  2789. $rcond = trim($rcond);
  2790. $operator = trim(substr($rcond, 0, strpos($rcond, ' ')));
  2791. $check = trim(substr($rcond, strpos($rcond, ' ')));
  2792. $quote = substr($check, 0, 1);
  2793. if (stristr($rcond, '\|') !== FALSE)
  2794. {
  2795. $array = explode('\|', str_replace($quote, '', $check));
  2796. $break_operator = ' OR ';
  2797. }
  2798. else
  2799. {
  2800. $array = explode('&', str_replace($quote, '', $check));
  2801. $break_operator = ' && ';
  2802. }
  2803. $rcond = $operator.' '.$quote;
  2804. $rcond .= implode($quote.$break_operator.$lcond.' '.$operator.' '.$quote, $array).$quote;
  2805. }
  2806. $cond = $lcond.' '.$rcond;
  2807. $cond = str_replace("\|", "|", $cond);
  2808. eval("\$result = (".$cond.");");
  2809. if ($result)
  2810. {
  2811. $str = str_replace($val[1], $val[2], $str);
  2812. }
  2813. else
  2814. {
  2815. $str = str_replace($val[1], '', $str);
  2816. }
  2817. }
  2818. return $str;
  2819. }
  2820. // --------------------------------------------------------------------
  2821. /**
  2822. * Log Item for Template Processing Log
  2823. *
  2824. * @access public
  2825. * @param string
  2826. * @return void
  2827. */
  2828. function log_item($str)
  2829. {
  2830. if ($this->debugging !== TRUE)
  2831. {
  2832. return;
  2833. }
  2834. if ($this->depth > 0)
  2835. {
  2836. $str = str_repeat('&nbsp;', $this->depth * 5).$str;
  2837. }
  2838. if (phpversion() < 5)
  2839. {
  2840. list($usec, $sec) = explode(" ", microtime());
  2841. $time = ((float)$usec + (float)$sec) - $this->start_microtime;
  2842. }
  2843. else
  2844. {
  2845. $time = microtime(TRUE)-$this->start_microtime;
  2846. }
  2847. $this->log[] = '('.number_format($time, 6).') '.$str;
  2848. }
  2849. // --------------------------------------------------------------------
  2850. /**
  2851. * Basic HTTP Authentication for Templates
  2852. *
  2853. * @access public
  2854. * @return header
  2855. */
  2856. function template_authentication_basic()
  2857. {
  2858. @header('WWW-Authenticate: Basic realm="'.$this->realm.'"');
  2859. $this->EE->output->set_status_header(401);
  2860. @header("Date: ".gmdate("D, d M Y H:i:s")." GMT");
  2861. exit("HTTP/1.0 401 Unauthorized");
  2862. }
  2863. // --------------------------------------------------------------------
  2864. /**
  2865. * HTTP Authentication Validation
  2866. *
  2867. * Takes the username/password from the HTTP Authentication and validates it against the
  2868. * member database and see if this member's member group has access to the template.
  2869. *
  2870. * @access public
  2871. * @param array
  2872. * @return header
  2873. */
  2874. function template_authentication_check_basic($not_allowed_groups = array())
  2875. {
  2876. if ( ! in_array('2', $not_allowed_groups))
  2877. {
  2878. $not_allowed_groups[] = 2;
  2879. $not_allowed_groups[] = 3;
  2880. $not_allowed_groups[] = 4;
  2881. }
  2882. /** ----------------------------------
  2883. /** Find Username, Please
  2884. /** ----------------------------------*/
  2885. if ( ! empty($_SERVER) && isset($_SERVER['PHP_AUTH_USER']))
  2886. {
  2887. $user = $_SERVER['PHP_AUTH_USER'];
  2888. }
  2889. elseif ( ! empty($_ENV) && isset($_ENV['REMOTE_USER']))
  2890. {
  2891. $user = $_ENV['REMOTE_USER'];
  2892. }
  2893. elseif ( @getenv('REMOTE_USER'))
  2894. {
  2895. $user = getenv('REMOTE_USER');
  2896. }
  2897. elseif ( ! empty($_ENV) && isset($_ENV['AUTH_USER']))
  2898. {
  2899. $user = $_ENV['AUTH_USER'];
  2900. }
  2901. elseif ( @getenv('AUTH_USER'))
  2902. {
  2903. $user = getenv('AUTH_USER');
  2904. }
  2905. /** ----------------------------------
  2906. /** Find Password, Please
  2907. /** ----------------------------------*/
  2908. if ( ! empty($_SERVER) && isset($_SERVER['PHP_AUTH_PW']))
  2909. {
  2910. $pass = $_SERVER['PHP_AUTH_PW'];
  2911. }
  2912. elseif ( ! empty($_ENV) && isset($_ENV['REMOTE_PASSWORD']))
  2913. {
  2914. $pass = $_ENV['REMOTE_PASSWORD'];
  2915. }
  2916. elseif ( @getenv('REMOTE_PASSWORD'))
  2917. {
  2918. $pass = getenv('REMOTE_PASSWORD');
  2919. }
  2920. elseif ( ! empty($_ENV) && isset($_ENV['AUTH_PASSWORD']))
  2921. {
  2922. $pass = $_ENV['AUTH_PASSWORD'];
  2923. }
  2924. elseif ( @getenv('AUTH_PASSWORD'))
  2925. {
  2926. $pass = getenv('AUTH_PASSWORD');
  2927. }
  2928. /** ----------------------------------
  2929. /** Authentication for IIS
  2930. /** ----------------------------------*/
  2931. if ( ! isset ($user) OR ! isset($pass) OR (empty($user) && empty($pass)))
  2932. {
  2933. if ( isset($_SERVER['HTTP_AUTHORIZATION']) && substr($_SERVER['HTTP_AUTHORIZATION'], 0, 6) == 'Basic ')
  2934. {
  2935. list($user, $pass) = explode(':', base64_decode(substr($HTTP_AUTHORIZATION, 6)));
  2936. }
  2937. elseif ( ! empty($_ENV) && isset($_ENV['HTTP_AUTHORIZATION']) && substr($_ENV['HTTP_AUTHORIZATION'], 0, 6) == 'Basic ')
  2938. {
  2939. list($user, $pass) = explode(':', base64_decode(substr($_ENV['HTTP_AUTHORIZATION'], 6)));
  2940. }
  2941. elseif (@getenv('HTTP_AUTHORIZATION') && substr(getenv('HTTP_AUTHORIZATION'), 0, 6) == 'Basic ')
  2942. {
  2943. list($user, $pass) = explode(':', base64_decode(substr(getenv('HTTP_AUTHORIZATION'), 6)));
  2944. }
  2945. }
  2946. /** ----------------------------------
  2947. /** Authentication for FastCGI
  2948. /** ----------------------------------*/
  2949. if ( ! isset ($user) OR ! isset($pass) OR (empty($user) && empty($pass)))
  2950. {
  2951. if ( ! empty($_ENV) && isset($_ENV['Authorization']) && substr($_ENV['Authorization'], 0, 6) == 'Basic ')
  2952. {
  2953. list($user, $pass) = explode(':', base64_decode(substr($_ENV['Authorization'], 6)));
  2954. }
  2955. elseif (@getenv('Authorization') && substr(getenv('Authorization'), 0, 6) == 'Basic ')
  2956. {
  2957. list($user, $pass) = explode(':', base64_decode(substr(getenv('Authorization'), 6)));
  2958. }
  2959. }
  2960. if ( ! isset ($user) OR ! isset($pass) OR (empty($user) && empty($pass)))
  2961. {
  2962. return FALSE;
  2963. }
  2964. /** ----------------------------------------
  2965. /** Check password lockout status
  2966. /** ----------------------------------------*/
  2967. if ($this->EE->session->check_password_lockout($user) === TRUE)
  2968. {
  2969. return FALSE;
  2970. }
  2971. /** ----------------------------------
  2972. /** Validate Username and Password
  2973. /** ----------------------------------*/
  2974. $query = $this->EE->db->query("SELECT password, group_id FROM exp_members WHERE username = '".$this->EE->db->escape_str($user)."'");
  2975. if ($query->num_rows() == 0)
  2976. {
  2977. $this->EE->session->save_password_lockout($user);
  2978. return FALSE;
  2979. }
  2980. if (in_array($query->row('group_id') , $not_allowed_groups))
  2981. {
  2982. return FALSE;
  2983. }
  2984. $this->EE->load->helper('security');
  2985. if ($query->row('password') == do_hash($pass))
  2986. {
  2987. return TRUE;
  2988. }
  2989. // just in case it's still in the db as MD5 from an old pMachine or EE 1.x install
  2990. if ($query->row('password') == do_hash($pass, 'md5'))
  2991. {
  2992. return TRUE;
  2993. }
  2994. else
  2995. {
  2996. $this->EE->session->save_password_lockout($user);
  2997. return FALSE;
  2998. }
  2999. }
  3000. // --------------------------------------------------------------------
  3001. /**
  3002. * Assign Form Params
  3003. *
  3004. * Extract form_class / form_id from tagdata, and assign it to a class property
  3005. * So it can be easily accessed.
  3006. *
  3007. * @access private
  3008. * @param array
  3009. * @return array
  3010. */
  3011. function _assign_form_params($tag_data)
  3012. {
  3013. $this->form_id = '';
  3014. $this->form_class = '';
  3015. if ( ! isset($tag_data['params']) OR ! is_array($tag_data['params']))
  3016. {
  3017. return $tag_data;
  3018. }
  3019. if (array_key_exists('form_id', $tag_data['params']))
  3020. {
  3021. $this->form_id = $tag_data['params']['form_id'];
  3022. }
  3023. if (array_key_exists('form_class', $tag_data['params']))
  3024. {
  3025. $this->form_class = $tag_data['params']['form_class'];
  3026. }
  3027. return $tag_data;
  3028. }
  3029. // --------------------------------------------------------------------
  3030. /**
  3031. * Fetch Site IDs for this Installation
  3032. *
  3033. * As ExpressionEngine can include data from other Sites in its installation, we need to validate
  3034. * these parameters and load the data from the correct site. We put it into a class variable
  3035. * so that it only has to happen once during a page request
  3036. *
  3037. * @access private
  3038. */
  3039. function _fetch_site_ids()
  3040. {
  3041. $this->site_ids = array();
  3042. if (isset($this->tagparams['site']))
  3043. {
  3044. if (count($this->sites) == 0 && $this->EE->config->item('multiple_sites_enabled') == 'y' && ! IS_FREELANCER)
  3045. {
  3046. $sites_query = $this->EE->db->query("SELECT site_id, site_name FROM exp_sites ORDER BY site_id");
  3047. foreach($sites_query->result_array() as $row)
  3048. {
  3049. $this->sites[$row['site_id']] = $row['site_name'];
  3050. }
  3051. }
  3052. if (substr($this->tagparams['site'], 0, 4) == 'not ')
  3053. {
  3054. $sites = array_diff($this->sites, explode('|', substr($this->tagparams['site'], 4)));
  3055. }
  3056. else
  3057. {
  3058. $sites = array_intersect($this->sites, explode('|', $this->tagparams['site']));
  3059. }
  3060. // Let us hear it for the preservation of array keys!
  3061. $this->site_ids = array_flip($sites);
  3062. }
  3063. // If no sites were assigned via parameter, then we use the current site's
  3064. // Templates, Channels, and various Site data
  3065. if (count($this->site_ids) == 0)
  3066. {
  3067. $this->site_ids[] = $this->EE->config->item('site_id');
  3068. }
  3069. }
  3070. // --------------------------------------------------------------------
  3071. /**
  3072. * Parse Variables
  3073. *
  3074. * Simplifies variable parsing for plugin
  3075. * and modules developers
  3076. *
  3077. * @access public
  3078. * @param string - the tagdata / text to be parsed
  3079. * @param array - the rows of variables and their data
  3080. * @return string
  3081. */
  3082. function parse_variables($tagdata, $variables)
  3083. {
  3084. if ($tagdata == '' OR ! is_array($variables) OR empty($variables) OR ! is_array($variables[0]))
  3085. {
  3086. return $tagdata;
  3087. }
  3088. // Reset and Match date variables
  3089. $this->date_vars = array();
  3090. $this->_match_date_vars($tagdata);
  3091. // Unfound Variables that We Need Not Parse - Reset
  3092. $this->unfound_vars = array(array()); // nested for depth 0
  3093. // Match {switch="foo|bar"} variables
  3094. $switch = array();
  3095. if (preg_match_all("/".LD."(switch\s*=.+?)".RD."/i", $tagdata, $matches, PREG_SET_ORDER))
  3096. {
  3097. foreach ($matches as $match)
  3098. {
  3099. $sparam = $this->EE->functions->assign_parameters($match[1]);
  3100. if (isset($sparam['switch']))
  3101. {
  3102. $sopt = explode("|", $sparam['switch']);
  3103. $switch[$match[1]] = $sopt;
  3104. }
  3105. }
  3106. }
  3107. // Blast through the array to build our output
  3108. $str = '';
  3109. $count = 0;
  3110. $total_results = count($variables);
  3111. while (($row = array_shift($variables)) !== NULL)
  3112. {
  3113. $count++;
  3114. // Add {count} variable
  3115. if ( ! isset($row['count']))
  3116. {
  3117. $row['count'] = $count;
  3118. }
  3119. // Add {total_results} variable
  3120. if ( ! isset($row['total_results']))
  3121. {
  3122. $row['total_results'] = $total_results;
  3123. }
  3124. // Set {switch} variable values
  3125. foreach ($switch as $key => $val)
  3126. {
  3127. $row[$key] = $switch[$key][($count + count($val) -1) % count($val)];
  3128. }
  3129. $str .= $this->parse_variables_row($tagdata, $row, FALSE);
  3130. }
  3131. if (($backspace = $this->fetch_param('backspace')) !== FALSE && is_numeric($backspace))
  3132. {
  3133. $str = substr($str, 0, -$backspace);
  3134. }
  3135. return $str;
  3136. }
  3137. // --------------------------------------------------------------------
  3138. /**
  3139. * Parse Variables Row
  3140. *
  3141. * Handles a "row" of variable data from
  3142. * the parse_variables() method
  3143. *
  3144. * @access public
  3145. * @param string - the tagdata / text to be parsed
  3146. * @param array - the variables and their data
  3147. * @param bool - coming from parse_variables() or part of set, forces some caching
  3148. *
  3149. * @return string
  3150. */
  3151. function parse_variables_row($tagdata, $variables, $solo = TRUE)
  3152. {
  3153. if ($tagdata == '' OR ! is_array($variables) OR empty($variables))
  3154. {
  3155. return $tagdata;
  3156. }
  3157. if ($solo === TRUE)
  3158. {
  3159. $this->unfound_vars = array(array()); // nested for depth = 0
  3160. }
  3161. // Match date variables if necessary
  3162. if (empty($this->date_vars) && $this->date_vars !== FALSE)
  3163. {
  3164. $this->_match_date_vars($tagdata);
  3165. }
  3166. $this->conditional_vars = $variables;
  3167. foreach ($variables as $name => $value)
  3168. {
  3169. if (isset($this->unfound_vars[0][$name])) continue;
  3170. if (strpos($tagdata, LD.$name) === FALSE)
  3171. {
  3172. $this->unfound_vars[0][$name] = TRUE;
  3173. continue;
  3174. }
  3175. // Pair variables are an array of arrays
  3176. if (is_array($value))
  3177. {
  3178. $tagdata = $this->_parse_var_pair($name, $value, $tagdata, 1);
  3179. }
  3180. else
  3181. {
  3182. $tagdata = $this->_parse_var_single($name, $value, $tagdata);
  3183. }
  3184. }
  3185. // Prep conditionals
  3186. $tagdata = $this->EE->functions->prep_conditionals($tagdata, $this->conditional_vars);
  3187. return $tagdata;
  3188. }
  3189. // --------------------------------------------------------------------
  3190. /**
  3191. * Parse Var Single
  3192. *
  3193. * Parses single variables from the parse_variables() method
  3194. *
  3195. * @access public
  3196. * @param string - the variable's name
  3197. * @param string - the variable's value
  3198. * @param string - the text to parse
  3199. * @return string
  3200. */
  3201. function _parse_var_single($name, $value, $string)
  3202. {
  3203. // parse date variables where applicable
  3204. if (isset($this->date_vars[$name]))
  3205. {
  3206. foreach ($this->date_vars[$name] as $dvar => $dval)
  3207. {
  3208. $val = array_shift($dval);
  3209. $string = str_replace(LD.$dvar.RD,
  3210. str_replace($dval, $this->EE->localize->convert_timestamp($dval, $value, TRUE), $val),
  3211. $string);
  3212. }
  3213. // unformatted dates
  3214. if (strpos($string, LD.$name.RD) !== FALSE)
  3215. {
  3216. $string = str_replace(LD.$name.RD, $value, $string);
  3217. }
  3218. return $string;
  3219. }
  3220. // Simple Variable - Find & Replace & Return
  3221. if (is_string($value))
  3222. {
  3223. return str_replace(LD.$name.RD, $value, $string);
  3224. }
  3225. //
  3226. // Complex Paths and Typography Variables
  3227. //
  3228. // If the variable's $value is an array where $value[0] is 'path' and $value[1] has either
  3229. // the key 'suffix' or 'url' set, then it is a path
  3230. if (is_array($value) && $value[0] == 'path' && isset($value[1]) && (isset($value[1]['suffix']) OR isset($value[1]['url'])))
  3231. {
  3232. // Um...not sure what to do here, quite yet.
  3233. return $string;
  3234. }
  3235. // If the single variable's value is an array, then
  3236. // $value[0] is the content and $value[1] is an array
  3237. // of parameters for the Typography class
  3238. elseif (is_array($value) && count($value) == 2 && is_array($value[1]))
  3239. {
  3240. $raw_content = $value[0];
  3241. $prefs = array();
  3242. foreach (array('text_format', 'html_format', 'auto_links', 'allow_img_url', 'convert_curly') as $pref)
  3243. {
  3244. if (isset($value[1][$pref]))
  3245. {
  3246. $prefs[$pref] = $value[1][$pref];
  3247. }
  3248. }
  3249. // Instantiate Typography only if necessary
  3250. $this->EE->load->library('typography');
  3251. $this->EE->typography->initialize();
  3252. $this->EE->typography->convert_curly = (isset($prefs['convert_curly']) && $prefs['convert_curly'] == 'n') ? FALSE : TRUE;
  3253. $value = $this->EE->typography->parse_type($raw_content, $prefs);
  3254. }
  3255. if (isset($raw_content))
  3256. {
  3257. $this->conditional_vars[$name] = $raw_content;
  3258. }
  3259. return str_replace(LD.$name.RD, $value, $string);
  3260. }
  3261. // --------------------------------------------------------------------
  3262. /**
  3263. * Parse Var Pair
  3264. *
  3265. * Parses pair variables from the parse_variables() method
  3266. *
  3267. * @access public
  3268. * @param string - the variable pair's name
  3269. * @param array - the variable pair's single variables
  3270. * @param string - the text to parse
  3271. * @param integer - iteration depth for unfound_vars
  3272. * @return string
  3273. */
  3274. function _parse_var_pair($name, $variables, $string, $depth = 0)
  3275. {
  3276. if ( ! preg_match("|".LD.$name.'.*?'.RD.'(.*?)'.LD.'/'.$name.RD."|s", $string, $match))
  3277. {
  3278. return $string;
  3279. }
  3280. if (empty($variables[0]))
  3281. {
  3282. return str_replace($match[0], '', $string);
  3283. }
  3284. if ( ! isset($this->unfound_vars[$depth]))
  3285. {
  3286. $this->unfound_vars[$depth] = array();
  3287. }
  3288. $str = '';
  3289. foreach ($variables as $set)
  3290. {
  3291. $temp = $match[1];
  3292. foreach ($set as $name => $value)
  3293. {
  3294. if (isset($this->unfound_vars[$depth][$name])) continue;
  3295. if (strpos($string, LD.$name) === FALSE)
  3296. {
  3297. $this->unfound_vars[$depth][$name] = TRUE;
  3298. continue;
  3299. }
  3300. // Pair variables are an array of arrays.
  3301. if (is_array($value) && is_array($value[0]))
  3302. {
  3303. $temp = $this->_parse_var_pair($name, $value, $temp, $depth + 1);
  3304. }
  3305. else
  3306. {
  3307. $temp = $this->_parse_var_single($name, $value, $temp);
  3308. }
  3309. }
  3310. // Prep conditionals
  3311. $temp = $this->EE->functions->prep_conditionals($temp, $set);
  3312. $str .= $temp;
  3313. }
  3314. return str_replace($match[0], $str, $string);
  3315. }
  3316. // --------------------------------------------------------------------
  3317. /**
  3318. * Match Date Vars
  3319. *
  3320. * Finds date variables within tagdata
  3321. * array structure:
  3322. * [name] => Array
  3323. * (
  3324. * [name format="%m/%d/%y"] => Array
  3325. * (
  3326. * [0] => %m/%d/%y
  3327. * [1] => %m
  3328. * [2] => %d
  3329. * [3] => %y
  3330. * )
  3331. *
  3332. * @access public
  3333. * @param string
  3334. * @return void
  3335. */
  3336. function _match_date_vars($str)
  3337. {
  3338. if (strpos($str, 'format=') === FALSE) return;
  3339. if (preg_match_all("/".LD."([^".RD."]*?)\s+format=[\"'](.*?)[\"']".RD."/s", $str, $matches, PREG_SET_ORDER))
  3340. {
  3341. for ($j = 0, $tot = count($matches); $j < $tot; $j++)
  3342. {
  3343. $matches[$j][0] = str_replace(array(LD,RD), '', $matches[$j][0]);
  3344. $this->date_vars[$matches[$j][1]][$matches[$j][0]] = array_merge(array($matches[$j][2]), $this->EE->localize->fetch_date_params($matches[$j][2]));
  3345. }
  3346. }
  3347. else
  3348. {
  3349. // make sure we don't try to parse date variables again on further calls to parse_variables() or parse_variables_row()
  3350. $this->date_vars = FALSE;
  3351. }
  3352. }
  3353. // --------------------------------------------------------------------
  3354. }
  3355. // END CLASS
  3356. /* End of file Template.php */
  3357. /* Location: ./system/expressionengine/libraries/Template.php */