PageRenderTime 76ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/cp/expressionengine/libraries/Template.php

https://bitbucket.org/sbeuken/artelux
PHP | 3955 lines | 3760 code | 81 blank | 114 comment | 53 complexity | 11391eadc288d12a85eb932d03c7f3c2 MD5 | raw file

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

  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * ExpressionEngine - by EllisLab
  4. *
  5. * @package ExpressionEngine
  6. * @author EllisLab Dev Team
  7. * @copyright Copyright (c) 2003 - 2012, EllisLab, Inc.
  8. * @license http://ellislab.com/expressionengine/user-guide/license.html
  9. * @link http://ellislab.com
  10. * @since Version 2.0
  11. * @filesource
  12. */
  13. // ------------------------------------------------------------------------
  14. /**
  15. * ExpressionEngine Template Parser Class
  16. *
  17. * @package ExpressionEngine
  18. * @subpackage Core
  19. * @category Core
  20. * @author EllisLab Dev Team
  21. * @link http://ellislab.com
  22. */
  23. class EE_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 = 'Restricted Content'; // 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. * @return void
  89. */
  90. public function __construct()
  91. {
  92. // Make a local reference to the ExpressionEngine super object
  93. $this->EE =& get_instance();
  94. if ($this->EE->config->item('multiple_sites_enabled') != 'y')
  95. {
  96. $this->sites[$this->EE->config->item('site_id')] = $this->EE->config->item('site_short_name');
  97. }
  98. if ($this->EE->config->item('template_debugging') === 'y' && $this->EE->session->userdata['group_id'] == 1)
  99. {
  100. $this->debugging = TRUE;
  101. $this->start_microtime = microtime(TRUE);
  102. }
  103. }
  104. // --------------------------------------------------------------------
  105. /**
  106. * Run Template Engine
  107. *
  108. * Upon a Page or a Preview, it Runs the Processing of a Template
  109. * based on URI request or method arguments
  110. *
  111. * @param string
  112. * @param string
  113. * @return void
  114. */
  115. public function run_template_engine($template_group = '', $template = '')
  116. {
  117. $this->log_item(" - Begin Template Processing - ");
  118. // Set the name of the cache folder for both tag and page caching
  119. if ($this->EE->uri->uri_string != '')
  120. {
  121. $this->t_cache_path .= md5($this->EE->functions->fetch_site_index().$this->EE->uri->uri_string).'/';
  122. $this->p_cache_path .= md5($this->EE->functions->fetch_site_index().$this->EE->uri->uri_string).'/';
  123. }
  124. else
  125. {
  126. $this->t_cache_path .= md5($this->EE->config->item('site_url').'index'.$this->EE->uri->query_string).'/';
  127. $this->p_cache_path .= md5($this->EE->config->item('site_url').'index'.$this->EE->uri->query_string).'/';
  128. }
  129. // We limit the total number of cache files in order to
  130. // keep some sanity with large sites or ones that get
  131. // hit by over-ambitious crawlers.
  132. if ($this->disable_caching == FALSE)
  133. {
  134. if ($dh = @opendir(APPPATH.'cache/page_cache'))
  135. {
  136. $i = 0;
  137. while (FALSE !== (readdir($dh)))
  138. {
  139. $i++;
  140. }
  141. $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');
  142. if ($i > $max)
  143. {
  144. $this->EE->functions->clear_caching('page');
  145. }
  146. }
  147. }
  148. $this->log_item("URI: ".$this->EE->uri->uri_string);
  149. $this->log_item("Path.php Template: {$template_group}/{$template}");
  150. $this->fetch_and_parse($template_group, $template, FALSE);
  151. $this->log_item(" - End Template Processing - ");
  152. $this->log_item("Parse Global Variables");
  153. if ($this->template_type == 'static')
  154. {
  155. $this->final_template = $this->restore_xml_declaration($this->final_template);
  156. }
  157. else
  158. {
  159. $this->final_template = $this->parse_globals($this->final_template);
  160. }
  161. $this->log_item("Template Parsing Finished");
  162. $this->EE->output->out_type = $this->template_type;
  163. $this->EE->output->set_output($this->final_template);
  164. }
  165. // --------------------------------------------------------------------
  166. /**
  167. * Fetch and Process Template
  168. *
  169. * Determines what template to process, fetches the template and its preferences, and then processes all of it
  170. *
  171. * @param string
  172. * @param string
  173. * @param bool
  174. * @param int
  175. * @return void
  176. */
  177. public function fetch_and_parse($template_group = '', $template = '', $sub = FALSE, $site_id = '')
  178. {
  179. // add this template to our subtemplate tracker
  180. $this->templates_sofar = $this->templates_sofar.'|'.$site_id.':'.$template_group.'/'.$template.'|';
  181. // Fetch the requested template
  182. // The template can either come from the DB or a cache file
  183. // Do not use a reference!
  184. $this->cache_status = 'NO_CACHE';
  185. $this->log_item("Retrieving Template");
  186. $this->template = ($template_group != '' AND $template != '') ?
  187. $this->fetch_template($template_group, $template, FALSE, $site_id) :
  188. $this->parse_template_uri();
  189. $this->log_item("Template Type: ".$this->template_type);
  190. $this->parse($this->template, $sub, $site_id);
  191. // -------------------------------------------
  192. // 'template_post_parse' hook.
  193. // - Modify template after tag parsing
  194. //
  195. if ($this->EE->extensions->active_hook('template_post_parse') === TRUE)
  196. {
  197. $this->final_template = $this->EE->extensions->call(
  198. 'template_post_parse',
  199. $this->final_template,
  200. $sub,
  201. $site_id
  202. );
  203. }
  204. //
  205. // -------------------------------------------
  206. }
  207. // --------------------------------------------------------------------
  208. /**
  209. * Parse a string as a template
  210. *
  211. * @param string
  212. * @param string
  213. * @return void
  214. */
  215. public function parse(&$str, $sub = FALSE, $site_id = '')
  216. {
  217. if ($str != '')
  218. {
  219. $this->template =& $str;
  220. }
  221. // Static Content, No Parsing
  222. if ($this->template_type == 'static' OR $this->embed_type == 'static')
  223. {
  224. if ($sub == FALSE)
  225. {
  226. $this->final_template = $this->template;
  227. }
  228. return;
  229. }
  230. /* -------------------------------------
  231. /* "Smart" Static Parsing
  232. /*
  233. /* Performed on embedded webpage templates only that do not have
  234. /* ExpressionEngine tags or PHP in them.
  235. /*
  236. /* Hidden Configuration Variable
  237. /* - smart_static_parsing => Bypass parsing of templates that could be
  238. /* of the type 'static' but aren't? (y/n)
  239. /* -------------------------------------*/
  240. if ($this->EE->config->item('smart_static_parsing') !== 'n' && $this->embed_type == 'webpage' && ! stristr($this->template, LD) && ! stristr($this->template, '<?'))
  241. {
  242. $this->log_item("Smart Static Parsing Triggered");
  243. if ($sub == FALSE)
  244. {
  245. $this->final_template = $this->template;
  246. }
  247. return;
  248. }
  249. // Parse 'Site' variables
  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. // Parse manual variables and Snippets
  260. // These are variables that can be set in the path.php file
  261. if (count($this->EE->config->_global_vars) > 0)
  262. {
  263. $this->log_item("Snippets (Keys): ".implode('|', array_keys($this->EE->config->_global_vars)));
  264. $this->log_item("Snippets (Values): ".trim(implode('|', $this->EE->config->_global_vars)));
  265. foreach ($this->EE->config->_global_vars as $key => $val)
  266. {
  267. $this->template = str_replace(LD.$key.RD, $val, $this->template);
  268. }
  269. // in case any of these variables have EE comments of their own
  270. $this->template = $this->remove_ee_comments($this->template);
  271. }
  272. // Parse URI segments
  273. // This code lets admins fetch URI segments which become
  274. // available as: {segment_1} {segment_2}
  275. for ($i = 1; $i < 10; $i++)
  276. {
  277. $this->template = str_replace(LD.'segment_'.$i.RD, $this->EE->uri->segment($i), $this->template);
  278. $this->segment_vars['segment_'.$i] = $this->EE->uri->segment($i);
  279. }
  280. // Parse {embed} tag variables
  281. if ($sub === TRUE && count($this->embed_vars) > 0)
  282. {
  283. $this->log_item("Embed Variables (Keys): ".implode('|', array_keys($this->embed_vars)));
  284. $this->log_item("Embed Variables (Values): ".trim(implode('|', $this->embed_vars)));
  285. foreach ($this->embed_vars as $key => $val)
  286. {
  287. // add 'embed:' to the key for replacement and so these variables work in conditionals
  288. $this->embed_vars['embed:'.$key] = $val;
  289. unset($this->embed_vars[$key]);
  290. $this->template = str_replace(LD.'embed:'.$key.RD, $val, $this->template);
  291. }
  292. }
  293. // cleanup of leftover/undeclared embed variables
  294. // don't worry with undeclared embed: vars in conditionals as the conditionals processor will handle that adequately
  295. if (strpos($this->template, LD.'embed:') !== FALSE)
  296. {
  297. $this->template = preg_replace('/'.LD.'embed:([^!]+?)'.RD.'/', '', $this->template);
  298. }
  299. // Parse date format string "constants"
  300. $date_constants = array('DATE_ATOM' => '%Y-%m-%dT%H:%i:%s%Q',
  301. 'DATE_COOKIE' => '%l, %d-%M-%y %H:%i:%s UTC',
  302. 'DATE_ISO8601' => '%Y-%m-%dT%H:%i:%s%Q',
  303. 'DATE_RFC822' => '%D, %d %M %y %H:%i:%s %O',
  304. 'DATE_RFC850' => '%l, %d-%M-%y %H:%m:%i UTC',
  305. 'DATE_RFC1036' => '%D, %d %M %y %H:%i:%s %O',
  306. 'DATE_RFC1123' => '%D, %d %M %Y %H:%i:%s %O',
  307. 'DATE_RFC2822' => '%D, %d %M %Y %H:%i:%s %O',
  308. 'DATE_RSS' => '%D, %d %M %Y %H:%i:%s %O',
  309. 'DATE_W3C' => '%Y-%m-%dT%H:%i:%s%Q'
  310. );
  311. foreach ($date_constants as $key => $val)
  312. {
  313. $this->template = str_replace(LD.$key.RD, $val, $this->template);
  314. }
  315. $this->log_item("Parse Date Format String Constants");
  316. // Template's Last Edit time {template_edit_date format="%Y %m %d %H:%i:%s"}
  317. if (strpos($this->template, LD.'template_edit_date') !== FALSE && preg_match_all("/".LD."template_edit_date\s+format=([\"\'])([^\\1]*?)\\1".RD."/", $this->template, $matches))
  318. {
  319. for ($j = 0; $j < count($matches[0]); $j++)
  320. {
  321. $this->template = str_replace($matches[0][$j], $this->EE->localize->decode_date($matches[2][$j], $this->template_edit_date), $this->template);
  322. }
  323. }
  324. // Current time {current_time format="%Y %m %d %H:%i:%s"}
  325. if (strpos($this->template, LD.'current_time') !== FALSE && preg_match_all("/".LD."current_time\s+format=([\"\'])([^\\1]*?)\\1".RD."/", $this->template, $matches))
  326. {
  327. for ($j = 0; $j < count($matches[0]); $j++)
  328. {
  329. $this->template = str_replace($matches[0][$j], $this->EE->localize->decode_date($matches[2][$j], $this->EE->localize->now), $this->template);
  330. }
  331. }
  332. $this->template = str_replace(LD.'current_time'.RD, $this->EE->localize->now, $this->template);
  333. $this->log_item("Parse Current Time Variables");
  334. // Is the main template cached?
  335. // If a cache file exists for the primary template
  336. // there is no reason to go further.
  337. // However we do need to fetch any subtemplates
  338. if ($this->cache_status == 'CURRENT' AND $sub == FALSE)
  339. {
  340. $this->log_item("Cached Template Used");
  341. $this->template = $this->parse_nocache($this->template);
  342. // Smite Our Enemies: Advanced Conditionals
  343. if (stristr($this->template, LD.'if'))
  344. {
  345. $this->template = $this->advanced_conditionals($this->template);
  346. }
  347. $this->log_item("Conditionals Parsed, Processing Sub Templates");
  348. $this->final_template = $this->template;
  349. $this->process_sub_templates($this->template);
  350. return;
  351. }
  352. // Remove whitespace from variables.
  353. // This helps prevent errors, particularly if PHP is used in a template
  354. $this->template = preg_replace("/".LD."\s*(\S+)\s*".RD."/U", LD."\\1".RD, $this->template);
  355. // Parse Input Stage PHP
  356. if ($this->parse_php == TRUE && $this->php_parse_location == 'input' && $this->cache_status != 'CURRENT')
  357. {
  358. $this->log_item("Parsing PHP on Input");
  359. $this->template = $this->parse_template_php($this->template);
  360. }
  361. // Smite Our Enemies: Conditionals
  362. $this->log_item("Parsing Segment, Embed, and Global Vars Conditionals");
  363. $this->template = $this->parse_simple_segment_conditionals($this->template);
  364. $this->template = $this->simple_conditionals($this->template, $this->embed_vars);
  365. $this->template = $this->simple_conditionals($this->template, $this->EE->config->_global_vars);
  366. // Assign Variables
  367. if (strpos($this->template, 'preload_replace') !== FALSE)
  368. {
  369. if (preg_match_all("/".LD."preload_replace:(.+?)=([\"\'])([^\\2]*?)\\2".RD."/i", $this->template, $matches))
  370. {
  371. $this->log_item("Processing Preload Text Replacements: ".trim(implode('|', $matches[1])));
  372. for ($j = 0; $j < count($matches[0]); $j++)
  373. {
  374. $this->template = str_replace($matches[0][$j], "", $this->template);
  375. $this->template = str_replace(LD.$matches[1][$j].RD, $matches[3][$j], $this->template);
  376. }
  377. }
  378. }
  379. // Parse Plugin and Module Tags
  380. $this->tags();
  381. if ($this->cease_processing === TRUE)
  382. {
  383. return;
  384. }
  385. // Parse Output Stage PHP
  386. if ($this->parse_php == TRUE AND $this->php_parse_location == 'output' AND $this->cache_status != 'CURRENT')
  387. {
  388. $this->log_item("Parsing PHP on Output");
  389. $this->template = $this->parse_template_php($this->template);
  390. }
  391. // Write the cache file if needed
  392. if ($this->cache_status == 'EXPIRED')
  393. {
  394. $this->template = $this->EE->functions->insert_action_ids($this->template);
  395. $this->write_cache_file($this->cache_hash, $this->template, 'template');
  396. }
  397. // Parse Our Uncacheable Forms
  398. $this->template = $this->parse_nocache($this->template);
  399. // Smite Our Enemies: Advanced Conditionals
  400. if (strpos($this->template, LD.'if') !== FALSE)
  401. {
  402. $this->log_item("Processing Advanced Conditionals");
  403. $this->template = $this->advanced_conditionals($this->template);
  404. }
  405. // Build finalized template
  406. // We only do this on the first pass.
  407. // The sub-template routine will insert embedded
  408. // templates into the master template
  409. if ($sub == FALSE)
  410. {
  411. $this->final_template = $this->template;
  412. $this->process_sub_templates($this->template);
  413. }
  414. }
  415. // --------------------------------------------------------------------
  416. /**
  417. * Processes Any Embedded Templates in String
  418. *
  419. * If any {embed=} tags are found, it processes those templates and does a replacement.
  420. *
  421. * @param string
  422. * @return void
  423. */
  424. public function process_sub_templates($template)
  425. {
  426. // Match all {embed=bla/bla} tags
  427. $matches = array();
  428. if ( ! preg_match_all("/(".LD."embed\s*=)(.*?)".RD."/s", $template, $matches))
  429. {
  430. return;
  431. }
  432. // Loop until we have parsed all sub-templates
  433. // For each embedded tag we encounter we'll run the template parsing
  434. // function - AND - through the beauty of recursive functions we
  435. // will also call THIS function as well, allowing us to parse
  436. // infinitely nested sub-templates in one giant loop o' love
  437. $this->log_item(" - Processing Sub Templates (Depth: ".($this->depth+1).") - ");
  438. $i = 0;
  439. $this->depth++;
  440. $this->log_item("List of Embeds: ".str_replace(array('"', "'"), '', trim(implode(',', $matches[2]))));
  441. // re-match the full tag of each if necessary before we start processing
  442. // necessary evil in case template globals are used inside the embed tag,
  443. // doing this within the processing loop will result in leaving unparsed
  444. // embed tags e.g. {embed="foo/bar" var="{global_var}/{custom_field}"}
  445. $temp = $template;
  446. foreach ($matches[2] as $key => $val)
  447. {
  448. if (strpos($val, LD) !== FALSE)
  449. {
  450. $matches[0][$key] = $this->EE->functions->full_tag($matches[0][$key], $temp);
  451. $matches[2][$key] = substr(str_replace($matches[1][$key], '', $matches[0][$key]), 0, -1);
  452. $temp = str_replace($matches[0][$key], '', $temp);
  453. }
  454. }
  455. // Load the string helper
  456. $this->EE->load->helper('string');
  457. foreach($matches[2] as $key => $val)
  458. {
  459. $parts = preg_split("/\s+/", $val, 2);
  460. $this->embed_vars = (isset($parts[1])) ? $this->EE->functions->assign_parameters($parts[1]) : array();
  461. if ($this->embed_vars === FALSE)
  462. {
  463. $this->embed_vars = array();
  464. }
  465. $val = trim_slashes(strip_quotes($parts[0]));
  466. if (strpos($val, '/') === FALSE)
  467. {
  468. continue;
  469. }
  470. $ex = explode("/", trim($val));
  471. if (count($ex) != 2)
  472. {
  473. continue;
  474. }
  475. // Determine Site
  476. $site_id = $this->EE->config->item('site_id');
  477. if (stristr($ex[0], ':'))
  478. {
  479. $name = substr($ex[0], 0, strpos($ex[0], ':'));
  480. if ($this->EE->config->item('multiple_sites_enabled') == 'y' && ! IS_CORE)
  481. {
  482. if (count($this->sites) == 0)
  483. {
  484. // This should really be cached somewhere
  485. $this->EE->db->select('site_id, site_name');
  486. $sites_query = $this->EE->db->get('sites');
  487. foreach($sites_query->result_array() as $row)
  488. {
  489. $this->sites[$row['site_id']] = $row['site_name'];
  490. }
  491. }
  492. $site_id = array_search($name, $this->sites);
  493. if (empty($site_id))
  494. {
  495. $site_id = $this->EE->config->item('site_id');
  496. }
  497. }
  498. $ex[0] = str_replace($name.':', '', $ex[0]);
  499. }
  500. // Loop Prevention
  501. /* -------------------------------------------
  502. /* Hidden Configuration Variable
  503. /* - template_loop_prevention => 'n'
  504. Whether or not loop prevention is enabled - y/n
  505. /* -------------------------------------------*/
  506. if (substr_count($this->templates_sofar, '|'.$site_id.':'.$ex['0'].'/'.$ex['1'].'|') > 1 && $this->EE->config->item('template_loop_prevention') != 'n')
  507. {
  508. $this->final_template = ($this->EE->config->item('debug') >= 1) ? str_replace('%s', $ex['0'].'/'.$ex['1'], $this->EE->lang->line('template_loop')) : "";
  509. return;
  510. }
  511. // Process Subtemplate
  512. $this->log_item("Processing Sub Template: ".$ex[0]."/".$ex[1]);
  513. $this->fetch_and_parse($ex[0], $ex[1], TRUE, $site_id);
  514. $this->final_template = str_replace($matches[0][$key], $this->template, $this->final_template);
  515. $this->embed_type = '';
  516. // Here we go again! Wheeeeeee.....
  517. $this->process_sub_templates($this->template);
  518. // pull the subtemplate tracker back a level to the parent template
  519. $this->templates_sofar = substr($this->templates_sofar, 0, - strlen('|'.$site_id.':'.$ex[0].'/'.$ex[1].'|'));
  520. }
  521. $this->depth--;
  522. if ($this->depth == 0)
  523. {
  524. $this->templates_sofar = '';
  525. }
  526. }
  527. // --------------------------------------------------------------------
  528. /**
  529. * Finds Tags, Parses Them
  530. *
  531. * Goes Through the Template, Finds the Beginning and End of Tags, and Stores Tag Data in a Class Array
  532. *
  533. * @return void
  534. */
  535. public function parse_tags()
  536. {
  537. while (TRUE)
  538. {
  539. // Make a "floating" copy of the template which we'll progressively slice into pieces with each loop
  540. $this->fl_tmpl = $this->template;
  541. // Identify the string position of the first occurence of a matched tag
  542. $this->in_point = strpos($this->fl_tmpl, LD.'exp:');
  543. // If the above variable returns FALSE we are done looking for tags
  544. // This single conditional keeps the template engine from spiraling
  545. // out of control in an infinite loop.
  546. if (FALSE === $this->in_point)
  547. {
  548. break;
  549. }
  550. else
  551. {
  552. // Process the tag data
  553. // These REGEXs parse out the various components contained in any given tag.
  554. // Grab the opening portion of the tag: {exp:some:tag param="value" param="value"}
  555. if ( ! preg_match("/".LD.'exp:'.".*?".RD."/s", $this->fl_tmpl, $matches))
  556. {
  557. $this->template = preg_replace("/".LD.'exp:'.".*?$/", '', $this->template);
  558. break;
  559. }
  560. // Checking for variables/tags embedded within tags
  561. // {exp:channel:entries channel="{master_channel_name}"}
  562. if (stristr(substr($matches[0], 1), LD) !== FALSE)
  563. {
  564. $matches[0] = $this->EE->functions->full_tag($matches[0]);
  565. }
  566. $this->log_item("Tag: ".$matches[0]);
  567. $raw_tag = str_replace(array("\r\n", "\r", "\n", "\t"), " ", $matches[0]);
  568. $tag_length = strlen($raw_tag);
  569. $data_start = $this->in_point + $tag_length;
  570. $tag = trim(substr($raw_tag, 1, -1));
  571. $args = trim((preg_match("/\s+.*/", $tag, $matches))) ? $matches[0] : '';
  572. $tag = trim(str_replace($args, '', $tag));
  573. $cur_tag_close = LD.'/'.$tag.RD;
  574. // Deprecate "weblog" tags, but allow them to work until 2.1, then remove this.
  575. if (strpos($tag, ':weblog:') !== FALSE OR strpos($tag, ' weblog=') !== FALSE)
  576. {
  577. $tag = str_replace(array(':weblog:', ' weblog='), array(':channel:', ' channel='), $tag);
  578. $this->log_item("WARNING: Deprecated 'weblog' tag used, please change to 'channel'");
  579. }
  580. // -----------------------------------------
  581. // Grab the class name and method names contained in the tag
  582. $class = explode(':', substr($tag, strlen('exp') + 1));
  583. // Tags can either have one segment or two:
  584. // {exp:first_segment}
  585. // {exp:first_segment:second_segment}
  586. //
  587. // These two segments represent either a "class:constructor"
  588. // or a "class:method". We need to determine which one it is.
  589. // if (count($class) == 1)
  590. // {
  591. // $class[1] = $class[0];
  592. // }
  593. foreach($class as $key => $value)
  594. {
  595. $class[$key] = trim($value);
  596. }
  597. // -----------------------------------------
  598. // Assign parameters based on the arguments from the tag
  599. $args = $this->EE->functions->assign_parameters($args);
  600. // standardized mechanism for "search" type parameters get some extra lovin'
  601. $search_fields = array();
  602. if ($args !== FALSE)
  603. {
  604. foreach ($args as $key => $val)
  605. {
  606. if (strncmp($key, 'search:', 7) == 0)
  607. {
  608. $search_fields[substr($key, 7)] = $val;
  609. }
  610. }
  611. }
  612. // Trim the floating template, removing the tag we just parsed.
  613. $this->fl_tmpl = substr($this->fl_tmpl, $this->in_point + $tag_length);
  614. $out_point = strpos($this->fl_tmpl, $cur_tag_close);
  615. // Do we have a tag pair?
  616. if (FALSE !== $out_point)
  617. {
  618. // Assign the data contained between the opening/closing tag pair
  619. $this->log_item("Closing Tag Found");
  620. $block = substr($this->template, $data_start, $out_point);
  621. // Fetch the "no_results" data
  622. $no_results = '';
  623. $no_results_block = '';
  624. if (strpos($block, 'if no_results') !== FALSE && preg_match("/".LD."if no_results".RD."(.*?)".LD.'\/'."if".RD."/s", $block, $match))
  625. {
  626. // Match the entirety of the conditional, dude. Bad Rick!
  627. if (stristr($match[1], LD.'if'))
  628. {
  629. $match[0] = $this->EE->functions->full_tag($match[0], $block, LD.'if', LD.'\/'."if".RD);
  630. }
  631. $no_results = substr($match[0], strlen(LD."if no_results".RD), -strlen(LD.'/'."if".RD));
  632. $no_results_block = $match[0];
  633. }
  634. // Define the entire "chunk" - from the left edge of the opening tag
  635. // to the right edge of closing tag.
  636. $out_point = $out_point + $tag_length + strlen($cur_tag_close);
  637. $chunk = substr($this->template, $this->in_point, $out_point);
  638. }
  639. else
  640. {
  641. // Single tag...
  642. $this->log_item("No Closing Tag");
  643. $block = ''; // Single tags don't contain data blocks
  644. $no_results = '';
  645. $no_results_block = '';
  646. // Define the entire opening tag as a "chunk"
  647. $chunk = substr($this->template, $this->in_point, $tag_length);
  648. }
  649. // Strip the "chunk" from the template, replacing it with a unique marker.
  650. if (stristr($raw_tag, 'random'))
  651. {
  652. $this->template = preg_replace("|".preg_quote($chunk)."|s", 'M'.$this->loop_count.$this->marker, $this->template, 1);
  653. }
  654. else
  655. {
  656. $this->template = str_replace($chunk, 'M'.$this->loop_count.$this->marker, $this->template);
  657. }
  658. $cfile = md5($chunk); // This becomes the name of the cache file
  659. // Build a multi-dimensional array containing all of the tag data we've assembled
  660. $this->tag_data[$this->loop_count]['tag'] = $raw_tag;
  661. $this->tag_data[$this->loop_count]['class'] = $class[0];
  662. $this->tag_data[$this->loop_count]['method'] = (isset($class[1])) ? $class[1] : FALSE;
  663. $this->tag_data[$this->loop_count]['tagparts'] = $class;
  664. $this->tag_data[$this->loop_count]['params'] = $args;
  665. $this->tag_data[$this->loop_count]['chunk'] = $chunk; // Matched data block - including opening/closing tags
  666. $this->tag_data[$this->loop_count]['block'] = $block; // Matched data block - no tags
  667. $this->tag_data[$this->loop_count]['cache'] = $args;
  668. $this->tag_data[$this->loop_count]['cfile'] = $cfile;
  669. $this->tag_data[$this->loop_count]['no_results'] = $no_results;
  670. $this->tag_data[$this->loop_count]['no_results_block'] = $no_results_block;
  671. $this->tag_data[$this->loop_count]['search_fields'] = $search_fields;
  672. } // END IF
  673. // Increment counter
  674. $this->loop_count++;
  675. } // END WHILE
  676. }
  677. // --------------------------------------------------------------------
  678. /**
  679. * Looks Through Template Looking for Tags
  680. *
  681. * Goes Through the Template, Finds the Beginning and End of Tags, and Stores Tag Data in a Class Array
  682. *
  683. * @return void
  684. */
  685. public function tags()
  686. {
  687. // Fetch installed modules and plugins if needed
  688. if (count($this->modules) == 0)
  689. {
  690. $this->fetch_addons();
  691. }
  692. // Parse the template.
  693. $this->log_item(" - Beginning Tag Processing - ");
  694. while (is_int(strpos($this->template, LD.'exp:')))
  695. {
  696. // Initialize values between loops
  697. $this->tag_data = array();
  698. $this->var_single = array();
  699. $this->var_cond = array();
  700. $this->var_pair = array();
  701. $this->loop_count = 0;
  702. $this->log_item("Parsing Tags in Template");
  703. // Run the template parser
  704. $this->parse_tags();
  705. $this->log_item("Processing Tags");
  706. // Run the class/method handler
  707. $this->process_tags();
  708. if ($this->cease_processing === TRUE)
  709. {
  710. return;
  711. }
  712. }
  713. $this->log_item(" - End Tag Processing - ");
  714. }
  715. // --------------------------------------------------------------------
  716. /**
  717. * Process Tags
  718. *
  719. * Takes the Class Array Full of Tag Data and Processes the Tags One by One. Class class, feeds
  720. * data to class, takes results, and puts it back into the Template.
  721. *
  722. * @return void
  723. */
  724. public function process_tags()
  725. {
  726. $plugins = array();
  727. $modules = array();
  728. // Fill an array with the names of all the classes that we previously extracted from the tags
  729. for ($i = 0, $ctd = count($this->tag_data); $i < $ctd; $i++)
  730. {
  731. // Check the tag cache file
  732. $cache_contents = $this->fetch_cache_file($this->tag_data[$i]['cfile'], 'tag', $this->tag_data[$i]['cache']);
  733. // Set cache status for final processing
  734. $this->tag_data[$i]['cache'] = $this->tag_cache_status;
  735. if ($this->tag_cache_status == 'CURRENT')
  736. {
  737. // If so, replace the marker in the tag with the cache data
  738. $this->log_item("Tag Cached and Cache is Current");
  739. $this->template = str_replace('M'.$i.$this->marker, $cache_contents, $this->template);
  740. }
  741. else
  742. {
  743. // Is a module or plug-in being requested?
  744. if ( ! in_array($this->tag_data[$i]['class'] , $this->modules))
  745. {
  746. if ( ! in_array($this->tag_data[$i]['class'] , $this->plugins))
  747. {
  748. $this->log_item("Invalid Tag");
  749. if ($this->EE->config->item('debug') >= 1)
  750. {
  751. if (isset($this->tag_data[$i]['tagparts'][1]) &&
  752. $this->tag_data[$i]['tagparts'][0] == $this->tag_data[$i]['tagparts'][1] &&
  753. ! isset($this->tag_data[$i]['tagparts'][2]))
  754. {
  755. unset($this->tag_data[$i]['tagparts'][1]);
  756. }
  757. $error = $this->EE->lang->line('error_tag_syntax');
  758. $error .= '<br /><br />';
  759. $error .= htmlspecialchars(LD);
  760. $error .= 'exp:'.implode(':', $this->tag_data[$i]['tagparts']);
  761. $error .= htmlspecialchars(RD);
  762. $error .= '<br /><br />';
  763. $error .= $this->EE->lang->line('error_fix_syntax');
  764. $this->EE->output->fatal_error($error);
  765. }
  766. else
  767. return FALSE;
  768. }
  769. else
  770. {
  771. $plugins[] = $this->tag_data[$i]['class'];
  772. $this->log_item("Plugin Tag: ".ucfirst($this->tag_data[$i]['class']).'/'.$this->tag_data[$i]['method']);
  773. }
  774. }
  775. else
  776. {
  777. $modules[] = $this->tag_data[$i]['class'];
  778. $this->log_item("Module Tag: ".ucfirst($this->tag_data[$i]['class']).'/'.$this->tag_data[$i]['method']);
  779. }
  780. }
  781. }
  782. // Remove duplicate class names and re-order the array
  783. $plugins = array_values(array_unique($plugins));
  784. $modules = array_values(array_unique($modules));
  785. // Dynamically require the file that contains each class
  786. $this->log_item("Including Files for Plugins and Modules");
  787. foreach ($plugins as $plugin)
  788. {
  789. // make sure it's not already included just in case
  790. if ( ! class_exists($plugin))
  791. {
  792. if (in_array($plugin ,$this->EE->core->native_plugins))
  793. {
  794. require_once PATH_PI."pi.{$plugin}.php";
  795. }
  796. else
  797. {
  798. require_once PATH_THIRD."{$plugin}/pi.{$plugin}.php";
  799. }
  800. }
  801. }
  802. foreach ($modules as $module)
  803. {
  804. // make sure it's not already included just in case
  805. if ( ! class_exists($module))
  806. {
  807. if (in_array($module, $this->EE->core->native_modules))
  808. {
  809. require_once PATH_MOD."{$module}/mod.{$module}.php";
  810. }
  811. else
  812. {
  813. require_once PATH_THIRD."{$module}/mod.{$module}.php";
  814. }
  815. }
  816. }
  817. $this->log_item("Files for Plugins and Modules All Included");
  818. // Only Retrieve Data if Not Done Before and Modules Being Called
  819. if (count($this->module_data) == 0 && count(array_intersect($this->modules, $modules)) > 0)
  820. {
  821. $this->EE->db->select('module_version, module_name');
  822. $query = $this->EE->db->get('modules');
  823. foreach($query->result_array() as $row)
  824. {
  825. $this->module_data[$row['module_name']] = array('version' => $row['module_version']);
  826. }
  827. }
  828. // Final data processing
  829. // Loop through the master array containing our extracted template data
  830. $this->log_item("Beginning Final Tag Data Processing");
  831. reset($this->tag_data);
  832. for ($i = 0; $i < count($this->tag_data); $i++)
  833. {
  834. if ($this->tag_data[$i]['cache'] != 'CURRENT')
  835. {
  836. $this->log_item("Calling Class/Method: ".ucfirst($this->tag_data[$i]['class'])."/".$this->tag_data[$i]['method']);
  837. /* ---------------------------------
  838. /* Plugin as Parameter
  839. /*
  840. /* - Example: channel="{exp:some_plugin}"
  841. /* - A bit of a hidden feature. Has been tested but not quite
  842. /* ready to say it is ready for prime time as I might want to
  843. /* move it to earlier in processing so that if there are
  844. /* multiple plugins being used as parameters it is only called
  845. /* once instead of for every single parameter. - Paul
  846. /* ---------------------------------*/
  847. 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')
  848. {
  849. foreach($this->tag_data[$i]['params'] as $name => $param)
  850. {
  851. if (stristr($this->tag_data[$i]['params'][$name], LD.'exp'))
  852. {
  853. $this->log_item("Plugin in Parameter, Processing Plugin First");
  854. $TMPL2 = clone $this;
  855. while (is_int(strpos($TMPL2->tag_data[$i]['params'][$name], LD.'exp:')))
  856. {
  857. unset($this->EE->TMPL);
  858. $this->EE->TMPL = new EE_Template();
  859. $this->EE->TMPL->start_microtime = $this->start_microtime;
  860. $this->EE->TMPL->template = $TMPL2->tag_data[$i]['params'][$name];
  861. $this->EE->TMPL->tag_data = array();
  862. $this->EE->TMPL->var_single = array();
  863. $this->EE->TMPL->var_cond = array();
  864. $this->EE->TMPL->var_pair = array();
  865. $this->EE->TMPL->plugins = $TMPL2->plugins;
  866. $this->EE->TMPL->modules = $TMPL2->modules;
  867. $this->EE->TMPL->parse_tags();
  868. $this->EE->TMPL->process_tags();
  869. $this->EE->TMPL->loop_count = 0;
  870. $TMPL2->tag_data[$i]['params'][$name] = $this->EE->TMPL->template;
  871. $TMPL2->log = array_merge($TMPL2->log, $this->EE->TMPL->log);
  872. }
  873. foreach (get_object_vars($TMPL2) as $key => $value)
  874. {
  875. $this->$key = $value;
  876. }
  877. unset($TMPL2);
  878. $this->EE->TMPL = $this;
  879. }
  880. }
  881. }
  882. // Nested Plugins...
  883. if (in_array($this->tag_data[$i]['class'] , $this->plugins) && strpos($this->tag_data[$i]['block'], LD.'exp:') !== FALSE)
  884. {
  885. if ( ! isset($this->tag_data[$i]['params']['parse']) OR $this->tag_data[$i]['params']['parse'] != 'inward')
  886. {
  887. $this->log_item("Nested Plugins in Tag, Parsing Outward First");
  888. $TMPL2 = clone $this;
  889. while (is_int(strpos($TMPL2->tag_data[$i]['block'], LD.'exp:')))
  890. {
  891. unset($this->EE->TMPL);
  892. $this->EE->TMPL = new EE_Template();
  893. $this->EE->TMPL->start_microtime = $this->start_microtime;
  894. $this->EE->TMPL->template = $TMPL2->tag_data[$i]['block'];
  895. $this->EE->TMPL->tag_data = array();
  896. $this->EE->TMPL->var_single = array();
  897. $this->EE->TMPL->var_cond = array();
  898. $this->EE->TMPL->var_pair = array();
  899. $this->EE->TMPL->plugins = $TMPL2->plugins;
  900. $this->EE->TMPL->modules = $TMPL2->modules;
  901. $this->EE->TMPL->parse_tags();
  902. $this->EE->TMPL->process_tags();
  903. $this->EE->TMPL->loop_count = 0;
  904. $TMPL2->tag_data[$i]['block'] = $this->EE->TMPL->template;
  905. $TMPL2->log = array_merge($TMPL2->log, $this->EE->TMPL->log);
  906. }
  907. foreach (get_object_vars($TMPL2) as $key => $value)
  908. {
  909. $this->$key = $value;
  910. }
  911. unset($TMPL2);
  912. $this->EE->TMPL = $this;
  913. }
  914. }
  915. // Assign the data chunk, parameters
  916. // We moved the no_results_block here because of nested tags. The first
  917. // parsed tag has priority for that conditional.
  918. $this->tagdata = str_replace($this->tag_data[$i]['no_results_block'], '', $this->tag_data[$i]['block']);
  919. $this->tagparams = $this->tag_data[$i]['params'];
  920. $this->tagchunk = $this->tag_data[$i]['chunk'];
  921. $this->tagproper = $this->tag_data[$i]['tag'];
  922. $this->tagparts = $this->tag_data[$i]['tagparts'];
  923. $this->no_results = $this->tag_data[$i]['no_results'];
  924. $this->search_fields = $this->tag_data[$i]['search_fields'];
  925. // Assign Sites for Tag
  926. $this->_fetch_site_ids();
  927. // Fetch Form Class/Id Attributes
  928. $this->tag_data[$i] = $this->_assign_form_params($this->tag_data[$i]);
  929. // Relationship Data Pulled Out
  930. // If the channel:entries tag or search:search_results is being called
  931. // we need to extract any relationship data that might be present.
  932. // Note: This needs to happen before extracting the variables
  933. // in the tag so it doesn't get confused as to which entry the
  934. // variables belong to.
  935. if (($this->tag_data[$i]['class'] == 'channel' AND $this->tag_data[$i]['method'] == 'entries')
  936. OR ($this->tag_data[$i]['class'] == 'search' AND $this->tag_data[$i]['method'] == 'search_results'))
  937. {
  938. $this->tagdata = $this->assign_relationship_data($this->tagdata);
  939. }
  940. // LEGACY CODE
  941. // Fetch the variables for this particular tag
  942. // Hopefully, with Jones' new parsing code we should be able to stop using the
  943. // assign_variables and assign_conditional_variables() methods entirely. -Paul
  944. $vars = $this->EE->functions->assign_variables($this->tag_data[$i]['block']);
  945. if (count($this->related_markers) > 0)
  946. {
  947. foreach ($this->related_markers as $mkr)
  948. {
  949. if ( ! isset($vars['var_single'][$mkr]))
  950. {
  951. $vars['var_single'][$mkr] = $mkr;
  952. }
  953. }
  954. }
  955. $this->var_single = $vars['var_single'];
  956. $this->var_pair = $vars['var_pair'];
  957. if ($this->related_id != '')
  958. {
  959. $this->var_single[$this->related_id] = $this->related_id;
  960. $this->related_id = '';
  961. }
  962. // Assign the class name and method name
  963. $class_name = ucfirst($this->tag_data[$i]['class']);
  964. $meth_name = $this->tag_data[$i]['method'];
  965. // If it's a third party class or a first party module,
  966. // add the root folder to the loader paths so we can use
  967. // libraries, models, and helpers
  968. $package_path = '';
  969. if ( ! in_array($this->tag_data[$i]['class'], $this->EE->core->native_plugins))
  970. {
  971. $package_path = in_array($this->tag_data[$i]['class'], $this->EE->core->native_modules) ? PATH_MOD : PATH_THIRD;
  972. $package_path .= strtolower($this->tag_data[$i]['class'].'/');
  973. $this->EE->load->add_package_path($package_path, FALSE);
  974. }
  975. // Dynamically instantiate the class.
  976. // If module, only if it is installed...
  977. if (in_array($this->tag_data[$i]['class'], $this->modules) && ! isset($this->module_data[$class_name]))
  978. {
  979. $this->log_item("Problem Processing Module: Module Not Installed");
  980. }
  981. else
  982. {
  983. $this->log_item(" -> Class Called: ".$class_name);
  984. $EE = new $class_name();
  985. }
  986. // This gives proper PHP5 __construct() support in
  987. // plugins and modules with only a single __construct()
  988. // and allows them to be named __construct() instead of a
  989. // PHP4-style contructor.
  990. if ($meth_name === FALSE && isset($EE))
  991. {
  992. if (method_exists($EE, $class_name))
  993. {
  994. $meth_name = $class_name;
  995. }
  996. elseif (method_exists($EE, '__construct'))
  997. {
  998. $meth_name = '__construct';
  999. }
  1000. }
  1001. // Does method exist? Is This A Module and Is It Installed?
  1002. if ((in_array($this->tag_data[$i]['class'], $this->modules) &&
  1003. ! isset($this->module_data[$class_name])) OR
  1004. ! is_callable(array($EE, $meth_name)))
  1005. {
  1006. $this->log_item("Tag Not Processed: Method Inexistent or Module Not Installed");
  1007. if ($this->EE->config->item('debug') >= 1)
  1008. {
  1009. if (isset($this->tag_data[$i]['tagparts'][1]) && $this->tag_data[$i]['tagparts'][0] == $this->tag_data[$i]['tagparts'][1] &&
  1010. ! isset($this->tag_data[$i]['tagparts'][2]))
  1011. {
  1012. unset($this->tag_data[$i]['tagparts'][1]);
  1013. }
  1014. $error = $this->EE->lang->line('error_tag_module_processing');
  1015. $error .= '<br /><br />';
  1016. $error .= htmlspecialchars(LD);
  1017. $error .= 'exp:'.implode(':', $this->tag_data[$i]['tagparts']);
  1018. $error .= htmlspecialchars(RD);
  1019. $error .= '<br /><br />';
  1020. $error .= str_replace('%x', $this->tag_data[$i]['class'], str_replace('%y', $meth_name, $this->EE->lang->line('error_fix_module_processing')));
  1021. $this->EE->output->fatal_error($error);
  1022. }
  1023. else
  1024. {
  1025. return;
  1026. }
  1027. }
  1028. /*
  1029. OK, lets grab the data returned from the class.
  1030. First, however, lets determine if the tag has one or two segments.
  1031. If it only has one, we don't want to call the constructor again since
  1032. it was already called during instantiation.
  1033. Note: If it only has one segment, only the object constructor will be called.
  1034. Since constructors can't return a value just by initialializing the object
  1035. the output of the class must be assigned to a variable called $this->return_data
  1036. */
  1037. $this->log_item(" -> Method Called: ".$meth_name);
  1038. if ((strtolower($class_name) == strtolower($meth_name)) OR ($meth_name == '__construct'))
  1039. {
  1040. $return_data = (isset($EE->return_data)) ? $EE->return_data : '';
  1041. }
  1042. else
  1043. {
  1044. $return_data = $EE->$meth_name();
  1045. }
  1046. // if it's a third party add-on or module, remove the temporarily added path for local libraries, models, etc.
  1047. // if a "no results" template is returned, $this->tag_data will be reset inside of the scope
  1048. // of the tag being processed. So let's use the locally scoped variable for the class name
  1049. if ($package_path)
  1050. {
  1051. $this->EE->load->remove_package_path($package_path);
  1052. }
  1053. // 404 Page Triggered, Cease All Processing of Tags From Now On
  1054. if ($this->cease_processing === TRUE)
  1055. {
  1056. return;
  1057. }
  1058. $this->log_item(" -> Data Returned");
  1059. // Write cache file if needed
  1060. if ($this->tag_data[$i]['cache'] == 'EXPIRED')
  1061. {
  1062. $this->write_cache_file($this->tag_data[$i]['cfile'], $return_data);
  1063. }
  1064. // Replace the temporary markers we added earlier with the fully parsed data
  1065. $this->template = str_replace('M'.$i.$this->marker, $return_data, $this->template);
  1066. // Initialize data in case there are susequent loops
  1067. $this->var_single = array();
  1068. $this->var_cond = array();
  1069. $this->var_pair = array();
  1070. unset($return_data);
  1071. unset($class_name);
  1072. unset($meth_name);
  1073. unset($EE);
  1074. }
  1075. }
  1076. }
  1077. // --------------------------------------------------------------------
  1078. /**
  1079. * Process Tags
  1080. *
  1081. * Channel entries can have related entries embedded within them.
  1082. * We'll extract the related tag data, stash it away in an array, and
  1083. * replace it with a marker string so that the template parser
  1084. * doesn't see it. In the channel class we'll check to see if the
  1085. * $this->EE->TMPL->related_data array contains anything. If so, we'll celebrate
  1086. * wildly.
  1087. *
  1088. * @param string
  1089. * @return string
  1090. */
  1091. public function assign_relationship_data($chunk)
  1092. {
  1093. $this->related_markers = array();
  1094. if (preg_match_all("/".LD."related_entries\s+id\s*=\s*[\"\'](.+?)[\"\']".RD."(.+?)".LD.'\/'."related_entries".RD."/is", $chunk, $matches))
  1095. {
  1096. $this->log_item("Assigning Related Entry Data");
  1097. $no_rel_content = '';
  1098. for ($j = 0; $j < count($matches[0]); $j++)
  1099. {
  1100. $rand = $this->EE->functions->random('alnum', 8);
  1101. $marker = LD.'REL['.$matches[1][$j].']'.$rand.'REL'.RD;
  1102. if (preg_match("/".LD."if no_related_entries".RD."(.*?)".LD.'\/'."if".RD."/s", $matches[2][$j], $no_rel_match))
  1103. {
  1104. // Match the entirety of the conditional
  1105. if (stristr($no_rel_match[1], LD.'if'))
  1106. {
  1107. $match[0] = $this->EE->functions->full_tag($no_rel_match[0], $matches[2][$j], LD.'if', LD.'\/'."if".RD);
  1108. }
  1109. $no_rel_content = substr($no_rel_match[0], strlen(LD."if no_related_entries".RD), -strlen(LD.'/'."if".RD));
  1110. }
  1111. $this->related_markers[] = $matches[1][$j];
  1112. $vars = $this->EE->functions->assign_variables($matches[2][$j]);
  1113. $this->related_id = $matches[1][$j];
  1114. $this->related_data[$rand] = array(
  1115. 'marker' => $rand,
  1116. 'field_name' => $matches[1][$j],
  1117. 'tagdata' => $matches[2][$j],
  1118. 'var_single' => $vars['var_single'],
  1119. 'var_pair' => $vars['var_pair'],
  1120. 'var_cond' => $this->EE->functions->assign_conditional_variables($matches[2][$j], '\/', LD, RD),
  1121. 'no_rel_content' => $no_rel_content
  1122. );
  1123. $chunk = str_replace($matches[0][$j], $marker, $chunk);
  1124. }
  1125. }
  1126. if (preg_match_all("/".LD."reverse_related_entries\s*(.*?)".RD."(.+?)".LD.'\/'."reverse_related_entries".RD."/is", $chunk, $matches))
  1127. {
  1128. $this->log_item("Assigning Reverse Related Entry Data");
  1129. for ($j = 0; $j < count($matches[0]); $j++)
  1130. {
  1131. $rand = $this->EE->functions->random('alnum', 8);
  1132. $marker = LD.'REV_REL['.$rand.']REV_REL'.RD;
  1133. $vars = $this->EE->functions->assign_variables($matches[2][$j]);
  1134. $no_rev_content = '';
  1135. if (preg_match("/".LD."if no_reverse_related_entries".RD."(.*?)".LD.'\/'."if".RD."/s", $matches[2][$j], $no_rev_match))
  1136. {
  1137. // Match the entirety of the conditional
  1138. if (stristr($no_rev_match[1], LD.'if'))
  1139. {
  1140. $match[0] = $this->EE->functions->full_tag($no_rev_match[0], $matches[2][$j], LD.'if', LD.'\/'."if".RD);
  1141. }
  1142. $no_rev_content = substr($no_rev_match[0], strlen(LD."if no_reverse_related_entries".RD), -strlen(LD.'/'."if".RD));
  1143. }
  1144. $this->reverse_related_data[$rand] = array(
  1145. 'marker' => $rand,
  1146. 'tagdata' => $matches[2][$j],
  1147. 'var_single' => $vars['var_single'],
  1148. 'var_pair' => $vars['var_pair'],
  1149. 'var_cond' => $this->EE->functions->assign_conditional_variables($matches[2][$j], '\/', LD, RD),
  1150. 'params' => $this->EE->functions->assign_parameters($matches[1][$j]),
  1151. 'no_rev_content' => $no_rev_content
  1152. );
  1153. $chunk = str_replace($matches[0][$j], $marker, $chunk);
  1154. }
  1155. }
  1156. return $chunk;
  1157. }
  1158. // --------------------------------------------------------------------
  1159. /**
  1160. * Fetch Parameter for Tag
  1161. *
  1162. * Used by Modules to fetch a paramter for the tag currently be processed. We also have code
  1163. * in here to convert legacy values like 'y' and 'on' to their more respectable full values.
  1164. * Further, if one assigns the second argument, it will be returned as the value if a
  1165. * parameter of the $which name does not exist for this tag. Handy for default values!
  1166. *
  1167. * @access string
  1168. * @access bool
  1169. * @return string
  1170. */
  1171. public function fetch_param($which, $default = FALSE)
  1172. {
  1173. if ( ! isset($this->tagparams[$which]))
  1174. {
  1175. return $default;
  1176. }
  1177. else
  1178. {
  1179. // Making yes/no tag parameters consistent. No "y/n" or "on/off".
  1180. switch($this->tagparams[$which])
  1181. {
  1182. case 'y' :
  1183. case 'on' :
  1184. return 'yes';
  1185. break;
  1186. case 'n' :
  1187. case 'off' :
  1188. return 'no';
  1189. break;
  1190. default :
  1191. return $this->tagparams[$which];
  1192. break;

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