Manual:Special pages

Special pages are pages that are created by the software on demand to perform a specific function. For example, a special page might show all pages that have one or more links to an external site or it might create a form providing user submitted feedback. Special pages are located in their own namespace (Special:) and are not editable directly like other pages. Developers can also create new special pages. These pages can be user-accessible and will generally show up in the list of all special pages at Special:SpecialPages. Some special pages are only accessible to users with certain permissions and accesses. Other special pages don't show up on the special page list at all and are only used by the wiki internally.

All of the ~75 built-in special pages that come with MediaWiki are called SpecialSomename.php and are located in the includes/specials directory. Special pages created by third party developers are generally stored in the extensions directory in their own file or as part of a larger extension. All special pages inherit from a class called SpecialPage which is defined in includes/SpecialPage.php. When a new special page is created, the user rights needed to access the page can be defined. These rights specify, among other things, whether the page will show up on Special:SpecialPages and whether the page is includable in other pages.

Special pages also have unique names that can be customized on a wiki. The general form is "Special:Pagename" where both "Special" and "Pagename" are customizable. The Special pseudo namespace can be translated in other languages. This translated namespace can be produced with the wikitext {{ns:special}}, on this wiki giving "Special". The name of the special page can also be redefined in a system message, for the site language, with the generic name of the special page as the ID.

There are various ways to make special pages, but the one below is used by the bulk of official extensions, and adherence to this style is recommended. Also, be sure to include a credits block in the new special page for 'specialpage'. See $wgExtensionCredits for more details.

Most special page extensions require three files: a small setup file, which will be loaded every time MediaWiki starts, a localisation file, and a file with the bulk of the code. All of them should be placed in a new directory inside the MediaWiki extensions/ directory. MediaWiki coding conventions define the four files like this:

Generally the special page should be named after the extension: so the Gadgets extension has the file Gadgets.php and SpecialGadgets.php. Obviously if your extension has more than one special page, you'll need more names.

In the example below, <SpecialPageName> is MyExtension. A working copy is available for download, see further down.

After creating the files listed below, add the following line to LocalSettings.php

The setup file (in this example, named MyExtension.php) looks like this:

<?php# Alert the user that this is not a valid access point to MediaWiki if they try to access the special pages file directly.
if(!defined('MEDIAWIKI')){echo<<<EOT
To install my extension, put the following line in LocalSettings.php:
require_once( "\$IP/extensions/MyExtension/MyExtension.php" );
EOT;exit(1);}$wgExtensionCredits['specialpage'][]=array('path'=>__FILE__,'name'=>'MyExtension','author'=>'My name','url'=>'https://www.mediawiki.org/wiki/Extension:MyExtension','descriptionmsg'=>'myextension-desc','version'=>'0.0.0',);$wgAutoloadClasses['SpecialMyExtension']= __DIR__ .'/SpecialMyExtension.php';# Location of the SpecialMyExtension class (Tell MediaWiki to load this file)
$wgMessagesDirs['MyExtension']= __DIR__ ."/i18n";# Location of localisation files (Tell MediaWiki to load them)
$wgExtensionMessagesFiles['MyExtensionAlias']= __DIR__ .'/MyExtension.alias.php';# Location of an aliases file (Tell MediaWiki to load it)
$wgSpecialPages['MyExtension']='SpecialMyExtension';# Tell MediaWiki about the new special page and its class name

The body file (in this example, named SpecialMyExtension.php) should contain a subclass of SpecialPage (or one of its subclasses). It will be loaded automatically when the special page is requested. This example implements the subclass SpecialMyExtension:

You need the __construct() constructor because its first parameter names your special page.

execute() is the main function that is called when a special page is accessed. The function overloads the function SpecialPage::execute(). It passes a single parameter $par, the subpage component of the current title. For example, if someone follows a link to Special:MyExtension/blah, $par will contain "blah".

Wikitext and HTML output should normally be run via $wgOut — do not use 'print' or 'echo' directly when working within the wiki's user interface.

It's specified through a message. The structure of the message is a key-value pair. The key, 'myextension', must be in all lowercase.

