Creating Modules for Phorum5
============================
This document describes the Phorum5 module system. It is targeted at
developers who want to do customization and extend the functionality
of Phorum5. Modules are the preferred way to archieve this in
Phorum5.
For much of this document, we will be talking about an example module
"foo". Of course you will not name your module "foo", but something much
more appropriate. If you're not familiar with the terms "foo" and "bar",
you can visit http://en.wikipedia.org/wiki/Metasyntactic_variable
Be sure to read at least the **CAUTIONS AND SECURITY ISSUES** section,
before making your own modules.
Table of contents:
1. Introduction
1.1 Modules
1.2 Hacks
1.3 Hooks
2. Creating your own modules
2.1 What modules are built of
2.1.1 Hook functions
2.1.2 Module information
2.1.3 Other "stuff"
2.2 Module structure
2.2.1 Single file modules
2.2.2 Multiple file modules
2.3 Supporting multiple languages
2.4 Storing message data
2.4.1 From hooks that are run before saving a message to the database
2.4.2 From other hooks
2.5 Storing user data
2.6 Creating custom URLs
2.7 Implementing settings for your module
2.8 Changing the template
2.9 Example modules
3. **CAUTIONS AND SECURITY ISSUES**
3.1 Make modules, not hacks
3.2 Reload your module if you change the module information
3.3 How to access the $PHORUM array from your hook functions
3.4 How to access additional files in your multi file module
3.5 Secure your PHP files agains hackers
3.6 Secure your pages from XSS
3.7 Prevent namespace collisions
3.7.1 (Hook) functions
3.7.2 Data stored in $PHORUM
3.7.3 Language strings stored in $PHORUM
3.7.4 Data stored in messages, users and settings
4. Overview of available Phorum hooks
4.1 Code hooks
4.2 Template hooks
5. Support
1. Introduction
-------------------------------------------------------------------------------
1.1 Modules
-----------
Modules are self contained pieces of software, that can be added to
Phorum to change or extend its functionality. Modules can do this
without having to change anything in the standard Phorum distribution
files or database structure. So installing a module means: drop in
the code, go to the admin "Modules" page, enable the module and it
works.
1.2 Hacks
---------
The moment it is neccessary to make changes to the standard Phorum
distribution files or database structure to implement some kind of
functionality, we're talking about a hack (even if the changes
that have to be made are accompanied by a drop in module).
Although there is nothing wrong with writing hacks, the Phorum team
wants to urge you to try if you can write a module before resorting
to a hack. Modules are the preferred way of modifying Phorum
functionality, because that will make both upgrading your distribution
and having your modification adopted by others easier.
1.3 Hooks
---------
Phorum uses hooks to run its modules. Hooks are points in the
application where Phorum stops and runs its data through the modules
that are configured to handle the hook. The modules can act upon and
change this data.
The following image visualizes what happens when Phorum reaches
a hook point in the application, for which two modules ("foo" and
"bar") have been configured.
Phorum
Application
(1) (1) Phorum is running.
| (2) Phorum reaches the
| hook named "some_hook".
v Phorum (3) Phorum sends data to
some_hook >----- data ------+ the module system.
(2) (3) | (4) The module "foo" is run.
v (5) The module "bar" is run.
(4) module "foo" (6) The Phorum data (which
| might be modified by the
v modules) is sent back
(5) module "bar" to Phorum.
| (7) Phorum continues running
Phorum Modified | with the modified data.
Application :
Here is a list of the types that can be used:
+--------+-----------------------------------------------------------+
| | |
+--------+-----------------------------------------------------------+
| title | This is the title for the module that is displayed in the |
| | "Modules" page of the admin interface. |
+--------+-----------------------------------------------------------+
| desc | This is the description that is displayed along with the |
| | title in the admin interface, to give a little more |
| | information about the module. Using HTML in the |
| | part is allowed. |
+--------+-----------------------------------------------------------+
| hook | This describes which hook functions are called for which |
| | Phorum hooks. The data consists of two fields, separated |
| | by a pipe "|" symbol. The first field contains the name |
| | of the hook that this module is hooking into. The second |
| | field contains the name of the hook function that will be |
| | called for the hook. |
+--------+-----------------------------------------------------------+
It is allowed to use multiple hook lines in your module information,
so your module can hook into multiple hooks. When doing this, it
is also allowed to use the same hook function for handling different
hooks in your module (asuming the hooks are compatible).
Here's an example of what the module information for our example
module "foo" might look like:
title: Foo example module
desc: This is the Foo module for Phorum. Nothing exciting...
hook: some_hook|phorum_mod_foo_some_hook
hook: some_other_hook|phorum_mod_foo_some_other_hook
hook: yet_another_hook|phorum_mod_foo_some_other_hook
So what this module info for example does, is telling Phorum that
when it gets to "some_other_hook", it will have to call the function
phorum_mod_foo_some_other_hook() in your module. It also tells
that for "yet_another_hook" the same function has to be called.
2.1.3 Other "stuff"
-------------------
Hook functions and the module information are all the parts needed
for creating a working module. However, your module might need
extra stuff like template, language and image files. You can
store these files along with your module when using the multiple
file module structure (see 2.2.2 below).
If you do not need to store any other stuff with your module, you
can also choose to use the single file (see 2.2.1 below) module
structure.
2.2 Module structure
--------------------
2.2.1 Single file modules
-------------------------
Single file modules are useful in case case no additional files have
to be distributed with your module. Because the module consist of
only one single file, it is very easy to distribute. Beware that the
moment you want to support for example a settings screen, multiple
languages or custom images, you will have to switch to the multiple
file module structure.
Single file modules consist of one single PHP file, which contains
both the module information and the hook functions. For storing the
module informaton, a special PHP comment is used. This comment must
look like the following:
/* phorum module info
*/
Using the example module info from 2.1.2, the complete single
file module would look like this (see 3.5 why we use the
check on PHORUM at the start of this file):
Installation of a single file module is done by putting the PHP
file (e.g. foo.php) directly in the directory {phorum dir}/mods/
and activating the module from the "Modules" screen in your
admin interface.
2.2.2 Multiple file modules
---------------------------
Multiple file modules are useful in case you need additional files
to be stored with your module, for example a settings screen,
language files or custom images.
Multiple file modules are stored in their own subdirectory below
the directory {phorum dir}/mods/. So if you have a module named
"foo", you will have to create a directory {phorum dir}/mods/foo/ for
storing all module files.
Inside this subdirectory, you will have to create a least two files.
The first is a file called "info.txt". This file contains the
module information for your module (see 2.1.2). The second file
is the PHP file which contains the hook functions for your module.
The basename of this file should be the same as the name of the
module subdirectory. So for our example module "foo", you will have
to create a file named "foo.php".
Using the example module info from 2.1.2, the complete multiple
file module would look like this (see 3.5 why we use the
check on PHORUM at the start of the PHP file):
info.txt:
title: Foo example module
desc: This is the Foo module for Phorum. Nothing exciting...
hook: some_hook|phorum_mod_foo_some_hook
hook: some_other_hook|phorum_mod_foo_some_other_hook
hook: yet_another_hook|phorum_mod_foo_some_other_hook
foo.php:
So far, the module has exactly same functionality as the single
file module from 2.2.1. From here on, the functionality can be
extended. Some of the possibilities are:
- Using custom files for your module (images, classes, libs, etc.);
- Letting your module support multiple languages;
- Creating a settings screen for your module;
2.3 Supporting multiple languages
---------------------------------
(this feature is only available for the multiple file module structure)
If your module includes text that will be displayed to end users,
you should strongly consider making it support multiple languages.
This will allow Phorum installations using another language to display
output of your module in the same language, instead of the language
you have written the module in.
For supporting multiple languages, the first thing to do is add the
following to your module information file (info.txt):
hook: lang|
There is no hook function configured here, because the "lang" hook
is only used as a marker for Phorum. This only tells Phorum that your
module supports multiple languages.
Next, you must provide at least one language file with your module.
Language files are stored in a subdirectory name "lang" inside your
module directory. So in our sample module, the full directory would be
{phorum dir}/foo/lang/. The language files must be named identical
to the main language files that Phorum uses. So, to include both
English and French, your module would have the following file
structure below the Phorum's mods directory:
foo/info.txt
foo/foo.php
foo/lang/english.php
foo/lang/french.php
The structure of your language files will be almost identical to that
of the main Phorum language files. However, for your own language files
it is advisable to add an extra level in the language variables, to
avoid conflicts with other modules or Phorum itself. Here is an
example of how you would do that:
Here, the extra inserted level is ["mod_foo"]. You can add as many
lines as you need for your module. To access the above language string,
from your module code you would use:
$PHORUM["DATA"]["LANG"]["mod_foo"]["Hello"]
From a template file, you would use:
{LANG->mod_foo->Hello}
In case a Phorum installation is using a language that your module
does not support, Phorum will automatically attempt to fallback to
English. So it is highly recommend that you include an english.php
language file in all your modules. If both the current language and
english.php are not found, Phorum will be unable to load a language
for your module and will display empty space instead of language
strings.
Try to reuse strings that are already in the main Phorum language
files itself. Only create custom strings when there is no alternative
available. Having more text to translate is more work for everybody,
especially the Phorum translators.
2.4 Storing message data
------------------------
If your module needs to store data along with a Phorum message,
you can make use of the meta information array that is attached
to each message ($message["meta"]). This array is a regular PHP
array, which is stored in the database as serialized data
(see http://www.php.net/serialize). Because Phorum and other modules
make use of this meta data as well, you should not squash it,
neither access the meta data in the database directly. Instead
use the methods described in this section.
Remark: because the meta data is stored as serialized data in the
database, it is not possible to include data you store in there
in SQL queries.
When storing information in the meta data from a hook function, you
can encounter two different situations, which both need a different
way of handling.
2.4.1 From hooks that are run before saving a message to the database
---------------------------------------------------------------------
There are some hooks that send a full message structure to the
hook functions, so these can change the message data before storing
the message in the database. Examples are the hooks "pre_post"
and "pre_edit". In this case you can simply update the meta
information directly. Here's an example of how this would look
in your hook function:
function phorum_mod_foo_pre_post ($message) {
$message["meta"]["mod_foo"]["foodata"] = "Some data";
$message["meta"]["mod_foo"]["bardata"] = "Some more data";
return $message;
}
Phorum will take care of storing the updated meta data in the database.
2.4.2 From other hooks
----------------------
For other hooks, the proper way to store information in the meta
data is to retrieve the current meta data using phorum_db_get_message(),
copy the meta data to a new message structure, make changes as needed
and use phorum_db_update_message() to update the message in the
database. Here is an example of how this could look in your hook
function:
function phorum_mod_foo_some_hook ($data) {
// Somehow you get the id for the message. Here we asume
// that it is stored in the $data parameter.
$message_id = $data["message_id"];
// Retrieve the current message data.
$message = phorum_db_get_message ($message_id);
// Create updated meta data.
$new_message = array("meta" => $message["meta"]);
$new_message["meta"]["mod_foo"]["foodata"] = "Some data";
$new_message["meta"]["mod_foo"]["bardata"] = "Some more data";
// Store the updated data in the database.
phorum_db_update_message($message_id, $new_message);
return $data;
}
Changing meta data for a message this way will ensure that the
existing meta data is kept intact.
2.5 Storing user data
---------------------
If your module needs to store data along with a Phorum user,
you can make use of custom profile fields. In the admin interface,
under "Custom Profiles", you can add your own profile fields
(see also docs/creating_custom_userfields.txt).
The custom profile fields will be accessible from within the user
data. E.g. if you have created a custom profile field named "foobar",
the value of that field will be stored in $user["foobar"].
When using a custom profile field for storing module information,
you can use a separate field for each piece of data you want to
store. But instead, you can also create a single field for storing
a complete array of information. Phorum will automatically take care
of storing this information (serialized) in the database. You only
should make sure that the custom profile field is large enough to
store all the data. When your module needs to store multiple fields,
this is the preferred way.
For storing data in the custom profile field, you can make use of the
phorum_user_save() function. This function needs the user_id of the
user and all fields that need to be updated. Below are two pieces of
code which show how our example module might store data for a user
(asuming $user_id is the id of the user that must be changed).
When using multiple fields "mod_foo_foodata" and "mod_foo_bardata":
$userdata = array(
"user_id" => $user_id,
"mod_foo_foodata" => "Some user data",
"mod_foo_bardata" => "Some more user data"
);
phorum_user_save($userdata);
When using a single custom field "mod_foo" for this module:
$user = phorum_user_get($user_id);
$userdata = array(
"user_id" => $user_id,
"mod_foo" => array (
"foodata" => "Some user data",
"bardata" => "Some more user data"
)
);
phorum_user_save($user);
2.6 Creating custom URLs
-------------------------
Phorum uses the function phorum_get_url() to consistenly build URLs
that point to parts of Phorum. It is recommended that you use this
function as well when creating links yourself, so special features
and future changes will automatically be incorporated in the links
you use.
Here's an example of building an URL, which will open the profile
for the user with user_id = 17:
$url = phorum_get_url(PHORUM_PROFILE_URL, 17);
The argument list that this function takes, depends on the first
argument which tells Phorum what kind of URL has to be built.
So when building other URLs, other arguments will probably
be used.
If you need to build a custom URL to link to your own module, you
can use phorum_get_url() as well. The way to go is simple. You
need to use PHORUM_CUSTOM_URL as the first argument and add all
URL building parameters to it.
The first parameter needs to be the filename of the file to link
to, without the (.php) extension. The second parameter needs to
be 0 or 1. If it is 1, the current forum_id is added to the URL.
All other parameters are added comma separated to the URL.
Here's an example of building a custom URL which links to the
file "myfile.php". The URL has to have the forum_id in it and
needs to contain the additional parameter "foo=bar":
$url = phorum_get_url(PHORUM_CUSTOM_URL, "myfile", 1, "foo=bar");
2.7 Implementing settings for your module
-----------------------------------------
(this feature is only available for the multiple file module structure)
Some modules that you write might need to store settings for later
use. For those, you can create a settings page which will be used
from within the admin interface.
The settings page must be put in your modules's directory by the
name of "settings.php". So for our example module "foo" the file
would go in {phorum dir}/mods/foo/settings.php. In the admin
interface under the option "Modules", a link to the settings.php
page will automatically be added if the settings.php file is
available for your module.
Although you can do anything you want from your settings.php script,
it is recommended that you use the tools that are handed to you
by Phorum for building pages and storing settings.
One of those tools is a PHP object "PhorumInputForm" which
can be used to create standard input forms and table displays in
the admin interface. The best example here is to look at one of the
modules that come with Phorum like "bbcode" or "replace".
Another tool is the function phorum_db_update_settings() which can
be used for storing settings in the database. To store settings using
this function you do something like the following:
$foo_settings["foodata"] = "Some setting data";
$foo_settings["bardata"] = "Some more setting data";
phorum_db_update_settings(array("mod_foo" => $foo_settings));
$foo_settings can be anything you like: an array, object, string, etc.
The first request after you have stored your settings, the setting
data for this example will be available in $PHORUM["mod_foo"].
To ensure that your settings file is only loaded from the admin
interface, place this line at the top of your settings.php file
(see also 3.5):
if(!defined("PHORUM_ADMIN")) return;
2.8 Changing the templates using template hooks
-----------------------------------------------
2.8.1 When to use a template hook
---------------------------------
Changing the templates should be avoided as much as possible when
writing a module. This will basically turn your mod into a hack,
because files have to be edited for it. Inexperienced users might
find it hard to install your module if they have to modify files
to get it to work.
If you cannot avoid changing the template, then consider to use
template hooks for this. You can use these if your template change
involves adding extra code to a template. The advantage is that
there's only little code that has to be added to the templates,
which makes things less confusing to users that want to install the
module.
2.8.2 How to use a template hook
--------------------------------
To create a template hook, you do the following:
* Add "{HOOK tpl_some_hook}" to the template at an appropriate spot;
* Put "hook: tpl_some_hook|phorum_mod_foo_tpl_some_hook" in your
module info;
* Create the hook function phorum_mod_foo_tpl_some_hook() that
prints out the code that has to be placed at the position of
the "{HOOK tpl_some_hook}" code the the template.
If you want to pass on the data from template variables to
the hook function, you can simply add the variables to the hook
definition in the template. Example:
{HOOK tpl_some_hook DATA1 DATA2}
The hook function will get the contents of these variables passed in
a single array. This can for example be useful if your template hook
needs access to loop data. Example:
{LOOP MESSAGES}
...
{HOOK tpl_some_hook MESSAGES}
...
{/LOOP MESSAGES}
2.8.3 Preventing collisions in hook names
-----------------------------------------
You can use any name for "tpl_some_hook", but beware that your
name does not collide with an already existing hook name.
The easiest way to do this is use the techniques from section 3.7.
As a rule of thumb, you can use the following format:
tpl_mod__
Example: If a buttonbar is added in one of the templates for a module
named "foo", the name for the hook could be "tpl_mod_foo_buttonbar".
2.9 Example modules
-------------------
The best way of learning how to write modules is probably looking
at existing module code. In your Phorum distribution's docs directory,
you will find the directory example_mods. This directory contains a
couple of example modules, demonstrating the features described in this
document. The modules have no real functional purpose, but they might
be easier to read than the real Phorum modules.
3. **CAUTIONS AND SECURITY ISSUES**
-------------------------------------------------------------------------------
3.1 Make modules, not hacks
---------------------------
Making modules that require database changes are discouraged and may
not be accepted as an approved module. We want modules to be as
transparent as possible for upgrades. Please attempt to store your
data in the proper place. See chapter 2 for more information on that.
3.2 Reload your module if you change the module information
-----------------------------------------------------------
If you are changing the module info for a module that is already
activated in your Phorum installation, you must deactivate and
reactivate it to have Phorum reload the changed information. For
performance reasons the module information is only read when the
module is activated.
If you have added a new hook function to your module and it seems
not to be run, it probably is because you did not do this.
3.3 How to access the $PHORUM array from your hook functions
------------------------------------------------------------
The $PHORUM array is in the global scope. From inside a function,
you can not directly access this array. So you will have to import
the $PHORUM array into your function scope. The Phorum team
recommends the following method for doing this (check out the
faq.txt to see why we do not use the "global" keyword):
function phorum_mod_foo_some_hook ($data) {
$PHORUM = $GLOBALS["PHORUM"];
// Do stuff for "some_hook".
return $data;
}
3.4 How to access additional files in your multi file module
------------------------------------------------------------
All standard Phorum pages are run from the Phorum installation
directory. The hook functions that you write also work from
the same directory. So if you want to access files in your module
directory, you will have to specify the relative path to those
files. This path looks like:
./mods//
So let's say that our module "foo" has a subdirectory "images"
which contains "bar.gif", then we could display that image
using the HTML code:
Another example: let's say that there is a function library
named "my_module_functions.php" in the module, which must be
included from then module code, then this is done using:
include("./mods/foo/my_module_functions.php");
3.5 Secure your PHP files agains hackers
----------------------------------------
To prevent hackers from loading your PHP module files directly, you
should add the following to the start of your PHP files:
if(!defined("PHORUM")) return;
This will make sure that the file will only work when loaded from
the Phorum application. If you are writing pages that are loaded
from the admin interface (like a settings screen for your module),
then use the following line instead:
if(!defined("PHORUM_ADMIN")) return;
This will make sure that the file will only work when loaded from
the Phorum admin interface.
3.6 Secure your pages from XSS
------------------------------
XSS stands for cross site scripting. This means that hackers
can feed HTML data to your application, which is displayed on
screen without stripping or escaping the HTML data. This way
it can be possible for hackers to feed malicous javascript
code into the browser of users on the forum, causing a security
risk. If you want to learn more about XSS, please visit
http://en.wikipedia.org/wiki/XSS
To prevent XSS security holes, you must take care that all
user input is properly sanitized before displaying it on screen.
Sanitizing can be done by either stripping all HTML from
the data (e.g. using http://www.php.net/strip_tags) or by escaping
all html characters (using http://www.php.net/htmlspecialchars).
Example:
If your module needs to display the username for a user on
screen, it must not simply do:
print $user["username"];
Instead you must use:
print htmlspecialchars($user["username"]);
It's not only for security that you have to sanitize data before
displaying it. You must use htmlspecialchars() to prevent some other
possible problems as well. Imagine you have a user with the username
"ob". Without htmlspecialchars() the username would be interpreted
as HTML code, possibly making the full page bold from the username on.
3.7 Prevent namespace collisions
--------------------------------
When creating modules, you must always be aware that you are
working in the same namespace as other modules and Phorum itself.
This means that there is a risk of duplicate use of function
and variable names. By following a couple of simple rules, you
can greatly reduce this risk.
3.7.1 (Hook) functions
----------------------
Always construct names for your module functions like this:
phorum_mod__
So if you are writing functions for a module named "foo", all
function names will look like:
phorum_mod_foo_
You can use whatever you like for the part. When writing
a hook function, it is recommended to use the name of the hook for
which you are writing the function (this will make clear what the
function does, without having to check the module info). So in case
you are writing a hook function for the hook "some_hook", the full
function name would be:
phorum_mod_foo_some_hook
If your hook function handles multiple hooks at once, then
simply use one of the hook's names as the or make up
something yourself.
3.7.2 Data stored in $PHORUM
----------------------------
When storing data in $PHORUM, always prepend the array key name
with mod_. If your module is named "foo", do not use:
$PHORUM["mydata"]
but instead:
$PHORUM["mod_foo_mydata"]
3.7.3 Language strings stored in $PHORUM
----------------------------------------
When storing your custom language strings, do not put them directly
in $PHORUM["DATA"]["LANG"] like Phorum does, because that might
result in conflicting language strings. Instead add an extra data level,
which makes sure that your module keeps all language strings to itself.
If your module is named "foo", you should store language strings in:
$PHORUM["DATA"]["LANG"]["mod_foo"]
See also section 2.3.
3.7.4 Data stored in messages, users and settings
-------------------------------------------------
When using the Phorum provided ways of storing data in messages,
users and settings, always prepend the data key with
mod_. SO if your module is named "foo", do not use
things like:
$new_message["meta"]["foodata"] = "Some data";
$user["foodata"] = "Some data";
phorum_db_update_settings(array("settings" => $foo_settings));
but instead:
$new_message["meta"]["mod_foo_foodata"] = "Some data";
$user["mod_foo_foodata"] = "Some data";
phorum_db_update_settings(array("mod_foo" => $foo_settings));
See also sections 2.4 (message data), 2.5 (user data) and 2.7 (settings).
4. Overview of available Phorum hooks
-------------------------------------------------------------------------------
In this chapter you will find an overview of all available Phorum
hooks and a description of what they do.
Remarks:
* Input is what your module function should expect as parameters.
* Return is what your module function should return. Most hooks
expect the same data structure as was sent. For those items,
the Return is listed simply as "Same as Input".
* Normally, hook functions are allowed to modify the data that was
sent as input. If this is not allowed, the input data will be
flagged as read-only.
* In most cases the hook description will provide only one or more
of the possible uses for the hook. The full leverage of each hook
is only limited by the imagination of the module writer (it's
as much a cliche as it is true).
* It may be that you need to hook into a spot where there is
currently no hook available. If that is the case, let the dev
team know by posting a message in the development forum
on phorum.org, explaining where and why you need an extra hook.
Hooks will be added as neccessary, especially while Phorum 5
is young.
4.1 Code hooks
--------------
Code hooks are hooks that are called from within the Phorum core
code. These hooks are typically used for modifying Phorum's internal
datastructures.
----------------------------------------------------------------------------
admin_general
Where : admin interface
When : Right before the PhorumInputForm object is shown.
Input : The PhorumInputForm object.
Return : Same as Input
This hook can be used for adding items to the form on the
"General Settings" page of the admin interface.
----------------------------------------------------------------------------
admin_file_purge
Where : admin interface, option "Purge Stale Files"
When : Right before stale files are deleted from the database.
Input : An array, containing a description of all stale files.
Return : Same as Input
The primary use of this hook would be to cleanup stale files, created
by an alternate storage system for attachments (see after_attach and
after_detach as well). The array that is passed on to the hook function
contains arrays, which contain the following fields:
file_id : Internal id to reference the file.
filename : Name of the file.
filesize : Filesize in KB.
add_datetime : Epoch timestamp for the time the file was created.
reason : A description why this file is considered to be stale.
----------------------------------------------------------------------------
after_attach
Where : include/posting/action_attachments.php
When : Just after a file attachment is saved in the database
Input : Two part array where the first element is the message array and
the second element is a file array that contains the name, size,
and file_id of the newly saved file.
Return : Same as Input
The primary use of this hook would be for creating an alternate storage
system for attachments. You would need to use the before_attach hook to
remove the file data and in this hook it could be saved properly. You will
need to use the file hook to retreive the file data later.
----------------------------------------------------------------------------
after_detach
Where : include/posting/action_attachments.php
When : Just after a file attachment is deleted from the database
Input : Two part array where the first element is the message array and
the second element is a file array that contains the name, size,
and file_id of the deleted file.
Return : Same as Input
The primary use of this hook would be for creating an alternate storage
system for attachments. Using this hook, you can delete the file from
your alternate storage.
----------------------------------------------------------------------------
after_header
Where : Every page, except for the admin interface pages
When : Right after the header is displayed.
Input : none
Return : none
This hook can be used for creating content at the end of the header,
just before the main content is displayed.
----------------------------------------------------------------------------
after_login
Where : login.php
When : After a successful login, just before redirecting the
user to a Phorum page.
Input : The redirection URL.
Return : Same as Input
This hook can be used for performing tasks after a successful user
login and for changing the page to which the user will be redirected
(by returning a different redirection URL). If you need to access the
user data, then you can do this through the global $PHORUM variable.
The user data will be in $PHORUM["user"].
----------------------------------------------------------------------------
after_logout
Where : login.php
When : After a logout, just before redirecting the user to
a Phorum page.
Input : The redirection URL.
Return : Same as Input
This hook can be used for performing tasks after a successful user
logout and for changing the page to which the user will be redirected
(by returning a different redirection URL). The user data will still
be availbale in $PHORUM["user"] at this point.
----------------------------------------------------------------------------
after_message_save
Where : action_post.php
When : After storing a new message and all database updates are done.
Input : Array containing message data.
Return : Same as Input
This hook can be used for performing actions based on what the message
contained or altering it before it is emailed to the subscribed users.
It is also useful for adding or removing subscriptions.
----------------------------------------------------------------------------
after_register
Where : register.php
When : Right after a successful registration of a new user is done
and all confirmation mails are sent.
Input : Array containing the user data of the user (read-only).
Return : Same as Input
This hook can be used for performing tasks (like logging and
notification) after a successful user registration.
----------------------------------------------------------------------------
before_attach
Where : include/posting/action_attachments.php
When : Just before a file attachment is saved in the database
Input : Two part array where the first element is the message array and
the second element is a file array that contains the name, size
and data.
Return : Same as Input
The primary use of this hook would be for creating an alternate storage
system for attachments. You would need to use the after_attach hook to
complete the process as you do not yet have the file_id for the file. You
will need to use the file hook to retreive the file data later.
----------------------------------------------------------------------------
before_editor
Where : posting.php
When : Just before the message editor is displayed.
Input : Array containing data for the message that will be shown
in the editor screen.
Return : Same as Input
This hook can be used for changing message data, just before the editor
is displayed. This is done after escaping message data for XSS prevention
is done. So in the hook, the module writer will have to be aware that
data is escaped and that he has to escape data himself if needed.
This hook is called every time the editor is displayed. If modifying
the message data does not have to be done on every request (for example
only on the first request when replying to a message), the module will
have to check the state the editor is in. Here's some hints on what
you could do to accomplish this:
* Check the editor mode: this can be done by looking at the "mode" field
in the message data. This field can be one of "post", "reply" and "edit".
* Check if it's the first request: this can be done by looking at the
$_POST array. If no field "message_id" can be found in there, the
editor is handing the first request.
Using this, an example hook function that appends the string "FOO!"
to the subject when replying to a message (how useful ;-) could look
like this:
function phorum_mod_foo_before_editor ($data)
{
if ($data["mode"] == "reply" && ! isset($_POST["message_id])) {
$data["reply"] = $data["reply"] . " FOO!";
}
return $data;
}
Beware: this hook function only changes message data before it is
displayed in the editor. From the editor, the user can still change
the data. Therefore, this hook cannot be used to control the data which
will be stored in the database. If you need that functionality, then
use the hooks pre_edit and/or pre_post instead.
----------------------------------------------------------------------------
before_footer
Where : Every page, except for the admin interface pages
When : Right before the footer is displayed.
Input : none
Return : none
This hook can be used for creating content at the end of the main
content, just before the footer. It can also be used for
performing tasks that have to be executed at the end of each page.
----------------------------------------------------------------------------
before_register
Where : register.php
When : Right before a new user is stored in the database.
Input : Array containing the user data of the user.
Return : Same as Input
This hook can be used for performing tasks before user registration.
This hook is useful if you want to add some data to or change some
data in the user data and to check if the user data is correct.
When checking the registration data, the hook can set the "error" field
in the returned user data array. When this field is set after running
the hook, the registration processed will be halted and the error
will be displayed. If you created a custom form field "foo" and you
require that field to be filled in, you could create a hook function
which looks like this:
function phorum_mod_foo_before_register ($data)
{
$myfield = trim($data['your_custom_field']);
if (empty($myfield)) {
$data['error'] = 'You need to fill in my custom field';
}
return $data;
}
The error must be safely HTML escaped, so if you use untrusted data in
your error, then make sure that it is escaped using htmlspecialchars()
to prevent XSS (see also paragraph 3.6: Secure your pages from XSS).
----------------------------------------------------------------------------
buddy_add
Where : pm.php
When : Right after a buddy has been added successfully.
Input : The user id of the buddy that has been added.
Return : Same as Input
This hook can be used for performing actions after a buddy has been
added for a user (e.g. sending the new buddy a PM about this event,
update popularity counters, do logging, synchronizing with other
databases, etc.).
----------------------------------------------------------------------------
buddy_delete
Where : pm.php
When : Right after a buddy has been deleted successfully.
Input : The user id of the buddy that has been deleted.
Return : Same as Input
This hook can be used for performing actions after a buddy has
been deleted for a user.
----------------------------------------------------------------------------
cc_save_user
Where : control.php
When : Right before data for a user is saved in the control panel.
Input : Array containing the user data to save.
Return : Same as Input
This hook works the same way as the before_register hook, so you can
also use it for changing and checking the user data that will be
saved in the database. There's one difference. If you want to
check a custom field, you'll also need to check the panel which
you are on, because this hook is called from multiple panels.
The panel that you are on, will be stored in the 'panel' field
of the user data.
If you have added a custom field to the template for the option
"Edit My Profile" in the control panel, your hook function will
look like this:
function phorum_mod_foo_cc_save_user ($data)
{
// Only check data for the panel "user".
if ($data['panel'] != "user") return $data;
$myfield = trim($data['your_custom_field']);
if (empty($myfield)) {
$data['error'] = 'You need to fill in my custom field';
}
return $data;
}
----------------------------------------------------------------------------
check_post
Where : post.php
When : Right after performing preliminary posting checks, unless
these checks have returned something bad.
Input : Array containing:
0 => the $_POST array with form data
1 => $error, to return errors in
Return : Same as Input
This hook can be used for modifying data in the $_POST array and for
running additional checks on the data. If an error is put in $error,
Phorum will stop posting the message and show the error to the user
in the post-form.
Beware that $error can already contain an error on input, in case
multiple modules are run for this hook. Therefore you might want to
return immediately in your hook function in case $error is already
set.
Below is an example of how a function for this hook could look.
This example will disallow the use of the word "bar" in the
message body.
function phorum_mod_foo_check_post ($args) {
list ($message, $error) = $args;
if (!empty($error)) return $args;
if (stristr($message["body"], "bar") !== false) {
return array($message, "The body may not contain 'bar'");
}
return $args;
}
----------------------------------------------------------------------------
close_thread
Where : moderation.php
When : Right after a thread has been closed by a moderator.
Input : The id of the thread that has been closed (read-only).
Return : Same as Input
This hook can be used for performing actions like sending notifications
or making log entries after closing threads.
----------------------------------------------------------------------------
common
Where : common.php, so in practice every page
When : Right before the end of the common.php include script.
Input : none
Return : none
This hook can be used for applying custom settings or altering
Phorum settings based on external parameters.
----------------------------------------------------------------------------
common_no_forum
Where : common.php, so in practice every page
When : Right after no forum settings were found, before doing the redirect
Input : none
Return : none
This hook can be used for returning some other message (i.e. a 404-page)
to the visitor if the requested forum was not found.
----------------------------------------------------------------------------
common_post_user
Where : common.php, so in practice every page
When : Right after loading the user from the database, but just
before making descisions on language and template.
Input : none
Return : none
This hook can be used for applying custom settings or altering
Phorum settings based on external parameters.
----------------------------------------------------------------------------
common_pre
Where : common.php, so in practice every page
When : Right after loading the settings from the database, but just
before making descisions on language, template and user.
Input : none
Return : none
This hook can be used for applying custom settings or altering
Phorum settings based on external parameters.
----------------------------------------------------------------------------
delete
Where : moderation.php
When : Right after deleting a message from the database.
Input : Array of ids for messages that have been deleted (read-only).
Return : none
This hook can be used for cleaning up anything you may have created
with the post_post hook or any other hook that stored data tied to
messages.
----------------------------------------------------------------------------
external
The external hook functions are never called from any of the standard
Phorum pages. These functions are called by invoking script.php on the
command line with the --module parameter. This can be used to pipe
output from some arbitrary command to a specific module, which can do
something with that input. If your module does not need any command
line input and is meant to be run on a regular basis, you should
consider using the scheduled hook.
Mind that for using an external hook, the module in which it is
handled must be enabled in your admin interface. So if an external
hook is not running, the containing module might be disabled.
To run the external hook from the command line, you have to be in
the phorum installation directory. So running the external hook of
a module named "external_foo" would be done like this on a UNIX
system prompt:
# cd /your/phorum/dir
# php ./script.php --module=external_foo
For easy use, you can of course put these commands in a script file.
----------------------------------------------------------------------------
failed_login
Where : login.php
When : When a user login fails.
Input : An array containing three fields: username, password and location.
The location field specifies where the login failure occurred and
its value can be either "forum" or "admin".
Return : Same as Input
This hook can be used for tracking failing login attempts. This can be used
for things like logging or implementing login failure penalties (like
temporary denying access after X login attempts).
----------------------------------------------------------------------------
file
Where : file.php
When : When attachments are requested.
Input : Two part array where the first element is the mime type already
detected by file.php and the second part is the file array that
contains the filename, file_data, filesize, etc.
Return : Same as Input
This hook could be used to count file downloads, or along with after_attach
an alternate file data storage mechanism could be created.
----------------------------------------------------------------------------
format
Where : phorum_format_messages() in include/format_functions.php
When : Everytime phorum_format_messages() is called for formatting
a message, just before it is sent to the templates.
Input : Array of messages.
Return : Same as Input
This hook can be used for applying custom formatting to messages. The
message fields that are most applicable for this are "body" and "author".
When writing a module using this hook, you probably want to format
those fields. In practice you can apply formatting to all the fields
you want.
The changes you make to the messages are for displaying purposes
only, so the changes are not stored in the database.
----------------------------------------------------------------------------
hide
Where : moderation.php
When : Right after a message has been hidden by a moderator.
Input : The id of the message that has been hidden (read-only).
Return : Same as Input
This hook can be used for performing actions like sending notifications
or making log entries after hiding a message.
----------------------------------------------------------------------------
index
Where : include/index_new.php and include/index_classic.php
When : Right before the list of forums is displayed.
Input : Array of forums.
Return : Same as Input
This hook can be used for changing or adding data to the forums
in the list.
----------------------------------------------------------------------------
lang
The lang hook is a only a 'marker'. It flags Phorum that your module
supports multiple languages. It does not take a hook function in
your module information. If you do define a hook function, it will
never be called.
Read section 2.3 for information on the use of multiple languages.
----------------------------------------------------------------------------
list
Where : list.php
When : Right before the messages are formatted and displayed.
Input : Array of threads (or messages in threaded mode).
Return : Same as Input
This hook can be used for changing or adding data to the messages
in the list.
----------------------------------------------------------------------------
moderation
Where : moderation.php
When : At the start of moderation.php
Input : The id of the moderation step which is run (read-only).
Return : none
This hook can be used for logging moderator actions. You can
use the $PHORUM-array to retrieve additional info like the
moderating user's id and similar.
The moderation step id is the variable $mod_step that is used in
moderation.php. Please read that script to see what moderation
steps are available and for what moderation actions they stand.
When checking the moderation step id for a certain step, always use
the contstants that are defined for this in include/constants.php.
The numerical value of this id can change between Phorum releases.
----------------------------------------------------------------------------
move_thread
Where : moderation.php
When : Right after a thread has been moved by a moderator.
Input : The id of the thread that has been moved (read-only).
Return : none
This hook can be used for performing actions like sending notifications
or for making log entries after moving a thread.
----------------------------------------------------------------------------
pm_sent
Where : include/controlcenter/pm.php
When : Right after a PM and its email notifications have been sent.
Input : Array containing the private message data (read-only).
Return : none
This hook can be used for performing actions after sending a PM. Before
PM notification by email was put in the Phorum core, this hook was
used to send those notifications.
----------------------------------------------------------------------------
post_edit
Where : include/moderation_functions.php
When : Right after storing an edited message in the database.
Input : Array containing message data (read-only).
Return : none
This hook can be used for sending notifications or for making log entries
in the database when editing takes place.
----------------------------------------------------------------------------
post_post
Where : post.php
When : After all posting work is done and just before the user is
redirected back to the list.
Input : Array containing message data (read-only).
Return : none
This hook can be used for performing actions based on what the message
contained.
----------------------------------------------------------------------------
posting_custom_action
Where : posting.php
When : Right after all the initialization tasks are done and
just before the posting script starts its own action processing.
Input : Array containing message data.
Return : Same as Input
This hook can be used by modules to handle (custom) data coming from the
posting form. The module is allowed to change the data that is in the
input message. When a module needs to change the meta data for a
message, then this is the designated hook for that task.
----------------------------------------------------------------------------
posting_init
Where : posting.php
When : Right after the posting.php script's configuration setup and
before starting the posting script processing.
Input : none
Return : none
This hook can be used for doing modifications to the environment
of the posting scripts at an early stage. One of the intended purposes
of this hook is to give mods a chance to change the configuration
of the posting fields in $PHORUM["posting_fields"].
----------------------------------------------------------------------------
posting_permission
Where : posting.php
When : Right after Phorum has determined all abilities that apply
to the logged in user.
Input : none
Ouput : none
This hook can be used for setting up custom abilities and permissions
for users, by updating the applicable fields in $GLOBALS["PHORUM"]["DATA"]
(e.g. for giving certain users the right to make postings sticky, without
having to make the full moderator for a forum).
Read the code in posting.php before this hook is called to find out
what fields can be used.
Beware: Only use this hook if you know what you are doing and understand
Phorum's editor permission code. If used wrong, you can open up security
holes in your Phorum installation!
----------------------------------------------------------------------------
pre_edit
Where : include/moderation_functions.php
When : Right before storing an edited message in the database.
Input : Array containing message data.
Return : Same as Input
This hook can be used for changing the message data before storing it
in the database.
----------------------------------------------------------------------------
pre_post
Where : post.php
When : Right before storing a new message in the database.
Input : Array containing message data.
Return : Same as Input
This hook can be used for changing the message data before storing it
in the database.
----------------------------------------------------------------------------
profile
Where : profile.php and include/controlcenter/summary.php
When : Right before a user profile is displayed.
Input : Array containing user profile data.
Return : Same as Input
This hook can be used for making changes to the profile data. This
is for displaying purposes only, so the changes are not stored in the
database.
----------------------------------------------------------------------------
quote
Where : reply.php, read.php (for inline reply form support)
When : Right after the message to reply to has been loaded.
Input : Array containing:
0 => The message author
1 => The message body
Return : The quoted body to use in the post form.
When quoting a message for reply, by default Phorum formats quoted
messages using an old school email style of quoting. By using the quote
hook, you can implement a different quoting mechanism.
Your hook function will retrieve an array containing two elements:
the author and the body of the message to be quoted. The return
value for your hook function must be the quoted body that will
be pre-filled into the reply form.
The BBCode module that is distributed with Phorum has a quote hook
function. Because it does not make sense to have more than one quote
hook active, the BBCode module has an option to disable its quote hook
function. You need to make sure that its quote hook function is disabled
when using your own quote hook.
----------------------------------------------------------------------------
read
Where : read.php
When : Right before messages are formatted for displaying.
Input : Array of messages.
Return : Same as Input
This hook can be used for making changes to the message data when
reading messages. This is for displaying purposes only, so the
changes are not stored in the database.
----------------------------------------------------------------------------
read_user_info
Where : read.php post.php include/moderation_functions.php
When : Right after retrieving user data.
Input : Array of users.
Return : Same as Input
This hook can be used for changing information for the users before
being displayed. For example: add a border around user signatures.
This is for displaying purposes only, so the changes are not stored
in the database. This hook modifies the MESSAGES->user (in read.tpl)
and MESSAGE->user (read_threads.tpl) data that is available in the
templates.
----------------------------------------------------------------------------
readthreads
Where : read.php
When : At the start of the threaded read handling, just before
sorting and displaying the threads.
Input : Array of messages.
Return : Same as Input
This hook does exactly the same as the read hook, except that this
one is only applied to messages when viewing the message list in
threaded mode.
----------------------------------------------------------------------------
reopen_thread
Where : moderation.php
When : Right after a thread has been reopened by a moderator.
Input : The id of the thread that has been reopened (read-only).
Return : Same as Input
This hook can be used for performing actions like sending notifications
or making log entries after reopening threads.
----------------------------------------------------------------------------
report
Where : report.php
When : Just before a reported message is sent to the moderators.
Input : Array with maildata (see report.php for the exact contents).
Return : Same as Input
This hook can be used for changing the report data that will be
sent to the moderators or for performing actions like making log
entries.
----------------------------------------------------------------------------
sanity_checks
Where : include/admin/sanity_checks.php
When : Just before the admin interface's sanity checks are run
Input : Array with sanity checks. Each sanity check is an array with:
function => The function that runs the sanity check
description => A description to show in the admin interface
Return : Same as Input
This hook can be used to add custom sanity checks to the admin
interface option "System Sanity Checks".
Each checking function is expected to return an array containing
two elements:
[0] A status, which can be one of
PHORUM_SANITY_OK No problem found
PHORUM_SANITY_WARN Problem found, but no fatal one
PHORUM_SANITY_CRIT Critical problem found
[1] A description of the problem that was found or NULL.
A general checking function looks like this:
function check_foo() {
$check_ok = ...some check...;
if (!$check_ok) {
return array(PHORUM_SANITY_CRIT, "Foo went wrong because ...");
} else {
return array(PHORUM_SANITY_OK, NULL);
}
}
----------------------------------------------------------------------------
scheduled
Scheduled hook functions are similar to external ones, except these
functions do not require any input from the command line. The modules
containing a scheduled hook are invoked by running script.php with
the --scheduled argument (no module name is taken; this argument
will run all scheduled hooks for all available modules).
Like the name of the hook already suggests, this hook can be used for
creating tasks which have to be executed on a regular basis. To
archieve this, you can let script.php run from a scheduling
service (like a cron job on a UNIX system).
In general, scheduled hooks are used for automating tasks you want
to execute without having to perform any manual action. Practical
uses for a scheduled hook could be housekeeping (cleanup of
stale/old data), daily content generation (like sending daily digests
containing all posted messages for that day) or forum statistics
generation.
Mind that for using a scheduled hook, the module in which it is
handled must be enabled in your admin interface. So if a scheduled
hook is not running, the containing module might be disabled.
To run the scheduled hook from the command line or from a scheduling
service, you have to be in the phorum installation directory. So
running the scheduled hooks for your Phorum installation would
be done like this on a UNIX system prompt:
# cd /your/phorum/dir
# php ./script.php --scheduled
When creating a scheduling service entry for running this
automatically, then remind to change the directory as well.
You might also have to use the full path to your PHP binary
(/usr/bin/php or whatever it is on your system), because
the scheduling service might not know the path to it. An entry
for the cron system on UNIX could look like this:
0 0 * * * cd /your/phorum/dir && /usr/bin/php ./script.php --scheduled
Please refer to your system's documentation to see how to
use your system's scheduling service.
----------------------------------------------------------------------------
search
Where : search.php
When : Right before messages are formatted for displaying.
Input : Array of messages.
Return : Same as Input
This hook can be used for making changes to the message data when
searching for messages. This is for displaying purposes only, so the
changes are not stored in the database.
----------------------------------------------------------------------------
search_action
Where : search.php
When : Right before the search request to the database
Input : Array of search-request data and a continue flag
Return : Same as Input, continue flag can be set to 0 to avoid the db-call
This hook can be used for using another search application / layer instead
of the buildin one.
----------------------------------------------------------------------------
send_mail
Where : include/email_functions.php in the function phorum_email_user()
When : Right before email is sent using PHP's mail() function.
Input : Array with maildata (read-only) containing:
addresses => Array of e-mail addresses,
from => The sender address,
subject => The mail subject,
body => The mail body,
bcc => Whether to use Bcc for mailing multiple recipients
Return : true/false - see description
This hook can be used for implementing an alternative mail sending
system (e.g. like the SMTP module does). The hook should return true if
Phorum should still send the mails himself. If you do not want to have
Phorum send the mails also, return false.
----------------------------------------------------------------------------
user_check_login
Where : include/users.php
When : Whenever phorum_user_check_login() is called.
Input : Array containing:
username => the username to check
password => the password to check
user_id => empty value
Return : Same as input
This hook can be used to check user authentication against an external
source. If the hook decides that the username and password are okay,
then it can set the user_id field to the user_id of the authenticated
user. If the hook wants to let Phorum use its standard authentication
mechanism, then it can set user_id to FALSE.
Here is an example (not too useful) hook function:
function phorum_mod_foo_user_check_login($login)
{
// Logging in with john / doe will authenticate the user with
// user_id 1234.
if ($login["username"] == "john" && $login["password"] == "doe") {
$login["user_id"] = 1234;
}
// For all other users, we let Phorum do the authentication.
else
{
$login["user_id"] = FALSE;
}
return $login;
}
Mind that when returning a user_id using this hook, the user data for
the user must be available in Phorum's users table. This hook only
handles the authentication step. So if this hook is used for
authenticating against an external source, somehow the user data
has to be put in Phorum's users table. There are multiple ways of
handling this. Here's two of them:
1) Synchronize the data from the external system when it has changed
there. So if a user is created, changed or deleted on the
external system, that system has to update the Phorum users
table with the new information. The advantage of this method, is
that the users table only is updated when changes occur and that
the users table is always up-to-date with the latest information.
2) Synchronize the data on-the-fly from the user_check_login hook.
As long as the user data is available before that hook has
ended, it will be okay for Phorum. The advantage of this method
is that updating the user data is done in an easy way at the
moment a user logs into Phorum. Disadvantages are that the user
data is only updated at the moment the user logs in and that a
user in only known to Phorum after the first time logging in.
Here's an example of how a hook function that uses on-the-fly
updates could look:
function phorum_mod_foo_user_check_login($login)
{
// Check the authentication against the external system.
$username = $login["username"];
$password = $login["password"];
if (! externalsystem_checkauth($username, $password)) {
return $login;
}
// Synchronize the external system with Phorum's users table.
$externaluser = externalsystem_getuser($username);
...code to put $externaluser in Phorum's user table ...
...and determine the Phorum $user_id to return ...
// Set the user_id to tell Phorum which user logged in.
$login["user_id"] = $user_id;
return $login;
}
----------------------------------------------------------------------------
user_list
Where : include/users.php include/controlcenter/groupmod.php
When : Whenever phorum_user_get_list() is called.
Input : Array containing:
=>
Where is an array containing:
username => the username for the user
displayname => the way to display the username
Return : Same as Input
This hook can be used for reformatting the list of users in some
way, such as changing the sort order or changing the format of
the displayed names.
4.2 Template hooks
------------------
Template hooks are called from within Phorum's template files.
These hooks can be used to extend the user interface with custom
elements. From a hook function for one of these template hooks, the
module writer can print the HTML code that has to be added to the
interface at the postition of the hook call in the template.
----------------------------------------------------------------------------
tpl_editor_after_subject
Where : posting_messageform.tpl
When : After the Subject: field in the message editor.
Input : none
Return : none
This hook can be used to add custom form fields to the message editor.
In the default template, the hook is run from within a two column table.
Column one contains the labels and column two the form fields. So your
hook function for adding a field to the editor could look like this:
function phorum_mod_foo_tpl_editor_after_subject()
{
$value = isset($_POST["mod_foo"]) ? $_POST["mod_foo"] : "";
?>