Extension:DynamicSkin

This extension allows the skin layout to be defined using normal wikitext articles instead of PHP script files. The layout and content can change automatically depending on the page being displayed.

What can this extension do?

 * Dynamic layout: This extension allows the skin layout to be defined using normal wikitext articles.
 * Controls the entire page layout, including the HTML header. you can add styles, scripts, meta tags and more without code changes.
 * Prebuilt code to generate standard page components
 * Select which skin components and classes are used.


 * Dynamic content: Menus, styles, navigation can change depending on the contents of the page being rendered.
 * Show, hide, or modify navigation and control menus for anonymous users
 * Extended sidebar syntax for easier conditional menus
 * Apply a different style to pages in a particular category or namespace

For a different approach to the same problem, see the WikiSkin extension (thanks to User:Nad for the idea).

Strictly speaking, this isn't an extension at all, but a skin: it's installed in the /skins directory, and does not use any extension hooks or global variables. However, unlike a regular skin, it doesn't define the layout or appearance. Instead it provides a framework for dynamically changing them.

While DynamicSkin makes it easy to customize skins without any code changes, it's written as a base class so you can add additional code in a derived class. For an example of a derived skin, and the site it's used on, see InfoDabble skin at ehartwell.com.

Usage
This extension allows the skin layout to be defined using normal wikitext articles instead of PHP script files. The layout and content can change automatically depending on the page being displayed.

Basics
DynamicSkin lets you define the skin layout using a normal wikitext article instead of PHP script files.
 * The name of the layout article matches the name of the skin: MediaWiki: Skin. For example, the default name for this skin is 'DynamicSkin', so the layout is in the page MediaWiki:DynamicSkinSkin.
 * If the layout article does not exist, it is created the first time DynamicSkin is used. The default layout duplicates the Monobook skin.
 * Note: DynamicSkin must be installed and configured by a user with Sysop privileges.

The default layout duplicates the Monobook skin and is shown at right.

The layout is specified by a combination of HTML and DynamicSkin tags. Wherever a tag of the for &TAG& is encountered, it is replaced by dynamically generated HTML corresponding to the MediaWiki installation.

The skin is built using the layout as follows:
 * 1) DynamicSkin pseudo-variables of the form &&VAR&& are evaluated.
 * 2) Templates and variables in the text are expanded. You can use parser functions like #if: if installed.
 * 3) Keyword tags of the form &TAG& are expanded according to the table below
 * 4) The resulting text is output to the browser. Raw HTML can be included in the skin layout, including normally forbidden tags such as SCRIPT.

Pseudo-variables
DynamicSkin adds a few pseudo-variables to the standard magic words for use in creating conditional output.

Sidebar
MediaWiki:Sidebar defines the standard navigation bar, which provides links to the most important locations in the wiki and supplies site administrators with a place to add a persistent collection of links. For instance, most wikis will link to their community discussion page and some useful tools.

The default Monobook skin places the navigation bar on the top-left (top-right for right-to-left languages) along with the search bar and toolbox, but the placement may be different in other skins. Other skins, like GuMax, move the navigation bar to the top of the page instead of the Discussion/Edit/History tools.


 * If the plain &SIDEBAR& tag is specified, DynamicSkin uses normal MediaWiki sidebar support using MediaWiki syntax.
 * If an alternate sidebar is specified (e.g. &SIDEBAR=MediaWiki:DynamicSkinSidebar&), then the substitute sidebar is generated using DynamicSkin syntax:
 * Uses '=' instead of '|' to separate the link from the target, which greatly simplifies the use of templates and conditionals.
 * DynamicSkin pseudo-variables are available for conditional navigation

Conditional skin content
Here are some examples of how to hide some skin components depending on the environment. You'll need the ParserFunctions extension for the #if: function and StringFunctions extension for the #pos: function to work.

Tags
The DynamicSkin tags generate HTML output according to the sample skin in Manual:Skinning.

Installation
Copy DynamicSkin.php to the /skins directory.

