This is a MediaWiki programming issue; if you're only interested in
admin stuff, you can safely delete.
So, I'd like to set up a system of hooks and filters for MediaWiki. We
have right now four ways to change how MediaWiki works:
1. Tag extensions. You can add new tags that the parser can
generate code for.
2. Special pages. You can add special pages that do different kinds
of queries.
3. Edit filtering. You can put a pre-filter on editing.
4. Skin changes. You can create new skins.
However, we don't have an easy way to change the behaviour of mainline
functionality. I think it would be a good idea to add some simple hook
processing to MediaWiki, so that extensions can add hooks and run custom
code when something happens.
Consider, for example, an extension to log key actions in the wiki to a
specialized logging system. The extension setup function could look
something like this:
function setup_logging {
global $wgLogFile;
wfAddHook('after_article_save', log_article_save, $wgLogFile);
wfAddHook('after_delete', log_deletion, $wgLogFile);
wfAddHook('after_user_ban', log_user_ban, $wgLogFile);
}
By calling the wfAddHook() function, the extension asks that a function
get called if/when an event happens. wfAddHook() takes three arguments:
the name of the event (say, after an article is deleted), a function to
call, and an optional data block that can be used by the function. This
way, we can use the same function for different hooks or different
actions, like:
wfAddHook('after_article_save', irc_notify, 'brion');
wfAddHook('after_article_save', irc_notify, 'TimStarling');
A hook function would look something like this:
function log_deletion(&$params, $data) {
$title = $params['title'];
$dbkey = $title->getDBkey();
$filename = $data;
foo_log_message("Article '$dbkey' deleted.", $filename);
return true;
}
The $params argument here is an associative array of the event-specific
parameters. We use this instead of named arguments so that the same
function could be used for different events. The $data is just the data
item that was used when the hook was added.
Hooks can return four possible values:
* true -- the hook has operated successfully and subsequent hooks
should be run
* false -- the hook has operated successfully but no subsequent
hooks should be run
* "some string" -- an error occurred; processing should stop and
the error should be shown to the user
* NULL -- the hook has successfully done the work necessary and
the calling function should skip
The last result would be for cases where the hook function replaces the
main functionality. For example, if you wanted to authenticate users to
a custom system (LDAP, another PHP program, whatever), you could do:
wfAddHook('before_user_login', ldap_login);
# ...
function ldap_login(&$params, $data) {
$user_id = $params['user_id'];
$password = $params['password'];
# log user into LDAP
return NULL;
}
Note that a reference to the parameters is passed to the hook function:
the hook could theoretically change its parameters, like so:
wfAddHook('before_article_save', reverse_title);
# ...
function reverse_title(&$params, $data) {
$old_title = $params['title'];
$params['title'] = Title::makeTitle($old_title->getNamespace(), strrev($old_title->getDBkey()));
return true;
}
A calling function or method would use the wfRunHooks() function to run
the hooks related to a particular event, like so:
class Article {
# ...
function submit(...) {
$params['title'] = ...;
$params['user'] = ...;
$params['section'] = ...;
$params['is_new'] = ...;
if (wfRunHooks('before_article_save', $params)) {
# save the article
wfRunHooks('after_article_save');
}
}
wfRunHooks() returns true if the calling function should continue
processing (the hooks ran OK), or false if it shouldn't (an error
occurred, or one of the hooks handled the action already). Checking the
return value matters more for "before_*" hooks than for "after_*" hooks.
The big advantage to this event-handling approach is that we can start
isolating site-specific features into extension files. The "mainline"
code only concerns itself with "mainline" features, and "extension" code
can handle more exotic ones. It simplifies our mainline code,
centralizes extension features into one easy-to-comprehend package, and
hopefully improves our quality and reliability. (The simpler our
mainline code is, the fewer bugs it will have; the easier it is to read,
the more people will want to help maintain it.)
It should also help us evaluate/implement experimental new features, and
obviate the need to patch the mainline code to do so. Lastly, it could
let us move little-used features out to extension files to simplify the
main code.
There are disadvantages, of course, too. If MediaWiki is only supposed
to work for a single wiki installation, or multiple installations that
should behave exactly the same, then the hooks code is unnecessary -- we
should just code it all in the mainline code. There's also some
additional complexity in the mainline code in setting up the parameters
and calling the hook functions; this can be balanced against the
complexity of adding each extension's code into the mainline functions.
Finally, there is a lot of documentation that would need to be done to
say 1) where hooks are defined and 2) what parameters they provide.
Anyways, I'm going to post this to meta, and implement it for 1.4. But I
figured this would be a good forum for discussion -- hopefully making
the feature better.
~ESP
--
Evan Prodromou <evan(a)wikitravel.org>