The MediaWiki application is probably best known for being the engine behind Wikipedia. Many people are finding that MediaWiki provides a usable environment for sharing information within workgroups and even entire organizations, as well as online communities. MediaWiki allows users to share information via blogs, wikis, and files. It also allows you to secure uploaded files, tag files for easy locating, and locate experts using a tag cloud. For more information, see Resources.
What if you want to introduce custom information that you don't want to manually update and insert into your wiki pages? What if you want to provide custom output formatting for a particular type of information? It's situations like these where MediaWiki makes sense, since you can easily add these site-specific features through the use of extensions.
Let's take a look at how you can create MediaWiki extensions that cooperate with different information sources while providing the data through the familiar user interface of the wiki page.
Extensions can add new tags to the wiki markup used to write articles, add new reporting and administrative features by creating special pages, change the look and feel of the wiki through formatting skins, and even integrate with external authentication methods (although authentication won't be covered in this article).
Extensions are written in PHP and make use of MediaWiki's various internal hooks, classes, and methods to get their jobs done efficiently. While you can develop and deploy MediaWiki using any supported Web server and your favorite PHP development environment, you will be using the following in this article:
- Eclipse V3.5.2 — Support for a wide range of programming languages and environments
- PHP Development Tools (PDT) V2.2.0 — A PHP plug-in for Eclipse
- MAMP Pro V1.9 — A handy package for Mac OS X, which provides Apache, MySQL, and PHP in an isolated environment with a helpful GUI front end. Even though OS X ships with Apache and PHP installed, I opted to use this because the version of PHP in OS X V10.6 Snow Leopard (V5.3.1) apparently has a bug that prevents MediaWiki from operating correctly.
- MediaWiki V1.15.3 — The current stable version of MediaWiki
You can find all these in the Resources section.
Before you jump into looking at the different kinds of extensions, let's take a quick look at the folder and file layout used by most of them. After that, you'll have a high-level overview of skin extensions to change the look and feel of your MediaWiki site. Next you'll create a special-page extension that produces some administrative statistics. Finally, you'll see how to add custom XML tag markup support you can take advantage of while writing wiki pages.
MediaWiki extensions get installed in the extensions directory in the main MediaWiki
path. Most modern extensions are installed in their own directory and
consist of three files (extension is the name
of the extension):
extension/extension.phpextension/extension.body.phpextension/extension.i18n.php
The first file performs initialization and any setup tasks. The second file is the body of the extension; this is the working code that implements the extension. Finally, the last file contains internationalization (i18n is a common short form) strings. By abstracting your extension's message strings into the i18n file, you can provide localized versions of your extension for any of MediaWiki's supported locales (assuming you can find some help translating the text).
As an example, I've created a sort of Hello World extension called CHTimeStamp (see Downloads for the source
code for this example and the other examples in this article). CHTimeStamp will insert the current date/time stamp whenever someone inserts {{CHSTAMP}} on a wiki page. It consists of the following files:
- CHTimeStamp/CHTimeStamp.php
- CHTimeStamp/CHTimeStamp.body.php
- CHTimeStamp/CHTimeStamp.i18n.php
Figure 1.
CHTimeStamp layout in Eclipse
The CHTimeStamp extension adds a {{CHSTAMP}} variable to MediaWiki's markup. Whenever you put
{{CHSTAMP}} into a page, it's replaced by a
timestamp. Trivial and easy to follow, right? If you're curious, please
take a look at the code (see Download); I'm only describing
it in broad strokes here to introduce you to the general layout and
conventions of MediaWiki extensions.
My CHTimeStamp.php registers the internationalization messages file, tells the wiki engine that it can find the CHTimeStamp class in CHTimeStamp.body.php and adds the CHTimeStamp::registerHooks method to the array of extension functions.
In CHTimeStamp.body.php, you define the CHTimeStamp class. If you look at the code, this is made up entirely of static methods, so it could have also been written as a series of functions without changing the extension's behavior. CHTimeStamp's registerHooks method registers static methods to create the {{CHSTAMP}} variable and to handle pages that use it.
Finally, in CHTimeStamp.i18n.php, I've created translations for the only static string in the extension: its description. With the help of Google Translate, CHTimeStamp supports French, German, and Spanish locales. But I hope the automated translations didn't turn my English into awkward (or inappropriate) blurbs!
After you've created or downloaded an extension, you'll need to install it into MediaWiki and activate it.
Once you have an interesting or useful extension ready for your MediaWiki site, you'll want to install and enable it:
- Copy or unpack the extension into MediaWiki's extensions directory.
- Edit LocalSettings.php in MediaWiki's root directory. Using your favorite text editor, add lines to configure the new extension, then activate it using PHP's
require_once()statement.
For example, to install CHTimeStamp, I've copied its CHTimeStamp directory to the extensions directory and added the following to LocalSettings.php: require_once( "$IP/extensions/CHTimeStamp/CHTimeStamp.php" );.
Check to make sure your extension has been loaded by visiting the wiki's Special:Version page. In addition to information about the MediaWiki version you're running, the Special:Version page lists the extensions that have successfully loaded.
Figure 2. Special:Version page showing
CHTimeStamp
MediaWiki takes advantage of PHP's ability to mix code and HTML markup to give you control over the look and feel of your wiki through the use of skins. Besides the main PHP code, a skin can include various CSS files and supporting images or JavaScript.
A skin generally consists of two PHP files, and a directory for additional support files. For example, the famous default skin, MonoBook, is made up of:
- MonoBook.php — Main MonoBook skin code
- MonoBook.deps.php — A fix for a bug in the APC opcode cache of PHP V5
- monobook/ — Supporting CSS and graphics
The naming convention for skins is very strict, requiring SkinName.php,
SkinName.deps.php and skinname (lowercase) as the support folder name.
Inside the skinname folder will be main.css for the skin's styling. Browser-specific
stylesheet fixes belong here, as well, so you'll often find FF2Fixes.css,
IE6Fixes.css, Opera6Fixes.css, etc. there.
SkinName.php will start off with some helpful metadata.
Listing 1. MediaWiki skin metadata
/** * [SkinName] skin * * @file * @ingroup Skins * @version [#].[#].[#] * @author [name] ([URL] / [E-Mail]) * @license [URL] [name] */ |
Replace everything in square brackets with something suitable for your skin.
Next, you'll need to create a subclass of SkinTemplate and
override the initPage method to indicate your
skin's name, style, and template. Remember to replace SkinName and skinname with the name
of your skin.
Listing 2. Extending
SkinTemplate to provide a new skin
// inherit main code from SkinTemplate, set the CSS and template filter
class SkinSkinName extends SkinTemplate {
function initPage( OutputPage $out ) {
parent::initPage( $out );
$this->skinname = 'skinname';
$this->stylename = 'skinname';
$this->template = 'SkinNameTemplate';
}
}
|
Your skin's workhorse will be your QuickTemplate subclass.
Listing 3. Most of the work is done in the template
class SkinNameTemplate extends QuickTemplate {
...
/**
* 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">
...
|
Inside the QuickTemplate subclass, you'll override methods
to format and style things like category listings and cross-references the way you want. This class's execute method lays out the entire page as an XHTML document, giving you complete control over page organization and styling.
You can't go into XHTML and CSS page layout and styling here, so take a look in your MediaWiki's skins folder for examples you can try out right away.
Special pages in MediaWiki are generated on demand to do something specific and possibly useful for the wiki, such as letting you edit the system-wide message text, list the installed extensions, or get a list of external links.
Unless you specify otherwise, your special page will be available to anyone and show up in the Special:SpecialPages list of special pages. You can also set up your special page so it can be included inline on a page using {{Special:YourPageName}} syntax.
Like other extensions, special pages are installed as a directory into the extensions folder. They generally consist of four files:
- specialpage/specialpage.php — The extension's setup file
- specialpage/specialpage.aliases.php — Aliases for the special page's name
- specialpage/specialpage.body.php — The main code for the special page
- specialpage/specialpage.i18n.php — Internationalization strings for the special page
For example, if you're creating a special page named CHStats, its layout will look like Figure 3.
Figure 3. CHStats in Eclipse
The code in CHStats.php adds credits for the extension, registers the aliases, body and
i18n files, and tells the wiki engine to autoload the CHStats class when it's needed.
Listing 4. Setting up the CHStats special page
<?php
# This is not a valid entry point to MediaWiki.
if( !defined( 'MEDIAWIKI' ) ) {
echo <<<EOT
To install CHStats, put the following line in LocalSettings.php:
require_once( "\$IP/extensions/CHStats/CHStats.php" );
EOT;
exit( 1 );
}
# Take credit for this extension.
$wgExtensionCredits['specialpage'][] = array(
'name' => 'CHStats',
'author' => 'Chris Herborth (chrish@pobox.com)',
'url' => 'http://www.pobox.com/~chrish/CHStats/',
'description' => 'A simple special page demonstration, showing some DB stats.',
'descriptionmsg' => 'chstats-desc',
'version' => '1.0.0',
);
$dir = dirname( __FILE__ ) . '/';
# Register the extension's main code/class.
$wgAutoloadClasses['CHStats'] = $dir . 'CHStats.body.php';
# Register our internationalization files.
$wgExtensionMessagesFiles['CHStats'] = $dir . 'CHStats.i18n.php';
$wgExtensionAliasesFiles['CHStats'] = $dir . 'CHStats.aliases.php';
# Let MediaWiki know about the new special page.
$wgSpecialPages['CHStats'] = 'CHStats';
?>
|
In CHStats.body.php, you create a new class, CHStats, which
extends the SpecialPage class. In the
constructor, you initialize the parent class, then load your
internationalization messages by calling wfLoadExtensionMessages. Check the Special Pages developer's
guide page (see Resources) for more information
about the SpecialPage class constructor, which
will let you restrict access, hide the page, etc.
The override for the execute method does the work of generating the page.
Listing 5. Generating the CHStats special page
# This is where the special page's output is created.
function execute( $par ) {
global $wgOut;
# Initialize the output page.
$this->setHeaders();
# Do stuff.
$wgOut->addWikiText( "Some stats about this '''Wiki''':" );
$db = wfGetDB( DB_SLAVE );
// SELECT ... FROM site_stats
$result = $db->select( 'site_stats',
array( 'ss_total_views', 'ss_total_edits',
'ss_total_pages', 'ss_users' ) );
$statList = array();
foreach( $result as $row ) {
$statList[] = '* Total page views: ' . $row->ss_total_views;
$statList[] = '* Total page edits: ' . $row->ss_total_edits;
$statList[] = '* Total # of users: ' . $row->ss_users;
}
$wgOut->addWikiText( implode( "\n", $statList ) );
$wgOut->addWikiText( "That's it." );
}
|
In the execute method, the $par argument is the sub-page. For example, if you loaded Special:CHStats/foo, $par would be set to foo (its name is apparently a historical oddity).
First you use the setHeaders method to set up the page
header, then call $wgOut->addWikiText to
write some markup to the output stream. You can also use $wgOut->addHTML to write formatted HTML
directly, but I'm using wiki markup in the output. Refer to the Special
Pages developer's guide for more information about these (see Resources), and how to properly add wiki markup
and/or HTML to special pages that can be included inline in other
pages.
The CHStats page uses the wfGetDB function to get a reference to the database (use DB_SLAVE for read-only operations, and DB_MASTER for write operations). Then you select several fields from the site_stats database and format the results as a bulleted list using wiki markup.
You can see what the output of this special page looks like in Figure 4.
Figure 4. CHStats in action
The internationalization strings found in CHStats.i18n.php consist of an array, with one entry per supported language (English, French, German, and Spanish, in this case). In each entry is an array mapping string IDs to what is hopefully their localized text. Google Translate was used for the French, German, and Spanish bits.
CHStats.aliases.php has a similar array containing the localized versions of the CHStats page name itself. This lets French users (for example) access the page as Spécial:StatsCH.
Another popular way to extend MediaWiki is by adding support for new XML tags to the markup. These tags can produce different output based on the tag attributes or the content and are useful for inserting inline HTML or even large blocks of formatted output.
Tag extensions are installed in their own directory under the extensions folder and use the three-file convention described at the start of this article. Let's look at a simple one I've cooked up named CHUser:
- CHUser/CHUser.php — Extension setup
- CHUser/CHUser.body.php — Main extension code
- CHUser/CHUser.i18n.php — Internationalization data
The extension setup going on in CHUser.php is similar to what you've already seen,
although it uses the $wgHooks array to add the
extension's init method to the ParserFirstCallInit list. CHUser::init will be called the first time it's used.
Listing 6. Setting up the tag extension
# Let MediaWiki know about the new tag. $wgHooks['ParserFirstCallInit'][] = 'CHUser::init'; |
Inside CHUser.body.php, the init method registers two
tags: <chuser> and <bz> (see Listing 7). This extension provides two different tags in just
one extension. You could easily combine all of the extensions discussed in
this article if you wanted to, there's no requirement (other than your own
sanity) to split things up.
Listing 7. Registering the
<chuser> and <bz> tags
public static function init( &$parser ) {
# Add our <chuser> tag handler, the continue.
$parser->setHook( 'chuser', 'CHUser::render' );
$parser->setHook( 'bz', 'CHUser::renderBugzilla' );
return true;
}
|
Whenever the wiki markup engine encounters a <chuser>
tag, it will call the CHUser::render method, and <bz> tags will call CHUser::renderBugzilla.
The <chuser> rendering method selects the specified
user's full name and e-mail address, and formats it as a mailto: link wrapped around the user's full
name (if there is one). You can see from Listing 8
that most of the logic is simply handling cases where the full name or
e-mail address isn't present in the database (such as for the Admin account
on my wiki).
Listing 8. Handling
<chuser>UserName</chuser>
public static function render( $input, $args, $parser, $frame ) {
$user = mysql_escape_string( $input );
$db = wfGetDB( DB_SLAVE );
// SELECT ... FROM user
$result = $db->select( 'user',
array( 'user_real_name', 'user_email' ),
'user_name = \'' . $user . '\'' );
$mailtos = array();
foreach( $result as $row ) {
$thisUser = '<span class="user">';
if( $row->user_email ) {
$thisUser = $thisUser . '<a href="mailto:'
. htmlspecialchars( $row->user_email ) . '">';
}
if( $row->user_real_name ) {
$thisUser = $thisUser . htmlspecialchars( $row->user_real_name );
} else {
$thisUser = $thisUser . htmlspecialchars( $input );
}
if( $row->user_email ) {
$thisUser = $thisUser . '</a>';
}
$thisUser = $thisUser . '</span>';
$mailtos[] = $thisUser;
}
return implode( ", ", $mailtos );
}
|
Listing 9 shows you how the <bz> tag uses its ID attribute
to link to a specified bug report in the MediaWiki Bugzilla database. This
shows how easy it is to work with external data sources. If you wanted to
get fancy you could use Ajax to load the bug report instead of linking to
it, and display some of its data instead of the link.
Listing 9.
<bz id="number"
/> links to the MediaWiki Bugzilla
public static function renderBugzilla( $input, $args, $parser, $frame ) {
$retval = '';
if( $args['id'] ) {
$bzId = htmlspecialchars( $args['id'] );
$retval = '<a href="https://bugzilla.wikimedia.org/show_bug.cgi?id='
. $bzId . '">MediaWiki Bug #' . $bzId . '</a>';
} else {
$retval = '<span class="error">No bug ID specified</span>';
}
return $retval;
}
|
You can see these two tags in action in Figure 5.
Figure 5. Your extensions in action
Listing 10 shows you what the wiki markup looks like for that section of the page.
Listing 10. The wiki markup demonstrating these extensions
== Extension testing ==
If this is working, we should see a timestamp: {{CHSTAMP}}
CHUser info:
* '''Admin''': <chuser>Admin</chuser>
* '''Chrish''': <chuser>Chrish</chuser>
<bz id="1024" />
|
As you can see from the CHUser extension, adding support for
custom XML tags is easy and lets you do almost anything. All PHP features and MediaWiki services are available to you, so you can pull data from (or send data to) external systems, change your behavior based on the current user's credentials and permissions or insert JavaScript to run tasks directly in the viewer's browser. The possibilities and endless and limited only by your specific needs and requirements.
In this article, you were introduced to several extension techniques supported by MediaWiki, an open source wiki system similar to commercial wiki software like Lotus® Connections.
You were shown MediaWiki's extension conventions and shown how to create a simple wiki variable extension as an introduction to writing MediaWiki extensions. An overview of MediaWiki's skinning features help you get started creating your own customized site layout.
Special pages are often used to generate information, whether retrieved from a database or other source, so you made one that displays some of the system's statistics. You then created a couple of custom XML tags that could be included in wiki markup on any page.
| Description | Name | Size | Download method |
|---|---|---|---|
| Article source code | os-mediawiki-CH.Examples.zip | 7KB | HTTP |
Information about download methods
Learn
-
Visit MediaWiki.
-
See a catalog of many MediaWiki extensions, organized into categories.
-
Get general information about writing extensions for MediaWiki in the Extensions manual.
-
Check out the Skinning manual
to learn how to develop skins for MediaWiki.
-
See the Special Pages
developer guide to learn how to develop special pages for MediaWiki.
-
Read the Tag Extensions
manual to learn how to develop tag extensions for MediaWiki.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
Stay current with developerWorks' Technical events and webcasts.
-
Follow developerWorks on Twitter.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products, as well as our most popular articles and tutorials.
-
The My developerWorks community is an example of a successful general community that covers a wide variety of topics.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
Get products and technologies
-
Get the Eclipse IDE used for creating the code in this article.
-
Get the PHP Development Tools for Eclipse.
-
Get MAMP Pro (Apache, MySQL, PHP package for Mac OS X).
-
Get MediaWiki.
- The Lotus Connections documentation page lists product documentation
and other resources for supported versions of Lotus Connections, IBM's
commercial collaboration platform.
-
Innovate your next open source development project with IBM trial software, available for download or on DVD.
- Download
IBM product evaluation versions
or explore
the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from
DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
-
Participate in developerWorks blogs and get involved in the developerWorks community.

Chris Herborth is an award-winning senior technical writer with more than 10 years of experience writing about operating systems and programming. When he's not playing with his son, Alex, or hanging out with his wife, Lynette, he spends his time designing, writing, and researching (that is, playing) video games.