If you want to change the name displayed on the skin preferences page, edit the line near the top of the source:
 * The second parameter, 'DynamicSkin', is the name displayed in the list of skin preferences
 * The third parameter, 'monobook', is the name of the skins/ subdirectory for style sheets and graphics.

Changes to LocalSettings.php
There are no changes to LocalSettings.php. Simply copy DynamicSkin.php to the /skins directory.

If you want to make DynamicSkin the default, add this to LocalSettings.php:. Note that the skin file name must be all in lower case and without the '.php' extension.

Code
element // (instead of returning a mostly unordered string, which is the default behavior). function getCategoryLinks { global $wgOut, $wgUser, $wgTitle, $wgUseCategoryBrowser; global $wgContLang;

if(count($wgOut->mCategoryLinks) == 0) return '';

$skin = $wgUser->getSkin;

# separator $sep = "";

// use Unicode bidi embedding override characters, // to make sure links don't smash each other up in ugly ways $dir = $wgContLang->isRTL ? 'rtl' : 'ltr'; $embed = ""; $pop = ''; $t = $embed. implode ( "{$pop} {$sep} {$embed}", $wgOut->mCategoryLinks ). $pop;

$msg = wfMsgExt('pagecategories', array('parsemag', 'escape'), count($wgOut->mCategoryLinks)); $s = $skin->makeLinkObj(Title::newFromText(wfMsgForContent('pagecategorieslink')), $msg) . $t;

# optional 'dmoz-like' category browser - will be shown under the list # of categories an article belongs to       if($wgUseCategoryBrowser) { $s .= ' ';

# get a big array of the parents tree $parenttree = $wgTitle->getParentCategoryTree; # Skin object passed by reference because it can not be           # accessed under the method subfunction drawCategoryBrowser $tempout = explode("\n", Skin::drawCategoryBrowser($parenttree, $this)); # clean out bogus first entry and sort them unset($tempout[0]); asort($tempout); # output one per line $s .= implode(" \n", $tempout); }       return $s; }

// Load and parse the dynamic sidebar // Similar to default processing in SKin.php function buildSidebar // except it uses the '!' character as a delimiter instead of '|' // which simplifies using conditional templates.

function buildDynamicSidebar( $name ) { // If a name was specified, try to load the sidebar from that page if ( !empty($name) ) $sidebar = $this->loadTemplate( $name );

// Default is standard MediaWiki sidebar if ( empty($name) || empty($sidebar) ) return( $this->data['sidebar'] );

// The navigation sidebar is formatted as a two-level list. // Parsing code is copied from SKin.php function buildSidebar $bar = array; $lines = explode( "\n", $sidebar ); foreach ( $lines as $line ) { if (strpos($line, '*') !== 0)          // Ignore lines that aren't part of a list continue; if (strpos($line, '**') !== 0) {       // Top level lines are start of a new heading $line = trim($line, '* '); $heading = $line; } else { if (strpos($line, '=') !== false) { // sanity check $line = explode( '=', trim($line, '* '), 2 ); $link = wfMsgForContent( $line[0] ); if ($link == '-') continue; if (wfEmptyMsg($line[1], $text = wfMsg($line[1]))) $text = $line[1]; if (wfEmptyMsg($line[0], $link)) $link = $line[0];

if ( preg_match( '/^(?:' . wfUrlProtocols . ')/', $link ) ) { $href = $link; } else { $title = Title::newFromText( $link ); if ( $title ) { $title = $title->fixSpecialName; $href = $title->getLocalURL; } else { $href = 'INVALID-TITLE'; }                   }

$bar[$heading][] = array(                       'text' => $text,                        'href' => $href,                        'id' => 'n-' . strtr($line[1], ' ', '-'),                        'active' => ($text == $this->activeLink )                    ); } else { continue; } }       }

return( $bar ); }

// The DynamicSkin extension can build the tabs (sidebar) dynamically, // and not necessarily from $template->data['sidebar'], so DynamicSkin // and derived skins have a setActiveSidebarLink function that can // be used for the Breadcrumbs and similar extensions. function setActiveSidebarLink( $activeLink ) { $this->activeLink = $activeLink;              // Save it for later }

