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
John, I'm really glad that Actions is getting some serious attention. I used Actions (and Workflow) in a pretty big way last year and learned to see them as the basis of "Drupal's scripting language." For background (to explain my feature request), I built Classified Ads functionality, complete with e-commerce integration, editorial review, a "revision required" state, publication, and expiration. I even have credit cards get authorized when the ad node is submitted but not charged until it is published. It is all driven by Workflow using several custom actions (particularly for e-commerce integration) and very little additional code. One of my meta-actions is "Convert this node into an EC product." The config form lets me specify product price, among other things. I needed to assign different prices based on the taxonomy term the user selected for the ad (job postings cost more than free stuff postings). So I ended up adding another field to my config form called "filter" that says "only perform this action on a node if it has the selected term." I can then create a separate instance of my meta-action for each term, assigning the appropriate price to each. When a node is created, the action set to filter to that node's term sets the price and productizes the node; the others just return because of the filter. It works great. However, I have another action that also needs term filtering. I had to add the same filtering code to both. So, what I really want is a module that implements "hook_action_form_alter()" that can add my term filtering to any action config form, get notified when the action is invoked, and decide whether or not it should execute. hook_action_form_alter() should have a $op argument for: 'form', 'validate', 'pre-execute' (can return false to prevent execution), 'execute' (action just executed), maybe others too. Thanks, Barry
So, what I really want is a module that implements "hook_action_form_alter()" that can add my term filtering to any action config form, get notified when the action is invoked, and decide whether or not it should execute. hook_action_form_alter() should have a $op argument for: 'form', 'validate', 'pre-execute' (can return false to prevent execution), 'execute' (action just executed), maybe others too.
Seems to me that this is a specific instance of a conditional, with a proposed mechanism for how conditionals would be implemented. I agree that conditionals are eventually necessary, probably on an action set and individual action level. You can form_alter the actions configuration form currently by targeting form id 'actions_configure'.
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.
It's great to see that you are working on improving actions :) I don't know, if you ever have read my workflow-ng proposal, which has some similiar things in. -> http://more.zites.net/node/3030 I've also posted it to the workflow group. Actually I've planned a layer between actions and hooks: events. It should achieve, what you have built now as contexts. Events sit on top of hooks and prepare the context for use with actions (and conditions..). E.g. some of your default actions load the $node for the context comment. Ideally that could be factored out of actions, so that a code reacts on comment insertion/update, prepares the data and calls the event, which fires configured actions / conditions. What do you think about that?
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.
My concept also builds upons conditions. Perhaps I can help, so that we could already get it into drupal 6 :) If you are interested in my help on this - Where can I reach you for talking about this stuff? regards, fago
I don't know, if you ever have read my workflow-ng proposal, which has some similiar things in. -> http://more.zites.net/node/3030 I've also posted it to the workflow group.
Actually I've planned a layer between actions and hooks: events. It should achieve, what you have built now as contexts. Events sit on top of hooks and prepare the context for use with actions (and conditions..).
Yes, I've read your proposal. It sounds a lot like something Vlado proposed a while back; essentially a layer of abstraction above our current hook system. Although I am drawn towards greater and greater abstraction, I think currently the best approach is to treat the current hooks themselves as events. They already occur in predictable contexts and pass predictable parameters. Modules are welcome to define their own hooks.
E.g. some of your default actions load the $node for the context comment. Ideally that could be factored out of actions, so that a code reacts on comment insertion/update, prepares the data and calls the event, which fires configured actions / conditions. What do you think about that?
I think that as long as we have types of actions, we can factor it out. For example, you know an action of type "Node" such as "Promote node to front page" is going to need a node to act upon. Thus, you can factor out the process that goes "Hey, this action is being called during the comment-insert hook/op combination and the node is not available; thus, pack the node into the context during the action.module's implementation of the comment hook." However, you have to be very careful about this. The action itself knows the most about what it needs, and we want to avoid stuffing everything and the kitchen sink into the context just in case the action needs it.
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.
My concept also builds upons conditions. Perhaps I can help, so that we could already get it into drupal 6 :)
If you are interested in my help on this - Where can I reach you for talking about this stuff?
I've created a group at http://groups.drupal.org/actions for such discussions.
Am Mittwoch, den 02.05.2007, 15:34 -0500 schrieb John VanDyk:
Although I am drawn towards greater and greater abstraction, I think currently the best approach is to treat the current hooks themselves as events. They already occur in predictable contexts and pass predictable parameters. Modules are welcome to define their own hooks.
E.g. some of your default actions load the $node for the context comment. Ideally that could be factored out of actions, so that a code reacts on comment insertion/update, prepares the data and calls the event, which fires configured actions / conditions. What do you think about that?
I think that as long as we have types of actions, we can factor it out. For example, you know an action of type "Node" such as "Promote node to front page" is going to need a node to act upon. Thus, you can factor out the process that goes "Hey, this action is being called during the comment-insert hook/op combination and the node is not available; thus, pack the node into the context during the action.module's implementation of the comment hook." However, you have to be very careful about this. The action itself knows the most about what it needs, and we want to avoid stuffing everything and the kitchen sink into the context just in case the action needs it.
I agree, that this has to be avoided. I've just committed some basic code to my sandbox, that implements the basic event concept like I have it in mind: http://cvs.drupal.org/viewcvs/drupal/contributions/sandbox/fago/workflow-ng/... It auto-loads arguments only when they are needed. So modules define events and the available arguments, as well as possible available arguments that the system loads, if there are actions defined for it. What's missing, is mapping the arguments to the actions.. This separates the "context" code out of the actions. Actually the action isn't aware of the context, it just has to define the argument(s) on which it wants to work. So, not every action has to re-invent the wheel and support each possible context - nevermind if we are invoked from the comment hook implementation or not! The action gets the argument(s) and can just do its work :)
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.
My concept also builds upons conditions. Perhaps I can help, so that we could already get it into drupal 6 :)
If you are interested in my help on this - Where can I reach you for talking about this stuff?
I've created a group at http://groups.drupal.org/actions for such discussions. great! I suggest moving this discussion over there.
regards, fago
participants (3)
-
Barry Jaspan -
fago -
John VanDyk