Due to the large number and wide range of functions plugins can cover it is
impractical to add all but the most popular to the SquirrelMail core project.
There is a separate
SM Plugins project registered at SourceForge to house plugin code,
developers should consider joining this project.
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 start 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 one of the hook functions. The 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 of the plugins/ directory. The
name of the subdirectory is considered to be the name of the plugin. Note that
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[] = '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.
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's performance down
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.2, this function is no longer
called at run time and is instead called (actually, just parsed) only
once at configuration time. Thus, the inclusion of any dynamic code
(anything except hook registration) here is strongly discouraged.
Adding functionality
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() in the file
plugins/demo/functions.php contains the plugin's core logic for the
generic_header hook.
In order to facilitate easier (and possibly automated) plugin management by
system administrators as well as in-SquirrelMail functionality that enables
cross-plugin compatibility and communication, plugin versioning should remain
consistent with that of SquirrelMail itself. While SquirrelMail does its
best to work with non-standard version strings, versioning such as that
explained in
Version numbering, with the
possible addition of an applicable SquirrelMail version, is the best way
to track your plugin's development (i.e., "1.0.4-1.4.5" or just "1.0.4").
Versions with only two digits ("1.0-1.4.5" or just "1.0") are also acceptable
(a third digit of zero is implied).
Version reporting
The way any automated process gets its hands on the plugin version string
depends on the SquirrelMail version being used. As of SqruirrelMail 1.5.2,
plugins should include an administrative function in setup.php that
returns an array of details that describe the plugin, including its version
number. The function must be named <plugin name>_info() and
should return an associative array of version and requirements information.
Currently, at least the following elements are required, but the more
information returned, the better.
plugin name
plugin version
minimum SquirrelMail
version
summary
details
requires_configuration
requires_source_patch
Depending on the plugin, other information such as required PHP extensions
or Pear packages should be included. Currently, SquirrelMail understands
the following values:
array(
'english_name' => 'My Plugin Name', // different than directory name
'version' => '1.0.0',
'required_sm_version' => '1.5.2',
'summary' => 'This is the short plugin description',
'details' => 'This is the long, detailed plugin description',
'requires_configuration' => 1, // can be 0 if no config file required
'requires_source_patch' => 0, // set to 2 if depends on SM version
'required_plugins' => array( // plugin name and version sub-array
'compatibility' => '2.0.3',
),
'required_php_modules' => array(
'recode'
),
'required_pear_packages' => array(
'Cache_Lite',
'MDB2',
),
'required_functions' => array(
'recode_string',
),
'required_php_version' => '4.1.0',
'other_requirements' => 'Courier Maildrop LDA',
'external_project_uri' => 'http://example.org/my_plugin'
);
The more of these that are returned to SquirrelMail, the more useful
and cooperative a plugin will be.
TODO: might be nice to see something like this for templates too (version/language(HTML,XML,XHTML,CHTML,WML,etc)/engine(Smarty,PHP,etc)/intended interface...
Prior to SquirrelMail 1.5.2, a function called <plugin name>_version()
was required. For the sake of compatibility, it can be piggy-backed on top of
the <plugin name>_info() function, such that setup.php looks like:
/**
* Returns info about this plugin
*
*/
function demo_info()
{
return array(
'english_name' => 'Favorite Colors Demo',
'version' => '1.0',
'required_sm_version' => '1.4.0',
'summary' => 'Shows user\'s favorite color in folder list.',
'details' => 'This plugin will show the user\'s favorite color above the folder list.',
'requires_configuration' => 0,
'requires_source_patch' => 0,
);
}
/**
* Returns version info about this plugin
*
*/
function demo_version()
{
$info = demo_info();
return $info['version'];
}
There is also a legacy version reporting mechanism that we would like plugin
authors to implement, since we are still in a transition period between the
older and newer reporting usages. The now deprecated reporting mechanism is to
create a file called version in the plugin directory. That file should
have only two lines: the first line should have the name of the plugin as
named on the SquirrelMail web site (this is often a prettified version of the plugin
directory name), and the second line must have the version and nothing more. So
for our "demo" plugin, whose name on the web site might be something like "Demo
Favorite Colors", the file plugins/demo/version should have these two
lines:
It is common to need a configuration file that holds some variables that are set
up at install time. For ease of installation and maintenance, you should place
all behavioral settings in a config file, isolated from the rest of your plugin
code. A typical file name to use is config.php. If you are using such a
file, you should not include a file called config.php in your plugin
distribution, but instead a copy of that file called config.sample.php.
This helps systems administrators avoid overwriting the config.php files
and losing all of their setup information when they upgrade your plugin.
All plugin authors should consider the security implications of their plugin.
Great care must always be taken if the plugin calls external programs, especially
ones that require set-uid permissions.
FIXME: more here about how to secure exec() calls with escapeshellarg() and named pipes method vs exec()
Storing sensitive data
If a plugin needs to store sensitive user configuration files or other such data,
please consider extra steps to secure such files. One very easy way to do so is
to wrap all configuration files in PHP tags (and C-style block comments if the
configuration data is not PHP code itself):
<?php /*
# below is example non-PHP configuration
# data that is secured from prying eyes
username_1 = admin
username_2 = super_admin
*/ ?>
Another approach is to store sensitive configuration data in SquirrelMail's
own $data directory, since most system administrators (at least those who
have read the installation instructions) know that the $data directory
needs to be protected and (hopefully) have made sure that it has been.
Never store unsecured configuration data that contains any user or system-specific
information in your plugin directory. Even the above suggestions
may not be sufficient depending on how sensitive the data is that you are
storing. In such a situation, you might think about a more complex encryption
scheme such as the one provided by the vadmin plugin.
Note that just shipping unsecured configuration files along with a
configuration file for Apache (.htaccess) is not sufficient because
not every system uses Apache as its web server.
Disallowing access for diabled plugins
In some cases, it is also prudent to make sure that the plugin doesn't perform
its function when it is not enabled. Any functions or files that contain PHP
code that is not wrapped inside a function can be protected from being
executed when the plugin is not activated by adding code similar to this:
if ( !is_plugin_enabled('my_plugin_name') ) {
exit('The plugin my_plugin_name isn't enabled in the SquirrelMail configuration.');
}
Note, however, that sometimes other plugins can legitimately access a
disabled plugin, so don't shoot yourself in the foot with too much protection.
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.
Hook types: parameters and return values
SquirrelMail uses four hook functions: do_hook(), do_hook_function(),
concat_hook_function(), and boolean_hook_function().
The do_hook() function is a simple function that allows injecting custom
HTML or override default interface data. It doesn't pass any data and doesn't
ask for anything back. 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!
Read more about the do_hook() function in the
stable and
development API documentation.
The do_hook_function() 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.
Read more about the do_hook_function() function in the
stable and
development API documentation.
The concat_hook_function() fixes some shortcomings of the
do_hook_function(). 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.
Read more about the concat_hook_function() function in the
stable and
development API documentation.
The boolean_hook_function() allows plugins to vote for a specified 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 a boolean TRUE or
FALSE values. 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 -- At least one TRUE will override all FALSE.
< 0 -- At least one FALSE will override all TRUE.
= 0 -- Majority wins. Any ties are broken with the last parameter in the
hook call.
The plugin's 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).
Read more about the boolean_hook_function() function in the
stable and
development API documentation.
List of hooks
This is a list of all hooks currently available in SquirrelMail, ordered by
file.
TODO: Update this list. (especially in 1.5.2, we are removing a great number of hooks to be replaced with the template_construct_<template> hook)
TODO: Reduce list of hooks by grouping them into separate chapters.
TODO: List is very out-of-synch with newest 1.5.2 and all its changes, as is the rest of the plugin documentation... much needs to be re-written
Hook name
Found in
Called with
Notes
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_function()
get_pref_override
functions/file_prefs.php
do_hook_function()
get_pref
functions/file_prefs.php
do_hook_function()
options_identities_process
functions/identity.php
do_hook()
&
options_identities_renumber
functions/identity.php
do_hook()
&%
special_mailbox
functions/imap_mailbox.php
do_hook_function()
rename_or_delete_folder
functions/imap_mailbox.php
do_hook_function()
%
folder_status
functions/imap_general.php (functions/imap_mailbox.php since 1.5.1)
do_hook_function()
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_function()
mailbox_display_buttons
functions/mailbox_display.php
do_hook()
mailbox_display_button_action
functions/mailbox_display.php
do_hook_function()
message_body
functions/mime.php
do_hook()
attachment $type0/$type1
functions/mime.php
do_hook()
^
attachment $type0/*
functions/mime.php
do_hook()
^
attachment */*
functions/mime.php
do_hook()
^
attachments_bottom
functions/mime.php
do_hook_function()
decode_body
functions/mime.php
do_hook_function()
generic_header
functions/page_header.php
do_hook()
menuline
functions/page_header.php
do_hook()
prefs_backend
functions/prefs.php
do_hook_function()
config_override (since 1.5.2)
include/init.php
do_hook()
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_function()
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_function()
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
do_hook() (concat_hook_function() since 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_function()
&
options_identities_buttons
src/options_identities.php
concat_hook_function()
&
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
do_hook_function()
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_function()
logout_above_text
src/signout.php
concat_hook_function()
info_bottom
plugins/info/options.php
do_hook()
O
% = This hook is used in multiple places in the given file
# = Called with hook type (see below)
O = Optional hook provided by a particular plugin
& = See "Identity hooks" below
^ = See "Attachment hooks" below
* = See "Options" below
! = See "Compose form hook" below
The address book hooks
SquirrelMail 1.4.5 and 1.5.1 introduced two hooks that allow custom address book
backends. These hooks are placed in functions/addressbook.php file.
The abook_add_class hook is a simple hook designed to load custom address
book classes before any other code is loaded. The abook_init hook allows to
modify the $abook object that represents the configured address books. The
hook is executed after the initiation of the local address book backends (file
and DB based ones) and before the remote (LDAP) backend init. The second
abook_init argument stores the address book object, and the third argument
stores the return value of the $abook->add_backend() method.
The attachment hooks
When a message has attachments, this hook is called for each attachment according
to its MIME type. For instance, a ZIP file will trigger a hook call to a hook
named "attachment application/x-zip". A plugin registered on such a hook typically
will show a link to do a specific action, such as "Verify" or "View" for a ZIP
file. Thus, to register your plugin for ZIP attachments, you'd do this in
setup.php (assuming your plugin is called "demo"):
This is a breakdown of the data passed in the array to the hook that is called:
[0] = An array of links for actions (see below) (alterable).
[1] = The message index of the first message on the message list page within
which the current message is found (startMessage). Can be used herein
for building URLs that point to the correct message list page.
[2] = The ID of the current message (id). Can be used herein to build URLs
that point to the correct message.
[3] = The mailbox name, encoded with urlencode() (urlMailbox). Can be used
herein to build URLs that point to the correct message list page.
[4] = The entity ID of the attachment inside the mail message (ent). Can
be used herein to build URLs that point to the correct attachment.
[5] = The default URL to go to when the filename is clicked upon (alterable,
but only one URL is allowed, thus, the last plugin to execute for
the current attachment wins out - usually it's better to just add a
new action to array element 1 above).
[6] = The filename that is displayed for the attachment.
[7] = The "where" criteria that was used to find the current message (where)
(only applicable when the message was in fact found using a search).
Can be used herein to build URLs that point to the correct message list
page.
[8] = The "what" criteria that was used to find the current message (what)
(only applicable when the message was in fact found using a search).
Can be used herein to build URLs that point to the correct message list
page.
To set up links for actions, add them to the array data passed in to the hook
like this:
$args[0]['<plugin_name>']['href'] = 'link URL';
$args[0]['<plugin_name>']['text'] = _("Text to display");
$args[0]['<plugin_name>']['extra'] =
'extra stuff, such as an <img ...> tag (will be part of the clickable link)';
Note: The syntax of _("Text to display") is explained in the section about
internationalization.
You can leave the text empty and put an image tag in extra to show an
image-only link for the attachment, or do the opposite (leave extra empty)
to display a text-only link.
It's also possible to specify a hook as "attachment type0/*", for example
"attachment text/*". This hook will be executed whenever there's no more
specific rule available for that type.
There is also an "attachment */*" hook for plugins that want to handle any
and all attachments, regardless of their mime types. Please use conservatively.
Putting all this together, the demo_handle_zip_attachment() function should
look like this (note how the argument is being passed):
function demo_handle_zip_attachment(&$args) {
include_once(SM_PATH . 'plugins/demo/functions.php');
demo_handle_zip_attachment_do($args);
}
And the demo_handle_zip_attachment_do() function in the
plugins/demo/functions.php file would typically (but not necessarily)
display a custom link:
Note that the text domain has to be changed to properly translate the link text.
The file plugins/demo/zip_handler.php can now do whatever it needs with
the attachment (note that the link will hand information about how to retrieve the
source message from the IMAP server as GET varibles).
The compose form hook
The compose_form hook allows plugins to insert their own code into the form
tag for the main message composition HTML form. Usually plugins will want to
insert some kind of code in an onsubmit event handler. In order to allow
more than one plugin to do so, all plugins using this hook to add some
onsubmit code need to add that code (without the enclosing attribute name
and quotes) as a new array entry to the global $compose_onsubmit array. The
code should use "return false" if the plugin has found a reason to stop form
submission, otherwise it should do nothing (that is, please do not use
"return true", as that will prevent other plugins from using the onsubmit
handler). SquirrelMail itself will insert a final "return true". All
onsubmit code will be enclosed in double quotes by SquirrelMail, so plugins
need to quote accordingly if needed. For example:
global $compose_onsubmit;
$compose_onsubmit[] = ' if (somevar == \'no\') return false; ';
Note the escaped single quotes. If you use double quotes, they would have to be
escaped as such:
global $compose_onsubmit;
$compose_onsubmit[] = ' if (somevar == \'no\') { alert(\\"Sorry\\"); return false; }';
Any other form tag additions by a plugin (beside onsubmit event code) can
currently be echoed directly to the browser.
The configuration testing hook
SquirrelMail has a script called configtest.php which can be used to test
the SquirrelMail configuration. Since SquirrelMail 1.5.2, the configtest script
includes the configtest hook. The hook uses the boolean hook function call.
Plugins that are attached to this hook should return a boolean TRUE if
they detect any errors in the plugin's configuration. Verbose messages can be
printed with a do_err('error message', FALSE) function call or with any
standard PHP inline output function.
The configtest script is executed in an anonymous, unauthenticated environment, so
username and password information isn't available as it would be in all other
SquirrelMail scripts. Only a limited set of functions are loaded. The
do_err() function is a special configtest script
function, which is used to print error messages. If a plugin uses the
do_err() function, it is recommended to set the second function argument to
FALSE even on fatal errors. The configuration testing will stop on a fatal
error, that is, if the hook call returns a boolean TRUE, and it's best to
let the script continue checking for other system configuration problems.
The identity hooks
This set of hooks allows plugins to add options to the SquirrelMail identity
preferences.
This set of hooks is passed special information in the array of arguments:
options_identities_process
This hook is called at the top of the "Identities" page, which is most useful
when the user has changed any identity settings. This is where you'll want to
save any custom information you are keeping for each identity or catch any
custom submit buttons that you may have added to the identities page. In
SquirrelMail 1.4.4 or older, and in 1.5.0, the arguments to this hook are:
[0] = hook name (always "options_identities_process")
[1] = should I run the <tt/SaveUpdateFunction()/ (alterable)
By default, SaveUpdateFunction() isn't called. If you want to trigger it,
you have to set the second array element to 1 or TRUE.
Since SquirrelMail 1.4.6 and 1.5.1, the arguments to this hook are:
[0] = hook name (always "options_identities_process")
[1] = action (hook is used only in 'update' action and any custom
action added to form with option_identities_table and
option_identities_buttons hooks)
[2] = processed identity number
This hook isn't available in SquirrelMail 1.4.5.
options_identities_renumber
This hook is called when one of the identities is being renumbered. If a user
has three identities and deletes the second, this hook would be called with an
array that looks like this: ('options_identities_renumber', 2, 1). The
arguments to this hook are:
[0] = hook name (always "options_identities_renumber")
[1] = being renumbered from ('default' or 1 through (# idents) - 1)
[2] = being renumbered to ('default' or 1 through (# idents) - 1)
This hook isn't available in SquirrelMail 1.4.5, and the renumbering order
differs since 1.4.5 and 1.5.1.
options_identities_table
This hook allows you to insert additional rows into the table that holds each
identity. The arguments to this hook are:
[0] = additional HTML attributes applied to table row.
use it like this in your plugin:
<tr "<?php echo $args[0]; ?>">
[1] = is this an empty section (the one at the end of the list)?
[2] = what is the 'post' value? (ident # or empty string if default)
You need to return any HTML you would like to add to the table. You could add a
table row with code similar to this:
The first hook argument was modified in 1.4.5 and 1.5.1. In SquirrelMail
1.4.1-1.4.4 and 1.5.0i, the argument only contains the background color. You
should use <tr bgcolor="<?php echo $args[0]; ?>"> in these
SquirrelMail versions.
options_identities_buttons
This hook allows you to add a button (or other HTML) to the row of buttons under
each identity. The arguments to this hook are:
[0] = is this an empty section (the one at the end of the list)?
[1] = what is the 'post' value? (ident # or empty string if default)
You need to return any HTML you would like to add here. You could add a button
with code similar to this:
function demo_identities_button(&$args) {
return '<input type="submit" name="demo_button_' . $args[1] .
'" value="Press Me" />';
}
Since SquirrelMail 1.4.6 and 1.5.1, the input element should use a
smaction[action_name][identity_no] value in the name attribute if you
want to process your button actions in the options_identity_process hook.
See sample implementation of identity hooks in the SquirrelMail demo plugin.
svn co https://squirrelmail.svn.sourceforge.net/svnroot/squirrelmail/trunk/squirrelmail/plugins/demo
The preference hooks
These hooks are used when you want to add preferences to existing preference
pages. See
Preferences.
Requesting new hooks
It's impossible for the SquirrelMail team to foresee all of the places where
hooks might be useful, so you might need to negotiate the insertion of a new
hook to make your plugin work. Start by writing a patch which will insert the
hook you want to add, and mail your request (with the patch attached) to the
SquirrelMail development mailing list. Don't forget to explain the need for the
new hook in your message.
Before you start adding user preferences to your plugin, please take a moment to
think about it: in some cases, more preferences may not be a good thing. Having
too many preferences can be confusing. Thinking from the user's perspective,
will the proposed preferences actually be used? Will users understand what these
preferences are for? Consider this carefully.
If you decide that more preferences really are needed for your plugin, there are
two ways to add them. When there's only a few preferences that doesn't merit an
entirely new preferences page, you can incorporate them as a section in an
existing SquirrelMail preference section ("Personal Information", "Display
Preferences", "Message Highlighting", "Folder Preferences", or "Index Order").
If there's an extensive number of preferences, a separate page might be needed.
There may also be other reasons to create a separate page for the user to
interact with.
Adding preferences to an existing page
There are two ways to accomplish the integration of your plugin's settings into
another preferences page. The first, and preferred, way to add preferences to an
existing preference page is to use one of the "optpage_loadhook_<pref
page> hooks. The sent_subfolders plugin has an excellent example of
this method. Briefly, this way of adding preferences consists of adding some
plugin-specific information to a predefined data structure which SquirrelMail
then uses to build the HTML input forms for you.
As an example, we'll use the optpage_loadhook_display hook to add a new
group of preferences to the display preferences page. First, we inform
SquirrelMail that we want to use this hook by adding a line to the
squirrelmail_plugin_init_demo() function in
plugins/demo/setup.php:
Make sure that the function demo_options() calls another function, located
elsewhere, called demo_options_do(). The demo_options() function needs
to add a new key to two arrays: $optpage_data['grps'] and
$optpage_data['vals']. The value associated with the key in
$optpage_data['grps'] should simply be the plugin's section heading at the
preferences page, and the value associated with the key in
$optpage_data['vals'] an array with all the plugin's preferences. (Yes,
that's four levels of nested arrays.) The specified preference attributes are
used by SquirrelMail to build the HTML input elements automatically. This
example includes just one input element, a select (drop-down) list:
The array used to specify each plugin preference has the following possible
attributes:
name
The preference name, used both as a key when saving the preferences and as the
input element name.
caption
The text that prefaces this preference.
trailing_text (optional)
A text that follows a text input or a select list. This is useful for
indicating units, meanings of special values, etc.
type
The type of input element, which should be one of:
SMOPT_TYPE_STRING for a string/text.
SMOPT_TYPE_STRLIST for a select list.
SMOPT_TYPE_TEXTAREA for a text area.
SMOPT_TYPE_INTEGER for an integer.
SMOPT_TYPE_FLOAT for a floating point number.
SMOPT_TYPE_BOOLEAN for a boolean (yes/no) radio buttons.
SMOPT_TYPE_HIDDEN for a hidden input (not actually shown on preferences page).
SMOPT_TYPE_COMMENT for a showing the text specified by the comment attribute, but no user input is needed.
SMOPT_TYPE_FLDRLIST for a select list of IMAP folders.
refresh (optional)
Indicates if a link should be shown to refresh part or all of the window after
saving. Possible values are:
SMOPT_REFRESH_NONE doesn't show a refresh link.
SMOPT_REFRESH_FOLDERLIST shows a link for refreshing the folder list.
SMOPT_REFRESH_ALL shows a link for refreshing the entire window.
initial_value
The value that should initially be placed in this input element.
posvals
For select lists, this should be an associative array where each key is an
actual input value and the corresponding value is what is displayed to the user
for that list item in the drop-down list.
save
You may indicate that special functionality needs to be used instead of just
saving this setting by giving the name of a function to call when this value
would otherwise just be saved in the user's preferences.
size
Specifies the size of certain input elements (typically textual inputs).
Possible values are:
SMOPT_SIZE_TINY
SMOPT_SIZE_SMALL
SMOPT_SIZE_MEDIUM
SMOPT_SIZE_LARGE
SMOPT_SIZE_HUGE
SMOPT_SIZE_NORMAL
comment
For SMOPT_TYPE_COMMENT type preferences, this is the text displayed to the
user.
extra_attributes
This is where you may add any additional JavaScript or other attributes to the
preference element. The value of this setting should be an array, where the
keys of the array are the attribute names as you expect them to show up in the
resulting HTML, and the values are the corresponding attribute values. For
example, you could add an "onchange" JavaScript handler to a drop-down list:
$optpage_data['vals'][1][] = array(
'extra_attributes' => array('onchange' => 'alert("Thank you for changing the drop-down list!");'),
...
post_script
You may specify some script (usually JavaScript) that will be placed after
(outside of) the input element.
htmlencoded
Disables HTML sanitizing. Don't disable the sanitizing if user input is
possible, unless your plugin uses its own sanitizing functions. Currently only
works with SMOPT_TYPE_STRLIST.
folder_filter
Controls folder list limits in SMOPT_TYPE_FLDRLIST widget. See the
$flag argument in the sqimap_mailbox_option_list() function.
Available since 1.5.1.
Note that you do not have to create a whole new section at the preferences page
if you merely want to add a simple input item or two to a preferences section
that already exists. For example, the "Display Options" page has these groups:
If you indicated a save attribute for any of your preferences, you must
create that function (you'll only need to do this if you need to do some special
processing for one of your settings). The function gets one parameter, which is
an object with mostly the same attributes you defined when you made the
preference above. The new_value (and possibly value, which is the
current value for this setting) is the most useful attribute in this context:
function save_plugin_demo_favorite_color($option) {
// if user chose orange, make note that they are really dumb
if ($option->new_value == 3) {
// more code here as needed
}
// don't even save this setting if user chose green (old setting will remain)
if ($option->new_value == 2) {
return;
}
// for all other colors, save as normal
save_option($option);
}
The second, deprecated, legacy method is to add the HTML code for your
preferences directly to the preferences page of your choice. Although currently
very popular, avoid it if you can. That said, here is how it works. Look for any
of the hooks named as options_<pref page>_inside, where <pref
page> is "display", "personal", etc. For this example, we'll use
options_display_inside and, as above, "demo" as our plugin name:
Inform SquirrelMail that we want to use this hook by adding a line to the
squirrelmail_plugin_init_demo() function in
plugins/demo/setup.php:
Note that there are also hooks such as options_display_bottom, however,
they place your preferences at the bottom of the preferences page, which is
usually not desirable (mostly because they also come after the closure of
the HTML form element). It is possible to use these hooks if you want to
create your own form with custom submission logic.
The function demo_show_options_do() should have output similar to this
(note that you will be inserting code into a table that is already defined with
two columns, so please be sure to keep this framework in your plugin):
Of course, you can place any text where "OPTION_NAME" is and any input tags
where "OPTION_INPUT" is.
You will want to use the options_<pref page>_save hook (in this case,
options_display_save) to save the user's settings after they have pressed
the "Submit" button. Again, inform SquirrelMail that we want to use this hook by
adding a line to the squirrelmail_plugin_init_demo() function in
plugins/demo/setup.php:
The function demo_save_options_do() should put the user's settings into
permanent storage (see
Saving and retrieving preferences below for more information). This example assumes that in the
preferences page, the input element's name attribute was set to
"demo_option":
global $data_dir, $username;
sqgetGlobalVar('demo_option', $demo_option);
setPref($data_dir, $username, 'demo_option', $demo_option);
Creating a custom preference page
It is also possible to create a custom preference page for a plugin. This is
particularly useful when the plugin has numerous preferences or needs to offer
special interaction with the user (for things such as changing password, etc.).
Here is an outline of how to do so (again, using the "demo" plugin name):
First of all you have to add a new listing to the main "Options page". Always
use the optpage_register_block hook where you create a simple array that
lets SquirrelMail build the HTML to add the plugin preferences entry
automatically. Inform SquirrelMail that we want to use this hook by adding a
line to the squirrelmail_plugin_init_demo() function in
plugins/demo/setup.php:
Assuming the function demo_options_block() calls another function elsewhere
called demo_options_block_do(), that function only needs to create a simple
array and add it to the $optpage_blocks array:
global $optpage_blocks;
bindtextdomain('demo', SM_PATH . 'locale');
textdomain('demo');
$optpage_blocks[] = array(
'name' => _("Favorite Color Settings"),
'url' => SM_PATH . 'plugins/demo/options.php',
'desc' => _("Change your favorite color and find new exciting colors"),
'js' => FALSE
);
bindtextdomain('squirrelmail', SM_PATH . 'locale');
textdomain('squirrelmail');
The array must have four elements:
name
The title of the plugin's preference page as it will be displayed at the Options
page. Note that the text domain has to be changed to properly translate this text.
url
The URI that points to your plugin's custom preferences page.
desc
A description of what the preferences page offers the user. This is displayed at
the Options page below the title. Note that the text domain has to be changed to
properly translate this text.
js
Indicates if this preference page requires the client browser to be
JavaScript-capable. Should be TRUE or FALSE.
There are a few places in a plugin, such as when hooking into the "menuline" or
"optpage_register_block" hooks, where you can provide a link to a file that is
called directly by the client browser. No matter what that page does, it should
always validate that the calling client has a current login session. Thus, all
such pages should start with the following code:
/*
* Pages that are called directly by the client browser need to load the
* SquirrelMail framework. This differs between various SquirrelMail versions.
* Plugins that don't support all SquirrelMail versions can strip some of this
* code away.
*/
if (file_exists('../../include/init.php')) {
/*
* "include/init.php" is used since SquirrelMail 1.5.2. This isn't needed
* for plugins that don't support SquirrelMail 1.5.2 and later.
*/
include_once('../../include/init.php');
} elseif (file_exists('../../include/validate.php')) {
/*
* "include/validate.php" is used since SquirrelMail 1.4.0 until version
* 1.5.1.
*/
define('SM_PATH', '../../');
include_once(SM_PATH . 'include/validate.php');
} else {
/*
* "src/validate.php" is used in SquirrelMail 1.2. This isn't needed for
* plugins that don't support SquirrelMail 1.2.
*/
chdir('..');
define('SM_PATH', '../');
include_once(SM_PATH . 'src/validate.php');
}
TODO: Possibly provide link to the include hierarchy provided by init.php/validate.php
Whenever new versions of SquirrelMail are released, there is always a
considerable lag time before it is widely adopted. During that transitional
time, especially when the new SquirrelMail version contains any architectural
and/or functional changes, plugin developers are put in a unique and very
difficult position. That is, there will be people running both the old and new
versions of SquirrelMail who want to use your plugin, and you will probably want
to accommodate them both.
One way to keep both sides happy is to keep two different versions of
your plugin up to date, one that runs under the older SquirrelMail, and one that
requires the newest SquirrelMail. This is inconvenient, however, especially if
you are continuing to develop the plugin.
Depending on the changes implemented in the new SquirrelMail version, another
approach you may be able to use is to include code that can auto-sense the
SquirrelMail version and make adjustments on the fly. There is a function called
check_sm_version() available which does that. Read more about it in the
stable and
development API documentation.
Finally, there is a plugin called "Compatibility" that is intended to solve
this problem without requiring any special coding on the part of plugin authors.
Authors can develop one version of their plugin and seamlessly support any
SquirrelMail version. Plugin code is typically developed against the newest
SquirrelMail release, and users running older versions of SquirrelMail can
use said plugin as long as they also have the "Compatibility" plugin. For
more inforamtion, please download "Compatibility" and read its README file.
Two more files are needed in your plugin directory. One file shall describes
your plugin and offers detailed instructions for configuration or help with
troubleshooting, etc. This file is usually entitled README. Some useful
sections to include might be:
There are a few files that you should make sure to include when you build your
final plugin distribution.
Plugin name and author
Current version
Plugin features
Detailed plugin description
How-to for plugin configuration
Change log
Future ideas/enhancements/to do list
The other files shall explain how to install your plugin. This file is typically
called INSTALL. If you do not require any special installation actions, you
can probably copy one from another plugin or use this as a template:
Installing the Demo plugin
==========================
1) Start with untaring the file into the plugins directory. Here is an example
for the 1.0 version of the Demo plugin.
cd plugins
tar -zxvf demo-1.0-1.4.0.tar.gz
2) Change into the demo directory, copy "config.php.sample" to "config.php" and
edit "config.php", making adjustments as you deem necessary. For more
detailed explanations about each of these parameters, consult the README
file.
cd demo
cp config.php.sample config.php
vi config.php
3) Then go to your config directory and run "conf.pl". Choose option 8 and move
the plugin from the "Available Plugins" category to the "Installed Plugins"
category. Save and exit.
cd ../../config/
./conf.pl
Upgrading the Demo plugin
=========================
1) Start with untaring the file into the plugins directory. Here is a example
for the 3.1 version of the demo plugin.
cd plugins
tar -zxvf demo-3.1-1.4.0.tar.gz
2) Change into the demo directory, check your "config.php" file against the new
version, to see if there are any new settings that you must add to your
"config.php" file.
diff -Nau config.php config.php.sample
Or simply replace your "config.php" file with the provided sample and
reconfigure the plugin from scratch (see step 2 under the installation
procedure above).
As long as you've consulted the list of plugin standards and done your best to
follow them, there's little standing in the way of great fame as an official
SquirrelMail plugin developer.
First you have to make a distribution file. The file should be named
demo-1.0-1.4.0.tar.gz, where "demo" is the name of your plugin, "1.0" is
the version of your plugin, and "1.4.0" is the version of SquirrelMail required
to use your plugin.
You can create the distribution file in most *nix environments by running this
command from the plugins directory (NOT your plugin directory):
tar -czvf demo-1.0-1.4.0.tar.gz demo
When the plugin is ready to be reviewed by the SquirrelMail plugin team, mail it
to the SquirrelMail plugins team members. Also consider asking on the SquirrelMail
plugins mailing list for access to upload your plugin to the
official third party plugin list after your plugin is approved.
When the plugin is approved and you're granted access to upload it, all you have
to do is the actual uploading along with filling out some general information
about the plugin and what it does.