plugin.txt 73 KB

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