A good example of a localisation file would be, in i18n/en.json:

{"@metadata":{"authors":["<your username>"]},"myextension":"My Extension","myextension-desc":"My Extension does fancy stuff","myextension-summary":"On this special page, do this simple thing and earn wonders",}

{"@metadata":{"authors":["<your username>"]},"myextension":"The name of the extension's entry in Special:SpecialPages","myextension-desc":"{{desc}}","myextension-summary":"Description appearing on top of [[Special:MyExtension]].",}

Note that IDs should not start with an uppercase letter, and that a space in the ID should be written in the code as an underscore.

The -summary message is optional. It's created automatically by the parent class and shown on top of the special page, usually for a concise description of what the user can do on it. If you don't define its content, it will only be used when wiki administrators customize it on the wiki for their own purposes.

We must also internationalize the name of the special page by creating aliases for it (here, in a file called "MyExtension.alias.php"). In this example, the special page MyExtension registers an alias so the page becomes accessible via .../Special:My Extension e.g. .../Spezial:Meine_Erweiterung in German, and so on.

Again, a space in the ID should be written in the code as an underscore. For the page header and linking, the usual rules for page names apply. If $wgCapitalLinks is true, a lowercase letter is converted to uppercase, and an underscore is displayed as a space. For example: instead of the above, we could have used 'my_extension' => 'My extension', assuming we consistently identified the extension as my_extension elsewhere. Note that, in the associative array for the English language, the string that identifies our SpecialPage (MyExtension in the example,) is also a valid title.

Also note: The first element of $specialPageAliases['en']['MyExtension']must be the same as the key ('MyExtension')! Otherwise the page will not be listed by Special:Specialpages.

You can set which group your special page is categorized under on Special:SpecialPages by overriding SpecialPage::getGroupName() in your subclass.

/**
* Override the parent to set where the special page appears on Special:SpecialPages
* 'other' is the default, so you do not need to override if that's what you want.
* Specify 'media' to use the <code>specialpages-group-media</code> system interface
* message, which translates to 'Media reports and uploads' in English;
*
* @return string
*/function getGroupName(){return'media';}

Some common values are 'login', 'maintenance', 'media', 'other', 'pagetools', 'redirects', 'users'. You can see the accepted values at Special:AllMessages (search for specialpages-group) or browse the wiki using the pseudo language 'qqx' (ie. go to Special:SpecialPages?uselang=qqx) and look at the headings. Specify the word 'media' to use the interface message 'specialpages-group-media'

If your special page doesn't seem to fit into any of the preconfigured headings, you can add a new heading by adding it to your localisation file (see The localisation file)

The standard page groups that come with MediaWiki are listed in the localisation file (for example, the English messages are in languages/i18n/en.json) and begin with specialpages-group-. If your special page is to be categorized under, for example, users, then the message looked for is specialpages-group-users. The value for this key is the text that appears as the name of that category, for example, Users and rights.

If your special page does not seem to fit under any of the existing categories, you can always make a new one. In your extension's localisation file simply insert a new key for the messages array. In this example, we will define the gamification group:

You can overload the constructor to initialize your own data, but the main reason you would want to do it is to change the behavior of the SpecialPage class itself. When you call the base class constructor from your child class, the following parameters are available:

Some special pages can be included from within another page. For example, if you add {{Special:RecentChanges}} to the wikitext of a page, it will insert a listing of recent changes within the existing content of the page.

Including a special page from another web page is only possible if you declared the page to be includable in the constructor. You can do this by adding the following in the __construct() method after the parent class initialization:

$this->mIncludable=true;

You can also define your special page class as extending the IncludableSpecialPage class.

The SpecialPage->including() function returns a boolean value telling you what context the special page is being called from: false if it is a separate web page, and true if it is being included from within another web page. Usually you will want to strip down the presentation somewhat if the page is being included.

This is the function which your child class should overload. It passes a single parameter, usually referred to cryptically as $par (short for $parameter, as it is the parameter the users can feed to your special page). This parameter is the subpage component of the current title. For example, if someone follows a link to Special:MyExtension/blah, $par will contain "blah".

