MediaWiki Introduction 2023
MediaWiki Introduction 2023 is a 3-part series by Timo Tijhof, to familiarize new maintainers and managers with MediaWiki. It was originally written in December 2023 for the MediaWiki Code Jam.
Part 1: MediaWiki core concepts
Topics covered in the video:
- High-level product and architecture perspective.
- Component walk-through.
Pages mentioned:
- Database layout
- Who writes Wikipedia
- Why create an account
- CSSJanus docs
- CSSJanus demo
- Permissions
- Change user rights
- Bot passwords
- Logging
- No original research
- Recent changes
- RCFeed
- Listen to Wikipedia
- CodePen
- ClueBot
- Namespaces
- Special pages
- Page table
- Revision table
- Text table
- External storage
- Example category
- Everything is a wiki page
- GLAM
- Glamorous tool
- Example photo
- Statistics
- JobQueue
- MediaWiki Engineering - Performance practices
- Multimedia
Part 2: Wikipedia's extensions
Topics covered in the video:
- What is an extension?
- How to install an extension in two easy steps.
- Walk-through the installed extensions via Wikipedia's Special:Version page.
Pages mentioned:
- What is an extension
- WikiEditor extension
- VisualEditor extension
- Wikipedia's extensions
- CentralAuth
- Echo
- Cite
- EasyTimeline
- EasyTimeline example: Version lifecycle
- InputBox
- InputBox example: Incident status
- Scribunto
- Lua example: SchemaDiagram
- What Lua scripting means for Wikimedia (2013)
- How we made editing Wikipedia twice as fast (2014)
- Save Timing in Grafana
- TimedMediaHandler
- 3D extension
- AbuseFilter
- Gadgets
- Gadget example: RTRC
- Gadget example: Stockphoto
- Half-timbered mansion, by Radomianin
Part 3: Write your first patch
Are you ready to write your first patch?
This is not a minimal example for learning Git, or contributing a typo-fix to MediaWiki (which you could do by cloning the Git repo, directly modifying a file, and relying on a code reviewer to test/merge it for you). Rather, the below prepares you to understand other people's patches (as manager), or to prepare you for becoming an active MediaWiki developer. As such, it includes learning how to preview the result of your change in a web browser, and running the unit tests on your machine.
Homework
Before you write your first patch:
- Download MediaWiki. I recommend Quickstart to install MediaWiki with only Git and PHP. (No Docker, Apache, or MySQL needed!)
- Run PHPUnit. This checks that everything got installed correctly. From your mediawiki directory, run
composer phpunit -- tests/phpunit/includes/ResourceLoader/
- Run QUnit, by browsing to http://localhost:4000/index.php/Special:JavaScriptTest
- Add the Examples extension to your MediaWiki site.
Exercise 1: Edit a localisation message
- Browse to http://localhost:4000/index.php/Special:HelloWorld
- Change the page title by editing the "example-helloworld" message in the
mediawiki/extensions/examples/i18n/en.json
file. - Refresh Special:HelloWorld in the browser and notice the change.
✅ Done!
The wfLoadExtension()
line you added to LocalSettings, loads extension.json
from the given extension. This file is the main source for everything related to that extension. Everything it can do starts in, and is discoverable from, this file. Have a look at mediawiki/extensions/examples/extension.json
.
The first few fields are metadata you may recognise from Special:Version, such as the authors, the url, and the "type" that each extension is grouped under. The "SpecialPages" field is the registry where we declare our special pages. "AutoloadNamespaces" is similarly a registry, where we define a PHP namespace the PHP classes in our repository. These are then discovered and loaded automatically by MediaWiki.
For later: Manual:Extension registration, Manual:Extension.json/Schema, Best practices for extensions, Manual:Coding conventions/PHP.As we learned in Part 1, Special:SpecialPages enumerates all special pages. Both from MediaWiki core, and from extensions you have enabled. The examples/extension.json
file registers "Special:HelloWorld", which you can visit at http://localhost:4000/index.php/Special:HelloWorld.
MediaWiki takes care of interpreting and routing URLs. This URL is for the "Special" namespace, which MediaWiki looks up in the SpecialPages registry. MediaWiki loves registries like these, which automatically take care of most of the work, allowing you to focus on business logic instead of boilerplate.
MediaWiki finds and constructs a SpecialHelloWorld instance for you, and wraps the skin (sidebar, navigation, styling etc) around its output. It gives control to you in the SpecialHelloWorld::execute() method. Have a look inexamples/includes/SpecialHelloWorld.php
.uselang=qqx
. This is harmless and works on the live Wikipedia, too. Visit Special:HelloWorld?uselang=qqx to see which message keys are used. These messages come from the /i18n/en.json
file in the examples repo.Exercise 2: Create a localisation message
- Add a new key to
/i18n/en.json
with a short sentence as the value. For example"example-goodbye": "That's all Folks!"
. - Render this message as the second paragraph on the page, by adding a statement to SpecialHelloWorld::execute. Look at the existing intro message for how to do this.
- Refresh Special:HelloWorld in the browser and notice the second paragraph.
✅ Done!
Exercise 3: Make a config change
- Browse to your wiki's Main Page at http://localhost:4000/ and notice the "Hi there" banner.
- Edit LocalSettings.php and disable this feature by adding
$wgExampleEnableWelcome = false;
. - Hard-refresh your wikis' Main Page in the browser and notice the the banner is gone.
- Comment out or remove the LocalSettings line to re-enable the banner. Refresh to see it come back.
✅ Done! How did that work?
This feature flag is defined in examples/extension.json
as "ExampleEnableWelcome", under "config". This flag has a default of true
.
This "config" map is another one of those automated registries that does heavy lifting for you. It takes care of validation, documentation, default values, applying LocalSettings, and letting you access the result in PHP code (for the current wiki) via MediaWikiServices->getMainConfig
.
As the site admin, you can change the value of any configuration value by editing LocalSettings, and setting variables named as "wg" (wiki global) + the configuration key. For example, $wgExampleEnableWelcome in this case.
For later: Manual:Configuration for developers, Manual:Extension.json/Schema#config.Excercise 4: Edit a hook handler
We're going to change the "Hi there" banner to display on special pages instead of articles. Notice the banner is currently shown on the Main Page, but not at Special:HelloWorld.
- Edit
function onBeforePageDisplay
inexamples/includes/Hooks.php
, and change the condition from NS_MAIN toNS_SPECIAL
. See also Namespaces, from Part 1. - Refresh and notice the banner now shows on Special:HelloWorld instead of Main Page.
✅ Done!
$x = [ 42 ]; HookRunner->onFoo( &$x );
. By default the $x
array will stay unchanged, but your extension may handle this hook as follows: namespace MediaWiki\Extension\Example;
class Hooks {
public function onFoo( &$x ) {
$x[] = 3.14;
}
}
HookRunner->onFoo()
directly called your function.
In the above exercise, we handled the BeforePageDisplay hook. Find the registration in examples/extension.json
, and follow the trail from there, via "HookHandlers", to "MediaWiki\Extension\Example\Hooks", to examples/includes/Hooks.php
.Excercise 5: Improve test and fix the bug
Did you notice that the welcome message at http://localhost:4000/ currently greets you with the wrong day? Let's fix this!
- Edit the
welcome.js
file in examples/resources/. Do you spot the off-by-one error? - Verify that today's greeting on the Main Page is now correct.
- Run the tests at http://localhost:4000/index.php/Special:JavaScriptTest?filter=example. The tests are currently passing both with and without your fix.
- Open the
ext.Example.welcome.test.js
file from the examples/tests/qunit/ directory. It uses 2011-04-01 as an example, which was a Friday. The test does not strictly assert the entire return value, it only checks that it includes a greeting. Improve the unit test by using assert.strictEqual() likeassert.strictEqual(actual, 'example-welcome-title-user')
instead ofassert.true(actual.includes('example-welcome-title-user'))
. We don't know the shape of this value yet, so we can expect a different''
value for now. Refresh Special:JavaScriptTest, and take the actual result from there. - Copy the actual result from the failing test at Special:JavaScriptTest, and use it as your expected value with assert.strictEqual(). We now have a passing test that (correctly) expects Friday instead of Saturday.
✅ Done! Now let's take a look underneath.
- extension.json ("ResourceModules" is another registry, "dependencies", "packageFiles")
- ResourceLoader/Developing with ResourceLoader
- onBeforePageDisplay calls OutputPage::addModule, which is responsible for changing the HTML so that your module is loaded on the page.
- The
welcome.js
file andstyles.css
files are part of this module.
- ext.Example.welcome.test.js (find it in extension.json)
- Run via Special:JavaScriptTest?filter=welcome (filter optional, but helps focus and runs faster)
🎉 Congratulations, you are well on your journey to becoming a MediaWiki hacker!
If you have a mentor or onboarding buddy, you can share your work via a Phabricator paste:
- Copy the output from
git diff
. For example,git diff | pbcopy
on macOS. Or on Linux/Windows, save it as a temporary file withgit diff > tmp.txt
, then open that file in your text editor, select all, and copy that to your clipboard. - Create Phabricator paste