mwForum Development

This file contains random development notes. You should have read or at least skimmed it when you intend to customize, port, extend or translate mwForum.

Contents

Please note that if you distribute your own version of mwForum, no matter how heavily customized or under what name - it's still copyrighted by me (Markus Wichitill), and you're not allowed to remove or change my copyright notices, you can only add your own notices if you did substantial changes or additions. You'll also have to use a different name than "mwForum".

Language Modules

Instructions for creating language modules:

Make a copy of MwfEnglish.pm and change its name to whatever your language is called. Adapt the line "package MwfEnglish" at the top of the file to match your new filename (without the ".pm"). Add the following lines before the $lng->{author} line:


# Default to English for missing strings
require MwfEnglish;
%$lng = %$MwfEnglish::lng;

Set $lng->{author} to your own name. If your translation is based on an existing translation, don't replace the original author's name, add your name to it. At the bottom of the file, change the language name in the "do 'MwfEnglishLocal.pm';" line to your language (the named file doesn't have to exist).

The main work is of course to translate all the strings on the right hand side. You'll have to take care to escape any characters that have a special meaning in Perl or HTML, except for where the strings are meant to contain actual HTML. If in doubt, just look at how it's done in the original English or German modules. The main issues to watch out for are:

Language modules need to be encoded in Unicode/UTF-8 for mwForum 2.15.0 and later. I can't really provide simple instructions on how to handle that, as there are too many different text editors out there, which in turn have different options for handling Unicode. If you want to upgrade an existing module to UTF-8, also check the instructions in Upgrade.html. Do not write accented characters etc. as HTML entities (e.g. ä) instead of using UTF-8, as in some cases that will lead to trouble.

To update your translation for future versions, you'll probably want to use some sort of "diff" utility on the old and new English modules to get a list of additions and changes to the string table. Not all of my changes have to be integrated into your translation, sometimes I just make small changes to the wording which aren't that important.

If someone uses your older translation with a newer version of mwForum which adds new strings, the software will fall back to the English original strings for missing translated strings (provided that you added the code above), but some strings might have changed their meaning completely, thus it's not always safe to use an outdated translation.

If you're wondering where the strings for administration functions are, they're hardcoded and not supposed to be translated. That's by design, as it would be a lot more work to internationalize all strings, and contributed language modules and updates for them are already few enough without multiplying the number of strings.

If you have created or updated a translation, please post it somewhere in the support forum (it will be moved to the download section by an admin).

mod_perl

If you want to change and redistribute the code, please make sure you know what is necessary to keep the code mod_perl-compatible. Lots of information on this topic is available in the mod_perl Guide. The two most important things to know are:

Plugins

There are places in the code where admins typically want to customize the forum behavior, and places where they want to fill in special functionality that isn't part of the official releases. The problem with modifying the original code is that it makes updating to newer releases problematic. For this reason, mwForum contains hooks for calling plugins in various places. Depending on the plugin type, functions in these plugins can replace default functions or are called in addition to the default code.

If you have developed a plugin that might be of general interest, you can post it anywhere in the support forum and it will be moved to the board in the Downloads category. But even if a plugin is for your internal usage only, you might want to mention it in the support forum, since I might change plugin types without warning as long as I'm under the impression that nobody except me uses them anyway.

Plugin types or subtypes that have never been used or tested by anybody might very well be buggy or badly documented, but if anybody wants to use them, I'll usually provide a fix or some more explanations ASAP.

Installation

Plugins are activated by uploading the Perl module and setting the respective options at the bottom of the forum options page. Some of the plugin types allow one or more plugin functions for different subtypes and take the form "subtype=MwfPlgModuleName::functionName", some allow multiple sequentially executed plugin functions and take the form of one or more "MwfPlgModuleName::functionName" lines, and some only allow a single plugin function using the same form.

The names of the Perl modules and functions can be freely chosen, except that the module names must start with "MwfPlg" for security reasons. You can copy all your used plugin functions into a single module if you want.

Include Plugins

Include plugins can be used to include output (headers, HTML etc.) at various positions on the forum's pages, but can also include code that does something else. See example/MwfPlgInclude.pm for some examples.

While there are a million different places where one might want to include stuff, this feature can only cover some of the more common places with the following subtypes:

early
Called early, directly after user authentication. Can be used for purposes like blocking users/bots, or anything else that can't wait for a later plugin call, or shouldn't for performance reasons. See example for a plugin function that only allows a limited number of requests per IP and timeframe.
httpHeader
Can print HTTP header lines.
htmlHeader
Can print HTML header lines.
top
Can print HTML at the top of every page.
middle
Can print HTML below the top bar of every page.
bottom
Can print HTML at the bottom of every page.
topUserLink
Can print button links for all users in the top bar of all pages.
forumUserLink
Can print button links for all users in the page bar on the forum page.
forumAdminLink
Can print button links for admins/mods in the page bar on forum pages.
boardUserLink
Can print button links for all users in the page bar on board pages. Parameters passed: board.
boardAdminLink
Can print button links for admins/mods in the page bar on board pages. Parameters passed: board.
topicUserLink
Can print button links for all users in the page bar on topic pages. Parameters passed: board, topic.
topicAdminLink
Can print button links for admins/mods in the page bar on topic pages. Parameters passed: board, topic.
userProfileLink
Can print button links for the respective user in the page bar on user profile pages. Parameters passed: user.
userOptionsLink
Can print button links for the respective user in the page bar on user options pages. Parameters passed: user.
userUserLink
Can print button links for all users in the page bar on user info pages. Parameters passed: user.
userAdminLink
Can print button links for admins in the page bar on user info pages. Parameters passed: user.
userInfo
Can print stuff on user info pages, after the profile section. Parameters passed: user.
topicData
Called before printing of posts on topic pages starts. Can either print something there or e.g. process posts in some way, prepare data for other plugins that are called per-post etc. Parameters passed: board, topic, postsById, pagePosts, boardAdmin and topicAdmin.
postHeader
Can print header items for posts on topic pages. Parameters passed: board, topic, post, boardAdmin and topicAdmin.
postLink
Can add button links for posts on topic pages. Parameters passed: board, topic, post, boardAdmin and topicAdmin. Note that implementations of this plugin subtype need to return their markup differently than the others, as shown in the example plugin.
tagButton
Can print additional [tag] buttons on post/message forms. Could also be used for other stuff in that place.
tagButtons
Can print tag buttons etc. on post/message forms. Replaces the built-in tag insertion code.

Event Plugins

This plugin type is called from the MwfMain::logAction() function that in turn is called after pretty much every successful script run. While it can be used to override the logging itself, it is mainly used for various other purposes. Event plugins are always called after the fact, so they can't influence/cancel the events directly anymore, although they could usually undo them. They're not called if the script was previously aborted because of an error. Multiple plugins can be installed concurrently.

Read FAQ.html for some more details about logging. To replace the standard logging (e.g. to log to a plaintext file), just leave the logLevel config option set to 0 and use your own config options if necessary.

The plugin functions get the same parameters as MwfMain::logAction(). See the .pl scripts to see which parameters contain what data for the respective event.

example/MwfPlgEvent.pm has a couple of real-world examples.

Message Display Plugins

Some admins want to do more message text filtering/transforming/replacing than is done by default, usually to add more formatting, emoticons and such. An enabled message display plugin is called every time a text with markup is displayed (public posts and private messages, as well as some other texts like reports).

The plugin is called at the start of MwfMain::dbToDisplay(). If the plugin returns 0, the rest of dbToDisplay() will run normally after that, if it returns 1 dbToDisplay() will end without further processing, and if it returns 2, dbToDisplay() will only do the processing that's likely to be safe and not interfere with whatever the plugin is doing (at the time of this writing that means adding attachments and signatures). If you chose to end or limit the processing in dbToDisplay() by returning 1 or 2, you might have to copy part of the code in dbToDisplay() to your plugin, possibly modified in some way that prevents interference with your own markup and code.

The examples in example/MwfPlgMsgDisplay.pm add another smiley and video tags and then let MwfMain::dbToDisplay() continue to do the rest of the work.

Notes:

Authentication Plugins

Note: Since there are so many different scenarios when it comes to authentication, and since the discussion in the development/support forum about this topic was also rather confused and fruitless, there's no good documentation about this complex topic.

There are two subtypes of user authentication plugins: login authentication plugins, and request authentication plugins. The former is called every time someone tries to login using mwForum's login page, the latter for every HTTP request independent of mwForum's normal authentication facilities. The two subtypes are not meant to be used together.

Typical plugin workflow:

  1. Get user credentials.
  2. Check credentials against external data source (SQL, LDAP, NTLM, etc.).
  3. Look for mwForum user account matching authenticated credentials.
  4. If no account exists, optionally create one and set some fields based on external data.
  5. Retrieve and return user acount from mwForum database.

Login authentication plugin details:

Request authentication plugin details:

There are various issues that are not addressed here and that depend on the exact scenario for which someone might want to use a plugin, including which user fields are used for identification (numeric userId, userName, email, etc.), uniqueness and length limitations of fields in mwForum, further synchronization of mwForum accounts with external accounts, etc.

The file example/MwfPlgAuthen.pm contains working examples of login authentication via LDAP, request authentication with HTTP authentication, and request authentication with SSL client certificates.

Alternatives to mwForum plugins: existing site registration/login scripts could be extended to create mwForum user accounts and/or mwForum login cookies. mwForum also supports OpenID for that warm fuzzy "Web 2.0"-esque feeling.

The registration authorization plugin type below is somewhat related to user authentication, though it's limited to accepting or denying a registration performed by user_register.pl.

Authorization Plugins

Authorization plugins allow finer grained control over whether users can perform certain actions. The possibilities of what to do with this data are manifold. You could check whether the user has entered a valid shareware code before letting him register or post. For a corporate forum, you could limit registration to users with a company email address. You could check how much data a user has already uploaded before letting him add another attachment.

Authorization plugins can only disallow the respective actions, they can't allow otherwise forbidden actions. For example, a normal user can't be allowed to edit another user's posts. An exception to this is the viewBoard action, plugins for which can tell the code in MwfMain.pm to skip the normal access checking.

Since forum admins are always allowed to do everything by definition, authorization plugin functions are skipped for them.

Authorization plugin subtypes currently exist for the following actions (more actions might be added on request):

regUser
Registering a user account.
viewBoard
Viewing posts (called per board not post for performance reasons).
newMessage
Sending a new private message.
newTopic
Posting a new topic.
newPost
Posting a reply.
editPost
Editing/deleting a post.
attach
Uploading an attachment.

Since version 2.13.1, none of these actions except viewBoard get passed any additional parameters except the MwfMain object anymore, since always retrieving that data was in some cases a waste of performance, with almost nobody using an authz plugin. Any data needs to be retrieved through the main object now, e.g. by using the $m->paramXyz() functions to get to CGI parameters, plus additional database queries as required.

See example/MwfPlgAuthz.pm for more details and two simple examples.

AJAX

mwForum itself doesn't use AJAX much, but version 2.21.3 makes some changes to enable addons to use AJAX on the server side (or AJAJ technically, as JSON and not XML is used). The same version also introduces jQuery on the client side, which makes Javascript a lot more fun to write.

A minimal Perl script in AJAX mode would look like the following:


my ($m, $cfg, $lng, $user, $userId) = MwfMain->new($_[0], ajax => 1);
$m->printHttpHeader();
print $m->json({ foo => "bar" });
$m->finish();

A minimal jQuery snippet using AJAX would look like this:


$.post('entity_verb' + mwf.p.m_ext, { foo: 'bar' }, function (json) { ... });

Notes