The below is old, and probably only works with old versions of MediaWiki. There is now a community-supported method for doing this.

I just hacked out my first piece of custom MediaWiki interface: adding custom tabs next to the standard “Article,” “Edit,” “Discussion,” etc. tabs. I was surprised that there was no existing documentation on how to do this. So I decided to write my own for the benefit of other amatuers like me. Here’s how you can do it:

1) Before starting, you have to be working from a skin that is built on SkinTemplate.php. If you aren’t sure, then you probably are. (I’m sure there are ways to add custom tabs to other skins, but I don’t know how.)

2) Create a new custom namespace. This is what will come before the colon in the new page’s address, eg “yourdomain.com/wiki/NewNamespace:Pagename”. (NOTE, 5/15/2008: The instructions for this step are based on an old version of MediaWiki (1.7). Rich points out in Comments that for versions back to at least 1.12, you should follow the official instructions here.)

To do this, open the LocalSettings.php file in your wiki’s directory. Around Line 150 or so, you’ll see some code like this:

$wgNamespacesWithSubpages = array(
NS_TALK           => true,
NS_USER           => true,

Right above that, add the line

$wgExtraNamespaces = array(100 => "New_namespace");

replacing New_namespace with your own custom name. If you want to add more, just make more lines like this one; the next custom namespaces would be 101, 102, etc. Now save LocalSettings.php and close it.

3) Go to the article MediaWiki:Monobook.js in your wiki. If you’re using version 1.9 or older, go to Common.js instead. Edit the file and append this to the end of it:

ta['namespace_prompt'] = new Array('c','New Custom Tab Label');

namespace_prompt can be whatever you want. ‘New Custom Tab Label’ should be whatever text you want to show up in your new tab. This line of code tells MediaWiki that whenever it sees “namespace_prompt” it should replace it with whatever’s in the array. Save that article. Let’s move on.

4) One more step. Open up the SkinTemplate.php file in the includes directory. Go down to Line 600. This is where the tabs are coded:

$nskey = $this->mTitle->getNamespaceKey();
//This is the Article tab...
$content_actions[$nskey] = $this->tabAction(
$subjpage,
$nskey,
!$this->mTitle->isTalkPage() && !$prevent_active_tabs,'', true);
//...and this is the Discussion tab
$content_actions['talk'] = $this->tabAction(
$talkpage,
'talk',
$this->mTitle->isTalkPage() && !$prevent_active_tabs,'',true);

You’re going to add your own bit of code for your new tab somewhere in here, depending on where you want your tab to show up: far right, far left, middle, whatever. Here’s the code:

//this first line means that the new tab should only appear when you're in the main,
//discussion or new custom namespace
if ( $ns == NS_MAIN || $ns == NS_TALK || $ns == 100)  {
$content_actions['namespace_prompt'] = $this->tabAction( Title::makeTitle
( 100, $this->mTitle->getText() ),
'namespace_prompt', $this->mTitle->getNamespace() == 100 &&
!$prevent_active_tabs,'', true);
}

Remember to replace namespace_prompt with whatever you put into Monobook.js (or Common.js). Note that you can’t refer to your new namespace by name like you can the existing namespaces. This caused me a few hours of trouble.

You’re done. Go to your site and check to see that it works.

Notice anything? When you click the new label, the article tab on the new page is screwed up. Here’s how you correct it. Go to the code that builds the article tab:

$content_actions[$nskey] = $this->tabAction(
$subjpage,
$nskey,
!$this->mTitle->isTalkPage() && !$prevent_active_tabs,'', true);

and replace it with this:

//if you are in the new namespace...
if ( $this->mTitle->getNamespace() == 100 ) {
//then build a link to the main namespace
$content_actions['nstab-main'] = $this->tabAction( Title::makeTitle(
NS_MAIN, $this->mTitle->getText() ),'nstab-main',!$this->mTitle->isTalkPage()
&& !$prevent_active_tabs,'', true);
}

else {
$content_actions[$nskey] = $this->tabAction(
$subjpage,
$nskey,
!$this->mTitle->isTalkPage() && !$prevent_active_tabs,'', true);
}

Phew. Why so hard? Hopefully they’ll make this easier in future releases. Thanks go to Vyznev at freenet’s #mediawiki channel.