Extension:Header Footer

Note on Caching
Version 1.14 addresses the parser cache issue by temporarily disabling the cache during article update/creation. Once the page is first viewed, pages targetted by the HeaderFooter extension are properly stored in the parser cache (if applicable/enabled of course).

Example1
Let's say one wants to add a 'Header' to all pages (and sub-pages) of the 'Main' namespace: add the following in 'LocalSettings.php': require_once("extensions/ArticleCacheClass.php");   // dependancy require_once("extensions/HeaderFooter.php"); $hfObj->setNsParams(NS_MAIN, array('enable'=>true)); // enable for the main namespace and create the 'Header' page in the 'Main' namespace (the default namespace).

Example2
Let's say one wants to add a 'Header' and a 'Footer' to all pages (and sub-pages) of the 'Category:Protocols' namespace: add the following in 'LocalSettings.php': require_once("extensions/ArticleCacheClass.php");   // dependancy require_once("extensions/HeaderFooter.php"); $hfObj->setNsParams(NS_CATEGORY, array('enable'=>true)); // enable for the Category namespace and create the 'Category:Protocols/Header' and 'Category:Protocols/Footer' pages.

HeaderFooter.php
<?php /* * HeaderFooter.php * * MediaWiki extension * @author: Jean-Lou Dupont (http://www.bluecortex.com) * $Id: HeaderFooter.php 202 2007-05-23 15:23:37Z JeanLou.Dupont $ * $LastChangedRevision: 202 $ * Purpose: insert an header and/or footer to an article *         upon a 'view' action. * * Features: * ********* * - Recursive (bottom-up) search for "Header" and "Footer" subpages * - section support * - Configurable per-namespace recursive level depth * - Configurable per-namespace enable/disable flag (all disable per default) * * DEPENDANCIES: * - ArticleCacheClass (for recursive search function) * * * Tested Compatibility: MW 1.8.2, 1.9.3 * * HISTORY: * -- Version 1.1:	added support for and * -- Version 1.11: changed interface to ArticleCacheClass: using singleton * -- Version 1.12: added check to ensure that the process is only performed once *                 i.e. the article requested in the transaction (and not other *                  articles fetched during the rendering process) *                 (helps with some other extensions that do fetch multiple pages *                   before returning the final render requested page). * -- Version 1.13: - Added support for '__NOHEADER__' and '__NOFOOTER__' magic words * -- Version 1.14: - Added support for better parser cache integration *                   i.e. the normal MW behavior is to save an updated/created article *                   in the parser cache BEFORE this extension has the chance to execute. *                   This extension update disables this behavior but don't worry, the article will *                   be saved by MW the next time the article is viewed. * -- Version 1.15: - Integration with 'ParserCacheControl' extension. * -- Version 1.16: - Added missing 'return true' in function handler. *                 - Added check for 'ParserCacheControl' in hook creation. * */ $wgExtensionCredits['other'][] = array( 	'name'   => 'HeaderFooter Extension', 	'version' => 'v1.16 $LastChangedRevision: 202 $',	'author'  => 'Jean-Lou Dupont', 	'url'     => 'http://www.bluecortex.com', );

require_once("HeaderFooterClass.php"); $hfObj = &HeaderFooterClass::singleton; $wgHooks['ArticleAfterFetchContent'][] = array( $hfObj, 'hAddHeaderFooter' );

// v1.16 if (!class_exists('ParserCacheControl')) $wgHooks['ArticleSave'][] =         array( $hfObj, 'hArticleSave' ); ?>

HeaderFooterClass.php
<?php /* * HeaderFooterClass.php * * MediaWiki extension * @author: Jean-Lou Dupont (http://www.bluecortex.com) * $Id: HeaderFooterClass.php 202 2007-05-23 15:23:37Z JeanLou.Dupont $ * $LastChangedRevision: 202 $ */

class HeaderFooterClass {	var $done; // v1.12 var $cache; var $level; var $inproc; var $nsPar = array;

public static function &singleton {		static $instance; if ( !isset( $instance ) ) $instance = new HeaderFooterClass; return $instance; }	// deprecated interface: use "singleton" functionality. static function getGlobalObjectName { return "hfObj";          } static function &getGlobalObject   { return $GLOBALS['hfObj']; } public function HeaderFooterClass {		$this->cache = &ArticleCacheClass::singleton; $this->level = 0 ; $this->done = false; }	/*	 *  Parameters: *  ==========	 *   Enable => true/false *  Level  => 0, 1, 2 etc.	 *             IF 0 THEN recurse to top of namespace (top:  NS:header, NS:footer) *            IF 1 THEN just check the current level e.g. NS:base/header, NS:base/footer */	public function setNsParams( $ns, $p ) { $this->nsPar[$ns] = $p; } public function hAddHeaderFooter( &$article, &$content ) {		global $action;

// only show up the header/footer on page views if ($action != 'view') return true;

if ( $this->done ) return true; $this->done = true;

// check the per-namespace enable/disable attribute. $ns = $article->mTitle->getNamespace; if (!$this->nsPar[$ns]['enable']) return true;

// Re-entrancy check // If this function is called recursively, // that probably means a processor code page // is being fetched. Get out. if ($this->inproc) return true; $this->inproc= true;

$name = $article->mTitle->getPrefixedDBkey; // Check if the title name ends with either "header" or "footer" // and skip if true. $e = explode("/", $name); $t = $e[count($e)-1]; $u = strtolower($t); // take care of namespace prefix if present. $v=explode(":", $u); if (count($v)>1) $u = $v[1];

$hdisable=false; $fdisable=false; // check for disabling directives. if (strpos($content, " ")!==false) $hdisable=true; if (strpos($content, " ")!==false) $fdisable=true; if (strpos($content, "__NOHEADER__")!==false) $hdisable=true; // v1.13 if (strpos($content, "__NOFOOTER__")!==false) $fdisable=true; // v1.13 $content = preg_replace('//si','', $content); $content = preg_replace('//si','', $content); $content = preg_replace('/__NOHEADER__/si','', $content); // v1.13 $content = preg_replace('/__NOFOOTER__/si','', $content); // v1.13 if ($u<>"header" and $u<>"footer") {				if (!$hdisable) $h = $this->cache->recurseGetArticleContent($name, "Header", $this->nsPar[$ns]['level'] ); if (!$fdisable) $f = $this->cache->recurseGetArticleContent($name, "Footer", $this->nsPar[$ns]['level'] ); if (!empty($h)) $h = preg_replace( '/ .*<\/noinclude>/si', '', $h ); if (!empty($f)) $f = preg_replace( '/ .*<\/noinclude>/si', '', $f ); $content = $h.$content.$f; }				// RESET re-entrancy flag. $this->inproc= false; return true; }	public function hArticleSave( &$article, &$user, &$text, &$summary, $minor, $watch, $sectionanchor, &$flags ) // V1.14 enhancement. {		global $wgParserCacheType; // check the per-namespace enable/disable attribute. // If the extension is enabled in this namespace, then proceed. $ns = $article->mTitle->getNamespace; if (!$this->nsPar[$ns]['enable']) return true;

// disable the parser cache for this transaction. // The hack below will affect the method Article::editUpdates // into not saving the current article to a potential real cache. // BUT, once the article is viewed, the article will then be stored in the real cache. $wgParserCacheType = CACHE_NONE; $apc =& wfGetParserCacheStorage; $pc = & ParserCache::singleton; $pc->mMemc = $apc;

return true; } } // END CLASS DEFINITION ?>