$Id$ In addition to this document, please check out the SquirrelMail development FAQ for more information. Also, help writing plugins is easily obtained by posting to the squirrelmail-plugins mailing list. (See details about mailing lists on the website) FAQ -> http://www.squirrelmail.org/wiki/DeveloperFAQ Plugin Development -> http://www.squirrelmail.org/wiki/DevelopingPlugins A FEW NOTES ON THE PLUGIN ARCHITECTURE ====================================== The plugin architecture of SquirrelMail is designed to make it possible to add new features without having to patch SquirrelMail itself. Functionality like password changing, displaying ads and calendars should be possible to add as plugins. The Idea -------- The idea is to be able to run random code at given places in the SquirrelMail code. This random code should then be able to do whatever needed to enhance the functionality of SquirrelMail. The places where code can be executed are called "hooks". There are some limitations in what these hooks can do. It is difficult to use them to change the layout and to change functionality that already is in SquirrelMail. Some way for the plugins to interact with the help subsystem and translations will be provided. The Implementation ------------------ The plugin jumping off point in the main SquirrelMail code is in the file functions/plugin.php. In places where hooks are made available, they are executed by calling the function do_hook('hookname'). The do_hook function then traverses the array $squirrelmail_plugin_hooks['hookname'] and executes all the functions that are named in that array. Those functions are placed there when plugins register themselves with SquirrelMail as discussed below. A plugin may add its own internal functions to this array under any hook name provided by the SquirrelMail developers. A plugin must reside in a subdirectory in the plugins/ directory. The name of the subdirectory is considered to be the name of the plugin. (The plugin will not function correctly if this is not the case.) To start using a plugin, its name must be added to the $plugins array in config.php like this: $plugins[0] = 'plugin_name'; When a plugin is registered, the file plugins/plugin_name/setup.php is included and the function squirrelmail_plugin_init_plugin_name() is called with no parameters. That function is where the plugin may register itself against any hooks it wishes to take advantage of. WRITING PLUGINS =============== All plugins must contain a file called setup.php and must include a function called squirrelmail_plugin_init_plugin_name() therein. Since including numerous plugins can slow SquirrelMail performance considerably, the setup.php file should contain little else. Any functions that are registered against plugin hooks should do little more than call another function in a different file. Any other files used by the plugin should also be placed in the plugin directory (or subdirectory thereof) and should contain the bulk of the plugin logic. The function squirrelmail_plugin_init_plugin_name() is called to initalize a plugin. This function could look something like this (if the plugin was named "demo" and resided in the directory plugins/demo/): function squirrelmail_plugin_init_demo () { global $squirrelmail_plugin_hooks; $squirrelmail_plugin_hooks['generic_header']['demo'] = 'plugin_demo_header'; $squirrelmail_plugin_hooks['menuline']['demo'] = 'plugin_demo_menuline'; } Please note that as of SquirrelMail 1.5.0, this function will no longer be called at run time and will instead be called only once at configure- time. Thus, the inclusion of any dynamic code (anything except hook registration) here is strongly discouraged. In this example, the "demo" plugin should also have two other functions in its setup.php file called plugin_demo_header() and plugin_demo_menuline(). The first of these might look something like this: function plugin_demo_header() { include_once(SM_PATH . 'plugins/demo/functions.php'); plugin_demo_header_do(); } The function called plugin_demo_header_do() would be in the file called functions.php in the demo plugin directory and would contain the plugin's core logic for the "generic_header" hook. Including Other Files --------------------- A plugin may need to reference functionality provided in other files, and therefore need to include those files. Most of the core SquirrelMail functions are already available to your plugin unless it has any files that are requested directly by the client browser (custom options page, etc.). In this case, you'll need to make sure you include the files you need (see below). Note that as of SquirrelMail 1.4.0, all files are accessed using a constant called SM_PATH that always contains the relative path to the main SquirrelMail directory. This constant is always available for you to use when including other files from the SquirrelMail core, your own plugin, or other plugins, should the need arise. If any of your plugin files are requested directly from the client browser, you will need to include the SquirrelMail initialization file which is present since SquirrelMail 1.5.2. The SquirrelMail initialization file which is located in include/init.php does take care of setting up the session, defining the constants like SM_PATH and it take care of including a minimum set of required files. The files which are included depends on which file the hook operates on. For all hook locations the following files are included: require(SM_PATH . 'functions/global.php'); require(SM_PATH . 'config/config.php'); require(SM_PATH . 'functions/plugin.php'); require(SM_PATH . 'include/constants.php'); require(SM_PATH . 'include/languages.php'); require(SM_PATH . 'functions/display_messages.php' ); require(SM_PATH . 'functions/page_header.php'); require(SM_PATH . 'functions/html.php'); Except login.php also the following files are included: require(SM_PATH . 'functions/prefs.php'); require(SM_PATH . 'functions/db_prefs.php'); OR require(SM_PATH . 'functions/file_prefs.php'); (dependent of the configured preference backend) For all files except login.php and redirect.php also the following files are included: require(SM_PATH . 'functions/strings.php'); require(SM_PATH . 'functions/auth.php'); Because the use of "require" in include/init.php your plugin will fail if it tries to include the file in the plugin as well. Be aware of that. To include the init.php file add the following in your plugin: /** * Include the SquirrelMail initialization file. */ require('../../include/init.php'); Other files then the files mentioned above can be included by your plugin like this: include_once(SM_PATH . 'functions/imap_general.php'); When including files, please make sure to use the include_once() function and NOT include(), require() because they can cause fatal errors when other plugins or SquirrelMail files include the same file. If you use require_once() instead of include_once() then if something cause wrong with the include then php will raise a fatal error. That's the reason plugins MUST use include_once() instead of require_once(). The files that you may need to include in a plugin will vary greatly depending upon what the plugin is designed to do. For files that are requested directly by the client browser, you MUST includey the file include/init.php , since it will set up the SquirrelMail environment automatically. It will ensure the the user has been authenticated and is currently logged in, load all user preferences, include internationalization support, call stripslashes() on all incoming data (if magic_quotes_gpc is on), and initialize and include all other basic SquirrelMail resources and functions. You may see other plugins that directly include other SquirrelMail files, but that is no longer necessary and is a hold-over from older SquirrelMail versions. List of files, that are included by include/validate.php (If SquirrelMail version is not listed, files are included from v.1.3.2.). This table is specific to SquirrelMail 1.3.2 - 1.5.1. Script layout was changed in 1.5.2. 1. class/mime.class.php 1.1. class/mime/Rfc822Header.class.php 1.2. class/mime/MessageHeader.class.php 1.3. class/mime/AddressStructure.class.php 1.4. class/mime/Message.class.php 1.5. class/mime/SMimeMessage.class.php 1.6. class/mime/Disposition.class.php 1.7. class/mime/Language.class.php 1.8. class/mime/ContentType.class.php 2. functions/global.php * fixes differences between php 4.0.x and 4.1+ globals (only in 1.4.x). * undoes magic_quotes_gpc=on sanitizing * sets $PHP_SELF (since 1.5.1) * starts session 3. functions/strings.php 3.1. functions/global.php 3.2. plugins/compatibility/functions.php (compatibility v.2.0.4+, requires code patching) * sets squirrelmail version variable and constant. * sets $PHP_SELF (before 1.5.1) 4. config/config.php 4.1. config/config_local.php (from 1.4.0rc1) 5. functions/i18n.php 5.1. functions/global.php (from 1.4.0) * reads 'squirrelmail_language' cookie * loads $languages (since 1.5.1 $languages array is built from locale/*/setup.php files) * loads own gettext functions, if php gettext is unavailable 6. functions/auth.php 7. include/load_prefs.php 7.1. include/validate.php 7.2. functions/prefs.php 7.2.1. functions/global.php (sqgetGlobalVar() function) 7.2.2. functions/plugin.php (do_hook_function() function,, since 1.4.4 and 1.5.1, see 7.3) 7.2.3. $prefs_backend (only in 1.4.3 and 1.5.0) do_hook_function('prefs_backend') (since 1.4.4 and 1.5.1) functions/db_prefs.php functions/file_prefs.php 7.2.3.1. functions/display_messages.php (loaded only by file_prefs.php) 7.2.3.2. files loaded by plugin that uses 'prefs_backend' hook 7.3. functions/plugin.php 7.3.1. functions/global.php (from 1.4.0 and 1.5.0) 7.3.2. functions/prefs.php (from 1.5.1) 7.3.3. plugins/*/setup.php files for enabled plugins. * starts all squirrelmail_plugin_init_pluginname functions 7.4. functions/constants.php 7.5. do_hook('loading_prefs') 7.5.1. files loaded by plugins that use 'loading_prefs' hook 8. functions/page_header.php 8.1. functions/strings.php 8.2. functions/html.php 8.3. functions/imap_mailbox.php 8.3.1. functions/imap_utf7_local.php 8.4. functions/global.php 9. functions/prefs.php (already loaded. see 7.2) Since SquirrelMail 1.5.1 functions/global.php file must be loaded before setting any own global variables. If variables are set before loading functions/global.php library, they can be corrupted in PHP register_globals=On setups. Hook Types: Parameters and Return Values ----------------------------------------- Hooks, when executed, are called with differing parameters and may or may not take return values, all depending on the type of hook being called and the context in which it is being used. On the source side (where the hook call originates), all hooks have at least one parameter, which is the name of the hook. After that, things get complicated. do_hook ------- Most hook calls don't pass any data and don't ask for anything back. These always use the do_hook call. A limited number of do_hook calls do pass some extra parameters, in which case your plugin may modify the given data if you do so by reference. It is not necessary to return anything from your function in such a case; modifying the parameter data by reference is what does the job (although the hook call itself (in the source) must grab the return value for this to work). Note that in this case, the parameter to your hook function will be an array, the first element simply being the hook name, followed by any other parameters that may have been included in the actual hook call in the source. Modify parameters with care! do_hook_function ---------------- This hook type was intended to be the main hook type used when the source needs to get something back from your plugin. It is somewhat limited in that it will only use the value returned from the LAST plugin registered against the hook. The source for this hook might use the return value for internal purposes, or might expect you to provide text or HTML to be sent to the client browser (you'll have to look at its use in context to understand how you should return values here). The parameters that your hook function gets will be anything you see AFTER the hook name in the actual hook call in the source. These cannot be changed in the same way that the do_hook parameters can be. concat_hook_function -------------------- This is a newer hook type meant to address the shortcomings of do_hook_function; specifically in that it uses the return values of all plugins registered against the hook. In order to do so, the return value is assumed to be a string, which is just piled on top of whatever it got from the other plugins working on the same hook. Again, you'll have to inspect the source code to see how such data is put to use, but most of the time, it is used to create a string of HTML to be inserted into the output page. The parameters that your hook function will get are the same as for the do_hook_function; they are anything AFTER the hook name in the actual hook call in the source. boolean_hook_function --------------------- The newest of the SquirrelMail hooks, this type is used to let all plugins registered against the hook to "vote" for some action. What that action is is entirely dependent on how the hook is used in the source (look for yourself). Plugins make their "vote" by returning TRUE or FALSE. This hook may be configured to "tally votes" in one of three ways. This configuration is done with the third parameter in the hook call in the source: > 0 -- Any one or more TRUEs will override any FALSEs < 0 -- Any one or more FALSEs will override any TRUEs = 0 -- Majority wins. Ties are broken in this case with the last parameter in the hook call in the source. Your hook function will get the second paramter in the hook call in the source as its parameter (this might be an array if multiple values need to be passed). See below for further discussion of special hook types and the values List of Hooks ------------- This is a list of all hooks currently available in SquirrelMail, ordered by file. Note that this list is accurate as of June 17, 2003 (should be close to what is contained in release 1.4.1, plus or minus a hook or two), but may be out of date soon thereafter. You never know. ;-) Hook Name Found In Called With(#) --------- -------- -------------- abook_init functions/addressbook.php do_hook abook_add_class functions/addressbook.php do_hook loading_constants functions/constants.php do_hook logout_error functions/display_messages.php do_hook error_box functions/display_messages.php concat_hook get_pref_override functions/file_prefs.php hook_func get_pref functions/file_prefs.php hook_func & options_identities_process functions/identity.php do_hook &% options_identities_renumber functions/identity.php do_hook special_mailbox functions/imap_mailbox.php hook_func % rename_or_delete_folder functions/imap_mailbox.php hook_func folder_status (since 1.5.1) functions/imap_mailbox.php hook_func functions/imap_general.php hook_func mailbox_index_before functions/mailbox_display.php do_hook mailbox_form_before functions/mailbox_display.php do_hook mailbox_index_after functions/mailbox_display.php do_hook check_handleAsSent_result functions/mailbox_display.php do_hook subject_link functions/mailbox_display.php concat_hook mailbox_display_buttons functions/mailbox_display.php do_hook mailbox_display_button_action functions/mailbox_display.php hook_func message_body functions/mime.php do_hook ^ attachment $type0/$type1 functions/mime.php do_hook attachments_bottom functions/mime.php hook_func decode_body functions/mime.php hook_func generic_header functions/page_header.php do_hook menuline functions/page_header.php do_hook prefs_backend functions/prefs.php hook_func loading_prefs include/load_prefs.php do_hook addrbook_html_search_below src/addrbook_search_html.php do_hook addressbook_bottom src/addressbook.php do_hook ! compose_form src/compose.php do_hook compose_bottom src/compose.php do_hook compose_button_row src/compose.php do_hook compose_send src/compose.php do_hook compose_send_after src/compose.php do_hook configtest (since 1.5.2) src/configtest.php boolean_hook folders_bottom src/folders.php do_hook help_top src/help.php do_hook help_chapter src/help.php do_hook help_bottom src/help.php do_hook left_main_after_each_folder src/left_main.php concat_hook left_main_before src/left_main.php do_hook left_main_after src/left_main.php do_hook login_cookie src/login.php do_hook login_top src/login.php do_hook login_form src/login.php concat_hook (was do_hook before 1.5.1) login_bottom src/login.php do_hook * optpage_set_loadinfo src/options.php do_hook * optpage_loadhook_personal src/options.php do_hook * optpage_loadhook_display src/options.php do_hook * optpage_loadhook_highlight src/options.php do_hook * optpage_loadhook_folder src/options.php do_hook * optpage_loadhook_order src/options.php do_hook * options_personal_save src/options.php do_hook * options_display_save src/options.php do_hook * options_folder_save src/options.php do_hook * options_save src/options.php do_hook * optpage_register_block src/options.php do_hook * options_link_and_description src/options.php do_hook * options_personal_inside src/options.php do_hook * options_display_inside src/options.php do_hook * options_highlight_inside src/options.php do_hook * options_folder_inside src/options.php do_hook * options_order_inside src/options.php do_hook * options_personal_bottom src/options.php do_hook * options_display_bottom src/options.php do_hook * options_highlight_bottom src/options.php do_hook * options_folder_bottom src/options.php do_hook * options_order_bottom src/options.php do_hook * options_highlight_bottom src/options_highlight.php do_hook & options_identities_top src/options_identities.php do_hook & options_identities_table src/options_identities.php concat_hook & options_identities_buttons src/options_identities.php concat_hook message_body src/printer_friendly_bottom.php do_hook read_body_header src/read_body.php do_hook read_body_menu_top src/read_body.php hook_func read_body_menu_bottom src/read_body.php do_hook read_body_header_right src/read_body.php do_hook read_body_top src/read_body.php do_hook read_body_bottom src/read_body.php do_hook login_before src/redirect.php do_hook login_verified src/redirect.php do_hook right_main_after_header src/right_main.php do_hook right_main_bottom src/right_main.php do_hook search_before_form src/search.php do_hook search_after_form src/search.php do_hook search_bottom src/search.php do_hook logout src/signout.php do_hook message_body (since 1.5.2) src/view_html.php do_hook message_body (since 1.5.2) src/view_text.php do_hook webmail_top src/webmail.php do_hook webmail_bottom src/webmail.php concat_hook logout_above_text src/signout.php concat_hook O info_bottom plugins/info/options.php do_hook % = This hook is used in multiple places in the given file # = Called with hook type (see below) & = Special identity hooks (see below) ^ = Special attachments hook (see below) * = Special options hooks (see below) O = Optional hook provided by a particular plugin ! = See below for notes about working with the compose page's