plugin.txt 69 KB

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