PageRenderTime 73ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/template.php

https://code.google.com/p/enanocms/
PHP | 3430 lines | 2340 code | 452 blank | 638 comment | 391 complexity | 64bfb35a8d199d6f57077d3ffa51c932 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /*
  3. * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
  4. * Copyright (C) 2006-2009 Dan Fuhry
  5. *
  6. * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
  7. * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  10. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
  11. */
  12. class template
  13. {
  14. var $tpl_strings, $tpl_bool, $vars_assign_history, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list, $default_theme, $default_style, $plugin_blocks, $plugin_blocks_content, $namespace_string, $style_list, $theme_loaded;
  15. var $theme_initted = false;
  16. var $page_initted = false;
  17. var $elements = false;
  18. var $page_id = false;
  19. var $namespace = false;
  20. var $js_preload = array();
  21. var $js_append = '';
  22. /**
  23. * Page action conditions
  24. * @var array
  25. */
  26. var $conds = array();
  27. /**
  28. * The PageProcessor for the current page
  29. * @var object
  30. */
  31. var $page = false;
  32. /**
  33. * The list of themes that are critical for Enano operation. This doesn't include oxygen which
  34. * remains a user theme. By default this is admin and printable which have to be loaded on demand.
  35. * @var array
  36. */
  37. var $system_themes = array('admin', 'printable');
  38. /**
  39. * Set to true if the site is disabled and thus a message needs to be shown. This should ONLY be changed by common.php.
  40. * @var bool
  41. * @access private
  42. */
  43. var $site_disabled = false;
  44. /**
  45. * One of the absolute best parts of Enano :-P
  46. * @var string
  47. */
  48. var $fading_button = '';
  49. /**
  50. * Context stack. You can save and restore the var set.
  51. * @var array
  52. */
  53. var $context_stack = array('bool' => array(), 'str' => array(), 'history' => array());
  54. function __construct()
  55. {
  56. global $db, $session, $paths, $template, $plugins; // Common objects
  57. global $cache;
  58. $this->tpl_bool = Array();
  59. $this->tpl_strings = Array();
  60. $this->sidebar_extra = '';
  61. $this->toolbar_menu = '';
  62. $this->additional_headers = '';
  63. $this->plugin_blocks = Array();
  64. $this->plugin_blocks_content = array();
  65. $this->theme_loaded = false;
  66. $this->theme_list = Array();
  67. $this->named_theme_list = Array();
  68. $this->vars_assign_history = array(
  69. 'strings' => array(),
  70. 'bool' => array()
  71. );
  72. if ( defined('IN_ENANO_UPGRADE') )
  73. {
  74. return $this->construct_compat();
  75. }
  76. if ( !$this->theme_list = $cache->fetch('themes') )
  77. {
  78. $q = $db->sql_query('SELECT theme_id, theme_name, enabled, default_style, group_policy, group_list FROM ' . table_prefix . 'themes;');
  79. if ( !$q )
  80. $db->_die('template.php selecting theme list');
  81. $i = 0;
  82. while ( $row = $db->fetchrow() )
  83. {
  84. $this->theme_list[$i] = $row;
  85. $i++;
  86. }
  87. unset($theme);
  88. $this->theme_list = array_values($this->theme_list);
  89. $cache->store('themes', $this->theme_list, -1);
  90. }
  91. // Create associative array of themes
  92. foreach ( $this->theme_list as $i => &$theme )
  93. $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i];
  94. unset($theme);
  95. $this->default_theme = ( $_ = getConfig('theme_default') ) ? $_ : $this->theme_list[0]['theme_id'];
  96. $this->named_theme_list[ $this->default_theme ]['css'] = $this->get_theme_css_files($this->default_theme);
  97. // Come up with the default style. If the CSS file specified in default_style exists, we're good, just
  98. // use that. Otherwise, use the first stylesheet that comes to mind.
  99. $df_data =& $this->named_theme_list[ $this->default_theme ];
  100. $this->default_style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0];
  101. }
  102. /**
  103. * Save the current context and start a blank one.
  104. */
  105. function context_push()
  106. {
  107. array_push($this->context_stack['str'], $this->tpl_strings);
  108. array_push($this->context_stack['bool'], $this->tpl_bool);
  109. array_push($this->context_stack['history'], $this->vars_assign_history);
  110. }
  111. function context_pop()
  112. {
  113. $this->tpl_strings = array_pop($this->context_stack['str']);
  114. $this->tpl_bool = array_pop($this->context_stack['bool']);
  115. $this->vars_assign_history = array_pop($this->context_stack['history']);
  116. }
  117. /**
  118. * Gets the list of available CSS files (styles) for the specified theme.
  119. * @param string Theme ID
  120. * @return array
  121. */
  122. function get_theme_css_files($theme_id)
  123. {
  124. $css = array();
  125. $dir = ENANO_ROOT . "/themes/{$theme_id}/css";
  126. if ( $dh = @opendir($dir) )
  127. {
  128. while ( ( $file = @readdir($dh) ) !== false )
  129. {
  130. if ( preg_match('/\.css$/', $file) )
  131. $css[] = preg_replace('/\.css$/', '', $file);
  132. }
  133. closedir($dh);
  134. }
  135. // No CSS files? If so, nuke it.
  136. if ( count($css) < 1 )
  137. {
  138. unset($this->theme_list[$theme_id]);
  139. }
  140. return $css;
  141. }
  142. /**
  143. * Failsafe constructor for upgrades.
  144. */
  145. function construct_compat()
  146. {
  147. global $db, $session, $paths, $template, $plugins; // Common objects
  148. $this->tpl_bool = Array();
  149. $this->tpl_strings = Array();
  150. $this->sidebar_extra = '';
  151. $this->toolbar_menu = '';
  152. $this->additional_headers = '';
  153. $this->plugin_blocks = Array();
  154. $this->theme_loaded = false;
  155. $this->fading_button = '<div style="background-image: url('.scriptPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
  156. <a style="background-image: none; padding-right: 0;" href="http://enanocms.org/" onclick="window.open(this.href); return false;"><img style="border-width: 0;" alt=" " src="'.scriptPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
  157. </div>';
  158. $this->theme_list = Array();
  159. $this->named_theme_list = Array();
  160. $q = $db->sql_query('SELECT theme_id, theme_name, enabled, default_style FROM ' . table_prefix . 'themes;');
  161. if ( !$q )
  162. $db->_die('template.php selecting theme list');
  163. $i = 0;
  164. while ( $row = $db->fetchrow() )
  165. {
  166. $this->theme_list[$i] = $row;
  167. $i++;
  168. }
  169. // List out all CSS files for this theme
  170. foreach ( $this->theme_list as $i => &$theme )
  171. {
  172. $theme['css'] = array();
  173. $dir = ENANO_ROOT . "/themes/{$theme['theme_id']}/css";
  174. if ( $dh = @opendir($dir) )
  175. {
  176. while ( ( $file = @readdir($dh) ) !== false )
  177. {
  178. if ( preg_match('/\.css$/', $file) )
  179. $theme['css'][] = preg_replace('/\.css$/', '', $file);
  180. }
  181. closedir($dh);
  182. }
  183. // No CSS files? If so, nuke it.
  184. if ( count($theme['css']) < 1 )
  185. {
  186. unset($this->theme_list[$i]);
  187. }
  188. }
  189. $this->theme_list = array_values($this->theme_list);
  190. // Create associative array of themes
  191. foreach ( $this->theme_list as $i => &$theme )
  192. $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i];
  193. $this->default_theme = ( $_ = getConfig('theme_default') ) ? $_ : $this->theme_list[0]['theme_id'];
  194. // Come up with the default style. If the CSS file specified in default_style exists, we're good, just
  195. // use that. Otherwise, use the first stylesheet that comes to mind.
  196. $df_data =& $this->named_theme_list[ $this->default_theme ];
  197. $this->default_style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0];
  198. }
  199. /**
  200. * Systematically deletes themes from available list if they're blocked by theme security settings. Called when session->start() finishes.
  201. */
  202. function process_theme_acls()
  203. {
  204. global $db, $session, $paths, $template, $plugins; // Common objects
  205. global $lang;
  206. // generate the fading button - needs to be done after sessions are started
  207. $admintitle = ( $session->user_level >= USER_LEVEL_ADMIN && is_object(@$lang) ) ? ' title="' . $lang->get('sidebar_btn_enanopowered_admin_tip') . '"' : '';
  208. $this->fading_button = '<div style="background-image: url('.cdnPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
  209. <a style="background-image: none; padding-right: 0;" href="http://enanocms.org/" onclick="window.open(this.href); return false;"' . $admintitle . '><img style="border-width: 0;" alt=" " src="'.cdnPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
  210. </div>';
  211. // For each theme, check ACLs and delete from RAM if not authorized
  212. foreach ( $this->theme_list as $i => $theme )
  213. {
  214. if ( !@$theme['group_list'] )
  215. continue;
  216. if ( $theme['theme_id'] === getConfig('theme_default') )
  217. continue;
  218. switch ( $theme['group_policy'] )
  219. {
  220. case 'allow_all':
  221. // Unconditionally allowed
  222. continue;
  223. break;
  224. case 'whitelist':
  225. // If we're not on the list, off to the left please
  226. $list = enano_json_decode($theme['group_list']);
  227. $allowed = false;
  228. foreach ( $list as $acl )
  229. {
  230. if ( !preg_match('/^(u|g):([0-9]+)$/', $acl, $match) )
  231. // Invalid list entry, silently allow (maybe not a good idea but
  232. // really, these things are checked before they're inserted)
  233. continue 2;
  234. $mode = $match[1];
  235. $id = intval($match[2]);
  236. switch ( $mode )
  237. {
  238. case 'u':
  239. $allowed = ( $id == $session->user_id );
  240. if ( $allowed )
  241. break 2;
  242. break;
  243. case 'g':
  244. $allowed = ( isset($session->groups[$id]) );
  245. if ( $allowed )
  246. break 2;
  247. }
  248. }
  249. if ( !$allowed )
  250. {
  251. unset($this->theme_list[$i]);
  252. }
  253. break;
  254. case 'blacklist':
  255. // If we're ON the list, off to the left please
  256. $list = enano_json_decode($theme['group_list']);
  257. $allowed = true;
  258. foreach ( $list as $acl )
  259. {
  260. if ( !preg_match('/^(u|g):([0-9]+)$/', $acl, $match) )
  261. // Invalid list entry, silently allow (maybe not a good idea but
  262. // really, these things are checked before they're inserted)
  263. continue 2;
  264. $mode = $match[1];
  265. $id = intval($match[2]);
  266. switch ( $mode )
  267. {
  268. case 'u':
  269. $allowed = ( $id != $session->user_id );
  270. if ( !$allowed )
  271. break 2;
  272. break;
  273. case 'g':
  274. $allowed = ( !isset($session->groups[$id]) );
  275. if ( !$allowed )
  276. break 2;
  277. }
  278. }
  279. if ( !$allowed )
  280. {
  281. unset($this->theme_list[$i]);
  282. }
  283. break;
  284. }
  285. }
  286. $this->theme_list = array_values($this->theme_list);
  287. // Rebuild associative theme list
  288. $this->named_theme_list = array();
  289. foreach ( $this->theme_list as $i => &$theme )
  290. $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i];
  291. }
  292. /**
  293. * Register a new sidebar block.
  294. * @param string Block title
  295. * @param string Block HTML
  296. * @param bool If true, the class of the block will be the same as the one used for blocks that are a list of links instead of HTML content. This can do some wacky things like make all your <a>s block level.
  297. */
  298. function sidebar_widget($t, $h, $use_normal_section = false)
  299. {
  300. global $db, $session, $paths, $template, $plugins; // Common objects
  301. if ( !$this->theme_loaded )
  302. {
  303. $this->load_theme($session->theme, $session->style);
  304. }
  305. if(!$this->sidebar_widgets)
  306. $this->sidebar_widgets = '';
  307. $tplvars = $this->extract_vars('elements.tpl');
  308. if ( $use_normal_section )
  309. {
  310. $parser = $this->makeParserText($tplvars['sidebar_section']);
  311. }
  312. else
  313. {
  314. $parser = $this->makeParserText($tplvars['sidebar_section_raw']);
  315. }
  316. $parser->assign_vars(Array('TITLE' => '{TITLE}','CONTENT' => $h));
  317. $this->plugin_blocks[$t] = $parser->run();
  318. $this->plugin_blocks_content[$t] = $h;
  319. $this->sidebar_widgets .= $parser->run();
  320. }
  321. function add_header($html)
  322. {
  323. /* debug only **
  324. $bt = debug_backtrace();
  325. $bt = $bt[1];
  326. $this->additional_headers .= "\n <!-- {$bt['file']}:{$bt['line']} -->\n " . $html;
  327. */
  328. $this->additional_headers .= "\n " . $html;
  329. }
  330. function get_css($s = false)
  331. {
  332. global $db, $session, $paths, $template, $plugins; // Common objects
  333. $this->init_vars();
  334. $path = ( $s ) ? 'css/'.$s : 'css/'.$this->style.'.css';
  335. if ( !file_exists(ENANO_ROOT . '/themes/' . $this->theme . '/' . $path) )
  336. {
  337. echo "/* WARNING: Falling back to default file because file $path does not exist */\n";
  338. $path = 'css/' . $this->style_list[0] . '.css';
  339. }
  340. return $this->process_template($path);
  341. }
  342. function load_theme($name = false, $css = false)
  343. {
  344. global $db, $session, $paths, $template, $plugins; // Common objects
  345. $this->theme = ( $name ) ? $name : $session->theme;
  346. $this->style = ( $css ) ? $css : $session->style;
  347. if ( !$this->theme )
  348. {
  349. $this->theme = $this->theme_list[0]['theme_id'];
  350. $this->style = preg_replace('/\.css$/', '', $this->theme_list[0]['default_style']);
  351. }
  352. // Make sure we're allowed to use this theme.
  353. if ( (
  354. // If it was removed, it's probably blocked by an ACL, or it was uninstalled
  355. !isset($this->named_theme_list[$this->theme]) ||
  356. // Check if the theme is disabled
  357. ( isset($this->named_theme_list[$this->theme]) && isset($this->named_theme_list[$this->theme]['enabled']) && $this->named_theme_list[$this->theme]['enabled'] == 0 ) )
  358. // Above all, if it's a system theme, don't inhibit the loading process.
  359. && !in_array($this->theme, $this->system_themes)
  360. )
  361. {
  362. // No, something is preventing it - fall back to site default
  363. $this->theme = $this->default_theme;
  364. // Come up with the default style. If the CSS file specified in default_style exists, we're good, just
  365. // use that. Otherwise, use the first stylesheet that comes to mind.
  366. $df_data =& $this->named_theme_list[ $this->theme ];
  367. $this->style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0];
  368. }
  369. // The list of styles for the currently selected theme
  370. $this->style_list =& $this->named_theme_list[ $this->theme ]['css'];
  371. $this->theme_loaded = true;
  372. }
  373. /**
  374. * Change the theme we're supposed to display.
  375. * @param string Theme name
  376. * @param string Style name; optional
  377. */
  378. function set_theme($theme = false, $style = false)
  379. {
  380. $this->theme_initted = false;
  381. $this->load_theme($theme, $style);
  382. }
  383. /**
  384. * Change the page we're supposed to generate for
  385. * @param mixed Page ID *or* PageProcessor. If a PageProcessor, pulls permission info and such from that; if not, starts a PageProcessor. YOU SHOULD USE A PageProcessor WHENEVER POSSIBLE! It improves efficiency.
  386. * @param string Namespace; not required if including a PageProcessor.
  387. */
  388. function set_page($page_id_or_pp, $namespace = false)
  389. {
  390. global $paths;
  391. if ( is_object($page_id_or_pp) && get_class($page_id_or_pp) === 'PageProcessor' )
  392. {
  393. $this->page_initted = false;
  394. $page =& $page_id_or_pp;
  395. $this->page = $page;
  396. $this->page_id = $page->page_id;
  397. $this->namespace = $page->namespace;
  398. }
  399. else if ( is_string($page_id_or_pp) )
  400. {
  401. if ( !is_string($namespace) )
  402. return false;
  403. if ( $page_id_or_pp === $this->page_id && $namespace === $this->namespace )
  404. return true;
  405. $this->page_initted = false;
  406. $this->page = false;
  407. $this->page_id = sanitize_page_id($page_id_or_pp);
  408. $this->namespace = $namespace;
  409. }
  410. else
  411. {
  412. return false;
  413. }
  414. $this->assign_vars(array(
  415. 'PAGE_URLNAME' => $paths->get_pathskey($this->page_id, $this->namespace)
  416. ));
  417. return true;
  418. }
  419. /**
  420. * Queue a Javascript file to be loaded with the page instead of on demand.
  421. * @param mixed Javascript file string or array thereof, extensions are optional
  422. * @example
  423. <code>
  424. $template->preload_js(array('jquery', 'jquery-ui'));
  425. $template->preload_js('admin-menu.js');
  426. </code>
  427. * @return null
  428. */
  429. function preload_js($filemixed)
  430. {
  431. if ( is_string($filemixed) )
  432. $files = array($filemixed);
  433. else if ( is_array($filemixed) )
  434. $files = $filemixed;
  435. else
  436. // :-/
  437. return null;
  438. $this->js_preload = array_values(array_merge($this->js_preload, $files));
  439. }
  440. /**
  441. * Queue some HTML to be inserted after the Javascript runtime.
  442. * @param string HTML glob
  443. */
  444. function add_header_js($html)
  445. {
  446. $this->js_append .= "$html\n ";
  447. }
  448. /**
  449. * Global, only-called-once init. Goes to all themes.
  450. */
  451. function init_global_vars()
  452. {
  453. global $db, $session, $paths, $template, $plugins; // Common objects
  454. global $lang;
  455. global $email;
  456. $is_opera = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'Opera')) ? true : false;
  457. $is_msie = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ? true : false;
  458. $this->assign_bool(array(
  459. 'auth_admin' => $session->user_level >= USER_LEVEL_ADMIN ? true : false,
  460. 'user_logged_in' => $session->user_logged_in,
  461. 'opera' => $is_opera,
  462. 'msie' => $is_msie,
  463. 'registration_disabled' => getConfig('account_activation', 'none') === 'disable'
  464. ));
  465. if ( $session->sid_super )
  466. {
  467. $ash = '&amp;auth=' . $session->sid_super;
  468. $asq = "?auth=" . $session->sid_super;
  469. $asa = "&auth=" . $session->sid_super;
  470. $as2 = htmlspecialchars(urlSeparator) . 'auth='.$session->sid_super;
  471. }
  472. else
  473. {
  474. $asq = '';
  475. $asa = '';
  476. $as2 = '';
  477. $ash = '';
  478. }
  479. // Append the Enano version to URLs to break the cache on upgrades
  480. $enano_version = enano_version();
  481. // Set up javascript includes
  482. // these depend heavily on whether we have a CDN to work with or not
  483. if ( getConfig('cdn_path') )
  484. {
  485. // we're on a CDN, point to static includes
  486. // CLI javascript compression script: includes/clientside/jscompress.php
  487. $js_head = '<script type="text/javascript" src="' . cdnPath . '/includes/clientside/static/enano-lib-basic.js?' . $enano_version . '"></script>';
  488. if ( !empty($this->js_preload) )
  489. {
  490. $loadlines = array();
  491. // make unique
  492. foreach ( $this->js_preload as &$script )
  493. {
  494. $script = preg_replace('/\.js$/', '', $script) . '.js';
  495. }
  496. unset($script);
  497. $this->js_preload = array_unique($this->js_preload);
  498. foreach ( $this->js_preload as $script )
  499. {
  500. $js_head .= "\n <script type=\"text/javascript\" src=\"" . cdnPath . "/includes/clientside/static/$script?$enano_version\"></script>";
  501. // special case for l10n: also load strings
  502. if ( $script == 'l10n.js' )
  503. {
  504. global $lang;
  505. $js_head .= "\n <script type=\"text/javascript\" src=\"" . makeUrlNS("Special", "LangExportJSON/$lang->lang_id", "$enano_version") . "\"></script>";
  506. }
  507. $loadlines[] = "loaded_components['$script'] = true;";
  508. }
  509. // tell the system that this stuff is already loaded
  510. $loadlines = implode("\n ", $loadlines);
  511. $js_head .= "\n <script type=\"text/javascript\">
  512. var loaded_components = loaded_components || {};
  513. $loadlines
  514. </script>";
  515. }
  516. $js_foot = <<<JSEOF
  517. <script type="text/javascript">
  518. // This initializes the Javascript runtime when the DOM is ready - not when the page is
  519. // done loading, because enano-lib-basic still has to load some 15 other script files
  520. // check for the init function - this is a KHTML fix
  521. // This doesn't seem to work properly in IE in 1.1.x - there are some problems with
  522. // tinyMCE and l10n.
  523. if ( typeof ( enano_init ) == 'function' && !IE )
  524. {
  525. enano_init();
  526. window.onload = function(e) { };
  527. }
  528. </script>
  529. $this->js_append
  530. JSEOF;
  531. }
  532. else
  533. {
  534. $cdnpath = cdnPath;
  535. $js_head = '';
  536. // point to jsres compressor
  537. $js_head .= <<<JSEOF
  538. <!-- Only load a basic set of functions for now. Let the rest of the API load when the page is finished. -->
  539. <script type="text/javascript" src="$cdnpath/includes/clientside/jsres.php?early&amp;$enano_version"></script>
  540. JSEOF;
  541. $js_foot = '';
  542. if ( !empty($this->js_preload) )
  543. {
  544. foreach ( $this->js_preload as &$script )
  545. {
  546. $script = preg_replace('/\.js$/', '', $script) . '.js';
  547. }
  548. $this->js_preload = array_unique($this->js_preload);
  549. if ( in_array('l10n.js', $this->js_preload) )
  550. {
  551. // special case for l10n: also load strings
  552. global $lang;
  553. $js_foot .= "\n <script type=\"text/javascript\" src=\"" . makeUrlNS("Special", "LangExportJSON/$lang->lang_id", $enano_version, true) . "\"></script>";
  554. }
  555. $scripts = implode(',', $this->js_preload);
  556. $js_foot .= "\n <script type=\"text/javascript\" src=\"" . cdnPath . "/includes/clientside/jsres.php?f=$scripts&amp;$enano_version\"></script>";
  557. }
  558. $js_foot .= <<<JSEOF
  559. <!-- jsres.php is a wrapper script that compresses and caches single JS files to minimize requests -->
  560. <script type="text/javascript" src="$cdnpath/includes/clientside/jsres.php?$enano_version"></script>
  561. <script type="text/javascript">//<![CDATA[
  562. // This initializes the Javascript runtime when the DOM is ready - not when the page is
  563. // done loading, because enano-lib-basic still has to load some 15 other script files
  564. // check for the init function - this is a KHTML fix
  565. // This doesn't seem to work properly in IE in 1.1.x - there are some problems with
  566. // tinyMCE and l10n.
  567. if ( typeof ( enano_init ) == 'function' && !IE )
  568. {
  569. enano_init();
  570. window.onload = function(e) { };
  571. }
  572. //]]></script>
  573. $this->js_append
  574. JSEOF;
  575. }
  576. $this->assign_bool(array(
  577. 'fixed_menus' => false,
  578. 'export' => false,
  579. 'right_sidebar' => true,
  580. 'enable_uploads' => ( getConfig('enable_uploads') == '1' && $session->get_permissions('upload_files') ) ? true : false,
  581. 'stupid_mode' => false,
  582. ));
  583. // Add the e-mail address client code to the header
  584. $this->add_header($email->jscode());
  585. // Assign our main variables
  586. $this->assign_vars(array(
  587. 'SITE_NAME' => htmlspecialchars(getConfig('site_name')),
  588. 'USERNAME' => $session->username,
  589. 'SITE_DESC' => htmlspecialchars(getConfig('site_desc')),
  590. 'SCRIPTPATH' => scriptPath,
  591. 'CONTENTPATH' => contentPath,
  592. 'CDNPATH' => cdnPath,
  593. 'ADMIN_SID_QUES' => $asq,
  594. 'ADMIN_SID_AMP' => $asa,
  595. 'ADMIN_SID_AMP_HTML' => $ash,
  596. 'ADMIN_SID_AUTO' => $as2,
  597. 'ADMIN_SID_RAW' => ( is_string($session->sid_super) ? $session->sid_super : '' ),
  598. 'CSRF_TOKEN' => $session->csrf_token,
  599. 'COPYRIGHT' => RenderMan::parse_internal_links(getConfig('copyright_notice')),
  600. 'REQUEST_URI' => ( defined('ENANO_CLI') ? '' : $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] ),
  601. 'SEARCH_ACTION' => makeUrlNS('Special', 'Search'),
  602. 'INPUT_TITLE' => ( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars( $paths->get_pathskey($this->page_id, $this->namespace) ) . '" />' : ''),
  603. 'INPUT_AUTH' => ( $session->sid_super ? '<input type="hidden" name="auth" value="' . $session->sid_super . '" />' : ''),
  604. 'MAIN_PAGE' => get_main_page(),
  605. 'UNREAD_PMS' => $session->unread_pms,
  606. 'JS_HEADER' => $js_head,
  607. 'JS_FOOTER' => $js_foot,
  608. 'URL_ABOUT_ENANO' => makeUrlNS('Special', 'About_Enano', '', true),
  609. 'ENANO_VERSION' => enano_version()
  610. ), true);
  611. $tpl_strings = array();
  612. foreach ( $paths->nslist as $ns_id => $ns_prefix )
  613. {
  614. $tpl_strings[ 'NS_' . strtoupper($ns_id) ] = $ns_prefix;
  615. }
  616. $this->assign_vars($tpl_strings, true);
  617. }
  618. /**
  619. * Init theme vars, like sidebar, global JS, that kind of stuff.
  620. */
  621. function init_theme_vars()
  622. {
  623. global $db, $session, $paths, $template, $plugins; // Common objects
  624. global $lang;
  625. // allows conditional testing of the theme ID (a bit crude, came from my NSIS days)
  626. $this->assign_bool(array(
  627. "theme_is_{$this->theme}" => true
  628. ));
  629. $this->elements = $this->extract_vars('elements.tpl');
  630. // Generate the code for the Administration and Edit Sidebar buttons
  631. // Once again, the new template parsing system can be used here
  632. $parser = $this->makeParserText($this->elements['sidebar_button']);
  633. $parser->assign_vars(Array(
  634. 'HREF'=>makeUrlNS('Special', 'Administration'),
  635. 'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { void(ajaxStartAdminLogin()); return false; }"',
  636. 'TEXT'=>$lang->get('sidebar_btn_administration'),
  637. ));
  638. $admin_link = $parser->run();
  639. // We're leaving this in for now, just blanked out, to avoid compatibility issues.
  640. $sidebar_link = '';
  641. $this->assign_vars(array(
  642. 'ADMIN_LINK' => $admin_link,
  643. 'SIDEBAR_LINK' => $sidebar_link,
  644. 'THEME_ID' => $this->theme,
  645. 'STYLE_ID' => $this->style
  646. ));
  647. // Add the site description sidebar block
  648. $this->sidebar_widget($lang->get('sidebar_title_about'), '<p>' . htmlspecialchars(getConfig('site_desc')) . '</p>');
  649. $this->theme_initted = true;
  650. }
  651. /**
  652. * Init page vars, like the toolbar, local JS, etc.
  653. */
  654. function init_page_vars()
  655. {
  656. global $db, $session, $paths, $template, $plugins; // Common objects
  657. global $lang;
  658. if ( !$this->page )
  659. {
  660. $this->page = new PageProcessor($this->page_id, $this->namespace);
  661. }
  662. $conds = $this->page->ns->get_conds();
  663. $this->assign_bool(array(
  664. 'in_admin' => ( ( $this->page_id == 'Administration' && $this->namespace == 'Special' ) || $this->namespace == 'Admin' ),
  665. 'auth_rename' => ( $conds['rename'] )
  666. ));
  667. // Get the "article" button text (depends on namespace)
  668. switch ( $this->namespace )
  669. {
  670. case "Article":
  671. default:
  672. $ns = $lang->get('onpage_lbl_page_article');
  673. break;
  674. case "Admin":
  675. $ns = $lang->get('onpage_lbl_page_admin');
  676. break;
  677. case "System":
  678. $ns = $lang->get('onpage_lbl_page_system');
  679. break;
  680. case "File":
  681. $ns = $lang->get('onpage_lbl_page_file');
  682. break;
  683. case "Help":
  684. $ns = $lang->get('onpage_lbl_page_help');
  685. break;
  686. case "User":
  687. $ns = $lang->get('onpage_lbl_page_user');
  688. break;
  689. case "Special":
  690. $ns = $lang->get('onpage_lbl_page_special');
  691. break;
  692. case "Template":
  693. $ns = $lang->get('onpage_lbl_page_template');
  694. break;
  695. case "Project":
  696. $ns = $lang->get('onpage_lbl_page_project');
  697. break;
  698. case "Category":
  699. $ns = $lang->get('onpage_lbl_page_category');
  700. break;
  701. case "API":
  702. $ns = $lang->get('onpage_lbl_page_external');
  703. break;
  704. }
  705. $this->namespace_string = $ns;
  706. unset($ns);
  707. // compatibility
  708. $local_namespace =& $this->namespace;
  709. $local_page_id =& $this->page_id;
  710. $code = $plugins->setHook('page_type_string_set');
  711. foreach ( $code as $cmd )
  712. {
  713. eval($cmd);
  714. }
  715. $ns =& $this->namespace_string;
  716. //
  717. // PAGE TOOLBAR (on-page controls/actions)
  718. //
  719. $local_page = $paths->get_pathskey($this->page_id, $this->namespace);
  720. $local_cdata = $this->page->ns->get_cdata();
  721. // Initialize the toolbar
  722. $tb = '';
  723. $this->toolbar_menu = '';
  724. // Create "xx page" button
  725. $btn_selected = ( isset($this->elements['toolbar_button_selected'])) ? $this->elements['toolbar_button_selected'] : $this->elements['toolbar_button'];
  726. $parser = $this->makeParserText($btn_selected);
  727. if ( $conds['article'] )
  728. {
  729. $parser->assign_vars(array(
  730. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxReset()); return false; }" title="' . $lang->get('onpage_tip_article') . '" accesskey="a"',
  731. 'PARENTFLAGS' => 'id="mdgToolbar_article"',
  732. 'HREF' => makeUrl($local_page, null, true),
  733. 'TEXT' => $this->namespace_string
  734. ));
  735. $tb .= $parser->run();
  736. }
  737. $button = $this->makeParserText($this->elements['toolbar_button']);
  738. // Page toolbar
  739. // Comments button
  740. if ( $conds['comments'] )
  741. {
  742. $cdata = $this->page->ns->get_cdata();
  743. if ( isset($cdata['comments_approved']) )
  744. {
  745. $approval_counts = array(
  746. COMMENT_APPROVED => $cdata['comments_approved'],
  747. COMMENT_UNAPPROVED => $cdata['comments_unapproved'],
  748. COMMENT_SPAM => $cdata['comments_spam']
  749. );
  750. $num_comments = $session->check_acl_scope('mod_comments', $this->namespace) && $this->page->perms->get_permissions('mod_comments')
  751. ? array_sum($approval_counts)
  752. : $approval_counts[COMMENT_APPROVED];
  753. }
  754. else
  755. {
  756. $e = $db->sql_query('SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$this->page_id.'\' AND namespace=\''.$this->namespace.'\';');
  757. if ( !$e )
  758. {
  759. $db->_die();
  760. }
  761. $num_comments = $db->numrows();
  762. $approval_counts = array(COMMENT_UNAPPROVED => 0, COMMENT_APPROVED => 0, COMMENT_SPAM => 0);
  763. while ( $r = $db->fetchrow() )
  764. {
  765. $approval_counts[$r['approved']]++;
  766. }
  767. }
  768. $db->free_result();
  769. // $n = ( $session->check_acl_scope('mod_comments', $this->namespace) && $perms->get_permissions('mod_comments') ) ? (string)$num_comments : (string)$na;
  770. if ( $session->check_acl_scope('mod_comments', $this->namespace) && $this->page->perms->get_permissions('mod_comments') && ( $approval_counts[COMMENT_UNAPPROVED] + $approval_counts[COMMENT_SPAM] ) > 0 )
  771. {
  772. $subst = array(
  773. 'num_comments' => $num_comments,
  774. 'num_app' => $approval_counts[COMMENT_APPROVED],
  775. 'num_unapp' => $approval_counts[COMMENT_UNAPPROVED],
  776. 'num_spam' => $approval_counts[COMMENT_SPAM]
  777. );
  778. $btn_text = $lang->get('onpage_btn_discussion_unapp', $subst);
  779. }
  780. else
  781. {
  782. $subst = array(
  783. 'num_comments' => $num_comments
  784. );
  785. $btn_text = $lang->get('onpage_btn_discussion', $subst);
  786. }
  787. $button->assign_vars(array(
  788. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxComments()); return false; }" title="' . $lang->get('onpage_tip_comments') . '" accesskey="c"',
  789. 'PARENTFLAGS' => 'id="mdgToolbar_discussion"',
  790. 'HREF' => makeUrl($local_page, 'do=comments', true),
  791. 'TEXT' => $btn_text,
  792. ));
  793. $tb .= $button->run();
  794. }
  795. // Edit button
  796. if( $conds['edit'] )
  797. {
  798. $button->assign_vars(array(
  799. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxEditor()); return false; }" title="' . $lang->get('onpage_tip_edit') . '" accesskey="e"',
  800. 'PARENTFLAGS' => 'id="mdgToolbar_edit"',
  801. 'HREF' => makeUrl($local_page, 'do=edit', true),
  802. 'TEXT' => $lang->get('onpage_btn_edit')
  803. ));
  804. $tb .= $button->run();
  805. // View source button
  806. }
  807. else if ( $conds['viewsource'] )
  808. {
  809. $button->assign_vars(array(
  810. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxEditor()); return false; }" title="' . $lang->get('onpage_tip_viewsource') . '" accesskey="e"',
  811. 'PARENTFLAGS' => 'id="mdgToolbar_edit"',
  812. 'HREF' => makeUrl($local_page, 'do=viewsource', true),
  813. 'TEXT' => $lang->get('onpage_btn_viewsource')
  814. ));
  815. $tb .= $button->run();
  816. }
  817. // History button
  818. if ( $conds['history'] )
  819. {
  820. $button->assign_vars(array(
  821. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxHistory()); return false; }" title="' . $lang->get('onpage_tip_history') . '" accesskey="h"',
  822. 'PARENTFLAGS' => 'id="mdgToolbar_history"',
  823. 'HREF' => makeUrl($local_page, 'do=history', true),
  824. 'TEXT' => $lang->get('onpage_btn_history')
  825. ));
  826. $tb .= $button->run();
  827. }
  828. $menubtn = $this->makeParserText($this->elements['toolbar_menu_button']);
  829. // Additional actions menu
  830. // Rename button
  831. if ( $conds['rename'] )
  832. {
  833. $menubtn->assign_vars(array(
  834. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxRename()); return false; }" title="' . $lang->get('onpage_tip_rename') . '" accesskey="r"',
  835. 'HREF' => makeUrl($local_page, 'do=rename', true),
  836. 'TEXT' => $lang->get('onpage_btn_rename'),
  837. ));
  838. $this->toolbar_menu .= $menubtn->run();
  839. }
  840. // Vote-to-delete button
  841. if ( $conds['delvote'] )
  842. {
  843. $menubtn->assign_vars(array(
  844. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxDelVote()); return false; }" title="' . $lang->get('onpage_tip_delvote') . '" accesskey="d"',
  845. 'HREF' => makeUrl($local_page, 'do=delvote', true),
  846. 'TEXT' => $lang->get('onpage_btn_votedelete'),
  847. ));
  848. $this->toolbar_menu .= $menubtn->run();
  849. }
  850. // Clear-votes button
  851. if ( $conds['resetvotes'] )
  852. {
  853. $menubtn->assign_vars(array(
  854. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxResetDelVotes()); return false; }" title="' . $lang->get('onpage_tip_resetvotes') . '" accesskey="y"',
  855. 'HREF' => makeUrl($local_page, 'do=resetvotes', true),
  856. 'TEXT' => $lang->get('onpage_btn_votedelete_reset'),
  857. ));
  858. $this->toolbar_menu .= $menubtn->run();
  859. }
  860. // Printable page button
  861. if ( $conds['printable'] )
  862. {
  863. $menubtn->assign_vars(array(
  864. 'FLAGS' => 'title="' . $lang->get('onpage_tip_printable') . '"',
  865. 'HREF' => makeUrl($local_page, 'printable=yes', true),
  866. 'TEXT' => $lang->get('onpage_btn_printable'),
  867. ));
  868. $this->toolbar_menu .= $menubtn->run();
  869. }
  870. // Protect button
  871. if ( $conds['protect'] )
  872. {
  873. switch($this->page->ns->cdata['protected'])
  874. {
  875. case PROTECT_FULL: $protect_status = $lang->get('onpage_btn_protect_on'); break;
  876. case PROTECT_SEMI: $protect_status = $lang->get('onpage_btn_protect_semi'); break;
  877. case PROTECT_NONE: $protect_status = $lang->get('onpage_btn_protect_off'); break;
  878. }
  879. $label = $this->makeParserText($this->elements['toolbar_label']);
  880. $label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_protect') . ' ' . "<b><span id=\"tb_ajax_protect_status\">$protect_status</span></b>"));
  881. $t0 = $label->run();
  882. $menubtn->assign_vars(array(
  883. 'FLAGS' => 'accesskey="p" onclick="ajaxProtect(' . $this->page->ns->cdata['protected'] . '); return false;" id="tb_ajax_protect_btn" title="' . $lang->get('onpage_tip_protect') . '"',
  884. 'HREF' => makeUrl($local_page, 'do=protect', true),
  885. 'TEXT' => $lang->get('onpage_btn_protect_change')
  886. ));
  887. $t1 = $menubtn->run();
  888. $this->toolbar_menu .= ' <table border="0" cellspacing="0" cellpadding="0">
  889. <tr>
  890. <td>'.$t0.'</td>
  891. <td>'.$t1.'</td>
  892. </tr>
  893. </table>';
  894. }
  895. // Wiki mode button
  896. if ( $conds['setwikimode'] )
  897. {
  898. // label at start
  899. $label = $this->makeParserText($this->elements['toolbar_label']);
  900. $label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_wikimode')));
  901. $t0 = $label->run();
  902. // on button
  903. $ctmp = '';
  904. if ( $local_cdata['wiki_mode'] == 1 )
  905. {
  906. $ctmp = ' style="text-decoration: underline;"';
  907. }
  908. $menubtn->assign_vars(array(
  909. 'FLAGS' => $ctmp,
  910. 'HREF' => makeUrl($local_page, 'do=setwikimode&level=1', true),
  911. 'TEXT' => $lang->get('onpage_btn_wikimode_on')
  912. ));
  913. $t1 = $menubtn->run();
  914. // off button
  915. $ctmp = '';
  916. if ( $local_cdata['wiki_mode'] == 0 )
  917. {
  918. $ctmp=' style="text-decoration: underline;"';
  919. }
  920. $menubtn->assign_vars(array(
  921. 'FLAGS' => $ctmp,
  922. 'HREF' => makeUrl($local_page, 'do=setwikimode&level=0', true),
  923. 'TEXT' => $lang->get('onpage_btn_wikimode_off')
  924. ));
  925. $t2 = $menubtn->run();
  926. // global button
  927. $ctmp = '';
  928. if ( $local_cdata['wiki_mode'] == 2 )
  929. {
  930. $ctmp=' style="text-decoration: underline;"';
  931. }
  932. $menubtn->assign_vars(array(
  933. 'FLAGS' => $ctmp,
  934. 'HREF' => makeUrl($local_page, 'do=setwikimode&level=2', true),
  935. 'TEXT' => $lang->get('onpage_btn_wikimode_global')
  936. ));
  937. $t3 = $menubtn->run();
  938. // Tack it onto the list of buttons that are already there...
  939. $this->toolbar_menu .= ' <table border="0" cellspacing="0" cellpadding="0">
  940. <tr>
  941. <td>'.$t0.'</td>
  942. <td>'.$t1.'</td>
  943. <td>'.$t2.'</td>
  944. <td>'.$t3.'</td>
  945. </tr>
  946. </table>';
  947. }
  948. // Clear logs button
  949. if ( $conds['clearlogs'] )
  950. {
  951. $menubtn->assign_vars(array(
  952. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxClearLogs()); return false; }" title="' . $lang->get('onpage_tip_flushlogs') . '" accesskey="l"',
  953. 'HREF' => makeUrl($local_page, 'do=flushlogs', true),
  954. 'TEXT' => $lang->get('onpage_btn_clearlogs'),
  955. ));
  956. $this->toolbar_menu .= $menubtn->run();
  957. }
  958. // Delete page button
  959. if ( $conds['delete'] )
  960. {
  961. $s = $lang->get('onpage_btn_deletepage');
  962. if ( $this->page->ns->cdata['delvotes'] == 1 )
  963. {
  964. $subst = array(
  965. 'num_votes' => $this->page->ns->cdata['delvotes'],
  966. 'plural' => ''
  967. );
  968. $s .= $lang->get('onpage_btn_deletepage_votes', $subst);
  969. }
  970. else if ( $this->page->ns->cdata['delvotes'] > 1 )
  971. {
  972. $subst = array(
  973. 'num_votes' => $this->page->ns->cdata['delvotes'],
  974. 'plural' => $lang->get('meta_plural')
  975. );
  976. $s .= $lang->get('onpage_btn_deletepage_votes', $subst);
  977. }
  978. $menubtn->assign_vars(array(
  979. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxDeletePage()); return false; }" title="' . $lang->get('onpage_tip_deletepage') . '" accesskey="k"',
  980. 'HREF' => makeUrl($local_page, 'do=deletepage', true),
  981. 'TEXT' => $s,
  982. ));
  983. $this->toolbar_menu .= $menubtn->run();
  984. }
  985. // Password-protect button
  986. if ( $conds['password'] )
  987. {
  988. // label at start
  989. $label = $this->makeParserText($this->elements['toolbar_label']);
  990. $label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_password')));
  991. $t0 = $label->run();
  992. $menubtn->assign_vars(array(
  993. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxSetPassword()); return false; }" title="' . $lang->get('onpage_tip_password') . '"',
  994. 'HREF' => '#',
  995. 'TEXT' => $lang->get('onpage_btn_password_set'),
  996. ));
  997. $t = $menubtn->run();
  998. $this->toolbar_menu .= '<table border="0" cellspacing="0" cellpadding="0"><tr><td>'.$t0.'</td><td><input type="password" id="mdgPassSetField" size="10" /></td><td>'.$t.'</td></tr></table>';
  999. }
  1000. // Manage ACLs button
  1001. if ( $conds['acledit'] )
  1002. {
  1003. $menubtn->assign_vars(array(
  1004. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { var s = ajaxOpenACLManager(); console.debug(s); return false; }" title="' . $lang->get('onpage_tip_aclmanager') . '" accesskey="m"',
  1005. 'HREF' => makeUrl($local_page, 'do=aclmanager', true),
  1006. 'TEXT' => $lang->get('onpage_btn_acl'),
  1007. ));
  1008. $this->toolbar_menu .= $menubtn->run();
  1009. }
  1010. // Administer page button
  1011. if ( $conds['adminpage'] )
  1012. {
  1013. $menubtn->assign_vars(array(
  1014. 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxAdminPage()); return false; }" title="' . $lang->get('onpage_tip_adminoptions') . '" accesskey="g"',
  1015. 'HREF' => makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'PageManager', true),
  1016. 'TEXT' => $lang->get('onpage_btn_admin'),
  1017. ));
  1018. $this->toolbar_menu .= $menubtn->run();
  1019. }
  1020. if ( strlen($this->toolbar_menu) > 0 )
  1021. {
  1022. $button->assign_vars(array(
  1023. 'FLAGS' => 'id="mdgToolbar_moreoptions" onclick="if ( !KILL_SWITCH ) { return false; }" title="' . $lang->get('onpage_tip_moreoptions') . '"',
  1024. 'PARENTFLAGS' => '',
  1025. 'HREF' => makeUrl($local_page, 'do=moreoptions', true),
  1026. 'TEXT' => $lang->get('onpage_btn_moreoptions')
  1027. ));
  1028. $tb .= $button->run();
  1029. }
  1030. // Generate the code for the Log in, Log out, Change theme, Administration, and Edit Sidebar buttons
  1031. // Once again, the new template parsing system can be used here
  1032. $parser = $this->makeParserText($this->elements['sidebar_button']);
  1033. $parser->assign_vars(Array(
  1034. 'HREF'=>makeUrlNS('Special', "Logout/{$session->csrf_token}/{$local_page}"),
  1035. 'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { mb_logout(); return false; }"',
  1036. 'TEXT'=>$lang->get('sidebar_btn_logout'),
  1037. ));
  1038. $logout_link = $parser->run();
  1039. $parser->assign_vars(Array(
  1040. 'HREF'=>makeUrlNS('Special', 'Login/' . $local_page),
  1041. 'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { ajaxStartLogin(); return false; }"' . ( $local_page_id == 'Login' && $local_namespace == 'Special' ? ' class="currentpage"' : '' ),
  1042. 'TEXT'=>$lang->get('sidebar_btn_login'),
  1043. ));
  1044. $login_link = $parser->run();
  1045. $parser->assign_vars(Array(
  1046. 'HREF'=>makeUrlNS('Special', 'ChangeStyle/'.$local_page),
  1047. 'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { ajaxChangeStyle(); return false; }"' . ( $local_page_id == 'ChangeStyle' && $local_namespace == 'Special' ? ' class="currentpage"' : '' ),
  1048. 'TEXT'=>$lang->get('sidebar_btn_changestyle'),
  1049. ));
  1050. $theme_link = $parser->run();
  1051. // Run hooks
  1052. $code = $plugins->setHook('tpl_compile_toolbar');
  1053. foreach ( $code as $cmd )
  1054. {
  1055. eval($cmd);
  1056. }
  1057. //
  1058. // ASSIGN VARIABLES
  1059. //
  1060. $this->assign_vars(array(
  1061. 'PAGE_NAME' => htmlspecialchars($this->page->ns->cdata['name']),
  1062. 'PAGE_URLNAME' => $paths->nslist[$this->namespace] . sanitize_page_id($this->page_id),
  1063. 'TOOLBAR' => $tb,
  1064. 'TOOLBAR_EXTRAS' => $this->toolbar_menu,
  1065. 'STYLE_LINK' => makeUrlNS('Special', 'CSS', null, true), //contentPath.$paths->nslist['Special'].'CSS' . $p,
  1066. 'LOGIN_LINK' => $login_link,
  1067. 'LOGOUT_LINK' => $logout_link,
  1068. 'THEME_LINK' => $theme_link
  1069. ), true);
  1070. $this->page_initted = true;
  1071. }
  1072. /**
  1073. * Generates and assigns the Javascript system variables
  1074. */
  1075. function generate_js_header()
  1076. {
  1077. global $db, $session, $paths, $template, $plugins; // Common objects
  1078. global $lang;
  1079. $SID = ($session->sid_super) ? $session->sid_super : '';
  1080. $local_page = $paths->get_pathskey($this->page_id, $this->namespace);
  1081. $local_fullpage = $paths->get_pathskey($this->page_id, $this->namespace) . substr($paths->fullpage, strlen($paths->page));
  1082. $urlname_clean = str_replace('\'', '\\\'', str_replace('\\', '\\\\', dirtify_page_id($local_fullpage)));
  1083. $urlname_clean = strtr( $urlname_clean, array( '<' => '&lt;', '>' => '&gt;' ) );
  1084. $urlname_jssafe = sanitize_page_id($local_fullpage);
  1085. $physical_urlname_jssafe = sanitize_page_id($paths->fullpage);
  1086. $protected = is_object($this->page) ? $this->page->ns->cdata['really_protected'] : false;
  1087. // Generate the dynamic javascript vars
  1088. // Sorry. I know. This block is a mess.
  1089. $js_dynamic = ' <script type="text/javascript">// <![CDATA[
  1090. // This section defines some basic and very important variables that are used later in the static Javascript library.
  1091. // SKIN DEVELOPERS: The template variable for this code block is {JS_DYNAMIC_VARS}. This MUST be inserted BEFORE the tag that links to the main Javascript lib.
  1092. var title = \''. $urlname_jssafe .'\';
  1093. var physical_title = \'' . $physical_urlname_jssafe . '\';
  1094. var on_main_page = ' . ( $local_page == get_main_page() ? 'true' : 'false' ) . ';
  1095. var main_page_members = \'' . addslashes(get_main_page(true)) . '\';
  1096. var page_exists = '. ( ( is_object($this->page) ? $this->page->ns->exists() : true ) ? 'true' : 'false' ) .';
  1097. var scriptPath = \'' . addslashes(scriptPath) . '\';
  1098. var contentPath = \'' . addslashes(contentPath) . '\';
  1099. var cdnPath = \'' . addslashes(cdnPath) . '\';
  1100. var ENANO_SID = \'' . $SID . '\';
  1101. var user_level = ' . $session->user_level . ';
  1102. var auth_level = ' . $session->auth_level . ';
  1103. var USER_LEVEL_GUEST = ' . USER_LEVEL_GUEST . ';
  1104. var USER_LEVEL_MEMBER = ' . USER_LEVEL_MEMBER . ';
  1105. var USER_LEVEL_CHPREF = ' . USER_LEVEL_CHPREF . ';
  1106. var USER_LEVEL_MOD = ' . USER_LEVEL_MOD . ';
  1107. var USER_LEVEL_ADMIN = ' . USER_LEVEL_ADMIN . ';
  1108. var disable_redirect = ' . ( isset($_GET['redirect']) && $_GET['redirect'] == 'no' ? 'true' : 'false' ) . ';
  1109. var pref_disable_js_fx = ' . ( @$session->user_extra['disable_js_fx'] == 1 ? 'true' : 'false' ) . ';
  1110. var csrf_token = "' . $session->csrf_token . '";
  1111. var prot = ' . ( ($protected) ? 'true' : 'false' ) .';
  1112. var ENANO_SPECIAL_CREATEPAGE = \''. makeUrl($paths->nslist['Special'].'CreatePage') .'\';
  1113. var ENANO_CREATEPAGE_PARAMS = \'_do=&pagename='. $this->page_id .'&namespace=' . $this->namespace . '\';
  1114. var ENANO_SPECIAL_CHANGESTYLE = \''. makeUrlNS('Special', 'ChangeStyle') .'\';
  1115. var namespace_list = [];
  1116. var msg_loading_component = \'' . addslashes($lang->get('ajax_msg_loading_component')) . '\';
  1117. var AES_BITS = '.AES_BITS.';
  1118. var AES_BLOCKSIZE = '.AES_BLOCKSIZE.';
  1119. var pagepass = \''. ( ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '' ) .'\';
  1120. var ENANO_LANG_ID = ' . $lang->lang_id . ';
  1121. var ENANO_PAGE_TYPE = "' . addslashes($this->namespace_string) . '";
  1122. var enano_version = "' . enano_version() . '";';
  1123. foreach ( $paths->nslist as $k => $c )
  1124. {
  1125. $js_dynamic .= "namespace_list['{$k}'] = '" . addslashes($c) . "';";
  1126. }
  1127. $js_dynamic .= "\n //]]>\n </script>";
  1128. $this->assign_vars(array(
  1129. 'JS_DYNAMIC_VARS' => $js_dynamic,
  1130. 'REPORT_URI' => makeUrl($local_fullpage, 'do=sql_report', true)
  1131. ), true);
  1132. }
  1133. /**
  1134. * Fetches, parses, and assigns the sidebar.
  1135. */
  1136. function assign_sidebar()
  1137. {
  1138. //
  1139. // COMPILE THE SIDEBAR
  1140. //
  1141. // This is done after the big assign_vars() so that sidebar code has access to the newly assigned variables
  1142. list($this->tpl_strings['SIDEBAR_LEFT'], $this->tpl_strings['SIDEBAR_RIGHT'], $min) = $this->fetch_sidebar();
  1143. $this->tpl_bool['sidebar_left'] = ( $this->tpl_strings['SIDEBAR_LEFT'] != $min) ? true : false;
  1144. $this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != $min) ? true : false;
  1145. $this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility
  1146. }
  1147. /**
  1148. * Initializes all variables related to on-page content. This includes sidebars and what have you.
  1149. * @param object Optional PageProcessor object to use for passing metadata and permissions on. If omitted, uses information from $paths and $session.
  1150. * @param bool If true, re-inits even if already initted with this page_id and namespace
  1151. */
  1152. function init_vars($page = false, $force_init = false)
  1153. {
  1154. global $db, $session, $paths, $template, $plugins; // Common objects
  1155. global $lang;
  1156. $need_shared_init = ( !$this->theme_initted || !$this->page_initted );
  1157. if ( $need_shared_init )
  1158. {
  1159. if ( !$this->theme || !$this->style )
  1160. {
  1161. $this->load_theme();
  1162. }
  1163. $code = $plugins->setHook('compile_template', true);
  1164. foreach ( $code as $cmd )
  1165. {
  1166. eval($cmd);
  1167. }
  1168. }
  1169. if ( !$this->theme_loaded )
  1170. $this->load_theme();
  1171. require(ENANO_ROOT . "/themes/{$this->theme}/theme.cfg");
  1172. if ( !$this->page_id || !$this->namespace )
  1173. {
  1174. $this->page_id = $paths->page_id;
  1175. $this->namespace = $paths->namespace;
  1176. }
  1177. profiler_log('template: prepped for var set (loaded theme, ran compile_template hook)');
  1178. $this->init_global_vars();
  1179. profiler_log('template: global vars set');
  1180. if ( !$this->theme_initted )
  1181. $this->init_theme_vars();
  1182. profiler_log('template: theme vars set');
  1183. if ( !$this->page_initted && !empty($this->namespace) )
  1184. {
  1185. profiler_log('template: page vars set');
  1186. $this->init_page_vars();
  1187. }
  1188. else
  1189. {
  1190. profiler_message('template: skipped setting page vars');
  1191. }
  1192. // Perform shared init (combine javascript, etc.)
  1193. if ( $need_shared_init )
  1194. {
  1195. $this->generate_js_header();
  1196. $this->assign_sidebar();
  1197. profiler_log('template: assigned sidebar and JS');
  1198. }
  1199. // and finally one string value that needs to be symlinked...
  1200. if ( !isset($this->tpl_strings['ADDITIONAL_HEADERS']) )
  1201. {
  1202. $this->tpl_strings['ADDITIONAL_HEADERS'] =& $this->additional_headers;
  1203. }
  1204. // done!
  1205. $code = $plugins->setHook('template_var_init_end');
  1206. foreach ( $code as $cmd )
  1207. {
  1208. eval($cmd);
  1209. }
  1210. }
  1211. function header($simple = false)
  1212. {
  1213. global $db, $session, $paths, $template, $plugins; // Common objects
  1214. global $lang;
  1215. define('ENANO_HEADERS_SENT', true);
  1216. echo $this->getHeader($simple);
  1217. }
  1218. function footer($simple = false)
  1219. {
  1220. echo $this->getFooter($simple);
  1221. }
  1222. function getHeader($simple = false)
  1223. {
  1224. global $db, $session, $paths, $template, $plugins; // Common objects
  1225. global $lang;
  1226. if ( !$this->theme_loaded )
  1227. {
  1228. $this->load_theme($session->theme, $session->style);
  1229. }
  1230. if ( !$this->page_initted || !$this->theme_initted )
  1231. $this->init_vars();
  1232. // I feel awful doing this.
  1233. if ( preg_match('/^W3C_Validator/', @$_SERVER['HTTP_USER_AGENT']) )
  1234. {
  1235. header('Content-type: application/xhtml+xml');
  1236. }
  1237. $header = '';
  1238. if ( !$this->no_headers )
  1239. {
  1240. $header = ( $simple ) ?
  1241. $this->process_template('simple-header.tpl') :
  1242. $this->process_template('header.tpl');
  1243. }
  1244. if ( !$simple && $session->user_logged_in && $session->unread_pms > 0 )
  1245. {
  1246. $header .= $this->notify_unread_pms();
  1247. }
  1248. if ( !$simple && $session->sw_timed_out )
  1249. {
  1250. $login_link = makeUrlNS('Special', 'Login/' . $paths->fullpage, 'level=' . $session->user_level, true);
  1251. $header .= '<div class="usermessage">';
  1252. $header .= $lang->get('user_msg_elev_timed_out', array( 'login_link' => $login_link ));
  1253. $header .= '</div>';
  1254. }
  1255. if ( $this->site_disabled && $session->user_level >= USER_LEVEL_ADMIN && ( $paths->page != $paths->nslist['Special'] . 'Administration' ) )
  1256. {
  1257. $admin_link = makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'GeneralConfig', true);
  1258. $header .= '<div class="usermessage"><b>' . $lang->get('page_sitedisabled_admin_msg_title') . '</b><br />
  1259. ' . $lang->get('page_sitedisabled_admin_msg_body', array('admin_link' => $admin_link)) . '
  1260. </div>';
  1261. }
  1262. return $header;
  1263. }
  1264. function getFooter($simple = false)
  1265. {
  1266. global $db, $session, $paths, $template, $plugins; // Common objects
  1267. global $lang;
  1268. if ( !$this->no_headers )
  1269. {
  1270. global $_starttime;
  1271. if(isset($_GET['sqldbg']) && $session->get_permissions('mod_misc'))
  1272. {
  1273. echo '<h3>' . $lang->get('page_heading_sql_list') . '</h3><pre style="margin-left: 1em">';
  1274. echo htmlspecialchars($db->sql_backtrace());
  1275. echo '</pre>';
  1276. }
  1277. $t = ( $simple ) ? $this->process_template('simple-footer.tpl') : $this->process_template('footer.tpl');
  1278. $f = microtime_float();
  1279. $f = $f - $_starttime;
  1280. $f = sprintf("%.02f", $f);
  1281. $t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f));
  1282. $t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f));
  1283. $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . $lang->get('page_msg_stats_sql', array('nq' => $db->num_queries)) . '</a>';
  1284. $dbg = $t_loc;
  1285. $dbg_long = $t_loc_long;
  1286. if ( $session->user_level >= USER_LEVEL_ADMIN || defined('ENANO_DEBUG') )
  1287. {
  1288. $dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
  1289. $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
  1290. }
  1291. $t = str_replace('[[Stats]]', $dbg, $t);
  1292. $t = str_replace('[[StatsLong]]', $dbg_long, $t);
  1293. $t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t);
  1294. $t = str_replace('[[GenTime]]', (string)$f, $t);
  1295. $t = str_replace('[[NumQueriesLoc]]', $q_loc, $t);
  1296. $t = str_replace('[[GenTimeLoc]]', $t_loc, $t);
  1297. $t = str_replace('[[EnanoPoweredLink]]', $lang->get('page_enano_powered', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
  1298. $t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
  1299. if ( defined('ENANO_PROFILE') )
  1300. {
  1301. $t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t);
  1302. // ob_end_clean();
  1303. // return profiler_make_html();
  1304. }
  1305. return $t;
  1306. }
  1307. else
  1308. {
  1309. return '';
  1310. }
  1311. }
  1312. /**
  1313. * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
  1314. * @param $vars array
  1315. * @param $from_internal bool Internal switch, just omit (@todo document)
  1316. */
  1317. function assign_vars($vars, $from_internal = false)
  1318. {
  1319. foreach ( $vars as $key => $value )
  1320. {
  1321. $replace = true;
  1322. if ( isset($this->vars_assign_history['strings'][$key]) )
  1323. {
  1324. if ( $this->vars_assign_history['strings'][$key] == 'api' )
  1325. {
  1326. $replace = false;
  1327. }
  1328. }
  1329. if ( $replace )
  1330. {
  1331. $this->tpl_strings[$key] = $value;
  1332. $this->vars_assign_history['strings'][$key] = ( $from_internal ) ? 'internal' : 'api';
  1333. }
  1334. }
  1335. }
  1336. /**
  1337. * Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements.
  1338. * @param $vars array
  1339. * @param $from_internal bool Internal switch, just omit (@todo document)
  1340. */
  1341. function assign_bool($vars, $from_internal = false)
  1342. {
  1343. foreach ( $vars as $key => $value )
  1344. {
  1345. $replace = true;
  1346. if ( isset($this->vars_assign_history['bool'][$key]) )
  1347. {
  1348. if ( $this->vars_assign_history['bool'][$key] == 'api' )
  1349. {
  1350. $replace = false;
  1351. }
  1352. }
  1353. if ( $replace )
  1354. {
  1355. $this->tpl_bool[$key] = $value;
  1356. $this->vars_assign_history['bool'][$key] = ( $from_internal ) ? 'internal' : 'api';
  1357. }
  1358. }
  1359. }
  1360. #
  1361. # COMPILER
  1362. #
  1363. /**
  1364. * Compiles and executes a template based on the current variables and booleans. Loads
  1365. * the theme and initializes variables if needed. This mostly just calls child functions.
  1366. * @param string File to process
  1367. * @return string
  1368. */
  1369. function process_template($file)
  1370. {
  1371. global $db, $session, $paths, $template, $plugins; // Common objects
  1372. if ( !$this->theme_initted || !$this->page_initted )
  1373. {
  1374. $this->init_vars();
  1375. }
  1376. $cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $file) . '.php';
  1377. if ( file_exists($cache_file) )
  1378. {
  1379. // this is about the time of the breaking change to cache file format
  1380. if ( ($m = filemtime($cache_file)) > 1215038089 )
  1381. {
  1382. $result = @include($cache_file);
  1383. if ( isset($md5) )
  1384. {
  1385. if ( $m >= filemtime(ENANO_ROOT . "/themes/{$this->theme}/$file") )
  1386. {
  1387. $result = $this->compile_template_text_post($result);
  1388. return $result;
  1389. }
  1390. }
  1391. }
  1392. }
  1393. $compiled = $this->compile_template($file);
  1394. $result = eval($compiled);
  1395. return $result;
  1396. }
  1397. /**
  1398. * Loads variables from the specified template file. Returns an associative array containing the variables.
  1399. * @param string Template file to process (elements.tpl)
  1400. * @return array
  1401. */
  1402. function extract_vars($file)
  1403. {
  1404. global $db, $session, $paths, $template, $plugins; // Common objects
  1405. // Sometimes this function gets called before the theme is loaded
  1406. // This is a bad coding practice so this function will always be picky.
  1407. if ( !$this->theme )
  1408. {
  1409. die('$template->extract_vars(): theme not yet loaded, so we can\'t open template files yet...this is a bug and should be reported.<br /><br />Backtrace, most recent call first:<pre>'.enano_debug_print_backtrace(true).'</pre>');
  1410. }
  1411. // Full pathname of template file
  1412. $tpl_file_fullpath = ( strstr($file, '/') ) ? $file : ENANO_ROOT . '/themes/' . $this->theme . '/' . $file;
  1413. // Make sure the template even exists
  1414. if ( !is_file($tpl_file_fullpath) )
  1415. {
  1416. die_semicritical('Cannot find template file',
  1417. '<p>The template parser was asked to load the file "' . htmlspecialchars($tpl_file_fullpath) . '", but that file couldn\'t be found in the directory for
  1418. the current theme.</p>
  1419. <p>Additional debugging information:<br />
  1420. <b>Theme currently in use: </b>' . $this->theme . '<br />
  1421. <b>Requested file: </b>' . $file . '
  1422. </p>');
  1423. }
  1424. // Retrieve file contents
  1425. $text = file_get_contents($tpl_file_fullpath);
  1426. if ( !$text )
  1427. {
  1428. return false;
  1429. }
  1430. // Get variables, regular expressions FTW
  1431. preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches);
  1432. // Initialize return values
  1433. $tplvars = Array();
  1434. // Loop through each match, setting $tplvars[ $first_subpattern ] to $second_subpattern
  1435. for ( $i = 0; $i < sizeof($matches[1]); $i++ )
  1436. {
  1437. $tplvars[ $matches[1][$i] ] = $matches[2][$i];
  1438. }
  1439. // All done!
  1440. return $tplvars;
  1441. }
  1442. /**
  1443. * Compiles a block of template code.
  1444. * @param string The text to process
  1445. * @return string
  1446. */
  1447. function compile_tpl_code($text)
  1448. {
  1449. return template_compiler_core($text);
  1450. }
  1451. /**
  1452. * Compiles the contents of a given template file, possibly using a cached copy, and returns the compiled code.
  1453. * @param string Filename of template (header.tpl)
  1454. * @return string
  1455. */
  1456. function compile_template($filename)
  1457. {
  1458. global $db, $session, $paths, $template, $plugins; // Common objects
  1459. // Full path to template file
  1460. $tpl_file_fullpath = ENANO_ROOT . '/themes/' . $this->theme . '/' . $filename;
  1461. // Make sure the file exists
  1462. if ( !is_file($tpl_file_fullpath) )
  1463. {
  1464. die_semicritical('Cannot find template file',
  1465. '<p>The template parser was asked to load the file "' . htmlspecialchars($filename) . '", but that file couldn\'t be found in the directory for
  1466. the current theme.</p>
  1467. <p>Additional debugging information:<br />
  1468. <b>Theme currently in use: </b>' . $this->theme . '<br />
  1469. <b>Requested file: </b>' . $file . '
  1470. </p>');
  1471. }
  1472. // We won't use the cached copy here.
  1473. $text = file_get_contents($tpl_file_fullpath);
  1474. // This will be used later when writing the cached file
  1475. $md5 = md5($text);
  1476. // Preprocessing and checks complete - compile the code
  1477. $text = $this->compile_tpl_code($text);
  1478. // Generate cache filename
  1479. $cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $filename) . '.php';
  1480. // Perhaps caching is enabled and the admin has changed the template?
  1481. if ( is_writable( ENANO_ROOT . '/cache/' ) && getConfig('cache_thumbs') == '1' )
  1482. {
  1483. $h = fopen($cache_file, 'w');
  1484. if ( !$h )
  1485. {
  1486. // Couldn't open the file - silently ignore and return
  1487. return $text;
  1488. }
  1489. // Final contents of cache file
  1490. $file_contents = <<<EOF
  1491. <?php
  1492. /*
  1493. * NOTE: This file was automatically generated by Enano and is based on compiled code. Do not edit this file.
  1494. * If you edit this file, any changes you make will be lost the next time the associated source template file is edited.
  1495. */
  1496. \$md5 = '$md5';
  1497. $text
  1498. EOF;
  1499. // This is really just a normal PHP file that sets a variable or two and exits.
  1500. // $tpl_text actually will contain the compiled code
  1501. fwrite($h, $file_contents);
  1502. fclose($h);
  1503. }
  1504. return $this->compile_template_text_post($text); //('<pre>'.htmlspecialchars($text).'</pre>');
  1505. }
  1506. /**
  1507. * Compiles (parses) some template code with the current master set of variables and booleans.
  1508. * @param string Text to process
  1509. * @return string
  1510. */
  1511. function compile_template_text($text)
  1512. {
  1513. // this might do something else in the future, possibly cache large templates
  1514. return $this->compile_template_text_post($this->compile_tpl_code($text));
  1515. }
  1516. /**
  1517. * For convenience - compiles AND parses some template code.
  1518. * @param string Text to process
  1519. * @return string
  1520. */
  1521. function parse($text)
  1522. {
  1523. $text = $this->compile_template_text($text);
  1524. $text = $this->compile_template_text_post($text);
  1525. return eval($text);
  1526. }
  1527. /**
  1528. * Post-processor for template code. Basically what this does is it localizes {lang:foo} blocks.
  1529. * @param string Mostly-processed TPL code
  1530. * @param bool Post-eval switch. If true, does not escape code.
  1531. * @return string
  1532. */
  1533. function compile_template_text_post($text, $post_eval = false)
  1534. {
  1535. global $db, $session, $paths, $template, $plugins; // Common objects
  1536. global $lang;
  1537. // Language strings
  1538. preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $text, $matches);
  1539. foreach ( $matches[1] as $i => $string_id )
  1540. {
  1541. $string = $lang->get($string_id);
  1542. if ( !$post_eval )
  1543. {
  1544. $string = str_replace('\\', '\\\\', $string);
  1545. $string = str_replace('\'', '\\\'', $string);
  1546. }
  1547. $text = str_replace_once($matches[0][$i], $string, $text);
  1548. }
  1549. // URLs
  1550. preg_match_all('/\{url:([A-z0-9]+):([^\}]+?)(?:;([^\s\}]+?))?(?:\|(escape))?\}/i', $text, $matches);
  1551. foreach ( $matches[1] as $i => $string_id )
  1552. {
  1553. $namespace =& $matches[1][$i];
  1554. $page_id =& $matches[2][$i];
  1555. $params =& $matches[3][$i];
  1556. $escape =& $matches[4][$i];
  1557. if ( !$params )
  1558. $params = false;
  1559. $escape = !empty($escape);
  1560. $result = makeUrlNS($namespace, sanitize_page_id($page_id), $params, $escape);
  1561. if ( !$post_eval )
  1562. {
  1563. $result = str_replace('\\', '\\\\', $result);
  1564. $result = str_replace('\'', '\\\'', $result);
  1565. }
  1566. $text = str_replace_once($matches[0][$i], $result, $text);
  1567. }
  1568. $code = $plugins->setHook('compie_template_text_post');
  1569. foreach ( $code as $cmd )
  1570. {
  1571. eval($cmd);
  1572. }
  1573. return $text;
  1574. }
  1575. /**
  1576. * Returns the output of a theme hook
  1577. * @param string Hook name
  1578. * @return string
  1579. */
  1580. function get_theme_hook($hook)
  1581. {
  1582. global $db, $session, $paths, $template, $plugins; // Common objects
  1583. global $lang;
  1584. ob_start();
  1585. $code = $plugins->setHook($hook);
  1586. foreach ( $code as $cmd )
  1587. {
  1588. eval($cmd);
  1589. }
  1590. $out = ob_get_contents();
  1591. ob_end_clean();
  1592. return $out;
  1593. }
  1594. // n00bish comments removed from here. 2008-03-13 @ 12:02AM when I had nothing else to do.
  1595. /**
  1596. * Takes a blob of HTML with the specially formatted template-oriented wikitext and formats it. Does not use eval().
  1597. * This function butchers every coding standard in Enano and should eventually be rewritten. The fact is that the
  1598. * code _works_ and does a good job of checking for errors and cleanly complaining about them.
  1599. * @param string Text to process
  1600. * @param bool Ignored for backwards compatibility
  1601. * @param string File to get variables for sidebar data from
  1602. * @return string
  1603. */
  1604. function tplWikiFormat($message, $filter_links = false, $filename = 'elements.tpl')
  1605. {
  1606. global $db, $session, $paths, $template, $plugins; // Common objects
  1607. global $lang;
  1608. $START = microtime_float();
  1609. // localize the whole string first
  1610. preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $message, $matches);
  1611. foreach ( $matches[1] as $i => $string_id )
  1612. {
  1613. $string = $lang->get($string_id);
  1614. $string = str_replace('\\', '\\\\', $string);
  1615. $string = str_replace('\'', '\\\'', $string);
  1616. $message = str_replace_once($matches[0][$i], $string, $message);
  1617. }
  1618. // first: the hackish optimization -
  1619. // if it's only a bunch of letters, numbers and spaces, just skip this sh*t.
  1620. if ( preg_match('/^[\w\s\.]*$/i', $message) )
  1621. {
  1622. return $message;
  1623. }
  1624. $filter_links = false;
  1625. $tplvars = $this->extract_vars($filename);
  1626. if($session->sid_super) $as = htmlspecialchars(urlSeparator).'auth='.$session->sid_super;
  1627. else $as = '';
  1628. error_reporting(E_ALL);
  1629. $random_id = sha1(microtime().''); // A temp value
  1630. /*
  1631. * PREPROCESSOR
  1632. */
  1633. // Variables
  1634. preg_match_all('#\$([A-Z_-]+)\$#', $message, $links);
  1635. $links = $links[1];
  1636. for($i=0;$i<sizeof($links);$i++)
  1637. {
  1638. if ( isset($this->tpl_strings[$links[$i]]) )
  1639. {
  1640. $message = str_replace('$'.$links[$i].'$', $this->tpl_strings[$links[$i]], $message);
  1641. }
  1642. }
  1643. // Conditionals
  1644. $message = $this->twf_parse_conditionals($message);
  1645. /*
  1646. * HTML RENDERER
  1647. */
  1648. // Images
  1649. $message = RenderMan::process_image_tags($message, $taglist);
  1650. $message = RenderMan::process_imgtags_stage2($message, $taglist);
  1651. // Internal links
  1652. $message = RenderMan::parse_internal_links($message, $tplvars['sidebar_button'], false, $this->page_id, $this->namespace);
  1653. // External links
  1654. $url_regexp = <<<EOF
  1655. (
  1656. (?:https?|ftp|irc):\/\/ # protocol
  1657. (?:[^@\s\]"\':]+@)? # username (FTP only but whatever)
  1658. (?:(?:(?:[a-z0-9-]+\.)*)[a-z0-9\[\]:]+) # hostname
  1659. (?::[0-9]+)? # port number
  1660. (?:\/[A-z0-9_%\|~`!\!@#\$\^&?=\*\(\):;\.,\/-]*)? # path
  1661. )
  1662. EOF;
  1663. $text_parser = $this->makeParserText($tplvars['sidebar_button']);
  1664. preg_match_all('/\[' . $url_regexp . '[ ]([^\]]+)\]/isx', $message, $ext_link);
  1665. for ( $i = 0; $i < count($ext_link[0]); $i++ )
  1666. {
  1667. $text_parser->assign_vars(Array(
  1668. 'HREF' => $ext_link[1][$i],
  1669. 'FLAGS' => '',
  1670. 'TEXT' => $ext_link[2][$i]
  1671. ));
  1672. $message = str_replace($ext_link[0][$i], $text_parser->run(), $message);
  1673. }
  1674. preg_match_all('/\[' . $url_regexp . '\]/is', $message, $ext_link);
  1675. for ( $i = 0; $i < count($ext_link[0]); $i++ )
  1676. {
  1677. $text_parser->assign_vars(Array(
  1678. 'HREF' => $ext_link[1][$i],
  1679. 'FLAGS' => '',
  1680. 'TEXT' => htmlspecialchars($ext_link[1][$i])
  1681. ));
  1682. $message = str_replace($ext_link[0][$i], $text_parser->run(), $message);
  1683. }
  1684. $TIME = microtime_float() - $START;
  1685. /*
  1686. if ( $TIME > 0.02 )
  1687. {
  1688. echo 'template: tplWikiFormat took a while for this one. string dump:<pre>';
  1689. echo htmlspecialchars($message);
  1690. echo '</pre>';
  1691. }
  1692. */
  1693. return $message;
  1694. }
  1695. /**
  1696. * Parses conditional {if} blocks in sidebars and other tplWikiFormatted things
  1697. * @param string A string potentially containing conditional blocks
  1698. * @return string Processed string
  1699. */
  1700. function twf_parse_conditionals($message)
  1701. {
  1702. if ( !preg_match_all('/\{(!?)if ([a-z0-9_\(\)\|&! ]+)\}(.*?)(?:\{else\}(.*?))?\{\/if\}/is', $message, $matches) )
  1703. {
  1704. return $message;
  1705. }
  1706. foreach ( $matches[0] as $match_id => $full_block )
  1707. {
  1708. // 1 = "not" flag
  1709. // 2 = condition
  1710. // 3 = if true
  1711. // 4 = else
  1712. $condresult = $this->process_condition($matches[2][$match_id]);
  1713. if ( !empty($matches[1][$match_id]) )
  1714. {
  1715. if ( $condresult == 1 )
  1716. $condresult = 2;
  1717. else if ( $condresult == 2 )
  1718. $condresult = 1;
  1719. }
  1720. switch($condresult)
  1721. {
  1722. case 1:
  1723. // evaluated to false
  1724. $message = str_replace_once($full_block, $matches[4][$match_id], $message);
  1725. break;
  1726. case 2:
  1727. // evaluated to true
  1728. $message = str_replace_once($full_block, $matches[3][$match_id], $message);
  1729. break;
  1730. case 3:
  1731. $message = str_replace_once($full_block, "Syntax error: mismatched parentheses (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
  1732. break;
  1733. case 4:
  1734. $message = str_replace_once($full_block, "Syntax error: illegal character (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
  1735. break;
  1736. case 5:
  1737. $message = str_replace_once($full_block, "Syntax error: illegal sequence (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
  1738. break;
  1739. }
  1740. }
  1741. return $message;
  1742. }
  1743. /**
  1744. * Inner-loop parser for a conditional block. Verifies a string condition to make sure it's syntactically correct, then returns what it evaluates to.
  1745. * Return values:
  1746. * 1 - string evaluates to true
  1747. * 2 - string evaluates to false
  1748. * 3 - Syntax error - mismatched parentheses
  1749. * 4 - Syntax error - unknown token
  1750. * 5 - Syntax error - invalid sequence
  1751. * @param string
  1752. * @return int
  1753. *
  1754. */
  1755. function process_condition($condition)
  1756. {
  1757. // make sure parentheses are matched
  1758. $parentheses = preg_replace('/[^\(\)]/', '', $condition);
  1759. if ( !empty($parentheses) )
  1760. {
  1761. $i = 0;
  1762. $parentheses = enano_str_split($parentheses);
  1763. foreach ( $parentheses as $chr )
  1764. {
  1765. $inc = ( $chr == '(' ) ? 1 : -1;
  1766. $i += $inc;
  1767. }
  1768. if ( $i != 0 )
  1769. {
  1770. // mismatched parentheses
  1771. return 3;
  1772. }
  1773. }
  1774. // sequencer check
  1775. // first, pad all sequences of characters with spaces
  1776. $seqcheck = preg_replace('/([a-z0-9_]+)/i', '\\1 ', $condition);
  1777. $seqcheck = preg_replace('/([&|()!])/i', '\\1 ', $seqcheck);
  1778. // now shrink all spaces to one space each
  1779. $seqcheck = preg_replace('/[ ]+/', ' ', $seqcheck);
  1780. // explode it. the allowed sequences are:
  1781. // - TOKEN_NOT + TOKEN_VARIABLE
  1782. // - TOKEN_NOT + TOKEN_PARENTHLEFT
  1783. // - TOKEN_BOOLOP + TOKEN_NOT
  1784. // - TOKEN_PARENTHRIGHT + TOKEN_NOT
  1785. // - TOKEN_VARIABLE + TOKEN_BOOLOP
  1786. // - TOKEN_BOOLOP + TOKEN_PARENTHLEFT
  1787. // - TOKEN_PARENTHLEFT + TOKEN_VARIABLE
  1788. // - TOKEN_BOOLOP + TOKEN_VARIABLE
  1789. // - TOKEN_VARIABLE + TOKEN_PARENTHRIGHT
  1790. // - TOKEN_PARENTHRIGHT + TOKEN_BOOLOP
  1791. $seqcheck = explode(' ', trim($seqcheck));
  1792. $last_item = TOKEN_BOOLOP;
  1793. foreach ( $seqcheck as $i => $token )
  1794. {
  1795. // determine type
  1796. if ( $token == '(' )
  1797. {
  1798. $type = TOKEN_PARENTHLEFT;
  1799. }
  1800. else if ( $token == ')' )
  1801. {
  1802. $type = TOKEN_PARENTHRIGHT;
  1803. }
  1804. else if ( $token == '!' )
  1805. {
  1806. $type = TOKEN_NOT;
  1807. }
  1808. else if ( strtolower($token) == 'and' || strtolower($token) == 'or' || $token == '&&' || $token == '||' )
  1809. {
  1810. $type = TOKEN_BOOLOP;
  1811. }
  1812. else if ( preg_match('/^[a-z0-9_]+$/i', $token) )
  1813. {
  1814. $type = TOKEN_VARIABLE;
  1815. // at this point it's considered safe to wrap it
  1816. $seqcheck[$i] = "( isset(\$this->tpl_bool['$token']) && \$this->tpl_bool['$token'] )";
  1817. }
  1818. else
  1819. {
  1820. // syntax error - doesn't match known token types
  1821. return 4;
  1822. }
  1823. // inner sequence check
  1824. if (
  1825. ( $last_item == TOKEN_BOOLOP && $type == TOKEN_NOT ) ||
  1826. ( $last_item == TOKEN_PARENTHRIGHT && $type == TOKEN_NOT ) ||
  1827. ( $last_item == TOKEN_NOT && $type == TOKEN_VARIABLE ) ||
  1828. ( $last_item == TOKEN_NOT && $type == TOKEN_PARENTHLEFT ) ||
  1829. ( $last_item == TOKEN_VARIABLE && $type == TOKEN_BOOLOP ) ||
  1830. ( $last_item == TOKEN_BOOLOP && $type == TOKEN_PARENTHLEFT ) ||
  1831. ( $last_item == TOKEN_PARENTHLEFT && $type == TOKEN_VARIABLE ) ||
  1832. ( $last_item == TOKEN_BOOLOP && $type == TOKEN_VARIABLE ) ||
  1833. ( $last_item == TOKEN_VARIABLE && $type == TOKEN_PARENTHRIGHT ) ||
  1834. ( $last_item == TOKEN_PARENTHRIGHT && $type == TOKEN_BOOLOP )
  1835. )
  1836. {
  1837. // sequence is good, continue
  1838. }
  1839. else
  1840. {
  1841. // sequence is invalid, break out
  1842. return 5;
  1843. }
  1844. $last_item = $type;
  1845. }
  1846. // passed all checks
  1847. $seqcheck = implode(' ', $seqcheck);
  1848. $result = eval("return ( $seqcheck ) ? true : false;");
  1849. return ( $result ) ? 2 : 1;
  1850. }
  1851. /**
  1852. * Print a text field that auto-completes a username entered into it.
  1853. * @param string $name - the name of the form field
  1854. * @return string
  1855. */
  1856. function username_field($name, $value = false)
  1857. {
  1858. global $db, $session, $paths, $template, $plugins; // Common objects
  1859. $value = $value ? htmlspecialchars($value) : false;
  1860. $val = $value ? "value=\"" . $value . "\"" : "";
  1861. if ( $session->user_id == 1 && getConfig('autofill_username_for_guests', 0) != 1 )
  1862. return "<input name=\"$name\" type=\"text\" size=\"30\" $val />";
  1863. $randomid = md5( time() . microtime() . mt_rand() );
  1864. $text = '<input name="'.$name.'" class="autofill username" onkeyup="new AutofillUsername(this);" type="text" size="30" id="userfield_'.$randomid.'" autocomplete="off"';
  1865. if($value) $text .= ' value="'.$value.'"';
  1866. $text .= ' />';
  1867. return $text;
  1868. }
  1869. /**
  1870. * Print a text field that auto-completes a page name entered into it.
  1871. * @param string $name - the name of the form field
  1872. * @return string
  1873. */
  1874. function pagename_field($name, $value = false)
  1875. {
  1876. $randomid = md5( time() . microtime() . mt_rand() );
  1877. $text = '<input name="'.$name.'" class="autofill page" onkeyup="new AutofillPage(this);" type="text" size="30" id="pagefield_'.$randomid.'" autocomplete="off"';
  1878. if($value) $text .= ' value="'.$value.'"';
  1879. $text .= ' />';
  1880. return $text;
  1881. }
  1882. /**
  1883. * Sends a textarea that can be converted to and from a TinyMCE widget on the fly.
  1884. * @param string The name of the form element
  1885. * @param string The initial content. Optional, defaults to blank
  1886. * @param int Rows in textarea
  1887. * @param int Columns in textarea
  1888. * @return string HTML and Javascript code.
  1889. */
  1890. function tinymce_textarea($name, $content = '', $rows = 20, $cols = 60)
  1891. {
  1892. global $lang;
  1893. $randomid = md5(microtime() . mt_rand());
  1894. $html = '';
  1895. $html .= '<textarea name="' . $name . '" rows="'.$rows.'" cols="'.$cols.'" style="width: 100%;" id="toggleMCEroot_'.$randomid.'">' . $content . '</textarea>';
  1896. /*
  1897. $html .= '<div style="float: right; display: table;" id="mceSwitchAgent_' . $randomid . '">' . $lang->get('etc_tinymce_btn_text') . '&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">' . $lang->get('etc_tinymce_btn_graphical') . '</a></div>';
  1898. $html .= '<script type="text/javascript">
  1899. // <![CDATA[
  1900. function toggleMCE_'.$randomid.'()
  1901. {
  1902. var the_obj = document.getElementById(\'toggleMCEroot_' . $randomid . '\');
  1903. var panel = document.getElementById(\'mceSwitchAgent_' . $randomid . '\');
  1904. var text_editor = $lang.get("etc_tinymce_btn_text");
  1905. var graphical_editor = $lang.get("etc_tinymce_btn_graphical");
  1906. if ( the_obj.dnIsMCE == "yes" )
  1907. {
  1908. $dynano(the_obj).destroyMCE();
  1909. panel.innerHTML = text_editor + \'&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">\' + graphical_editor + \'</a>\';
  1910. }
  1911. else
  1912. {
  1913. $dynano(the_obj).switchToMCE();
  1914. panel.innerHTML = \'<a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">\' + text_editor + \'</a>&nbsp;&nbsp;|&nbsp;&nbsp;\' + graphical_editor;
  1915. }
  1916. }
  1917. // ]]>
  1918. </script>';
  1919. */
  1920. return $html;
  1921. }
  1922. /**
  1923. * Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;)
  1924. * Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class.
  1925. * @param $filename the filename of the template to be parsed
  1926. * @return object
  1927. */
  1928. function makeParser($filename)
  1929. {
  1930. global $db, $session, $paths, $template, $plugins; // Common objects
  1931. $filename = ENANO_ROOT.'/themes/'.$template->theme.'/'.$filename;
  1932. if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist');
  1933. $code = file_get_contents($filename);
  1934. $parser = new templateIndividual($code);
  1935. return $parser;
  1936. }
  1937. /**
  1938. * Same as $template->makeParser(), but takes a string instead of a filename.
  1939. * @param $text the text to parse
  1940. * @return object
  1941. */
  1942. function makeParserText($code)
  1943. {
  1944. $parser = new templateIndividual($code);
  1945. return $parser;
  1946. }
  1947. /**
  1948. * Fetch the HTML for a plugin-added sidebar block
  1949. * @param $name the plugin name
  1950. * @return string
  1951. */
  1952. function fetch_block($id, $just_the_innards_maam = false)
  1953. {
  1954. if ( $just_the_innards_maam )
  1955. {
  1956. $source =& $this->plugin_blocks_content;
  1957. }
  1958. else
  1959. {
  1960. $source =& $this->plugin_blocks;
  1961. }
  1962. return isset($source[$id]) ? $source[$id] : false;
  1963. }
  1964. /**
  1965. * Fetches the contents of both sidebars.
  1966. * @return array - key 0 is left, key 1 is right, key 2 is the HTML that makes up an empty sidebar
  1967. * @example list($left, $right) = $template->fetch_sidebar();
  1968. */
  1969. function fetch_sidebar()
  1970. {
  1971. global $db, $session, $paths, $template, $plugins; // Common objects
  1972. // first, check the cache
  1973. if ( $result = $this->fetch_cached_sidebar() )
  1974. {
  1975. return $result;
  1976. }
  1977. require(ENANO_ROOT . "/themes/{$this->theme}/theme.cfg");
  1978. profiler_log('Started sidebar parsing');
  1979. // init our block contents
  1980. $left = '';
  1981. $right = '';
  1982. $min = '';
  1983. // also might want the links block
  1984. if ( !$this->fetch_block('Links') )
  1985. $this->initLinksWidget();
  1986. // templates to work with
  1987. $vars = $this->extract_vars('elements.tpl');
  1988. // sidebar_top and sidebar_bottom are HTML that is prepended/appended to sidebar content. Themes can
  1989. // choose whether or not to display a sidebar depending on whether the sidebar is empty ( $left == $min )
  1990. // or not using the sidebar_left and sidebar_right template booleans (the oxygen theme does this).
  1991. if ( isset($vars['sidebar_top']) )
  1992. {
  1993. $top = $this->parse($vars['sidebar_top']);
  1994. $left .= $top;
  1995. $right .= $top;
  1996. $min .= $top;
  1997. }
  1998. // grab the blocks from the DB
  1999. $q = $db->sql_query('SELECT item_id,sidebar_id,block_name,block_type,block_content FROM '.table_prefix.'sidebar' . "\n"
  2000. . ' WHERE item_enabled=1 ORDER BY sidebar_id ASC, item_order ASC;');
  2001. if ( !$q )
  2002. $db->_die('The sidebar text data could not be selected.');
  2003. // explicitly specify $q in case a plugin or PHP block makes a query
  2004. while ( $row = $db->fetchrow($q) )
  2005. {
  2006. // should we skip this block?
  2007. if (
  2008. ( $row['item_id'] === 2 && !empty($theme['sb_hide_tools']) ) ||
  2009. ( $row['item_id'] === 3 && !empty($theme['sb_hide_user']) ) ||
  2010. ( $row['item_id'] === 4 && !empty($theme['sb_hide_search']) )
  2011. )
  2012. continue;
  2013. // format the block
  2014. $block_content = $this->format_sidebar_block($row, $vars, $parser);
  2015. // is there a {restrict} or {hideif} block?
  2016. if ( preg_match('/\{(restrict|hideif) ([a-z0-9_\(\)\|&! ]+)\}/', $block_content, $match) )
  2017. {
  2018. // we have one, check the condition
  2019. $type =& $match[1];
  2020. $cond =& $match[2];
  2021. $result = $this->process_condition($cond);
  2022. if ( ( $type == 'restrict' && $result == 1 ) || ( $type == 'hideif' && $result == 2 ) )
  2023. {
  2024. // throw out this block, it's hidden for whatever reason by the sidebar script
  2025. continue;
  2026. }
  2027. // didn't get a match, so hide the conditional logic
  2028. // FIXME: this needs to be verbose about syntax errors
  2029. $block_content = str_replace_once($match[0], '', $block_content);
  2030. }
  2031. // if we made it here, this block definitely needs to be displayed. send it to the
  2032. // parser (set by format_sidebar_block) and decide where to append it (but not in that order ;))
  2033. $appender = false;
  2034. if ( $row['sidebar_id'] == SIDEBAR_LEFT )
  2035. {
  2036. $appender =& $left;
  2037. }
  2038. else if ( $row['sidebar_id'] == SIDEBAR_RIGHT )
  2039. {
  2040. $appender =& $right;
  2041. }
  2042. else
  2043. {
  2044. // uhoh, block_id isn't valid. maybe a plugin wants to put this block somewhere else?
  2045. $code = $plugins->setHook('sidebar_block_id');
  2046. foreach ( $code as $cmd )
  2047. {
  2048. eval($cmd);
  2049. }
  2050. // if $appender wasn't set by a plugin, don't parse this block to save some CPU cycles
  2051. if ( !$appender )
  2052. {
  2053. continue;
  2054. }
  2055. }
  2056. // assign variables to parser
  2057. $block_name = $this->tplWikiFormat($row['block_name']);
  2058. $parser->assign_vars(array(
  2059. // be nice and format the title (FIXME, does this use a lot of CPU time? still needs l10n in some cases though)
  2060. 'TITLE' => $block_name,
  2061. 'CONTENT' => $block_content
  2062. ));
  2063. $parsed = $parser->run();
  2064. // plugins are parsed earlier due to the way disabled/missing plugins that add sidebar blocks are
  2065. // handled, so the {TITLE} var won't be replaced until now. this allows completely eliminating a
  2066. // block if it's not available
  2067. if ( $row['block_type'] == BLOCK_PLUGIN )
  2068. {
  2069. $parsed = str_replace('{TITLE}', $block_name, $parsed);
  2070. }
  2071. // done parsing - append and continue
  2072. $appender .= $parsed;
  2073. // we're done with this - unset it because it's a reference and we don't want it overwritten.
  2074. // also free the parser to get some RAM back
  2075. unset($appender, $parser);
  2076. }
  2077. // lastly, append any footer HTML
  2078. if(isset($vars['sidebar_bottom']))
  2079. {
  2080. $bottom = $this->parse($vars['sidebar_bottom']);
  2081. $left .= $bottom;
  2082. $right .= $bottom;
  2083. $min .= $bottom;
  2084. }
  2085. $return = array($left, $right, $min);
  2086. // allow any plugins to append what they want to the return
  2087. $code = $plugins->setHook('sidebar_fetch_return');
  2088. foreach ( $code as $cmd )
  2089. {
  2090. eval($cmd);
  2091. }
  2092. // cache the result if appropriate
  2093. $this->cache_compiled_sidebar($return);
  2094. profiler_log('Finished sidebar parsing');
  2095. return $return;
  2096. }
  2097. /**
  2098. * Runs the appropriate formatting routine on a sidebar row.
  2099. * @param array Row in sidebar table
  2100. * @param array Template variable set from elements.tpl
  2101. * @param null Pass by reference, will be filled with the parser according to the block's type (sidebar_section or sidebar_section_raw)
  2102. * @return string HTML + directives like {restrict} or {hideif}
  2103. */
  2104. function format_sidebar_block($row, $vars, &$parser)
  2105. {
  2106. // import globals in case a PHP block wants to call the Enano API
  2107. global $db, $session, $paths, $template, $plugins; // Common objects
  2108. $parser = null;
  2109. switch($row['block_type'])
  2110. {
  2111. case BLOCK_WIKIFORMAT:
  2112. $parser = $this->makeParserText($vars['sidebar_section_raw']);
  2113. $c = RenderMan::render($row['block_content']);
  2114. break;
  2115. case BLOCK_TEMPLATEFORMAT:
  2116. $parser = $this->makeParserText($vars['sidebar_section']);
  2117. $c = $this->tplWikiFormat($row['block_content']);
  2118. break;
  2119. case BLOCK_HTML:
  2120. $parser = $this->makeParserText($vars['sidebar_section_raw']);
  2121. $c = $row['block_content'];
  2122. break;
  2123. case BLOCK_PHP:
  2124. // PHP blocks need to be sent directly to eval()
  2125. $parser = $this->makeParserText($vars['sidebar_section_raw']);
  2126. ob_start();
  2127. @eval($row['block_content']);
  2128. $c = ob_get_contents();
  2129. ob_end_clean();
  2130. break;
  2131. case BLOCK_PLUGIN:
  2132. $parser = $this->makeParserText('{CONTENT}');
  2133. $c = '<!-- PLUGIN -->' . (gettype($this->fetch_block($row['block_content'])) == 'string') ?
  2134. $this->fetch_block($row['block_content']) :
  2135. // This used to say "can't find plugin block" but I think it's more friendly to just silently hide it.
  2136. '';
  2137. break;
  2138. default:
  2139. // unknown block type - can a plugin handle it?
  2140. $code = $plugins->setHook('format_sidebar_block');
  2141. foreach ( $code as $cmd )
  2142. {
  2143. eval($cmd);
  2144. }
  2145. if ( !isset($c) )
  2146. {
  2147. // default to wiki formatting (this was going to be straight HTML but this is done for backwards compatibility reasons)
  2148. $c = RenderMan::render($row['block_content']);
  2149. }
  2150. if ( !$parser )
  2151. {
  2152. // no parser defined, use the "raw" section by default (plugins are more likely to want raw content
  2153. // rather than a list of links, and they can set the parser to use sidebar_section if they want)
  2154. $parser = $this->makeParserText($vars['sidebar_section_raw']);
  2155. }
  2156. break;
  2157. }
  2158. return $c;
  2159. }
  2160. /**
  2161. * Returns the list of things that should not be cached (sorry, I was listening to "The Thing That Should Not Be" by Metallica when I
  2162. * wrote this routine. You should get a copy of Master of Puppets, it's a damn good album.)
  2163. * @return array
  2164. */
  2165. function get_cache_replacements()
  2166. {
  2167. global $db, $session, $paths, $template, $plugins; // Common objects
  2168. return array(
  2169. '$LOGIN_LINK$' => $this->tpl_strings['LOGIN_LINK'],
  2170. '$MAIN_PAGE$' => $this->tpl_strings['MAIN_PAGE'],
  2171. '$USERNAME$' => $session->username
  2172. );
  2173. }
  2174. /**
  2175. * Attempts to load a cached compiled sidebar.
  2176. * @return array Same format as fetch_sidebar()
  2177. */
  2178. function fetch_cached_sidebar()
  2179. {
  2180. global $db, $session, $paths, $template, $plugins; // Common objects
  2181. global $cache;
  2182. $cached = false;
  2183. if ( ($result = $cache->fetch('anon_sidebar')) && !$session->user_logged_in )
  2184. {
  2185. if ( isset($result[$this->theme]) )
  2186. {
  2187. $cached = $result[$this->theme];
  2188. }
  2189. }
  2190. // if we haven't been able to fetch yet, see if a plugin wants to give us something
  2191. if ( !$cached )
  2192. {
  2193. $code = $plugins->setHook('fetch_cached_sidebar');
  2194. foreach ( $code as $cmd )
  2195. {
  2196. eval($cmd);
  2197. }
  2198. }
  2199. if ( is_array($cached) )
  2200. {
  2201. // fetch certain variables that can't be stored in the cache and quickly substitute
  2202. $replace = $this->get_cache_replacements();
  2203. foreach ( $cached as &$val )
  2204. {
  2205. $val = strtr($val, $replace);
  2206. }
  2207. // done processing, send it back
  2208. return $cached;
  2209. }
  2210. return false;
  2211. }
  2212. /**
  2213. * Caches a completely compiled sidebar, if appropriate
  2214. * @param array Effectively, return from fetch_sidebar()
  2215. */
  2216. function cache_compiled_sidebar($sidebar)
  2217. {
  2218. global $db, $session, $paths, $template, $plugins; // Common objects
  2219. global $cache;
  2220. // check if conditions are right
  2221. if ( !$session->user_logged_in && getConfig('cache_thumbs') === '1' )
  2222. {
  2223. // load any existing cache to make sure other themes' cached sidebars aren't discarded
  2224. $cached = ( $_ = $cache->fetch('anon_sidebar') ) ? $_ : array();
  2225. // replace variables
  2226. $replace = array_flip($this->get_cache_replacements());
  2227. foreach ( $sidebar as &$section )
  2228. {
  2229. $section = strtr($section, $replace);
  2230. }
  2231. // compile
  2232. $cached[$this->theme] = $sidebar;
  2233. // store
  2234. $cache->store('anon_sidebar', $cached, 15);
  2235. }
  2236. }
  2237. function initLinksWidget()
  2238. {
  2239. global $db, $session, $paths, $template, $plugins; // Common objects
  2240. // SourceForge/W3C buttons
  2241. $ob = Array();
  2242. if(getConfig('sflogo_enabled')=='1')
  2243. {
  2244. $sflogo_secure = ( isset($_SERVER['HTTPS']) ) ? 'https' : 'http';
  2245. $ob[] = '<a style="text-align: center;" href="http://sourceforge.net/" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border-width: 0px;" alt="SourceForge.net Logo" src="' . $sflogo_secure . '://sflogo.sourceforge.net/sflogo.php?group_id='.getConfig('sflogo_groupid').'&amp;type='.getConfig('sflogo_type').'" /></a>';
  2246. }
  2247. if(getConfig('w3c_v32') =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid HTML 3.2" src="http://www.w3.org/Icons/valid-html32" /></a>';
  2248. if(getConfig('w3c_v40') =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid HTML 4.0" src="http://www.w3.org/Icons/valid-html40" /></a>';
  2249. if(getConfig('w3c_v401') =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid HTML 4.01" src="http://www.w3.org/Icons/valid-html401" /></a>';
  2250. if(getConfig('w3c_vxhtml10')=='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid XHTML 1.0" src="http://www.w3.org/Icons/valid-xhtml10" /></a>';
  2251. if(getConfig('w3c_vxhtml11')=='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid XHTML 1.1" src="http://www.w3.org/Icons/valid-xhtml11" /></a>';
  2252. if(getConfig('w3c_vcss') =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid CSS" src="http://www.w3.org/Icons/valid-css" /></a>';
  2253. if(getConfig('dbd_button') =='1') $ob[] = '<a style="text-align: center;" href="http://www.defectivebydesign.org/join/button" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="DRM technology restricts what you can do with your computer" src="' . cdnPath . '/images/defectivebydesign.png" /><br /><small>Protect your freedom >></small></a>';
  2254. $code = $plugins->setHook('links_widget');
  2255. foreach ( $code as $cmd )
  2256. {
  2257. eval($cmd);
  2258. }
  2259. if(count($ob) > 0 || getConfig('powered_btn', '1') == '1') $sb_links = '<div style="text-align: center; padding: 5px 0;">'. ( ( getConfig('powered_btn', '1') == '1' ) ? $this->fading_button : '' ) . implode('<br />', $ob).'</div>';
  2260. else $sb_links = '';
  2261. $this->sidebar_widget('Links', $sb_links);
  2262. }
  2263. /**
  2264. * Builds a box showing unread private messages.
  2265. */
  2266. function notify_unread_pms()
  2267. {
  2268. global $db, $session, $paths, $template, $plugins; // Common objects
  2269. if ( ( $paths->page_id == 'PrivateMessages' || $paths->page_id == 'Preferences' ) && $paths->namespace == 'Special' )
  2270. {
  2271. return '';
  2272. }
  2273. $ob = '<div class="usermessage">'."\n";
  2274. $s = ( $session->unread_pms == 1 ) ? '' : 's';
  2275. $ob .= " <b>You have $session->unread_pms <a href=" . '"' . makeUrlNS('Special', 'PrivateMessages' ) . '"' . ">unread private message$s</a>.</b><br />\n Messages: ";
  2276. $q = $db->sql_query('SELECT message_id,message_from,subject,date FROM '.table_prefix.'privmsgs WHERE message_to=\'' . $session->username . '\' AND message_read=0 ORDER BY date DESC;');
  2277. if ( !$q )
  2278. $db->_die();
  2279. $messages = array();
  2280. while ( $row = $db->fetchrow() )
  2281. {
  2282. $messages[] = '<a href="' . makeUrlNS('Special', 'PrivateMessages/View/' . $row['message_id']) . '" title="Sent ' . enano_date(ED_DATE | ED_TIME, $row['date']) . ' by ' . $row['message_from'] . '">' . $row['subject'] . '</a>';
  2283. }
  2284. $ob .= implode(",\n " , $messages)."\n";
  2285. $ob .= '</div>'."\n";
  2286. return $ob;
  2287. }
  2288. /**
  2289. * Parse a system message.
  2290. * @param string message
  2291. * @return string
  2292. */
  2293. function parse_system_message($text)
  2294. {
  2295. ob_start();
  2296. eval( '?>' . $text );
  2297. $result = ob_get_contents();
  2298. ob_end_clean();
  2299. return $this->parse($result);
  2300. }
  2301. /**
  2302. * Return the wiki mode edit notice, rendered and addslashes()'ed.
  2303. * @return string
  2304. */
  2305. function get_wiki_edit_notice()
  2306. {
  2307. global $cache;
  2308. if ( getConfig('wiki_edit_notice', 0) != 1 )
  2309. return '';
  2310. $notice = RenderMan::render(getConfig('wiki_edit_notice_text'));
  2311. return $notice;
  2312. }
  2313. } // class template
  2314. /**
  2315. * The core of the template compilation engine. Independent from the Enano API for failsafe operation.
  2316. * @param string text to process
  2317. * @return string Compiled PHP code
  2318. * @access private
  2319. */
  2320. function template_compiler_core($text)
  2321. {
  2322. global $db, $session, $paths, $template, $plugins; // Common objects
  2323. // A random seed used to salt tags
  2324. $seed = md5 ( microtime() . mt_rand() );
  2325. // Strip out PHP sections
  2326. preg_match_all('/<\?php(.+?)\?>/is', $text, $php_matches);
  2327. foreach ( $php_matches[0] as $i => $match )
  2328. {
  2329. // Substitute the PHP section with a random tag
  2330. $tag = "{PHP:$i:$seed}";
  2331. $text = str_replace_once($match, $tag, $text);
  2332. }
  2333. // Escape slashes and single quotes in template code
  2334. $text = str_replace('\\', '\\\\', $text);
  2335. $text = str_replace('\'', '\\\'', $text);
  2336. // Initialize the PHP compiled code
  2337. $text = 'ob_start(); global $paths, $template; echo \''.$text.'\'; $tpl_code = ob_get_contents(); ob_end_clean(); return $tpl_code;';
  2338. ##
  2339. ## Main rules
  2340. ##
  2341. //
  2342. // Conditionals
  2343. //
  2344. $keywords = array('BEGIN', 'BEGINNOT', 'IFSET', 'IFPLUGIN');
  2345. // only do this if the plugins API is loaded
  2346. if ( is_object(@$plugins) )
  2347. {
  2348. $code = $plugins->setHook('template_compile_logic_keyword');
  2349. foreach ( $code as $cmd )
  2350. {
  2351. eval($cmd);
  2352. }
  2353. }
  2354. $keywords = implode('|', $keywords);
  2355. // Matches
  2356. // 1 2 3 4 56 7 8 9
  2357. $regexp = '/(<!-- ?(' . $keywords . ') ([A-z0-9_-]+) ?-->)([\w\W]*)((<!-- ?BEGINELSE \\3 ?-->)([\w\W]*))?(<!-- ?END(IF)? \\3 ?-->)/isU';
  2358. /*
  2359. The way this works is: match all blocks using the standard form with a different keyword in the block each time,
  2360. and replace them with appropriate PHP logic. Plugin-extensible now. :-)
  2361. */
  2362. // This is a workaround for what seems like a PCRE bug
  2363. while ( preg_match_all($regexp, $text, $matches) )
  2364. {
  2365. for ( $i = 0; $i < count($matches[0]); $i++ )
  2366. {
  2367. $start_tag =& $matches[1][$i];
  2368. $type =& $matches[2][$i];
  2369. $test =& $matches[3][$i];
  2370. $particle_true =& $matches[4][$i];
  2371. $else_tag =& $matches[6][$i];
  2372. $particle_else =& $matches[7][$i];
  2373. $end_tag =& $matches[8][$i];
  2374. switch($type)
  2375. {
  2376. case 'BEGIN':
  2377. $cond = "isset(\$this->tpl_bool['$test']) && \$this->tpl_bool['$test']";
  2378. break;
  2379. case 'BEGINNOT':
  2380. $cond = "!isset(\$this->tpl_bool['$test']) || ( isset(\$this->tpl_bool['$test']) && !\$this->tpl_bool['$test'] )";
  2381. break;
  2382. case 'IFPLUGIN':
  2383. $cond = "getConfig('plugin_$test') == '1'";
  2384. break;
  2385. case 'IFSET':
  2386. $cond = "isset(\$this->tpl_strings['$test'])";
  2387. break;
  2388. default:
  2389. // only do this if the plugins API is loaded
  2390. if ( is_object(@$plugins) )
  2391. {
  2392. $code = $plugins->setHook('template_compile_logic_cond');
  2393. foreach ( $code as $cmd )
  2394. {
  2395. eval($cmd);
  2396. }
  2397. }
  2398. break;
  2399. }
  2400. if ( !isset($cond) || ( isset($cond) && !is_string($cond) ) )
  2401. continue;
  2402. $tag_complete = <<<TPLCODE
  2403. ';
  2404. /* START OF CONDITION: $type ($test) */
  2405. if ( $cond )
  2406. {
  2407. echo '$particle_true';
  2408. /* ELSE OF CONDITION: $type ($test) */
  2409. }
  2410. else
  2411. {
  2412. echo '$particle_else';
  2413. /* END OF CONDITION: $type ($test) */
  2414. }
  2415. echo '
  2416. TPLCODE;
  2417. $text = str_replace_once($matches[0][$i], $tag_complete, $text);
  2418. }
  2419. }
  2420. // For debugging ;-)
  2421. // die("<pre>&lt;?php\n" . htmlspecialchars($text."\n\n".print_r($matches,true)) . "\n\n?&gt;</pre>");
  2422. //
  2423. // Data substitution/variables
  2424. //
  2425. // System messages
  2426. $text = preg_replace('/<!-- SYSMSG ([A-z0-9\._-]+?) -->/is', '\' . $this->parse_system_message($paths->sysMsg(\'\\1\')) . \'', $text);
  2427. // Hooks
  2428. $text = preg_replace('/<!-- HOOK ([A-z0-9_]+) -->/', '\' . $this->get_theme_hook(\'\\1\') . \'', $text);
  2429. // only do this if the plugins API is loaded
  2430. if ( is_object(@$plugins) )
  2431. {
  2432. $code = $plugins->setHook('template_compile_subst');
  2433. foreach ( $code as $cmd )
  2434. {
  2435. eval($cmd);
  2436. }
  2437. }
  2438. // Template variables
  2439. $text = preg_replace('/\{([A-z0-9_-]+?)\}/is', '\' . $this->tpl_strings[\'\\1\'] . \'', $text);
  2440. // Reinsert PHP
  2441. foreach ( $php_matches[1] as $i => $match )
  2442. {
  2443. // Substitute the random tag with the "real" PHP code
  2444. $tag = "{PHP:$i:$seed}";
  2445. $text = str_replace_once($tag, "'; $match echo '", $text);
  2446. }
  2447. // echo('<pre>' . htmlspecialchars($text) . '</pre>');
  2448. return $text;
  2449. }
  2450. /**
  2451. * Handles parsing of an individual template file. Instances should only be created through $template->makeParser(). To use:
  2452. * - Call $template->makeParser(template file name) - file name should be something.tpl, css/whatever.css, etc.
  2453. * - Make an array of strings you want the template to access. $array['STRING'] would be referenced in the template like {STRING}
  2454. * - Make an array of boolean values. These can be used for conditionals in the template (<!-- IF something --> whatever <!-- ENDIF something -->)
  2455. * - Call assign_vars() to pass the strings to the template parser. Same thing with assign_bool().
  2456. * - Call run() to parse the template and get your fully compiled HTML.
  2457. * @access private
  2458. */
  2459. class templateIndividual extends template
  2460. {
  2461. var $tpl_strings, $tpl_bool, $tpl_code;
  2462. var $compiled = false;
  2463. /**
  2464. * Constructor.
  2465. */
  2466. function __construct($text)
  2467. {
  2468. global $db, $session, $paths, $template, $plugins; // Common objects
  2469. $this->tpl_code = $text;
  2470. $this->tpl_strings = $template->tpl_strings;
  2471. // echo 'templateIndividual init. Using strings: <pre>' . htmlspecialchars(print_r($this->tpl_strings, true)) . '</pre>';
  2472. $this->tpl_bool = $template->tpl_bool;
  2473. }
  2474. /**
  2475. * PHP 4 constructor. Deprecated in 1.1.x.
  2476. */
  2477. /*
  2478. function templateIndividual($text)
  2479. {
  2480. $this->__construct($text);
  2481. }
  2482. */
  2483. /**
  2484. * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
  2485. * @param $vars array
  2486. */
  2487. // We add the unused variable $from_internal here to silence "declaration should be compatible" errors
  2488. function assign_vars($vars, $from_internal = false)
  2489. {
  2490. $this->tpl_strings = array_merge($this->tpl_strings, $vars);
  2491. }
  2492. /**
  2493. * Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements.
  2494. * @param $vars array
  2495. */
  2496. // We add the unused variable $from_internal here to silence "declaration should be compatible" errors
  2497. function assign_bool($vars, $from_internal = false)
  2498. {
  2499. $this->tpl_bool = array_merge($this->tpl_bool, $vars);
  2500. }
  2501. /**
  2502. * Compiles and executes the template code.
  2503. * @return string
  2504. */
  2505. function run()
  2506. {
  2507. global $db, $session, $paths, $template, $plugins; // Common objects
  2508. if(!$this->compiled)
  2509. {
  2510. $this->tpl_code = $this->compile_template_text($this->tpl_code);
  2511. $this->compiled = true;
  2512. }
  2513. return eval($this->tpl_code);
  2514. }
  2515. }
  2516. /**
  2517. * A version of the template compiler that does not rely at all on the other parts of Enano. Used during installation and for showing
  2518. * "critical error" messages. ** REQUIRES ** the Oxygen theme.
  2519. */
  2520. class template_nodb
  2521. {
  2522. var $fading_button, $tpl_strings, $tpl_bool, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list;
  2523. function __construct()
  2524. {
  2525. $this->tpl_bool = Array();
  2526. $this->tpl_strings = Array();
  2527. $this->sidebar_extra = '';
  2528. $this->sidebar_widgets = '';
  2529. $this->toolbar_menu = '';
  2530. $this->additional_headers = '<style type="text/css">div.pagenav { border-top: 1px solid #CCC; padding-top: 7px; margin-top: 10px; }</style>';
  2531. $this->fading_button = '<div style="background-image: url('.scriptPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
  2532. <a href="http://enanocms.org/" onclick="window.open(this.href); return false;"><img style="border-width: 0;" alt=" " src="'.scriptPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
  2533. </div>';
  2534. // get list of themes
  2535. $this->theme_list = array();
  2536. $this->named_theme_list = array();
  2537. $order = 0;
  2538. if ( $dir = @opendir( ENANO_ROOT . '/themes' ) )
  2539. {
  2540. while ( $dh = @readdir($dir) )
  2541. {
  2542. if ( $dh == '.' || $dh == '..' || !is_dir( ENANO_ROOT . "/themes/$dh" ) )
  2543. continue;
  2544. $theme_dir = ENANO_ROOT . "/themes/$dh";
  2545. if ( !file_exists("$theme_dir/theme.cfg") )
  2546. continue;
  2547. $data = array(
  2548. 'theme_id' => $dh,
  2549. 'theme_name' => ucwords($dh),
  2550. 'enabled' => 1,
  2551. 'theme_order' => ++$order,
  2552. 'default_style' => $this->get_default_style($dh)
  2553. );
  2554. $this->named_theme_list[$dh] = $data;
  2555. $this->theme_list[] =& $this->named_theme_list[$dh];
  2556. }
  2557. @closedir($dir);
  2558. }
  2559. }
  2560. function template() {
  2561. $this->__construct();
  2562. }
  2563. function get_default_style($theme_id)
  2564. {
  2565. if ( !is_dir( ENANO_ROOT . "/themes/$theme_id/css" ) )
  2566. return false;
  2567. $ds = false;
  2568. if ( $dh = @opendir( ENANO_ROOT . "/themes/$theme_id/css" ) )
  2569. {
  2570. while ( $dir = @readdir($dh) )
  2571. {
  2572. if ( !preg_match('/\.css$/', $dir) )
  2573. continue;
  2574. if ( $dir == '_printable.css' )
  2575. continue;
  2576. $ds = preg_replace('/\.css$/', '', $dir);
  2577. break;
  2578. }
  2579. closedir($dh);
  2580. }
  2581. else
  2582. {
  2583. return false;
  2584. }
  2585. return $ds;
  2586. }
  2587. function get_css($s = false)
  2588. {
  2589. if ( $s )
  2590. return $this->process_template('css/'.$s);
  2591. else
  2592. return $this->process_template('css/'.$this->style.'.css');
  2593. }
  2594. function load_theme($name, $css, $auto_init = true)
  2595. {
  2596. if ( !isset($this->named_theme_list[$name]) )
  2597. $name = $this->theme_list[0]['theme_id'];
  2598. if ( !file_exists(ENANO_ROOT . "/themes/$name/css/$css.css") )
  2599. $css = $this->named_theme_list[$name]['default_style'];
  2600. $this->theme = $name;
  2601. $this->style = $css;
  2602. $this->tpl_strings['SCRIPTPATH'] = scriptPath;
  2603. if ( $auto_init )
  2604. $this->init_vars();
  2605. }
  2606. function add_header($html)
  2607. {
  2608. $this->additional_headers .= "\n<!-- ----------------------------------------------------------- -->\n\n " . $html;
  2609. }
  2610. function init_vars()
  2611. {
  2612. global $sideinfo;
  2613. global $this_page;
  2614. global $lang;
  2615. global $db, $session, $paths, $template, $plugins; // Common objects
  2616. $tplvars = $this->extract_vars('elements.tpl');
  2617. $tb = '';
  2618. // Get the "article" button text (depends on namespace)
  2619. if(defined('IN_ENANO_INSTALL') && is_object($lang)) $ns = $lang->get('meta_btn_article');
  2620. else if ( isset($GLOBALS['article_btn']) ) $ns = $GLOBALS['article_btn'];
  2621. else $ns = 'system error page';
  2622. $t = str_replace('{FLAGS}', 'onclick="return false;" class="current" title="Hey! A button that doesn\'t do anything. Clever..." accesskey="a"', $tplvars['toolbar_button']);
  2623. $t = str_replace('{HREF}', '#', $t);
  2624. $t = str_replace('{TEXT}', $ns, $t);
  2625. $tb .= $t;
  2626. // Page toolbar
  2627. $this->tpl_bool = Array(
  2628. 'auth_admin'=>true,
  2629. 'user_logged_in'=>true,
  2630. 'right_sidebar'=>false,
  2631. );
  2632. $this->tpl_bool['in_sidebar_admin'] = false;
  2633. $this->tpl_bool['auth_rename'] = false;
  2634. $asq = $asa = '';
  2635. $this->tpl_bool['fixed_menus'] = false;
  2636. $slink = defined('IN_ENANO_INSTALL') ? scriptPath.'/install.php?mode=css' : makeUrlNS('Special', 'CSS');
  2637. $title = ( is_object($paths) ) ? $paths->page : 'Critical error';
  2638. $headers = '<style type="text/css">div.pagenav { border-top: 1px solid #CCC; padding-top: 7px; margin-top: 10px; }</style>';
  2639. $js_dynamic = '';
  2640. if ( defined('IN_ENANO_INSTALL') )
  2641. {
  2642. $js_dynamic .= '<script type="text/javascript" src="install.php?mode=langjs"></script>';
  2643. }
  2644. $js_dynamic .= '<script type="text/javascript">var title="'. $title .'"; var scriptPath="'.scriptPath.'"; var cdnPath="'.scriptPath.'"; var ENANO_SID=""; var AES_BITS='.AES_BITS.'; var AES_BLOCKSIZE=' . AES_BLOCKSIZE . '; var pagepass=\'\'; var ENANO_LANG_ID = 1; var enano_version = \'' . enano_version() . '\'; var msg_loading_component = \'Loading %component%...\';</script>';
  2645. global $site_name, $site_desc;
  2646. $site_default_name = ( !empty($site_name) ) ? $site_name : 'Critical error';
  2647. $site_default_desc = ( !empty($site_desc) ) ? $site_desc : 'This site is experiencing a problem and cannot load.';
  2648. $site_name_final = ( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_site_name') : $site_default_name;
  2649. $site_desc_final = ( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_site_desc') : $site_default_desc;
  2650. // The rewritten template engine will process all required vars during the load_template stage instead of (cough) re-processing everything each time around.
  2651. $tpl_strings = Array(
  2652. 'PAGE_NAME'=>$this_page,
  2653. 'PAGE_URLNAME'=>'Null',
  2654. 'SITE_NAME' => $site_name_final,
  2655. 'USERNAME'=>'admin',
  2656. 'SITE_DESC' => $site_desc_final,
  2657. 'TOOLBAR'=>$tb,
  2658. 'SCRIPTPATH'=>scriptPath,
  2659. 'CONTENTPATH'=>contentPath,
  2660. 'CDNPATH' => scriptPath,
  2661. 'JS_HEADER' => '<script type="text/javascript" src="' . scriptPath . '/includes/clientside/static/enano-lib-basic.js"></script>',
  2662. 'JS_FOOTER' => '',
  2663. 'ADMIN_SID_QUES'=>$asq,
  2664. 'ADMIN_SID_AMP'=>$asa,
  2665. 'ADMIN_SID_AMP_HTML'=>'',
  2666. 'ADDITIONAL_HEADERS'=>$this->additional_headers,
  2667. 'SIDEBAR_EXTRA'=>'',
  2668. 'COPYRIGHT'=>( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_enano_copyright') : ( defined('ENANO_CONFIG_FETCHED') ? getConfig('copyright_notice') : '' ),
  2669. 'TOOLBAR_EXTRAS'=>'',
  2670. 'REQUEST_URI'=>( isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '' ).$_SERVER['REQUEST_URI'],
  2671. 'STYLE_LINK'=>$slink,
  2672. 'LOGOUT_LINK'=>'',
  2673. 'THEME_LINK'=>'',
  2674. 'TEMPLATE_DIR'=>scriptPath.'/themes/'.$this->theme,
  2675. 'THEME_ID'=>$this->theme,
  2676. 'STYLE_ID'=>$this->style,
  2677. 'JS_DYNAMIC_VARS'=>$js_dynamic,
  2678. 'SIDEBAR_RIGHT'=>'',
  2679. 'REPORT_URI' => '',
  2680. 'URL_ABOUT_ENANO' => 'http://enanocms.org/',
  2681. 'ENANO_VERSION' => enano_version()
  2682. );
  2683. $this->tpl_strings = array_merge($tpl_strings, $this->tpl_strings);
  2684. $sidebar = ( is_array(@$sideinfo) ) ? $sideinfo : '';
  2685. if ( $sidebar != '' )
  2686. {
  2687. if ( isset($tplvars['sidebar_top']) )
  2688. {
  2689. $text = $this->makeParserText($tplvars['sidebar_top']);
  2690. $top = $text->run();
  2691. }
  2692. else
  2693. {
  2694. $top = '';
  2695. }
  2696. $p = $this->makeParserText($tplvars['sidebar_section']);
  2697. $b = $this->makeParserText($tplvars['sidebar_button']);
  2698. $sidebar_text = '';
  2699. foreach ( $sidebar as $title => $links )
  2700. {
  2701. $p->assign_vars(array(
  2702. 'TITLE' => $title
  2703. ));
  2704. // build content
  2705. $content = '';
  2706. foreach ( $links as $link_text => $url )
  2707. {
  2708. $b->assign_vars(array(
  2709. 'HREF' => htmlspecialchars($url),
  2710. 'FLAGS' => '',
  2711. 'TEXT' => $link_text
  2712. ));
  2713. $content .= $b->run();
  2714. }
  2715. $p->assign_vars(array(
  2716. 'CONTENT' => $content
  2717. ));
  2718. $sidebar_text .= $p->run();
  2719. }
  2720. if ( isset($tplvars['sidebar_bottom']) )
  2721. {
  2722. $text = $this->makeParserText($tplvars['sidebar_bottom']);
  2723. $bottom = $text->run();
  2724. }
  2725. else
  2726. {
  2727. $bottom = '';
  2728. }
  2729. $sidebar = $top . $sidebar_text . $bottom;
  2730. }
  2731. $this->tpl_strings['SIDEBAR_LEFT'] = $sidebar;
  2732. $this->tpl_bool['sidebar_left'] = ( $this->tpl_strings['SIDEBAR_LEFT'] != '') ? true : false;
  2733. $this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != '') ? true : false;
  2734. $this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility
  2735. $this->tpl_bool['stupid_mode'] = true;
  2736. }
  2737. function header($simple = false)
  2738. {
  2739. $filename = ( $simple ) ? 'simple-header.tpl' : 'header.tpl';
  2740. if ( !$this->no_headers )
  2741. {
  2742. echo $this->process_template($filename);
  2743. }
  2744. }
  2745. function footer($simple = false)
  2746. {
  2747. global $db, $session, $paths, $template, $plugins; // Common objects
  2748. global $lang;
  2749. if(!$this->no_headers) {
  2750. global $_starttime;
  2751. $filename = ( $simple ) ? 'simple-footer.tpl' : 'footer.tpl';
  2752. $t = $this->process_template($filename);
  2753. $f = microtime_float();
  2754. $f = $f - $_starttime;
  2755. $f = round($f, 4);
  2756. if ( is_object($lang) )
  2757. {
  2758. $t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f));
  2759. $t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f));
  2760. $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . $lang->get('page_msg_stats_sql', array('nq' => ( is_object($db) ? $db->num_queries : 'N/A' ))) . '</a>';
  2761. $dbg = $t_loc;
  2762. $dbg_long = $t_loc_long;
  2763. if ( $session->user_level >= USER_LEVEL_ADMIN )
  2764. {
  2765. $dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
  2766. $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
  2767. }
  2768. $t = str_replace('[[EnanoPoweredLink]]', $lang->get('page_enano_powered', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
  2769. $t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
  2770. }
  2771. else
  2772. {
  2773. $t_loc = "Time: {$f}s";
  2774. $t_loc_long = "Generated in {$f}sec";
  2775. $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . ( is_object($db) ? "{$db->num_queries} SQL" : 'Queries: N/A' ) . '</a>';
  2776. $dbg = $t_loc;
  2777. $dbg_long = $t_loc_long;
  2778. if ( is_object($session) )
  2779. {
  2780. if ( $session->user_level >= USER_LEVEL_ADMIN )
  2781. {
  2782. $dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
  2783. $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
  2784. }
  2785. }
  2786. $t = str_replace('[[EnanoPoweredLink]]', 'Powered by <a href="http://enanocms.org/" onclick="window.open(this.href); return false;">Enano</a>', $t);
  2787. $t = str_replace('[[EnanoPoweredLinkLong]]', 'Website engine powered by <a href="http://enanocms.org/" onclick="window.open(this.href); return false;">Enano</a>', $t);
  2788. }
  2789. $t = str_replace('[[Stats]]', $dbg, $t);
  2790. $t = str_replace('[[StatsLong]]', $dbg_long, $t);
  2791. $t = str_replace('[[NumQueries]]', ( is_object($db) ? (string)$db->num_queries : '0' ), $t);
  2792. $t = str_replace('[[GenTime]]', (string)$f, $t);
  2793. $t = str_replace('[[NumQueriesLoc]]', $q_loc, $t);
  2794. $t = str_replace('[[GenTimeLoc]]', $t_loc, $t);
  2795. if ( defined('ENANO_PROFILE') )
  2796. {
  2797. $t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t);
  2798. }
  2799. echo $t;
  2800. }
  2801. else return '';
  2802. }
  2803. function getHeader()
  2804. {
  2805. if(!$this->no_headers) return $this->process_template('header.tpl');
  2806. else return '';
  2807. }
  2808. function getFooter()
  2809. {
  2810. global $db, $session, $paths, $template, $plugins; // Common objects
  2811. if(!$this->no_headers) {
  2812. global $_starttime;
  2813. $f = microtime(true);
  2814. $f = $f - $_starttime;
  2815. $f = round($f, 4);
  2816. if(defined('IN_ENANO_INSTALL')) $nq = 'N/A';
  2817. else $nq = $db->num_queries;
  2818. if($nq == 0) $nq = 'N/A';
  2819. $dbg = 'Time: '.$f.'s | Queries: '.$nq;
  2820. if($nq == 0) $nq = 'N/A';
  2821. $t = $this->process_template('footer.tpl');
  2822. $t = str_replace('[[Stats]]', $dbg, $t);
  2823. return $t;
  2824. }
  2825. else return '';
  2826. }
  2827. function process_template($file)
  2828. {
  2829. $compiled = $this->compile_template($file);
  2830. $result = eval($compiled);
  2831. return $result;
  2832. }
  2833. function extract_vars($file) {
  2834. global $db, $session, $paths, $template, $plugins; // Common objects
  2835. if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file)) die('Cannot find '.$file.' file for style "'.$this->theme.'", exiting');
  2836. $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file);
  2837. preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches);
  2838. $tplvars = Array();
  2839. for($i=0;$i<sizeof($matches[1]);$i++)
  2840. {
  2841. $tplvars[$matches[1][$i]] = $matches[2][$i];
  2842. }
  2843. return $tplvars;
  2844. }
  2845. function compile_template($text)
  2846. {
  2847. $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text);
  2848. return $this->compile_template_text_post(template_compiler_core($text));
  2849. }
  2850. function compile_template_text($text)
  2851. {
  2852. return $this->compile_template_text_post(template_compiler_core($text));
  2853. }
  2854. /**
  2855. * Post-processor for template code. Basically what this does is it localizes {lang:foo} blocks.
  2856. * @param string Mostly-processed TPL code
  2857. * @return string
  2858. */
  2859. function compile_template_text_post($text)
  2860. {
  2861. global $lang;
  2862. preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $text, $matches);
  2863. foreach ( $matches[1] as $i => $string_id )
  2864. {
  2865. if ( is_object(@$lang) )
  2866. {
  2867. $string = $lang->get($string_id);
  2868. }
  2869. else
  2870. {
  2871. $string = '[language not loaded]';
  2872. }
  2873. $string = str_replace('\\', '\\\\', $string);
  2874. $string = str_replace('\'', '\\\'', $string);
  2875. $text = str_replace_once($matches[0][$i], $string, $text);
  2876. }
  2877. return $text;
  2878. }
  2879. /**
  2880. * Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;)
  2881. * Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class.
  2882. * @param $filename the filename of the template to be parsed
  2883. * @return object
  2884. */
  2885. function makeParser($filename)
  2886. {
  2887. $filename = ENANO_ROOT.'/themes/'.$this->theme.'/'.$filename;
  2888. if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist');
  2889. $code = file_get_contents($filename);
  2890. $parser = new templateIndividualSafe($code, $this);
  2891. return $parser;
  2892. }
  2893. /**
  2894. * Same as $template->makeParser(), but takes a string instead of a filename.
  2895. * @param $text the text to parse
  2896. * @return object
  2897. */
  2898. function makeParserText($code)
  2899. {
  2900. $parser = new templateIndividualSafe($code, $this);
  2901. return $parser;
  2902. }
  2903. /**
  2904. * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
  2905. * @param $vars array
  2906. */
  2907. function assign_vars($vars, $_ignored = false)
  2908. {
  2909. if(is_array($this->tpl_strings))
  2910. $this->tpl_strings = array_merge($this->tpl_strings, $vars);
  2911. else
  2912. $this->tpl_strings = $vars;
  2913. }
  2914. function get_theme_hook()
  2915. {
  2916. return '';
  2917. }
  2918. } // class template_nodb
  2919. /**
  2920. * Identical to templateIndividual, except extends template_nodb instead of template
  2921. * @see class template
  2922. */
  2923. class templateIndividualSafe extends template_nodb
  2924. {
  2925. var $tpl_strings, $tpl_bool, $tpl_code;
  2926. var $compiled = false;
  2927. /**
  2928. * Constructor.
  2929. */
  2930. function __construct($text, $parent)
  2931. {
  2932. global $db, $session, $paths, $template, $plugins; // Common objects
  2933. $this->tpl_code = $text;
  2934. $this->tpl_strings = $parent->tpl_strings;
  2935. $this->tpl_bool = $parent->tpl_bool;
  2936. }
  2937. /**
  2938. * PHP 4 constructor.
  2939. */
  2940. function templateIndividual($text)
  2941. {
  2942. $this->__construct($text);
  2943. }
  2944. /**
  2945. * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
  2946. * @param $vars array
  2947. */
  2948. function assign_vars($vars, $_ignored = false)
  2949. {
  2950. if(is_array($this->tpl_strings))
  2951. $this->tpl_strings = array_merge($this->tpl_strings, $vars);
  2952. else
  2953. $this->tpl_strings = $vars;
  2954. }
  2955. /**
  2956. * Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements.
  2957. * @param $vars array
  2958. */
  2959. function assign_bool($vars)
  2960. {
  2961. $this->tpl_bool = array_merge($this->tpl_bool, $vars);
  2962. }
  2963. /**
  2964. * Compiles and executes the template code.
  2965. * @return string
  2966. */
  2967. function run()
  2968. {
  2969. global $db, $session, $paths, $template, $plugins; // Common objects
  2970. if(!$this->compiled)
  2971. {
  2972. $this->tpl_code = $this->compile_template_text($this->tpl_code);
  2973. $this->compiled = true;
  2974. }
  2975. return eval($this->tpl_code);
  2976. }
  2977. }
  2978. ?>