342ac78b13c1cfb8ed92eb4d9e28913f4ea14d44
[squirrelmail.git] / doc / plugin.txt
1 $Id$
2
3 In addition to this document, please check out the SquirrelMail 
4 development FAQ for more information.  Also, help writing plugins 
5 is easily obtained by posting to the squirrelmail-plugins mailing
6 list.  (See details about mailing lists on the website)
7
8 FAQ -> http://www.squirrelmail.org/wiki/wiki.php?DeveloperFAQ
9 Plugin Development -> 
10        http://www.squirrelmail.org/wiki/wiki.php?DevelopingPlugins
11
12
13 A FEW NOTES ON THE PLUGIN ARCHITECTURE
14 ======================================
15
16 The plugin architecture of SquirrelMail is designed to make it possible 
17 to add new features without having to patch SquirrelMail itself. 
18 Functionality like password changing, displaying ads and calendars should 
19 be possible to add as plugins.
20
21
22 The Idea
23 --------
24
25 The idea is to be able to run random code at given places in the
26 SquirrelMail code. This random code should then be able to do whatever
27 needed to enhance the functionality of SquirrelMail. The places where
28 code can be executed are called "hooks".
29
30 There are some limitations in what these hooks can do. It is difficult
31 to use them to change the layout and to change functionality that
32 already is in SquirrelMail.
33
34 Some way for the plugins to interact with the help subsystem and
35 translations will be provided.
36
37
38 The Implementation
39 ------------------
40
41 The plugin jumping off point in the main SquirrelMail code is in the
42 file functions/plugin.php.  In places where hooks are made available,
43 they are executed by calling the function do_hook('hookname').  The 
44 do_hook function then traverses the array 
45 $squirrelmail_plugin_hooks['hookname'] and executes all the functions 
46 that are named in that array.  Those functions are placed there when 
47 plugins register themselves with SquirrelMail as discussed below.  A 
48 plugin may add its own internal functions to this array under any 
49 hook name provided by the SquirrelMail developers.
50
51 A plugin must reside in a subdirectory in the plugins/ directory. The
52 name of the subdirectory is considered to be the name of the plugin.  
53 (The plugin will not function correctly if this is not the case.)
54
55 To start using a plugin, its name must be added to the $plugins array
56 in config.php like this:
57
58    $plugins[0] = 'plugin_name';
59
60 When a plugin is registered, the file plugins/plugin_name/setup.php is
61 included and the function squirrelmail_plugin_init_plugin_name() is
62 called with no parameters.  That function is where the plugin may 
63 register itself against any hooks it wishes to take advantage of.
64
65
66 WRITING PLUGINS
67 ===============
68
69 All plugins must contain a file called setup.php and must include a 
70 function called squirrelmail_plugin_init_plugin_name() therein.  Since 
71 including numerous plugins can slow SquirrelMail performance 
72 considerably, the setup.php file should contain little else.  Any 
73 functions that are registered against plugin hooks should do little 
74 more than call another function in a different file.
75
76 Any other files used by the plugin should also be placed in the 
77 plugin directory (or subdirectory thereof) and should contain the 
78 bulk of the plugin logic.
79
80 The function squirrelmail_plugin_init_plugin_name() is called to
81 initalize a plugin. This function could look something like this (if 
82 the plugin was named "demo" and resided in the directory plugins/demo/):
83
84 function squirrelmail_plugin_init_demo () 
85 {
86    global $squirrelmail_plugin_hooks;
87
88    $squirrelmail_plugin_hooks['generic_header']['demo'] = 'plugin_demo_header';
89    $squirrelmail_plugin_hooks['menuline']['demo'] = 'plugin_demo_menuline';
90 }
91
92 Please note that as of SquirrelMail 1.5.0, this function will no longer
93 be called at run time and will instead be called only once at configure-
94 time.  Thus, the inclusion of any dynamic code (anything except hook 
95 registration) here is strongly discouraged.
96
97 In this example, the "demo" plugin should also have two other functions
98 in its setup.php file called plugin_demo_header() and plugin_demo_menuline().
99 The first of these might look something like this:
100
101 function plugin_demo_header()
102 {
103    include_once(SM_PATH . 'plugins/demo/functions.php');
104    plugin_demo_header_do();
105 }
106
107 The function called plugin_demo_header_do() would be in the file called
108 functions.php in the demo plugin directory and would contain the plugin's
109 core logic for the "generic_header" hook.
110
111
112 Including Other Files
113 ---------------------
114
115 A plugin may need to reference functionality provided in other 
116 files, and therefore need to include those files.  Most of the
117 core SquirrelMail functions are already available to your plugin
118 unless it has any files that are requested directly by the client
119 browser (custom options page, etc.).  In this case, you'll need 
120 to make sure you include the files you need (see below).
121
122 Note that as of SquirrelMail 1.4.0, all files are accessed using a
123 constant called SM_PATH that always contains the relative path to
124 the main SquirrelMail directory.  This constant is always available 
125 for you to use when including other files from the SquirrelMail core, 
126 your own plugin, or other plugins, should the need arise.  If any of 
127 your plugin files are requested directly from the client browser, 
128 you will need to define this constant before you do anything else:
129
130   define('SM_PATH', '../../');
131
132 Files are included like this:
133
134   include_once(SM_PATH . 'include/validate.php');
135
136 When including files, please make sure to use the include_once() function
137 and NOT include(), require(), or require_once(), since these all are much 
138 less efficient than include_once() and can have a cumulative effect on 
139 SquirrelMail performance.
140
141 The files that you may need to include in a plugin will vary greatly
142 depending upon what the plugin is designed to do.  For files that are
143 requested directly by the client browser, we strongly recommend that 
144 you include the file include/validate.php, since it will set up the 
145 SquirrelMail environment automatically.  It will ensure the the user 
146 has been authenticated and is currently logged in, load all user 
147 preferences, include internationalization support, call stripslashes()
148 on all incoming data (if magic_quotes_gpc is on), and initialize and 
149 include all other basic SquirrelMail resources and functions.  You may 
150 see other plugins that directly include other SquirrelMail files, but 
151 that is no longer necessary and is a hold-over from older SquirrelMail 
152 versions.
153
154
155 Hook Types:  Parameters and Return Values
156 -----------------------------------------
157
158 Hooks, when executed, are called with one parameter, an array of data
159 that is passed to the hook.  The first element in the array is the name
160 of the hook that is being called.  Any other elements in the array are
161 dependant on the type of hook that is being called.  Most hooks do not
162 pass any other data, but be sure to check the hook you are using for 
163 any useful information it may provide.  Generally speaking, in the case 
164 that any extra data is available here, your plugin should NOT change 
165 it unless you know what you are doing or it is documented otherwise.
166 See below for further discussion of special hook types and the values
167
168 Most hooks, when executed, are called using the do_hook() function,
169 where no return value is used.  There are a limited number of hooks, 
170 however, that are called using the do_hook_function() and 
171 concat_hook_function() function calls.  Both of these hook types may
172 use the value returned by your plugin for its own purposes or to
173 display in the resultant HTML output (you need to research the specific
174 hook to determine its use).  The do_hook_function() type hook will 
175 only use the return value it retrieves from the LAST plugin in the 
176 list of plugins registered against such a hook, whereas the
177 concat_hook_function() type hook will concatenate the return values
178 from all plugins that are registered against the hook and use that
179 value (usually as a string of HTML code to output to the client).
180
181
182 List of Hooks
183 -------------
184
185 This is a list of all hooks currently available in SquirrelMail, ordered
186 by file.  Note that this list is accurate as of June 17, 2003 (should be
187 close to what is contained in release 1.4.1, plus or minus a hook or two),
188 but may be out of date soon thereafter.  You never know.  ;-)
189
190   Hook Name                      Found In                        Called With(#)
191   ---------                      --------                        --------------
192   loading_constants              functions/constants.php         do_hook
193   get_pref_override              functions/file_prefs.php        hook_func
194   get_pref                       functions/file_prefs.php        hook_func
195   special_mailbox                functions/imap_mailbox.php      hook_func
196   % rename_or_delete_folder      functions/imap_mailbox.php      hook_func
197   msg_envelope                   functions/mailbox_display.php   do_hook
198   mailbox_index_before           functions/mailbox_display.php   do_hook
199   mailbox_form_before            functions/mailbox_display.php   do_hook
200   mailbox_index_after            functions/mailbox_display.php   do_hook
201   check_handleAsSent_result      functions/mailbox_display.php   do_hook
202   subject_link                   functions/mailbox_display.php   concat_hook
203   mailbox_display_buttons        functions/mailbox_display.php   do_hook
204   message_body                   functions/mime.php              do_hook
205   ^ attachment $type0/$type1     functions/mime.php              do_hook
206   attachments_bottom             functions/mime.php              hook_func
207   decode_body                    functions/mime.php              hook_func
208   generic_header                 functions/page_header.php       do_hook
209   menuline                       functions/page_header.php       do_hook
210   internal_link                  functions/page_header.php       hook_func
211   loading_prefs                  include/load_prefs.php          do_hook
212   addrbook_html_search_below     src/addrbook_search_html.php    do_hook
213   addressbook_bottom             src/addressbook.php             do_hook
214   compose_form                   src/compose.php                 do_hook
215   compose_bottom                 src/compose.php                 do_hook
216   compose_button_row             src/compose.php                 do_hook
217   compose_send                   src/compose.php                 do_hook
218   folders_bottom                 src/folders.php                 do_hook
219   help_top                       src/help.php                    do_hook
220   help_chapter                   src/help.php                    do_hook
221   help_bottom                    src/help.php                    do_hook
222   left_main_after_each_folder    src/left_main.php               concat_hook
223   left_main_before               src/left_main.php               do_hook
224   left_main_after                src/left_main.php               do_hook
225   create_collapse_link           src/left_main.php               hook_func
226   login_cookie                   src/login.php                   do_hook
227   login_top                      src/login.php                   do_hook
228   login_form                     src/login.php                   do_hook
229   login_bottom                   src/login.php                   do_hook
230   move_before_move               src/move_messages.php           do_hook
231   move_messages_button_action    src/move_messages.php           concat_hook
232   * optpage_set_loadinfo         src/options.php                 do_hook
233   * optpage_loadhook_personal    src/options.php                 do_hook
234   * optpage_loadhook_display     src/options.php                 do_hook
235   * optpage_loadhook_highlight   src/options.php                 do_hook
236   * optpage_loadhook_folder      src/options.php                 do_hook
237   * optpage_loadhook_order       src/options.php                 do_hook
238   * options_personal_save        src/options.php                 do_hook
239   * options_display_save         src/options.php                 do_hook
240   * options_folder_save          src/options.php                 do_hook
241   * options_save                 src/options.php                 do_hook
242   * optpage_register_block       src/options.php                 do_hook
243   * options_link_and_description src/options.php                 do_hook
244   * options_personal_inside      src/options.php                 do_hook
245   * options_display_inside       src/options.php                 do_hook
246   * options_highlight_inside     src/options.php                 do_hook
247   * options_folder_inside        src/options.php                 do_hook
248   * options_order_inside         src/options.php                 do_hook
249   * options_personal_bottom      src/options.php                 do_hook
250   * options_display_bottom       src/options.php                 do_hook
251   * options_highlight_bottom     src/options.php                 do_hook
252   * options_folder_bottom        src/options.php                 do_hook
253   * options_order_bottom         src/options.php                 do_hook
254   * options_highlight_bottom     src/options_highlight.php       do_hook
255   & options_identities_process   src/options_identities.php      do_hook
256   & options_identities_top       src/options_identities.php      do_hook
257   &% options_identities_renumber src/options_identities.php      do_hook
258   & options_identities_table     src/options_identities.php      concat_hook
259   & options_identities_buttons   src/options_identities.php      concat_hook
260   message_body                   src/printer_friendly_bottom.php do_hook
261   read_body_header               src/read_body.php               do_hook
262   read_body_menu_top             src/read_body.php               do_hook
263   read_body_menu_bottom          src/read_body.php               do_hook
264   read_body_header_right         src/read_body.php               do_hook
265   html_top                       src/read_body.php               do_hook
266   read_body_top                  src/read_body.php               do_hook
267   read_body_bottom               src/read_body.php               do_hook
268   html_bottom                    src/read_body.php               do_hook
269   login_before                   src/redirect.php                do_hook
270   login_verified                 src/redirect.php                do_hook
271   generic_header                 src/right_main.php              do_hook
272   right_main_after_header        src/right_main.php              do_hook
273   right_main_bottom              src/right_main.php              do_hook
274   search_before_form             src/search.php                  do_hook
275   search_after_form              src/search.php                  do_hook
276   search_bottom                  src/search.php                  do_hook
277   logout                         src/signout.php                 do_hook
278   webmail_top                    src/webmail.php                 do_hook
279   webmail_bottom                 src/webmail.php                 do_hook
280   logout_above_text              src/signout.php                 concat_hook
281    
282 % = This hook is used in multiple places in the given file
283 # = Called with hook type (see below)
284 & = Special identity hooks (see below)
285 ^ = Special attachments hook (see below)
286 * = Special options hooks (see below)
287
288
289 (#) Called With
290 ---------------
291 Each hook is called using the hook type specified in the list above:
292    do_hook       do_hook()
293    hook_func     do_hook_function()
294    concat_hook   concat_hook_function()
295
296
297 (&) Identity Hooks
298 ------------------
299 This set of hooks is passed special information in the array of arguments:
300
301 options_identities_process
302
303    This hook is called at the top of the Identities page, which is 
304    most useful when the user has changed any identity settings - this
305    is where you'll want to save any custom information you are keeping
306    for each identity or catch any custom submit buttons that you may
307    have added to the identities page.  The arguments to this hook are:
308
309       [0] = hook name (always "options_identities_process")
310       [1] = should I run the SaveUpdateFunction() (alterable)
311
312    Obviously, set the second array element to 1/true if you want to
313    trigger SaveUpdateFunction() after the hook is finished - by default,
314    it will not be called.
315
316 options_identities_renumber
317
318    This hook is called when one of the identities is being renumbered,
319    such as if the user had three identities and deletes the second - 
320    this hook would be called with an array that looks like this:
321    ('options_identities_renumber', 2, 1).  The arguments to this hook
322    are:
323
324       [0] = hook name (always "options_identities_renumber")
325       [1] = being renumbered from ('default' or 1 through (# idents) - 1)
326       [2] = being renumbered to ('default' or 1 through (# idents) - 1)
327    
328 options_identities_table
329
330    This hook allows you to insert additional rows into the table that
331    holds each identity.  The arguments to this hook are:
332
333       [0] = color of table (use it like this in your plugin:
334                <tr bgcolor="<?PHP echo $info[1]?>">
335       [1] = is this an empty section (the one at the end of the list)?
336       [2] = what is the 'post' value? (ident # or empty string if default)
337
338    You need to return any HTML you would like to add to the table.
339    You could add a table row with code similar to this:
340
341       function demo_identities_table(&$args) 
342       {
343          return '<tr bgcolor="' . $args[0] . '"><td>&nbsp;</td><td>'
344               . 'YOUR CODE HERE' . '</td></tr>' . "\n";
345       }
346   
347 options_identities_buttons
348
349    This hook allows you to add a button (or other HTML) to the row of
350    buttons under each identity.  The arguments to this hook are:
351
352       [0] = is this an empty section (the one at the end of the list)?
353       [1] = what is the 'post' value? (ident # or empty string if default)
354
355    You need to return any HTML you would like to add here.  You could add 
356    a button with code similar to this:
357
358       function demo_identities_button(&$args)
359       {
360          return '<input type="submit" name="demo_button_' . $args[1]
361               . '" value="Press Me">';
362       }
363
364
365 (^) Attachment Hooks
366 --------------------
367 When a message has attachments, this hook is called with the MIME types.  For
368 instance, a .zip file hook is "attachment application/x-zip".  The hook should
369 probably show a link to do a specific action, such as "Verify" or "View" for a
370 .zip file.  Thus, to register your plugin for .zip attachments, you'd do this
371 in setup.php (assuming your plugin is called "demo"):
372
373    $squirrelmail_plugin_hooks['attachment application/x-zip']['demo']
374       = 'demo_handle_zip_attachment';
375
376 This is a breakdown of the data passed in the array to the hook that is called:
377
378   [0] = Hook's name ('attachment text/plain')
379   [1] = Array of links of actions (see below) (alterable)
380   [2] = Used for returning to mail message (startMessage)
381   [3] = Used for finding message to display (id)
382   [4] = Mailbox name, urlencode()'d (urlMailbox)
383   [5] = Entity ID inside mail message (ent)
384   [6] = Default URL to go to when filename is clicked on (alterable)
385   [7] = Filename that is displayed for the attachment
386   [8] = Sent if message was found from a search (where)
387   [9] = Sent if message was found from a search (what)
388   
389 To set up links for actions, you assign them like this:
390   
391   $Args[1]['<plugin_name>']['href'] = 'URL to link to';
392   $Args[1]['<plugin_name>']['text'] = 'What to display';
393     
394 It's also possible to specify a hook as "attachment type0/*",
395 for example "attachment text/*". This hook will be executed whenever there's
396 no more specific rule available for that type.
397
398 Putting all this together, the demo_handle_zip_attachment() function should
399 look like this (note the argument being passed):
400
401    function demo_handle_zip_attachment(&$Args)
402    {
403       include_once(SM_PATH . 'plugins/demo/functions.php');
404       demo_handle_zip_attachment_do($Args);
405    }
406
407 And the demo_handle_zip_attachment_do() function in the
408 plugins/demo/functions.php file would typically (but not necessarily)
409 display a custom link:
410
411    function demo_handle_zip_attachment_do(&$Args)
412    {
413       $Args[1]['demo']['href'] = SM_PATH . 'plugins/demo/zip_handler.php?'
414          . 'passed_id=' . $Args[3] . '&mailbox=' . $Args[4] 
415          . '&passed_ent_id=' . $Args[5];
416       $Args[1]['demo']['text'] = 'show zip contents';
417    }
418
419 The file plugins/demo/zip_handler.php can now do whatever it needs with the
420 attachment (note that this will hand information about how to retrieve the
421 source message from the IMAP server as GET varibles).
422
423
424 (*) Options
425 -----------
426 Before you start adding user preferences to your plugin, please take a moment
427 to think about it:  in some cases, more options may not be a good thing.  
428 Having too many options can be confusing.  Thinking from the user's 
429 perspective, will the proposed options actually be used?  Will users
430 understand what these options are for?
431
432 There are two ways to add options for your plugin.  When you only have a few
433 options that don't merit an entirely new preferences page, you can incorporate
434 them into an existing section of SquirrelMail preferences (Personal 
435 Information, Display Preferences, Message Highlighting, Folder Preferences or 
436 Index Order).  Or, if you have an extensive number of settings or for some 
437 reason need a separate page for the user to interact with, you can create your
438 own preferences page.
439
440
441 Integrating Your Options Into Existing SquirrelMail Preferences Pages
442 ---------------------------------------------------------------------
443
444 There are two ways to accomplish the integration of your plugin's settings
445 into another preferences page.  The first method is to add the HTML code 
446 for your options directly to the preferences page of your choice.  Although
447 currently very popular, this method will soon be deprecated, so avoid it
448 if you can.  That said, here is how it works.  :)  Look for any of the hooks 
449 named as "options_<pref page>_inside", where <pref page> is "display", 
450 "personal", etc.  For this example, we'll use "options_display_inside" and, 
451 as above, "demo" as our plugin name:
452
453   1.  In setup.php in the squirrelmail_plugin_init_demo() function:
454
455          $squirrelmail_plugin_hooks['options_display_inside']['demo'] 
456             = 'demo_show_options';
457
458       Note that there are also hooks such as "options_display_bottom",
459       however, they place your options at the bottom of the preferences
460       page, which is usually not desirable (mostly because they also
461       come AFTER the HTML FORM tag is already closed).  It is possible
462       to use these hooks if you want to create your own FORM with custom
463       submission logic.
464
465   2.  Assuming the function demo_show_options() calls another function
466       elsewhere called demo_show_options_do(), that function should have
467       output similar to this (note that you will be inserting code into
468       a table that is already defined with two columns, so please be sure
469       to keep this framework in your plugin):
470
471          ------cut here-------
472          <tr>
473             <td>
474                OPTION_NAME
475             </td>
476             <td>
477                OPTION_INPUT
478             </td>
479          </tr>   
480          ------cut here-------
481
482       Of course, you can place any text where OPTION_NAME is and any input
483       tags where OPTION_INPUT is. 
484
485   3.  You will want to use the "options_<pref page>_save" hook (in this case,
486       "options_display_save") to save the user's settings after they have
487       pressed the "Submit" button.  Again, back in setup.php in the 
488       squirrelmail_plugin_init_demo() function:
489
490          $squirrelmail_plugin_hooks['options_display_save']['demo'] 
491             = 'demo_save_options';
492
493   4.  Assuming the function demo_save_options() calls another function
494       elsewhere called demo_save_options_do(), that function should put
495       the user's settings into permanent storage (see the preferences
496       section below for more information).  This example assumes that
497       in the preferences page, the INPUT tag's NAME attribute was set
498       to "demo_option":
499
500          global $data_dir, $username;
501          sqgetGlobalVar('demo_option', $demo_option);
502          setPref($data_dir, $username, 'demo_option', $demo_option);
503
504
505 The second way to add options to one of the SquirrelMail preferences page is
506 to use one of the "optpage_loadhook_<pref page>" hooks.  The sent_subfolders
507 plugin is an excellent example of this method.  Briefly, this way of adding
508 options consists of adding some plugin-specific information to a predefined
509 data structure which SquirrelMail then uses to build the HTML input forms
510 for you.  This is the preferred method of building options lists going forward.
511
512   1.  We'll use the "optpage_loadhook_display" hook to add a new group of
513       options to the display preferences page.  In setup.php in the 
514       squirrelmail_plugin_init_demo() function:
515
516          $squirrelmail_plugin_hooks['optpage_loadhook_display']['demo'] 
517             = 'demo_options';
518
519   2.  Assuming the function demo_options() calls another function elsewhere
520       called demo_options_do(), that function needs to add a new key to two
521       arrays, $optpage_data['grps'] and $optpage_data['vals'].  The value
522       associated with that key should simply be a section heading for your
523       plugin on the preferences page for the $optpage_data['grps'] array,
524       and yet another array with all of your plugin's options for the 
525       $optpage_data['vals'] array.  The options are built as arrays (yes, 
526       that's four levels of nested arrays) that specify attributes that are
527       used by SquirrelMail to build your HTML input tags automatically.
528       This example includes just one input element, a SELECT (drop-down)
529       list:
530
531          global $optpage_data;
532          $optpage_data['grps']['DEMO_PLUGIN'] = 'Demo Options';
533          $optionValues = array();
534          $optionValues[] = array(
535             'name'    => 'plugin_demo_favorite_color',
536             'caption' => 'Please Choose Your Favorite Color',
537             'type'    => SMOPT_TYPE_STRLIST,
538             'refresh' => SMOPT_REFRESH_ALL,
539             'posvals' => array(0 => 'red',
540                                1 => 'blue',
541                                2 => 'green',
542                                3 => 'orange'),
543             'save'    => 'save_plugin_demo_favorite_color'
544          );
545          $optpage_data['vals']['DEMO_PLUGIN'] = $optionValues;
546
547       The array that you use to specify each plugin option has the following
548       possible attributes:
549
550          name           The name of this setting, which is used not only for
551                         the INPUT tag name, but also for the name of this
552                         setting in the user's preferences
553          caption        The text that prefaces this setting on the preferences
554                         page
555          type           The type of INPUT element, which should be one of:
556                            SMOPT_TYPE_STRING     String/text input
557                            SMOPT_TYPE_STRLIST    Select list input
558                            SMOPT_TYPE_TEXTAREA   Text area input
559                            SMOPT_TYPE_INTEGER    Integer input
560                            SMOPT_TYPE_FLOAT      Floating point number input
561                            SMOPT_TYPE_BOOLEAN    Boolean (yes/no radio buttons)
562                                                    input
563                            SMOPT_TYPE_HIDDEN     Hidden input (not actually
564                                                  shown on preferences page)
565                            SMOPT_TYPE_COMMENT    Text is shown (specified by the
566                                                  'comment' attribute), but no
567                                                  user input is needed
568                            SMOPT_TYPE_FLDRLIST   Select list of IMAP folders
569          refresh        Indicates if a link should be shown to refresh part or
570                         all of the window (optional).  Possible values are:
571                            SMOPT_REFRESH_NONE         No refresh link is shown
572                            SMOPT_REFRESH_FOLDERLIST   Link is shown to refresh
573                                                       only the folder list
574                            SMOPT_REFRESH_ALL          Link is shown to refresh
575                                                     the entire window
576          initial_value  The value that should initially be placed in this 
577                         INPUT element
578          posvals        For select lists, this should be an associative array,
579                         where each key is an actual input value and the
580                         corresponding value is what is displayed to the user
581                         for that list item in the drop-down list
582          value          Specify the default/preselected value for this option
583                         input
584          save           You may indicate that special functionality needs to be
585                         used instead of just saving this setting by giving the
586                         name of a function to call when this value would 
587                         otherwise just be saved in the user's preferences
588          size           Specifies the size of certain input items (typically
589                         textual inputs).  Possible values are:
590                            SMOPT_SIZE_TINY
591                            SMOPT_SIZE_SMALL
592                            SMOPT_SIZE_MEDIUM
593                            SMOPT_SIZE_LARGE
594                            SMOPT_SIZE_HUGE
595                            SMOPT_SIZE_NORMAL
596          comment        For SMOPT_TYPE_COMMENT type options, this is the text
597                         displayed to the user
598          script         This is where you may add any additional javascript 
599                         or other code to the user input
600          post_script    You may specify some script (usually Javascript) that
601                         will be placed after (outside of) the INPUT tag.
602
603   3.  If you indicated a 'save' attribute for any of your options, you must
604       create that function (you'll only need to do this if you need to do
605       some special processing for one of your settings).  The function gets
606       one parameter, which is an object with mostly the same attributes you 
607       defined when you made the option above... the 'new_value' (and possibly
608       'value', which is the current value for this setting) is the most useful
609       attribute in this context:
610
611          function save_plugin_demo_favorite_color($option)
612          {
613             // if user chose orange, make note that they are really dumb
614             if ($option->new_value == 3)
615             {
616                // more code here as needed
617             }
618
619             // don't even save this setting if user chose green (old
620             // setting will remain)
621             if ($option->new_value == 2)
622                return;
623
624             // for all other colors, save as normal
625             save_option($option);
626          }
627
628
629 Creating Your Own Preferences Page
630 ----------------------------------
631
632 It is also possible to create your own preferences page for a plugin.  This
633 is particularly useful when your plugin has numerous options or needs to 
634 offer special interaction with the user (for things such as changing password,
635 etc.).  Here is an outline of how to do so (again, using the "demo" plugin
636 name):
637
638   1.  Add a new listing to the main Options page.  Older versions of 
639       SquirrelMail offered a hook called "options_link_and_description"
640       although its use is deprecated (and it is harder to use in that
641       it requires you to write your own HTML to add the option).  Instead,
642       you should always use the "optpage_register_block" hook where you
643       create a simple array that lets SquirrelMail build the HTML
644       to add the plugin options entry automatically.  In setup.php in the
645       squirrelmail_plugin_init_demo() function:
646   
647          $squirrelmail_plugin_hooks['optpage_register_block']['demo']
648             = 'demo_options_block';
649
650   2.  Assuming the function demo_options_block() calls another function
651       elsewhere called demo_options_block_do(), that function only needs
652       to create a simple array and add it to the $optpage_blocks array:
653
654          global $optpage_blocks;
655          $optpage_blocks[] = array(
656              'name' => 'Favorite Color Settings',
657              'url'  => SM_PATH . 'plugins/demo/options.php',
658              'desc' => 'Change your favorite color & find new exciting colors',
659              'js'   => FALSE
660          );
661
662       The array should have four elements:
663          name   The title of the plugin's options as it will be displayed on
664                 the Options page
665          url    The URI that points to your plugin's custom preferences page
666          desc   A description of what the preferences page offers the user,
667                 displayed on the Options page below the title
668          js     Indicates if this option page requires the client browser
669                 to be Javascript-capable.  Should be TRUE or FALSE.
670
671   3.  There are two different ways to create the actual preferences page 
672       itself.  One is to simply write all of your own HTML and other 
673       interactive functionality, while the other is to define some data 
674       structures that allow SquirrelMail to build your user inputs and save
675       your data automatically.  
676
677       Building your own page is wide open, and for ideas, you should look at 
678       any of the plugins that currently have their own preferences pages.  If
679       you do this, make sure to read step number 4 below for information on 
680       saving settings.  In order to maintain security, consistant look and 
681       feel, internationalization support and overall integrity, there are just
682       a few things you should always do in this case:  define the SM_PATH
683       constant, include the file include/validate.php (see the section about
684       including other files above) and make a call to place the standard page
685       heading at the top of your preferences page.  The top of your PHP file
686       might look something like this:
687
688          define('SM_PATH', '../../');
689          include_once(SM_PATH . 'include/validate.php');
690          global $color;
691          displayPageHeader($color, 'None');
692
693       From here you are on your own, although you are encouraged to do things
694       such as use the $color array to keep your HTML correctly themed, etc.
695
696       If you want SquirrelMail to build your preferences page for you, 
697       creating input forms and automatically saving users' settings, then 
698       you should change the 'url' attribute in the options block you created
699       in step number 2 above to read as follows:
700
701          'url' => SM_PATH . 'src/options.php?optpage=plugin_demo',
702
703       Now, you will need to use the "optpage_set_loadinfo" hook to tell 
704       SquirrelMail about your new preferences page.  In setup.php in the 
705       squirrelmail_plugin_init_demo() function:
706   
707          $squirrelmail_plugin_hooks['optpage_set_loadinfo']['demo']
708             = 'demo_optpage_loadinfo';
709
710       Assuming the function demo_optpage_loadinfo() calls another function
711       elsewhere called demo_optpage_loadinfo_do(), that function needs to 
712       define values for four variables (make sure you test to see that it 
713       is your plugin that is being called by checking the GET variable you
714       added to the url just above):
715           
716          global $optpage, $optpage_name, $optpage_file, 
717                 $optpage_loader, $optpage_loadhook;
718          if ($optpage == 'plugin_demo')
719          {
720             $optpage_name = "Favorite Color Preferences";
721             $optpage_file = SM_PATH . 'plugins/demo/options.php';
722             $optpage_loader = 'load_optpage_data_demo';
723             $optpage_loadhook = 'optpage_loadhook_demo';
724          }
725
726       Now you are ready to build all of your options.  In the file you
727       indicated for the variable $optpage_file above, you'll need to create
728       a function named the same as the value you used for $optpage_loader
729       above.  In this example, the file plugins/demo/options.php should
730       have at least this function in it:
731
732          function load_optpage_data_demo()
733          {
734             $optpage_data = array();
735             $optpage_data['grps']['DEMO_PLUGIN'] = 'Demo Options';
736             $optionValues = array();
737             $optionValues[] = array(
738                'name'    => 'plugin_demo_favorite_color',
739                'caption' => 'Please Choose Your Favorite Color',
740                'type'    => SMOPT_TYPE_STRLIST,
741                'refresh' => SMOPT_REFRESH_ALL,
742                'posvals' => array(0 => 'red',
743                                   1 => 'blue',
744                                   2 => 'green',
745                                   3 => 'orange'),
746                'save'    => 'save_plugin_demo_favorite_color'
747             );
748             $optpage_data['vals']['DEMO_PLUGIN'] = $optionValues;
749             return $optpage_data;
750          }
751
752       For a detailed description of how you build these options, please read
753       step number 2 for the second method of adding options to an existing
754       preferences page above.  Notice that the only difference here is in the
755       very first and last lines of this function where you are actually
756       creating and returning the options array instead of just adding onto it.
757
758       That's all there is to it - SquirrelMail will create a preferences page
759       titled as you indicated for $optpage_name above, and other plugins
760       can even add extra options to this new preferences page.  To do so,
761       they should use the hook name you specified for $optpage_loadhook above
762       and use the second method for adding option settings to existing
763       preferences pages described above.
764
765   4.  Saving your options settings: if you used the second method in step
766       number 3 above, your settings will be saved automatically (or you can
767       define special functions to save special settings such as the 
768       save_plugin_demo_favorite_color() function in the example described
769       above) and there is probably no need to follow this step.  If you
770       created your own preferences page from scratch, you'll need to follow
771       this step.  First, you need to register your plugin against the
772       "options_save" hook.  In setup.php in the squirrelmail_plugin_init_demo()
773       function:
774   
775          $squirrelmail_plugin_hooks['options_save']['demo']
776             = 'demo_save_options';
777
778       Assuming the function demo_save_options() calls another function
779       elsewhere called demo_save_options_do(), that function needs to grab
780       all of your POST and/or GET settings values and save them in the user's
781       preferences (for more about preferences, see that section below).  Since
782       this is a generic hook called for all custom preferences pages, you 
783       should always set "optpage" as a POST or GET variable with a string that
784       uniquely identifies your plugin:
785
786          <input type="hidden" name="optpage" value="plugin_demo">
787
788       Now in your demo_save_options_do() function, do something like this:
789
790          global $username, $data_dir, $optpage, $favorite_color;
791          if ($optpage == 'plugin_demo')
792          {
793             sqgetGlobalVar('favorite_color', $favorite_color, SQ_FORM);
794             setPref($data_dir, $username, 'favorite_color', $favorite_color);
795          }
796         
797       Note that $favorite_color may not need to be globalized, although 
798       experience has shown that some versions of PHP don't behave as expected
799       unless you do so.  Even when you use SquirrelMail's built-in preferences
800       page generation functionality, you may still use this hook, although 
801       there should be no need to do so.  If you need to do some complex 
802       validation routines, note that it might be better to do so in the file
803       you specified as the "$optpage_file" (in our example, that was the
804       plugins/demo/options.php file), since at this point, you can still 
805       redisplay your preferences page.  You could put code similar to this
806       in the plugins/demp/options.php file (note that there is no function;
807       this code needs to be executed at include time):
808
809          global $optmode;
810          if ($optmode == 'submit') 
811          {
812             // do something here such as validation, etc
813             if (you want to redisplay your preferences page)
814                $optmode = '';
815          }
816
817
818 Preferences
819 -----------
820
821 Saving and retrieving user preferences is very easy in SquirrelMail.
822 SquirrelMail supports preference storage in files or in a database 
823 backend, however, the code you need to write to manipulate preferences
824 is the same in both cases.
825
826 Setting preferences: 
827
828    Setting preferences is done for you if you use the built-in facilities
829    for automatic options construction and presentation (see above).  If
830    you need to manually set preferences, however, all you need to do is:
831
832       global $data_dir, $username;
833       setPref($data_dir, $username, 'pref_name', $pref_value);
834
835    Where "pref_name" is the key under which the value will be stored
836    and "pref_value" is a variable that should contain the actual 
837    preference value to be stored.
838
839 Loading preferences:
840
841    There are two approaches to retrieving plugin (or any other) preferences.
842    You can grab individual preferences one at a time or you can add your
843    plugin's preferences to the routine that loads up user preferences at 
844    the beginning of each page request.  If you do the latter, making sure
845    to place your preference variables into the global scope, they will be
846    immediately available in all other plugin code.  To retrieve a single
847    preference value at any time, do this:
848
849       global $data_dir, $username;
850       $pref_value = getPref($data_dir, $username, 'pref_name', 'default value');
851
852    Where "pref_name" is the preference you are retrieving, "default_value"
853    is what will be returned if the preference is not found for this user,
854    and, of course, "pref_value" is the variable that will get the actual
855    preference value.
856
857    To have all your preferences loaded at once when each page request is
858    made, you'll need to register a function against the "loading_prefs" hook.
859    For our "demo" plugin, in setup.php in the squirrelmail_plugin_init_demo()
860    function:
861   
862       $squirrelmail_plugin_hooks['loading_prefs']['demo']
863          = 'demo_load_prefs';
864
865    Assuming the function demo_load_prefs() calls another function
866    elsewhere called demo_load_prefs_do(), that function just needs to
867    pull out any all all preferences you'll be needing elsewhere:
868
869       global $data_dir, $username, $pref_value;
870       $pref_value = getPref($data_dir, $username, 'pref_name', 'default value');
871
872    Remember to globalize each preference, or this code is useless.
873
874
875 Internationalization
876 --------------------
877
878 Although this document may only be available in English, we sure hope that you
879 are thinking about making your plugin useful to the thousands of non-English
880 speaking SquirrelMail users out there!  It is almost rude not to do so, and
881 it isn't much trouble, either.  This document will only describe how you can
882 accomplish the internationalization of a plugin.  For more general information
883 about PHP and SquirrelMail translation facilities, see:
884
885 http://www.squirrelmail.org/wiki/wiki.php?LanguageTranslation
886
887 The unofficial way to internationalize a plugin is to put all plugin output
888 into the proper format but to rely on the SquirrelMail translation facilities
889 for all the rest.  If the plugin were really to get translated, you'd need
890 to make sure that all output strings for your plugin are either added to or
891 already exist in the main SquirrelMail locale files.
892
893 The better way to make sure your plugin is translated is to create your own
894 locale files and what is called a "gettext domain" (see the link above for
895 more information).
896
897 There are three basic steps to getting your plugins internationalized:  put
898 all output into the proper format, switch gettext domains and create locale 
899 files.
900
901   1.  Putting plugin output into the correct format is quite easy.  The hard
902       part is making sure you catch every last echo statement.  You need to
903       echo text like this:
904
905          echo _("Hello");
906
907       So, even in the HTML segments of your plugin files, you need to do this:
908
909          <input type="submit" value="<?php echo _("Submit") ?>">
910
911       You can put any text you want inside of the quotes (you MUST use double
912       quotes!), including HTML tags, etc.  What you should think carefully 
913       about is that some languages may use different word ordering, so this 
914       might be problematic:
915
916          echo _("I want to eat a ") . $fruitName . _(" before noon");
917
918       Because some languages (Japanese, for instance) would need to translate
919       such a sentence to "Before noon " . $fruitName . " I want to eat", but 
920       with the format above, they are stuck having to translate each piece 
921       separately.  You might want to reword your original sentence:
922
923          echo _("This is what I want to eat before noon: ") . $fruitName;
924
925   2.  By default, the SquirrelMail gettext domain is always in use.  That
926       means that any text in the format described above will be translated
927       using the locale files found in the main SquirrelMail locale directory.
928       Unless your plugin produces no output or only output that is in fact
929       translated under the default SquirrelMail domain, you need to create
930       your own gettext domain.  The PHP for doing so is very simple.  At
931       the top of any file that produces any output, place the following code
932       (again, using "demo" as the plugin name):
933
934          bindtextdomain('demo', SM_PATH . 'plugins/demo/locale');
935          textdomain('demo');
936
937       Now all output will be translated using your own custom locale files.
938       Please be sure to switch back to the SquirrelMail domain at the end
939       of the file, or many of the other SquirrelMail files may misbehave:
940
941          bindtextdomain('squirrelmail', SM_PATH . 'locale');
942          textdomain('squirrelmail');
943
944       Note that if, in the middle of your plugin file, you use any
945       SquirrelMail functions that send output to the browser, you'll need
946       to temporarily switch back to the SquirrelMail domain:
947
948          bindtextdomain('squirrelmail', SM_PATH . 'locale');
949          textdomain('squirrelmail');
950          displayPageHeader($color, 'None');
951          bindtextdomain('demo', SM_PATH . 'plugins/demo/locale');
952          textdomain('demo');
953
954       Note that technically speaking, you only need to have one bindtextdomain
955       call per file, you should always use it before every textdomain call,
956       since PHP installations without gettext compiled into them will not
957       function properly if you do not.
958
959   3.  Finally, you just need to create your own locale.  You should create
960       a directory structure like this in the plugin directory:
961
962          demo
963            |
964            ------locale
965                     |
966                     ------de_DE
967                     |       |
968                     |       ------LC_MESSAGES
969                     |
970                     ------ja_JP
971                             |
972                             ------LC_MESSAGES
973
974       Create a directories such as de_DE for each language (de_DE is German,
975       ja_JP is Japanese, etc. - check the SquirrelMail locale directory for
976       a fairly comprehensive listing).  Inside of each LC_MESSAGES directory
977       you should place two files, one with your translations in it, called
978       <plugin name>.po (in this case, "demo.po"), and one that is a compiled
979       version of the ".po" file, called <plugin name>.mo (in this case,
980       "demo.mo").  On most linux systems, there is a tool you can use to pull
981       out most of the strings that you need to have translated from your PHP
982       files into a sample .po file:
983
984          xgettext --keyword=_ -d <plugin name> -s -C *.php 
985
986          --keyword option tells xgettext what your strings are enclosed in
987          -d is the domain of your plugin which should be the plugin's name
988          -s tells xgettext to sort the results and remove duplicate strings
989          -C means you are translating a file with C/C++ type syntax (ie. PHP)
990          *.php is all the files you want translations for
991
992       Note, however, that this will not always pick up all strings, so you 
993       should double-check manually.  Of course, it's easiest if you just keep
994       track of all your strings as you are coding your plugin.  Your .po file
995       will now look something like:
996
997          # SOME DESCRIPTIVE TITLE.
998          # Copyright (C) YEAR Free Software Foundation, Inc.
999          # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
1000          #
1001          #, fuzzy
1002          msgid ""
1003          msgstr ""
1004          "Project-Id-Version: PACKAGE VERSION\n"
1005          "POT-Creation-Date: 2003-06-18 11:22-0600\n"
1006          "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1007          "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1008          "Language-Team: LANGUAGE <LL@li.org>\n"
1009          "MIME-Version: 1.0\n"
1010          "Content-Type: text/plain; charset=CHARSET\n"
1011          "Content-Transfer-Encoding: ENCODING\n"
1012          
1013          #: functions.php:45
1014          msgid "Hello"
1015          msgstr ""
1016          
1017          #: functions.php:87
1018          msgid "Favorite Color"
1019          msgstr ""
1020          
1021       You should change the header to look something more like:
1022
1023          # Copyright (c) 1999-2003 The Squirrelmail Development Team
1024          # Roland Bauerschmidt <rb@debian.org>, 1999.
1025          msgid ""
1026          msgstr ""
1027          "Project-Id-Version: $Id: squirrelmail.po,v 1.10 2003/06/04 15:01:59
1028          philippe_mingo Exp $\n"
1029          "POT-Creation-Date: 2003-01-21 19:21+0100\n"
1030          "PO-Revision-Date: 2003-01-21 21:01+0100\n"
1031          "Last-Translator: Juergen Edner <juergen.edner@epost.de>\n"
1032          "Language-Team: German <squirrelmail-i18n@lists.squirrelmail.net>\n"
1033          "MIME-Version: 1.0\n"
1034          "Content-Type: text/plain; charset=ISO-8859-1\n"
1035          "Content-Transfer-Encoding: 8bit\n"
1036
1037       The most important thing to change here is the charset on the next to
1038       last line.  You'll want to keep a master copy of the .po file and make
1039       a copy for each language you have a translation for.  You'll need to 
1040       translate each string in the .po file:
1041
1042          msgid "Hello"
1043          msgstr "Guten Tag"
1044
1045       After you're done translating, you can create the .mo file very simply 
1046       by running the following command (available on most linux systems):
1047
1048          msgfmt -o <plugin name>.mo <plugin name>.po
1049
1050       In the case of the "demo" plugin:
1051
1052          msgfmt -o demo.mo demo.po
1053
1054       Please be sure that the .po and .mo files both are named exactly the
1055       same as the domain you bound in step 2 above and everything else works
1056       automatically.  In SquirrelMail, go to Options -> Display Preferences
1057       and change your Language setting to see the translations in action!
1058
1059
1060 PLUGIN STANDARDS AND REQUIREMENTS
1061 =================================
1062
1063 The SquirrelMail project has some important goals, such as avoiding the
1064 use of JavaScript, avoiding non-standard HTML tags, keeping file sizes
1065 small and providing the fastest webmail client on the Internet.  As such,
1066 we'd like it if plugin authors coded with the same goals in mind that the
1067 core developers do.  Common sense is always a good tool to have in your
1068 programming repertoire, but below is an outline of some standards that we 
1069 ask you as a plugin developer to meet.  Depending upon how far you bend 
1070 these rules, we may not want to post your plugin on the SquirrelMail 
1071 website... and of course, no one really wants your efforts to go to waste
1072 and for the SquirrelMail community to miss out on a potentially useful
1073 plugin, so please try to follow these guidelines as closely as possible.
1074
1075
1076 Small setup.php
1077 ---------------
1078
1079 In order for SquirrelMail to remain fast and lean, we are now asking
1080 that all plugin authors remove all unnecessary functionality from setup.php
1081 and refactor it into another file.  There are a few ways to accomplish
1082 this, none of which are difficult.  At a minimum, you'll want to have the
1083 squirrelmail_plugin_init_<plugin name>() function in setup.php, and naturally,
1084 you'll need functions that are merely stubs for each hook that you are using.
1085 One (but not the only) way to do it is:
1086
1087    function squirrelmail_plugin_init_demo() 
1088    {
1089       global $squirrelmail_plugin_hooks;
1090       $squirrelmail_plugin_hooks['generic_header']['demo'] = 'plugin_demo_header';
1091    }
1092    function plugin_demo_header()
1093    {
1094       include_once(SM_PATH . 'plugins/demo/functions.php');
1095       plugin_demo_header_do();
1096    }
1097
1098
1099 Internationalization
1100 --------------------
1101
1102 Q: What is more disappointing to users in France who would make good 
1103    use of your plugin than learning that it is written entirely in English?
1104 A: Learning that they cannot send you a French translation file for your
1105    plugin.
1106
1107 There are thousands of users out there whose native tongue is not English,
1108 and when you develop your plugin without going through the three simple steps
1109 needed to internationalize it, you are effectively writing them all off.  
1110 PLEASE consider internationalizing your plugin!
1111
1112
1113 Developing with E_ALL
1114 ---------------------
1115
1116 When you are developing your plugin, you should always have error reporting
1117 turned all the way up.  You can do this by changing two settings in your
1118 php.ini and restarting your web server:
1119
1120    display_errors = On
1121    error_reporting  =  E_ALL
1122
1123 This way, you'll be sure to see all Notices, Warnings and Errors that your
1124 code generates (it's OK, really, it happens to the best of us... except me!).
1125 Please make sure to fix them all before you release the plugin.
1126
1127
1128 Compatibility with register_globals=Off
1129 ---------------------------------------
1130
1131 Most sensible systems administrators now run their PHP systems with the
1132 setting "register_globals" as OFF.  This is a prudent security setting,
1133 and as the SquirrelMail core code has long since been upgraded to work
1134 in such an environment, we are now requiring that all plugins do the same.
1135 Compatibility with this setting amounts to little more than explicitly
1136 gathering any and all variables you sent from a <form> tag as GET or POST
1137 values instead of just assuming that they will be placed in the global
1138 scope automatically.  There is nothing more to do than this:
1139
1140    global $favorite_color;
1141    sqgetGlobalVar('favorite_color', $favorite_color, SQ_FORM);
1142
1143
1144 Extra Blank Lines
1145 -----------------
1146
1147 It may seem innocuous, but if you have any blank lines either before the
1148 first <?php tag or after the last ?> tag in any of your plugin files, you
1149 you will break SquirrelMail in ways that may seem entirely unrelated.  For
1150 instance, this will often cause a line feed character to be included with
1151 email attachments when they are viewed or downloaded, rendering them useless!
1152
1153
1154 include_once
1155 ------------
1156
1157 When including files, please make sure to use the include_once() function
1158 and NOT include(), require(), or require_once(), since these all are much 
1159 less efficient than include_once() and can have a cumulative effect on 
1160 SquirrelMail performance.
1161
1162
1163 Version Reporting
1164 -----------------
1165
1166 In order for systems administrators to keep better track of your plugin and
1167 get upgrades more efficiently, you are requested to make version information
1168 available to SquirrelMail in a format that it understands.  There are two 
1169 ways to do this.  Presently, we are asking that you do both, since we are 
1170 still in a transition period between the two.  This is painless, so please 
1171 be sure to include it:
1172
1173   1.  Create a file called "version" in the plugin directory.  That file
1174       should have only two lines: the first line should have the name of
1175       the plugin as named on the SquirrelMail web site (this is often a
1176       prettified version of the plugin directory name), the second line 
1177       must have the version and nothing more.  So for our "demo" plugin,
1178       whose name on the web site might be something like "Demo Favorite 
1179       Colors", the file plugins/demo/version should have these two lines:
1180
1181          Demo Favorite Colors
1182          1.0
1183
1184   2.  In setup.php, you should have a function called <plugin name>_version().
1185       That function should return the version of your plugin.  For the "demo"
1186       plugin, that should look like this:
1187
1188          function demo_version()
1189          {
1190             return '1.0';
1191          }
1192
1193
1194 Configuration Files
1195 -------------------
1196
1197 It is common to need a configuration file that holds some variables that
1198 are set up at install time.  For ease of installation and maintenance, you
1199 should place all behavioral settings in a config file, isolated from the
1200 rest of your plugin code.  A typical file name to use is "config.php".  If
1201 you are using such a file, you should NOT include a file called "config.php"
1202 in your plugin distribution, but instead a copy of that file called 
1203 "config.php.sample".  This helps systems administrators avoid overwriting
1204 the "config.php" files and losing all of their setup information when they
1205 upgrade your plugin.
1206
1207
1208 Session Variables
1209 -----------------
1210
1211 In the past, there have been some rather serious issues with PHP sessions
1212 and SquirrelMail, and certain people have worked long and hard to ensure
1213 that these problems no longer occur in an extremely wide variety of OS/PHP/
1214 web server environments.  Thus, if you need to place any values into the 
1215 user's session, there are some built-in SquirrelMail functions that you are 
1216 strongly encouraged to make use of.  Using them also makes your job easier.
1217
1218   1.  To place a variable into the session:
1219
1220          global $favorite_color;  
1221          $favoriteColor = 'green';
1222          sqsession_register($favorite_color, 'favorite_color');
1223
1224       Strictly speaking, globalizing the variable shouldn't be necessary,
1225       but certain versions of PHP seem to behave more predictably if you do.
1226
1227   2.  To retrieve a variable from the session:
1228
1229          global $favorite_color;
1230          sqgetGlobalVar('favorite_color', $favorite_color, SQ_SESSION);
1231
1232   3.  You can also check for the presence of a variable in the session:
1233
1234          if (sqsession_is_registered('favorite_color'))
1235             // do something important
1236
1237   4.  To remove a variable from the session:
1238
1239          global $favorite_color;
1240          sqsession_unregister('favorite_color');
1241
1242       Strictly speaking, globalizing the variable shouldn't be necessary,
1243       but certain versions of PHP seem to behave more predictably if you do.
1244
1245
1246 Form Variables
1247 --------------
1248
1249 You are also encouraged to use SquirrelMail's built-in facilities to 
1250 retrieve variables from POST and GET submissions.  This is also much
1251 easier on you and makes sure that all PHP installations are accounted
1252 for (such as those that don't make the $_POST array automatically 
1253 global, etc.):
1254
1255    global $favorite_color;
1256    sqgetGlobalVar('favorite_color', $favorite_color, SQ_FORM);
1257
1258
1259 Files In Plugin Directory
1260 -------------------------
1261
1262 There are a few files that you should make sure to include when you build
1263 your final plugin distribution:
1264
1265   1.  A copy of the file index.php from the main plugins directory.  When 
1266       working in your plugin directory, just copy it in like this:
1267
1268          $ cp ../index.php .
1269
1270       This will redirect anyone who tries to browse to your plugin directory
1271       to somewhere more appropriate.  If you create other directories under
1272       your plugin directory, you may copy the file there as well to be extra
1273       safe.  If you are storing sensitive configuration files or other data
1274       in such a directory, you could even include a .htaccess file with the
1275       contents "Deny From All" that will disallow access to that directory 
1276       entirely (when the target system is running the Apache web server).
1277       Keep in mind that not all web servers will honor an .htaccess file, so
1278       don't depend on it for security. Make sure not to put such a file in
1279       your main plugin directory!
1280
1281   2.  A file that describes your plugin and offers detailed instructions for 
1282       configuration or help with troubleshooting, etc.  This file is usually 
1283       entitled "README".  Some useful sections to include might be:
1284
1285          Plugin Name and Author
1286          Current Version
1287          Plugin Features
1288          Detailed Plugin Description
1289          How-to for Plugin Configuration
1290          Change Log
1291          Future Ideas/Enhancements/To Do List
1292
1293   3.  A file that explains how to install your plugin.  This file is typically
1294       called "INSTALL".  If you do not require any special installation 
1295       actions, you can probably copy one from another plugin or use this as
1296       a template:
1297
1298          Installing the Demo Plugin
1299          ==========================
1300
1301          1) Start with untaring the file into the plugins directory.
1302             Here is a example for the 1.0 version of the Demo plugin.
1303
1304            $ cd plugins
1305            $ tar -zxvf demo-1.0-1.4.0.tar.gz
1306
1307          2) Change into the demo directory, copy config.php.sample
1308             to config.php and edit config.php, making adjustments as
1309             you deem necessary.  For more detailed explanations about
1310             each of these parameters, consult the README file.
1311          
1312            $ cd demo
1313            $ cp config.php.sample config.php
1314            $ vi config.php
1315          
1316          
1317          3) Then go to your config directory and run conf.pl.  Choose
1318             option 8 and move the plugin from the "Available Plugins"
1319             category to the "Installed Plugins" category.  Save and exit.
1320          
1321            $ cd ../../config/
1322            $ ./conf.pl
1323          
1324
1325          Upgrading the Demo Plugin
1326          =========================
1327
1328          1) Start with untaring the file into the plugins directory.
1329             Here is a example for the 3.1 version of the demo plugin.
1330
1331            $ cd plugins
1332            $ tar -zxvf demo-3.1-1.4.0.tar.gz
1333
1334
1335          2) Change into the demo directory, check your config.php
1336             file against the new version, to see if there are any new
1337             settings that you must add to your config.php file.
1338
1339            $ diff -Nau config.php config.php.sample
1340          
1341             Or simply replace your config.php file with the provided sample
1342             and reconfigure the plugin from scratch (see step 2 under the
1343             installation procedure above).
1344
1345
1346 COMPATIBILITY WITH OLDER VERSIONS OF SQUIRRELMAIL
1347 =================================================
1348
1349 Whenever new versions of SquirrelMail are released, there is always a
1350 considerable lag time before it is widely adopted.  During that transitional
1351 time, especially when the new SquirrelMail version contains any architectural
1352 and/or functional changes, plugin developers are put in a unique and very
1353 difficult position.  That is, there will be people running both the old and
1354 new versions of SquirrelMail who want to use your plugin, and you will 
1355 probably want to accomodate them both.
1356
1357 The easiest way to keep both sides happy is to keep two different versions
1358 of your pluign up to date, one that runs under the older SquirrelMail, and
1359 one that requires the newest SquirrelMail.  This is inconvenient, however,
1360 especially if you are continuing to develop the plugin.  Depending on the
1361 changes the SquirrelMail has implemented in the new version, you may be able
1362 to include code that can auto-sense SquirrelMail version and make adjustments
1363 on the fly.  There is a function available to you for determining the 
1364 SquirrelMail version called check_sm_version() and it can be used as such:
1365
1366    check_sm_version(1, 4, 0)
1367
1368 This will return TRUE if the SquirrelMail being used is at least 1.4.0, and
1369 FALSE otherwise.
1370
1371 As this document is written, we are in a transition period between versions
1372 1.2.11 and 1.4.0.  There is a plugin called "Compatibilty" that is intended
1373 for use by plugin authors so they can develop one version of their plugin
1374 and seamlessly support both 1.2.x and 1.4.x SquirrelMail installations.  For
1375 more information about how to use the "Compatibility" plugin, download it and
1376 read its README file or see:
1377
1378    http://www.squirrelmail.org/wiki/wiki.php?PluginUpgrading
1379
1380
1381 REQUESTING NEW HOOKS
1382 ====================
1383
1384 It's impossible to foresee all of the places where hooks might be useful
1385 (it's also impossible to put in hooks everywhere!), so you might need to
1386 negotiate the insertion of a new hook to make your plugin work.  In order
1387 to do so, you should post such a request to the squirrelmail-devel mailing
1388 list.
1389
1390
1391 HOW TO RELEASE YOUR PLUGIN
1392 ==========================
1393
1394 As long as you've consulted the list of plugin standards and done your
1395 best to follow them, there's little standing in the way of great fame as an
1396 official SquirrelMail plugin developer.
1397
1398   1.  Make a distribution file.  There is a convenient Perl script in
1399       the plugins directory that will help you do this:
1400
1401          make_archive.pl -v demo 1.0 1.4.0
1402
1403       -v    is optional and indicates that the script should run in verbose mode
1404       demo  is the name of your plugin
1405       1.0   is the version of your plugin
1406       1.4.0 is the version of SquirrelMail that is required to run your plugin
1407
1408       You can also create the distribution file manually in most *nix 
1409       environments by running this command from the plugins directory (NOT 
1410       your plugin directory):
1411
1412          $ tar czvf demo-1.0-1.4.0.tar.gz demo
1413
1414       Where "demo" is the name of your plugin, "1.0" is the version of
1415       your plugin, and "1.4.0" is the version of SquirrelMail required
1416       to use your plugin.
1417
1418   2.  Consult the SquirrelMail web site for contact information for the
1419       Plugins Team Leaders, to whom you should make your request.  If they 
1420       do not respond, you should feel free to ask for help contacting them 
1421       on the squirrelmail-plugins mailing list.
1422
1423          http://www.squirrelmail.org/wiki/wiki.php?SquirrelMailLeadership
1424