[development] Actions in core

John VanDyk jvandyk at iastate.edu
Tue May 1 22:08:01 UTC 2007


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


More information about the development mailing list