What are actions? ----------------- Warning: please disassociate any thoughts you have about workflow and actions. They are two separate modules. Workflow implements a state machine to move a node through various states. That is its main purpose. Actions only come into play because the workflow module knows how to assign and fire actions. So, putting any thoughts of workflow aside, let's proceed. Actions are like stored procedures that can be loosely coupled with events in Drupal. Let's examine a ridiculously simple example. Suppose you want to be draconian with your Drupal site's comment policy. Any user who leaves a comment gets blocked immediately. You can implement this quite easily with the comment hook: foo_comment($a1, $op) { if ($op == 'insert') { global $user; // block user here... } } What an action does is wrap up this code into a nice package. That package can then be associated with the nodeapi hook. Or some other hook. Actions takes this function, which we'll call "Block current user", and allows you to assign it to any hook-op combination. We do that by several coding conventions. Here is the skeleton of a simple action: /** * Implementation of a Drupal action. */ function action_block_user($op, $context = array(), &$object) { switch ($op) { case 'do': global $user; // block user here... break; case 'metadata': return array( 'description' => t('Block current user'), 'type' => 'User', 'batchable' => TRUE, 'configurable' => FALSE, 'supported hooks' => array( 'nodeapi' => array( 'delete' => 1, 'insert' => 1, 'update' => 1, 'view' => 1 ), 'comment' => array( 'insert' => 1, 'update' => 1, 'delete' => 1 ) ) ); } } We've implemented two ops. The 'metadata' op describes the action by declaring some things, including what hook/op combinations it supports. The 'do' op is actually where the code runs. We list the 'do' op first in the switch statement for performance, since Drupal will then encounter it first during execution. The above is a nonconfigurable action. There's nothing about it you can change. It will block the current user and that's that. It's basically a singleton action. On the other hand, an action like "Send email" needs to know some things in order to run. It needs a subject, and body, a recipient, and such. Configurable actions are not available for use until they have been configured (obviously). They can be configured from the main actions interface at admin/build/actions. Check out the HEAD version of actions and install it to play along. Note that I'm currently developing against Drupal 5 until things are a bit more stable. What's new? ---------- All of this is not new. I've been saying the above for years now. So here's what's new. 1. We now have a central interface to assign actions to hooks through the browser. There's a new "assign actions" section at admin/build/actions/assign. I demonstrated that at OSCMS for those of you who were there. So you can now assign the "Block current user" action to the nodeapi view op, and so on. You can't assign it to the cron hook, because that would be stupid. 2. The actions engine has been pulled out into a separate .inc file which will live at includes/actions.inc. This is so that, like menu.module, actions.module can be disabled if you're not using actions, or are using actions only within your own modules. What are the current problems? ------------------------------ We are still feeling around for the best way to unify information fed into actions. The current approach is to pass an action-specific $op (like 'do' or 'metadata', see above), a context array, and an object. So when an action gets an object, it can look at the context and know whether it's receiving a node object or a comment object or whatever. See the code in default_actions.inc for examples of actions figuring out what to do based on the context. I think this is a simple approach that will work. Another issue is, uh, factoring of Drupal code. Core has a lot of things that have never been factored out. In our example above, you'd expect to be able to call a function like user_block_user($uid) so that the action's 'do' op just becomes a wrapper around existing functionality. Instead, currently user blocking is done inside user_save(). So you could say "well, just do user_load(), change the status, and do user_save() and stop mumbling about factoring!" OK, that leads me to the next issue. Recursion. If you take that approach, and you do a user_load() and a user_save(), lots of hooks are going to fire. If you're not careful, you might have assigned actions to those hooks which call functions which call other hooks. The complexity of the execution path can become an issue. Currently the actions engine keeps a stack and if more than 25 calls are made to actions_do() it bails out. For more about recursion, see the beginning of this paragraph. What is the future look like for actions? ---------------------------------------- I'd like to see actions for each module in core. If we refactor some code in a smart way, adding actions will just be adding wrappers around existing functionality. So statistics.module can have a "Clear access log" action, taxonomy.module can have a "Add title to vocabulary" action, etc. etc. The current default_actions.inc contains actions that will be distributed to the various core modules. The next major hurdle for actions consists of action sets and conditionals. Action sets are groups of actions that go together. Conditionals evaluate and determine whether an action set actually executes. eaton has paved the way for both of these in his voting actions module. Unless we're very busy bees, that will have to wait for Drupal 7. What can you do to help? ------------------------ Install Drupal 5. Download the HEAD version of actions module. Throw the actions.inc file into your includes directory. Poke around. Write some actions. Give some feedback. Cheers, John