static function getDefaultSkinText {        return(        /**** Monobook skin (MediaWiki 1.10) ****/           "&DOCTYPE&\n"         . "&HEAD_START& &STYLES& &SCRIPTS&\n"        . "&BODY_START&\n"        . "\n"        . " \n"        . "  \n"        . "   &TITLE&\n"        . "   \n"        . "    &SITESUB& &SUBTITLE& &JUMPLINKS&\n"        . "    &CONTENTS& \n"        . "   \n"        . "  \n"        . " \n"        . " \n"        . "  &PAGETOOLS& &PERSONAL&\n"        . "  &LOGO& &SIDEBAR&\n"        . "  &SEARCH& &TOOLBOX&\n"        . "  &SITENOTICE& &USERMSG& &LANGUAGE& &UNDELETE&\n"        . "  &CATLINKS&\n"        . " \n"        . "  \n"        . "  &FOOTER&\n"        . " \n"        . "&END&\n"     // No &SITENAME&

/**** Default layout according to MediaWiki Manual:Skinning "&DOCTYPE&\n" . "&HEAD_START& &STYLES& &SCRIPTS&\n" . "&BODY_START& &SITENAME& &LOGO& &SITESUB& &SITENOTICE&\n" . "&USERMSG& &PERSONAL& &JUMPLINKS& &SEARCH&\n" . "&SIDEBAR& &TOOLBOX& &LANGUAGE&\n" . "&TITLE& &SUBTITLE& &UNDELETE&\n" . "&CONTENTS&\n" . "&CATLINKS& &PAGETOOLS&\n" . "&FOOTER&\n" . "&END&\n" ****/       );    } } ?>

Implementation
As an example, the &LOGO& tag invokes the following code:

Which generates the following output, where the values in red will be replaced according to the wiki configuration. &lt;div class=&quot;portlet&quot; id=&quot;p-logo&quot;&gt; &lt;a style=&quot;background-image: url(logopath );&quot; href=&quot;navmainpage &quot; maintooltip &gt;&lt;/a&gt; &lt;/div&gt; &lt;script type=&quot;mimetype &quot;&gt; if (window.isMSIE55) fixalpha; &lt;/script&gt;

Derived classes
While DynamicSkin makes it easy to customize skins without any code changes, it's written as a base class so you can add additional code in a derived class. For an example of a derived skin, and the site it's used on, see InfoDabble skin at ehartwell.com.

At a minimum, a derived class must override :
 * MediaWiki builds the list of skin names from the list of .php files in the skins/ subdirectory. For a class to be recognized as a MediaWiki skin, its name must be Skin . For example, if your derived skin is called MySkin, the source file must be skins/MySkin.php and the class name must be SkinMySkin, or the skin won't be recognized.
 * The second parameter, MySkin, is the name displayed in the list of skin preferences
 * The third parameter, MySkin, is the name of the skins/ subdirectory for style sheets and graphics.
 * The fourth parameter, MySkinTemplate, is the name of your overriden template class.

If you're using a derived class and you don't want DynamicSkin to appear on the Preferences page, rename DynamicSkin.php to DynamicSkin.php.dep and change the reference in the derived class.

Tips, tricks, and hoops

 * This version embeds some of the class information ( and  ) in the code and some in the template page. There's a tradeoff between providing flexibility and ensuring that the standard MediaWiki style classes are present for derived skins. The default generates the Monobook skin with code derived from the MediaWiki Skinning sample.
 * Depending on what's dynamic in the skin, you may want to disable caching. Caching is automatically disabled if the &&USERID&& pseudo-variable is used.

Version history

 * 2007.9.06 v1.92: Added hook for Extension:BreadCrumbs2
 * 2007.8.24 v1.91: Added image file parameter to &LOGO& tag (thanks to Patricia Barden for the idea). Also added description for the &CUSTOMTOOLBAR& tag.
 * 2007.8.22 v1.9: Code cleanup, document, first published version