Plugins Overview Interactive Tools does not provide a Plugins Tutorial or HowTo of any value so I created this document for my own use. Having said that, I should add that IA's CMSB program from a developers percpective (ie my percpective) is outstanding. Support via the forums is timely, and contributed documentation by others make up for any short falls in IA's documentation. The "plugins" concept provides a means of customization that will never be over written by a CMSB program upgrade or new release. This sets it apart from other software in that your customization isn't lost if/when you upgrade. My most recent plugins include code generators which like regular plugins simply make life easier. This is all work in progress, created for my own use, that I'm happy to share with you. @ToDo Add step by step example of a plugin @ToDo Add step by step example of a code generator I'll start with "What you should know before getting started". Actions and FiltersaddAction() and addFilter()There are 3 types of Plugin Operations: i) Actions, ii) Filters and iii) a combination of Actions and Filters. The fundamental difference between an Action and a Filter is: i) Actions do not return a value. doAction("hookName"[, $arg [,$arg, ...]]); When doAction is encountered the hookName functions are executed which perform operations that do not return any results. doAction() may or may not have any input arguments. ii) Filters do return a value. applyFilters("hookName"[, $variable [,$arg, $arg,...]]); When applyFilters is encountered the $variable is evaluated by the plugin(s) that reference "hookName". The plugin is expected to return a value. The filter may return a empty() value if that is the desired result. applyFilters() will usually have an input variable although it may not.Note:
If the hookName doesn't exist, for instance when the plugin is not active, doAction() simply returns (without error),
where as applyFilters() returns the input variable's value unchanged.
The net affect is as if the Action of Filter doesn't exist.The Plugin Header The plugin file "Header" aka "Doc Comment"The CMSB source code for these functions is in lib/plugin_funtions.php: // alias for add_action function addFilter($hookName, $functionName, $priority = 10, $acceptedArgs = 1) { return addAction($hookName, $functionName, $priority, $acceptedArgs); } // function addAction($hookName, $functionName, $priority = 10, $acceptedArgs = 1) { if (!$hookName) { die(__FUNCTION__ . ": No hookname specified!"); } if (!$functionName) { die(__FUNCTION__ . ": No functioname specified!"); } // add actions $actionList = &_getActionList(); $actionList[$hookName][$priority][$functionName] = $acceptedArgs; }As seen in the source code, these functions do exactly the same thing but have fundamentaly different meanings when used to build a plugin.
Use the one that represents the functions return paramater as explained in the previous section.
The first paramater "$hookName" is the sting value of any one of the HookNames found in "Admin > Plugins > Developer's Plugin Hook List" list.
The second paramater "$functionName" is the string value of the name of your custom function. It must be unique or a php error will occur.
The third paramater "$priority", an integer, is used to decide the order of the doAction("hookName") or applyFilters("hookName") when there is more than one call to the same "hookName".
The actions and filters are processed in order of lowest to highest priority.
e.g. doAction("init_complete","functionA") would be proccessed after doAction("init_complete","functionB",5) since "functionA" has a default priority of 10.
The fourth paramater "$acceptedArgs" is the number of paramaters the filter or action is passing to the plugin.
You will need to look up the "hookName" in the CMSB source file and count the number of params being used. The "$acceptedArgs" value should be the total number of args listed minus 1. Minus one because the first paramater is the filter or action "hookName" and is not an argument paramater passed to your plugin function.Usage based on the operation being performed:
If the plugin is expected to return a value, use: addFilter("hookName","yourFunctionName",null, 1).
If your plugin is intended to, for example, modify the values of an array before it's accessed, use: addAction("hookName","yourFunctionName",null,1).
In this case the plugin isn't returning any thing but instead modifying an array for later use.
Creating a Plugin 1. Create a *.php file in the plugins directory and give it an informative name. eg. _modify_header.php 2. If you use Zend Studios, use zend editor doc comment notation so mouse over information will be readable in the zend editor sidebar. (End the "Description" line with a period to show the "Plugin Name" and "Description" in the zend editor.) 3. If activated in the Plugins Menu, these hookNames (and functions) will be made available to CMSB script files. 4. Since CMSB parses the plugins directory don't put any unneccessary *.php files in the plugins directory. Have a look at the next section to see the technique I use for managing plugins.Header fields and descriptions used by CMSB are in plugin_functions.php, function getPluginData(), (CMSB version 2.08) $textKeyToFieldname['Plugin Name'] = 'name'; // shown in plugin menu $textKeyToFieldname['Plugin URI'] = 'uri'; // shown in plugin menu (turns "Plugin Name" into an anchor tag) $textKeyToFieldname['Version'] = 'version'; // shown in plugin menu $textKeyToFieldname['Description'] = 'description'; // shown in plugin menu $textKeyToFieldname['Author'] = 'author'; // shown in plugin menu $textKeyToFieldname['Author URI'] = 'authorUri'; // shown in plugin menu (turns the "Author" into an anchor tag) #$textKeyToFieldname['Text Domain'] = 'textDomain'; // not yet used #$textKeyToFieldname['Domain Path'] = 'domainPath'; // not yet used $textKeyToFieldname['Requires at least'] = 'requiresAtLeast'; // minimum CMS version to be able to activate the plugin $textKeyToFieldname['Required System Plugin'] = 'isSystemPlugin'; // always load this plugin - can't be de-activated At the top of your plugin file create the header. The required lines are in red, all others may be omitted. <?php /* Plugin Name: Plugin name Description: Plugin description. Plugin URI: http://.... Possible usage would be a link to a full description of the plugin Author: Your Name Author URI: http://www.example.com/about_me' target='_blank (notice the single quotes used to make link open in a new window) Version: 1.0 Requires at least: 2.00 // required only if your plugin would fail on an earlier version of CMSB Required System Plugin: 0 // optional [ 0 || 1*] (default is 0) (if 1 then "Requires at least:" is ignored) */"Plugin URI" and "Author URI":
When the URI's are parsed they are enclosed in a set up single quotes representing the href value of an anchor tag.
It is possible therefore to add additional anchor attributes by ending your URI value with a single quote and starting the next attribute.
eg. Author URI: http://www.example.com/about_me' target='_blank' tilte='Title Text
NOTE: Do not add a single quote to the end of the line.* System plugins are not listed in the "settings.dat.php" file. They are loaded automatically by virtue of their existence in the plugins directory.<?php /** * Plugin Name: Plugin name * Description: Plugin description. * Version: 1.0 * Requires at least: 2.00 */ addFilter("menulinks_array", "_menulinks_array",null,1); addAction("sort_menulinks_array", "_sort_menulinks_array",null,1); /** * Zend Editor Description * * @param Mixed $input * @return Mixed */ function _menulinks_array($inputArray) { doAction("sort_menulinks_array", $inputArray); $var = array_shift($inputArray); return $var; } /** * The Function Description * */ function _sort_menulinks_array(&$inputArray) { ksort($inputArray); // done } ?>The addFilter() Paramaters
Arg 1 (String). The Hook name "menulinks_array" is a pre set Hook Name in CMSB and is found in "lib/menus/header_functions.php".
Refer to "Admin > Plugins > Developer's Plugin Hook List" for a list of all the Hook Names.
Arg 2 (String). "_menulinks_array" is the name of your custom function.
Arg 3 (Integer). The third paramater "null" is the filter priority.
Since "null" is being passed in this will default to 10 which is the addAction() function's $priority default value.
Arg 4 (Integer). The fouth paramater is the number of arguments the hook "menulinks_array" is passing to the plugin.
After locating the filter in "header_functions.php", "applyFilters('menulinks_array', $menus)" we see their is only 1 (one) plugin argument, "$menus".
Remember, the first arg "menulinks_array" is the filter HookName and not considered a plugin paramater argument.
addAction()
In this example, addAction() uses a Hook Name created by you, that being "_sort_menulinks_array".
Notice that this Action Hook is activated in "function _menulinks_array()" by the line: doAction("sort_menulinks_array", $inputArray);Caution: There should not be any additional white space after the closing php ?> tag. Turn on line numbering if your editor supports this feature, other wise place your cursor after the closing php tag to check for blank lines. eg. consider the following : 1. <?php 2. /** 3. * Plugin Name: Plugin name ... 9. */ 10. ... your code ... 23. ... end of your code 24. // the next line tells PHP to return to HTML mode. 25. ?> 26. 27. 28. The white space that appears on lines 27 and 28 will generate a php headers_sent() error and a return value of true in some circumstances. For example, if you are using "Interactive Tools - Instant Website" the page may not display "for some site visitors", not all, instead they will see the message: "You must load (__FILE__) at top of file! Make sure there are no spaces before it!" The space being refered to is actually at the end of the plugin (which is before the __FILE__ is loaded)This error is extremely hard to reproduce during development and is inconsistent on a production server, however it is suffice to say it will not occur if you make sure you delete all white space after the closing php tag (after line 26 in the above example).This is OK. 24. ... 25. ?> 26.
(I'm not sure if this should be refered to as a bug or quirk but it is good programming to remove all white space anyway.)If you are using Interative Tools Instant Website you should do the following in "website_init.php": 1. On line 11, comment out: // if (headers_sent()){ die("You must load " .basename(__FILE__). " at top of file! // Make sure there are no spaces before it!\n"); } This will prevent displaying the error message and the page will load and display as expected. 2. Or comment it out and send your self an email error message instead. if(headers_sent($file,$line)) { $to = $SETTINGS['adminEmail']; $headers = "From: " . $SETTINGS['adminEmail']; $subject = "Headers Sent on ".$_SERVER['SERVER_NAME']; $msg = "Headers already sent accessing: ".$_SERVER['SERVER_NAME']. "\nIn $file, on line $line, from IP: ".$_SERVER['REMOTE_ADDR']; mail($to,$subject,$msg,$headers); } This will also prevent displaying the error message and the site will load as expected but will also send you an email alerting you the file that the has additional white space. The above also applies if you are calling headers_sent() in any custom scripts you have created.
Hook/Task referenceHook Reference with Task Descriptions
Hook List (CMSB 2.51) | Task | Action/Filter hook paramaters... | By |
---|---|---|---|
1. admin_footer lib/menus/footer.php |
Add content to the bottom of the HTML file (the footer) | addAction ( 'admin_footer', 'my_function', null, 0 ); doAction('admin_footer'); |
ITI |
2. admin_head lib/menus/header.php |
Add content in the head section of the HTML file. | addAction ( 'admin_head', 'my_function', null, 0 ); doAction('admin_head'); |
ITI |
3. admin_postlogin admin.php |
Do something after user authenticates | addAction ( 'admin_postlogin', 'my_function', null, 0 ); doAction('admin_postlogin'); |
ITI |
4. admin_prelogin admin.php |
Do something before user authenticates | addAction ( 'admin_prelogin', 'my_function', null, 0 ); doAction('admin_prelogin'); |
ITI |
5. backupDatabase_skippedTables lib/database_functions.php |
addFilter ( 'backupDatabase_skippedTables', 'my_function', null, 1 ); $skippedTables = applyFilters('backupDatabase_skippedTables', $skippedTables); |
||
6. edit_advancedCommands lib/menus/default/edit.php |
addFilter ( 'edit_advancedCommands', 'my_function', null, 1 ); $advancedCommands = applyFilters('edit_advancedCommands', $advancedCommands); |
||
7. edit_buttonsRight lib/menus/default/edit.php |
addFilter ( 'edit_buttonsRight', 'my_function', null, 3 ); $buttonsRight = applyFilters('edit_buttonsRight', $buttonsRight, $tableName, $GLOBALS['RECORD']); |
||
8. edit_fieldSchema lib/menus/default/edit_functions.php |
Modify schema record values before proccessing HTML in "edit" view. | addFilter ( 'edit_fieldSchema', 'my_function', null, 2 ); $fieldSchema = applyFilters('edit_fieldSchema', $fieldSchema, $tableName); |
ITI |
9. edit_show_field lib/menus/default/edit_functions.php |
Modify the HTML ouput of a schema record in "edit" view. | addFilter ( 'edit_show_field', 'my_function', null, 3 ); if (!applyFilters('edit_show_field', true, $fieldSchema, $record)) { continue; |
ITI |
10. edit_show_upload_link lib/menus/default/edit_functions.php |
addFilter ( 'edit_show_upload_link', 'my_function', null, 3 ); $displayDefaultLink = applyFilters('edit_show_upload_link', true, $fieldSchema, $record); |
||
11. emailTemplate_addDefaults lib/common.php |
addAction ( 'emailTemplate_addDefaults', 'my_function', null, 0 ); doAction('emailTemplate_addDefaults'); |
||
12. execute_seconds lib/menus/footer.php |
addFilter ( 'execute_seconds', 'my_function', null, 1 ); echo applyFilters('execute_seconds', $executeSecondsString); |
||
13. header_links lib/menus/sidebar.php |
Modify links in the CMSB left frame (the side bar) | addFilter ( 'header_links', 'my_function', null, 1 ); echo applyFilters('header_links', $headerLinks); |
ITI |
14. home_content lib/menus/home.php |
Modify the content that appears after login on the home page | addFilter ( 'home_content', 'my_function', null, 1 ); $content = applyFilters('home_content', $content); |
ITI |
15. home_title lib/menus/home.php |
Modify the title that appears after login on the home page | addFilter ( 'home_title', 'my_function', null, 1 ); $title = applyFilters('home_title', $title); |
ITI |
16. init_complete lib/init.php |
Do something after CMSB has been initialized (before admin_prelogin) | addAction ( 'init_complete', 'my_function', null, 0 ); doAction('init_complete'); |
ITI |
17. listHeader_checkAll lib/menus/default/list_functions.php |
addFilter ( 'listHeader_checkAll', 'my_function', null, 2 ); $html = applyFilters('listHeader_checkAll', $html, $tableName); |
||
18. listHeader_displayLabel lib/menus/default/list_functions.php |
addFilter ( 'listHeader_displayLabel', 'my_function', null, 3 ); $label = applyFilters('listHeader_displayLabel', $label, $tableName, $fieldname); |
||
19. listHeader_thAttributes lib/menus/default/list_functions.php |
addFilter ( 'listHeader_thAttributes', 'my_function', null, 3 ); $thAttrs = applyFilters('listHeader_thAttributes', $thAttrs, $tableName, $fieldname); |
||
20. listPage_footer lib/menus/default/list.php |
addFilter ( 'listPage_footer', 'my_function', null, 1 ); applyFilters('listPage_footer', $tableName); |
||
21. listRow_actionLinks lib/menus/default/list_functions.php |
addFilter ( 'listRow_actionLinks', 'my_function', null, 3 ); $actionLinks = applyFilters('listRow_actionLinks', $actionLinks, $tableName, $record); |
||
22. listRow_displayValue lib/menus/default/list_functions.php |
addFilter ( 'listRow_displayValue', 'my_function', null, 4 ); $displayValue = applyFilters('listRow_displayValue', $displayValue, $tableName, $fieldname, $record); |
||
23. listRow_tdAttributes lib/menus/default/list_functions.php |
addFilter ( 'listRow_tdAttributes', 'my_function', null, 4 ); $tdAttributes = applyFilters('listRow_tdAttributes', $tdAttributes, $tableName, $fieldname, $record); |
||
24. listRow_trStyle lib/menus/default/list_functions.php |
addFilter ( 'listRow_trStyle', 'my_function', null, 3 ); $trStyle = applyFilters('listRow_trStyle', '', $tableName, $record); |
||
25. list_advancedCommands lib/menus/default/list.php |
addFilter ( 'list_advancedCommands', 'my_function', null, 1 ); $advancedCommands = applyFilters('list_advancedCommands', $advancedCommands); |
||
26. list_buttonsRight lib/fieldtypes/relatedRecords.php lib/menus/default/list.php |
addFilter ( 'list_buttonsRight', 'my_function', null, 3 ); $buttonsRight = applyFilters('list_buttonsRight', $buttonsRight, $tableName, $isRelatedTable); |
||
27. list_orderBy lib/menus/default/list_functions.php |
addFilter ( 'list_orderBy', 'my_function', null, 2 ); $orderBy = applyFilters('list_orderBy', $orderBy, $tableName); |
||
28. list_postAdvancedSearch lib/menus/default/list.php |
addAction ( 'list_postAdvancedSearch', 'my_function', null, 1 ); doAction('list_postAdvancedSearch', @$_REQUEST['menu']); |
||
29. list_postselect lib/menus/default/list.php |
addAction ( 'list_postselect', 'my_function', null, 3 ); doAction('list_postselect', $records, $listFields, $metaData); |
||
30. list_where lib/menus/default/list_functions.php |
addFilter ( 'list_where', 'my_function', null, 2 ); $accessWhere = applyFilters('list_where', $accessWhere, $tableName); |
||
31. login_content lib/menus/login.php |
addFilter ( 'login_content', 'my_function', null, 1 ); $content = applyFilters('login_content', $content); |
||
32. login_isValidLogin lib/login_functions.php |
addFilter ( 'login_isValidLogin', 'my_function', null, 3 ); list($isValidLogin, $user, $updateLastLogin) = applyFilters('login_isValidLogin', array($isValidLogin, $user, $updateLastLogin)); |
||
33. menulinks_array lib/menus/header_functions.php |
addFilter ( 'menulinks_array', 'my_function', null, 1 ); $menus = applyFilters('menulinks_array', $menus); |
||
34. menulinks_rowHtml lib/menus/header_functions.php |
Modify the menu links in the left side bar | addFilter ( 'menulinks_rowHtml', 'my_function', null, 2 ); $rowHtml = applyFilters('menulinks_rowHtml', $rowHtml, $row); |
ITI |
35. plugin_actions lib/menus/admin/plugins.php |
addAction ( 'plugin_actions', 'my_function', null, 1 ); doAction('plugin_actions', $pluginData['filename']); |
||
36. plugin_activate lib/plugin_functions.php |
Do something when a plugin is being activated. (i.e. when inactive and "Activate" is clicked in "Admin > Plugins") |
addAction ( 'plugin_activate', 'my_function', null, 1 ); doAction( 'plugin_activate', $file ); |
ITI |
37. plugin_deactivate lib/plugin_functions.php |
Do something when a plugin is being deactivated. (i.e. when active and "Deactivate" is clicked in "Admin > Plugins") |
addAction ( 'plugin_deactivate', 'my_function', null, 1 ); doAction( 'plugin_deactivate', $file ); |
ITI |
38. record_access_where lib/menus/default/actionHandler.php lib/menus/default/list_functions.php |
addFilter ( 'record_access_where', 'my_function', null, 2 ); $accessWhere = applyFilters('record_access_where', $accessWhere, $tableName); |
||
39. record_posterase lib/menus/default/common.php |
addAction ( 'record_posterase', 'my_function', null, 2 ); doAction('record_posterase', $tableName, $recordNumsAsCSV); |
||
40. record_postsave lib/menus/default/save.php |
Do something after saving the record to its database table | addAction ( 'record_postsave', 'my_function', null, 4 ); doAction('record_postsave', $tableName, $isNewRecord, $oldRecord, $_REQUEST['num']); |
ITI |
41. record_preedit lib/menus/default/edit.php lib/menus/default/view.php |
addAction ( 'record_preedit', 'my_function', null, 2 ); doAction('record_preedit', $tableName, @$_REQUEST['num']); |
||
42. record_preerase lib/menus/default/common.php |
addAction ( 'record_preerase', 'my_function', null, 2 ); doAction('record_preerase', $tableName, $recordNumsAsCSV); |
||
43. record_presave lib/menus/default/save.php |
Do something before saving the record to its database table. | addAction ( 'record_presave', 'my_function', null, 3 ); doAction('record_presave', $tableName, $isNewRecord, $oldRecord); |
ITI |
44. record_save_errorchecking lib/menus/default/save.php |
addAction ( 'record_save_errorchecking', 'my_function', null, 3 ); doAction('record_save_errorchecking', $tableName, $recordExists, $oldRecord); |
||
45. record_save_posterrorchecking lib/menus/default/save.php |
addAction ( 'record_save_posterrorchecking', 'my_function', null, 3 ); doAction('record_save_posterrorchecking', $tableName, $recordExists, $oldRecord); |
||
46. record_saved_message lib/menus/default/actionHandler.php |
addFilter ( 'record_saved_message', 'my_function', null, 3 ); $message = applyFilters('record_saved_message', $message, $tableName, $recordNum); |
||
47. section_init lib/menus/default/actionHandler.php |
addAction ( 'section_init', 'my_function', null, 2 ); doAction('section_init', $tableName, $action); |
||
48. section_preDispatch lib/menus/default/actionHandler.php |
addAction ( 'section_preDispatch', 'my_function', null, 2 ); doAction('section_preDispatch', $tableName, $action); |
||
49. section_unknownAction lib/menus/default/actionHandler.php |
addAction ( 'section_unknownAction', 'my_function', null, 2 ); doAction('section_unknownAction', $tableName, $action); |
||
50. sendMessage lib/common.php |
addFilter ( 'sendMessage', 'my_function', null, 2 ); $eventState = applyFilters('sendMessage', $eventState, $options); |
||
51. ui_footer lib/admin_functions.php |
addFilter ( 'ui_footer', 'my_function', null, 1 ); if (applyFilters('ui_footer', TRUE)) { |
||
52. ui_header lib/admin_functions.php |
addFilter ( 'ui_header', 'my_function', null, 1 ); if (applyFilters('ui_header', TRUE)) { |
||
53. uploadModify_infoFieldHTML lib/menus/default/uploadModify.php |
addFilter ( 'uploadModify_infoFieldHTML', 'my_function', null, 6 ); $fieldHTML = applyFilters('uploadModify_infoFieldHTML', $fieldHTML, $tableName, $fieldName, $infoFieldname, $formFieldName, $uploadRecord); |
||
54. upload_adopted lib/upload_functions.php |
addAction ( 'upload_adopted', 'my_function', null, 2 ); doAction('upload_adopted', $tableName, $newRecordNum); |
||
55. upload_save lib/upload_functions.php |
addAction ( 'upload_save', 'my_function', null, 4 ); doAction('upload_saved', $tablename, $fieldname, $recordNum, $newUploadNum); |
||
56. upload_saveAsFilename lib/upload_functions.php |
addFilter ( 'upload_saveAsFilename', 'my_function', null, 3 ); $saveAsFilename = applyFilters('upload_saveAsFilename', $saveAsFilename, $uploadedAsFilename, $uploadDir); |
||
57. upload_saved lib/upload_functions.php |
addAction ( 'upload_saved', 'my_function', null, 4 ); doAction('upload_saved', $tablename, $fieldname, $recordNum, $newUploadNum); |
||
58. upload_thumbnail_save lib/upload_functions.php |
addAction ( 'upload_thumbnail_save', 'my_function', null, 4 ); doAction('upload_thumbnail_save', array($tableNameWithoutPrefix, $_REQUEST['fieldname'], $thumbNum, $thumbSavePath)); |
||
59. userHasFieldAccess lib/user_functions.php |
addFilter ( 'userHasFieldAccess', 'my_function', null, 2 ); $hasAccess = applyFilters('userHasFieldAccess', $hasAccess, $fieldSchema); |
||
60. userSectionAccess lib/user_functions.php |
addFilter ( 'userSectionAccess', 'my_function', null, 2 ); $accessLevel = applyFilters('userSectionAccess', $accessLevel, $tableName); |
||
61. view_buttonsRight lib/menus/default/view.php |
addFilter ( 'view_buttonsRight', 'my_function', null, 3 ); $buttonsRight = applyFilters('view_buttonsRight', $buttonsRight, $tableName, $GLOBALS['RECORD']); |
||
62. viewerOnly_allowed_actions lib/menus/default/actionHandler.php |
addFilter ( 'viewerOnly_allowed_actions', 'my_function', null, 1 ); $validActions = applyFilters('viewerOnly_allowed_actions', $validActions); |
||
63. viewer_link_field_content lib/viewer_functions.php |
addFilter ( 'viewer_link_field_content', 'my_function', null, 3 ); $filenameValue = applyFilters('viewer_link_field_content', $filenameValue, $fieldValue, $record); |
||
64. viewer_output_rows lib/viewer_functions.php |
addFilter ( 'viewer_output_rows', 'my_function', null, 3 ); $rows = applyFilters('viewer_output_rows', $rows, $listDetails, $schema); |
||
65. viewer_postinit lib/viewer_functions.php |
addAction ( 'viewer_postinit', 'my_function', null, 0 ); doAction('viewer_postinit'); |
As mentioned earlier, CMSB reads and parses all files in the "plugins" directory as well as 2 sub-directories below it.
For every file it reads, a regular expression search is performed looking for the key words listed in the "plugins Header" section above.
This happens 7 times in every file (once for each key word). In the case of a missing optional key word the entire file is searched from beginning to end.
When the first occurence of any "header-key-word & value" is matched, the searching stops and its value is stored and displayed in CMSB's Admin plugins list.
There is a lot of unnecessary file reading and proccessing which can easily be eliminated with the technique I use:
cmsAdmin | 3rdParty | data | lib | plugins - addApplyButton.php | - displayDemoNotice.php | - DirA - myPluginX1.php | - DirB - myPluginX2php | - myPluginX3.php | - DirC - otherFile.php < This directory will not be read by CMSB as it is 3 levels deep. | plugin-files - addApplyButton.php | - displayDemoNotice.php | - DirA - myPluginX1.php | - DirB - myPluginX2.php | - myPluginX3.php | ...The new "addApplyButton.php" file in the CMSB "plugins" directory now looks like this:
The original plugin files are moved to the new "plugin-files" directory.
A new file is created in the CMSB "plugins" directory with the same name as the plugin file.
Copy only the header information from the plugin and add one line of code linking it to the file in the "plugin-files" directory.
As you can see the entire file consists of only the Doc Comment and a single line linking it to the original file.<?php /* Plugin Name: Add Apply Button Description: Adds an "Apply" button beside "Save" buttons Author: Chris Version: 0.02 Requires at least: 2.06 */ require_once(str_ireplace('plugins','plugin-files',__FILE__)); ?>Note: The action/filter lines:
addFilter("menulinks_array", "_menulinks_array",null,1);
addAction("sort_menulinks_array", "_sort_menulinks_array",null,1);
Should be in the require_once() file to be correctly referenced in the "Where it's used..." column of the "Developer's Plugin Hook List"
Exception: Plugin Code Generators
If you create your own Code Generators the filter "addGenerator(...)" must be in plugin header file not in the plugin-files dir.
/*
...
*/
addGenerator("functionName", "name", "description);
require_once(str_ireplace('plugins','plugin-files',__FILE__));There are 3 reasons why I use this technique.AppendixCertainly one could argue with todays processing power that loading large files is irrelevant but why load a file containing of 1000's of lines of code and parse it 7 times
- A more efficient means of loading and listing the plugins within the CMSB Admin Interface
- When performing an upgrade the plugin-files directory containing the actual plugin code will not be over written thus preserving any customization.
- Every click on every page by every site visitor parses the plugin files ...
when all you ever need is the first half dozen lines from the original file.
The second reason is self explanatory especially if you don't backup your files regularly.
As for the third reason, I can only imagine the wasted proccessing that would occur on High volume site.
And what's worse is that in all likely hood none of the plugins are needed to view the web pages.
(I actually use a seperate file of viewing functions that does not require the plugin files to be loaded/parsed to eliminate this issue when viewing website pages.)
The example file I used above is quite small and really irrelevant. However, I have and am working on a lot of different plugins for different purposes.
My plugins vary in size from 300 to 1000 lines each. When you add it up, that's a lot of unnecessary parsing (times 7)!
Your feedback is welcome. (Send a note.)
CMSB Errors in version 2.11 (and earlier)As of CMSB ver 2.09 the plugins page contains a link for displaying all hooks and filters.Upgrade Notice: This bug has been fixed in CMSB version 2.12. If you are using a version earlier than 2.12 you should make the changes indicated below or upgrade to CMSB 2.12 or newer.
As of CMSB ver 2.11 the "Developer's Plugin Hook List" only lists actions and filters that have been enclosed in "single quotes". eg. The following will appear in the list addAction('admin_postlogin','_admin_postlogin'); where as this will not appear in the list: addAction("admin_postlogin","_admin_postlogin"); The reason is that the array of hookTypes is only looking for single quotes. // top of lib/menus/admin/pluginHooks.php $hookTypes = array( 'filter' => array( 'callerRegex' => "|applyFilters\(\s*'(.*?)'|", 'pluginRegex' => "|addAction\('(.*?)'|" ), << in wrong array 'action' => array( 'callerRegex' => "|doAction\(\s*'(.*?)'|", 'pluginRegex' => "|addFilter\('(.*?)'|" ), << in wrong array ); The following modification will find either single or double quotes and list it with the correct "type": $hookTypes = array( 'filter' => array( 'callerRegex' => "|applyFilters\(\s*['\"](.*?)['\"]|", 'pluginRegex' => "|addFilter\(['\"](.*?)['\"]|" ), 'action' => array( 'callerRegex' => "|doAction\(\s*['\"](.*?)['\"]|", 'pluginRegex' => "|addAction\(['\"](.*?)['\"]|" ), );
While your at it, you may want to add the counter value at the beginning of the line since the $counter variable is set anyway. //Line 126 in CMSB ver 2.11: <?php echo htmlspecialchars($hookName); ?> // changed to: <?php echo htmlspecialchars("[".$counter."] ".$hookName); ?> The plugins list will now be numbered as: Hook Name Type ..... [1] admin_footer action [2] admin_head action [3] admin_postlogin action ... etc It would make references to hooks a little easier since the list can be quite lengthy.