Subject: GL2-like actions in GL1.5(?)
Posted on: 06/10/06 02:21am
I find the current way to extend functions rather backward. A system I worked with years ago works something like the GL2 action manager (see the wiki). I don't see any reason such an idea couldn't be used in GL1. Granted, I wouldn't do it with classes since plugins don't work that way in GL1 but the idea of having named actions (core_userlogin, core_newuser, core_deleteuser, forum_newtopic, forum_newreply, poll_newpoll, poll_userresponse, event_addtocalander, etc). Currently, for example, PLG_getMenuItems asks each plugin if it wishes to add items to the menubar. If instead plugins subscribed to a core_addtomainmenu action then only those plugins would be called.
This would require a transitional period where both the old plugin extension and the new action handling would coexist. But even that would make it easier to go into the existing codebase and add a bunch of event triggers. The triggers would do nothing until someone wrote a plugin to take advantage of them. (I started this as a plugin. But it makes no sense as a plugin. You can't make calls to a plugin from the core unless the plugin ships with the core.)
A file named lib-action.php would go in the system directory and it would be loaded as early as possible in the lib-common.php file (so lib-common could fire triggers). The action API would consist of:
function ACT_publish(string $action, string $owning_plugin = '')
function ACT_subscribe(string $action, string $subscribing_plugin)
These two functions would only be called during plugin installation. Actions triggered by the plugin are announce to the system using the publish function. Actions acted on by the plugin are subscribed using the subscribe function. The call to ACT_publish would trigger the "action_publish" action to any existing plugin interested in creation of new action words.
function ACT_withdraw(string $action, string $owning_plugin)
ACT_withdraw removes an action from the system, deleting all subscriptions to the action. This would normally be called from the uninstall function.
function ACT_unsubscribe(string $action, string $subscribed_plugin)
ACT_unsubscribe removes a plugin from the listener list of an action. This would normally be called from the uninstall function.
function ACT_trigger(string $action, mixed &$data = Array())
ACT_trigger loops over each subscribed plugin calling the function "action_$action_$plugin" and passing the $data by reference. So for example, COM_siteHeader might call ACT_trigger('core_addtomainmenu', &$mainmenu) so items could be added to the main menu by subscribed plugins. Another cool part of ACT_trigger would be to assume that a "custom" plugin always exists and is always subscribed to all actions so that site owners could easily hook actions without making a plugin by putting functions in lib-custom.php named "action_$action_custom".
Lib-action would probably load the gl_act_sub and gl_act_def tables into global arrays so that looping through the subscriber lists would not require continuous db lookups. But that might turn out to be premature optimization.
Please remember the examples above would only exist in an ideal world. Mostly there are too many older plugins that will not take advantage of this code. But there are lots of places in the GL1 core where I wish I could add a few lines of PHP without having to folk my installation. Asking the developers "Could you add an action to the next version of GL1 called 'story_contributor_display' so I can make a multiple author hack?" And they would just have to make a few modifications to add the functionality. A call to ACT_publish in the installer and a couple calls to ACT_trigger in lib-story.php and article.php later and the user can hack on the contributer line to his heart's content without affecting al the other users. And even if the devs say no, the hack doesn't make deep mods in the GL core requiring a significant fork.
Adding this functionality to GL1 might point out pitfalls to the action manager system before GL2 coding is in full swing. You don't want a trigger every other line but how often is too often and how often is not often enough? Using GL1 to guinea pig the idea could help GL2 in the long run. Thoughts?
Geeklog - Forum