Manual:Skinning

From MediaWiki.org

Jump to: navigation, search

This page contains instructions on how to create a skin for MediaWiki. Needs update for 1.14 1.15!

Note Note: The term FooBar will be used as a placeholder for the newly-created skin's name. (Notice the use of the uppercase FooBar and the lowercase foobar in different contexts.)

Note Note: There is also a more general walkthrough here.

Contents

[edit] File Locations

All skin files are located in the skins subfolder of the MediaWiki installation directory.

There are two files for each skin in that folder:

  • FooBar.php: The main file, defining the page layout.
  • FooBar.deps.php: a workaround for a bug in the APC opcode cache on PHP 5

For all other files, a subfolder with the skin's name should be created - for example:

  • skins/foobar/main.css: The FooBar skin's main style sheet
  • skins/foobar/IE60Fixes.css: browser-specific style sheet fixes

[edit] Code Structure and Elements

This section outlines the code structure of a typical MediaWiki skin.

The respective code portions have been extracted from MediaWiki's default MonoBook skin. For the full PHP code in its entirety, see /Example.

[edit] Metadata

This section contains meta-information about the skin.

/**
 * [FooBar] skin
 *
 * @file
 * @ingroup Skins
 * @version [#].[#].[#]
 * @author [name] ([URL] / [E-Mail])
 * @license [URL] [name]
 */

[edit] Initialization

Here the required PHP classes are defined. Instances of [FooBar] and [foobar] need to be replaced with the skin's name.

// initialize
if( !defined( 'MEDIAWIKI' ) ){
	die( "This skin file is not a valid entry point.\n" );
}
 
#Only needed for older MediaWiki instances
#require_once('includes/SkinTemplate.php');

// inherit main code from SkinTemplate, set the CSS and template filter
class Skin[FooBar] extends SkinTemplate {
	function initPage( OutputPage $out ) {
		parent::initPage( $out );
		$this->skinname  = '[foobar]';
		$this->stylename = '[foobar]';
		$this->template  = '[FooBar]Template';
	}
}
 
class [FooBar]Template extends QuickTemplate {

[edit] Category List Fix

This is a fix for returning category links as a proper UL element (instead of returning a mostly unordered string, which is the default behavior).

	/* hijack category functions to create a proper list */
 
	function getCategories() {
		$catlinks = $this->getCategoryLinks();
		if( !empty( $catlinks ) ) {
			return "<ul id='catlinks'>{$catlinks}</ul>";
		}
	}
 
	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 = "<li dir='$dir'>";
		$pop = '</li>';
		$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 .= '<br /><hr />';
 
			# 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( "<br />\n", $tempout );
		}
 
		return $s;
	}

These functions need to be placed in the function that extends SkinTemplate in your template file. If you want to see the mediawiki versions of these functions, look for them in Skin.php.

[edit] XHTML Output

