Rev 831 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
Creating Modules for Phorum5============================This document describes the Phorum5 module system. It is targeted atdevelopers who want to do customization and extend the functionalityof Phorum5. Modules are the preferred way to archieve this inPhorum5.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 muchmore appropriate. If you're not familiar with the terms "foo" and "bar",you can visit http://en.wikipedia.org/wiki/Metasyntactic_variableBe sure to read at least the **CAUTIONS AND SECURITY ISSUES** section,before making your own modules.Table of contents:1. Introduction1.1 Modules1.2 Hacks1.3 Hooks2. Creating your own modules2.1 What modules are built of2.1.1 Hook functions2.1.2 Module information2.1.3 Other "stuff"2.2 Module structure2.2.1 Single file modules2.2.2 Multiple file modules2.3 Supporting multiple languages2.4 Storing message data2.4.1 From hooks that are run before saving a message to the database2.4.2 From other hooks2.5 Storing user data2.6 Creating custom URLs2.7 Implementing settings for your module2.8 Changing the template2.9 Example modules3. **CAUTIONS AND SECURITY ISSUES**3.1 Make modules, not hacks3.2 Reload your module if you change the module information3.3 How to access the $PHORUM array from your hook functions3.4 How to access additional files in your multi file module3.5 Secure your PHP files agains hackers3.6 Secure your pages from XSS3.7 Prevent namespace collisions3.7.1 (Hook) functions3.7.2 Data stored in $PHORUM3.7.3 Language strings stored in $PHORUM3.7.4 Data stored in messages, users and settings4. Overview of available Phorum hooks4.1 Code hooks4.2 Template hooks5. Support1. Introduction-------------------------------------------------------------------------------1.1 Modules-----------Modules are self contained pieces of software, that can be added toPhorum to change or extend its functionality. Modules can do thiswithout having to change anything in the standard Phorum distributionfiles or database structure. So installing a module means: drop inthe code, go to the admin "Modules" page, enable the module and itworks.1.2 Hacks---------The moment it is neccessary to make changes to the standard Phorumdistribution files or database structure to implement some kind offunctionality, we're talking about a hack (even if the changesthat have to be made are accompanied by a drop in module).Although there is nothing wrong with writing hacks, the Phorum teamwants to urge you to try if you can write a module before resortingto a hack. Modules are the preferred way of modifying Phorumfunctionality, because that will make both upgrading your distributionand having your modification adopted by others easier.1.3 Hooks---------Phorum uses hooks to run its modules. Hooks are points in theapplication where Phorum stops and runs its data through the modulesthat are configured to handle the hook. The modules can act upon andchange this data.The following image visualizes what happens when Phorum reachesa hook point in the application, for which two modules ("foo" and"bar") have been configured.PhorumApplication(1) (1) Phorum is running.| (2) Phorum reaches the| hook named "some_hook".v Phorum (3) Phorum sends data tosome_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 thev modules) is sent back(5) module "bar" to Phorum.| (7) Phorum continues runningPhorum Modified | with the modified data.Application <---- data ------+(7) (6)||v2. Creating your own modules-------------------------------------------------------------------------------2.1 What modules are built of-----------------------------2.1.1 Hook functions--------------------A module contains one or more PHP functions that act as hookfunctions. Hook functions will receive some data in a variablefrom Phorum and have to return the (possibly modified) data, whichwill then go either back to Phorum or to the input of another modulewhich also handles the same hook (see 1.3). So the most basic (anduseless :-) hook function you could write would look somewhat like this(see 3.7 for an explanation of the naming of the function):function phorum_mod_foo_some_hook ($data) {return $data;}The exact nature of the data that is sent to the hook functionsdepends solely on the hook that is run. In chapter 4 of this documentyou will find a description of all supported hooks, including aspecification of the type of data that is sent.2.1.2 Module information------------------------For each hook that you want to handle in your module, you will haveto point the module system to the function in your module that willhandle the hook. Together with some other information, used fordescribing the module, this is stored in the module information.The module information acts as the glue between Phorum and yourmodule.Module information is formatted using lines of plain text. Each linecontains a bit of information about the module. The general formatof the lines in the module information is:<type>: <data>Here is a list of the types that can be used:+--------+-----------------------------------------------------------+| <type> | <data> |+--------+-----------------------------------------------------------+| 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 <data> || | 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, itis also allowed to use the same hook function for handling differenthooks in your module (asuming the hooks are compatible).Here's an example of what the module information for our examplemodule "foo" might look like:title: Foo example moduledesc: This is the Foo module for Phorum. Nothing exciting...hook: some_hook|phorum_mod_foo_some_hookhook: some_other_hook|phorum_mod_foo_some_other_hookhook: yet_another_hook|phorum_mod_foo_some_other_hookSo what this module info for example does, is telling Phorum thatwhen it gets to "some_other_hook", it will have to call the functionphorum_mod_foo_some_other_hook() in your module. It also tellsthat 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 neededfor creating a working module. However, your module might needextra stuff like template, language and image files. You canstore these files along with your module when using the multiplefile module structure (see 2.2.2 below).If you do not need to store any other stuff with your module, youcan also choose to use the single file (see 2.2.1 below) modulestructure.2.2 Module structure--------------------2.2.1 Single file modules-------------------------Single file modules are useful in case case no additional files haveto be distributed with your module. Because the module consist ofonly one single file, it is very easy to distribute. Beware that themoment you want to support for example a settings screen, multiplelanguages or custom images, you will have to switch to the multiplefile module structure.Single file modules consist of one single PHP file, which containsboth the module information and the hook functions. For storing themodule informaton, a special PHP comment is used. This comment mustlook like the following:/* phorum module info<module information lines go here>*/Using the example module info from 2.1.2, the complete singlefile module would look like this (see 3.5 why we use thecheck on PHORUM at the start of this file):<?phpif(!defined("PHORUM")) return;/* phorum module infotitle: Foo example moduledesc: This is the Foo module for Phorum. Nothing exciting...hook: some_hook|phorum_mod_foo_some_hookhook: some_other_hook|phorum_mod_foo_some_other_hookhook: yet_another_hook|phorum_mod_foo_some_other_hook*/function phorum_mod_foo_some_hook ($data) {// Do stuff for "some_hook".return $data;}function phorum_mod_foo_some_other_hook ($data) {// Do stuff for "some_other_hook" and "yet_another_hook".return $data;}?>Installation of a single file module is done by putting the PHPfile (e.g. foo.php) directly in the directory {phorum dir}/mods/and activating the module from the "Modules" screen in youradmin interface.2.2.2 Multiple file modules---------------------------Multiple file modules are useful in case you need additional filesto be stored with your module, for example a settings screen,language files or custom images.Multiple file modules are stored in their own subdirectory belowthe directory {phorum dir}/mods/. So if you have a module named"foo", you will have to create a directory {phorum dir}/mods/foo/ forstoring 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 themodule information for your module (see 2.1.2). The second fileis the PHP file which contains the hook functions for your module.The basename of this file should be the same as the name of themodule subdirectory. So for our example module "foo", you will haveto create a file named "foo.php".Using the example module info from 2.1.2, the complete multiplefile module would look like this (see 3.5 why we use thecheck on PHORUM at the start of the PHP file):info.txt:title: Foo example moduledesc: This is the Foo module for Phorum. Nothing exciting...hook: some_hook|phorum_mod_foo_some_hookhook: some_other_hook|phorum_mod_foo_some_other_hookhook: yet_another_hook|phorum_mod_foo_some_other_hookfoo.php:<?phpif(!defined("PHORUM")) return;function phorum_mod_foo_some_hook ($data) {// Do stuff for "some_hook".return $data;}function phorum_mod_foo_some_other_hook ($data) {// Do stuff for "some_other_hook" and "yet_another_hook".return $data;}?>So far, the module has exactly same functionality as the singlefile module from 2.2.1. From here on, the functionality can beextended. 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 displayoutput of your module in the same language, instead of the languageyou have written the module in.For supporting multiple languages, the first thing to do is add thefollowing to your module information file (info.txt):hook: lang|There is no hook function configured here, because the "lang" hookis only used as a marker for Phorum. This only tells Phorum that yourmodule 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 yourmodule directory. So in our sample module, the full directory would be{phorum dir}/foo/lang/. The language files must be named identicalto the main language files that Phorum uses. So, to include bothEnglish and French, your module would have the following filestructure below the Phorum's mods directory:foo/info.txtfoo/foo.phpfoo/lang/english.phpfoo/lang/french.phpThe structure of your language files will be almost identical to thatof the main Phorum language files. However, for your own language filesit is advisable to add an extra level in the language variables, toavoid conflicts with other modules or Phorum itself. Here is anexample of how you would do that:<?php$PHORUM["DATA"]["LANG"]["mod_foo"]["Hello"] = "Hello!";?>Here, the extra inserted level is ["mod_foo"]. You can add as manylines 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 moduledoes not support, Phorum will automatically attempt to fallback toEnglish. So it is highly recommend that you include an english.phplanguage file in all your modules. If both the current language andenglish.php are not found, Phorum will be unable to load a languagefor your module and will display empty space instead of languagestrings.Try to reuse strings that are already in the main Phorum languagefiles itself. Only create custom strings when there is no alternativeavailable. 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 attachedto each message ($message["meta"]). This array is a regular PHParray, which is stored in the database as serialized data(see http://www.php.net/serialize). Because Phorum and other modulesmake use of this meta data as well, you should not squash it,neither access the meta data in the database directly. Insteaduse the methods described in this section.Remark: because the meta data is stored as serialized data in thedatabase, it is not possible to include data you store in therein SQL queries.When storing information in the meta data from a hook function, youcan encounter two different situations, which both need a differentway 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 thehook functions, so these can change the message data before storingthe message in the database. Examples are the hooks "pre_post"and "pre_edit". In this case you can simply update the metainformation directly. Here's an example of how this would lookin 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 metadata is to retrieve the current meta data using phorum_db_get_message(),copy the meta data to a new message structure, make changes as neededand use phorum_db_update_message() to update the message in thedatabase. Here is an example of how this could look in your hookfunction: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 theexisting 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 userdata. 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 tostore. But instead, you can also create a single field for storinga complete array of information. Phorum will automatically take careof storing this information (serialized) in the database. You onlyshould make sure that the custom profile field is large enough tostore 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 thephorum_user_save() function. Below are two pieces of code which showhow our example module might store data for a user (asuming $user_idis the id of the user that must be changed).When using multiple fields "mod_foo_foodata" and "mod_foo_bardata":$user = phorum_user_get($user_id);$user["mod_foo_foodata"] = "Some user data";$user["mod_foo_bardata"] = "Some more user data";phorum_user_save($user);When using a single custom field "mod_foo" for this module:$user = phorum_user_get($user_id);$user["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 URLsthat point to parts of Phorum. It is recommended that you use thisfunction as well when creating links yourself, so special featuresand future changes will automatically be incorporated in the linksyou use.Here's an example of building an URL, which will open the profilefor the user with user_id = 17:$url = phorum_get_url(PHORUM_PROFILE_URL, 17);The argument list that this function takes, depends on the firstargument which tells Phorum what kind of URL has to be built.So when building other URLs, other arguments will probablybe used.If you need to build a custom URL to link to your own module, youcan use phorum_get_url() as well. The way to go is simple. Youneed to use PHORUM_CUSTOM_URL as the first argument and add allURL building parameters to it.The first parameter needs to be the filename of the file to linkto, without the (.php) extension. The second parameter needs tobe 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 thefile "myfile.php". The URL has to have the forum_id in it andneeds 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 lateruse. For those, you can create a settings page which will be usedfrom within the admin interface.The settings page must be put in your modules's directory by thename of "settings.php". So for our example module "foo" the filewould go in {phorum dir}/mods/foo/settings.php. In the admininterface under the option "Modules", a link to the settings.phppage will automatically be added if the settings.php file isavailable 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 youby Phorum for building pages and storing settings.One of those tools is a PHP object "PhorumInputForm" whichcan be used to create standard input forms and table displays inthe admin interface. The best example here is to look at one of themodules that come with Phorum like "bbcode" or "replace".Another tool is the function phorum_db_update_settings() which canbe used for storing settings in the database. To store settings usingthis 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 settingdata for this example will be available in $PHORUM["mod_foo"].To ensure that your settings file is only loaded from the admininterface, 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 whenwriting a module. This will basically turn your mod into a hack,because files have to be edited for it. Inexperienced users mightfind it hard to install your module if they have to modify filesto get it to work.If you cannot avoid changing the template, then consider to usetemplate hooks for this. You can use these if your template changeinvolves adding extra code to a template. The advantage is thatthere's only little code that has to be added to the templates,which makes things less confusing to users that want to install themodule.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 yourmodule info;* Create the hook function phorum_mod_foo_tpl_some_hook() thatprints out the code that has to be placed at the position ofthe "{HOOK tpl_some_hook}" code the the template.If you want to pass on the data from template variables tothe hook function, you can simply add the variables to the hookdefinition in the template. Example:{HOOK tpl_some_hook DATA1 DATA2}The hook function will get the contents of these variables passed ina single array. This can for example be useful if your template hookneeds 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 yourname 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_<modulename>_<identifier>Example: If a buttonbar is added in one of the templates for a modulenamed "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 lookingat existing module code. In your Phorum distribution's docs directory,you will find the directory example_mods. This directory contains acouple of example modules, demonstrating the features described in thisdocument. The modules have no real functional purpose, but they mightbe 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 maynot be accepted as an approved module. We want modules to be astransparent as possible for upgrades. Please attempt to store yourdata 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 alreadyactivated in your Phorum installation, you must deactivate andreactivate it to have Phorum reload the changed information. Forperformance reasons the module information is only read when themodule is activated.If you have added a new hook function to your module and it seemsnot 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 importthe $PHORUM array into your function scope. The Phorum teamrecommends the following method for doing this (check out thefaq.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 installationdirectory. The hook functions that you write also work fromthe same directory. So if you want to access files in your moduledirectory, you will have to specify the relative path to thosefiles. This path looks like:./mods/<module>/<filename>So let's say that our module "foo" has a subdirectory "images"which contains "bar.gif", then we could display that imageusing the HTML code:<img src="./mods/foo/images/bar.gif" />Another example: let's say that there is a function librarynamed "my_module_functions.php" in the module, which must beincluded 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, youshould 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 fromthe Phorum application. If you are writing pages that are loadedfrom 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 fromthe Phorum admin interface.3.6 Secure your pages from XSS------------------------------XSS stands for cross site scripting. This means that hackerscan feed HTML data to your application, which is displayed onscreen without stripping or escaping the HTML data. This wayit can be possible for hackers to feed malicous javascriptcode into the browser of users on the forum, causing a securityrisk. If you want to learn more about XSS, please visithttp://en.wikipedia.org/wiki/XSSTo prevent XSS security holes, you must take care that alluser input is properly sanitized before displaying it on screen.Sanitizing can be done by either stripping all HTML fromthe data (e.g. using http://www.php.net/strip_tags) or by escapingall html characters (using http://www.php.net/htmlspecialchars).Example:If your module needs to display the username for a user onscreen, 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 beforedisplaying it. You must use htmlspecialchars() to prevent some otherpossible problems as well. Imagine you have a user with the username"<b>ob". Without htmlspecialchars() the username would be interpretedas 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 areworking in the same namespace as other modules and Phorum itself.This means that there is a risk of duplicate use of functionand variable names. By following a couple of simple rules, youcan greatly reduce this risk.3.7.1 (Hook) functions----------------------Always construct names for your module functions like this:phorum_mod_<module name>_<identifier>So if you are writing functions for a module named "foo", allfunction names will look like:phorum_mod_foo_<identifier>You can use whatever you like for the <identifier> part. When writinga hook function, it is recommended to use the name of the hook forwhich you are writing the function (this will make clear what thefunction does, without having to check the module info). So in caseyou are writing a hook function for the hook "some_hook", the fullfunction name would be:phorum_mod_foo_some_hookIf your hook function handles multiple hooks at once, thensimply use one of the hook's names as the <identifier> or make upsomething yourself.3.7.2 Data stored in $PHORUM----------------------------When storing data in $PHORUM, always prepend the array key namewith mod_<module name>. 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 directlyin $PHORUM["DATA"]["LANG"] like Phorum does, because that mightresult 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 withmod_<module name>. SO if your module is named "foo", do not usethings 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 Phorumhooks 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 hooksexpect 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 wassent as input. If this is not allowed, the input data will beflagged as read-only.* In most cases the hook description will provide only one or moreof the possible uses for the hook. The full leverage of each hookis only limited by the imagination of the module writer (it'sas much a cliche as it is true).* It may be that you need to hook into a spot where there iscurrently no hook available. If that is the case, let the devteam know by posting a message in the development forumon phorum.org, explaining where and why you need an extra hook.Hooks will be added as neccessary, especially while Phorum 5is young.4.1 Code hooks--------------Code hooks are hooks that are called from within the Phorum corecode. These hooks are typically used for modifying Phorum's internaldatastructures.----------------------------------------------------------------------------admin_generalWhere : admin interfaceWhen : Right before the PhorumInputForm object is shown.Input : The PhorumInputForm object.Return : Same as InputThis hook can be used for adding items to the form on the"General Settings" page of the admin interface.----------------------------------------------------------------------------admin_file_purgeWhere : 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 InputThe primary use of this hook would be to cleanup stale files, createdby an alternate storage system for attachments (see after_attach andafter_detach as well). The array that is passed on to the hook functioncontains 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_attachWhere : include/posting/action_attachments.phpWhen : Just after a file attachment is saved in the databaseInput : Two part array where the first element is the message array andthe second element is a file array that contains the name, size,and file_id of the newly saved file.Return : Same as InputThe primary use of this hook would be for creating an alternate storagesystem for attachments. You would need to use the before_attach hook toremove the file data and in this hook it could be saved properly. You willneed to use the file hook to retreive the file data later.----------------------------------------------------------------------------after_detachWhere : include/posting/action_attachments.phpWhen : Just after a file attachment is deleted from the databaseInput : Two part array where the first element is the message array andthe second element is a file array that contains the name, size,and file_id of the deleted file.Return : Same as InputThe primary use of this hook would be for creating an alternate storagesystem for attachments. Using this hook, you can delete the file fromyour alternate storage.----------------------------------------------------------------------------after_headerWhere : Every page, except for the admin interface pagesWhen : Right after the header is displayed.Input : noneReturn : noneThis hook can be used for creating content at the end of the header,just before the main content is displayed.----------------------------------------------------------------------------after_loginWhere : login.phpWhen : After a successful login, just before redirecting theuser to a Phorum page.Input : The redirection URL.Return : Same as InputThis hook can be used for performing tasks after a successful userlogin and for changing the page to which the user will be redirected(by returning a different redirection URL). If you need to access theuser data, then you can do this through the global $PHORUM variable.The user data will be in $PHORUM["user"].----------------------------------------------------------------------------after_logoutWhere : login.phpWhen : After a logout, just before redirecting the user toa Phorum page.Input : The redirection URL.Return : Same as InputThis hook can be used for performing tasks after a successful userlogout and for changing the page to which the user will be redirected(by returning a different redirection URL). The user data will stillbe availbale in $PHORUM["user"] at this point.----------------------------------------------------------------------------after_registerWhere : register.phpWhen : Right after a successful registration of a new user is doneand all confirmation mails are sent.Input : Array containing the user data of the user (read-only).Return : Same as InputThis hook can be used for performing tasks (like logging andnotification) after a successful user registration.----------------------------------------------------------------------------before_attachWhere : include/posting/action_attachments.phpWhen : Just before a file attachment is saved in the databaseInput : Two part array where the first element is the message array andthe second element is a file array that contains the name, sizeand data.Return : Same as InputThe primary use of this hook would be for creating an alternate storagesystem for attachments. You would need to use the after_attach hook tocomplete the process as you do not yet have the file_id for the file. Youwill need to use the file hook to retreive the file data later.----------------------------------------------------------------------------before_editorWhere : posting.phpWhen : Just before the message editor is displayed.Input : Array containing data for the message that will be shownin the editor screen.Return : Same as InputThis hook can be used for changing message data, just before the editoris displayed. This is done after escaping message data for XSS preventionis done. So in the hook, the module writer will have to be aware thatdata is escaped and that he has to escape data himself if needed.This hook is called every time the editor is displayed. If modifyingthe message data does not have to be done on every request (for exampleonly on the first request when replying to a message), the module willhave to check the state the editor is in. Here's some hints on whatyou could do to accomplish this:* Check the editor mode: this can be done by looking at the "mode" fieldin 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, theeditor 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 looklike 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 isdisplayed in the editor. From the editor, the user can still changethe data. Therefore, this hook cannot be used to control the data whichwill be stored in the database. If you need that functionality, thenuse the hooks pre_edit and/or pre_post instead.----------------------------------------------------------------------------before_footerWhere : Every page, except for the admin interface pagesWhen : Right before the footer is displayed.Input : noneReturn : noneThis hook can be used for creating content at the end of the maincontent, just before the footer. It can also be used forperforming tasks that have to be executed at the end of each page.----------------------------------------------------------------------------before_registerWhere : register.phpWhen : Right before a new user is stored in the database.Input : Array containing the user data of the user.Return : Same as InputThis hook can be used for performing tasks before user registration.This hook is useful if you want to add some data to or change somedata in the user data and to check if the user data is correct.When checking the registration data, the hook can set the "error" fieldin the returned user data array. When this field is set after runningthe hook, the registration processed will be halted and the errorwill be displayed. If you created a custom form field "foo" and yourequire that field to be filled in, you could create a hook functionwhich 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;}----------------------------------------------------------------------------buddy_addWhere : pm.phpWhen : Right after a buddy has been added successfully.Input : The user id of the buddy that has been added.Return : Same as InputThis hook can be used for performing actions after a buddy has beenadded for a user (e.g. sending the new buddy a PM about this event,update popularity counters, do logging, synchronizing with otherdatabases, etc.).----------------------------------------------------------------------------buddy_deleteWhere : pm.phpWhen : Right after a buddy has been deleted successfully.Input : The user id of the buddy that has been deleted.Return : Same as InputThis hook can be used for performing actions after a buddy hasbeen deleted for a user.----------------------------------------------------------------------------cc_save_userWhere : control.phpWhen : Right before data for a user is saved in the control panel.Input : Array containing the user data to save.Return : Same as InputThis hook works the same way as the before_register hook, so you canalso use it for changing and checking the user data that will besaved in the database. There's one difference. If you want tocheck a custom field, you'll also need to check the panel whichyou are on, because this hook is called from multiple panels.The panel that you are on, will be stored in the 'panel' fieldof 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 willlook 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_postWhere : post.phpWhen : Right after performing preliminary posting checks, unlessthese checks have returned something bad.Input : Array containing:0 => the $_POST array with form data1 => $error, to return errors inReturn : Same as InputThis hook can be used for modifying data in the $_POST array and forrunning additional checks on the data. If an error is put in $error,Phorum will stop posting the message and show the error to the userin the post-form.Beware that $error can already contain an error on input, in casemultiple modules are run for this hook. Therefore you might want toreturn immediately in your hook function in case $error is alreadyset.Below is an example of how a function for this hook could look.This example will disallow the use of the word "bar" in themessage 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_threadWhere : moderation.phpWhen : 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 InputThis hook can be used for performing actions like sending notificationsor making log entries after closing threads.----------------------------------------------------------------------------commonWhere : common.php, so in practice every pageWhen : Right before the end of the common.php include script.Input : noneReturn : noneThis hook can be used for applying custom settings or alteringPhorum settings based on external parameters.----------------------------------------------------------------------------common_no_forumWhere : common.php, so in practice every pageWhen : Right after no forum settings were found, before doing the redirectInput : noneReturn : noneThis 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_userWhere : common.php, so in practice every pageWhen : Right after loading the user from the database, but justbefore making descisions on language and template.Input : noneReturn : noneThis hook can be used for applying custom settings or alteringPhorum settings based on external parameters.----------------------------------------------------------------------------common_preWhere : common.php, so in practice every pageWhen : Right after loading the settings from the database, but justbefore making descisions on language, template and user.Input : noneReturn : noneThis hook can be used for applying custom settings or alteringPhorum settings based on external parameters.----------------------------------------------------------------------------deleteWhere : moderation.phpWhen : Right after deleting a message from the database.Input : Array of ids for messages that have been deleted (read-only).Return : noneThis hook can be used for cleaning up anything you may have createdwith the post_post hook or any other hook that stored data tied tomessages.----------------------------------------------------------------------------externalThe external hook functions are never called from any of the standardPhorum pages. These functions are called by invoking script.php on thecommand line with the --module parameter. This can be used to pipeoutput from some arbitrary command to a specific module, which can dosomething with that input. If your module does not need any commandline input and is meant to be run on a regular basis, you shouldconsider using the scheduled hook.Mind that for using an external hook, the module in which it ishandled must be enabled in your admin interface. So if an externalhook is not running, the containing module might be disabled.To run the external hook from the command line, you have to be inthe phorum installation directory. So running the external hook ofa module named "external_foo" would be done like this on a UNIXsystem prompt:# cd /your/phorum/dir# php ./script.php --module=external_fooFor easy use, you can of course put these commands in a script file.----------------------------------------------------------------------------fileWhere : file.phpWhen : When attachments are requested.Input : Two part array where the first element is the mime type alreadydetected by file.php and the second part is the file array thatcontains the filename, file_data, filesize, etc.Return : Same as InputThis hook could be used to count file downloads, or along with after_attachan alternate file data storage mechanism could be created.----------------------------------------------------------------------------formatWhere : phorum_format_messages() in include/format_functions.phpWhen : Everytime phorum_format_messages() is called for formattinga message, just before it is sent to the templates.Input : Array of messages.Return : Same as InputThis hook can be used for applying custom formatting to messages. Themessage fields that are most applicable for this are "body" and "author".When writing a module using this hook, you probably want to formatthose fields. In practice you can apply formatting to all the fieldsyou want.The changes you make to the messages are for displaying purposesonly, so the changes are not stored in the database.----------------------------------------------------------------------------hideWhere : moderation.phpWhen : 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 InputThis hook can be used for performing actions like sending notificationsor making log entries after hiding a message.----------------------------------------------------------------------------indexWhere : include/index_new.php and include/index_classic.phpWhen : Right before the list of forums is displayed.Input : Array of forums.Return : Same as InputThis hook can be used for changing or adding data to the forumsin the list.----------------------------------------------------------------------------langThe lang hook is a only a 'marker'. It flags Phorum that your modulesupports multiple languages. It does not take a hook function inyour module information. If you do define a hook function, it willnever be called.Read section 2.3 for information on the use of multiple languages.----------------------------------------------------------------------------listWhere : list.phpWhen : Right before the messages are formatted and displayed.Input : Array of threads (or messages in threaded mode).Return : Same as InputThis hook can be used for changing or adding data to the messagesin the list.----------------------------------------------------------------------------moderationWhere : moderation.phpWhen : At the start of moderation.phpInput : The id of the moderation step which is run (read-only).Return : noneThis hook can be used for logging moderator actions. You canuse the $PHORUM-array to retrieve additional info like themoderating user's id and similar.The moderation step id is the variable $mod_step that is used inmoderation.php. Please read that script to see what moderationsteps are available and for what moderation actions they stand.When checking the moderation step id for a certain step, always usethe contstants that are defined for this in include/constants.php.The numerical value of this id can change between Phorum releases.----------------------------------------------------------------------------move_threadWhere : moderation.phpWhen : Right after a thread has been moved by a moderator.Input : The id of the thread that has been moved (read-only).Return : noneThis hook can be used for performing actions like sending notificationsor for making log entries after moving a thread.----------------------------------------------------------------------------pm_sentWhere : include/controlcenter/pm.phpWhen : Right after a PM and its email notifications have been sent.Input : Array containing the private message data (read-only).Return : noneThis hook can be used for performing actions after sending a PM. BeforePM notification by email was put in the Phorum core, this hook wasused to send those notifications.----------------------------------------------------------------------------post_editWhere : include/moderation_functions.phpWhen : Right after storing an edited message in the database.Input : Array containing message data (read-only).Return : noneThis hook can be used for sending notifications or for making log entriesin the database when editing takes place.----------------------------------------------------------------------------post_postWhere : post.phpWhen : Right after storing a new message in the database and justbefore the user is redirected back to the list.Input : Array containing message data (read-only).Return : noneThis hook can be used for performing actions based on what the messagecontained.----------------------------------------------------------------------------posting_permissionWhere : posting.phpWhen : Right after Phorum has determined all abilities that applyto the logged in user.Input : noneOuput : noneThis hook can be used for setting up custom abilities and permissionsfor users, by updating the applicable fields in $GLOBALS["PHORUM"]["DATA"](e.g. for giving certain users the right to make postings sticky, withouthaving to make the full moderator for a forum).Read the code in posting.php before this hook is called to find outwhat fields can be used.Beware: Only use this hook if you know what you are doing and understandPhorum's editor permission code. If used wrong, you can open up securityholes in your Phorum installation!----------------------------------------------------------------------------pre_editWhere : include/moderation_functions.phpWhen : Right before storing an edited message in the database.Input : Array containing message data.Return : Same as InputThis hook can be used for changing the message data before storing itin the database.----------------------------------------------------------------------------pre_postWhere : post.phpWhen : Right before storing a new message in the database.Input : Array containing message data.Return : Same as InputThis hook can be used for changing the message data before storing itin the database.----------------------------------------------------------------------------profileWhere : profile.php and include/controlcenter/summary.phpWhen : Right before a user profile is displayed.Input : Array containing user profile data.Return : Same as InputThis hook can be used for making changes to the profile data. Thisis for displaying purposes only, so the changes are not stored in thedatabase.----------------------------------------------------------------------------quoteWhere : 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 author1 => The message bodyReturn : The quoted body to use in the post form.When quoting a message for reply, by default Phorum formats quotedmessages using an old school email style of quoting. By using the quotehook, 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 returnvalue for your hook function must be the quoted body that willbe pre-filled into the reply form.The BBCode module that is distributed with Phorum has a quote hookfunction. Because it does not make sense to have more than one quotehook active, the BBCode module has an option to disable its quote hookfunction. You need to make sure that its quote hook function is disabledwhen using your own quote hook.----------------------------------------------------------------------------readWhere : read.phpWhen : Right before messages are formatted for displaying.Input : Array of messages.Return : Same as InputThis hook can be used for making changes to the message data whenreading messages. This is for displaying purposes only, so thechanges are not stored in the database.----------------------------------------------------------------------------read_user_infoWhere : read.php post.php include/moderation_functions.phpWhen : Right after retrieving user data.Input : Array of users.Return : Same as InputThis hook can be used for changing information for the users beforebeing displayed. For example: add a border around user signatures.This is for displaying purposes only, so the changes are not storedin the database.----------------------------------------------------------------------------readthreadsWhere : read.phpWhen : At the start of the threaded read handling, just beforesorting and displaying the threads.Input : Array of messages.Return : Same as InputThis hook does exactly the same as the read hook, except that thisone is only applied to messages when viewing the message list inthreaded mode.----------------------------------------------------------------------------reopen_threadWhere : moderation.phpWhen : 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 InputThis hook can be used for performing actions like sending notificationsor making log entries after reopening threads.----------------------------------------------------------------------------reportWhere : report.phpWhen : Just before a reported message is sent to the moderators.Input : Array with maildata (see report.php for the exact contents).Return : Same as InputThis hook can be used for changing the report data that will besent to the moderators or for performing actions like making logentries.----------------------------------------------------------------------------sanity_checksWhere : include/admin/sanity_checks.phpWhen : Just before the admin interface's sanity checks are runInput : Array with sanity checks. Each sanity check is an array with:function => The function that runs the sanity checkdescription => A description to show in the admin interfaceReturn : Same as InputThis hook can be used to add custom sanity checks to the admininterface option "System Sanity Checks".Each checking function is expected to return an array containingtwo elements:[0] A status, which can be one ofPHORUM_SANITY_OK No problem foundPHORUM_SANITY_WARN Problem found, but no fatal onePHORUM_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);}}----------------------------------------------------------------------------scheduledScheduled hook functions are similar to external ones, except thesefunctions do not require any input from the command line. The modulescontaining a scheduled hook are invoked by running script.php withthe --scheduled argument (no module name is taken; this argumentwill run all scheduled hooks for all available modules).Like the name of the hook already suggests, this hook can be used forcreating tasks which have to be executed on a regular basis. Toarchieve this, you can let script.php run from a schedulingservice (like a cron job on a UNIX system).In general, scheduled hooks are used for automating tasks you wantto execute without having to perform any manual action. Practicaluses for a scheduled hook could be housekeeping (cleanup ofstale/old data), daily content generation (like sending daily digestscontaining all posted messages for that day) or forum statisticsgeneration.Mind that for using a scheduled hook, the module in which it ishandled must be enabled in your admin interface. So if a scheduledhook is not running, the containing module might be disabled.To run the scheduled hook from the command line or from a schedulingservice, you have to be in the phorum installation directory. Sorunning the scheduled hooks for your Phorum installation wouldbe done like this on a UNIX system prompt:# cd /your/phorum/dir# php ./script.php --scheduledWhen creating a scheduling service entry for running thisautomatically, 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), becausethe scheduling service might not know the path to it. An entryfor the cron system on UNIX could look like this:0 0 * * * cd /your/phorum/dir && /usr/bin/php ./script.php --scheduledPlease refer to your system's documentation to see how touse your system's scheduling service.----------------------------------------------------------------------------searchWhere : search.phpWhen : Right before messages are formatted for displaying.Input : Array of messages.Return : Same as InputThis hook can be used for making changes to the message data whensearching for messages. This is for displaying purposes only, so thechanges are not stored in the database.----------------------------------------------------------------------------send_mailWhere : 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 recipientsReturn : true/false - see descriptionThis hook can be used for implementing an alternative mail sendingsystem (e.g. like the SMTP module does). The hook should return true ifPhorum should still send the mails himself. If you do not want to havePhorum send the mails also, return false.----------------------------------------------------------------------------user_listWhere : include/users.php include/controlcenter/groupmod.phpWhen : Whenever phorum_user_get_list() is called.Input : Array containing:<user_id> => <data>Where <data> is an array containing:username => the username for the userdisplayname => the way to display the usernameReturn : Same as InputThis hook can be used for reformatting the list of users in someway, such as changing the sort order or changing the format ofthe 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 customelements. From a hook function for one of these template hooks, themodule writer can print the HTML code that has to be added to theinterface at the postition of the hook call in the template.----------------------------------------------------------------------------tpl_editor_after_subjectWhere : posting_messageform.tplWhen : After the Subject: field in the message editor.Input : noneReturn : noneThis 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 yourhook 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"] : "";?><tr><td>Foo field</td><td><input type="text" name="mod_foo"value="<?php print htmlspecialchars($value) ?>"/></td></tr><?php}----------------------------------------------------------------------------tpl_cc_usersettingsWhere : cc_usersettings.tplWhen : After the built-in usersettings fields.Input : Array containing the user's current profile.Return : Same as InputThis hook can be used to add extra fields to the settings pages in theuser's control center. Here's an example hook function that will addan extra field to the "Edit My Profile" page.function phorum_mod_foo_tpl_cc_usersettings($profile){// Check if we're on the userprofile page of cc_usersettings.tpl.if (! isset($profile["USERPROFILE"]) || ! $profile["USERPROFILE"])return;$value = isset($profile["shoesize"])? htmlspecialchars($profile["shoesize"]) : "";?><tr><td>Shoe size</td><td><input name="shoesize" type="text" value="<?=$value?>"></td></tr><?php}----------------------------------------------------------------------------tpl_editor_attachment_buttonsWhere : posting_attachments_list.tplWhen : Before the delete button for an attachment.Input : Array containing attachment information.Return : Same as InputThis hook can be used to add extra buttons to each attachment in theeditor, so you can do custom actions for attachments.To make your buttons look the same as Phorum's buttons, use theCSS class "PhorumSubmit".Here's an example hook function that will add a button to the attachments,which will send a javascript alert to the user when clicked.function phorum_mod_foo_tpl_editor_attachment_buttons($data){$id = $data["file_id"];?><input type="submit" class="PhorumSubmit" value="Say it!"onclick="alert('You clicked attachment id <?php print $id ?>')" /><?php}----------------------------------------------------------------------------tpl_editor_before_textareaWhere : posting_messageform.tplWhen : Before the textarea.Input : noneReturn : noneThis hook can be used to add custom user interface elements before thetextarea in the message editor.----------------------------------------------------------------------------tpl_editor_buttonsWhere : posting_buttons.tplWhen : Before the main editor buttons (like Preview, Post and Cancel).Input : noneReturn : noneThis hook can be used to add extra buttons to the editor buttons.To make your buttons look the same as Phorum's buttons, use theCSS class "PhorumSubmit".Here's an example hook function that will add a button to the editor,which will send a javascript alert to the user when clicked.function phorum_mod_foo_tpl_editor_buttons(){ ?><input type="submit" class="PhorumSubmit" value="Say it!"onclick="alert('Hello, foo!')" /><?php}6. Support-------------------------------------------------------------------------------If you have questions about creating modules for Phorum, please visitthe website http://phorum.org/ and ask the development team for help inthe Development forum.