--- a/includes/template.php Sun Aug 24 13:39:46 2008 -0400
+++ b/includes/template.php Sun Aug 24 13:43:04 2008 -0400
@@ -2051,84 +2051,59 @@
/**
* Fetches the contents of both sidebars.
- * @return array - key 0 is left, key 1 is right
+ * @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
- global $cache;
+ // first, check the cache
+ if ( $result = $this->fetch_cached_sidebar() )
+ {
+ return $result;
+ }
+
+ profiler_log('Started sidebar parsing');
+
+ // init our block contents
$left = '';
$right = '';
+ $min = '';
- // check the cache
- if ( !$session->user_logged_in && $data = $cache->fetch('anon_sidebar') )
- {
- if ( @$data['_theme_'] === $this->theme )
- {
- unset($data['_theme_']);
- foreach ( $data as &$md )
- {
- $md = str_replace('$USERNAME$', $session->username, $md);
- $md = str_replace('$PAGEID$', $paths->page, $md);
- $md = str_replace('$MAIN_PAGE$', getConfig('main_page'), $md);
- }
- return $data;
- }
- }
-
+ // also might want the links block
if ( !$this->fetch_block('Links') )
$this->initLinksWidget();
- $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.');
-
+ // templates to work with
$vars = $this->extract_vars('elements.tpl');
- if(isset($vars['sidebar_top']))
+ // 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;
}
- while($row = $db->fetchrow())
+ // 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) )
{
- switch($row['block_type'])
- {
- case BLOCK_WIKIFORMAT:
- default:
- $parser = $this->makeParserText($vars['sidebar_section']);
- $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:
- $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;
- }
+ // 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_\(\)\|&! ]+)\}/', $c, $match) )
+ if ( preg_match('/\{(restrict|hideif) ([a-z0-9_\(\)\|&! ]+)\}/', $block_content, $match) )
{
// we have one, check the condition
$type =& $match[1];
@@ -2140,53 +2115,253 @@
continue;
}
// didn't get a match, so hide the conditional logic
- $c = str_replace_once($match[0], '', $c);
+ // FIXME: this needs to be verbose about syntax errors
+ $block_content = str_replace_once($match[0], '', $block_content);
}
- $parser->assign_vars(Array( 'TITLE'=>$this->tplWikiFormat($row['block_name']), 'CONTENT'=>$c ));
- $run = $parser->run();
+ // 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 )
{
- $run = str_replace('{TITLE}', $this->tplWikiFormat($row['block_name']), $run);
+ $parsed = str_replace('{TITLE}', $block_name, $parsed);
}
- if ($row['sidebar_id'] == SIDEBAR_LEFT ) $left .= $run;
- elseif($row['sidebar_id'] == SIDEBAR_RIGHT) $right .= $run;
- unset($parser);
+
+ // 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);
}
- $db->free_result();
+
+ // lastly, append any footer HTML
if(isset($vars['sidebar_bottom']))
{
$bottom = $this->parse($vars['sidebar_bottom']);
$left .= $bottom;
$right .= $bottom;
+ $min .= $bottom;
}
- $min = '';
- if(isset($vars['sidebar_top']))
+
+ $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 )
{
- $min .= $top;
+ eval($cmd);
}
- if(isset($vars['sidebar_bottom']))
- {
- $min .= $bottom;
- }
- $return = Array($left, $right, $min);
- if ( getConfig('cache_thumbs') == '1' && !$session->user_logged_in )
+
+ // 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'])
{
- $cachestore = enano_json_encode($return);
- $cachestore = str_replace($session->username, '$USERNAME$', $cachestore);
- $cachestore = str_replace($paths->page, '$PAGEID$', $cachestore);
- $cachestore = str_replace('__STATICLINK__', $paths->page, $cachestore);
- $cachestore = str_replace('__MAINPAGELINK__', '$MAIN_PAGE$', $cachestore);
- $cachestore = enano_json_decode($cachestore);
- $cachestore['_theme_'] = $this->theme;
- $cache->store('anon_sidebar', $cachestore, 10);
-
- foreach ( $return as &$el )
+ case BLOCK_WIKIFORMAT:
+ $parser = $this->makeParserText($vars['sidebar_section']);
+ $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]) )
{
- $el = str_replace('__STATICLINK__', $paths->page, $el);
+ $cached = $result[$this->theme];
}
}
- return $return;
+
+ // 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()