This is the beginning of the Template filter callback, and usually does not need to be altered.

	/**
	 * Template filter callback for this skin.
	 * Takes an associative array of data set from a SkinTemplate-based
	 * class, and a wrapper for MediaWiki's localization database, and
	 * outputs a formatted page.
	 */
	public function execute() {
		global $wgUser, $wgSitename;
		$skin = $wgUser->getSkin();
 
		// retrieve site name
		$this->set( 'sitename', $wgSitename );
 
		// suppress warnings to prevent notices about missing indexes in $this->data
		wfSuppressWarnings();
 
		/* compose XHTML output */
 
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="<?php $this->text('xhtmldefaultnamespace') ?>" <?php 
	foreach( $this->data['xhtmlnamespaces'] as $tag => $ns ) {
		?>xmlns:<?php echo "{$tag}=\"{$ns}\" ";
	} ?>xml:lang="<?php $this->text('lang') ?>" lang="<?php $this->text('lang') ?>" dir="<?php $this->text('dir') ?>">

[edit] Head

The HTML head, including metadata, style sheets and scripts. This section usually does not require any adjustments, though the style sheets can be altered if desired.

	<head>
		<meta http-equiv="Content-Type" content="<?php $this->text('mimetype') ?>; charset=<?php $this->text('charset') ?>" />
		<?php $this->html('headlinks') ?>
		<title><?php $this->text('pagetitle') ?></title>
		<?php /*** general style sheets ***/ ?>
		<style type="text/css" media="screen,projection">/*<![CDATA[*/
			@import "<?php $this->text('stylepath') ?>/<?php $this->text('stylename') ?>/main.css?<?php echo $GLOBALS['wgStyleVersion'] ?>";
			@import "<?php $this->text('stylepath') ?>/<?php $this->text('stylename') ?>/contents.css?<?php echo $GLOBALS['wgStyleVersion'] ?>";
		/*]]>*/</style>
		<?php /*** media-specific style sheets ***/ ?>
		<link rel="stylesheet" type="text/css" <?php if(empty($this->data['printable']) ) { ?>media="print"<?php } ?> href="<?php $this->text('stylepath') ?>/common/commonPrint.css?<?php echo $GLOBALS['wgStyleVersion'] ?>" />
		<link rel="stylesheet" type="text/css" media="handheld" href="<?php $this->text('stylepath') ?>/<?php $this->text('stylename') ?>/handheld.css?<?php echo $GLOBALS['wgStyleVersion'] ?>" />
		<?php /*** browser-specific style sheets ***/ ?>
		<!--[if lt IE 5.5000]><style type="text/css">@import "<?php $this->text('stylepath') ?>/<?php $this->text('stylename') ?>/IE50Fixes.css?<?php echo $GLOBALS['wgStyleVersion'] ?>";</style><![endif]-->
		<!--[if IE 5.5000]><style type="text/css">@import "<?php $this->text('stylepath') ?>/<?php $this->text('stylename') ?>/IE55Fixes.css?<?php echo $GLOBALS['wgStyleVersion'] ?>";</style><![endif]-->
		<!--[if IE 6]><style type="text/css">@import "<?php $this->text('stylepath') ?>/<?php $this->text('stylename') ?>/IE60Fixes.css?<?php echo $GLOBALS['wgStyleVersion'] ?>";</style><![endif]-->
		<!--[if IE 7]><style type="text/css">@import "<?php $this->text('stylepath') ?>/<?php $this->text('stylename') ?>/IE70Fixes.css?<?php echo $GLOBALS['wgStyleVersion'] ?>";</style><![endif]-->
		<?php /*** general IE fixes ***/ ?>
		<!--[if lt IE 7]>
		<script type="<?php $this->text('jsmimetype') ?>" src="<?php $this->text('stylepath') ?>/common/IEFixes.js?<?php echo $GLOBALS['wgStyleVersion'] ?>"></script>
		<meta http-equiv="imagetoolbar" content="no" />
		<![endif]-->
 
		<?php print Skin::makeGlobalVariablesScript($this->data); ?>
 
		<?php /*** various MediaWiki-related scripts and styles ***/ ?>
		<script type="<?php $this->text('jsmimetype') ?>" src="<?php $this->text('stylepath') ?>/common/wikibits.js?<?php echo $GLOBALS['wgStyleVersion'] ?>"><!-- wikibits js --></script>
<?php	if($this->data['jsvarurl']) { ?>
		<script type="<?php $this->text('jsmimetype') ?>" src="<?php $this->text('jsvarurl') ?>"><!-- site js --></script>
<?php	} ?>
<?php	if($this->data['pagecss']) { ?>
		<style type="text/css"><?php $this->html('pagecss') ?></style>
<?php	}
		if($this->data['usercss']) { ?>
		<style type="text/css"><?php $this->html('usercss') ?></style>
<?php	}
		if($this->data['userjs']) { ?>
		<script type="<?php $this->text('jsmimetype') ?>" src="<?php $this->text('userjs' ) ?>"></script>
<?php	}
		if($this->data['userjsprev']) { ?>
		<script type="<?php $this->text('jsmimetype') ?>"><?php $this->html('userjsprev') ?></script>
<?php	}
		if($this->data['trackbackhtml']) print $this->data['trackbackhtml']; ?>
		<!-- Head Scripts -->
		<?php $this->html('headscripts') ?>
	</head>

[edit] Body (Page Elements)

The HTML body, composing the basic page structure.

The initial BODY tag's attributes usually do not require any adjustments.

<!-- Body -->
<body<?php if( $this->data['body_ondblclick'] ) { ?> ondblclick="<?php $this->text('body_ondblclick') ?>"<?php } ?>
<?php if( $this->data['body_onload'] ) { ?> onload="<?php $this->text('body_onload') ?>"<?php } ?>
 class="mediawiki <?php $this->text('dir') ?> <?php $this->text('pageclass') ?> <?php $this->text('skinnameclass') ?>">

[edit] Site Name

<?php echo $this->text('sitename') ?>

[edit] Logo Image

	<div class="portlet" id="p-logo">
		<a style="background-image: url(<?php $this->text('logopath') ?>);" <?php
			?>href="<?php echo htmlspecialchars($this->data['nav_urls']['mainpage']['href'])?>"<?php
			echo $skin->tooltipAndAccesskey('n-mainpage') ?>></a>
	</div>
	<script type="<?php $this->text('jsmimetype') ?>"> if (window.isMSIE55) fixalpha(); </script> <!-- IE alpha-transparency fix -->

[edit] Tagline (Site Subtitle)

<h3 id="siteSub"><?php $this->msg('tagline') ?></h3>

[edit] Site Notice

[conditional]

<?php if( $this->data['sitenotice'] ) { ?><div id="siteNotice"><?php $this->html('sitenotice') ?></div><?php } ?>

[edit] User-Messages Notification

[conditional]

<?php if( $this->data['newtalk'] ) { ?><div class="usermessage"><?php $this->html('newtalk') ?></div><?php } ?>

[edit] User Toolbar

[iterative]

	<div class="portlet" id="p-personal">
		<h5><?php $this->msg('personaltools') ?></h5> <!-- User Toolbar Label/Caption [optional] -->
		<div class="pBody">
			<ul>
<?php 			foreach( $this->data['personal_urls'] as $key => $item ) { ?>
				<li id="pt-<?php echo Sanitizer::escapeId( $key ) ?>"<?php
					if ($item['active']) { ?> class="active"<?php } ?>><a href="<?php
				echo htmlspecialchars( $item['href'] ) ?>"<?php echo $skin->tooltipAndAccesskey('pt-'.$key) ?><?php
				if( !empty( $item['class'] ) ) { ?> class="<?php
				echo htmlspecialchars( $item['class'] ) ?>"<?php } ?>><?php
				echo htmlspecialchars( $item['text'] ) ?></a></li>
<?php			} ?>
			</ul>
		</div>
	</div>

[edit] Jump-To Links

(intra-page navigation) [conditional], [optional]

<?php if( $this->data['showjumplinks'] ) { ?><div id="jump-to-nav"><?php $this->msg('jumpto') ?> <a href="#column-one"><?php $this->msg('jumptonavigation') ?></a>, <a href="#searchInput"><?php $this->msg('jumptosearch') ?></a></div><?php } ?>
 
<!-- Search Box -->
	<div id="p-search" class="portlet">
		<h5><label for="searchInput"><?php $this->msg('search') ?></label></h5>
		<div id="searchBody" class="pBody">
			<form action="<?php $this->text('searchaction') ?>" id="searchform"><div>
				<input id="searchInput" name="search" type="text"<?php echo $skin->tooltipAndAccesskey('search');
					if( isset( $this->data['search'] ) ) {
						?> value="<?php $this->text('search') ?>"<?php } ?> />
				<input type='submit' name="go" class="searchButton" id="searchGoButton"	value="<?php $this->msg('searcharticle') ?>" />&nbsp;
				<input type='submit' name="fulltext" class="searchButton" id="mw-searchButton" value="<?php $this->msg('searchbutton') ?>" />
			</div></form>
		</div>
	</div>

[edit] Sidebar Navigation

[iterative]

	<?php foreach( $this->data['sidebar'] as $bar => $cont ) { ?>
	<div class='portlet' id='p-<?php echo Sanitizer::escapeId( $bar ) ?>'<?php echo $skin->tooltip('p-'.$bar) ?>>
		<h5><?php $out = wfMsg( $bar ); if( wfEmptyMsg( $bar, $out ) ) echo $bar; else echo $out; ?></h5>
		<div class='pBody'>
			<ul>
<?php 			foreach( $cont as $key => $val ) { ?>
				<li id="<?php echo Sanitizer::escapeId( $val['id'] ) ?>"<?php
					if( $val['active'] ) { ?> class="active" <?php }
				?>><a href="<?php echo htmlspecialchars( $val['href'] ) ?>"<?php echo $skin->tooltipAndAccesskey($val['id']) ?>><?php echo htmlspecialchars( $val['text'] ) ?></a></li>
<?php			} ?>
			</ul>
		</div>
	</div>
	<?php } ?>

[edit] Search

	<div id="p-search" class="portlet">
		<h5><label for="searchInput"><?php $this->msg('search') ?></label></h5>
		<div id="searchBody" class="pBody">
			<form action="<?php $this->text('searchaction') ?>" id="searchform"><div>
				<input id="searchInput" name="search" type="text"<?php echo $skin->tooltipAndAccesskey('search');
					if( isset( $this->data['search'] ) ) {
						?> value="<?php $this->text('search') ?>"<?php } ?> />
				<input type='submit' name="go" class="searchButton" id="searchGoButton"	value="<?php $this->msg('searcharticle') ?>"<?php echo $skin->tooltipAndAccesskey( 'search-go' ); ?> />&nbsp;
				<input type='submit' name="fulltext" class="searchButton" id="mw-searchButton" value="<?php $this->msg('searchbutton') ?>"<?php echo $skin->tooltipAndAccesskey( 'search-fulltext' ); ?> />
			</div></form>
		</div>
	</div>

[edit] Toolbox

	<div class="portlet" id="p-tb">
		<h5><?php $this->msg('toolbox') ?></h5>
		<div class="pBody">
			<ul>
<?php
		if( $this->data['notspecialpage'] ) { ?>
				<li id="t-whatlinkshere"><a href="<?php
				echo htmlspecialchars($this->data['nav_urls']['whatlinkshere']['href'])
				?>"<?php echo $skin->tooltipAndAccesskey('t-whatlinkshere') ?>><?php $this->msg('whatlinkshere') ?></a></li>
<?php
			if( $this->data['nav_urls']['recentchangeslinked'] ) { ?>
				<li id="t-recentchangeslinked"><a href="<?php
				echo htmlspecialchars($this->data['nav_urls']['recentchangeslinked']['href'])
				?>"<?php echo $skin->tooltipAndAccesskey('t-recentchangeslinked') ?>><?php $this->msg('recentchangeslinked') ?></a></li>
<?php 		}
		}
		if( isset( $this->data['nav_urls']['trackbacklink'] ) ) { ?>
			<li id="t-trackbacklink"><a href="<?php
				echo htmlspecialchars($this->data['nav_urls']['trackbacklink']['href'])
				?>"<?php echo $skin->tooltipAndAccesskey('t-trackbacklink') ?>><?php $this->msg('trackbacklink') ?></a></li>
<?php 	}
		if( $this->data['feeds'] ) { ?>
			<li id="feedlinks"><?php foreach($this->data['feeds'] as $key => $feed) {
					?><span id="feed-<?php echo Sanitizer::escapeId($key) ?>"><a href="<?php
					echo htmlspecialchars($feed['href']) ?>"<?php echo $skin->tooltipAndAccesskey('feed-'.$key) ?>><?php echo htmlspecialchars($feed['text'])?></a>&nbsp;</span>
					<?php } ?></li><?php
		}
 
		foreach( array( 'contributions', 'blockip', 'emailuser', 'upload', 'specialpages' ) as $special ) {
 
			if( $this->data['nav_urls'][$special] ) {
				?><li id="t-<?php echo $special ?>"><a href="<?php echo htmlspecialchars($this->data['nav_urls'][$special]['href'])
				?>"<?php echo $skin->tooltipAndAccesskey('t-'.$special) ?>><?php $this->msg($special) ?></a></li>
<?php		}
		}
 
		if( !empty( $this->data['nav_urls']['print']['href'] ) ) { ?>
				<li id="t-print"><a href="<?php echo htmlspecialchars($this->data['nav_urls']['print']['href'])
				?>"<?php echo $skin->tooltipAndAccesskey('t-print') ?>><?php $this->msg('printableversion') ?></a></li><?php
		}
 
		if( !empty( $this->data['nav_urls']['permalink']['href'] ) ) { ?>
				<li id="t-permalink"><a href="<?php echo htmlspecialchars($this->data['nav_urls']['permalink']['href'])
				?>"<?php echo $skin->tooltipAndAccesskey('t-permalink') ?>><?php $this->msg('permalink') ?></a></li><?php
		} elseif( $this->data['nav_urls']['permalink']['href'] === '' ) { ?>
				<li id="t-ispermalink"<?php echo $skin->tooltip('t-ispermalink') ?>><?php $this->msg('permalink') ?></li><?php
		}
 
		wfRunHooks( 'MonoBookTemplateToolboxEnd', array( &$this ) );
?>
			</ul>
		</div>
	</div>

[edit] Interlanguage Links

<?php if( $this->data['language_urls'] ) { ?>
	<div id="p-lang" class="portlet">
		<h5><?php $this->msg('otherlanguages') ?></h5>
		<div class="pBody">
			<ul>
<?php		foreach( $this->data['language_urls'] as $langlink ) { ?>
				<li class="<?php echo htmlspecialchars($langlink['class'])?>"><?php
				?><a href="<?php echo htmlspecialchars($langlink['href']) ?>"><?php echo $langlink['text'] ?></a></li>
<?php		} ?>
			</ul>
		</div>
	</div>
<?php } ?>

[edit] Page Name

<h1 class="firstHeading"><?php $this->data['displaytitle'] != '' ? $this->html('title') : $this->text('title') ?></h1>

[edit] Page Subtitle

(e.g. redirect notice)

<div id="contentSub"><?php $this->html('subtitle') ?></div>

[edit] Undelete Notice

[conditional]

<?php if( $this->data['undelete'] ) { ?><div id="contentSub2"><?php $this->html('undelete') ?></div><?php } ?>

[edit] Page Contents

<?php $this->html('bodytext') ?>

[edit] Category Links

[conditional]

<?php if( $this->data['catlinks'] ) { ?><div id="catlinks"><?php $this->html('catlinks') ?></div><?php } ?>

[edit] Page Toolbar

[iterative]

	<div id="p-cactions" class="portlet">
		<h5><?php $this->msg('views') ?></h5> <!-- Page Toolbar Label/Caption [optional] -->
		<div class="pBody">
			<ul>
	<?php			foreach( $this->data['content_actions'] as $key => $tab ) { ?>
				 <li id="ca-<?php echo Sanitizer::escapeId( $key ) ?>"<?php
					 	if( $tab['class'] ) { ?> class="<?php echo htmlspecialchars($tab['class']) ?>"<?php }
					 ?>><a href="<?php echo htmlspecialchars($tab['href']) ?>"<?php echo $skin->tooltipAndAccesskey('ca-'.$key) ?>><?php
					 echo htmlspecialchars($tab['text']) ?></a></li>
	<?php			 } ?>
			</ul>
		</div>
	</div>

[edit] Footer

[iterative]

			<div id="footer">
<?php
		if( $this->data['poweredbyico'] ) { ?>
				<div id="f-poweredbyico"><?php $this->html('poweredbyico') ?></div>
<?php 	}
		if( $this->data['copyrightico'] ) { ?>
				<div id="f-copyrightico"><?php $this->html('copyrightico') ?></div>
<?php	}
 
		// generate additional footer links
?>
			<ul id="f-list">
<?php
		$footerlinks = array(
			'lastmod', 'viewcount', 'numberofwatchingusers', 'credits', 'copyright',
			'privacy', 'about', 'disclaimer', 'tagline',
		);
		foreach( $footerlinks as $aLink ) {
			if( isset( $this->data[$aLink] ) && $this->data[$aLink] ) {
?>				<li id="<?php echo $aLink ?>"><?php $this->html( $aLink ) ?></li>
<?php 		}
		}
?>
			</ul>
		</div>

[edit] Closing Trail

This last section simply closes any sections from above and usually does not need to be altered.

<!-- scripts and debugging information -->
<?php $this->html('bottomscripts'); /* JS call to runBodyOnloadHook */ ?>
<?php $this->html('reporttime') ?>
<?php if ( $this->data['debug'] ): ?>
<!-- Debug output:
<?php $this->text( 'debug' ); ?>
 
-->
<?php endif; ?>
</body>
 
</html>
<?php
	wfRestoreWarnings();
	} // end of execute() method
} // end of class

[edit] See also

[edit] External