PageRenderTime 65ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/system/core/core.template.php

https://github.com/danboy/Croissierd
PHP | 3422 lines | 3219 code | 99 blank | 104 comment | 47 complexity | 1df5302c798edffb0e6a4a0cd94a61e7 MD5 | raw file
  1. <?php
  2. /*
  3. =====================================================
  4. ExpressionEngine - by EllisLab
  5. -----------------------------------------------------
  6. http://expressionengine.com/
  7. -----------------------------------------------------
  8. Copyright (c) 2003 - 2010 EllisLab, Inc.
  9. =====================================================
  10. THIS IS COPYRIGHTED SOFTWARE
  11. PLEASE READ THE LICENSE AGREEMENT
  12. http://expressionengine.com/docs/license.html
  13. =====================================================
  14. File: core.template.php
  15. -----------------------------------------------------
  16. Purpose: Template parsing class.
  17. =====================================================
  18. */
  19. if ( ! defined('EXT'))
  20. {
  21. exit('Invalid file request');
  22. }
  23. class 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 $cache_timestamp = '';
  33. var $template_type = ''; // Type of template (webpage, rss)
  34. var $embed_type = ''; // Type of template for embedded template
  35. var $template_hits = 0;
  36. var $php_parse_location = 'output'; // Where in the chain the PHP gets parsed
  37. var $template_edit_date = ''; // Template edit date
  38. var $encode_email = TRUE; // Whether to use the email encoder. This is set automatically
  39. var $hit_lock_override = FALSE; // Set to TRUE if you want hits tracked on sub-templates
  40. var $hit_lock = FALSE; // Lets us lock the hit counter if sub-templates are contained in a template
  41. var $parse_php = FALSE; // Whether to parse PHP or not
  42. var $protect_javascript = TRUE; // Protect javascript in conditionals
  43. var $templates_sofar = ''; // Templates processed so far, subtemplate tracker
  44. var $tag_data = array(); // Data contained in tags
  45. var $modules = array(); // List of installed modules
  46. var $module_data = array(); // Data for modules from exp_weblogs
  47. var $plugins = array(); // List of installed plug-ins
  48. var $native_modules = array(); // List of native modules with EE
  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(); // Tag parameters that begin with 'search:'
  61. var $related_data = array(); // A multi-dimensional array containing any related tags
  62. var $related_id = ''; // Used temporarily for the related ID number
  63. var $related_markers = array(); // Used temporarily
  64. var $site_ids = array(); // Site IDs for the Sites Request for a Tag
  65. var $sites = array(); // Array of sites with site_id as key and site_name as value, used to determine site_ids for tag, above.
  66. var $site_prefs_cache = array(); // Array of cached site prefs, to allow fetching of another site's template files
  67. var $reverse_related_data = array(); // A multi-dimensional array containing any reverse related tags
  68. var $t_cache_path = 'tag_cache/'; // Location of the tag cache file
  69. var $p_cache_path = 'page_cache/'; // Location of the page cache file
  70. var $disable_caching = FALSE;
  71. var $debugging = FALSE; // Template parser debugging on?
  72. var $cease_processing = FALSE; // Used with no_results() method.
  73. var $log = array(); // Log of Template processing
  74. var $start_microtime = 0; // For Logging (= microtime())
  75. var $strict_urls = FALSE; // Whether to make URLs operate strictly or not. This is set via a template global pref
  76. var $realm = 'ExpressionEngine Template'; // Localize?
  77. var $marker = '0o93H7pQ09L8X1t49cHY01Z5j4TT91fGfr'; // Temporary marker used as a place-holder for template data
  78. /** -------------------------------------
  79. /** Constructor
  80. /** -------------------------------------*/
  81. function Template()
  82. {
  83. global $IN, $PREFS;
  84. $this->native_modules = array('blacklist', 'comment', 'email', 'forum',
  85. 'gallery', 'mailinglist', 'member', 'query',
  86. 'referrer', 'rss', 'search', 'stats',
  87. 'trackback', 'updated_sites', 'weblog',
  88. 'simple_commerce', 'commerce');
  89. $this->global_vars = $IN->global_vars;
  90. if ($PREFS->ini('multiple_sites_enabled') != 'y')
  91. {
  92. $this->sites[$PREFS->ini('site_id')] = $PREFS->ini('site_short_name');
  93. }
  94. if ($PREFS->ini('template_debugging') === 'y' && $this->start_microtime == 0)
  95. {
  96. $this->debugging = TRUE;
  97. if (phpversion() < 5)
  98. {
  99. list($usec, $sec) = explode(" ", microtime());
  100. $this->start_microtime = ((float)$usec + (float)$sec);
  101. }
  102. else
  103. {
  104. $this->start_microtime = microtime(TRUE);
  105. }
  106. }
  107. }
  108. /* END */
  109. /** -------------------------------------
  110. /** Run the template engine
  111. /** -------------------------------------*/
  112. function run_template_engine($template_group = '', $template = '')
  113. {
  114. global $OUT, $IN, $FNS, $PREFS;
  115. $this->log_item(" - Begin Template Processing - ");
  116. // Set the name of the cache folder for both tag and page caching
  117. if ($IN->URI != '')
  118. {
  119. $this->t_cache_path .= md5($FNS->fetch_site_index().$IN->URI).'/';
  120. $this->p_cache_path .= md5($FNS->fetch_site_index().$IN->URI).'/';
  121. }
  122. else
  123. {
  124. $this->t_cache_path .= md5($PREFS->ini('site_url').'index'.$IN->QSTR).'/';
  125. $this->p_cache_path .= md5($PREFS->ini('site_url').'index'.$IN->QSTR).'/';
  126. }
  127. // We limit the total number of cache files in order to
  128. // keep some sanity with large sites or ones that get
  129. // hit by over-ambitious crawlers.
  130. if ($this->disable_caching == FALSE)
  131. {
  132. if ($dh = @opendir(PATH_CACHE.'page_cache'))
  133. {
  134. $i = 0;
  135. while (false !== (readdir($dh)))
  136. {
  137. $i++;
  138. }
  139. $max = ( ! $PREFS->ini('max_caches') OR ! is_numeric($PREFS->ini('max_caches')) OR $PREFS->ini('max_caches') > 1000) ? 1000 : $PREFS->ini('max_caches');
  140. if ($i > $max)
  141. {
  142. $FNS->clear_caching('page');
  143. }
  144. }
  145. }
  146. $this->log_item("URI: ".$IN->URI);
  147. $this->log_item("Path.php Template: {$template_group}/{$template}");
  148. $this->process_template($template_group, $template, FALSE);
  149. $this->log_item(" - End Template Processing - ");
  150. $this->log_item("Parse Global Variables");
  151. if ($this->template_type == 'static')
  152. {
  153. $this->final_template = $this->restore_xml_declaration($this->final_template);
  154. }
  155. else
  156. {
  157. $this->final_template = $this->parse_globals($this->final_template);
  158. }
  159. $this->log_item("Template Parsing Finished");
  160. $OUT->out_type = $this->template_type;
  161. $OUT->build_queue($this->final_template);
  162. }
  163. /* END */
  164. /** -------------------------------------
  165. /** Process Template
  166. /** -------------------------------------*/
  167. function process_template($template_group = '', $template = '', $sub = FALSE, $site_id = '')
  168. {
  169. global $LOC, $PREFS, $REGX, $LANG, $IN, $FNS;
  170. // add this template to our subtemplate tracker
  171. $this->templates_sofar = $this->templates_sofar.'|'.$site_id.':'.$template_group.'/'.$template.'|';
  172. /** -------------------------------------
  173. /** Fetch the requested template
  174. /** -------------------------------------*/
  175. // The template can either come from the DB or a cache file
  176. // Do not use a reference!
  177. $this->cache_status = 'NO_CACHE';
  178. $this->log_item("Retrieving Template");
  179. $this->template = ($template_group != '' AND $template != '') ? $this->fetch_template($template_group, $template, FALSE, $site_id) : $this->parse_template_uri();
  180. $this->log_item("Template Type: ".$this->template_type);
  181. /** -------------------------------------
  182. /** Static Content, No Parsing
  183. /** -------------------------------------*/
  184. if ($this->template_type == 'static' OR $this->embed_type == 'static')
  185. {
  186. if ($sub == FALSE)
  187. {
  188. $this->final_template = $this->template;
  189. }
  190. return;
  191. }
  192. /* -------------------------------------
  193. /* "Smart" Static Parsing
  194. /*
  195. /* Performed on embedded webpage templates only that do not have
  196. /* ExpressionEngine tags or PHP in them.
  197. /*
  198. /* Hidden Configuration Variable
  199. /* - smart_static_parsing => Bypass parsing of templates that could be
  200. /* of the type 'static' but aren't? (y/n)
  201. /* -------------------------------------*/
  202. if ($PREFS->ini('smart_static_parsing') !== 'n' && $this->embed_type == 'webpage' && ! stristr($this->template, LD) && ! stristr($this->template, '<?'))
  203. {
  204. $this->log_item("Smart Static Parsing Triggered");
  205. if ($sub == FALSE)
  206. {
  207. $this->final_template = $this->template;
  208. }
  209. return;
  210. }
  211. /** -------------------------------------
  212. /** Replace "logged_out" variables
  213. /** -------------------------------------*/
  214. // We do this for backward compatibility
  215. // Note: My plan is to deprecate this, but we need to update
  216. // every template in an installation with the new syntax.
  217. // I would have done it for 1.2 but I added this late, after the
  218. // beta testers already got a copy, so we'll do it in a future update.
  219. $logvars = array('NOT_LOGGED_IN' => 'logged_out', 'not_logged_in' => 'logged_out', 'LOGGED_IN' => 'logged_in');
  220. foreach($logvars as $key => $val)
  221. {
  222. $this->template = str_replace(LD.'if '.$key.RD, LD.'if '.$val.RD, $this->template);
  223. }
  224. /** -------------------------------------
  225. /** Parse URI segments
  226. /** -------------------------------------*/
  227. // This code lets admins fetch URI segments which become
  228. // available as: {segment_1} {segment_2}
  229. for ($i = 1; $i < 10; $i++)
  230. {
  231. $this->template = str_replace(LD.'segment_'.$i.RD, $IN->fetch_uri_segment($i), $this->template);
  232. $this->segment_vars['segment_'.$i] = $IN->fetch_uri_segment($i);
  233. }
  234. /** -------------------------------------
  235. /** Parse {embed} tag variables
  236. /** -------------------------------------*/
  237. if ($sub === TRUE && count($this->embed_vars) > 0)
  238. {
  239. $this->log_item("Embed Variables (Keys): ".implode('|', array_keys($this->embed_vars)));
  240. $this->log_item("Embed Variables (Values): ".trim(implode('|', $this->embed_vars)));
  241. foreach ($this->embed_vars as $key => $val)
  242. {
  243. // add 'embed:' to the key for replacement and so these variables work in conditionals
  244. $this->embed_vars['embed:'.$key] = $val;
  245. unset($this->embed_vars[$key]);
  246. $this->template = str_replace(LD.'embed:'.$key.RD, $val, $this->template);
  247. }
  248. }
  249. // cleanup of leftover/undeclared embed variables
  250. // don't worry with undeclared embed: vars in conditionals as the conditionals processor will handle that adequately
  251. if (strpos($this->template, LD.'embed:') !== FALSE)
  252. {
  253. $this->template = preg_replace('/'.LD.'embed:(.+?)'.RD.'/', '', $this->template);
  254. }
  255. /** --------------------------------------------------
  256. /** Parse 'Site' variables
  257. /** --------------------------------------------------*/
  258. $this->log_item("Parsing Site Variables");
  259. // load site variables into the global_vars array
  260. foreach (array('site_id', 'site_label', 'site_short_name') as $site_var)
  261. {
  262. $this->global_vars[$site_var] = stripslashes($PREFS->ini($site_var));
  263. }
  264. /** -------------------------------------
  265. /** Parse manual variables
  266. /** -------------------------------------*/
  267. // These are variables that can be set in the path.php file
  268. if (count($this->global_vars) > 0)
  269. {
  270. $this->log_item("Global Path.php Variables (Keys): ".implode('|', array_keys($this->global_vars)));
  271. $this->log_item("Global Path.php Variables (Values): ".trim(implode('|', $this->global_vars)));
  272. foreach ($this->global_vars as $key => $val)
  273. {
  274. $this->template = str_replace(LD.$key.RD, $val, $this->template);
  275. }
  276. }
  277. /** -------------------------------------
  278. /** Parse date format string "constants"
  279. /** -------------------------------------*/
  280. $date_constants = array('DATE_ATOM' => '%Y-%m-%dT%H:%i:%s%Q',
  281. 'DATE_COOKIE' => '%l, %d-%M-%y %H:%i:%s UTC',
  282. 'DATE_ISO8601' => '%Y-%m-%dT%H:%i:%s%O',
  283. 'DATE_RFC822' => '%D, %d %M %y %H:%i:%s %O',
  284. 'DATE_RFC850' => '%l, %d-%M-%y %H:%m:%i UTC',
  285. 'DATE_RFC1036' => '%D, %d %M %y %H:%i:%s %O',
  286. 'DATE_RFC1123' => '%D, %d %M %Y %H:%i:%s %O',
  287. 'DATE_RFC2822' => '%D, %d %M %Y %H:%i:%s %O',
  288. 'DATE_RSS' => '%D, %d %M %Y %H:%i:%s %O',
  289. 'DATE_W3C' => '%Y-%m-%dT%H:%i:%s%Q'
  290. );
  291. foreach ($date_constants as $key => $val)
  292. {
  293. $this->template = str_replace(LD.$key.RD, $val, $this->template);
  294. }
  295. $this->log_item("Parse Date Format String Constants");
  296. /** --------------------------------------------------
  297. /** Template's Last Edit time {template_edit_date format="%Y %m %d %H:%i:%s"}
  298. /** --------------------------------------------------*/
  299. if (strpos($this->template, LD.'template_edit_date') !== FALSE && preg_match_all("/".LD."template_edit_date\s+format=([\"\'])([^\\1]*?)\\1".RD."/", $this->template, $matches))
  300. {
  301. for ($j = 0; $j < count($matches['0']); $j++)
  302. {
  303. $this->template = preg_replace("/".$matches['0'][$j]."/", $LOC->decode_date($matches['2'][$j], $this->template_edit_date), $this->template, 1);
  304. }
  305. }
  306. /** --------------------------------------------------
  307. /** Current time {current_time format="%Y %m %d %H:%i:%s"}
  308. /** --------------------------------------------------*/
  309. if (strpos($this->template, LD.'current_time') !== FALSE && preg_match_all("/".LD."current_time\s+format=([\"\'])([^\\1]*?)\\1".RD."/", $this->template, $matches))
  310. {
  311. for ($j = 0; $j < count($matches['0']); $j++)
  312. {
  313. $this->template = preg_replace("/".preg_quote($matches['0'][$j], '/')."/", $LOC->decode_date($matches['2'][$j], $LOC->now), $this->template, 1);
  314. }
  315. }
  316. $this->template = str_replace(LD.'current_time'.RD, $LOC->now, $this->template);
  317. $this->log_item("Parse Current Time Variables");
  318. /** -------------------------------------
  319. /** Is the main template cached?
  320. /** -------------------------------------*/
  321. // If a cache file exists for the primary template
  322. // there is no reason to go further.
  323. // However we do need to fetch any subtemplates
  324. if ($this->cache_status == 'CURRENT' AND $sub == FALSE)
  325. {
  326. $this->log_item("Cached Template Used");
  327. $this->template = $this->parse_nocache($this->template);
  328. /** -------------------------------------
  329. /** Smite Our Enemies: Advanced Conditionals
  330. /** -------------------------------------*/
  331. if (stristr($this->template, LD.'if'))
  332. {
  333. $this->template = $this->advanced_conditionals($this->template);
  334. }
  335. $this->log_item("Conditionals Parsed, Processing Sub Templates");
  336. $this->final_template = $this->template;
  337. $this->process_sub_templates($this->template);
  338. return;
  339. }
  340. // Remove whitespace from variables.
  341. // This helps prevent errors, particularly if PHP is used in a template
  342. $this->template = preg_replace("/".LD."\s*(\S+)\s*".RD."/U", LD."\\1".RD, $this->template);
  343. /** -------------------------------------
  344. /** Parse Input Stage PHP
  345. /** -------------------------------------*/
  346. if ($this->parse_php == TRUE AND $this->php_parse_location == 'input' AND $this->cache_status != 'CURRENT')
  347. {
  348. $this->log_item("Parsing PHP on Input");
  349. $this->template = $this->parse_template_php($this->template);
  350. }
  351. /** -------------------------------------
  352. /** Smite Our Enemies: Conditionals
  353. /** -------------------------------------*/
  354. $this->log_item("Parsing Segment, Embed, and Global Vars Conditionals");
  355. $this->template = $this->segment_conditionals($this->template);
  356. $this->template = $this->array_conditionals($this->template, $this->embed_vars);
  357. $this->template = $this->array_conditionals($this->template, $this->global_vars);
  358. /** -------------------------------------
  359. /** Set global variable assignment
  360. /** -------------------------------------*/
  361. if (preg_match_all("/".LD."assign_variable:(.+?)=([\"\'])([^\\2]*?)\\2".RD."/i", $this->template, $matches))
  362. {
  363. $this->log_item("Processing Assigned Variables: ".trim(implode('|', $matches['1'])));
  364. for ($j = 0; $j < count($matches['0']); $j++)
  365. {
  366. $this->template = str_replace($matches['0'][$j], "", $this->template);
  367. $this->template = str_replace(LD.$matches['1'][$j].RD, $matches['3'][$j], $this->template);
  368. }
  369. }
  370. /** -------------------------------------
  371. /** Process the template
  372. /** -------------------------------------*/
  373. // Replace forward slashes with entity to prevent preg_replace errors.
  374. $this->template = str_replace('/', SLASH, $this->template);
  375. // Fetch installed modules and plugins if needed
  376. if (count($this->modules) == 0)
  377. {
  378. $this->fetch_modules();
  379. }
  380. if (count($this->plugins) == 0)
  381. {
  382. $this->fetch_plugins();
  383. }
  384. // Parse the template.
  385. $this->log_item(" - Beginning Tag Processing - ");
  386. while (is_int(strpos($this->template, LD.'exp:')))
  387. {
  388. // Initialize values between loops
  389. $this->tag_data = array();
  390. $this->var_single = array();
  391. $this->var_cond = array();
  392. $this->var_pair = array();
  393. $this->loop_count = 0;
  394. $this->log_item("Parsing Tags in Template");
  395. // Run the template parser
  396. $this->parse_template();
  397. $this->log_item("Processing Tags");
  398. // Run the class/method handler
  399. $this->class_handler();
  400. if ($this->cease_processing === TRUE)
  401. {
  402. return;
  403. }
  404. }
  405. $this->log_item(" - End Tag Processing - ");
  406. // Decode forward slash entities back to ascii
  407. $this->template = str_replace(SLASH, '/', $this->template);
  408. /** -------------------------------------
  409. /** Parse Output Stage PHP
  410. /** -------------------------------------*/
  411. if ($this->parse_php == TRUE AND $this->php_parse_location == 'output' AND $this->cache_status != 'CURRENT')
  412. {
  413. $this->log_item("Parsing PHP on Output");
  414. $this->template = $this->parse_template_php($this->template);
  415. }
  416. /** -------------------------------------
  417. /** Write the cache file if needed
  418. /** -------------------------------------*/
  419. if ($this->cache_status == 'EXPIRED')
  420. {
  421. $this->template = $FNS->insert_action_ids($this->template);
  422. $this->write_cache_file($this->cache_hash, $this->template, 'template');
  423. }
  424. /** -------------------------------------
  425. /** Parse Our Uncacheable Forms
  426. /** -------------------------------------*/
  427. $this->template = $this->parse_nocache($this->template);
  428. /** -------------------------------------
  429. /** Smite Our Enemies: Advanced Conditionals
  430. /** -------------------------------------*/
  431. if (stristr($this->template, LD.'if'))
  432. {
  433. $this->log_item("Processing Advanced Conditionals");
  434. $this->template = $this->advanced_conditionals($this->template);
  435. }
  436. // <?php This fixes a BBEdit bug that makes the list of function not work right. Seems related to the PHP declarations above.
  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. /* END */
  450. /** -------------------------------------
  451. /** Parse embedded sub-templates
  452. /** -------------------------------------*/
  453. function process_sub_templates($template)
  454. {
  455. global $REGX, $FNS, $LANG, $PREFS, $DB;
  456. /** -------------------------------------
  457. /** Match all {embed=bla/bla} tags
  458. /** -------------------------------------*/
  459. $matches = array();
  460. if ( ! preg_match_all("/(".LD."embed\s*=)(.*?)".RD."/s", $template, $matches))
  461. {
  462. return;
  463. }
  464. /** -------------------------------------
  465. /** Loop until we have parsed all sub-templates
  466. /** -------------------------------------*/
  467. // For each embedded tag we encounter we'll run the template parsing
  468. // function - AND - through the beauty of recursive functions we
  469. // will also call THIS function as well, allowing us to parse
  470. // infinitely nested sub-templates in one giant loop o' love
  471. $this->log_item(" - Processing Sub Templates (Depth: ".($this->depth+1).") - ");
  472. $i = 0;
  473. $this->depth++;
  474. $this->log_item("List of Embeds: ".str_replace(array('"', "'"), '', trim(implode(',', $matches['2']))));
  475. // re-match the full tag of each if necessary before we start processing
  476. // necessary evil in case template globals are used inside the embed tag,
  477. // doing this within the processing loop will result in leaving unparsed
  478. // embed tags e.g. {embed="foo/bar" var="{global_var}/{custom_field}"}
  479. $temp = $template;
  480. foreach ($matches[2] as $key => $val)
  481. {
  482. if (strpos($val, LD) !== FALSE)
  483. {
  484. $matches[0][$key] = $FNS->full_tag($matches[0][$key], $temp);
  485. $matches[2][$key] = substr(str_replace($matches[1][$key], '', $matches[0][$key]), 0, -1);
  486. $temp = str_replace($matches[0][$key], '', $temp);
  487. }
  488. }
  489. foreach($matches['2'] as $key => $val)
  490. {
  491. $parts = preg_split("/\s+/", $val, 2);
  492. $this->embed_vars = (isset($parts['1'])) ? $FNS->assign_parameters($parts['1']) : array();
  493. if ($this->embed_vars === FALSE)
  494. {
  495. $this->embed_vars = array();
  496. }
  497. $val = $REGX->trim_slashes($REGX->strip_quotes($parts['0']));
  498. if ( ! stristr($val, '/'))
  499. {
  500. continue;
  501. }
  502. $ex = explode("/", trim($val));
  503. if (count($ex) != 2)
  504. {
  505. continue;
  506. }
  507. /** ----------------------------------
  508. /** Determine Site
  509. /** ----------------------------------*/
  510. $site_id = $PREFS->ini('site_id');
  511. if (stristr($ex[0], ':'))
  512. {
  513. $name = substr($ex[0], 0, strpos($ex[0], ':'));
  514. if ($PREFS->ini('multiple_sites_enabled') == 'y')
  515. {
  516. if (sizeof($this->sites) == 0)
  517. {
  518. $sites_query = $DB->query("SELECT site_id, site_name FROM exp_sites");
  519. foreach($sites_query->result as $row)
  520. {
  521. $this->sites[$row['site_id']] = $row['site_name'];
  522. }
  523. }
  524. $site_id = array_search($name, $this->sites);
  525. if (empty($site_id))
  526. {
  527. $site_id = $PREFS->ini('site_id');
  528. }
  529. }
  530. $ex[0] = str_replace($name.':', '', $ex[0]);
  531. }
  532. /** ----------------------------------
  533. /** Loop Prevention
  534. /** ----------------------------------*/
  535. /* -------------------------------------------
  536. /* Hidden Configuration Variable
  537. /* - template_loop_prevention => 'n'
  538. Whether or not loop prevention is enabled - y/n
  539. /* -------------------------------------------*/
  540. if (substr_count($this->templates_sofar, '|'.$site_id.':'.$ex['0'].'/'.$ex['1'].'|') > 1 && $PREFS->ini('template_loop_prevention') != 'n')
  541. {
  542. $this->final_template = ($PREFS->ini('debug') >= 1) ? str_replace('%s', $ex['0'].'/'.$ex['1'], $LANG->line('template_loop')) : "";
  543. return;
  544. }
  545. /** ----------------------------------
  546. /** Process Subtemplate
  547. /** ----------------------------------*/
  548. $this->log_item("Processing Sub Template: ".$ex['0']."/".$ex['1']);
  549. $this->process_template($ex['0'], $ex['1'], TRUE, $site_id);
  550. $this->final_template = str_replace($matches['0'][$key], $this->template, $this->final_template);
  551. $this->embed_type = '';
  552. // Here we go again! Wheeeeeee.....
  553. $this->process_sub_templates($this->template);
  554. // pull the subtemplate tracker back a level to the parent template
  555. $this->templates_sofar = substr($this->templates_sofar, 0, - strlen('|'.$site_id.':'.$ex[0].'/'.$ex[1].'|'));
  556. }
  557. $this->depth--;
  558. if ($this->depth == 0)
  559. {
  560. $this->templates_sofar = '';
  561. }
  562. }
  563. /* END */
  564. /** -------------------------------------
  565. /** Parse the template
  566. /** -------------------------------------*/
  567. function parse_template()
  568. {
  569. global $FNS;
  570. while (TRUE)
  571. {
  572. // Make a "floating" copy of the template which we'll progressively slice into pieces with each loop
  573. $this->fl_tmpl = $this->template;
  574. // Identify the string position of the first occurence of a matched tag
  575. $this->in_point = strpos($this->fl_tmpl, LD.'exp:');
  576. // If the above variable returns false we are done looking for tags
  577. // This single conditional keeps the template engine from spiraling
  578. // out of control in an infinite loop.
  579. if (FALSE === $this->in_point)
  580. {
  581. break;
  582. }
  583. else
  584. {
  585. /** ------------------------------------------
  586. /** Process the tag data
  587. /** ------------------------------------------*/
  588. // These REGEXs parse out the various components contained in any given tag.
  589. // Grab the opening portion of the tag: {exp:some:tag param="value" param="value"}
  590. if ( ! preg_match("/".LD.'exp:'.".*?".RD."/s", $this->fl_tmpl, $matches))
  591. {
  592. $this->template = preg_replace("/".LD.'exp:'.".*?$/", '', $this->template);
  593. break;
  594. }
  595. $this->log_item("Tag: ".$matches['0']);
  596. // Checking for variables/tags embedded within tags
  597. // {exp:weblog:entries weblog="{master_weblog_name}"}
  598. if (stristr(substr($matches['0'], 1), LD) !== false)
  599. {
  600. $matches['0'] = $FNS->full_tag($matches['0']);
  601. }
  602. $raw_tag = preg_replace("/(\r\n)|(\r)|(\n)|(\t)/", ' ', $matches['0']);
  603. $tag_length = strlen($raw_tag);
  604. $data_start = $this->in_point + $tag_length;
  605. $tag = trim(substr($raw_tag, 1, -1));
  606. $args = trim((preg_match("/\s+.*/", $tag, $matches))) ? $matches['0'] : '';
  607. $tag = trim(str_replace($args, '', $tag));
  608. $cur_tag_close = LD.SLASH.$tag.RD;
  609. // -----------------------------------------
  610. // Assign the class name/method name and any parameters
  611. $class = $this->assign_class(substr($tag, strlen('exp') + 1));
  612. $args = $FNS->assign_parameters($args);
  613. // standardized mechanism for "search" type parameters get some extra lovin'
  614. $search_fields = array();
  615. if ($args !== FALSE)
  616. {
  617. foreach ($args as $key => $val)
  618. {
  619. if (strncmp($key, 'search:', 7) == 0)
  620. {
  621. $search_fields[substr($key, 7)] = str_replace(SLASH, '/', $val);
  622. }
  623. }
  624. }
  625. // Trim the floating template, removing the tag we just parsed.
  626. $this->fl_tmpl = substr($this->fl_tmpl, $this->in_point + $tag_length);
  627. $out_point = strpos($this->fl_tmpl, $cur_tag_close);
  628. // Do we have a tag pair?
  629. if (FALSE !== $out_point)
  630. {
  631. // Assign the data contained between the opening/closing tag pair
  632. $this->log_item("Closing Tag Found");
  633. $block = substr($this->template, $data_start, $out_point);
  634. // Fetch the "no_results" data
  635. $no_results = '';
  636. $no_results_block = '';
  637. if (preg_match("/".LD."if no_results".RD."(.*?)".LD.SLASH."if".RD."/s", $block, $match))
  638. {
  639. // Match the entirety of the conditional, dude. Bad Rick!
  640. if (stristr($match['1'], LD.'if'))
  641. {
  642. $match['0'] = $FNS->full_tag($match['0'], $block, LD.'if', LD.SLASH."if".RD);
  643. }
  644. $no_results = substr($match['0'], strlen(LD."if no_results".RD), -strlen(LD.SLASH."if".RD));
  645. $no_results_block = $match['0'];
  646. }
  647. // Define the entire "chunk" - from the left edge of the opening tag
  648. // to the right edge of closing tag.
  649. $out_point = $out_point + $tag_length + strlen($cur_tag_close);
  650. $chunk = substr($this->template, $this->in_point, $out_point);
  651. }
  652. else
  653. {
  654. // Single tag...
  655. $this->log_item("No Closing Tag");
  656. $block = ''; // Single tags don't contain data blocks
  657. $no_results = '';
  658. $no_results_block = '';
  659. // Define the entire opening tag as a "chunk"
  660. $chunk = substr($this->template, $this->in_point, $tag_length);
  661. }
  662. // Strip the "chunk" from the template, replacing it with a unique marker.
  663. if (stristr($raw_tag, 'random'))
  664. {
  665. $this->template = preg_replace("|".preg_quote($chunk)."|s", 'M'.$this->loop_count.$this->marker, $this->template, 1);
  666. }
  667. else
  668. {
  669. $this->template = str_replace($chunk, 'M'.$this->loop_count.$this->marker, $this->template);
  670. }
  671. $cfile = md5($chunk); // This becomes the name of the cache file
  672. // Build a multi-dimensional array containing all of the tag data we've assembled
  673. $this->tag_data[$this->loop_count]['tag'] = $raw_tag;
  674. $this->tag_data[$this->loop_count]['class'] = $class['0'];
  675. $this->tag_data[$this->loop_count]['method'] = $class['1'];
  676. $this->tag_data[$this->loop_count]['tagparts'] = $class;
  677. $this->tag_data[$this->loop_count]['params'] = $args;
  678. $this->tag_data[$this->loop_count]['chunk'] = $chunk; // Matched data block - including opening/closing tags
  679. $this->tag_data[$this->loop_count]['block'] = $block; // Matched data block - no tags
  680. $this->tag_data[$this->loop_count]['cache'] = $this->cache_status($cfile, $args);
  681. $this->tag_data[$this->loop_count]['cfile'] = $cfile;
  682. $this->tag_data[$this->loop_count]['no_results'] = $no_results;
  683. $this->tag_data[$this->loop_count]['no_results_block'] = $no_results_block;
  684. $this->tag_data[$this->loop_count]['search_fields'] = $search_fields;
  685. } // END IF
  686. // Increment counter
  687. $this->loop_count++;
  688. } // END WHILE
  689. }
  690. /* END */
  691. /** -------------------------------------
  692. /** Class/Method handler
  693. /** -------------------------------------*/
  694. function class_handler()
  695. {
  696. global $FNS, $TMPL, $DB, $PREFS;
  697. $classes = array();
  698. // Fill an array with the names of all the classes that we previously extracted from the tags
  699. for ($i = 0; $i < count($this->tag_data); $i++)
  700. {
  701. // Should we use the tag cache file?
  702. if ($this->tag_data[$i]['cache'] == 'CURRENT')
  703. {
  704. // If so, replace the marker in the tag with the cache data
  705. $this->log_item("Tag Cached and Cache is Current");
  706. $this->replace_marker($i, $this->get_cache_file($this->tag_data[$i]['cfile']));
  707. }
  708. else
  709. {
  710. // Is a module or plug-in being requested?
  711. if ( ! in_array($this->tag_data[$i]['class'] , $this->modules))
  712. {
  713. if ( ! in_array($this->tag_data[$i]['class'] , $this->plugins))
  714. {
  715. global $LANG, $PREFS, $OUT;
  716. $this->log_item("Invalid Tag");
  717. if ($PREFS->ini('debug') >= 1)
  718. {
  719. if ($this->tag_data[$i]['tagparts']['0'] == $this->tag_data[$i]['tagparts']['1'] &&
  720. ! isset($this->tag_data[$i]['tagparts']['2']))
  721. {
  722. unset($this->tag_data[$i]['tagparts']['1']);
  723. }
  724. $error = $LANG->line('error_tag_syntax');
  725. $error .= '<br /><br />';
  726. $error .= htmlspecialchars(LD);
  727. $error .= 'exp:'.implode(':', $this->tag_data[$i]['tagparts']);
  728. $error .= htmlspecialchars(RD);
  729. $error .= '<br /><br />';
  730. $error .= $LANG->line('error_fix_syntax');
  731. $OUT->fatal_error($error);
  732. }
  733. else
  734. return false;
  735. }
  736. else
  737. {
  738. $classes[] = 'pi.'.$this->tag_data[$i]['class'];
  739. $this->log_item("Plugin Tag: ".ucfirst($this->tag_data[$i]['class']).'/'.$this->tag_data[$i]['method']);
  740. }
  741. }
  742. else
  743. {
  744. $classes[] = $this->tag_data[$i]['class'];
  745. $this->log_item("Module Tag: ".ucfirst($this->tag_data[$i]['class']).'/'.$this->tag_data[$i]['method']);
  746. }
  747. }
  748. }
  749. // Remove duplicate class names and re-order the array
  750. $classes = array_values(array_unique($classes));
  751. // Dynamically require the file that contains each class
  752. $this->log_item("Including Files for Tag and Modules");
  753. for ($i = 0; $i < count($classes); $i++)
  754. {
  755. // But before we do, make sure it hasn't already been included...
  756. if ( ! class_exists($classes[$i]))
  757. {
  758. if (substr($classes[$i], 0, 3) == 'pi.')
  759. {
  760. require_once PATH_PI.$classes[$i].EXT;
  761. }
  762. else
  763. {
  764. require_once PATH_MOD.$classes[$i].'/mod.'.$classes[$i].EXT;
  765. }
  766. }
  767. }
  768. /** -----------------------------------
  769. /** Only Retrieve Data if Not Done Before and Modules Being Called
  770. /** -----------------------------------*/
  771. if (sizeof($this->module_data) == 0 && sizeof(array_intersect($this->modules, $classes)) > 0)
  772. {
  773. $query = $DB->query("SELECT module_version, module_name FROM exp_modules");
  774. foreach($query->result as $row)
  775. {
  776. $this->module_data[$row['module_name']] = array('version' => $row['module_version']);
  777. }
  778. }
  779. // Final data processing
  780. // Loop through the master array containing our extracted template data
  781. $this->log_item("Beginning Final Tag Data Processing");
  782. reset($this->tag_data);
  783. for ($i = 0; $i < count($this->tag_data); $i++)
  784. {
  785. if ($this->tag_data[$i]['cache'] != 'CURRENT')
  786. {
  787. $this->log_item("Calling Class/Method: ".ucfirst($this->tag_data[$i]['class'])."/".$this->tag_data[$i]['method']);
  788. /* ---------------------------------
  789. /* Plugin as Parameter
  790. /*
  791. /* - Example: weblog="{exp:some_plugin}"
  792. /* - A bit of a hidden feature. Has been tested but not quite
  793. /* ready to say it is ready for prime time as I might want to
  794. /* move it to earlier in processing so that if there are
  795. /* multiple plugins being used as parameters it is only called
  796. /* once instead of for every single parameter. - Paul
  797. /* ---------------------------------*/
  798. 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')
  799. {
  800. foreach($this->tag_data[$i]['params'] as $name => $param)
  801. {
  802. if (stristr($this->tag_data[$i]['params'][$name], LD.'exp'))
  803. {
  804. $this->log_item("Plugin in Parameter, Processing Plugin First");
  805. $TMPL2 = $FNS->clone_object($this);
  806. while (is_int(strpos($TMPL2->tag_data[$i]['params'][$name], LD.'exp:')))
  807. {
  808. $TMPL = new Template();
  809. $TMPL->start_microtime = $this->start_microtime;
  810. $TMPL->template = $TMPL2->tag_data[$i]['params'][$name];
  811. $TMPL->tag_data = array();
  812. $TMPL->var_single = array();
  813. $TMPL->var_cond = array();
  814. $TMPL->var_pair = array();
  815. $TMPL->plugins = $TMPL2->plugins;
  816. $TMPL->modules = $TMPL2->modules;
  817. $TMPL->parse_template();
  818. $TMPL->class_handler();
  819. $TMPL->loop_count = 0;
  820. $TMPL2->tag_data[$i]['params'][$name] = $TMPL->template;
  821. $TMPL2->log = array_merge($TMPL2->log, $TMPL->log);
  822. }
  823. foreach (get_object_vars($TMPL2) as $key => $value)
  824. {
  825. $this->$key = $value;
  826. }
  827. unset($TMPL2);
  828. $TMPL = $this;
  829. }
  830. }
  831. }
  832. /** ---------------------------------
  833. /** Nested Plugins...
  834. /** ---------------------------------*/
  835. if (in_array($this->tag_data[$i]['class'] , $this->plugins) && strpos($this->tag_data[$i]['block'], LD.'exp:') !== false)
  836. {
  837. if ( ! isset($this->tag_data[$i]['params']['parse']) OR $this->tag_data[$i]['params']['parse'] != 'inward')
  838. {
  839. $this->log_item("Nested Plugins in Tag, Parsing Outward First");
  840. $TMPL2 = $FNS->clone_object($this);
  841. while (is_int(strpos($TMPL2->tag_data[$i]['block'], LD.'exp:')))
  842. {
  843. $TMPL = new Template();
  844. $TMPL->start_microtime = $this->start_microtime;
  845. $TMPL->template = $TMPL2->tag_data[$i]['block'];
  846. $TMPL->tag_data = array();
  847. $TMPL->var_single = array();
  848. $TMPL->var_cond = array();
  849. $TMPL->var_pair = array();
  850. $TMPL->plugins = $TMPL2->plugins;
  851. $TMPL->modules = $TMPL2->modules;
  852. $TMPL->parse_template();
  853. $TMPL->class_handler();
  854. $TMPL->loop_count = 0;
  855. $TMPL2->tag_data[$i]['block'] = $TMPL->template;
  856. $TMPL2->log = array_merge($TMPL2->log, $TMPL->log);
  857. }
  858. foreach (get_object_vars($TMPL2) as $key => $value)
  859. {
  860. $this->$key = $value;
  861. }
  862. unset($TMPL2);
  863. $TMPL = $this;
  864. }
  865. }
  866. // Assign the data chunk, parameters
  867. // We moved the no_results_block here because of nested tags. The first
  868. // parsed tag has priority for that conditional.
  869. $this->tagdata = str_replace($this->tag_data[$i]['no_results_block'], '', $this->tag_data[$i]['block']);
  870. $this->tagparams = $this->tag_data[$i]['params'];
  871. $this->tagchunk = $this->tag_data[$i]['chunk'];
  872. $this->tagproper = $this->tag_data[$i]['tag'];
  873. $this->tagparts = $this->tag_data[$i]['tagparts'];
  874. $this->no_results = $this->tag_data[$i]['no_results'];
  875. $this->search_fields = $this->tag_data[$i]['search_fields'];
  876. /** -------------------------------------
  877. /** Assign Sites for Tag
  878. /** -------------------------------------*/
  879. $this->_fetch_site_ids();
  880. /** -------------------------------------
  881. /** Relationship Data Pulled Out
  882. /** -------------------------------------*/
  883. // If the weblog:entries tag or search:search_results is being called
  884. // we need to extract any relationship data that might be present.
  885. // Note: This needs to happen before extracting the variables
  886. // in the tag so it doesn't get confused as to which entry the
  887. // variables belong to.
  888. if (($this->tag_data[$i]['class'] == 'weblog' AND $this->tag_data[$i]['method'] == 'entries')
  889. OR ($this->tag_data[$i]['class'] == 'search' AND $this->tag_data[$i]['method'] == 'search_results'))
  890. {
  891. $this->tagdata = $this->assign_relationship_data($this->tagdata);
  892. }
  893. // Fetch the variables for this particular tag
  894. $vars = $FNS->assign_variables($this->tag_data[$i]['block']);
  895. if (count($this->related_markers) > 0)
  896. {
  897. foreach ($this->related_markers as $mkr)
  898. {
  899. if ( ! isset($vars['var_single'][$mkr]))
  900. {
  901. $vars['var_single'][$mkr] = $mkr;
  902. }
  903. }
  904. $this->related_markers = array();
  905. }
  906. $this->var_single = $vars['var_single'];
  907. $this->var_pair = $vars['var_pair'];
  908. // Redundant see above loop for related_markers - R.S.
  909. //if ($this->related_id != '')
  910. //{
  911. // $this->var_single[$this->related_id] = $this->related_id;
  912. // $this->related_id = '';
  913. //}
  914. // Assign Conditional Variables
  915. if ( ! in_array($this->tag_data[$i]['class'],$this->native_modules))
  916. {
  917. $this->var_cond = $FNS->assign_conditional_variables($this->tag_data[$i]['block'], SLASH, LD, RD);
  918. }
  919. // Assign the class name and method name
  920. $class_name = ucfirst($this->tag_data[$i]['class']);
  921. if ($class_name == 'Commerce')
  922. {
  923. // The Commerce module is special in that it has its own modules and its
  924. // constructor handles everything for us
  925. $meth_name = 'commerce';
  926. }
  927. else
  928. {
  929. $meth_name = $this->tag_data[$i]['method'];
  930. }
  931. // Dynamically instantiate the class.
  932. // If module, only if it is installed...
  933. if (in_array($this->tag_data[$i]['class'], $this->modules) && ! isset($this->module_data[$class_name]))
  934. {
  935. $this->log_item("Problem Processing Module: Module Not Installed");
  936. }
  937. else
  938. {
  939. $this->log_item(" -> Class Called: ".$class_name);
  940. $EE = new $class_name();
  941. }
  942. /** ----------------------------------
  943. /** Does method exist? Is This A Module and Is It Installed?
  944. /** ----------------------------------*/
  945. if ((in_array($this->tag_data[$i]['class'], $this->modules) && ! isset($this->module_data[$class_name])) OR ! method_exists($EE, $meth_name))
  946. {
  947. global $LANG, $PREFS, $OUT;
  948. $this->log_item("Tag Not Processed: Method Inexistent or Module Not Installed");
  949. if ($PREFS->ini('debug') >= 1)
  950. {
  951. if ($this->tag_data[$i]['tagparts']['0'] == $this->tag_data[$i]['tagparts']['1'] &&
  952. ! isset($this->tag_data[$i]['tagparts']['2']))
  953. {
  954. unset($this->tag_data[$i]['tagparts']['1']);
  955. }
  956. $error = $LANG->line('error_tag_module_processing');
  957. $error .= '<br /><br />';
  958. $error .= htmlspecialchars(LD);
  959. $error .= 'exp:'.implode(':', $this->tag_data[$i]['tagparts']);
  960. $error .= htmlspecialchars(RD);
  961. $error .= '<br /><br />';
  962. $error .= str_replace('%x', $this->tag_data[$i]['class'], str_replace('%y', $meth_name, $LANG->line('error_fix_module_processing')));
  963. $OUT->fatal_error($error);
  964. }
  965. else
  966. return;
  967. }
  968. /*
  969. OK, lets grab the data returned from the class.
  970. First, however, lets determine if the tag has one or two segments.
  971. If it only has one, we don't want to call the constructor again since
  972. it was already called during instantiation.
  973. Note: If it only has one segment, only the object constructor will be called.
  974. Since constructors can't return a value just by initialializing the object
  975. the output of the class must be assigned to a variable called $this->return_data
  976. */
  977. $this->log_item(" -> Method Called: ".$meth_name);
  978. if (strtolower($class_name) == $meth_name)
  979. {
  980. $return_data = (isset($EE->return_data)) ? $EE->return_data : '';
  981. }
  982. else
  983. {
  984. $return_data = $EE->$meth_name();
  985. }
  986. /** ----------------------------------
  987. /** 404 Page Triggered, Cease All Processing of Tags From Now On
  988. /** ----------------------------------*/
  989. if ($this->cease_processing === TRUE)
  990. {
  991. return;
  992. }
  993. $this->log_item(" -> Data Returned");
  994. // Write cache file if needed
  995. if ($this->tag_data[$i]['cache'] == 'EXPIRED')
  996. {
  997. $this->write_cache_file($this->tag_data[$i]['cfile'], $return_data);
  998. }
  999. // Replace the temporary markers we added earlier with the fully parsed data
  1000. $this->replace_marker($i, $return_data);
  1001. // Initialize data in case there are susequent loops
  1002. $this->var_single = array();
  1003. $this->var_cond = array();
  1004. $this->var_pair = array();
  1005. unset($return_data);
  1006. unset($class_name);
  1007. unset($meth_name);
  1008. unset($EE);
  1009. }
  1010. }
  1011. }
  1012. /* END */
  1013. /** -------------------------------------
  1014. /** Assign the related data
  1015. /** -------------------------------------*/
  1016. // Weblog entries can have related entries embedded within them.
  1017. // We'll extract the related tag data, stash it away in an array, and
  1018. // replace it with a marker string so that the template parser
  1019. // doesn't see it. In the weblog class we'll check to see if the
  1020. // $TMPL->related_data array contains anything. If so, we'll celebrate
  1021. // wildly.
  1022. function assign_relationship_data($chunk)
  1023. {
  1024. global $FNS;
  1025. $this->related_markers = array();
  1026. if (preg_match_all("/".LD."related_entries\s+id\s*=\s*[\"\'](.+?)[\"\']".RD."(.+?)".LD.SLASH."related_entries".RD."/is", $chunk, $matches))
  1027. {
  1028. $this->log_item("Assigning Related Entry Data");
  1029. $no_rel_content = '';
  1030. for ($j = 0; $j < count($matches['0']); $j++)
  1031. {
  1032. $rand = $FNS->random('alpha', 8);
  1033. $marker = LD.'REL['.$matches['1'][$j].']'.$rand.'REL'.RD;
  1034. if (preg_match("/".LD."if no_related_entries".RD."(.*?)".LD.SLASH."if".RD."/s", $matches['2'][$j], $no_rel_match))
  1035. {
  1036. // Match the entirety of the conditional
  1037. if (stristr($no_rel_match['1'], LD.'if'))
  1038. {
  1039. $match['0'] = $FNS->full_tag($no_rel_match['0'], $matches['2'][$j], LD.'if', LD.SLASH."if".RD);
  1040. }
  1041. $no_rel_content = substr($no_rel_match['0'], strlen(LD."if no_related_entries".RD), -strlen(LD.SLASH."if".RD));
  1042. }
  1043. $this->related_markers[] = $matches['1'][$j];
  1044. $vars = $FNS->assign_variables($matches['2'][$j]);
  1045. // Depreciated as redundant. R.S.
  1046. //$this->related_id = $matches['1'][$j];
  1047. $this->related_data[$rand] = array(
  1048. 'marker' => $rand,
  1049. 'field_name' => $matches['1'][$j],
  1050. 'tagdata' => $matches['2'][$j],
  1051. 'var_single' => $vars['var_single'],
  1052. 'var_pair' => $vars['var_pair'],
  1053. 'var_cond' => $FNS->assign_conditional_variables($matches['2'][$j], SLASH, LD, RD),
  1054. 'no_rel_content' => $no_rel_content
  1055. );
  1056. $chunk = str_replace($matches['0'][$j], $marker, $chunk);
  1057. }
  1058. }
  1059. if (preg_match_all("/".LD."reverse_related_entries\s*(.*?)".RD."(.+?)".LD.SLASH."reverse_related_entries".RD."/is", $chunk, $matches))
  1060. {
  1061. $this->log_item("Assigning Reverse Related Entry Data");
  1062. for ($j = 0; $j < count($matches['0']); $j++)
  1063. {
  1064. $rand = $FNS->random('alpha', 8);
  1065. $marker = LD.'REV_REL['.$rand.']REV_REL'.RD;
  1066. $vars = $FNS->assign_variables($matches['2'][$j]);
  1067. $no_rev_content = '';
  1068. if (preg_match("/".LD."if no_reverse_related_entries".RD."(.*?)".LD.SLASH."if".RD."/s", $matches['2'][$j], $no_rev_match))
  1069. {
  1070. // Match the entirety of the conditional
  1071. if (stristr($no_rev_match['1'], LD.'if'))
  1072. {
  1073. $match['0'] = $FNS->full_tag($no_rev_match['0'], $matches['2'][$j], LD.'if', LD.SLASH."if".RD);
  1074. }
  1075. $no_rev_content = substr($no_rev_match['0'], strlen(LD."if no_reverse_related_entries".RD), -strlen(LD.SLASH."if".RD));
  1076. }
  1077. $this->reverse_related_data[$rand] = array(
  1078. 'marker' => $rand,
  1079. 'tagdata' => $matches['2'][$j],
  1080. 'var_single' => $vars['var_single'],
  1081. 'var_pair' => $vars['var_pair'],
  1082. 'var_cond' => $FNS->assign_conditional_variables($matches['2'][$j], SLASH, LD, RD),
  1083. 'params' => $FNS->assign_parameters($matches['1'][$j]),
  1084. 'no_rev_content' => $no_rev_content
  1085. );
  1086. $chunk = str_replace($matches['0'][$j], $marker, $chunk);
  1087. }
  1088. }
  1089. return $chunk;
  1090. }
  1091. /* END */
  1092. /** -------------------------------------
  1093. /** Assign class and method name
  1094. /** -------------------------------------*/
  1095. function assign_class($tag)
  1096. {
  1097. $result = array();
  1098. // Grab the class name and method names contained
  1099. // in the tag and assign them to variables.
  1100. $result = explode(':', $tag);
  1101. // Tags can either have one segment or two:
  1102. // {exp:first_segment}
  1103. // {exp:first_segment:second_segment}
  1104. //
  1105. // These two segments represent either a "class:constructor"
  1106. // or a "class:method". We need to determine which one it is.
  1107. if (count($result) == 1)
  1108. {
  1109. $result['0'] = trim($result['0']);
  1110. $result['1'] = trim($result['0']);
  1111. }
  1112. else
  1113. {
  1114. foreach($result as $key => $value)
  1115. {
  1116. $result[$key] = trim($result[$key]);
  1117. }
  1118. }
  1119. return $result;
  1120. }
  1121. /* END */
  1122. /** ---------------------------------------
  1123. /** Fetch a specific parameter
  1124. /** ---------------------------------------*/
  1125. function fetch_param($which)
  1126. {
  1127. return ( ! isset($this->tagparams[$which])) ? FALSE : $this->tagparams[$which];
  1128. }
  1129. /* END */
  1130. /** ---------------------------------------
  1131. /** Swap single variables with final value
  1132. /** ---------------------------------------*/
  1133. function swap_var_single($search, $replace, $source)
  1134. {
  1135. return str_replace(LD.$search.RD, $replace, $source);
  1136. }
  1137. /* END */
  1138. /** ---------------------------------------
  1139. /** Swap variable pairs with final value
  1140. /** ---------------------------------------*/
  1141. function swap_var_pairs($open, $close, $source)
  1142. {
  1143. return preg_replace("/".LD.preg_quote($open).RD."(.*?)".LD.SLASH.$close.RD."/s", "\\1", $source);
  1144. }
  1145. /* END */
  1146. /** ---------------------------------------
  1147. /** Delete variable pairs
  1148. /** ---------------------------------------*/
  1149. function delete_var_pairs($open, $close, $source)
  1150. {
  1151. return preg_replace("/".LD.preg_quote($open).RD."(.*?)".LD.SLASH.$close.RD."/s", "", $source);
  1152. }
  1153. /* END */
  1154. /** ---------------------------------------
  1155. /** Swap conditional variables
  1156. /** ---------------------------------------*/
  1157. function swap_conditional($search, $replace, $source)
  1158. {
  1159. return str_replace($search, $replace, $source);
  1160. }
  1161. /* END */
  1162. /** -----------------------------------------
  1163. /** Fetch the data in-between two variables
  1164. /** -----------------------------------------*/
  1165. function fetch_data_between_var_pairs($str, $variable)
  1166. {
  1167. if ($str == '' || $variable == '')
  1168. return;
  1169. if ( ! preg_match("/".LD.$variable.".*?".RD."(.*?)".LD.SLASH.$variable.RD."/s", $str, $match))
  1170. return;
  1171. return $match['1'];
  1172. }
  1173. /* END */
  1174. /** -------------------------------------
  1175. /** Parse PHP in template
  1176. /** -------------------------------------*/
  1177. function parse_template_php($str)
  1178. {
  1179. global $FNS;
  1180. ob_start();
  1181. echo $FNS->evaluate($str);
  1182. $str = ob_get_contents();
  1183. ob_end_clean();
  1184. $this->parse_php = FALSE;
  1185. return $str;
  1186. }
  1187. /* END */
  1188. /** ---------------------------------------
  1189. /** Replace marker with final data
  1190. /** ---------------------------------------*/
  1191. function replace_marker($i, $return_data)
  1192. {
  1193. $this->template = str_replace('M'.$i.$this->marker, $return_data, $this->template);
  1194. }
  1195. /* END */
  1196. /** -----------------------------------------
  1197. /** Set caching status
  1198. /** -----------------------------------------*/
  1199. function cache_status($cfile, $args, $cache_type = 'tag')
  1200. {
  1201. // Three caching states:
  1202. // NO_CACHE = do not cache
  1203. // EXPIRED = cache file has expired
  1204. // CURRENT = cache file has not expired
  1205. if ( ! isset($args['cache']))
  1206. return 'NO_CACHE';
  1207. if ($args['cache'] != 'yes')
  1208. return 'NO_CACHE';
  1209. $cache_dir = ($cache_type == 'tag') ? PATH_CACHE.$this->t_cache_path : $cache_dir = PATH_CACHE.$this->p_cache_path;
  1210. $cache_file = $cache_dir.'t_'.$cfile;
  1211. if ( ! file_exists($cache_file))
  1212. return 'EXPIRED';
  1213. if ( ! $fp = @fopen($cache_file, 'rb'))
  1214. return 'EXPIRED';
  1215. flock($fp, LOCK_SH);
  1216. $timestamp = trim(@fread($fp, filesize($cache_file)));
  1217. flock($fp, LOCK_UN);
  1218. fclose($fp);
  1219. $refresh = ( ! isset($args['refresh'])) ? 0 : $args['refresh'];
  1220. if (time() > ($timestamp + ($refresh * 60)))
  1221. {
  1222. return 'EXPIRED';
  1223. }
  1224. else
  1225. {
  1226. if ( ! file_exists($cache_dir.'c_'.$cfile))
  1227. {
  1228. return 'EXPIRED';
  1229. }
  1230. $this->cache_timestamp = $timestamp;
  1231. return 'CURRENT';
  1232. }
  1233. }
  1234. /* END */
  1235. /** -----------------------------------------
  1236. /** Get cache file
  1237. /** -----------------------------------------*/
  1238. function get_cache_file($cfile, $cache_type = 'tag')
  1239. {
  1240. $cache = '';
  1241. $cache_dir = ($cache_type == 'tag') ? PATH_CACHE.$this->t_cache_path : $cache_dir = PATH_CACHE.$this->p_cache_path;
  1242. $fp = @fopen($cache_dir.'c_'.$cfile, 'rb');
  1243. flock($fp, LOCK_SH);
  1244. $cache = @fread($fp, filesize($cache_dir.'c_'.$cfile));
  1245. flock($fp, LOCK_UN);
  1246. fclose($fp);
  1247. return $cache;
  1248. }
  1249. /* END */
  1250. /** -----------------------------------------
  1251. /** Write cache file
  1252. /** -----------------------------------------*/
  1253. function write_cache_file($cfile, $data, $cache_type = 'tag')
  1254. {
  1255. global $PREFS;
  1256. if ($this->disable_caching == TRUE)
  1257. {
  1258. return;
  1259. }
  1260. /* -------------------------------------
  1261. /* Disable Tag Caching
  1262. /*
  1263. /* All for you, Nevin! Disables tag caching, which if used unwisely
  1264. /* on a high traffic site can lead to disastrous disk i/o
  1265. /* This setting allows quick thinking admins to temporarily disable
  1266. /* it without hacking or modifying folder permissions
  1267. /*
  1268. /* Hidden Configuration Variable
  1269. /* - disable_tag_caching => Disable tag caching? (y/n)
  1270. /* -------------------------------------*/
  1271. if ($cache_type == 'tag' && $PREFS->ini('disable_tag_caching') == 'y')
  1272. {
  1273. return;
  1274. }
  1275. $cache_dir = ($cache_type == 'tag') ? PATH_CACHE.$this->t_cache_path : $cache_dir = PATH_CACHE.$this->p_cache_path;
  1276. $cache_base = ($cache_type == 'tag') ? PATH_CACHE.'tag_cache' : PATH_CACHE.'page_cache';
  1277. $time_file = $cache_dir.'t_'.$cfile;
  1278. $cache_file = $cache_dir.'c_'.$cfile;
  1279. $dirs = array($cache_base, $cache_dir);
  1280. foreach ($dirs as $dir)
  1281. {
  1282. if ( ! @is_dir($dir))
  1283. {
  1284. if ( ! @mkdir($dir, 0777))
  1285. {
  1286. return;
  1287. }
  1288. if ($dir == $cache_base && $fp = @fopen($dir.'/index.html', 'wb'))
  1289. {
  1290. fclose($fp);
  1291. }
  1292. @chmod($dir, 0777);
  1293. }
  1294. }
  1295. // Write the timestamp file
  1296. if ( ! $fp = @fopen($time_file, 'wb'))
  1297. return;
  1298. flock($fp, LOCK_EX);
  1299. fwrite($fp, time());
  1300. flock($fp, LOCK_UN);
  1301. fclose($fp);
  1302. @chmod($time_file, 0777);
  1303. // Write the data cache
  1304. if ( ! $fp = @fopen($cache_file, 'wb'))
  1305. return;
  1306. flock($fp, LOCK_EX);
  1307. fwrite($fp, $data);
  1308. flock($fp, LOCK_UN);
  1309. fclose($fp);
  1310. @chmod($cache_file, 0777);
  1311. }
  1312. /* END */
  1313. /** -------------------------------------
  1314. /** Parse Template URI Data
  1315. /** -------------------------------------*/
  1316. function parse_template_uri()
  1317. {
  1318. global $PREFS, $LANG, $OUT, $DB, $LOC, $IN, $REGX;
  1319. $this->log_item("Parsing Template URI");
  1320. // Does the first segment exist? No? Show the default template
  1321. if ($IN->fetch_uri_segment(1) === FALSE)
  1322. {
  1323. return $this->fetch_template('', 'index', TRUE);
  1324. }
  1325. // Is only the pagination showing in the URI?
  1326. elseif(count($IN->SEGS) == 1 && preg_match("#^(P\d+)$#", $IN->fetch_uri_segment(1), $match))
  1327. {
  1328. $IN->QSTR = $match['1'];
  1329. return $this->fetch_template('', 'index', TRUE);
  1330. }
  1331. // Set the strict urls pref
  1332. if ($PREFS->ini('strict_urls') !== FALSE)
  1333. {
  1334. $this->strict_urls = ($PREFS->ini('strict_urls') == 'y') ? TRUE : FALSE;
  1335. }
  1336. // At this point we know that we have at least one segment in the URI, so
  1337. // let's try to determine what template group/template we should show
  1338. // Is the first segment the name of a template group?
  1339. $query = $DB->query("SELECT group_id FROM exp_template_groups WHERE group_name = '".$DB->escape_str($IN->fetch_uri_segment(1))."' AND site_id = '".$DB->escape_str($PREFS->ini('site_id'))."'");
  1340. // Template group found!
  1341. if ($query->num_rows == 1)
  1342. {
  1343. // Set the name of our template group
  1344. $template_group = $IN->fetch_uri_segment(1);
  1345. // Set the group_id so we can use it in the next query
  1346. $group_id = $query->row['group_id'];
  1347. // Does the second segment of the URI exist? If so...
  1348. if ($IN->fetch_uri_segment(2) !== FALSE)
  1349. {
  1350. // Is the second segment the name of a valid template?
  1351. $query = $DB->query("SELECT COUNT(*) AS count FROM exp_templates WHERE group_id = '{$group_id}' AND template_name = '".$DB->escape_str($IN->fetch_uri_segment(2))."'");
  1352. // We have a template name!
  1353. if ($query->row['count'] == 1)
  1354. {
  1355. // Assign the template name
  1356. $template = $IN->fetch_uri_segment(2);
  1357. // Re-assign the query string variable in the Input class so the various tags can show the correct data
  1358. $IN->QSTR = ( ! $IN->fetch_uri_segment(3) AND $IN->fetch_uri_segment(2) != 'index') ? '' : $REGX->trim_slashes(substr($IN->URI, strlen('/'.$IN->fetch_uri_segment(1).'/'.$IN->fetch_uri_segment(2))));
  1359. }
  1360. else // A valid template was not found
  1361. {
  1362. // Set the template to index
  1363. $template = 'index';
  1364. // Re-assign the query string variable in the Input class so the various tags can show the correct data
  1365. $IN->QSTR = ( ! $IN->fetch_uri_segment(3)) ? $IN->fetch_uri_segment(2) : $REGX->trim_slashes(substr($IN->URI, strlen('/'.$IN->fetch_uri_segment(1))));
  1366. }
  1367. }
  1368. // The second segment of the URL does not exist
  1369. else
  1370. {
  1371. // Set the template as "index"
  1372. $template = 'index';
  1373. }
  1374. }
  1375. // The first segment in the URL does NOT correlate to a valid template group. Oh my!
  1376. else
  1377. {
  1378. // If we are enforcing strict URLs we need to show a 404
  1379. if ($this->strict_urls == TRUE)
  1380. {
  1381. if ($PREFS->ini('site_404'))
  1382. {
  1383. $this->log_item("Template group and template not found, showing 404 page");
  1384. return $this->fetch_template('', '', FALSE);
  1385. }
  1386. else
  1387. {
  1388. return $this->_404();
  1389. }
  1390. }
  1391. // We we are not enforcing strict URLs, so Let's fetch the the name of the default template group
  1392. $result = $DB->query("SELECT group_name, group_id FROM exp_template_groups WHERE is_site_default = 'y' AND site_id = '".$DB->escape_str($PREFS->ini('site_id'))."'");
  1393. // No result? Bail out...
  1394. // There's really nothing else to do here. We don't have a valid template group in the URL
  1395. // and the admin doesn't have a template group defined as the site default.
  1396. if ($result->num_rows == 0)
  1397. {
  1398. // Turn off caching
  1399. $this->disable_caching = TRUE;
  1400. // Show the user-specified 404
  1401. if ($PREFS->ini('site_404'))
  1402. {
  1403. $this->log_item("Template group and template not found, showing 404 page");
  1404. return $this->fetch_template('', '', FALSE);
  1405. }
  1406. else
  1407. {
  1408. // Show the default 404
  1409. return $this->_404();
  1410. }
  1411. }
  1412. // Since the first URI segment isn't a template group name, could it be the name of a template in the default group?
  1413. $query = $DB->query("SELECT COUNT(*) AS count FROM exp_templates WHERE group_id = '".$result->row['group_id']."' AND template_name = '".$DB->escape_str($IN->fetch_uri_segment(1))."'");
  1414. // We found a valid template!
  1415. if ($query->row['count'] == 1)
  1416. {
  1417. // Set the template group name from the prior query result (we use the default template group name)
  1418. $template_group = $result->row['group_name'];
  1419. // Set the template name
  1420. $template = $IN->fetch_uri_segment(1);
  1421. // Re-assign the query string variable in the Input class so the various tags can show the correct data
  1422. if ($IN->fetch_uri_segment(2))
  1423. {
  1424. $IN->QSTR = $REGX->trim_slashes(substr($IN->URI, strlen('/'.$IN->fetch_uri_segment(1))));
  1425. }
  1426. }
  1427. // 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
  1428. else
  1429. {
  1430. // Turn off caching
  1431. $this->disable_caching = TRUE;
  1432. // is 404 preference set, we wet our group/template names as blank.
  1433. // The fetch_template() function below will fetch the 404 and show it
  1434. if ($PREFS->ini('site_404'))
  1435. {
  1436. $template_group = '';
  1437. $template = '';
  1438. $this->log_item("Template group and template not found, showing 404 page");
  1439. }
  1440. else
  1441. // No 404 preference is set so we will show the index template from the default template group
  1442. {
  1443. $IN->QSTR = $REGX->trim_slashes($IN->URI);
  1444. $template_group = $result->row['group_name'];
  1445. $template = 'index';
  1446. $this->log_item("Showing index. Template not found: ".$IN->fetch_uri_segment(1));
  1447. }
  1448. }
  1449. }
  1450. // Fetch the template!
  1451. return $this->fetch_template($template_group, $template, FALSE);
  1452. }
  1453. // END
  1454. /** -----------------------------------------
  1455. /** 404 page
  1456. /** -----------------------------------------*/
  1457. function _404()
  1458. {
  1459. global $OUT;
  1460. $this->log_item("404 Page Returned");
  1461. $OUT->http_status_header(404);
  1462. echo '<html><head><title>404 Page Not Found</title></head><body><h1>Status: 404 Page Not Found</h1></body></html>';
  1463. exit;
  1464. }
  1465. /** -----------------------------------------
  1466. /** Fetch the requested template
  1467. /** -----------------------------------------*/
  1468. function fetch_template($template_group, $template, $show_default = TRUE, $site_id = '')
  1469. {
  1470. global $PREFS, $LANG, $OUT, $DB, $IN, $SESS, $FNS, $LOC;
  1471. if ($site_id == '' OR ! is_numeric($site_id))
  1472. {
  1473. $site_id = $PREFS->ini('site_id');
  1474. }
  1475. $this->log_item("Retrieving Template from Database: ".$template_group.'/'.$template);
  1476. $sql_404 = '';
  1477. /** ---------------------------------------
  1478. /** Is this template supposed to be "hidden"?
  1479. /** ---------------------------------------*/
  1480. /* -------------------------------------------
  1481. /* Hidden Configuration Variable
  1482. /* - hidden_template_indicator => '.'
  1483. The character(s) used to designate a template as "hidden"
  1484. /* -------------------------------------------*/
  1485. $hidden_indicator = ($PREFS->ini('hidden_template_indicator') === FALSE) ? '.' : $PREFS->ini('hidden_template_indicator');
  1486. if ($this->depth == 0 AND substr($template, 0, 1) == $hidden_indicator)
  1487. {
  1488. /* -------------------------------------------
  1489. /* Hidden Configuration Variable
  1490. /* - hidden_template_404 => y/n
  1491. If a hidden template is encountered, the default behavior is
  1492. to throw a 404. With this set to 'n', the template group's
  1493. index page will be shown instead
  1494. /* -------------------------------------------*/
  1495. if ($PREFS->ini('hidden_template_404') !== 'n')
  1496. {
  1497. $x = explode("/", $PREFS->ini('site_404'));
  1498. if (isset($x['0']) AND isset($x['1']))
  1499. {
  1500. $OUT->out_type = '404';
  1501. $this->template_type = '404';
  1502. $sql_404 = " AND exp_template_groups.group_name='".$DB->escape_str($x['0'])."' AND exp_templates.template_name='".$DB->escape_str($x['1'])."'";
  1503. }
  1504. else
  1505. {
  1506. $template = 'index';
  1507. }
  1508. }
  1509. else
  1510. {
  1511. $template = 'index';
  1512. }
  1513. }
  1514. if ($template_group == '' && $show_default == FALSE && USER_BLOG === FALSE && $PREFS->ini('site_404') != '')
  1515. {
  1516. $treq = $PREFS->ini('site_404');
  1517. $x = explode("/", $treq);
  1518. if (isset($x['0']) AND isset($x['1']))
  1519. {
  1520. $OUT->out_type = '404';
  1521. $this->template_type = '404';
  1522. $sql_404 = " AND exp_template_groups.group_name='".$DB->escape_str($x['0'])."' AND exp_templates.template_name='".$DB->escape_str($x['1'])."'";
  1523. }
  1524. }
  1525. $sql = "SELECT exp_templates.template_name,
  1526. exp_templates.template_id,
  1527. exp_templates.template_data,
  1528. exp_templates.template_type,
  1529. exp_templates.edit_date,
  1530. exp_templates.save_template_file,
  1531. exp_templates.cache,
  1532. exp_templates.refresh,
  1533. exp_templates.no_auth_bounce,
  1534. exp_templates.enable_http_auth,
  1535. exp_templates.allow_php,
  1536. exp_templates.php_parse_location,
  1537. exp_templates.hits,
  1538. exp_template_groups.group_name
  1539. FROM exp_template_groups, exp_templates
  1540. WHERE exp_template_groups.group_id = exp_templates.group_id
  1541. AND exp_template_groups.site_id = '".$DB->escape_str($site_id)."' ";
  1542. if ($sql_404 != '')
  1543. {
  1544. $sql .= $sql_404;
  1545. }
  1546. else
  1547. {
  1548. if ($template != '')
  1549. $sql .= " AND exp_templates.template_name = '".$DB->escape_str($template)."' ";
  1550. if ($show_default == TRUE && USER_BLOG === FALSE)
  1551. {
  1552. $sql .= "AND exp_template_groups.is_site_default = 'y'";
  1553. }
  1554. else
  1555. {
  1556. $sql .= "AND exp_template_groups.group_name = '".$DB->escape_str($template_group)."'";
  1557. }
  1558. }
  1559. $query = $DB->query($sql);
  1560. if ($query->num_rows == 0)
  1561. {
  1562. $this->log_item("Template Not Found");
  1563. return FALSE;
  1564. }
  1565. $this->log_item("Template Found");
  1566. /** ----------------------------------------------------
  1567. /** HTTP Authentication
  1568. /** ----------------------------------------------------*/
  1569. if ($query->row['enable_http_auth'] == 'y')
  1570. {
  1571. $this->log_item("HTTP Authentication in Progress");
  1572. $results = $DB->query("SELECT member_group
  1573. FROM exp_template_no_access
  1574. WHERE template_id = '".$DB->escape_str($query->row['template_id'])."'");
  1575. $not_allowed_groups = array('2', '3', '4');
  1576. if ($results->num_rows > 0)
  1577. {
  1578. foreach($results->result as $row)
  1579. {
  1580. $not_allowed_groups[] = $row['member_group'];
  1581. }
  1582. }
  1583. if ($this->template_authentication_check_basic($not_allowed_groups) !== TRUE)
  1584. {
  1585. $this->template_authentication_basic();
  1586. }
  1587. }
  1588. /** ----------------------------------------------------
  1589. /** Is the current user allowed to view this template?
  1590. /** ----------------------------------------------------*/
  1591. if ($query->row['enable_http_auth'] != 'y' && $query->row['no_auth_bounce'] != '')
  1592. {
  1593. $this->log_item("Determining Template Access Privileges");
  1594. $result = $DB->query("SELECT count(*) AS count FROM exp_template_no_access WHERE template_id = '".$DB->escape_str($query->row['template_id'])."' AND member_group = '".$DB->escape_str($SESS->userdata['group_id'])."'");
  1595. if ($result->row['count'] > 0)
  1596. {
  1597. if ($this->depth > 0)
  1598. {
  1599. return '';
  1600. }
  1601. $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
  1602. FROM exp_templates a, exp_template_groups b
  1603. WHERE a.group_id = b.group_id
  1604. AND template_id = '".$DB->escape_str($query->row['no_auth_bounce'])."'";
  1605. $query = $DB->query($sql);
  1606. }
  1607. }
  1608. if ($query->num_rows == 0)
  1609. {
  1610. return false;
  1611. }
  1612. /** -----------------------------------------
  1613. /** Is PHP allowed in this template?
  1614. /** -----------------------------------------*/
  1615. if ($query->row['allow_php'] == 'y' AND $PREFS->ini('demo_date') == FALSE)
  1616. {
  1617. $this->parse_php = TRUE;
  1618. $this->php_parse_location = ($query->row['php_parse_location'] == 'i') ? 'input' : 'output';
  1619. }
  1620. /** -----------------------------------------
  1621. /** Increment hit counter
  1622. /** -----------------------------------------*/
  1623. if (($this->hit_lock == FALSE OR $this->hit_lock_override == TRUE) AND $PREFS->ini('enable_hit_tracking') != 'n')
  1624. {
  1625. $this->template_hits = $query->row['hits'] + 1;
  1626. $this->hit_lock = TRUE;
  1627. $DB->query("UPDATE exp_templates SET hits = '".$this->template_hits."' WHERE template_id = '".$DB->escape_str($query->row['template_id'])."'");
  1628. }
  1629. /** -----------------------------------------
  1630. /** Set template edit date
  1631. /** -----------------------------------------*/
  1632. $this->template_edit_date = $query->row['edit_date'];
  1633. /** -----------------------------------------
  1634. /** Set template type for our page headers
  1635. /** -----------------------------------------*/
  1636. if ($this->template_type == '')
  1637. {
  1638. $this->template_type = $query->row['template_type'];
  1639. $FNS->template_type = $query->row['template_type'];
  1640. /** -----------------------------------------
  1641. /** If JS or CSS request, reset Tracker Cookie
  1642. /** -----------------------------------------*/
  1643. if ($this->template_type == 'js' OR $this->template_type == 'css')
  1644. {
  1645. if (sizeof($SESS->tracker) <= 1)
  1646. {
  1647. $SESS->tracker = array();
  1648. }
  1649. else
  1650. {
  1651. $removed = array_shift($SESS->tracker);
  1652. }
  1653. $FNS->set_cookie('tracker', serialize($SESS->tracker), '0');
  1654. }
  1655. }
  1656. if ($this->depth > 0)
  1657. {
  1658. $this->embed_type = $query->row['template_type'];
  1659. }
  1660. /** -----------------------------------------
  1661. /** Cache Override
  1662. /** -----------------------------------------*/
  1663. // We can manually set certian things not to be cached, like the
  1664. // search template and the member directory after it's updated
  1665. // Note: I think search caching is OK.
  1666. // $cache_override = array('member' => 'U', 'search' => FALSE);
  1667. $cache_override = array('member');
  1668. foreach ($cache_override as $val)
  1669. {
  1670. if (preg_match("#^/".preg_quote($val, '#')."/#", $IN->URI))
  1671. {
  1672. $query->row['cache'] = 'n';
  1673. }
  1674. }
  1675. /** -----------------------------------------
  1676. /** Retreive cache
  1677. /** -----------------------------------------*/
  1678. $this->cache_hash = md5($site_id.'-'.$template_group.'-'.$template);
  1679. if ($query->row['cache'] == 'y')
  1680. {
  1681. $this->cache_status = $this->cache_status($this->cache_hash, array('cache' => 'yes', 'refresh' => $query->row['refresh']), 'template');
  1682. if ($this->cache_status == 'CURRENT')
  1683. {
  1684. return $this->convert_xml_declaration($this->get_cache_file($this->cache_hash, 'template'));
  1685. }
  1686. }
  1687. /** -----------------------------------------
  1688. /** Retreive template file if necessary
  1689. /** -----------------------------------------*/
  1690. if ($query->row['save_template_file'] == 'y')
  1691. {
  1692. $site_switch = FALSE;
  1693. if ($PREFS->ini('site_id') != $site_id)
  1694. {
  1695. $site_switch = $PREFS->core_ini;
  1696. if (isset($this->site_prefs_cache[$site_id]))
  1697. {
  1698. $PREFS->core_ini = $this->site_prefs_cache[$site_id];
  1699. }
  1700. else
  1701. {
  1702. $PREFS->site_prefs('', $site_id);
  1703. $this->site_prefs_cache[$site_id] = $PREFS->core_ini;
  1704. }
  1705. }
  1706. if ($PREFS->ini('save_tmpl_files') == 'y' AND $PREFS->ini('tmpl_file_basepath') != '')
  1707. {
  1708. $this->log_item("Retrieving Template from File");
  1709. $basepath = rtrim($PREFS->ini('tmpl_file_basepath'), '/').'/';
  1710. $basepath .= $query->row['group_name'].'/'.$query->row['template_name'].'.php';
  1711. if ($fp = @fopen($basepath, 'rb'))
  1712. {
  1713. flock($fp, LOCK_SH);
  1714. $query->row['template_data'] = (filesize($basepath) == 0) ? '' : fread($fp, filesize($basepath));
  1715. flock($fp, LOCK_UN);
  1716. fclose($fp);
  1717. }
  1718. }
  1719. if ($site_switch !== FALSE)
  1720. {
  1721. $PREFS->core_ini = $site_switch;
  1722. }
  1723. }
  1724. // standardize newlines
  1725. $query->row['template_data'] = preg_replace("/(\015\012)|(\015)|(\012)/", "\n", $query->row['template_data']);
  1726. return $this->convert_xml_declaration($this->remove_ee_comments($query->row['template_data']));
  1727. }
  1728. /* END */
  1729. /** -------------------------------------
  1730. /** "no results" tag
  1731. /** -------------------------------------*/
  1732. function no_results()
  1733. {
  1734. global $FNS, $PREFS, $OUT;
  1735. if ( ! preg_match("/".LD."redirect\s*=\s*(\042|\047)([^\\1]*?)\\1".RD."/si", $this->no_results, $match))
  1736. {
  1737. $this->log_item("Returning No Results Content");
  1738. return $this->no_results;
  1739. }
  1740. else
  1741. {
  1742. $this->log_item("Processing No Results Redirect");
  1743. if ($match['2'] == "404")
  1744. {
  1745. $template = explode('/', $PREFS->ini('site_404'));
  1746. if (isset($template['1']))
  1747. {
  1748. $this->log_item('Processing "'.$template['0'].'/'.$template['1'].'" Template as 404 Page');
  1749. $OUT->out_type = "404";
  1750. $this->template_type = "404";
  1751. $this->process_template($template['0'], $template['1']);
  1752. $this->cease_processing = TRUE;
  1753. }
  1754. else
  1755. {
  1756. $this->log_item('404 redirect requested, but no 404 page is specified in the Global Template Preferences');
  1757. return $this->no_results;
  1758. }
  1759. }
  1760. else
  1761. {
  1762. return $FNS->redirect($FNS->create_url($FNS->extract_path("=".str_replace("&#47;", "/", $match['2']))));
  1763. }
  1764. }
  1765. }
  1766. /* END */
  1767. /** -------------------------------------
  1768. /** Convert XML declaration in RSS page
  1769. /** -------------------------------------*/
  1770. // This fixes a parsing error when PHP is used in RSS templates
  1771. function convert_xml_declaration($str)
  1772. {
  1773. return preg_replace("/\<\?xml(.+?)\?\>/", "<XXML\\1/XXML>", $str);
  1774. }
  1775. /* END */
  1776. /** -------------------------------------
  1777. /** Restore XML declaration in RSS page
  1778. /** -------------------------------------*/
  1779. function restore_xml_declaration($str)
  1780. {
  1781. return preg_replace("/\<XXML(.+?)\/XXML\>/", "<?xml\\1?>", $str); // <?
  1782. }
  1783. /* END */
  1784. /** -------------------------------------
  1785. /** Remove EE Template Comments
  1786. /** -------------------------------------*/
  1787. function remove_ee_comments($str)
  1788. {
  1789. return preg_replace("/\{!--.*?--\}/s", '', $str);
  1790. }
  1791. /* END */
  1792. /** -------------------------------------
  1793. /** Fetch installed modules
  1794. /** -------------------------------------*/
  1795. function fetch_modules()
  1796. {
  1797. if (sizeof($this->modules) == 0 && $fp = @opendir(PATH_MOD))
  1798. {
  1799. while (false !== ($file = readdir($fp)))
  1800. {
  1801. if ( is_dir(PATH_MOD.$file) && ! preg_match("/[^a-z\_0-9]/", $file))
  1802. {
  1803. $this->modules[] = $file;
  1804. }
  1805. }
  1806. closedir($fp);
  1807. }
  1808. }
  1809. /* END */
  1810. /** -------------------------------------
  1811. /** Fetch installed plugins
  1812. /** -------------------------------------*/
  1813. function fetch_plugins()
  1814. {
  1815. if ($fp = @opendir(PATH_PI))
  1816. {
  1817. while (false !== ($file = readdir($fp)))
  1818. {
  1819. if ( preg_match("/pi\.[a-z\_0-9]+?".preg_quote(EXT, '/')."$/", $file))
  1820. {
  1821. $this->plugins[] = substr($file, 3, - strlen(EXT));
  1822. }
  1823. }
  1824. closedir($fp);
  1825. }
  1826. }
  1827. /* END */
  1828. /** ------------------------------------------------
  1829. /** Parse global variables and non-cachable stuff
  1830. /** ------------------------------------------------*/
  1831. // The syntax is generally: {global:variable_name}
  1832. function parse_globals($str)
  1833. {
  1834. global $LANG, $PREFS, $FNS, $IN, $DB, $LOC, $SESS;
  1835. $charset = '';
  1836. $lang = '';
  1837. $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');
  1838. /** --------------------------------------------------
  1839. /** Redirect - if we have one of these, no need to go further
  1840. /** --------------------------------------------------*/
  1841. if (strpos($str, LD.'redirect') !== FALSE)
  1842. {
  1843. if (preg_match("/".LD."redirect\s*=\s*(\042|\047)([^\\1]*?)\\1".RD."/si", $str, $match))
  1844. {
  1845. if ($match['2'] == "404")
  1846. {
  1847. $template = explode('/', $PREFS->ini('site_404'));
  1848. if (isset($template['1']))
  1849. {
  1850. $this->log_item('Processing "'.$template['0'].'/'.$template['1'].'" Template as 404 Page');
  1851. $this->template_type = "404";
  1852. $this->process_template($template['0'], $template['1']);
  1853. $this->cease_processing = TRUE;
  1854. // the resulting template will not have globals parsed unless we do this
  1855. return $this->parse_globals($this->final_template);
  1856. }
  1857. else
  1858. {
  1859. $this->log_item('404 redirect requested, but no 404 page is specified in the Global Template Preferences');
  1860. return $this->_404();
  1861. }
  1862. }
  1863. else
  1864. {
  1865. // $FNS->redirect() exit;s on its own
  1866. $FNS->redirect($FNS->create_url($FNS->extract_path("=".str_replace("&#47;", "/", $match['2']))));
  1867. }
  1868. }
  1869. }
  1870. /** --------------------------------------------------
  1871. /** Restore XML declaration if it was encoded
  1872. /** --------------------------------------------------*/
  1873. $str = $this->restore_xml_declaration($str);
  1874. /** --------------------------------------------------
  1875. /** {hits}
  1876. /** --------------------------------------------------*/
  1877. $str = str_replace(LD.'hits'.RD, $this->template_hits, $str);
  1878. /** --------------------------------------------------
  1879. /** {ip_address} and {ip_hostname}
  1880. /** --------------------------------------------------*/
  1881. $str = str_replace(LD.'ip_address'.RD, $IN->IP, $str);
  1882. // Turns out gethostbyaddr() is WAY SLOW on many systems so I'm killing it.
  1883. // $str = str_replace(LD.'ip_hostname'.RD, @gethostbyaddr($IN->IP), $str);
  1884. $str = str_replace(LD.'ip_hostname'.RD, $IN->IP, $str);
  1885. /** --------------------------------------------------
  1886. /** {homepage}
  1887. /** --------------------------------------------------*/
  1888. $str = str_replace(LD.'homepage'.RD, $FNS->fetch_site_index(), $str);
  1889. /** --------------------------------------------------
  1890. /** {site_name} {site_url} {site_index}
  1891. /** --------------------------------------------------*/
  1892. $str = str_replace(LD.'site_name'.RD, stripslashes($PREFS->ini('site_name')), $str);
  1893. $str = str_replace(LD.'site_url'.RD, stripslashes($PREFS->ini('site_url')), $str);
  1894. $str = str_replace(LD.'site_index'.RD, stripslashes($PREFS->ini('site_index')), $str);
  1895. $str = str_replace(LD.'webmaster_email'.RD, stripslashes($PREFS->ini('webmaster_email')), $str);
  1896. /** --------------------------------------------------
  1897. /** Stylesheet variable: {stylesheet=group/template}
  1898. /** --------------------------------------------------*/
  1899. $qs = ($PREFS->ini('force_query_string') == 'y') ? '' : '?';
  1900. if (preg_match_all("/".LD."\s*stylesheet=[\042\047]?(.*?)[\042\047]?".RD."/", $str, $css_matches))
  1901. {
  1902. $css_versions = array();
  1903. if ($PREFS->ini('send_headers') == 'y')
  1904. {
  1905. $sql = "SELECT t.template_name, tg.group_name, t.edit_date FROM exp_templates t, exp_template_groups tg
  1906. WHERE t.group_id = tg.group_id
  1907. AND t.template_type = 'css'
  1908. AND t.site_id = '".$DB->escape_str($PREFS->ini('site_id'))."'";
  1909. foreach($css_matches[1] as $css_match)
  1910. {
  1911. $ex = explode('/', $css_match, 2);
  1912. if (isset($ex[1]))
  1913. {
  1914. $css_parts[] = "(t.template_name = '".$DB->escape_str($ex[1])."' AND tg.group_name = '".$DB->escape_str($ex[0])."')";
  1915. }
  1916. }
  1917. $css_query = ( ! isset($css_parts)) ? $DB->query($sql) : $DB->query($sql.' AND ('.implode(' OR ', $css_parts) .')');
  1918. if ($css_query->num_rows > 0)
  1919. {
  1920. foreach($css_query->result as $row)
  1921. {
  1922. $css_versions[$row['group_name'].'/'.$row['template_name']] = $row['edit_date'];
  1923. }
  1924. }
  1925. }
  1926. for($ci=0, $cs=sizeof($css_matches[0]); $ci < $cs; ++$ci)
  1927. {
  1928. $str = str_replace($css_matches[0][$ci], $FNS->fetch_site_index().$qs.'css='.$css_matches[1][$ci].(isset($css_versions[$css_matches[1][$ci]]) ? '.v.'.$css_versions[$css_matches[1][$ci]] : ''), $str);
  1929. }
  1930. unset($css_matches);
  1931. unset($css_versions);
  1932. }
  1933. /** --------------------------------------------------
  1934. /** Email encode: {encode="you@yoursite.com" title="click Me"}
  1935. /** --------------------------------------------------*/
  1936. if ($this->encode_email == TRUE)
  1937. {
  1938. if (preg_match_all("/".LD."encode=(.+?)".RD."/i", $str, $matches))
  1939. {
  1940. for ($j = 0; $j < count($matches['0']); $j++)
  1941. {
  1942. $str = preg_replace('/'.preg_quote($matches['0'][$j], '/').'/', $FNS->encode_email($matches['1'][$j]), $str, 1);
  1943. }
  1944. }
  1945. }
  1946. else
  1947. {
  1948. /* -------------------------------------------
  1949. /* Hidden Configuration Variable
  1950. /* - encode_removed_text => Text to display if there is an {encode=""}
  1951. tag but emails are not to be encoded
  1952. /* -------------------------------------------*/
  1953. $str = preg_replace("/".LD."\s*encode=(.+?)".RD."/",
  1954. ($PREFS->ini('encode_removed_text') !== FALSE) ? $PREFS->ini('encode_removed_text') : '',
  1955. $str);
  1956. }
  1957. /** --------------------------------------------------
  1958. /** Path variable: {path=group/template}
  1959. /** --------------------------------------------------*/
  1960. $str = preg_replace_callback("/".LD."\s*path=(.*?)".RD."/", array(&$FNS, 'create_url'), $str);
  1961. /** --------------------------------------------------
  1962. /** Debug mode: {debug_mode}
  1963. /** --------------------------------------------------*/
  1964. $str = str_replace(LD.'debug_mode'.RD, ($PREFS->ini('debug') > 0) ? $LANG->line('on') : $LANG->line('off'), $str);
  1965. /** --------------------------------------------------
  1966. /** GZip mode: {gzip_mode}
  1967. /** --------------------------------------------------*/
  1968. $str = str_replace(LD.'gzip_mode'.RD, ($PREFS->ini('gzip_output') == 'y') ? $LANG->line('enabled') : $LANG->line('disabled'), $str);
  1969. /** --------------------------------------------------
  1970. /** App version: {version}
  1971. /** --------------------------------------------------*/
  1972. $str = str_replace(LD.'app_version'.RD, APP_VER, $str);
  1973. $str = str_replace(LD.'version'.RD, APP_VER, $str);
  1974. /** --------------------------------------------------
  1975. /** App version: {build}
  1976. /** --------------------------------------------------*/
  1977. $str = str_replace(LD.'app_build'.RD, APP_BUILD, $str);
  1978. $str = str_replace(LD.'build'.RD, APP_BUILD, $str);
  1979. /** --------------------------------------------------
  1980. /** {charset} and {lang}
  1981. /** --------------------------------------------------*/
  1982. if (preg_match("/\{charset\}/i", $str) OR preg_match("/\{lang\}/i", $str))
  1983. {
  1984. if ( ! USER_BLOG)
  1985. {
  1986. $str = str_replace(LD.'charset'.RD, $PREFS->ini('charset'), $str);
  1987. $str = str_replace(LD.'lang'.RD, $PREFS->ini('xml_lang'), $str);
  1988. }
  1989. else
  1990. {
  1991. $query = $DB->query("SELECT blog_lang, blog_encoding FROM exp_weblogs WHERE weblog_id = '".$DB->escape_str(UB_BLOG_ID)."'");
  1992. $str = str_replace(LD.'charset'.RD, $query->row['blog_encoding'], $str);
  1993. $str = str_replace(LD.'lang'.RD, $query->row['blog_lang'], $str);
  1994. }
  1995. }
  1996. /** --------------------------------------------------
  1997. /** Parse User-defined Global Variables
  1998. /** --------------------------------------------------*/
  1999. $ub_id = ( ! defined('UB_BLOG_ID')) ? 0 : UB_BLOG_ID;
  2000. $query = $DB->query("SELECT variable_name, variable_data FROM exp_global_variables
  2001. WHERE site_id = '".$DB->escape_str($PREFS->ini('site_id'))."'
  2002. AND user_blog_id = '".$DB->escape_str($ub_id)."' ");
  2003. if ($query->num_rows > 0)
  2004. {
  2005. foreach ($query->result as $row)
  2006. {
  2007. $str = str_replace(LD.$row['variable_name'].RD, $row['variable_data'], $str);
  2008. }
  2009. }
  2010. /** --------------------------------------------------
  2011. /** {member_profile_link}
  2012. /** --------------------------------------------------*/
  2013. if ($SESS->userdata('member_id') != 0)
  2014. {
  2015. $name = ($SESS->userdata['screen_name'] == '') ? $SESS->userdata['username'] : $SESS->userdata['screen_name'];
  2016. $path = "<a href='".$FNS->create_url('/member/'.$SESS->userdata('member_id'))."'>".$name."</a>";
  2017. $str = str_replace(LD.'member_profile_link'.RD, $path, $str);
  2018. }
  2019. else
  2020. {
  2021. $str = str_replace(LD.'member_profile_link'.RD, '', $str);
  2022. }
  2023. /** -----------------------------------
  2024. /** Fetch captcha
  2025. /** -----------------------------------*/
  2026. if (preg_match("/({captcha})/", $str))
  2027. {
  2028. $str = preg_replace("/{captcha}/", $FNS->create_captcha(), $str);
  2029. }
  2030. /** -----------------------------------
  2031. /** Add security hashes to forms
  2032. /** -----------------------------------*/
  2033. // We do this here to keep the security hashes from being cached
  2034. $str = $FNS->add_form_security_hash($str);
  2035. /** -----------------------------------
  2036. /** Add Action IDs form forms and links
  2037. /** -----------------------------------*/
  2038. $str = $FNS->insert_action_ids($str);
  2039. /** -----------------------------------
  2040. /** Parse non-cachable variables
  2041. /** -----------------------------------*/
  2042. $SESS->userdata['member_group'] = $SESS->userdata['group_id'];
  2043. foreach ($user_vars as $val)
  2044. {
  2045. if (isset($SESS->userdata[$val]) AND ($val == 'group_description' OR strval($SESS->userdata[$val]) != ''))
  2046. {
  2047. $str = str_replace(LD.$val.RD, $SESS->userdata[$val], $str);
  2048. $str = str_replace('{out_'.$val.'}', $SESS->userdata[$val], $str);
  2049. $str = str_replace('{global->'.$val.'}', $SESS->userdata[$val], $str);
  2050. $str = str_replace('{logged_in_'.$val.'}', $SESS->userdata[$val], $str);
  2051. }
  2052. }
  2053. return $str;
  2054. }
  2055. /* END */
  2056. /** -----------------------------------
  2057. /** Parse Forms that Cannot Be Cached
  2058. /** -----------------------------------*/
  2059. function parse_nocache($str)
  2060. {
  2061. global $FNS;
  2062. if ( ! stristr($str, '{NOCACHE'))
  2063. {
  2064. return $str;
  2065. }
  2066. /** -----------------------------------
  2067. /** Generate Comment Form if needed
  2068. /** -----------------------------------*/
  2069. // In order for the comment form not to cache the "save info"
  2070. // data we need to generate dynamically if necessary
  2071. if (preg_match_all("#{NOCACHE_(\S+)_FORM=\"(.*?)\"}(.+?){/NOCACHE_FORM}#s", $str, $match))
  2072. {
  2073. for($i=0, $s=sizeof($match['0']); $i < $s; $i++)
  2074. {
  2075. $class = $FNS->filename_security(strtolower($match['1'][$i]));
  2076. if ( ! class_exists($class))
  2077. {
  2078. require PATH_MOD.$class.'/mod.'.$class.EXT;
  2079. }
  2080. $this->tagdata = $match['3'][$i];
  2081. $vars = $FNS->assign_variables($match['3'][$i], '/');
  2082. $this->var_single = $vars['var_single'];
  2083. $this->var_pair = $vars['var_pair'];
  2084. $this->tagparams = $FNS->assign_parameters($match['2'][$i]);
  2085. $this->var_cond = $FNS->assign_conditional_variables($match['3'][$i], '/', LD, RD);
  2086. // Assign sites for the tag
  2087. $this->_fetch_site_ids();
  2088. if ($class == 'gallery')
  2089. $str = str_replace($match['0'][$i], Gallery::comment_form(TRUE, $FNS->cached_captcha), $str);
  2090. elseif ($class == 'comment')
  2091. $str = str_replace($match['0'][$i], Comment::form(TRUE, $FNS->cached_captcha), $str);
  2092. $str = str_replace('{PREVIEW_TEMPLATE}', $match['2'][$i], $str);
  2093. }
  2094. }
  2095. /** -----------------------------------
  2096. /** Generate Stand-alone Publish form
  2097. /** -----------------------------------*/
  2098. if (preg_match_all("#{{NOCACHE_WEBLOG_FORM(.*?)}}(.+?){{/NOCACHE_FORM}}#s", $str, $match))
  2099. {
  2100. for($i=0, $s=sizeof($match['0']); $i < $s; $i++)
  2101. {
  2102. if ( ! class_exists('Weblog'))
  2103. {
  2104. require PATH_MOD.'weblog/mod.weblog'.EXT;
  2105. }
  2106. $this->tagdata = $match['2'][$i];
  2107. $vars = $FNS->assign_variables($match['2'][$i], '/');
  2108. $this->var_single = $vars['var_single'];
  2109. $this->var_pair = $vars['var_pair'];
  2110. $this->tagparams = $FNS->assign_parameters($match['1'][$i]);
  2111. // Assign sites for the tag
  2112. $this->_fetch_site_ids();
  2113. $XX = new Weblog();
  2114. $str = str_replace($match['0'][$i], $XX->entry_form(TRUE, $FNS->cached_captcha), $str);
  2115. $str = str_replace('{PREVIEW_TEMPLATE}', (isset($_POST['PRV'])) ? $_POST['PRV'] : $this->fetch_param('preview'), $str);
  2116. }
  2117. }
  2118. /** -----------------------------------
  2119. /** Generate Trackback hash if needed
  2120. /** -----------------------------------*/
  2121. if (preg_match("#{NOCACHE_TRACKBACK_HASH}#s", $str, $match))
  2122. {
  2123. if ( ! class_exists('Trackback'))
  2124. {
  2125. require PATH_MOD.'trackback/mod.trackback'.EXT;
  2126. }
  2127. $str = str_replace($match['0'], Trackback::url(TRUE, $FNS->random('alpha', 8)), $str);
  2128. }
  2129. return $str;
  2130. }
  2131. /* END */
  2132. /** -----------------------------------
  2133. /** Parse advanced conditionals conditionals
  2134. /** -----------------------------------*/
  2135. function advanced_conditionals($str)
  2136. {
  2137. global $SESS, $IN, $FNS, $PREFS, $LOC;
  2138. if (stristr($str, LD.'if') === false)
  2139. return $str;
  2140. /* ---------------------------------
  2141. /* Hidden Configuration Variables
  2142. /* - protect_javascript => Prevents advanced conditional parser from processing anything in <script> tags
  2143. /* ---------------------------------*/
  2144. if ($PREFS->ini('protect_javascript') == 'n')
  2145. {
  2146. $this->protect_javascript = FALSE;
  2147. }
  2148. $user_vars = array('member_id', 'group_id', 'group_description', 'group_title', 'username', 'screen_name',
  2149. 'email', 'ip_address', 'location', 'total_entries',
  2150. 'total_comments', 'private_messages', 'total_forum_posts', 'total_forum_topics', 'total_forum_replies');
  2151. for($i=0,$s=sizeof($user_vars), $data = array(); $i < $s; ++$i)
  2152. {
  2153. $data[$user_vars[$i]] = $SESS->userdata[$user_vars[$i]];
  2154. $data['logged_in_'.$user_vars[$i]] = $SESS->userdata[$user_vars[$i]];
  2155. }
  2156. // Define an alternate variable for {group_id} since some tags use
  2157. // it natively, causing it to be unavailable as a global
  2158. $data['member_group'] = $SESS->userdata['group_id'];
  2159. // Logged in and logged out variables
  2160. $data['logged_in'] = ($SESS->userdata['member_id'] == 0) ? 'FALSE' : 'TRUE';
  2161. $data['logged_out'] = ($SESS->userdata['member_id'] != 0) ? 'FALSE' : 'TRUE';
  2162. // current time
  2163. $data['current_time'] = $LOC->now;
  2164. /** ------------------------------------
  2165. /** Member Group in_group('1') function, Super Secret! Shhhhh!
  2166. /** ------------------------------------*/
  2167. if (preg_match_all("/in_group\(([^\)]+)\)/", $str, $matches))
  2168. {
  2169. $groups = (is_array($SESS->userdata['group_id'])) ? $SESS->userdata['group_id'] : array($SESS->userdata['group_id']);
  2170. for($i=0, $s=sizeof($matches[0]); $i < $s; ++$i)
  2171. {
  2172. $check = explode('|', str_replace(array('"', "'"), '', $matches[1][$i]));
  2173. $str = str_replace($matches[0][$i], (sizeof(array_intersect($check, $groups)) > 0) ? 'TRUE' : 'FALSE', $str);
  2174. }
  2175. }
  2176. /** ------------------------------------
  2177. /** Final Prep, Safety On
  2178. /** ------------------------------------*/
  2179. $str = $FNS->prep_conditionals($str, array_merge($this->segment_vars, $this->embed_vars, $this->global_vars, $data), 'y');
  2180. /** ------------------------------------
  2181. /** Protect Already Existing Unparsed PHP
  2182. /** ------------------------------------*/
  2183. $opener = '90Parse89Me34Not18Open';
  2184. $closer = '90Parse89Me34Not18Close';
  2185. $str = str_replace(array('<?', '?'.'>'),
  2186. array($opener.'?', '?'.$closer),
  2187. $str);
  2188. /** ------------------------------------
  2189. /** Protect <script> tags
  2190. /** ------------------------------------*/
  2191. $protected = array();
  2192. $front_protect = '89Protect17';
  2193. $back_protect = '21Me01Please47';
  2194. if ($this->protect_javascript !== FALSE &&
  2195. stristr($str, '<script') &&
  2196. preg_match_all("/<script.*?".">.*?<\/script>/is", $str, $matches))
  2197. {
  2198. for($i=0, $s=sizeof($matches['0']); $i < $s; ++$i)
  2199. {
  2200. $protected[$front_protect.$i.$back_protect] = $matches['0'][$i];
  2201. }
  2202. $str = str_replace(array_values($protected), array_keys($protected), $str);
  2203. }
  2204. /** ------------------------------------
  2205. /** Convert EE Conditionals to PHP
  2206. /** ------------------------------------*/
  2207. $str = str_replace(array(LD.'/if'.RD, LD.'if:else'.RD), array('<?php endif; ?'.'>','<?php else : ?'.'>'), $str);
  2208. $str = preg_replace("/".preg_quote(LD)."((if:(else))*if)\s+(.*?)".preg_quote(RD)."/s", '<?php \\3if(\\4) : ?'.'>', $str);
  2209. $str = $this->parse_template_php($str);
  2210. /** ------------------------------------
  2211. /** Unprotect <script> tags
  2212. /** ------------------------------------*/
  2213. if (sizeof($protected) > 0)
  2214. {
  2215. $str = str_replace(array_keys($protected), array_values($protected), $str);
  2216. }
  2217. /** ------------------------------------
  2218. /** Unprotect Already Existing Unparsed PHP
  2219. /** ------------------------------------*/
  2220. $str = str_replace(array($opener.'?', '?'.$closer),
  2221. array('<'.'?', '?'.'>'),
  2222. $str);
  2223. return $str;
  2224. }
  2225. /* END */
  2226. /** -----------------------------------
  2227. /** Parse segment conditionals
  2228. /** -----------------------------------*/
  2229. function segment_conditionals($str)
  2230. {
  2231. global $SESS, $IN, $FNS;
  2232. if ( ! preg_match("/".LD."if\s+segment_.+".RD."/", $str))
  2233. {
  2234. return $str;
  2235. }
  2236. $this->var_cond = $FNS->assign_conditional_variables($str);
  2237. foreach ($this->var_cond as $val)
  2238. {
  2239. // Make sure this is for a segment conditional
  2240. // And that this is not an advanced conditional
  2241. if ( ! preg_match('/^segment_\d+$/i', $val['3']) OR
  2242. sizeof(preg_split("/(\!=|==|<=|>=|<>|<|>|AND|XOR|OR|&&|\|\|)/", $val['0'])) > 2 OR
  2243. stristr($val['2'], 'if:else') OR
  2244. stristr($val['0'], 'if:else'))
  2245. {
  2246. continue;
  2247. }
  2248. $cond = $FNS->prep_conditional($val['0']);
  2249. $lcond = substr($cond, 0, strpos($cond, ' '));
  2250. $rcond = substr($cond, strpos($cond, ' '));
  2251. if ( ! stristr($rcond, '"') && ! stristr($rcond, "'")) continue;
  2252. $n = substr($val['3'], 8);
  2253. $temp = (isset($IN->SEGS[$n])) ? $IN->SEGS[$n] : '';
  2254. $lcond = str_replace($val['3'], "\$temp", $lcond);
  2255. if (stristr($rcond, '\|') !== FALSE OR stristr($rcond, '&') !== FALSE)
  2256. {
  2257. $rcond = trim($rcond);
  2258. $operator = trim(substr($rcond, 0, strpos($rcond, ' ')));
  2259. $check = trim(substr($rcond, strpos($rcond, ' ')));
  2260. $quote = substr($check, 0, 1);
  2261. if (stristr($rcond, '\|') !== FALSE)
  2262. {
  2263. $array = explode('\|', str_replace($quote, '', $check));
  2264. $break_operator = ' || ';
  2265. }
  2266. else
  2267. {
  2268. $array = explode('&', str_replace($quote, '', $check));
  2269. $break_operator = ' && ';
  2270. }
  2271. $rcond = $operator.' '.$quote;
  2272. $rcond .= implode($quote.$break_operator.$lcond.' '.$operator.' '.$quote, $array).$quote;
  2273. }
  2274. $cond = $lcond.' '.$rcond;
  2275. $cond = str_replace("\|", "|", $cond);
  2276. eval("\$result = (".$cond.");");
  2277. if ($result)
  2278. {
  2279. $str = str_replace($val['1'], $val['2'], $str);
  2280. }
  2281. else
  2282. {
  2283. $str = str_replace($val['1'], '', $str);
  2284. }
  2285. }
  2286. return $str;
  2287. } /* END */
  2288. /** -----------------------------------
  2289. /** Parse Global Vars conditionals
  2290. /** -----------------------------------*/
  2291. function global_vars_conditionals($str)
  2292. {
  2293. return $this->array_conditionals($str, $this->global_vars);
  2294. }
  2295. function array_conditionals($str, $vars = array())
  2296. {
  2297. global $SESS, $IN, $FNS;
  2298. if (sizeof($vars) == 0 OR ! stristr($str, LD.'if'))
  2299. {
  2300. return $str;
  2301. }
  2302. $this->var_cond = $FNS->assign_conditional_variables($str);
  2303. if (sizeof($this->var_cond) == 0)
  2304. {
  2305. return $str;
  2306. }
  2307. foreach ($this->var_cond as $val)
  2308. {
  2309. // Make sure there is such a $global_var
  2310. // And that this is not an advanced conditional
  2311. if ( ! isset($vars[$val['3']]) OR
  2312. sizeof(preg_split("/(\!=|==|<=|>=|<>|<|>|AND|XOR|OR|&&|\|\|)/", $val['0'])) > 2 OR
  2313. stristr($val['2'], 'if:else') OR
  2314. stristr($val['0'], 'if:else'))
  2315. {
  2316. continue;
  2317. }
  2318. $cond = $FNS->prep_conditional($val['0']);
  2319. $lcond = substr($cond, 0, strpos($cond, ' '));
  2320. $rcond = substr($cond, strpos($cond, ' '));
  2321. if ( ! stristr($rcond, '"') && ! stristr($rcond, "'")) continue;
  2322. $temp = $vars[$val['3']];
  2323. $lcond = str_replace($val['3'], "\$temp", $lcond);
  2324. if (stristr($rcond, '\|') !== FALSE OR stristr($rcond, '&') !== FALSE)
  2325. {
  2326. $rcond = trim($rcond);
  2327. $operator = trim(substr($rcond, 0, strpos($rcond, ' ')));
  2328. $check = trim(substr($rcond, strpos($rcond, ' ')));
  2329. $quote = substr($check, 0, 1);
  2330. if (stristr($rcond, '\|') !== FALSE)
  2331. {
  2332. $array = explode('\|', str_replace($quote, '', $check));
  2333. $break_operator = ' || ';
  2334. }
  2335. else
  2336. {
  2337. $array = explode('&', str_replace($quote, '', $check));
  2338. $break_operator = ' && ';
  2339. }
  2340. $rcond = $operator.' '.$quote;
  2341. $rcond .= implode($quote.$break_operator.$lcond.' '.$operator.' '.$quote, $array).$quote;
  2342. }
  2343. $cond = $lcond.' '.$rcond;
  2344. $cond = str_replace("\|", "|", $cond);
  2345. eval("\$result = (".$cond.");");
  2346. if ($result)
  2347. {
  2348. $str = str_replace($val['1'], $val['2'], $str);
  2349. }
  2350. else
  2351. {
  2352. $str = str_replace($val['1'], '', $str);
  2353. }
  2354. }
  2355. return $str;
  2356. } /* END */
  2357. /** ----------------------------------
  2358. /** Add an Item to the Template Log
  2359. /** ----------------------------------*/
  2360. function log_item($str)
  2361. {
  2362. global $SESS;
  2363. if ($this->debugging !== TRUE OR $SESS->userdata['group_id'] != 1)
  2364. {
  2365. return;
  2366. }
  2367. if ($this->depth > 0)
  2368. {
  2369. $str = str_repeat('&nbsp;', $this->depth * 5).$str;
  2370. }
  2371. if (phpversion() < 5)
  2372. {
  2373. list($usec, $sec) = explode(" ", microtime());
  2374. $time = ((float)$usec + (float)$sec) - $this->start_microtime;
  2375. }
  2376. else
  2377. {
  2378. $time = microtime(TRUE)-$this->start_microtime;
  2379. }
  2380. $this->log[] = '('.number_format($time, 6).') '.$str;
  2381. }
  2382. /* END */
  2383. /** ----------------------------------
  2384. /** Template Authentication - Basic
  2385. /** ----------------------------------*/
  2386. function template_authentication_basic()
  2387. {
  2388. global $PREFS, $OUT;
  2389. @header('WWW-Authenticate: Basic realm="'.$this->realm.'"');
  2390. $OUT->http_status_header(401);
  2391. @header("Date: ".gmdate("D, d M Y H:i:s")." GMT");
  2392. exit("HTTP/1.0 401 Unauthorized");
  2393. }
  2394. /* END */
  2395. /** ----------------------------------
  2396. /** Template Authentication - Digest
  2397. /** ----------------------------------*/
  2398. function template_authentication_digest()
  2399. {
  2400. global $PREF, $OUT;
  2401. @header('WWW-Authenticate: Digest realm="'.$this->realm.'",gop="auth", nonce="'.uniqid('').'", opaque="'.md5($this->realm).'"');
  2402. $OUT->http_status_header(401);
  2403. @header("Date: ".gmdate("D, d M Y H:i:s")." GMT");
  2404. exit("HTTP/1.0 401 Unauthorized");
  2405. }
  2406. /* END */
  2407. /** ----------------------------------
  2408. /** Check Template Authentication - Digest
  2409. /** ----------------------------------*/
  2410. function template_authentication_check_digest($not_allowed_groups = array())
  2411. {
  2412. global $DB, $SESS, $PREFS, $FNS;
  2413. if ( ! in_array('2', $not_allowed_groups))
  2414. {
  2415. $not_allowed_groups[] = 2;
  2416. $not_allowed_groups[] = 3;
  2417. $not_allowed_groups[] = 4;
  2418. }
  2419. if (empty($_SERVER) OR ! isset($_SERVER['PHP_AUTH_DIGEST']))
  2420. {
  2421. return FALSE;
  2422. }
  2423. $required = array('uri' => '',
  2424. 'response' => '',
  2425. 'realm' => $this->realm,
  2426. 'username' => '',
  2427. 'nonce' => 1,
  2428. 'nc' => 1,
  2429. 'cnonce' => 1,
  2430. 'qop' => 1);
  2431. $params = $FNS->assign_parameters($_SERVER['PHP_AUTH_DIGEST']);
  2432. extract($required);
  2433. extract($params);
  2434. /** ----------------------------------------
  2435. /** Check password lockout status
  2436. /** ----------------------------------------*/
  2437. if ($SESS->check_password_lockout() === TRUE)
  2438. {
  2439. return FALSE;
  2440. }
  2441. /** ----------------------------------
  2442. /** Validate Username and Password
  2443. /** ----------------------------------*/
  2444. $query = $DB->query("SELECT password, group_id FROM exp_members WHERE username = '".$DB->escape_str($username)."'");
  2445. if ($query->num_rows == 0)
  2446. {
  2447. $SESS->save_password_lockout();
  2448. return FALSE;
  2449. }
  2450. if (in_array($query->row['group_id'], $not_allowed_groups))
  2451. {
  2452. return FALSE;
  2453. }
  2454. $parts = array(
  2455. md5($username.':'.$realm.':'.$query->row['password']),
  2456. md5($_SERVER['REQUEST_METHOD'].':'.$uri)
  2457. );
  2458. $valid_response = md5($parts['0'].':'.$nonce.':'.$nc.':'.$cnonce.':'.$qop.':'.$parts['1']);
  2459. if ($valid_response == $response)
  2460. {
  2461. return TRUE;
  2462. }
  2463. else
  2464. {
  2465. $SESS->save_password_lockout();
  2466. return FALSE;
  2467. }
  2468. }
  2469. /* END */
  2470. /** ----------------------------------
  2471. /** Check Template Authentication - Basic
  2472. /** ----------------------------------*/
  2473. function template_authentication_check_basic($not_allowed_groups = array())
  2474. {
  2475. global $DB, $SESS, $PREFS, $FNS;
  2476. if ( ! in_array('2', $not_allowed_groups))
  2477. {
  2478. $not_allowed_groups[] = 2;
  2479. $not_allowed_groups[] = 3;
  2480. $not_allowed_groups[] = 4;
  2481. }
  2482. /** ----------------------------------
  2483. /** Find Username, Please
  2484. /** ----------------------------------*/
  2485. if ( ! empty($_SERVER) && isset($_SERVER['PHP_AUTH_USER']))
  2486. {
  2487. $user = $_SERVER['PHP_AUTH_USER'];
  2488. }
  2489. elseif ( !empty($_ENV) && isset($_ENV['REMOTE_USER']))
  2490. {
  2491. $user = $_ENV['REMOTE_USER'];
  2492. }
  2493. elseif ( @getenv('REMOTE_USER'))
  2494. {
  2495. $user = getenv('REMOTE_USER');
  2496. }
  2497. elseif ( ! empty($_ENV) && isset($_ENV['AUTH_USER']))
  2498. {
  2499. $user = $_ENV['AUTH_USER'];
  2500. }
  2501. elseif ( @getenv('AUTH_USER'))
  2502. {
  2503. $user = getenv('AUTH_USER');
  2504. }
  2505. /** ----------------------------------
  2506. /** Find Password, Please
  2507. /** ----------------------------------*/
  2508. if ( ! empty($_SERVER) && isset($_SERVER['PHP_AUTH_PW']))
  2509. {
  2510. $pass = $_SERVER['PHP_AUTH_PW'];
  2511. }
  2512. elseif ( ! empty($_ENV) && isset($_ENV['REMOTE_PASSWORD']))
  2513. {
  2514. $pass = $_ENV['REMOTE_PASSWORD'];
  2515. }
  2516. elseif ( @getenv('REMOTE_PASSWORD'))
  2517. {
  2518. $pass = getenv('REMOTE_PASSWORD');
  2519. }
  2520. elseif ( ! empty($_ENV) && isset($_ENV['AUTH_PASSWORD']))
  2521. {
  2522. $pass = $_ENV['AUTH_PASSWORD'];
  2523. }
  2524. elseif ( @getenv('AUTH_PASSWORD'))
  2525. {
  2526. $pass = getenv('AUTH_PASSWORD');
  2527. }
  2528. /** ----------------------------------
  2529. /** Authentication for IIS
  2530. /** ----------------------------------*/
  2531. if ( ! isset ($user) OR ! isset($pass) OR (empty($user) && empty($pass)))
  2532. {
  2533. if ( isset($_SERVER['HTTP_AUTHORIZATION']) && substr($_SERVER['HTTP_AUTHORIZATION'], 0, 6) == 'Basic ')
  2534. {
  2535. list($user, $pass) = explode(':', base64_decode(substr($HTTP_AUTHORIZATION, 6)));
  2536. }
  2537. elseif ( ! empty($_ENV) && isset($_ENV['HTTP_AUTHORIZATION']) && substr($_ENV['HTTP_AUTHORIZATION'], 0, 6) == 'Basic ')
  2538. {
  2539. list($user, $pass) = explode(':', base64_decode(substr($_ENV['HTTP_AUTHORIZATION'], 6)));
  2540. }
  2541. elseif (@getenv('HTTP_AUTHORIZATION') && substr(getenv('HTTP_AUTHORIZATION'), 0, 6) == 'Basic ')
  2542. {
  2543. list($user, $pass) = explode(':', base64_decode(substr(getenv('HTTP_AUTHORIZATION'), 6)));
  2544. }
  2545. }
  2546. /** ----------------------------------
  2547. /** Authentication for FastCGI
  2548. /** ----------------------------------*/
  2549. if ( ! isset ($user) OR ! isset($pass) OR (empty($user) && empty($pass)))
  2550. {
  2551. if (!empty($_ENV) && isset($_ENV['Authorization']) && substr($_ENV['Authorization'], 0, 6) == 'Basic ')
  2552. {
  2553. list($user, $pass) = explode(':', base64_decode(substr($_ENV['Authorization'], 6)));
  2554. }
  2555. elseif (@getenv('Authorization') && substr(getenv('Authorization'), 0, 6) == 'Basic ')
  2556. {
  2557. list($user, $pass) = explode(':', base64_decode(substr(getenv('Authorization'), 6)));
  2558. }
  2559. }
  2560. if ( ! isset ($user) OR ! isset($pass) OR (empty($user) && empty($pass)))
  2561. {
  2562. return FALSE;
  2563. }
  2564. /** ----------------------------------------
  2565. /** Check password lockout status
  2566. /** ----------------------------------------*/
  2567. if ($SESS->check_password_lockout() === TRUE)
  2568. {
  2569. return FALSE;
  2570. }
  2571. /** ----------------------------------
  2572. /** Validate Username and Password
  2573. /** ----------------------------------*/
  2574. $query = $DB->query("SELECT password, group_id FROM exp_members WHERE username = '".$DB->escape_str($user)."'");
  2575. if ($query->num_rows == 0)
  2576. {
  2577. $SESS->save_password_lockout();
  2578. return FALSE;
  2579. }
  2580. if (in_array($query->row['group_id'], $not_allowed_groups))
  2581. {
  2582. return FALSE;
  2583. }
  2584. if ($query->row['password'] == $FNS->hash(stripslashes($pass)))
  2585. {
  2586. return TRUE;
  2587. }
  2588. $orig_enc_type = $PREFS->ini('encryption_type');
  2589. $PREFS->core_ini['encryption_type'] = ($PREFS->ini('encryption_type') == 'md5') ? 'sha1' : 'md5';
  2590. if ($query->row['password'] == $FNS->hash(stripslashes($pass)))
  2591. {
  2592. return TRUE;
  2593. }
  2594. else
  2595. {
  2596. $SESS->save_password_lockout();
  2597. return FALSE;
  2598. }
  2599. }
  2600. /* END */
  2601. /** ---------------------------------------
  2602. /** Fetch Template site id's for tags
  2603. /** ---------------------------------------*/
  2604. function _fetch_site_ids()
  2605. {
  2606. global $DB, $PREFS;
  2607. $this->site_ids = array();
  2608. if (isset($this->tagparams['site']))
  2609. {
  2610. if (sizeof($this->sites) == 0 && $PREFS->ini('multiple_sites_enabled') == 'y')
  2611. {
  2612. $sites_query = $DB->query("SELECT site_id, site_name FROM exp_sites");
  2613. foreach($sites_query->result as $row)
  2614. {
  2615. $this->sites[$row['site_id']] = $row['site_name'];
  2616. }
  2617. }
  2618. if (substr($this->tagparams['site'], 0, 4) == 'not ')
  2619. {
  2620. $sites = array_diff($this->sites, explode('|', substr($this->tagparams['site'], 4)));
  2621. }
  2622. else
  2623. {
  2624. $sites = array_intersect($this->sites, explode('|', $this->tagparams['site']));
  2625. }
  2626. // Let us hear it for the preservation of array keys!
  2627. $this->site_ids = array_flip($sites);
  2628. }
  2629. // If no sites were assigned via parameter, then we use the current site's
  2630. // Templates, Weblogs, and various Site data
  2631. if (sizeof($this->site_ids) == 0)
  2632. {
  2633. $this->site_ids[] = $PREFS->ini('site_id');
  2634. }
  2635. }
  2636. }
  2637. // END CLASS
  2638. ?>