includes/template.php
changeset 708 162763d69256
parent 704 077887be639d
child 723 78d391d37700
equal deleted inserted replaced
707:7be26f1d1068 708:162763d69256
  2049     else return false;
  2049     else return false;
  2050   }
  2050   }
  2051   
  2051   
  2052   /**
  2052   /**
  2053    * Fetches the contents of both sidebars.
  2053    * Fetches the contents of both sidebars.
  2054    * @return array - key 0 is left, key 1 is right
  2054    * @return array - key 0 is left, key 1 is right, key 2 is the HTML that makes up an empty sidebar
  2055    * @example list($left, $right) = $template->fetch_sidebar();
  2055    * @example list($left, $right) = $template->fetch_sidebar();
  2056    */
  2056    */
  2057   
  2057   
  2058   function fetch_sidebar()
  2058   function fetch_sidebar()
  2059   {
  2059   {
  2060     global $db, $session, $paths, $template, $plugins; // Common objects
  2060     global $db, $session, $paths, $template, $plugins; // Common objects
  2061     global $cache;
  2061     
  2062     
  2062     // first, check the cache
       
  2063     if ( $result = $this->fetch_cached_sidebar() )
       
  2064     {
       
  2065       return $result;
       
  2066     }
       
  2067     
       
  2068     profiler_log('Started sidebar parsing');
       
  2069     
       
  2070     // init our block contents
  2063     $left = '';
  2071     $left = '';
  2064     $right = '';
  2072     $right = '';
  2065     
  2073     $min = '';
  2066     // check the cache
  2074     
  2067     if ( !$session->user_logged_in && $data = $cache->fetch('anon_sidebar') )
  2075     // also might want the links block
  2068     {
       
  2069       if ( @$data['_theme_'] === $this->theme )
       
  2070       {
       
  2071         unset($data['_theme_']);
       
  2072         foreach ( $data as &$md )
       
  2073         {
       
  2074           $md = str_replace('$USERNAME$', $session->username, $md);
       
  2075           $md = str_replace('$PAGEID$', $paths->page, $md);
       
  2076           $md = str_replace('$MAIN_PAGE$', getConfig('main_page'), $md);
       
  2077         }
       
  2078         return $data;
       
  2079       }
       
  2080     }
       
  2081     
       
  2082     if ( !$this->fetch_block('Links') )
  2076     if ( !$this->fetch_block('Links') )
  2083       $this->initLinksWidget();
  2077       $this->initLinksWidget();
  2084     
  2078     
  2085     $q = $db->sql_query('SELECT item_id,sidebar_id,block_name,block_type,block_content FROM '.table_prefix.'sidebar' . "\n"
  2079     // templates to work with
  2086                            . '  WHERE item_enabled=1 ORDER BY sidebar_id ASC, item_order ASC;');
       
  2087     if(!$q) $db->_die('The sidebar text data could not be selected.');
       
  2088     
       
  2089     $vars = $this->extract_vars('elements.tpl');
  2080     $vars = $this->extract_vars('elements.tpl');
  2090     
  2081     
  2091     if(isset($vars['sidebar_top'])) 
  2082     // sidebar_top and sidebar_bottom are HTML that is prepended/appended to sidebar content. Themes can
       
  2083     // choose whether or not to display a sidebar depending on whether the sidebar is empty ( $left == $min )
       
  2084     // or not using the sidebar_left and sidebar_right template booleans (the oxygen theme does this).
       
  2085     if ( isset($vars['sidebar_top']) ) 
  2092     {
  2086     {
  2093       $top = $this->parse($vars['sidebar_top']);
  2087       $top = $this->parse($vars['sidebar_top']);
  2094       $left  .= $top;
  2088       $left  .= $top;
  2095       $right .= $top;
  2089       $right .= $top;
  2096     }
  2090       $min .= $top;
  2097     
  2091     }
  2098     while($row = $db->fetchrow())
  2092     
  2099     {
  2093     // grab the blocks from the DB
  2100       switch($row['block_type'])
  2094     $q = $db->sql_query('SELECT item_id,sidebar_id,block_name,block_type,block_content FROM '.table_prefix.'sidebar' . "\n"
  2101       {
  2095                            . '  WHERE item_enabled=1 ORDER BY sidebar_id ASC, item_order ASC;');
  2102         case BLOCK_WIKIFORMAT:
  2096     if ( !$q )
  2103         default:
  2097       $db->_die('The sidebar text data could not be selected.');
  2104           $parser = $this->makeParserText($vars['sidebar_section']);
  2098     
  2105           $c = RenderMan::render($row['block_content']);
  2099     // explicitly specify $q in case a plugin or PHP block makes a query
  2106           break;
  2100     while ( $row = $db->fetchrow($q) )
  2107         case BLOCK_TEMPLATEFORMAT:
  2101     {
  2108           $parser = $this->makeParserText($vars['sidebar_section']);
  2102       // format the block
  2109           $c = $this->tplWikiFormat($row['block_content']);
  2103       $block_content = $this->format_sidebar_block($row, $vars, $parser);
  2110           break;
  2104       
  2111         case BLOCK_HTML:
       
  2112           $parser = $this->makeParserText($vars['sidebar_section_raw']);
       
  2113           $c = $row['block_content'];
       
  2114           break;
       
  2115         case BLOCK_PHP:
       
  2116           $parser = $this->makeParserText($vars['sidebar_section_raw']);
       
  2117           ob_start();
       
  2118           @eval($row['block_content']);
       
  2119           $c = ob_get_contents();
       
  2120           ob_end_clean();
       
  2121           break;
       
  2122         case BLOCK_PLUGIN:
       
  2123           $parser = $this->makeParserText('{CONTENT}');
       
  2124           $c = '<!-- PLUGIN -->' . (gettype($this->fetch_block($row['block_content'])) == 'string') ?
       
  2125                   $this->fetch_block($row['block_content']) :
       
  2126                   // This used to say "can't find plugin block" but I think it's more friendly to just silently hide it.
       
  2127                   '';
       
  2128           break;
       
  2129       }
       
  2130       // is there a {restrict} or {hideif} block?
  2105       // is there a {restrict} or {hideif} block?
  2131       if ( preg_match('/\{(restrict|hideif) ([a-z0-9_\(\)\|&! ]+)\}/', $c, $match) )
  2106       if ( preg_match('/\{(restrict|hideif) ([a-z0-9_\(\)\|&! ]+)\}/', $block_content, $match) )
  2132       {
  2107       {
  2133         // we have one, check the condition
  2108         // we have one, check the condition
  2134         $type =& $match[1];
  2109         $type =& $match[1];
  2135         $cond =& $match[2];
  2110         $cond =& $match[2];
  2136         $result = $this->process_condition($cond);
  2111         $result = $this->process_condition($cond);
  2138         {
  2113         {
  2139           // throw out this block, it's hidden for whatever reason by the sidebar script
  2114           // throw out this block, it's hidden for whatever reason by the sidebar script
  2140           continue;
  2115           continue;
  2141         }
  2116         }
  2142         // didn't get a match, so hide the conditional logic
  2117         // didn't get a match, so hide the conditional logic
  2143         $c = str_replace_once($match[0], '', $c);
  2118         // FIXME: this needs to be verbose about syntax errors
  2144       }
  2119         $block_content = str_replace_once($match[0], '', $block_content);
  2145       
  2120       }
  2146       $parser->assign_vars(Array( 'TITLE'=>$this->tplWikiFormat($row['block_name']), 'CONTENT'=>$c ));
  2121       
  2147       $run = $parser->run();
  2122       // if we made it here, this block definitely needs to be displayed. send it to the
       
  2123       // parser (set by format_sidebar_block) and decide where to append it (but not in that order ;))
       
  2124       $appender = false;
       
  2125       
       
  2126       if ( $row['sidebar_id'] == SIDEBAR_LEFT )
       
  2127       {
       
  2128         $appender =& $left;
       
  2129       }
       
  2130       else if ( $row['sidebar_id'] == SIDEBAR_RIGHT )
       
  2131       {
       
  2132         $appender =& $right;
       
  2133       }
       
  2134       else
       
  2135       {
       
  2136         // uhoh, block_id isn't valid. maybe a plugin wants to put this block somewhere else?
       
  2137         $code = $plugins->setHook('sidebar_block_id');
       
  2138         foreach ( $code as $cmd )
       
  2139         {
       
  2140           eval($cmd);
       
  2141         }
       
  2142         // if $appender wasn't set by a plugin, don't parse this block to save some CPU cycles
       
  2143         if ( !$appender )
       
  2144         {
       
  2145           continue;
       
  2146         }
       
  2147       }
       
  2148       
       
  2149       // assign variables to parser
       
  2150       $block_name = $this->tplWikiFormat($row['block_name']);
       
  2151       $parser->assign_vars(array(
       
  2152           // be nice and format the title (FIXME, does this use a lot of CPU time? still needs l10n in some cases though)
       
  2153           'TITLE' => $block_name,
       
  2154           'CONTENT' => $block_content
       
  2155         ));
       
  2156       $parsed = $parser->run();
       
  2157       
       
  2158       // plugins are parsed earlier due to the way disabled/missing plugins that add sidebar blocks are
       
  2159       // handled, so the {TITLE} var won't be replaced until now. this allows completely eliminating a
       
  2160       // block if it's not available
  2148       if ( $row['block_type'] == BLOCK_PLUGIN )
  2161       if ( $row['block_type'] == BLOCK_PLUGIN )
  2149       {
  2162       {
  2150         $run = str_replace('{TITLE}', $this->tplWikiFormat($row['block_name']), $run);
  2163         $parsed = str_replace('{TITLE}', $block_name, $parsed);
  2151       }
  2164       }
  2152       if    ($row['sidebar_id'] == SIDEBAR_LEFT ) $left  .= $run;
  2165       
  2153       elseif($row['sidebar_id'] == SIDEBAR_RIGHT) $right .= $run;
  2166       // done parsing - append and continue
  2154       unset($parser);
  2167       $appender .= $parsed;
  2155     }
  2168       
  2156     $db->free_result();
  2169       // we're done with this - unset it because it's a reference and we don't want it overwritten.
       
  2170       // also free the parser to get some RAM back
       
  2171       unset($appender, $parser);
       
  2172     }
       
  2173     
       
  2174     // lastly, append any footer HTML
  2157     if(isset($vars['sidebar_bottom'])) 
  2175     if(isset($vars['sidebar_bottom'])) 
  2158     {
  2176     {
  2159       $bottom = $this->parse($vars['sidebar_bottom']);
  2177       $bottom = $this->parse($vars['sidebar_bottom']);
  2160       $left  .= $bottom;
  2178       $left  .= $bottom;
  2161       $right .= $bottom;
  2179       $right .= $bottom;
  2162     }
  2180       $min   .= $bottom;
  2163     $min = '';
  2181     }
  2164     if(isset($vars['sidebar_top'])) 
  2182     
  2165     {
  2183     $return = array($left, $right, $min);
  2166       $min .= $top;
  2184     
  2167     }
  2185     // allow any plugins to append what they want to the return
  2168     if(isset($vars['sidebar_bottom']))
  2186     $code = $plugins->setHook('sidebar_fetch_return');
  2169     {
  2187     foreach ( $code as $cmd )
  2170       $min .= $bottom;
  2188     {
  2171     }
  2189       eval($cmd);
  2172     $return = Array($left, $right, $min);
  2190     }
  2173     if ( getConfig('cache_thumbs') == '1' && !$session->user_logged_in )
  2191     
  2174     {
  2192     // cache the result if appropriate
  2175       $cachestore = enano_json_encode($return);
  2193     $this->cache_compiled_sidebar($return);
  2176       $cachestore = str_replace($session->username, '$USERNAME$', $cachestore);
  2194     
  2177       $cachestore = str_replace($paths->page, '$PAGEID$', $cachestore);
  2195     profiler_log('Finished sidebar parsing');
  2178       $cachestore = str_replace('__STATICLINK__', $paths->page, $cachestore);
  2196     
  2179       $cachestore = str_replace('__MAINPAGELINK__', '$MAIN_PAGE$', $cachestore);
       
  2180       $cachestore = enano_json_decode($cachestore);
       
  2181       $cachestore['_theme_'] = $this->theme;
       
  2182       $cache->store('anon_sidebar', $cachestore, 10);
       
  2183       
       
  2184       foreach ( $return as &$el )
       
  2185       {
       
  2186         $el = str_replace('__STATICLINK__', $paths->page, $el);
       
  2187       }
       
  2188     }
       
  2189     return $return;
  2197     return $return;
       
  2198   }
       
  2199   
       
  2200   /**
       
  2201    * Runs the appropriate formatting routine on a sidebar row.
       
  2202    * @param array Row in sidebar table
       
  2203    * @param array Template variable set from elements.tpl
       
  2204    * @param null Pass by reference, will be filled with the parser according to the block's type (sidebar_section or sidebar_section_raw)
       
  2205    * @return string HTML + directives like {restrict} or {hideif}
       
  2206    */
       
  2207   
       
  2208   function format_sidebar_block($row, $vars, &$parser)
       
  2209   {
       
  2210     // import globals in case a PHP block wants to call the Enano API
       
  2211     global $db, $session, $paths, $template, $plugins; // Common objects
       
  2212     
       
  2213     $parser = null;
       
  2214     
       
  2215     switch($row['block_type'])
       
  2216     {
       
  2217       case BLOCK_WIKIFORMAT:
       
  2218         $parser = $this->makeParserText($vars['sidebar_section']);
       
  2219         $c = RenderMan::render($row['block_content']);
       
  2220         break;
       
  2221         
       
  2222       case BLOCK_TEMPLATEFORMAT:
       
  2223         $parser = $this->makeParserText($vars['sidebar_section']);
       
  2224         $c = $this->tplWikiFormat($row['block_content']);
       
  2225         break;
       
  2226         
       
  2227       case BLOCK_HTML:
       
  2228         $parser = $this->makeParserText($vars['sidebar_section_raw']);
       
  2229         $c = $row['block_content'];
       
  2230         break;
       
  2231         
       
  2232       case BLOCK_PHP:
       
  2233         // PHP blocks need to be sent directly to eval()
       
  2234         $parser = $this->makeParserText($vars['sidebar_section_raw']);
       
  2235         ob_start();
       
  2236         @eval($row['block_content']);
       
  2237         $c = ob_get_contents();
       
  2238         ob_end_clean();
       
  2239         break;
       
  2240         
       
  2241       case BLOCK_PLUGIN:
       
  2242         $parser = $this->makeParserText('{CONTENT}');
       
  2243         $c = '<!-- PLUGIN -->' . (gettype($this->fetch_block($row['block_content'])) == 'string') ?
       
  2244                 $this->fetch_block($row['block_content']) :
       
  2245                 // This used to say "can't find plugin block" but I think it's more friendly to just silently hide it.
       
  2246                 '';
       
  2247         break;
       
  2248       default:
       
  2249         // unknown block type - can a plugin handle it?
       
  2250         $code = $plugins->setHook('format_sidebar_block');
       
  2251         foreach ( $code as $cmd )
       
  2252         {
       
  2253           eval($cmd);
       
  2254         }
       
  2255         if ( !isset($c) )
       
  2256         {
       
  2257           // default to wiki formatting (this was going to be straight HTML but this is done for backwards compatibility reasons)
       
  2258           $c = RenderMan::render($row['block_content']);
       
  2259         }
       
  2260         if ( !$parser )
       
  2261         {
       
  2262           // no parser defined, use the "raw" section by default (plugins are more likely to want raw content
       
  2263           // rather than a list of links, and they can set the parser to use sidebar_section if they want)
       
  2264           $parser = $this->makeParserText($vars['sidebar_section_raw']);
       
  2265         }
       
  2266         
       
  2267         break;
       
  2268     }
       
  2269     
       
  2270     return $c;
       
  2271   }
       
  2272   
       
  2273   /**
       
  2274    * 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
       
  2275    * wrote this routine. You should get a copy of Master of Puppets, it's a damn good album.)
       
  2276    * @return array
       
  2277    */
       
  2278   
       
  2279   function get_cache_replacements()
       
  2280   {
       
  2281     global $db, $session, $paths, $template, $plugins; // Common objects
       
  2282     
       
  2283     return array(
       
  2284           '$LOGIN_LINK$' => $this->tpl_strings['LOGIN_LINK'],
       
  2285           '$MAIN_PAGE$' => $this->tpl_strings['MAIN_PAGE'],
       
  2286           '$USERNAME$' => $session->username
       
  2287         );
       
  2288   }
       
  2289   
       
  2290   /**
       
  2291    * Attempts to load a cached compiled sidebar.
       
  2292    * @return array Same format as fetch_sidebar()
       
  2293    */
       
  2294   
       
  2295   function fetch_cached_sidebar()
       
  2296   {
       
  2297     global $db, $session, $paths, $template, $plugins; // Common objects
       
  2298     global $cache;
       
  2299     
       
  2300     $cached = false;
       
  2301     if ( ($result = $cache->fetch('anon_sidebar')) && !$session->user_logged_in )
       
  2302     {
       
  2303       if ( isset($result[$this->theme]) )
       
  2304       {
       
  2305         $cached = $result[$this->theme];
       
  2306       }
       
  2307     }
       
  2308     
       
  2309     // if we haven't been able to fetch yet, see if a plugin wants to give us something
       
  2310     if ( !$cached )
       
  2311     {
       
  2312       $code = $plugins->setHook('fetch_cached_sidebar');
       
  2313       foreach ( $code as $cmd )
       
  2314       {
       
  2315         eval($cmd);
       
  2316       }
       
  2317     } 
       
  2318     
       
  2319     if ( is_array($cached) )
       
  2320     {
       
  2321       // fetch certain variables that can't be stored in the cache and quickly substitute
       
  2322       $replace = $this->get_cache_replacements();
       
  2323       foreach ( $cached as &$val )
       
  2324       {
       
  2325         $val = strtr($val, $replace);
       
  2326       }
       
  2327       
       
  2328       // done processing, send it back
       
  2329       return $cached;
       
  2330     }
       
  2331     
       
  2332     return false;
       
  2333   }
       
  2334   
       
  2335   /**
       
  2336    * Caches a completely compiled sidebar, if appropriate
       
  2337    * @param array Effectively, return from fetch_sidebar()
       
  2338    */
       
  2339   
       
  2340   function cache_compiled_sidebar($sidebar)
       
  2341   {
       
  2342     global $db, $session, $paths, $template, $plugins; // Common objects
       
  2343     global $cache;
       
  2344     
       
  2345     // check if conditions are right
       
  2346     if ( !$session->user_logged_in && getConfig('cache_thumbs') === '1' )
       
  2347     {
       
  2348       // load any existing cache to make sure other themes' cached sidebars aren't discarded
       
  2349       $cached = ( $_ = $cache->fetch('anon_sidebar') ) ? $_ : array();
       
  2350       
       
  2351       // replace variables
       
  2352       $replace = array_flip($this->get_cache_replacements());
       
  2353       
       
  2354       foreach ( $sidebar as &$section )
       
  2355       {
       
  2356         $section = strtr($section, $replace);
       
  2357       }
       
  2358       
       
  2359       // compile
       
  2360       $cached[$this->theme] = $sidebar;
       
  2361       
       
  2362       // store
       
  2363       $cache->store('anon_sidebar', $cached, 15);
       
  2364     }
  2190   }
  2365   }
  2191   
  2366   
  2192   function initLinksWidget()
  2367   function initLinksWidget()
  2193   {
  2368   {
  2194     global $db, $session, $paths, $template, $plugins; // Common objects
  2369     global $db, $session, $paths, $template, $plugins; // Common objects