includes/template.php
changeset 1227 bdac73ed481e
parent 1220 fbfd6c852c89
child 1274 673a1b6712fa
--- a/includes/template.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/template.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,1686 +13,1686 @@
  
 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 = '&amp;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
+	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 = '&amp;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&amp;$enano_version"></script>
+		}
+		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&amp;$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&amp;$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
+			$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&amp;$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( '<' => '&lt;', '>' => '&gt;' ) );
-    
-    $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 .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
-        $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$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
+		}
+	
+		
+		$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( '<' => '&lt;', '>' => '&gt;' ) );
+		
+		$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 .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+				$dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$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
 
 /*
@@ -1704,943 +1704,943 @@
 
 $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
+			// 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
+	(?: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']);
+		$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') . '&nbsp;&nbsp;|&nbsp;&nbsp;<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 + \'&nbsp;&nbsp;|&nbsp;&nbsp;<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>&nbsp;&nbsp;|&nbsp;&nbsp;\' + 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').'&amp;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;
-  }
-  
+		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') . '&nbsp;&nbsp;|&nbsp;&nbsp;<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 + \'&nbsp;&nbsp;|&nbsp;&nbsp;<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>&nbsp;&nbsp;|&nbsp;&nbsp;\' + 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').'&amp;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
 
 /**
@@ -2652,160 +2652,160 @@
 
 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
+	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 '
+		/* 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>&lt;?php\n" . htmlspecialchars($text."\n\n".print_r($matches,true)) . "\n\n?&gt;</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;
+		
+		$text = str_replace_once($matches[0][$i], $tag_complete, $text);
+	}
+	}
+	
+	// For debugging ;-)
+	// die("<pre>&lt;?php\n" . htmlspecialchars($text."\n\n".print_r($matches,true)) . "\n\n?&gt;</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;
 }
 
 /**
@@ -2820,64 +2820,64 @@
 
 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);
-  }
+	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);
+	}
 }
 
 /**
@@ -2887,443 +2887,443 @@
 
 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 .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
-          $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$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 .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
-            $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$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 '';
-  }
-   
+	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 .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+					$dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$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 .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+						$dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$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
 
 /**
@@ -3333,58 +3333,58 @@
  
 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);
-  }
+	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);
+	}
 }
 
 ?>