
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 ownnamespace (Special:) and are not editable directly like other pages.Developers can also create new special pages.
These pages by default are user-accessible and will generally show up in the list of all special pages atSpecial: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 built-in special pages that come with MediaWiki are calledSpecialSomename.php and are located in theincludes/specials directory.Core special pages must be registered in the core list located inincludes/specialpage/SpecialPageFactory.php in order to be loaded by MediaWiki.Special pages created by third party developers are generally stored in theextensions directory in their own file or as part of a larger extension. All special pages inherit from a class calledSpecialPage which is defined inincludes/specialpage/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 onSpecial: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 pseudonamespace can be translated in other languages. This translated namespace can be produced with the wikitext{{ns:special}}, on this wiki givingSpecial. Thename 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.
A special page may or may not allow input. For example,Special:Export allows a user to define a specific page to export by callingSpecial:Export/Sun. If the special page allows complex input, additional parameters will be sent to thequery string component of the URL for processing, e.g.https://www.mediawiki.org/w/index.php?title=Special:Recentchanges&days=3&limit=250.
$wgExtensionCredits for more details.$wgOut->allowClickjacking();| MediaWiki 1.25 introduced anew way to load an extension. For older unsupported versions of MediaWiki, see anolder revision. |
Special pages for extensions require at least four files, defined as follows:
MyExtension/extension.json – The setup file, which loads every time MediaWiki starts.MyExtension/includes/SpecialMyExtension.php – The PHP class file with the bulk of the code.i18n/en.json andi18n/qqq.json – Thelocalisation files.Place all of the files in a new directory inside your MediaWikiextensions/ directory.
You should name the special page file after the extension. For example,Extension:Gadgets contains the fileSpecialGadgets.php.If your extension uses more than one special page, you'll need more names.
In the example below, the special page's name isMyExtension.
After creating the files listed below, adding the following line toLocalSettings.php enables the extension:
wfLoadExtension('MyExtension');
Example setup file forMyExtension/extension.json:
{"name":"MyExtension","version":"0.1.0","author":["Your Name"],"url":"https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:MyExtension","descriptionmsg":"myextension-desc","license-name":"MIT","type":"other","AutoloadNamespaces":{"MediaWiki\\Extension\\MyExtension\\":"src/"},"SpecialPages":{"MyExtension":"MediaWiki\\Extension\\MyExtension\\SpecialMyExtension"},"MessagesDirs":{"MyExtension":["i18n"]},"manifest_version":2,"requires":{"MediaWiki":">= 1.44.0","platform":{"php":">= 8.2"}}}
This file registers several important and mandatory things:
MediaWiki\Extension\MyExtension\SpecialMyExtension class;InvalidArgumentException: Provided specification is not an array, it generally means that MediaWiki could not find the class specified in extension.json that implements your special page.The body file (MyExtension/src/SpecialMyExtension.php) should contain a subclass ofMediaWiki\SpecialPage\SpecialPage or one of its subclasses. This file loads automatically when someone requests the special page. The example below implements the subclass SpecialMyExtension.
You need the__construct() constructor because its first parameter names your special page.
execute() is the main function called when a special page is accessed. This function overrides the functionSpecialPage::execute(). It passes the single parameter$par, the subpage component of the current title. For example, if someone follows a link toSpecial:MyExtension/blah,$par contains "blah".
You should run Wikitext and HTML output through$wgOut. Do not use 'print' or 'echo' directly when working within the wiki's user interface.
However, if you use your special page as an access point to custom XML or binary output, seeTaking over output in your special page.
<?phpnamespaceMediaWiki\Extension\MyExtension;classSpecialMyExtensionextendsMediaWiki\SpecialPage\SpecialPage{publicfunction__construct(){parent::__construct('MyExtension');}publicfunctionexecute($par){$request=$this->getRequest();$output=$this->getOutput();$this->setHeaders();# Get request data from, e.g.$param=$request->getText('param');# Do stuff# ...$wikitext='Hello world!';$output->addWikiTextAsInterface($wikitext);}}
All special pages specify a title, like'My Extension'.
<title> and<h1> elements of the extension's page and onSpecial:SpecialPages.'myextension',must be all lowercase.An example of a localisation file inMyExtension/i18n/en.json:
{"@metadata":{"authors":["<your username>"]},"myextension":"My Extension","myextension-desc":"Adds the MyExtension functionality.","myextension-summary":"On this special page, do this simple thing and earn wonders.","group-myextensionrole":"Role of myextension","group-myextensionrole-member":"Member of role of myextension","grouppage-myextensionrole":"{{ns:project}}:Role of myextension","action-myextension":"XYZ doing.","right-myextension":"to do xyz"}
Ini18n/qqq.json, themessage documentation:
{"@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.","action-myextension":"{{doc-action|myextension}}","right-myextension":"{{doc-right|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.
You can also internationalize the name of the special page by creating aliases for it. The example below uses the file "MyExtension.i18n.alias.php".In this example, the special pageMyExtension registers analias so the page becomes accessible at.../Special:My Extension and.../Spezial:Meine_Erweiterung in German.
Add your alias file toextension.json:
..."ExtensionMessagesFiles":{"MyExtensionAlias":"MyExtension.i18n.alias.php"},...
Add special page aliases toMyExtension.i18n.alias.php:
<?php/** * Aliases for myextension * * @file * @ingroup Extensions */$specialPageAliases=[];/** English * @author <your username> */$specialPageAliases['en']=['MyExtension'=>['MyExtension','My Extension'],];/** Deutsch * @author <your username> */$specialPageAliases['de']=['MyExtension'=>['MeineErweiterung','Meine Erweiterung'],];
Again, you should write a space in the ID and an underscore in the code.
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 use'my_extension' => 'My extension', assuming we consistently identified the extension asmy_extension elsewhere.
Note that in the associative array for the English language, the string identifying our SpecialPage (MyExtension in the example) isalso a valid title.
Also note, the first element of$specialPageAliases['en']['MyExtension']must be the same as the key ('MyExtension')! OtherwiseSpecial:Specialpages will not list the page.
You can set which group your special page appears under onSpecial:SpecialPages by overridingSpecialPage::getGroupName() in your subclass.
/** * Override the parent to set where the special page appears on Special:SpecialPages * 'other' is the default. If that's what you want, you do not need to override. * Specify 'media' to use the <code>specialpages-group-media</code> system interface message, which translates to 'Media reports and uploads' in English; * * @return string */functiongetGroupName(){return'media';}
Some common values are 'login', 'maintenance', 'media', 'other', 'pagetools', 'redirects', 'users'. You can see the accepted values at Special:AllMessages (search forspecialpages-group or browse the wiki using the pseudo language 'qqx' by going to Special:SpecialPages?uselang=qqx) and looking at the headings. Specify the word 'media' to use the interface message 'specialpages-group-media'.
If your special page doesn't fit into any of the preconfigured headings, you can add a new heading by adding it to your localisation file (seeThe localisation file).
The standard page groups that come with MediaWiki are listed in the localisation file. For example, the English messages are inlanguages/i18n/en.json and begin withspecialpages-group-. If you want to categorize your special page underusers, then the message isspecialpages-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 themessages array. In this example, we define thegamification group:
{"myextension":"My Extension","myextension-desc":"Adds the MyExtension functionality.","myextension-summary":"On this special page, do this simple thing and earn wonders","specialpages-group-gamification":"Gamification"}
Now, assuming you set the return value for the methodSpecialPage::getGroupName() asgamification in your class definition, reloadSpecial:SpecialPages to see your new category.
You canoverload 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:
function__construct($name='',$restriction='',$listed=true);
$name Name of the special page, as seen in links and URLs$restrictionUser right required, e.g. "block" or "delete"; also seeRestricting page access$listed Whether the page is listed in Special:SpecialpagesSpecialPage->setHeaders()This initialises the OutputPage object$wgOut with the name and description of your special page.It should always be called from yourexecute() method.
SpecialPage->getOutput()This method returns an OutputPage object which can be accessed as described below. As in the example code, use the following code instead of the deprecated$wgOut global variable:
$output=$this->getOutput();$output->addWikiTextAsInterface('Hello, World');
SpecialPage->getRequest()This method returns a WebRequest object which can be accessed as described below. As in the example code, use the following code instead of the deprecated$wgRequest global variable:
$request=$this->getRequest();$myparam=$request->getText('myparam');
SpecialPage->including()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 SpecialPage class as extending the IncludableSpecialPage class.
TheSpecialPage->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.
SpecialPage->execute()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 toSpecial:MyExtension/blah,$par will contain "blah".
| MediaWiki version: | ≥ 1.25 |
It's useful to addhelp pages on MediaWiki.org, where they'll betranslatable. To make sure users find your help page, it's advisable and very simple for your special page to link the help page in question:
$this->addHelpLink('Help:Extension:MyExtension');
OutputPage.php contains the class definition for objects of typeOutputPage. You can get an object of this class from your SpecialPage using
$output=$this->getOutput();
The variable name $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 useecho orprint). If you want to use it somewhere, create the variable and then use it:
functionrandomFunction(){$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 viewingincludes/OutputPage.php (indeed, all of these can be inspected), but there are a few methods you should definitely know about.
OutputPage->addHTML()Essentially the quick and dirty substitute forecho. 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 ofSatan injected in. You're better off using escaping (such as with the php function htmlentities) or the XML builders class to build trusted output.
$output->addHTML('<form action="'.$action.'" method="post">');
OutputPage->addWikiText()For most output, you should be using this function. It's a bit of a black magic function: wikitext goes in, HTML comes out, and a whole lotta arcane code and demon summonings happen in between.
$output->addWikiText("This is some ''lovely'' [[wikitext]] that will '''get''' parsed nicely.");
What's worth noting is that the parser will view your chunks as cohesive wholes and paragraph accordingly. That is...
$output->addWikiText('* Item 1');$output->addWikiText('* Item 2');$output->addWikiText('* Item 3');
Will output three lists with one item each, which probably wasn't intended.
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.
OutputPage->showErrorPage()An error page is shown. The arguments$title and$msg specify keys into $this->msg(), not text. An example:
$output->showErrorPage('error','badarticleerror');
Error.This action cannot be performed on this page..You can also specify message objects or add parameters:
$output->showErrorPage('error','badarticleerror',['param1','param2']);
$messageObject=newMessage(...);...$output->showErrorPage('error',$messageObject);
$titleMessageObject=newMessage(...);$messageObject=newMessage(...);...$output->showErrorPage($titleMessageObject,$messageObject);
TheWebRequest class is used to obtain information from the GET and POST arrays. Using this is recommended over directly accessing the superglobals.The WebRequest object is accessible from extensions by using theRequestContext.
MediaWiki has a load of convenience functions and wrappers for interacting with the database, using the\Wikimedia\Rdbms\Database class. It also has an interesting load balancing scheme in place. It's recommended you use these wrappers. Check outDatabase.php for a complete listing of all the convenience functions, because these docs will only tell you about the non-obvious caveats. SeeManual:Database access.
TheUser class is used to represent users on the system. SpecialPage->getUser() should be used to obtain a User object for the currently logged in user.The use of the global$wgUser is deprecated
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 usegetLocalURL() to get a URL to that page.
To get a title object for your special page from outside of the SpecialPage class, you can useSpecialPage::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:
This page is intentionally left blank. message, if using a subpage ofSpecial:BlankPage). In MediaWiki:Common.js, check forwgPageName, then hide the MediaWiki-generated content (just append CSS{visibility:hidden;} ), and inject custom HTML (innerHTML) into thedocument.getElementById('bodyContent') ordocument.getElementById('mw_contentholder'). For an example, seemeta:User:Krinkle/Tools/Real-Time Recent Changes.MediaWiki does not set the title of the extension, which is the developer's job.It will look for the name of the extension whenSpecial:Specialpages is called or the special page is loaded. In thefunction execute( $par ) section, use OutputPage methods to title the extension like:$this->getOutput()->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 ofgetDescription(), 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.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 isDirtyPages and the name comes out to "List of Dirty Pages" but you want it to be (andexcuse my poor French) "Liste de Pages Sales". Well, it's as simple as this:
Andvoilà (pardon the pun), the change is applied.
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 outSpecial: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:
$wgOut->addWikiText($this->msg('dirtypageshelp'));
Then this message too can be customized atMediaWiki:Dirtypageshelp.
See alsoHelp:System message.
Sometimes you may want to limit the visibility of your Special Page by removing it fromSpecial:SpecialPages and making it visible to only those users with a particular right. You can do this in theconstructor by passing in a$restriction parameter; e.g., “editinterface”, a right only assigned to sysops by default; see theUser rights manual for other available user rights.
function__construct(){parent::__construct('MyExtension','editinterface');// restrict to sysops}
Or you can create your own right inthe setup file and assign it to sysops, e.g.:
"AvailableRights":["myextension-right"],"GroupPermissions":{"sysop":{"myextension-right":true}}
and then call the constructor with your right:
function__construct(){parent::__construct('MyExtension','myextension-right');}
Even if you restrict your page in the constructor, as mentioned above, it will still be viewable directly via the URL, e.g. at Special:MySpecialPage.In order to actually limit access to your SpecialPage you must call$this->checkPermissions() in theexecute method.
If you need more fine-grained control over permissions, you can override$this->checkPermissions(), and/or add whatever permissions-checking is required for your extension.
In LocalSettings.php you can use theSpecialPage_initList hook tounset unwanted built-in special pages. See"making a few SpecialPages restricted" if you needconditional 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.
$wgHooks['SpecialPage_initList'][]=function(&$list){unset($list['Userlogout']);unset($list['Userlogin']);returntrue;};
A different approach would be to use the DisabledSpecialPage callback.This approach may be preferred if you're only disabling the special page "temporarily", because the default message in this case would say:This page has been disabled by a system administrator. instead of pretending the page does not exist at all.This gives clear hint that the page may be activated at a later time.
$wgSpecialPages['Userlogout']=DisabledSpecialPage::getCallback('Userlogout');$wgSpecialPages['Userlogin']=DisabledSpecialPage::getCallback('Userlogin');
It is also possible to add custom lengthy explanation of why you're disabling the special page, by giving a message key as the second argument of the callback.To do so first create a system message "MediaWiki:Userlogout-disable-reason" and write all the explanation there.The message will be parsed in a block format.Then in LocalSettings.php add:
$wgSpecialPages['Userlogout']=DisabledSpecialPage::getCallback('Userlogout','Userlogout-disable-reason');
On MediaWiki, all actions by users on wiki are tracked for transparency and collaboration.SeeManual:Logging to Special:Log for how to do it.
If you're an extension developer, you have to implement thegetGroupName() method as described inthe Special page group section of this page.
Since MediaWiki 1.21, the special page group can be overridden by editing asystem message. This method is not intended to be used by extension developers, but by site admins. The group name must be placed in thespecialpages-specialpagegroup-<special page name> message, where<special page name> is the canonical name (in English) of the special page in lowercase. For example, if you want to set the group under whichSpecial:MyLittlePage is displayed onSpecial:Specialpages toMyLittleGroup, you just have to createMediaWiki:Specialpages-specialpagegroup-mylittlepage with contentMyLittleGroup.Special:MyLittlePage will then show up under the groupMyLittleGroup, which you can name underMediaWiki:Specialpages-group-mylittlegroup.
If you want to change the group of existing special pages, have a look onSpecial:SpecialPages&uselang=qqx and use those names instead of "mylittlepage".
To remove a special page from theSpecial:Specialpages altogether, pass afalse as a third parameter to the SpecialPage parent constructor, as described inthe SpecialPage Constructor section of this page.If you need more complicated logic to determine whether the page should be listed or not, you can also override theisListed() function, but using the constructor parameter is simpler.
Simply use the "siteinfo" API module to retrieve the information from the wiki like e.g./api.php?action=query&meta=siteinfo&siprop=specialpagealiases.