OutputPage.php contains the class definition for objects of type OutputPage). You can get an object of this class from your SpecialPage using

$output=$this->getOutput();

The variablename $output is, of course, arbitrary. Whatever you call it, this is the variable you will use the most, because it is the way to send output to the browser (no, you don't use echo or print). If you want to use it somewhere, declare the variable global:

function randomFunction(){$output=$this->getOutput();$output->addHTML('<b>This is not a pipe...</b>');}

If you want to, you can create multiple OutputPage objects in different methods in your SpecialPage extension. They will add to the output in the order they are executed.

You can inspect the OutputPage class by viewing includes/OutputPage.php (indeed, all of these can be inspected), but there are a few methods you should definitely know about.

Essentially the quick and dirty substitute for echo. It takes your input and adds it to the buffer: no questions asked. In the below action, if $action contains user-data, it could easily have XSS, evil stuff, or the spawn of Satan injected in. You're better off using escaping (such as with the php function htmlentities) or the XML builders class to build trusted output.

Will output three lists with one item each, which probably wasn't intended.

Warning:

If your special page is intended to be included in other pages, you should probably not use addWikiText() (or any other function that calls the parser except for message related functions that parse (wfMessage) which are OK to call on modern versions of MediaWiki). Due to a bug in MediaWiki (Bug 16129), an included special page will mess up any inclusion before it on the same including page, showing strings like UNIQ10842e596cbb71da.

Note however, if you just want to insert a system message and have it treated like parsed wikitext, you can use code like $this->getOutput()->addHtml( $this->msg( 'key-of-message' )->parse() ). This will not have the issue with nested parser calls mentioned above.

# Assuming this is inside the same class as the rest of your special page code
function sandboxParse($wikiText){global$wgTitle,$wgUser;$myParser=new Parser();$myParserOptions= ParserOptions::newFromUser($wgUser);$result=$myParser->parse($wikiText,$wgTitle,$myParserOptions);return$result->getText();}

I tried the above, and found that the same problem now applied to any <tag>s in the transcluded text. This won't be a problem for a lot of extensions, but the extension I was writing was intended to show wikitext from another page as part of its functionality, so this was a problem.

The process for parsing a page which transcludes a special page seems to be this:

Replace {{Special:MyExtension}} with a UNIQ-QINU marker (because SpecialPage output is expected to be ready-to-output HTML)

Replace any <tag>s with QINU markers as above

Parse everything else from wikitext to HTML

Replace all QINU markers with their respective stored values, in a single pass

The process for parsing a page which transcludes a non-special page, though, is apparently like this:

Replace all QINU markers with their respective stored values, in a single pass

The problem is apparently that in the earlier case, the parsing of the SpecialPage's wiki text is lacking the final QINU decoding step (why?), so all the QINU markers are left undecoded. (This may be a leftover from using the same syntax to invoke transclusion of a wikitext page, which is just pasted straight into the host page's wikitext contents and parsed, as is used to invoke transclusion of a SpecialPage, which must not be parsed at all. Wherever the code is that decides "wait, this is a special page -- replace it with a QINU", it should be doing the extra unstripGeneral before doing the QINU substitution.)

So I just did the following -- after this line:

$htOut=$wgParser->recursiveTagParse($iText);

...I added these lines (the second one is only because the function definition for the first one recommends it):

