author | Dan |
Tue, 30 Mar 2010 11:37:00 -0400 | |
changeset 1231 | 4797a4a88533 |
parent 1227 | bdac73ed481e |
child 1274 | 673a1b6712fa |
permissions | -rw-r--r-- |
<?php /* * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between * Copyright (C) 2006-2009 Dan Fuhry * * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. */ class template { 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; var $theme_initted = false; var $page_initted = false; var $elements = false; var $page_id = false; var $namespace = false; var $js_preload = array(); var $js_append = ''; /** * Page action conditions * @var array */ var $conds = array(); /** * The PageProcessor for the current page * @var object */ var $page = false; /** * The list of themes that are critical for Enano operation. This doesn't include oxygen which * remains a user theme. By default this is admin and printable which have to be loaded on demand. * @var array */ var $system_themes = array('admin', 'printable'); /** * Set to true if the site is disabled and thus a message needs to be shown. This should ONLY be changed by common.php. * @var bool * @access private */ var $site_disabled = false; /** * One of the absolute best parts of Enano :-P * @var string */ var $fading_button = ''; function __construct() { global $db, $session, $paths, $template, $plugins; // Common objects global $cache; $this->tpl_bool = Array(); $this->tpl_strings = Array(); $this->sidebar_extra = ''; $this->toolbar_menu = ''; $this->additional_headers = ''; $this->plugin_blocks = Array(); $this->plugin_blocks_content = array(); $this->theme_loaded = false; $this->theme_list = Array(); $this->named_theme_list = Array(); $this->vars_assign_history = array( 'strings' => array(), 'bool' => array() ); if ( defined('IN_ENANO_UPGRADE') ) { return $this->construct_compat(); } if ( !$this->theme_list = $cache->fetch('themes') ) { $q = $db->sql_query('SELECT theme_id, theme_name, enabled, default_style, group_policy, group_list FROM ' . table_prefix . 'themes;'); if ( !$q ) $db->_die('template.php selecting theme list'); $i = 0; while ( $row = $db->fetchrow() ) { $this->theme_list[$i] = $row; $i++; } unset($theme); $this->theme_list = array_values($this->theme_list); $cache->store('themes', $this->theme_list, -1); } // Create associative array of themes foreach ( $this->theme_list as $i => &$theme ) $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i]; unset($theme); $this->default_theme = ( $_ = getConfig('theme_default') ) ? $_ : $this->theme_list[0]['theme_id']; $this->named_theme_list[ $this->default_theme ]['css'] = $this->get_theme_css_files($this->default_theme); // Come up with the default style. If the CSS file specified in default_style exists, we're good, just // use that. Otherwise, use the first stylesheet that comes to mind. $df_data =& $this->named_theme_list[ $this->default_theme ]; $this->default_style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0]; } /** * Gets the list of available CSS files (styles) for the specified theme. * @param string Theme ID * @return array */ function get_theme_css_files($theme_id) { $css = array(); $dir = ENANO_ROOT . "/themes/{$theme_id}/css"; if ( $dh = @opendir($dir) ) { while ( ( $file = @readdir($dh) ) !== false ) { if ( preg_match('/\.css$/', $file) ) $css[] = preg_replace('/\.css$/', '', $file); } closedir($dh); } // No CSS files? If so, nuke it. if ( count($css) < 1 ) { unset($this->theme_list[$theme_id]); } return $css; } /** * Failsafe constructor for upgrades. */ function construct_compat() { global $db, $session, $paths, $template, $plugins; // Common objects $this->tpl_bool = Array(); $this->tpl_strings = Array(); $this->sidebar_extra = ''; $this->toolbar_menu = ''; $this->additional_headers = ''; $this->plugin_blocks = Array(); $this->theme_loaded = false; $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;"> <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> </div>'; $this->theme_list = Array(); $this->named_theme_list = Array(); $q = $db->sql_query('SELECT theme_id, theme_name, enabled, default_style FROM ' . table_prefix . 'themes;'); if ( !$q ) $db->_die('template.php selecting theme list'); $i = 0; while ( $row = $db->fetchrow() ) { $this->theme_list[$i] = $row; $i++; } // List out all CSS files for this theme foreach ( $this->theme_list as $i => &$theme ) { $theme['css'] = array(); $dir = ENANO_ROOT . "/themes/{$theme['theme_id']}/css"; if ( $dh = @opendir($dir) ) { while ( ( $file = @readdir($dh) ) !== false ) { if ( preg_match('/\.css$/', $file) ) $theme['css'][] = preg_replace('/\.css$/', '', $file); } closedir($dh); } // No CSS files? If so, nuke it. if ( count($theme['css']) < 1 ) { unset($this->theme_list[$i]); } } $this->theme_list = array_values($this->theme_list); // Create associative array of themes foreach ( $this->theme_list as $i => &$theme ) $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i]; $this->default_theme = ( $_ = getConfig('theme_default') ) ? $_ : $this->theme_list[0]['theme_id']; // Come up with the default style. If the CSS file specified in default_style exists, we're good, just // use that. Otherwise, use the first stylesheet that comes to mind. $df_data =& $this->named_theme_list[ $this->default_theme ]; $this->default_style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0]; } /** * Systematically deletes themes from available list if they're blocked by theme security settings. Called when session->start() finishes. */ function process_theme_acls() { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; // generate the fading button - needs to be done after sessions are started $admintitle = ( $session->user_level >= USER_LEVEL_ADMIN && is_object(@$lang) ) ? ' title="' . $lang->get('sidebar_btn_enanopowered_admin_tip') . '"' : ''; $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;"> <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> </div>'; // For each theme, check ACLs and delete from RAM if not authorized foreach ( $this->theme_list as $i => $theme ) { if ( !@$theme['group_list'] ) continue; if ( $theme['theme_id'] === getConfig('theme_default') ) continue; switch ( $theme['group_policy'] ) { case 'allow_all': // Unconditionally allowed continue; break; case 'whitelist': // If we're not on the list, off to the left please $list = enano_json_decode($theme['group_list']); $allowed = false; foreach ( $list as $acl ) { if ( !preg_match('/^(u|g):([0-9]+)$/', $acl, $match) ) // Invalid list entry, silently allow (maybe not a good idea but // really, these things are checked before they're inserted) continue 2; $mode = $match[1]; $id = intval($match[2]); switch ( $mode ) { case 'u': $allowed = ( $id == $session->user_id ); if ( $allowed ) break 2; break; case 'g': $allowed = ( isset($session->groups[$id]) ); if ( $allowed ) break 2; } } if ( !$allowed ) { unset($this->theme_list[$i]); } break; case 'blacklist': // If we're ON the list, off to the left please $list = enano_json_decode($theme['group_list']); $allowed = true; foreach ( $list as $acl ) { if ( !preg_match('/^(u|g):([0-9]+)$/', $acl, $match) ) // Invalid list entry, silently allow (maybe not a good idea but // really, these things are checked before they're inserted) continue 2; $mode = $match[1]; $id = intval($match[2]); switch ( $mode ) { case 'u': $allowed = ( $id != $session->user_id ); if ( !$allowed ) break 2; break; case 'g': $allowed = ( !isset($session->groups[$id]) ); if ( !$allowed ) break 2; } } if ( !$allowed ) { unset($this->theme_list[$i]); } break; } } $this->theme_list = array_values($this->theme_list); // Rebuild associative theme list $this->named_theme_list = array(); foreach ( $this->theme_list as $i => &$theme ) $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i]; } /** * Register a new sidebar block. * @param string Block title * @param string Block HTML * @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. */ function sidebar_widget($t, $h, $use_normal_section = false) { global $db, $session, $paths, $template, $plugins; // Common objects if ( !$this->theme_loaded ) { $this->load_theme($session->theme, $session->style); } if(!$this->sidebar_widgets) $this->sidebar_widgets = ''; $tplvars = $this->extract_vars('elements.tpl'); if ( $use_normal_section ) { $parser = $this->makeParserText($tplvars['sidebar_section']); } else { $parser = $this->makeParserText($tplvars['sidebar_section_raw']); } $parser->assign_vars(Array('TITLE' => '{TITLE}','CONTENT' => $h)); $this->plugin_blocks[$t] = $parser->run(); $this->plugin_blocks_content[$t] = $h; $this->sidebar_widgets .= $parser->run(); } function add_header($html) { /* debug only ** $bt = debug_backtrace(); $bt = $bt[1]; $this->additional_headers .= "\n <!-- {$bt['file']}:{$bt['line']} -->\n " . $html; */ $this->additional_headers .= "\n " . $html; } function get_css($s = false) { global $db, $session, $paths, $template, $plugins; // Common objects $this->init_vars(); $path = ( $s ) ? 'css/'.$s : 'css/'.$this->style.'.css'; if ( !file_exists(ENANO_ROOT . '/themes/' . $this->theme . '/' . $path) ) { echo "/* WARNING: Falling back to default file because file $path does not exist */\n"; $path = 'css/' . $this->style_list[0] . '.css'; } return $this->process_template($path); } function load_theme($name = false, $css = false) { global $db, $session, $paths, $template, $plugins; // Common objects $this->theme = ( $name ) ? $name : $session->theme; $this->style = ( $css ) ? $css : $session->style; if ( !$this->theme ) { $this->theme = $this->theme_list[0]['theme_id']; $this->style = preg_replace('/\.css$/', '', $this->theme_list[0]['default_style']); } // Make sure we're allowed to use this theme. if ( ( // If it was removed, it's probably blocked by an ACL, or it was uninstalled !isset($this->named_theme_list[$this->theme]) || // Check if the theme is disabled ( isset($this->named_theme_list[$this->theme]) && isset($this->named_theme_list[$this->theme]['enabled']) && $this->named_theme_list[$this->theme]['enabled'] == 0 ) ) // Above all, if it's a system theme, don't inhibit the loading process. && !in_array($this->theme, $this->system_themes) ) { // No, something is preventing it - fall back to site default $this->theme = $this->default_theme; // Come up with the default style. If the CSS file specified in default_style exists, we're good, just // use that. Otherwise, use the first stylesheet that comes to mind. $df_data =& $this->named_theme_list[ $this->theme ]; $this->style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0]; } // The list of styles for the currently selected theme $this->style_list =& $this->named_theme_list[ $this->theme ]['css']; $this->theme_loaded = true; } /** * Change the theme we're supposed to display. * @param string Theme name * @param string Style name; optional */ function set_theme($theme = false, $style = false) { $this->theme_initted = false; $this->load_theme($theme, $style); } /** * Change the page we're supposed to generate for * @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. * @param string Namespace; not required if including a PageProcessor. */ function set_page($page_id_or_pp, $namespace = false) { if ( is_object($page_id_or_pp) && get_class($page_id_or_pp) === 'PageProcessor' ) { $this->page_initted = false; $page =& $page_id_or_pp; $this->page = $page; $this->page_id = $page->page_id; $this->namespace = $page->namespace; } else if ( is_string($page_id_or_pp) ) { if ( !is_string($namespace) ) return false; if ( $page_id_or_pp === $this->page_id && $namespace === $this->namespace ) return true; $this->page_initted = false; $this->page = false; $this->page_id = sanitize_page_id($page_id_or_pp); $this->namespace = $namespace; } else { return false; } return true; } /** * Queue a Javascript file to be loaded with the page instead of on demand. * @param mixed Javascript file string or array thereof, extensions are optional * @example <code> $template->preload_js(array('jquery', 'jquery-ui')); $template->preload_js('admin-menu.js'); </code> * @return null */ function preload_js($filemixed) { if ( is_string($filemixed) ) $files = array($filemixed); else if ( is_array($filemixed) ) $files = $filemixed; else // :-/ return null; $this->js_preload = array_values(array_merge($this->js_preload, $files)); } /** * Queue some HTML to be inserted after the Javascript runtime. * @param string HTML glob */ function add_header_js($html) { $this->js_append .= "$html\n "; } /** * Global, only-called-once init. Goes to all themes. */ function init_global_vars() { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; global $email; $is_opera = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'Opera')) ? true : false; $is_msie = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ? true : false; $this->assign_bool(array( 'auth_admin' => $session->user_level >= USER_LEVEL_ADMIN ? true : false, 'user_logged_in' => $session->user_logged_in, 'opera' => $is_opera, 'msie' => $is_msie )); if ( $session->sid_super ) { $ash = '&auth=' . $session->sid_super; $asq = "?auth=" . $session->sid_super; $asa = "&auth=" . $session->sid_super; $as2 = htmlspecialchars(urlSeparator) . 'auth='.$session->sid_super; } else { $asq = ''; $asa = ''; $as2 = ''; $ash = ''; } // Append the Enano version to URLs to break the cache on upgrades $enano_version = enano_version(); // Set up javascript includes // these depend heavily on whether we have a CDN to work with or not if ( getConfig('cdn_path') ) { // we're on a CDN, point to static includes // CLI javascript compression script: includes/clientside/jscompress.php $js_head = '<script type="text/javascript" src="' . cdnPath . '/includes/clientside/static/enano-lib-basic.js?' . $enano_version . '"></script>'; if ( !empty($this->js_preload) ) { $loadlines = array(); // make unique foreach ( $this->js_preload as &$script ) { $script = preg_replace('/\.js$/', '', $script) . '.js'; } unset($script); $this->js_preload = array_unique($this->js_preload); foreach ( $this->js_preload as $script ) { $js_head .= "\n <script type=\"text/javascript\" src=\"" . cdnPath . "/includes/clientside/static/$script?$enano_version\"></script>"; // special case for l10n: also load strings if ( $script == 'l10n.js' ) { global $lang; $js_head .= "\n <script type=\"text/javascript\" src=\"" . makeUrlNS("Special", "LangExportJSON/$lang->lang_id", "$enano_version") . "\"></script>"; } $loadlines[] = "loaded_components['$script'] = true;"; } // tell the system that this stuff is already loaded $loadlines = implode("\n ", $loadlines); $js_head .= "\n <script type=\"text/javascript\"> var loaded_components = loaded_components || {}; $loadlines </script>"; } $js_foot = <<<JSEOF <script type="text/javascript"> // This initializes the Javascript runtime when the DOM is ready - not when the page is // done loading, because enano-lib-basic still has to load some 15 other script files // check for the init function - this is a KHTML fix // This doesn't seem to work properly in IE in 1.1.x - there are some problems with // tinyMCE and l10n. if ( typeof ( enano_init ) == 'function' && !IE ) { enano_init(); window.onload = function(e) { }; } </script> $this->js_append JSEOF; } else { $cdnpath = cdnPath; $js_head = ''; // point to jsres compressor $js_head .= <<<JSEOF <!-- Only load a basic set of functions for now. Let the rest of the API load when the page is finished. --> <script type="text/javascript" src="$cdnpath/includes/clientside/jsres.php?early&$enano_version"></script> JSEOF; $js_foot = ''; if ( !empty($this->js_preload) ) { foreach ( $this->js_preload as &$script ) { $script = preg_replace('/\.js$/', '', $script) . '.js'; } $this->js_preload = array_unique($this->js_preload); if ( in_array('l10n.js', $this->js_preload) ) { // special case for l10n: also load strings global $lang; $js_foot .= "\n <script type=\"text/javascript\" src=\"" . makeUrlNS("Special", "LangExportJSON/$lang->lang_id", $enano_version, true) . "\"></script>"; } $scripts = implode(',', $this->js_preload); $js_foot .= "\n <script type=\"text/javascript\" src=\"" . cdnPath . "/includes/clientside/jsres.php?f=$scripts&$enano_version\"></script>"; } $js_foot .= <<<JSEOF <!-- jsres.php is a wrapper script that compresses and caches single JS files to minimize requests --> <script type="text/javascript" src="$cdnpath/includes/clientside/jsres.php?$enano_version"></script> <script type="text/javascript">//<![CDATA[ // This initializes the Javascript runtime when the DOM is ready - not when the page is // done loading, because enano-lib-basic still has to load some 15 other script files // check for the init function - this is a KHTML fix // This doesn't seem to work properly in IE in 1.1.x - there are some problems with // tinyMCE and l10n. if ( typeof ( enano_init ) == 'function' && !IE ) { enano_init(); window.onload = function(e) { }; } //]]></script> $this->js_append JSEOF; } $this->assign_bool(array( 'fixed_menus' => false, 'export' => false, 'right_sidebar' => true, 'enable_uploads' => ( getConfig('enable_uploads') == '1' && $session->get_permissions('upload_files') ) ? true : false, 'stupid_mode' => false, )); // Add the e-mail address client code to the header $this->add_header($email->jscode()); // Assign our main variables $this->assign_vars(array( 'SITE_NAME' => htmlspecialchars(getConfig('site_name')), 'USERNAME' => $session->username, 'SITE_DESC' => htmlspecialchars(getConfig('site_desc')), 'SCRIPTPATH' => scriptPath, 'CONTENTPATH' => contentPath, 'CDNPATH' => cdnPath, 'ADMIN_SID_QUES' => $asq, 'ADMIN_SID_AMP' => $asa, 'ADMIN_SID_AMP_HTML' => $ash, 'ADMIN_SID_AUTO' => $as2, 'ADMIN_SID_RAW' => ( is_string($session->sid_super) ? $session->sid_super : '' ), 'CSRF_TOKEN' => $session->csrf_token, 'COPYRIGHT' => RenderMan::parse_internal_links(getConfig('copyright_notice')), 'REQUEST_URI' => ( defined('ENANO_CLI') ? '' : $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] ), 'SEARCH_ACTION' => makeUrlNS('Special', 'Search'), 'INPUT_TITLE' => ( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars( $paths->get_pathskey($this->page_id, $this->namespace) ) . '" />' : ''), 'INPUT_AUTH' => ( $session->sid_super ? '<input type="hidden" name="auth" value="' . $session->sid_super . '" />' : ''), 'MAIN_PAGE' => get_main_page(), 'UNREAD_PMS' => $session->unread_pms, 'JS_HEADER' => $js_head, 'JS_FOOTER' => $js_foot, 'URL_ABOUT_ENANO' => makeUrlNS('Special', 'About_Enano', '', true), 'ENANO_VERSION' => enano_version() ), true); $tpl_strings = array(); foreach ( $paths->nslist as $ns_id => $ns_prefix ) { $tpl_strings[ 'NS_' . strtoupper($ns_id) ] = $ns_prefix; } $this->assign_vars($tpl_strings, true); } /** * Init theme vars, like sidebar, global JS, that kind of stuff. */ function init_theme_vars() { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; // allows conditional testing of the theme ID (a bit crude, came from my NSIS days) $this->assign_bool(array( "theme_is_{$this->theme}" => true )); $this->elements = $this->extract_vars('elements.tpl'); // Generate the code for the Administration and Edit Sidebar buttons // Once again, the new template parsing system can be used here $parser = $this->makeParserText($this->elements['sidebar_button']); $parser->assign_vars(Array( 'HREF'=>makeUrlNS('Special', 'Administration'), 'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { void(ajaxStartAdminLogin()); return false; }"', 'TEXT'=>$lang->get('sidebar_btn_administration'), )); $admin_link = $parser->run(); $parser->assign_vars(Array( 'HREF'=>makeUrlNS('Special', 'EditSidebar'), 'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { void(ajaxLoginNavTo(\'Special\', \'EditSidebar\', ' . USER_LEVEL_ADMIN . ')); return false; }"', 'TEXT'=>$lang->get('sidebar_btn_editsidebar'), )); $sidebar_link = $parser->run(); $this->assign_vars(array( 'ADMIN_LINK' => $admin_link, 'SIDEBAR_LINK' => $sidebar_link, 'THEME_ID' => $this->theme, 'STYLE_ID' => $this->style )); // Add the site description sidebar block $this->sidebar_widget($lang->get('sidebar_title_about'), '<p>' . htmlspecialchars(getConfig('site_desc')) . '</p>'); $this->theme_initted = true; } /** * Init page vars, like the toolbar, local JS, etc. */ function init_page_vars() { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; if ( !$this->page ) { $this->page = new PageProcessor($this->page_id, $this->namespace); } $conds = $this->page->ns->get_conds(); $this->assign_bool(array( 'in_admin' => ( ( $this->page_id == 'Administration' && $this->namespace == 'Special' ) || $this->namespace == 'Admin' ), 'auth_rename' => ( $conds['rename'] ) )); // Get the "article" button text (depends on namespace) switch ( $this->namespace ) { case "Article": default: $ns = $lang->get('onpage_lbl_page_article'); break; case "Admin": $ns = $lang->get('onpage_lbl_page_admin'); break; case "System": $ns = $lang->get('onpage_lbl_page_system'); break; case "File": $ns = $lang->get('onpage_lbl_page_file'); break; case "Help": $ns = $lang->get('onpage_lbl_page_help'); break; case "User": $ns = $lang->get('onpage_lbl_page_user'); break; case "Special": $ns = $lang->get('onpage_lbl_page_special'); break; case "Template": $ns = $lang->get('onpage_lbl_page_template'); break; case "Project": $ns = $lang->get('onpage_lbl_page_project'); break; case "Category": $ns = $lang->get('onpage_lbl_page_category'); break; case "API": $ns = $lang->get('onpage_lbl_page_external'); break; } $this->namespace_string = $ns; unset($ns); // compatibility $local_namespace =& $this->namespace; $local_page_id =& $this->page_id; $code = $plugins->setHook('page_type_string_set'); foreach ( $code as $cmd ) { eval($cmd); } $ns =& $this->namespace_string; // // PAGE TOOLBAR (on-page controls/actions) // $local_page = $paths->get_pathskey($this->page_id, $this->namespace); $local_cdata = $this->page->ns->get_cdata(); // Initialize the toolbar $tb = ''; $this->toolbar_menu = ''; // Create "xx page" button $btn_selected = ( isset($this->elements['toolbar_button_selected'])) ? $this->elements['toolbar_button_selected'] : $this->elements['toolbar_button']; $parser = $this->makeParserText($btn_selected); if ( $conds['article'] ) { $parser->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxReset()); return false; }" title="' . $lang->get('onpage_tip_article') . '" accesskey="a"', 'PARENTFLAGS' => 'id="mdgToolbar_article"', 'HREF' => makeUrl($local_page, null, true), 'TEXT' => $this->namespace_string )); $tb .= $parser->run(); } $button = $this->makeParserText($this->elements['toolbar_button']); // Page toolbar // Comments button if ( $conds['comments'] ) { $cdata = $this->page->ns->get_cdata(); if ( isset($cdata['comments_approved']) ) { $approval_counts = array( COMMENT_APPROVED => $cdata['comments_approved'], COMMENT_UNAPPROVED => $cdata['comments_unapproved'], COMMENT_SPAM => $cdata['comments_spam'] ); $num_comments = $session->check_acl_scope('mod_comments', $this->namespace) && $this->page->perms->get_permissions('mod_comments') ? array_sum($approval_counts) : $approval_counts[COMMENT_APPROVED]; } else { $e = $db->sql_query('SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$this->page_id.'\' AND namespace=\''.$this->namespace.'\';'); if ( !$e ) { $db->_die(); } $num_comments = $db->numrows(); $approval_counts = array(COMMENT_UNAPPROVED => 0, COMMENT_APPROVED => 0, COMMENT_SPAM => 0); while ( $r = $db->fetchrow() ) { $approval_counts[$r['approved']]++; } } $db->free_result(); // $n = ( $session->check_acl_scope('mod_comments', $this->namespace) && $perms->get_permissions('mod_comments') ) ? (string)$num_comments : (string)$na; 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 ) { $subst = array( 'num_comments' => $num_comments, 'num_app' => $approval_counts[COMMENT_APPROVED], 'num_unapp' => $approval_counts[COMMENT_UNAPPROVED], 'num_spam' => $approval_counts[COMMENT_SPAM] ); $btn_text = $lang->get('onpage_btn_discussion_unapp', $subst); } else { $subst = array( 'num_comments' => $num_comments ); $btn_text = $lang->get('onpage_btn_discussion', $subst); } $button->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxComments()); return false; }" title="' . $lang->get('onpage_tip_comments') . '" accesskey="c"', 'PARENTFLAGS' => 'id="mdgToolbar_discussion"', 'HREF' => makeUrl($local_page, 'do=comments', true), 'TEXT' => $btn_text, )); $tb .= $button->run(); } // Edit button if( $conds['edit'] ) { $button->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxEditor()); return false; }" title="' . $lang->get('onpage_tip_edit') . '" accesskey="e"', 'PARENTFLAGS' => 'id="mdgToolbar_edit"', 'HREF' => makeUrl($local_page, 'do=edit', true), 'TEXT' => $lang->get('onpage_btn_edit') )); $tb .= $button->run(); // View source button } else if ( $conds['viewsource'] ) { $button->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxEditor()); return false; }" title="' . $lang->get('onpage_tip_viewsource') . '" accesskey="e"', 'PARENTFLAGS' => 'id="mdgToolbar_edit"', 'HREF' => makeUrl($local_page, 'do=viewsource', true), 'TEXT' => $lang->get('onpage_btn_viewsource') )); $tb .= $button->run(); } // History button if ( $conds['history'] ) { $button->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxHistory()); return false; }" title="' . $lang->get('onpage_tip_history') . '" accesskey="h"', 'PARENTFLAGS' => 'id="mdgToolbar_history"', 'HREF' => makeUrl($local_page, 'do=history', true), 'TEXT' => $lang->get('onpage_btn_history') )); $tb .= $button->run(); } $menubtn = $this->makeParserText($this->elements['toolbar_menu_button']); // Additional actions menu // Rename button if ( $conds['rename'] ) { $menubtn->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxRename()); return false; }" title="' . $lang->get('onpage_tip_rename') . '" accesskey="r"', 'HREF' => makeUrl($local_page, 'do=rename', true), 'TEXT' => $lang->get('onpage_btn_rename'), )); $this->toolbar_menu .= $menubtn->run(); } // Vote-to-delete button if ( $conds['delvote'] ) { $menubtn->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxDelVote()); return false; }" title="' . $lang->get('onpage_tip_delvote') . '" accesskey="d"', 'HREF' => makeUrl($local_page, 'do=delvote', true), 'TEXT' => $lang->get('onpage_btn_votedelete'), )); $this->toolbar_menu .= $menubtn->run(); } // Clear-votes button if ( $conds['resetvotes'] ) { $menubtn->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxResetDelVotes()); return false; }" title="' . $lang->get('onpage_tip_resetvotes') . '" accesskey="y"', 'HREF' => makeUrl($local_page, 'do=resetvotes', true), 'TEXT' => $lang->get('onpage_btn_votedelete_reset'), )); $this->toolbar_menu .= $menubtn->run(); } // Printable page button if ( $conds['printable'] ) { $menubtn->assign_vars(array( 'FLAGS' => 'title="' . $lang->get('onpage_tip_printable') . '"', 'HREF' => makeUrl($local_page, 'printable=yes', true), 'TEXT' => $lang->get('onpage_btn_printable'), )); $this->toolbar_menu .= $menubtn->run(); } // Protect button if ( $conds['protect'] ) { switch($this->page->ns->cdata['protected']) { case PROTECT_FULL: $protect_status = $lang->get('onpage_btn_protect_on'); break; case PROTECT_SEMI: $protect_status = $lang->get('onpage_btn_protect_semi'); break; case PROTECT_NONE: $protect_status = $lang->get('onpage_btn_protect_off'); break; } $label = $this->makeParserText($this->elements['toolbar_label']); $label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_protect') . ' ' . "<b><span id=\"tb_ajax_protect_status\">$protect_status</span></b>")); $t0 = $label->run(); $menubtn->assign_vars(array( 'FLAGS' => 'accesskey="p" onclick="ajaxProtect(' . $this->page->ns->cdata['protected'] . '); return false;" id="tb_ajax_protect_btn" title="' . $lang->get('onpage_tip_protect') . '"', 'HREF' => makeUrl($local_page, 'do=protect', true), 'TEXT' => $lang->get('onpage_btn_protect_change') )); $t1 = $menubtn->run(); $this->toolbar_menu .= ' <table border="0" cellspacing="0" cellpadding="0"> <tr> <td>'.$t0.'</td> <td>'.$t1.'</td> </tr> </table>'; } // Wiki mode button if ( $conds['setwikimode'] ) { // label at start $label = $this->makeParserText($this->elements['toolbar_label']); $label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_wikimode'))); $t0 = $label->run(); // on button $ctmp = ''; if ( $local_cdata['wiki_mode'] == 1 ) { $ctmp = ' style="text-decoration: underline;"'; } $menubtn->assign_vars(array( 'FLAGS' => $ctmp, 'HREF' => makeUrl($local_page, 'do=setwikimode&level=1', true), 'TEXT' => $lang->get('onpage_btn_wikimode_on') )); $t1 = $menubtn->run(); // off button $ctmp = ''; if ( $local_cdata['wiki_mode'] == 0 ) { $ctmp=' style="text-decoration: underline;"'; } $menubtn->assign_vars(array( 'FLAGS' => $ctmp, 'HREF' => makeUrl($local_page, 'do=setwikimode&level=0', true), 'TEXT' => $lang->get('onpage_btn_wikimode_off') )); $t2 = $menubtn->run(); // global button $ctmp = ''; if ( $local_cdata['wiki_mode'] == 2 ) { $ctmp=' style="text-decoration: underline;"'; } $menubtn->assign_vars(array( 'FLAGS' => $ctmp, 'HREF' => makeUrl($local_page, 'do=setwikimode&level=2', true), 'TEXT' => $lang->get('onpage_btn_wikimode_global') )); $t3 = $menubtn->run(); // Tack it onto the list of buttons that are already there... $this->toolbar_menu .= ' <table border="0" cellspacing="0" cellpadding="0"> <tr> <td>'.$t0.'</td> <td>'.$t1.'</td> <td>'.$t2.'</td> <td>'.$t3.'</td> </tr> </table>'; } // Clear logs button if ( $conds['clearlogs'] ) { $menubtn->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxClearLogs()); return false; }" title="' . $lang->get('onpage_tip_flushlogs') . '" accesskey="l"', 'HREF' => makeUrl($local_page, 'do=flushlogs', true), 'TEXT' => $lang->get('onpage_btn_clearlogs'), )); $this->toolbar_menu .= $menubtn->run(); } // Delete page button if ( $conds['delete'] ) { $s = $lang->get('onpage_btn_deletepage'); if ( $this->page->ns->cdata['delvotes'] == 1 ) { $subst = array( 'num_votes' => $this->page->ns->cdata['delvotes'], 'plural' => '' ); $s .= $lang->get('onpage_btn_deletepage_votes', $subst); } else if ( $this->page->ns->cdata['delvotes'] > 1 ) { $subst = array( 'num_votes' => $this->page->ns->cdata['delvotes'], 'plural' => $lang->get('meta_plural') ); $s .= $lang->get('onpage_btn_deletepage_votes', $subst); } $menubtn->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxDeletePage()); return false; }" title="' . $lang->get('onpage_tip_deletepage') . '" accesskey="k"', 'HREF' => makeUrl($local_page, 'do=deletepage', true), 'TEXT' => $s, )); $this->toolbar_menu .= $menubtn->run(); } // Password-protect button if ( $conds['password'] ) { // label at start $label = $this->makeParserText($this->elements['toolbar_label']); $label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_password'))); $t0 = $label->run(); $menubtn->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxSetPassword()); return false; }" title="' . $lang->get('onpage_tip_password') . '"', 'HREF' => '#', 'TEXT' => $lang->get('onpage_btn_password_set'), )); $t = $menubtn->run(); $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>'; } // Manage ACLs button if ( $conds['acledit'] ) { $menubtn->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { var s = ajaxOpenACLManager(); console.debug(s); return false; }" title="' . $lang->get('onpage_tip_aclmanager') . '" accesskey="m"', 'HREF' => makeUrl($local_page, 'do=aclmanager', true), 'TEXT' => $lang->get('onpage_btn_acl'), )); $this->toolbar_menu .= $menubtn->run(); } // Administer page button if ( $conds['adminpage'] ) { $menubtn->assign_vars(array( 'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxAdminPage()); return false; }" title="' . $lang->get('onpage_tip_adminoptions') . '" accesskey="g"', 'HREF' => makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'PageManager', true), 'TEXT' => $lang->get('onpage_btn_admin'), )); $this->toolbar_menu .= $menubtn->run(); } if ( strlen($this->toolbar_menu) > 0 ) { $button->assign_vars(array( 'FLAGS' => 'id="mdgToolbar_moreoptions" onclick="if ( !KILL_SWITCH ) { return false; }" title="' . $lang->get('onpage_tip_moreoptions') . '"', 'PARENTFLAGS' => '', 'HREF' => makeUrl($local_page, 'do=moreoptions', true), 'TEXT' => $lang->get('onpage_btn_moreoptions') )); $tb .= $button->run(); } // Generate the code for the Log in, Log out, Change theme, Administration, and Edit Sidebar buttons // Once again, the new template parsing system can be used here $parser = $this->makeParserText($this->elements['sidebar_button']); $parser->assign_vars(Array( 'HREF'=>makeUrlNS('Special', "Logout/{$session->csrf_token}/{$local_page}"), 'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { mb_logout(); return false; }"', 'TEXT'=>$lang->get('sidebar_btn_logout'), )); $logout_link = $parser->run(); $parser->assign_vars(Array( 'HREF'=>makeUrlNS('Special', 'Login/' . $local_page), 'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { ajaxStartLogin(); return false; }"' . ( $local_page_id == 'Login' && $local_namespace == 'Special' ? ' class="currentpage"' : '' ), 'TEXT'=>$lang->get('sidebar_btn_login'), )); $login_link = $parser->run(); $parser->assign_vars(Array( 'HREF'=>makeUrlNS('Special', 'ChangeStyle/'.$local_page), 'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { ajaxChangeStyle(); return false; }"' . ( $local_page_id == 'ChangeStyle' && $local_namespace == 'Special' ? ' class="currentpage"' : '' ), 'TEXT'=>$lang->get('sidebar_btn_changestyle'), )); $theme_link = $parser->run(); // Run hooks $code = $plugins->setHook('tpl_compile_toolbar'); foreach ( $code as $cmd ) { eval($cmd); } // // ASSIGN VARIABLES // $this->assign_vars(array( 'PAGE_NAME' => htmlspecialchars($this->page->ns->cdata['name']), 'PAGE_URLNAME' => $paths->nslist[$this->namespace] . sanitize_page_id($this->page_id), 'TOOLBAR' => $tb, 'TOOLBAR_EXTRAS' => $this->toolbar_menu, 'STYLE_LINK' => makeUrlNS('Special', 'CSS', null, true), //contentPath.$paths->nslist['Special'].'CSS' . $p, 'LOGIN_LINK' => $login_link, 'LOGOUT_LINK' => $logout_link, 'THEME_LINK' => $theme_link ), true); $this->page_initted = true; } /** * Generates and assigns the Javascript system variables */ function generate_js_header() { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; $SID = ($session->sid_super) ? $session->sid_super : ''; $local_page = $paths->get_pathskey($this->page_id, $this->namespace); $local_fullpage = $paths->get_pathskey($this->page_id, $this->namespace) . substr($paths->fullpage, strlen($paths->page)); $urlname_clean = str_replace('\'', '\\\'', str_replace('\\', '\\\\', dirtify_page_id($local_fullpage))); $urlname_clean = strtr( $urlname_clean, array( '<' => '<', '>' => '>' ) ); $urlname_jssafe = sanitize_page_id($local_fullpage); $physical_urlname_jssafe = sanitize_page_id($paths->fullpage); $protected = is_object($this->page) ? $this->page->ns->cdata['really_protected'] : false; // Generate the dynamic javascript vars // Sorry. I know. This block is a mess. $js_dynamic = ' <script type="text/javascript">// <![CDATA[ // This section defines some basic and very important variables that are used later in the static Javascript library. // 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. var title = \''. $urlname_jssafe .'\'; var physical_title = \'' . $physical_urlname_jssafe . '\'; var on_main_page = ' . ( $local_page == get_main_page() ? 'true' : 'false' ) . '; var main_page_members = \'' . addslashes(get_main_page(true)) . '\'; var page_exists = '. ( ( is_object($this->page) ? $this->page->ns->exists() : true ) ? 'true' : 'false' ) .'; var scriptPath = \'' . addslashes(scriptPath) . '\'; var contentPath = \'' . addslashes(contentPath) . '\'; var cdnPath = \'' . addslashes(cdnPath) . '\'; var ENANO_SID = \'' . $SID . '\'; var user_level = ' . $session->user_level . '; var auth_level = ' . $session->auth_level . '; var USER_LEVEL_GUEST = ' . USER_LEVEL_GUEST . '; var USER_LEVEL_MEMBER = ' . USER_LEVEL_MEMBER . '; var USER_LEVEL_CHPREF = ' . USER_LEVEL_CHPREF . '; var USER_LEVEL_MOD = ' . USER_LEVEL_MOD . '; var USER_LEVEL_ADMIN = ' . USER_LEVEL_ADMIN . '; var disable_redirect = ' . ( isset($_GET['redirect']) && $_GET['redirect'] == 'no' ? 'true' : 'false' ) . '; var pref_disable_js_fx = ' . ( @$session->user_extra['disable_js_fx'] == 1 ? 'true' : 'false' ) . '; var csrf_token = "' . $session->csrf_token . '"; var prot = ' . ( ($protected) ? 'true' : 'false' ) .'; var ENANO_SPECIAL_CREATEPAGE = \''. makeUrl($paths->nslist['Special'].'CreatePage') .'\'; var ENANO_CREATEPAGE_PARAMS = \'_do=&pagename='. $this->page_id .'&namespace=' . $this->namespace . '\'; var ENANO_SPECIAL_CHANGESTYLE = \''. makeUrlNS('Special', 'ChangeStyle') .'\'; var namespace_list = []; var msg_loading_component = \'' . addslashes($lang->get('ajax_msg_loading_component')) . '\'; var AES_BITS = '.AES_BITS.'; var AES_BLOCKSIZE = '.AES_BLOCKSIZE.'; var pagepass = \''. ( ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '' ) .'\'; var ENANO_LANG_ID = ' . $lang->lang_id . '; var ENANO_PAGE_TYPE = "' . addslashes($this->namespace_string) . '"; var enano_version = "' . enano_version() . '";'; foreach ( $paths->nslist as $k => $c ) { $js_dynamic .= "namespace_list['{$k}'] = '" . addslashes($c) . "';"; } $js_dynamic .= "\n //]]>\n </script>"; $this->assign_vars(array( 'JS_DYNAMIC_VARS' => $js_dynamic, 'REPORT_URI' => makeUrl($local_fullpage, 'do=sql_report', true) ), true); } /** * Fetches, parses, and assigns the sidebar. */ function assign_sidebar() { // // COMPILE THE SIDEBAR // // This is done after the big assign_vars() so that sidebar code has access to the newly assigned variables list($this->tpl_strings['SIDEBAR_LEFT'], $this->tpl_strings['SIDEBAR_RIGHT'], $min) = $this->fetch_sidebar(); $this->tpl_bool['sidebar_left'] = ( $this->tpl_strings['SIDEBAR_LEFT'] != $min) ? true : false; $this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != $min) ? true : false; $this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility } /** * Initializes all variables related to on-page content. This includes sidebars and what have you. * @param object Optional PageProcessor object to use for passing metadata and permissions on. If omitted, uses information from $paths and $session. * @param bool If true, re-inits even if already initted with this page_id and namespace */ function init_vars($page = false, $force_init = false) { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; $need_shared_init = ( !$this->theme_initted || !$this->page_initted ); if ( $need_shared_init ) { if ( !$this->theme || !$this->style ) { $this->load_theme(); } $code = $plugins->setHook('compile_template', true); foreach ( $code as $cmd ) { eval($cmd); } } if ( !$this->theme_loaded ) $this->load_theme(); require(ENANO_ROOT . "/themes/{$this->theme}/theme.cfg"); if ( !$this->page_id || !$this->namespace ) { $this->page_id = $paths->page_id; $this->namespace = $paths->namespace; } profiler_log('template: prepped for var set (loaded theme, ran compile_template hook)'); $this->init_global_vars(); profiler_log('template: global vars set'); if ( !$this->theme_initted ) $this->init_theme_vars(); profiler_log('template: theme vars set'); if ( !$this->page_initted && !empty($this->namespace) ) { profiler_log('template: page vars set'); $this->init_page_vars(); } else { profiler_message('template: skipped setting page vars'); } // Perform shared init (combine javascript, etc.) if ( $need_shared_init ) { $this->generate_js_header(); $this->assign_sidebar(); profiler_log('template: assigned sidebar and JS'); } // and finally one string value that needs to be symlinked... if ( !isset($this->tpl_strings['ADDITIONAL_HEADERS']) ) { $this->tpl_strings['ADDITIONAL_HEADERS'] =& $this->additional_headers; } // done! $code = $plugins->setHook('template_var_init_end'); foreach ( $code as $cmd ) { eval($cmd); } } function header($simple = false) { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; define('ENANO_HEADERS_SENT', true); echo $this->getHeader($simple); } function footer($simple = false) { echo $this->getFooter($simple); } function getHeader($simple = false) { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; if ( !$this->theme_loaded ) { $this->load_theme($session->theme, $session->style); } if ( !$this->page_initted || !$this->theme_initted ) $this->init_vars(); // I feel awful doing this. if ( preg_match('/^W3C_Validator/', @$_SERVER['HTTP_USER_AGENT']) ) { header('Content-type: application/xhtml+xml'); } $header = ''; if ( !$this->no_headers ) { $header = ( $simple ) ? $this->process_template('simple-header.tpl') : $this->process_template('header.tpl'); } if ( !$simple && $session->user_logged_in && $session->unread_pms > 0 ) { $header .= $this->notify_unread_pms(); } if ( !$simple && $session->sw_timed_out ) { $login_link = makeUrlNS('Special', 'Login/' . $paths->fullpage, 'level=' . $session->user_level, true); $header .= '<div class="usermessage">'; $header .= $lang->get('user_msg_elev_timed_out', array( 'login_link' => $login_link )); $header .= '</div>'; } if ( $this->site_disabled && $session->user_level >= USER_LEVEL_ADMIN && ( $paths->page != $paths->nslist['Special'] . 'Administration' ) ) { $admin_link = makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'GeneralConfig', true); $header .= '<div class="usermessage"><b>' . $lang->get('page_sitedisabled_admin_msg_title') . '</b><br /> ' . $lang->get('page_sitedisabled_admin_msg_body', array('admin_link' => $admin_link)) . ' </div>'; } return $header; } function getFooter($simple = false) { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; if ( !$this->no_headers ) { global $_starttime; if(isset($_GET['sqldbg']) && $session->get_permissions('mod_misc')) { echo '<h3>' . $lang->get('page_heading_sql_list') . '</h3><pre style="margin-left: 1em">'; echo htmlspecialchars($db->sql_backtrace()); echo '</pre>'; } $t = ( $simple ) ? $this->process_template('simple-footer.tpl') : $this->process_template('footer.tpl'); $f = microtime_float(); $f = $f - $_starttime; $f = round($f, 2); $t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f)); $t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f)); $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . $lang->get('page_msg_stats_sql', array('nq' => $db->num_queries)) . '</a>'; $dbg = $t_loc; $dbg_long = $t_loc_long; if ( $session->user_level >= USER_LEVEL_ADMIN || defined('ENANO_DEBUG') ) { $dbg .= " | $q_loc"; $dbg_long .= " | $q_loc"; } $t = str_replace('[[Stats]]', $dbg, $t); $t = str_replace('[[StatsLong]]', $dbg_long, $t); $t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t); $t = str_replace('[[GenTime]]', (string)$f, $t); $t = str_replace('[[NumQueriesLoc]]', $q_loc, $t); $t = str_replace('[[GenTimeLoc]]', $t_loc, $t); $t = str_replace('[[EnanoPoweredLink]]', $lang->get('page_enano_powered', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t); $t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t); if ( defined('ENANO_PROFILE') ) { $t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t); // ob_end_clean(); // return profiler_make_html(); } return $t; } else { return ''; } } /** * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file. * @param $vars array * @param $from_internal bool Internal switch, just omit (@todo document) */ function assign_vars($vars, $from_internal = false) { foreach ( $vars as $key => $value ) { $replace = true; if ( isset($this->vars_assign_history['strings'][$key]) ) { if ( $this->vars_assign_history['strings'][$key] == 'api' ) { $replace = false; } } if ( $replace ) { $this->tpl_strings[$key] = $value; $this->vars_assign_history['strings'][$key] = ( $from_internal ) ? 'internal' : 'api'; } } } /** * Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements. * @param $vars array * @param $from_internal bool Internal switch, just omit (@todo document) */ function assign_bool($vars, $from_internal = false) { foreach ( $vars as $key => $value ) { $replace = true; if ( isset($this->vars_assign_history['bool'][$key]) ) { if ( $this->vars_assign_history['bool'][$key] == 'api' ) { $replace = false; } } if ( $replace ) { $this->tpl_bool[$key] = $value; $this->vars_assign_history['bool'][$key] = ( $from_internal ) ? 'internal' : 'api'; } } } # # COMPILER # /** * Compiles and executes a template based on the current variables and booleans. Loads * the theme and initializes variables if needed. This mostly just calls child functions. * @param string File to process * @return string */ function process_template($file) { global $db, $session, $paths, $template, $plugins; // Common objects if ( !$this->theme_initted || !$this->page_initted ) { $this->init_vars(); } $cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $file) . '.php'; if ( file_exists($cache_file) ) { // this is about the time of the breaking change to cache file format if ( ($m = filemtime($cache_file)) > 1215038089 ) { $result = @include($cache_file); if ( isset($md5) ) { if ( $m >= filemtime(ENANO_ROOT . "/themes/{$this->theme}/$file") ) { $result = $this->compile_template_text_post($result); return $result; } } } } $compiled = $this->compile_template($file); $result = eval($compiled); return $result; } /** * Loads variables from the specified template file. Returns an associative array containing the variables. * @param string Template file to process (elements.tpl) * @return array */ function extract_vars($file) { global $db, $session, $paths, $template, $plugins; // Common objects // Sometimes this function gets called before the theme is loaded // This is a bad coding practice so this function will always be picky. if ( !$this->theme ) { 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>'); } // Full pathname of template file $tpl_file_fullpath = ( strstr($file, '/') ) ? $file : ENANO_ROOT . '/themes/' . $this->theme . '/' . $file; // Make sure the template even exists if ( !is_file($tpl_file_fullpath) ) { die_semicritical('Cannot find template file', '<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 the current theme.</p> <p>Additional debugging information:<br /> <b>Theme currently in use: </b>' . $this->theme . '<br /> <b>Requested file: </b>' . $file . ' </p>'); } // Retrieve file contents $text = file_get_contents($tpl_file_fullpath); if ( !$text ) { return false; } // Get variables, regular expressions FTW preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches); // Initialize return values $tplvars = Array(); // Loop through each match, setting $tplvars[ $first_subpattern ] to $second_subpattern for ( $i = 0; $i < sizeof($matches[1]); $i++ ) { $tplvars[ $matches[1][$i] ] = $matches[2][$i]; } // All done! return $tplvars; } /** * Compiles a block of template code. * @param string The text to process * @return string */ function compile_tpl_code($text) { return template_compiler_core($text); } /** * Compiles the contents of a given template file, possibly using a cached copy, and returns the compiled code. * @param string Filename of template (header.tpl) * @return string */ function compile_template($filename) { global $db, $session, $paths, $template, $plugins; // Common objects // Full path to template file $tpl_file_fullpath = ENANO_ROOT . '/themes/' . $this->theme . '/' . $filename; // Make sure the file exists if ( !is_file($tpl_file_fullpath) ) { die_semicritical('Cannot find template file', '<p>The template parser was asked to load the file "' . htmlspecialchars($filename) . '", but that file couldn\'t be found in the directory for the current theme.</p> <p>Additional debugging information:<br /> <b>Theme currently in use: </b>' . $this->theme . '<br /> <b>Requested file: </b>' . $file . ' </p>'); } // We won't use the cached copy here. $text = file_get_contents($tpl_file_fullpath); // This will be used later when writing the cached file $md5 = md5($text); // Preprocessing and checks complete - compile the code $text = $this->compile_tpl_code($text); // Generate cache filename $cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $filename) . '.php'; // Perhaps caching is enabled and the admin has changed the template? if ( is_writable( ENANO_ROOT . '/cache/' ) && getConfig('cache_thumbs') == '1' ) { $h = fopen($cache_file, 'w'); if ( !$h ) { // Couldn't open the file - silently ignore and return return $text; } // Final contents of cache file $file_contents = <<<EOF <?php /* * NOTE: This file was automatically generated by Enano and is based on compiled code. Do not edit this file. * If you edit this file, any changes you make will be lost the next time the associated source template file is edited. */ \$md5 = '$md5'; $text EOF; // This is really just a normal PHP file that sets a variable or two and exits. // $tpl_text actually will contain the compiled code fwrite($h, $file_contents); fclose($h); } return $this->compile_template_text_post($text); //('<pre>'.htmlspecialchars($text).'</pre>'); } /** * Compiles (parses) some template code with the current master set of variables and booleans. * @param string Text to process * @return string */ function compile_template_text($text) { // this might do something else in the future, possibly cache large templates return $this->compile_template_text_post($this->compile_tpl_code($text)); } /** * For convenience - compiles AND parses some template code. * @param string Text to process * @return string */ function parse($text) { $text = $this->compile_template_text($text); $text = $this->compile_template_text_post($text); return eval($text); } /** * Post-processor for template code. Basically what this does is it localizes {lang:foo} blocks. * @param string Mostly-processed TPL code * @param bool Post-eval switch. If true, does not escape code. * @return string */ function compile_template_text_post($text, $post_eval = false) { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; // Language strings preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $text, $matches); foreach ( $matches[1] as $i => $string_id ) { $string = $lang->get($string_id); if ( !$post_eval ) { $string = str_replace('\\', '\\\\', $string); $string = str_replace('\'', '\\\'', $string); } $text = str_replace_once($matches[0][$i], $string, $text); } // URLs preg_match_all('/\{url:([A-z0-9]+):([^\}]+?)(?:;([^\s\}]+?))?(?:\|(escape))?\}/i', $text, $matches); foreach ( $matches[1] as $i => $string_id ) { $namespace =& $matches[1][$i]; $page_id =& $matches[2][$i]; $params =& $matches[3][$i]; $escape =& $matches[4][$i]; if ( !$params ) $params = false; $escape = !empty($escape); $result = makeUrlNS($namespace, sanitize_page_id($page_id), $params, $escape); if ( !$post_eval ) { $result = str_replace('\\', '\\\\', $result); $result = str_replace('\'', '\\\'', $result); } $text = str_replace_once($matches[0][$i], $result, $text); } $code = $plugins->setHook('compie_template_text_post'); foreach ( $code as $cmd ) { eval($cmd); } return $text; } /** * Returns the output of a theme hook * @param string Hook name * @return string */ function get_theme_hook($hook) { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; ob_start(); $code = $plugins->setHook($hook); foreach ( $code as $cmd ) { eval($cmd); } $out = ob_get_contents(); ob_end_clean(); return $out; } // n00bish comments removed from here. 2008-03-13 @ 12:02AM when I had nothing else to do. /** * Takes a blob of HTML with the specially formatted template-oriented wikitext and formats it. Does not use eval(). * This function butchers every coding standard in Enano and should eventually be rewritten. The fact is that the * code _works_ and does a good job of checking for errors and cleanly complaining about them. * @param string Text to process * @param bool Ignored for backwards compatibility * @param string File to get variables for sidebar data from * @return string */ function tplWikiFormat($message, $filter_links = false, $filename = 'elements.tpl') { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; $START = microtime_float(); // localize the whole string first preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $message, $matches); foreach ( $matches[1] as $i => $string_id ) { $string = $lang->get($string_id); $string = str_replace('\\', '\\\\', $string); $string = str_replace('\'', '\\\'', $string); $message = str_replace_once($matches[0][$i], $string, $message); } // first: the hackish optimization - // if it's only a bunch of letters, numbers and spaces, just skip this sh*t. if ( preg_match('/^[\w\s\.]*$/i', $message) ) { return $message; } $filter_links = false; $tplvars = $this->extract_vars($filename); if($session->sid_super) $as = htmlspecialchars(urlSeparator).'auth='.$session->sid_super; else $as = ''; error_reporting(E_ALL); $random_id = sha1(microtime().''); // A temp value /* * PREPROCESSOR */ // Variables preg_match_all('#\$([A-Z_-]+)\$#', $message, $links); $links = $links[1]; for($i=0;$i<sizeof($links);$i++) { if ( isset($this->tpl_strings[$links[$i]]) ) { $message = str_replace('$'.$links[$i].'$', $this->tpl_strings[$links[$i]], $message); } } // Conditionals $message = $this->twf_parse_conditionals($message); /* * HTML RENDERER */ // Images $message = RenderMan::process_image_tags($message, $taglist); $message = RenderMan::process_imgtags_stage2($message, $taglist); // Internal links $message = RenderMan::parse_internal_links($message, $tplvars['sidebar_button'], false, $this->page_id, $this->namespace); // External links $url_regexp = <<<EOF ( (?:https?|ftp|irc):\/\/ # protocol (?:[^@\s\]"\':]+@)? # username (FTP only but whatever) (?:(?:(?:[a-z0-9-]+\.)*)[a-z0-9\[\]:]+) # hostname (?::[0-9]+)? # port number (?:\/[A-z0-9_%\|~`!\!@#\$\^&?=\*\(\):;\.,\/-]*)? # path ) EOF; $text_parser = $this->makeParserText($tplvars['sidebar_button']); preg_match_all('/\[' . $url_regexp . '[ ]([^\]]+)\]/isx', $message, $ext_link); for ( $i = 0; $i < count($ext_link[0]); $i++ ) { $text_parser->assign_vars(Array( 'HREF' => $ext_link[1][$i], 'FLAGS' => '', 'TEXT' => $ext_link[2][$i] )); $message = str_replace($ext_link[0][$i], $text_parser->run(), $message); } preg_match_all('/\[' . $url_regexp . '\]/is', $message, $ext_link); for ( $i = 0; $i < count($ext_link[0]); $i++ ) { $text_parser->assign_vars(Array( 'HREF' => $ext_link[1][$i], 'FLAGS' => '', 'TEXT' => htmlspecialchars($ext_link[1][$i]) )); $message = str_replace($ext_link[0][$i], $text_parser->run(), $message); } $TIME = microtime_float() - $START; /* if ( $TIME > 0.02 ) { echo 'template: tplWikiFormat took a while for this one. string dump:<pre>'; echo htmlspecialchars($message); echo '</pre>'; } */ return $message; } /** * Parses conditional {if} blocks in sidebars and other tplWikiFormatted things * @param string A string potentially containing conditional blocks * @return string Processed string */ function twf_parse_conditionals($message) { if ( !preg_match_all('/\{(!?)if ([a-z0-9_\(\)\|&! ]+)\}(.*?)(?:\{else\}(.*?))?\{\/if\}/is', $message, $matches) ) { return $message; } foreach ( $matches[0] as $match_id => $full_block ) { // 1 = "not" flag // 2 = condition // 3 = if true // 4 = else $condresult = $this->process_condition($matches[2][$match_id]); if ( !empty($matches[1][$match_id]) ) { if ( $condresult == 1 ) $condresult = 2; else if ( $condresult == 2 ) $condresult = 1; } switch($condresult) { case 1: // evaluated to false $message = str_replace_once($full_block, $matches[4][$match_id], $message); break; case 2: // evaluated to true $message = str_replace_once($full_block, $matches[3][$match_id], $message); break; case 3: $message = str_replace_once($full_block, "Syntax error: mismatched parentheses (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message); break; case 4: $message = str_replace_once($full_block, "Syntax error: illegal character (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message); break; case 5: $message = str_replace_once($full_block, "Syntax error: illegal sequence (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message); break; } } return $message; } /** * Inner-loop parser for a conditional block. Verifies a string condition to make sure it's syntactically correct, then returns what it evaluates to. * Return values: * 1 - string evaluates to true * 2 - string evaluates to false * 3 - Syntax error - mismatched parentheses * 4 - Syntax error - unknown token * 5 - Syntax error - invalid sequence * @param string * @return int * */ function process_condition($condition) { // make sure parentheses are matched $parentheses = preg_replace('/[^\(\)]/', '', $condition); if ( !empty($parentheses) ) { $i = 0; $parentheses = enano_str_split($parentheses); foreach ( $parentheses as $chr ) { $inc = ( $chr == '(' ) ? 1 : -1; $i += $inc; } if ( $i != 0 ) { // mismatched parentheses return 3; } } // sequencer check // first, pad all sequences of characters with spaces $seqcheck = preg_replace('/([a-z0-9_]+)/i', '\\1 ', $condition); $seqcheck = preg_replace('/([&|()!])/i', '\\1 ', $seqcheck); // now shrink all spaces to one space each $seqcheck = preg_replace('/[ ]+/', ' ', $seqcheck); // explode it. the allowed sequences are: // - TOKEN_NOT + TOKEN_VARIABLE // - TOKEN_NOT + TOKEN_PARENTHLEFT // - TOKEN_BOOLOP + TOKEN_NOT // - TOKEN_PARENTHRIGHT + TOKEN_NOT // - TOKEN_VARIABLE + TOKEN_BOOLOP // - TOKEN_BOOLOP + TOKEN_PARENTHLEFT // - TOKEN_PARENTHLEFT + TOKEN_VARIABLE // - TOKEN_BOOLOP + TOKEN_VARIABLE // - TOKEN_VARIABLE + TOKEN_PARENTHRIGHT // - TOKEN_PARENTHRIGHT + TOKEN_BOOLOP $seqcheck = explode(' ', trim($seqcheck)); $last_item = TOKEN_BOOLOP; foreach ( $seqcheck as $i => $token ) { // determine type if ( $token == '(' ) { $type = TOKEN_PARENTHLEFT; } else if ( $token == ')' ) { $type = TOKEN_PARENTHRIGHT; } else if ( $token == '!' ) { $type = TOKEN_NOT; } else if ( strtolower($token) == 'and' || strtolower($token) == 'or' || $token == '&&' || $token == '||' ) { $type = TOKEN_BOOLOP; } else if ( preg_match('/^[a-z0-9_]+$/i', $token) ) { $type = TOKEN_VARIABLE; // at this point it's considered safe to wrap it $seqcheck[$i] = "( isset(\$this->tpl_bool['$token']) && \$this->tpl_bool['$token'] )"; } else { // syntax error - doesn't match known token types return 4; } // inner sequence check if ( ( $last_item == TOKEN_BOOLOP && $type == TOKEN_NOT ) || ( $last_item == TOKEN_PARENTHRIGHT && $type == TOKEN_NOT ) || ( $last_item == TOKEN_NOT && $type == TOKEN_VARIABLE ) || ( $last_item == TOKEN_NOT && $type == TOKEN_PARENTHLEFT ) || ( $last_item == TOKEN_VARIABLE && $type == TOKEN_BOOLOP ) || ( $last_item == TOKEN_BOOLOP && $type == TOKEN_PARENTHLEFT ) || ( $last_item == TOKEN_PARENTHLEFT && $type == TOKEN_VARIABLE ) || ( $last_item == TOKEN_BOOLOP && $type == TOKEN_VARIABLE ) || ( $last_item == TOKEN_VARIABLE && $type == TOKEN_PARENTHRIGHT ) || ( $last_item == TOKEN_PARENTHRIGHT && $type == TOKEN_BOOLOP ) ) { // sequence is good, continue } else { // sequence is invalid, break out return 5; } $last_item = $type; } // passed all checks $seqcheck = implode(' ', $seqcheck); $result = eval("return ( $seqcheck ) ? true : false;"); return ( $result ) ? 2 : 1; } /** * Print a text field that auto-completes a username entered into it. * @param string $name - the name of the form field * @return string */ function username_field($name, $value = false) { $randomid = md5( time() . microtime() . mt_rand() ); $text = '<input name="'.$name.'" class="autofill username" onkeyup="new AutofillUsername(this);" type="text" size="30" id="userfield_'.$randomid.'" autocomplete="off"'; if($value) $text .= ' value="'.$value.'"'; $text .= ' />'; return $text; } /** * Print a text field that auto-completes a page name entered into it. * @param string $name - the name of the form field * @return string */ function pagename_field($name, $value = false) { $randomid = md5( time() . microtime() . mt_rand() ); $text = '<input name="'.$name.'" class="autofill page" onkeyup="new AutofillPage(this);" type="text" size="30" id="pagefield_'.$randomid.'" autocomplete="off"'; if($value) $text .= ' value="'.$value.'"'; $text .= ' />'; return $text; } /** * Sends a textarea that can be converted to and from a TinyMCE widget on the fly. * @param string The name of the form element * @param string The initial content. Optional, defaults to blank * @param int Rows in textarea * @param int Columns in textarea * @return string HTML and Javascript code. */ function tinymce_textarea($name, $content = '', $rows = 20, $cols = 60) { global $lang; $randomid = md5(microtime() . mt_rand()); $html = ''; $html .= '<textarea name="' . $name . '" rows="'.$rows.'" cols="'.$cols.'" style="width: 100%;" id="toggleMCEroot_'.$randomid.'">' . $content . '</textarea>'; $html .= '<div style="float: right; display: table;" id="mceSwitchAgent_' . $randomid . '">' . $lang->get('etc_tinymce_btn_text') . ' | <a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">' . $lang->get('etc_tinymce_btn_graphical') . '</a></div>'; $html .= '<script type="text/javascript"> // <![CDATA[ function toggleMCE_'.$randomid.'() { var the_obj = document.getElementById(\'toggleMCEroot_' . $randomid . '\'); var panel = document.getElementById(\'mceSwitchAgent_' . $randomid . '\'); var text_editor = $lang.get("etc_tinymce_btn_text"); var graphical_editor = $lang.get("etc_tinymce_btn_graphical"); if ( the_obj.dnIsMCE == "yes" ) { $dynano(the_obj).destroyMCE(); panel.innerHTML = text_editor + \' | <a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">\' + graphical_editor + \'</a>\'; } else { $dynano(the_obj).switchToMCE(); panel.innerHTML = \'<a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">\' + text_editor + \'</a> | \' + graphical_editor; } } // ]]> </script>'; return $html; } /** * Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;) * Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class. * @param $filename the filename of the template to be parsed * @return object */ function makeParser($filename) { global $db, $session, $paths, $template, $plugins; // Common objects $filename = ENANO_ROOT.'/themes/'.$template->theme.'/'.$filename; if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist'); $code = file_get_contents($filename); $parser = new templateIndividual($code); return $parser; } /** * Same as $template->makeParser(), but takes a string instead of a filename. * @param $text the text to parse * @return object */ function makeParserText($code) { $parser = new templateIndividual($code); return $parser; } /** * Fetch the HTML for a plugin-added sidebar block * @param $name the plugin name * @return string */ function fetch_block($id, $just_the_innards_maam = false) { if ( $just_the_innards_maam ) { $source =& $this->plugin_blocks_content; } else { $source =& $this->plugin_blocks; } return isset($source[$id]) ? $source[$id] : false; } /** * Fetches the contents of both sidebars. * @return array - key 0 is left, key 1 is right, key 2 is the HTML that makes up an empty sidebar * @example list($left, $right) = $template->fetch_sidebar(); */ function fetch_sidebar() { global $db, $session, $paths, $template, $plugins; // Common objects // first, check the cache if ( $result = $this->fetch_cached_sidebar() ) { return $result; } require(ENANO_ROOT . "/themes/{$this->theme}/theme.cfg"); profiler_log('Started sidebar parsing'); // init our block contents $left = ''; $right = ''; $min = ''; // also might want the links block if ( !$this->fetch_block('Links') ) $this->initLinksWidget(); // templates to work with $vars = $this->extract_vars('elements.tpl'); // sidebar_top and sidebar_bottom are HTML that is prepended/appended to sidebar content. Themes can // choose whether or not to display a sidebar depending on whether the sidebar is empty ( $left == $min ) // or not using the sidebar_left and sidebar_right template booleans (the oxygen theme does this). if ( isset($vars['sidebar_top']) ) { $top = $this->parse($vars['sidebar_top']); $left .= $top; $right .= $top; $min .= $top; } // grab the blocks from the DB $q = $db->sql_query('SELECT item_id,sidebar_id,block_name,block_type,block_content FROM '.table_prefix.'sidebar' . "\n" . ' WHERE item_enabled=1 ORDER BY sidebar_id ASC, item_order ASC;'); if ( !$q ) $db->_die('The sidebar text data could not be selected.'); // explicitly specify $q in case a plugin or PHP block makes a query while ( $row = $db->fetchrow($q) ) { // should we skip this block? if ( ( $row['item_id'] === 2 && !empty($theme['sb_hide_tools']) ) || ( $row['item_id'] === 3 && !empty($theme['sb_hide_user']) ) || ( $row['item_id'] === 4 && !empty($theme['sb_hide_search']) ) ) continue; // format the block $block_content = $this->format_sidebar_block($row, $vars, $parser); // is there a {restrict} or {hideif} block? if ( preg_match('/\{(restrict|hideif) ([a-z0-9_\(\)\|&! ]+)\}/', $block_content, $match) ) { // we have one, check the condition $type =& $match[1]; $cond =& $match[2]; $result = $this->process_condition($cond); if ( ( $type == 'restrict' && $result == 1 ) || ( $type == 'hideif' && $result == 2 ) ) { // throw out this block, it's hidden for whatever reason by the sidebar script continue; } // didn't get a match, so hide the conditional logic // FIXME: this needs to be verbose about syntax errors $block_content = str_replace_once($match[0], '', $block_content); } // if we made it here, this block definitely needs to be displayed. send it to the // parser (set by format_sidebar_block) and decide where to append it (but not in that order ;)) $appender = false; if ( $row['sidebar_id'] == SIDEBAR_LEFT ) { $appender =& $left; } else if ( $row['sidebar_id'] == SIDEBAR_RIGHT ) { $appender =& $right; } else { // uhoh, block_id isn't valid. maybe a plugin wants to put this block somewhere else? $code = $plugins->setHook('sidebar_block_id'); foreach ( $code as $cmd ) { eval($cmd); } // if $appender wasn't set by a plugin, don't parse this block to save some CPU cycles if ( !$appender ) { continue; } } // assign variables to parser $block_name = $this->tplWikiFormat($row['block_name']); $parser->assign_vars(array( // be nice and format the title (FIXME, does this use a lot of CPU time? still needs l10n in some cases though) 'TITLE' => $block_name, 'CONTENT' => $block_content )); $parsed = $parser->run(); // plugins are parsed earlier due to the way disabled/missing plugins that add sidebar blocks are // handled, so the {TITLE} var won't be replaced until now. this allows completely eliminating a // block if it's not available if ( $row['block_type'] == BLOCK_PLUGIN ) { $parsed = str_replace('{TITLE}', $block_name, $parsed); } // done parsing - append and continue $appender .= $parsed; // we're done with this - unset it because it's a reference and we don't want it overwritten. // also free the parser to get some RAM back unset($appender, $parser); } // lastly, append any footer HTML if(isset($vars['sidebar_bottom'])) { $bottom = $this->parse($vars['sidebar_bottom']); $left .= $bottom; $right .= $bottom; $min .= $bottom; } $return = array($left, $right, $min); // allow any plugins to append what they want to the return $code = $plugins->setHook('sidebar_fetch_return'); foreach ( $code as $cmd ) { eval($cmd); } // cache the result if appropriate $this->cache_compiled_sidebar($return); profiler_log('Finished sidebar parsing'); return $return; } /** * Runs the appropriate formatting routine on a sidebar row. * @param array Row in sidebar table * @param array Template variable set from elements.tpl * @param null Pass by reference, will be filled with the parser according to the block's type (sidebar_section or sidebar_section_raw) * @return string HTML + directives like {restrict} or {hideif} */ function format_sidebar_block($row, $vars, &$parser) { // import globals in case a PHP block wants to call the Enano API global $db, $session, $paths, $template, $plugins; // Common objects $parser = null; switch($row['block_type']) { case BLOCK_WIKIFORMAT: $parser = $this->makeParserText($vars['sidebar_section_raw']); $c = RenderMan::render($row['block_content']); break; case BLOCK_TEMPLATEFORMAT: $parser = $this->makeParserText($vars['sidebar_section']); $c = $this->tplWikiFormat($row['block_content']); break; case BLOCK_HTML: $parser = $this->makeParserText($vars['sidebar_section_raw']); $c = $row['block_content']; break; case BLOCK_PHP: // PHP blocks need to be sent directly to eval() $parser = $this->makeParserText($vars['sidebar_section_raw']); ob_start(); @eval($row['block_content']); $c = ob_get_contents(); ob_end_clean(); break; case BLOCK_PLUGIN: $parser = $this->makeParserText('{CONTENT}'); $c = '<!-- PLUGIN -->' . (gettype($this->fetch_block($row['block_content'])) == 'string') ? $this->fetch_block($row['block_content']) : // This used to say "can't find plugin block" but I think it's more friendly to just silently hide it. ''; break; default: // unknown block type - can a plugin handle it? $code = $plugins->setHook('format_sidebar_block'); foreach ( $code as $cmd ) { eval($cmd); } if ( !isset($c) ) { // default to wiki formatting (this was going to be straight HTML but this is done for backwards compatibility reasons) $c = RenderMan::render($row['block_content']); } if ( !$parser ) { // no parser defined, use the "raw" section by default (plugins are more likely to want raw content // rather than a list of links, and they can set the parser to use sidebar_section if they want) $parser = $this->makeParserText($vars['sidebar_section_raw']); } break; } return $c; } /** * 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 * wrote this routine. You should get a copy of Master of Puppets, it's a damn good album.) * @return array */ function get_cache_replacements() { global $db, $session, $paths, $template, $plugins; // Common objects return array( '$LOGIN_LINK$' => $this->tpl_strings['LOGIN_LINK'], '$MAIN_PAGE$' => $this->tpl_strings['MAIN_PAGE'], '$USERNAME$' => $session->username ); } /** * Attempts to load a cached compiled sidebar. * @return array Same format as fetch_sidebar() */ function fetch_cached_sidebar() { global $db, $session, $paths, $template, $plugins; // Common objects global $cache; $cached = false; if ( ($result = $cache->fetch('anon_sidebar')) && !$session->user_logged_in ) { if ( isset($result[$this->theme]) ) { $cached = $result[$this->theme]; } } // if we haven't been able to fetch yet, see if a plugin wants to give us something if ( !$cached ) { $code = $plugins->setHook('fetch_cached_sidebar'); foreach ( $code as $cmd ) { eval($cmd); } } if ( is_array($cached) ) { // fetch certain variables that can't be stored in the cache and quickly substitute $replace = $this->get_cache_replacements(); foreach ( $cached as &$val ) { $val = strtr($val, $replace); } // done processing, send it back return $cached; } return false; } /** * Caches a completely compiled sidebar, if appropriate * @param array Effectively, return from fetch_sidebar() */ function cache_compiled_sidebar($sidebar) { global $db, $session, $paths, $template, $plugins; // Common objects global $cache; // check if conditions are right if ( !$session->user_logged_in && getConfig('cache_thumbs') === '1' ) { // load any existing cache to make sure other themes' cached sidebars aren't discarded $cached = ( $_ = $cache->fetch('anon_sidebar') ) ? $_ : array(); // replace variables $replace = array_flip($this->get_cache_replacements()); foreach ( $sidebar as &$section ) { $section = strtr($section, $replace); } // compile $cached[$this->theme] = $sidebar; // store $cache->store('anon_sidebar', $cached, 15); } } function initLinksWidget() { global $db, $session, $paths, $template, $plugins; // Common objects // SourceForge/W3C buttons $ob = Array(); if(getConfig('sflogo_enabled')=='1') { $sflogo_secure = ( isset($_SERVER['HTTPS']) ) ? 'https' : 'http'; $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').'&type='.getConfig('sflogo_type').'" /></a>'; } 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>'; 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>'; 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>'; 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>'; 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>'; 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>'; 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>'; $code = $plugins->setHook('links_widget'); foreach ( $code as $cmd ) { eval($cmd); } 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>'; else $sb_links = ''; $this->sidebar_widget('Links', $sb_links); } /** * Builds a box showing unread private messages. */ function notify_unread_pms() { global $db, $session, $paths, $template, $plugins; // Common objects if ( ( $paths->page_id == 'PrivateMessages' || $paths->page_id == 'Preferences' ) && $paths->namespace == 'Special' ) { return ''; } $ob = '<div class="usermessage">'."\n"; $s = ( $session->unread_pms == 1 ) ? '' : 's'; $ob .= " <b>You have $session->unread_pms <a href=" . '"' . makeUrlNS('Special', 'PrivateMessages' ) . '"' . ">unread private message$s</a>.</b><br />\n Messages: "; $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;'); if ( !$q ) $db->_die(); $messages = array(); while ( $row = $db->fetchrow() ) { $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>'; } $ob .= implode(",\n " , $messages)."\n"; $ob .= '</div>'."\n"; return $ob; } /** * Parse a system message. * @param string message * @return string */ function parse_system_message($text) { ob_start(); eval( '?>' . $text ); $result = ob_get_contents(); ob_end_clean(); return $this->parse($result); } /** * Return the wiki mode edit notice, rendered and addslashes()'ed. * @return string */ function get_wiki_edit_notice() { global $cache; if ( getConfig('wiki_edit_notice', 0) != 1 ) return ''; $notice = RenderMan::render(getConfig('wiki_edit_notice_text')); return $notice; } } // class template /** * The core of the template compilation engine. Independent from the Enano API for failsafe operation. * @param string text to process * @return string Compiled PHP code * @access private */ function template_compiler_core($text) { global $db, $session, $paths, $template, $plugins; // Common objects // A random seed used to salt tags $seed = md5 ( microtime() . mt_rand() ); // Strip out PHP sections preg_match_all('/<\?php(.+?)\?>/is', $text, $php_matches); foreach ( $php_matches[0] as $i => $match ) { // Substitute the PHP section with a random tag $tag = "{PHP:$i:$seed}"; $text = str_replace_once($match, $tag, $text); } // Escape slashes and single quotes in template code $text = str_replace('\\', '\\\\', $text); $text = str_replace('\'', '\\\'', $text); // Initialize the PHP compiled code $text = 'ob_start(); global $paths, $template; echo \''.$text.'\'; $tpl_code = ob_get_contents(); ob_end_clean(); return $tpl_code;'; ## ## Main rules ## // // Conditionals // $keywords = array('BEGIN', 'BEGINNOT', 'IFSET', 'IFPLUGIN'); // only do this if the plugins API is loaded if ( is_object(@$plugins) ) { $code = $plugins->setHook('template_compile_logic_keyword'); foreach ( $code as $cmd ) { eval($cmd); } } $keywords = implode('|', $keywords); // Matches // 1 2 3 4 56 7 8 9 $regexp = '/(<!-- ?(' . $keywords . ') ([A-z0-9_-]+) ?-->)([\w\W]*)((<!-- ?BEGINELSE \\3 ?-->)([\w\W]*))?(<!-- ?END(IF)? \\3 ?-->)/isU'; /* The way this works is: match all blocks using the standard form with a different keyword in the block each time, and replace them with appropriate PHP logic. Plugin-extensible now. :-) */ // This is a workaround for what seems like a PCRE bug while ( preg_match_all($regexp, $text, $matches) ) { for ( $i = 0; $i < count($matches[0]); $i++ ) { $start_tag =& $matches[1][$i]; $type =& $matches[2][$i]; $test =& $matches[3][$i]; $particle_true =& $matches[4][$i]; $else_tag =& $matches[6][$i]; $particle_else =& $matches[7][$i]; $end_tag =& $matches[8][$i]; switch($type) { case 'BEGIN': $cond = "isset(\$this->tpl_bool['$test']) && \$this->tpl_bool['$test']"; break; case 'BEGINNOT': $cond = "!isset(\$this->tpl_bool['$test']) || ( isset(\$this->tpl_bool['$test']) && !\$this->tpl_bool['$test'] )"; break; case 'IFPLUGIN': $cond = "getConfig('plugin_$test') == '1'"; break; case 'IFSET': $cond = "isset(\$this->tpl_strings['$test'])"; break; default: // only do this if the plugins API is loaded if ( is_object(@$plugins) ) { $code = $plugins->setHook('template_compile_logic_cond'); foreach ( $code as $cmd ) { eval($cmd); } } break; } if ( !isset($cond) || ( isset($cond) && !is_string($cond) ) ) continue; $tag_complete = <<<TPLCODE '; /* START OF CONDITION: $type ($test) */ if ( $cond ) { echo '$particle_true'; /* ELSE OF CONDITION: $type ($test) */ } else { echo '$particle_else'; /* END OF CONDITION: $type ($test) */ } echo ' TPLCODE; $text = str_replace_once($matches[0][$i], $tag_complete, $text); } } // For debugging ;-) // die("<pre><?php\n" . htmlspecialchars($text."\n\n".print_r($matches,true)) . "\n\n?></pre>"); // // Data substitution/variables // // System messages $text = preg_replace('/<!-- SYSMSG ([A-z0-9\._-]+?) -->/is', '\' . $this->parse_system_message($paths->sysMsg(\'\\1\')) . \'', $text); // Hooks $text = preg_replace('/<!-- HOOK ([A-z0-9_]+) -->/', '\' . $this->get_theme_hook(\'\\1\') . \'', $text); // only do this if the plugins API is loaded if ( is_object(@$plugins) ) { $code = $plugins->setHook('template_compile_subst'); foreach ( $code as $cmd ) { eval($cmd); } } // Template variables $text = preg_replace('/\{([A-z0-9_-]+?)\}/is', '\' . $this->tpl_strings[\'\\1\'] . \'', $text); // Reinsert PHP foreach ( $php_matches[1] as $i => $match ) { // Substitute the random tag with the "real" PHP code $tag = "{PHP:$i:$seed}"; $text = str_replace_once($tag, "'; $match echo '", $text); } // echo('<pre>' . htmlspecialchars($text) . '</pre>'); return $text; } /** * Handles parsing of an individual template file. Instances should only be created through $template->makeParser(). To use: * - Call $template->makeParser(template file name) - file name should be something.tpl, css/whatever.css, etc. * - Make an array of strings you want the template to access. $array['STRING'] would be referenced in the template like {STRING} * - Make an array of boolean values. These can be used for conditionals in the template (<!-- IF something --> whatever <!-- ENDIF something -->) * - Call assign_vars() to pass the strings to the template parser. Same thing with assign_bool(). * - Call run() to parse the template and get your fully compiled HTML. * @access private */ class templateIndividual extends template { var $tpl_strings, $tpl_bool, $tpl_code; var $compiled = false; /** * Constructor. */ function __construct($text) { global $db, $session, $paths, $template, $plugins; // Common objects $this->tpl_code = $text; $this->tpl_strings = $template->tpl_strings; $this->tpl_bool = $template->tpl_bool; } /** * PHP 4 constructor. Deprecated in 1.1.x. */ /* function templateIndividual($text) { $this->__construct($text); } */ /** * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file. * @param $vars array */ // We add the unused variable $from_internal here to silence "declaration should be compatible" errors function assign_vars($vars, $from_internal = false) { $this->tpl_strings = array_merge($this->tpl_strings, $vars); } /** * Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements. * @param $vars array */ // We add the unused variable $from_internal here to silence "declaration should be compatible" errors function assign_bool($vars, $from_internal = false) { $this->tpl_bool = array_merge($this->tpl_bool, $vars); } /** * Compiles and executes the template code. * @return string */ function run() { global $db, $session, $paths, $template, $plugins; // Common objects if(!$this->compiled) { $this->tpl_code = $this->compile_template_text($this->tpl_code); $this->compiled = true; } return eval($this->tpl_code); } } /** * A version of the template compiler that does not rely at all on the other parts of Enano. Used during installation and for showing * "critical error" messages. ** REQUIRES ** the Oxygen theme. */ class template_nodb { var $fading_button, $tpl_strings, $tpl_bool, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list; function __construct() { $this->tpl_bool = Array(); $this->tpl_strings = Array(); $this->sidebar_extra = ''; $this->sidebar_widgets = ''; $this->toolbar_menu = ''; $this->additional_headers = '<style type="text/css">div.pagenav { border-top: 1px solid #CCC; padding-top: 7px; margin-top: 10px; }</style>'; $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;"> <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> </div>'; // get list of themes $this->theme_list = array(); $this->named_theme_list = array(); $order = 0; if ( $dir = @opendir( ENANO_ROOT . '/themes' ) ) { while ( $dh = @readdir($dir) ) { if ( $dh == '.' || $dh == '..' || !is_dir( ENANO_ROOT . "/themes/$dh" ) ) continue; $theme_dir = ENANO_ROOT . "/themes/$dh"; if ( !file_exists("$theme_dir/theme.cfg") ) continue; $data = array( 'theme_id' => $dh, 'theme_name' => ucwords($dh), 'enabled' => 1, 'theme_order' => ++$order, 'default_style' => $this->get_default_style($dh) ); $this->named_theme_list[$dh] = $data; $this->theme_list[] =& $this->named_theme_list[$dh]; } @closedir($dir); } } function template() { $this->__construct(); } function get_default_style($theme_id) { if ( !is_dir( ENANO_ROOT . "/themes/$theme_id/css" ) ) return false; $ds = false; if ( $dh = @opendir( ENANO_ROOT . "/themes/$theme_id/css" ) ) { while ( $dir = @readdir($dh) ) { if ( !preg_match('/\.css$/', $dir) ) continue; if ( $dir == '_printable.css' ) continue; $ds = preg_replace('/\.css$/', '', $dir); break; } closedir($dh); } else { return false; } return $ds; } function get_css($s = false) { if ( $s ) return $this->process_template('css/'.$s); else return $this->process_template('css/'.$this->style.'.css'); } function load_theme($name, $css, $auto_init = true) { if ( !isset($this->named_theme_list[$name]) ) $name = $this->theme_list[0]['theme_id']; if ( !file_exists(ENANO_ROOT . "/themes/$name/css/$css.css") ) $css = $this->named_theme_list[$name]['default_style']; $this->theme = $name; $this->style = $css; $this->tpl_strings['SCRIPTPATH'] = scriptPath; if ( $auto_init ) $this->init_vars(); } function add_header($html) { $this->additional_headers .= "\n<!-- ----------------------------------------------------------- -->\n\n " . $html; } function init_vars() { global $sideinfo; global $this_page; global $lang; global $db, $session, $paths, $template, $plugins; // Common objects $tplvars = $this->extract_vars('elements.tpl'); $tb = ''; // Get the "article" button text (depends on namespace) if(defined('IN_ENANO_INSTALL') && is_object($lang)) $ns = $lang->get('meta_btn_article'); else if ( isset($GLOBALS['article_btn']) ) $ns = $GLOBALS['article_btn']; else $ns = 'system error page'; $t = str_replace('{FLAGS}', 'onclick="return false;" class="current" title="Hey! A button that doesn\'t do anything. Clever..." accesskey="a"', $tplvars['toolbar_button']); $t = str_replace('{HREF}', '#', $t); $t = str_replace('{TEXT}', $ns, $t); $tb .= $t; // Page toolbar $this->tpl_bool = Array( 'auth_admin'=>true, 'user_logged_in'=>true, 'right_sidebar'=>false, ); $this->tpl_bool['in_sidebar_admin'] = false; $this->tpl_bool['auth_rename'] = false; $asq = $asa = ''; $this->tpl_bool['fixed_menus'] = false; $slink = defined('IN_ENANO_INSTALL') ? scriptPath.'/install.php?mode=css' : makeUrlNS('Special', 'CSS'); $title = ( is_object($paths) ) ? $paths->page : 'Critical error'; $headers = '<style type="text/css">div.pagenav { border-top: 1px solid #CCC; padding-top: 7px; margin-top: 10px; }</style>'; $js_dynamic = ''; if ( defined('IN_ENANO_INSTALL') ) { $js_dynamic .= '<script type="text/javascript" src="install.php?mode=langjs"></script>'; } $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>'; global $site_name, $site_desc; $site_default_name = ( !empty($site_name) ) ? $site_name : 'Critical error'; $site_default_desc = ( !empty($site_desc) ) ? $site_desc : 'This site is experiencing a problem and cannot load.'; $site_name_final = ( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_site_name') : $site_default_name; $site_desc_final = ( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_site_desc') : $site_default_desc; // The rewritten template engine will process all required vars during the load_template stage instead of (cough) re-processing everything each time around. $tpl_strings = Array( 'PAGE_NAME'=>$this_page, 'PAGE_URLNAME'=>'Null', 'SITE_NAME' => $site_name_final, 'USERNAME'=>'admin', 'SITE_DESC' => $site_desc_final, 'TOOLBAR'=>$tb, 'SCRIPTPATH'=>scriptPath, 'CONTENTPATH'=>contentPath, 'CDNPATH' => scriptPath, 'JS_HEADER' => '<script type="text/javascript" src="' . scriptPath . '/includes/clientside/static/enano-lib-basic.js"></script>', 'JS_FOOTER' => '', 'ADMIN_SID_QUES'=>$asq, 'ADMIN_SID_AMP'=>$asa, 'ADMIN_SID_AMP_HTML'=>'', 'ADDITIONAL_HEADERS'=>$this->additional_headers, 'SIDEBAR_EXTRA'=>'', 'COPYRIGHT'=>( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_enano_copyright') : ( defined('ENANO_CONFIG_FETCHED') ? getConfig('copyright_notice') : '' ), 'TOOLBAR_EXTRAS'=>'', 'REQUEST_URI'=>( isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '' ).$_SERVER['REQUEST_URI'], 'STYLE_LINK'=>$slink, 'LOGOUT_LINK'=>'', 'THEME_LINK'=>'', 'TEMPLATE_DIR'=>scriptPath.'/themes/'.$this->theme, 'THEME_ID'=>$this->theme, 'STYLE_ID'=>$this->style, 'JS_DYNAMIC_VARS'=>$js_dynamic, 'SIDEBAR_RIGHT'=>'', 'REPORT_URI' => '', 'URL_ABOUT_ENANO' => 'http://enanocms.org/', 'ENANO_VERSION' => enano_version() ); $this->tpl_strings = array_merge($tpl_strings, $this->tpl_strings); $sidebar = ( is_array(@$sideinfo) ) ? $sideinfo : ''; if ( $sidebar != '' ) { if ( isset($tplvars['sidebar_top']) ) { $text = $this->makeParserText($tplvars['sidebar_top']); $top = $text->run(); } else { $top = ''; } $p = $this->makeParserText($tplvars['sidebar_section']); $b = $this->makeParserText($tplvars['sidebar_button']); $sidebar_text = ''; foreach ( $sidebar as $title => $links ) { $p->assign_vars(array( 'TITLE' => $title )); // build content $content = ''; foreach ( $links as $link_text => $url ) { $b->assign_vars(array( 'HREF' => htmlspecialchars($url), 'FLAGS' => '', 'TEXT' => $link_text )); $content .= $b->run(); } $p->assign_vars(array( 'CONTENT' => $content )); $sidebar_text .= $p->run(); } if ( isset($tplvars['sidebar_bottom']) ) { $text = $this->makeParserText($tplvars['sidebar_bottom']); $bottom = $text->run(); } else { $bottom = ''; } $sidebar = $top . $sidebar_text . $bottom; } $this->tpl_strings['SIDEBAR_LEFT'] = $sidebar; $this->tpl_bool['sidebar_left'] = ( $this->tpl_strings['SIDEBAR_LEFT'] != '') ? true : false; $this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != '') ? true : false; $this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility $this->tpl_bool['stupid_mode'] = true; } function header($simple = false) { $filename = ( $simple ) ? 'simple-header.tpl' : 'header.tpl'; if ( !$this->no_headers ) { echo $this->process_template($filename); } } function footer($simple = false) { global $db, $session, $paths, $template, $plugins; // Common objects global $lang; if(!$this->no_headers) { global $_starttime; $filename = ( $simple ) ? 'simple-footer.tpl' : 'footer.tpl'; $t = $this->process_template($filename); $f = microtime_float(); $f = $f - $_starttime; $f = round($f, 4); if ( is_object($lang) ) { $t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f)); $t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f)); $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>'; $dbg = $t_loc; $dbg_long = $t_loc_long; if ( $session->user_level >= USER_LEVEL_ADMIN ) { $dbg .= " | $q_loc"; $dbg_long .= " | $q_loc"; } $t = str_replace('[[EnanoPoweredLink]]', $lang->get('page_enano_powered', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t); $t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t); } else { $t_loc = "Time: {$f}s"; $t_loc_long = "Generated in {$f}sec"; $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . ( is_object($db) ? "{$db->num_queries} SQL" : 'Queries: N/A' ) . '</a>'; $dbg = $t_loc; $dbg_long = $t_loc_long; if ( is_object($session) ) { if ( $session->user_level >= USER_LEVEL_ADMIN ) { $dbg .= " | $q_loc"; $dbg_long .= " | $q_loc"; } } $t = str_replace('[[EnanoPoweredLink]]', 'Powered by <a href="http://enanocms.org/" onclick="window.open(this.href); return false;">Enano</a>', $t); $t = str_replace('[[EnanoPoweredLinkLong]]', 'Website engine powered by <a href="http://enanocms.org/" onclick="window.open(this.href); return false;">Enano</a>', $t); } $t = str_replace('[[Stats]]', $dbg, $t); $t = str_replace('[[StatsLong]]', $dbg_long, $t); $t = str_replace('[[NumQueries]]', ( is_object($db) ? (string)$db->num_queries : '0' ), $t); $t = str_replace('[[GenTime]]', (string)$f, $t); $t = str_replace('[[NumQueriesLoc]]', $q_loc, $t); $t = str_replace('[[GenTimeLoc]]', $t_loc, $t); if ( defined('ENANO_PROFILE') ) { $t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t); } echo $t; } else return ''; } function getHeader() { if(!$this->no_headers) return $this->process_template('header.tpl'); else return ''; } function getFooter() { global $db, $session, $paths, $template, $plugins; // Common objects if(!$this->no_headers) { global $_starttime; $f = microtime(true); $f = $f - $_starttime; $f = round($f, 4); if(defined('IN_ENANO_INSTALL')) $nq = 'N/A'; else $nq = $db->num_queries; if($nq == 0) $nq = 'N/A'; $dbg = 'Time: '.$f.'s | Queries: '.$nq; if($nq == 0) $nq = 'N/A'; $t = $this->process_template('footer.tpl'); $t = str_replace('[[Stats]]', $dbg, $t); return $t; } else return ''; } function process_template($file) { $compiled = $this->compile_template($file); $result = eval($compiled); return $result; } function extract_vars($file) { global $db, $session, $paths, $template, $plugins; // Common objects if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file)) die('Cannot find '.$file.' file for style "'.$this->theme.'", exiting'); $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file); preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches); $tplvars = Array(); for($i=0;$i<sizeof($matches[1]);$i++) { $tplvars[$matches[1][$i]] = $matches[2][$i]; } return $tplvars; } function compile_template($text) { $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text); return $this->compile_template_text_post(template_compiler_core($text)); } function compile_template_text($text) { return $this->compile_template_text_post(template_compiler_core($text)); } /** * Post-processor for template code. Basically what this does is it localizes {lang:foo} blocks. * @param string Mostly-processed TPL code * @return string */ function compile_template_text_post($text) { global $lang; preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $text, $matches); foreach ( $matches[1] as $i => $string_id ) { if ( is_object(@$lang) ) { $string = $lang->get($string_id); } else { $string = '[language not loaded]'; } $string = str_replace('\\', '\\\\', $string); $string = str_replace('\'', '\\\'', $string); $text = str_replace_once($matches[0][$i], $string, $text); } return $text; } /** * Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;) * Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class. * @param $filename the filename of the template to be parsed * @return object */ function makeParser($filename) { $filename = ENANO_ROOT.'/themes/'.$this->theme.'/'.$filename; if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist'); $code = file_get_contents($filename); $parser = new templateIndividualSafe($code, $this); return $parser; } /** * Same as $template->makeParser(), but takes a string instead of a filename. * @param $text the text to parse * @return object */ function makeParserText($code) { $parser = new templateIndividualSafe($code, $this); return $parser; } /** * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file. * @param $vars array */ function assign_vars($vars, $_ignored = false) { if(is_array($this->tpl_strings)) $this->tpl_strings = array_merge($this->tpl_strings, $vars); else $this->tpl_strings = $vars; } function get_theme_hook() { return ''; } } // class template_nodb /** * Identical to templateIndividual, except extends template_nodb instead of template * @see class template */ class templateIndividualSafe extends template_nodb { var $tpl_strings, $tpl_bool, $tpl_code; var $compiled = false; /** * Constructor. */ function __construct($text, $parent) { global $db, $session, $paths, $template, $plugins; // Common objects $this->tpl_code = $text; $this->tpl_strings = $parent->tpl_strings; $this->tpl_bool = $parent->tpl_bool; } /** * PHP 4 constructor. */ function templateIndividual($text) { $this->__construct($text); } /** * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file. * @param $vars array */ function assign_vars($vars, $_ignored = false) { if(is_array($this->tpl_strings)) $this->tpl_strings = array_merge($this->tpl_strings, $vars); else $this->tpl_strings = $vars; } /** * Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements. * @param $vars array */ function assign_bool($vars) { $this->tpl_bool = array_merge($this->tpl_bool, $vars); } /** * Compiles and executes the template code. * @return string */ function run() { global $db, $session, $paths, $template, $plugins; // Common objects if(!$this->compiled) { $this->tpl_code = $this->compile_template_text($this->tpl_code); $this->compiled = true; } return eval($this->tpl_code); } } ?>