Since I have now documented this, of course, I will now find a tragic flaw with it and feel really stupid... but as long as it seems to be working, I had to note it here. (It is also important to note the problem with work-around #1.) Also, I have only tested this with MediaWiki 1.10.1. The problem still exists under MW 1.14, but this solution may or may not work. --Woozle 18:26, 9 April 2009 (UTC)

The WebRequest class is used to obtain information from the GET and POST arrays. Using this is recommended over directly accessing the superglobals, since the object does fun stuff like magic_quotes cleaning. The WebRequest object is accessible from extensions by using the RequestContext.

MediaWiki has a load of convenience functions and wrappers for interacting with the database. It also has an interesting load balancing scheme in place. It's recommended you use these wrappers. Check out Database.php for a complete listing of all the convenience functions, because these docs will only tell you about the non-obvious caveats. See Manual:Database access.

As this name suggests, this function gets you a reference of the database. There is no global that contains a database object.

When you call the function, you should pass it a parameter, the constant DB_MASTER or DB_SLAVE. Generally, you interact with the slave database when you're only performing read operations, and interact with the master when you're writing to the database. It's real easy to do, so do it, even if you only have one database.

Title represents the name of a page in the wiki. This is useful because MediaWiki does all sorts of fun escaping and special case logic to page names, so instead of rolling your own convert title to URL function, you create a Title object with your page name, and then use escapeLocalURL() to get a URL to that page.

To get a title object for your special page from outside of the special page class, you can use SpecialPage::getTitleFor( 'YourCanonicalSpecialPageName' ). It will give you a localised title in the wiki's language.

There are various ways to provide your own special pages not bundled within MediaWiki:

One method is to install an extension that generates a form to create or edit an article. A list of extensions currently available, can be found at Category:Special page extensions.

You can also write an extension which provides your own special page. Writing your own extension requires PHP coding skill and comfort with object oriented design and databases also is helpful. You will also need to know how to use code to create and edit MediaWiki articles. For more information, please see this discussion.

You can also display a custom page through Javascript, in place of the default error message "Unknown special page". In MediaWiki:Common.js, check for wgPageName, then hide the MediaWiki-generated content (just appendCSS {visibility:hidden;} ), and inject custom HTML (innerHTML) into the document.getElementById('bodyContent') or document.getElementById('mw_contentholder'). For an example, see w:en:User:Splarka/electrocute.js.

MediaWiki does not set the title of the extension, which is the developer's job. It will look for the name of the extension when Special:Specialpages is called or the special page is loaded (specifically right before the registered wfSpecial*() function is called). In the function execute( $par ) section, use $wgOut to title the extension like: $wgOut->setPageTitle("your title");

The place where the extension can be found (as specified by what is passed into the SpecialPage constructor) is the key--except that it is not capitalized because of getDescription(), the internally used function that finds out the title (or, what they call description) of the special page, strtolower the name. "ThisIsACoolSpecialPage"'s key would be "thisisacoolspecialpage."

Theoretically, getDescription can be overloaded in order to avoid interacting with the message cache but, as the source code states: "Derived classes can override this, but usually it is easier to keep the default behavior. Messages can be added at run-time--see MessageCache.php". Furthermore, this prevents the MediaWiki namespace from overloading the message, as below.

So you've just installed a shiny new MediaWiki extension and realize: "Oh no, my wiki is in French, but the page is showing up as English!" Most people wouldn't care, but it's actually a quite simple task to fix (as long as the developer used the method explained on this page). No noodling around in source code. Let's say the name of the page is DirtyPages and the name comes out to "List of Dirty Pages" but you want it to be (and excuse my poor French) "Liste de Pages Sales". Well, it's as simple as this:

This is also useful for customizing the title for your wiki within your language: for instance, the developer called it "List of Dirty Pages" but you don't like that name, so you rename it "List of Pages needing Cleanup". Check out Special:Allmessages to learn more.

Also, if your extension has a large block of text that does change, like a warning, don't directly output the text. Instead, add it to the message cache and when the time comes to output the text in your code, do this:

Sometimes you may want to limit the visibility of your SpecialPage by removing it from the Special:SpecialPages page and making it visible to only a set group of users. You can do this in the constructor by passing in a restriction parameter; e.g., “editinterface”, a right only assigned to sysops by default; see the User rights manual for other available user rights.

Caution: Even if you restrict your page in the constructor, as mentioned above, your SpecialPage will still be viewable directly via the url, e.g., Special:MySpecialPage. In order to limit access to your SpecialPage via the url, make sure the following is in your extension’s execute method.

In LocalSettings.php you can use the SpecialPage_initList hook to unset unwanted built-in special pages. See "making a few SpecialPages restricted" if you need conditional unsetting of special pages for example for certain user groups. The general message "You have requested an invalid special page." is shown if users try to access such unset special pages.