Right now, SimpleVote is the best example of how an external module can hook into VotingAPI's action functionality. I'm writing up more detailed docs on how it works internally, but for now let's take a quick tour. First, there's hook_votingapi_action_sets(), a hook that any module can implement to return a collection of 'action sets' for consideration whenever a vote result is processed.
Each action set includes some key information:
- Meta-data, like name and description
- Some behavioral data, like whether ALL of its conditions must be true or only ONE,
- A set of 'condition' records that are evaluated
- A set of 'actions' that are executed if the conditions are satisfied
For SimpleVote, the first chunk is... well, simple.
function simplevote_votingapi_action_sets() {
$sets = array(
'simple_promotion' => array(
'description' => 'Promotes a node to the front page if enough users rate it highly.',
'content_type' => 'node',
'condition_mask' => 'AND',
'enabled' => 1,
'source' => 'SimpleVote','content_type' is something like node, comment, etc. It corresponds to the 'content_type' field of a votingAPI vote record. It is used to keep conditions from unecessarily attempting to process data types they wouldn't understand.
'condition_mask' can be AND , or OR. It indicates whether all the conditions in the action set must return 'true' for the set to be processed, or whether AT LEAST ONE must return true.
Next is the 'conditions' chunk.
'conditions' => array(
'is_promoted' => array(
'description' => 'Node is not promoted',
'handler' => 'votingapi_node_properties_handler',
'data' => array(
'property' => 'promote',
'comparison' => '!=',
'value' => 1,
),
),
'min_vote' => array(
'description' => 'Average higher than 75',
'handler' => 'votingapi_vote_result_handler',
'data' => array(
'function' => 'average',
'tag' => 'vote',
'comparison' => '>',
'value' => 75,
),
),
),The array-of-arrays format should be familiar to FormAPI veterans. Each key is a unique condition-name. 'description' is just an explanitory blurb, while 'handler' and 'data' do the real work. 'handler' is a function name, while 'data' is an optional array of keyed values that can be passed into the function. The handler function is passed the content object, the vote results, the original votes, and the 'data' array from this condition record. It can evaluate all those pieces of information and return a boolean indicating 'pass' or 'fail.'
The 'votingapi_node_properties_handler' accepts the name of a property, the boolean comparison to use when evaluating it, and the value to check that property against. In the example above, it makes sure that a node is NOT already promoted to avoid re-promoting nodes over and over.
The 'votingapi_vote_result_handler' accepts a set of filters (function, tag, and value_type) standard to all votingapi result records, as well as a 'comparison' boolean operator and a 'value' to compare to.. In the example above, it looks for a 'vote average' result record whose value is at least 75.
Multiple conditions with the same handler can be chainged -- for example, another votingapi_vote_result_handler based condition could be added to check that the 'vote count' is at least 5 before promoting -- to keep ONE enthusiastic member from promoting everything.
Finally, there's the 'actions' chunk.
'actions' => array(
'action_node_promote',
),
),
);It's just an array of action names from the actions table. If the conditions pass, VotingAPI executes each of the actions in this list with the content object in question.
Further updates and clarifications as I have more time today. :)
--Jeff

Comments
workflow
a set of actions and conditions is similar to a workflow, right? just thinking about allthese cool pieces. there is a lot here which is not specific to voting ... cool stuff.
Yep.
There's not really much that's specific to votingapi in this action-handling code. It's almost all just built around managing hierarchies of sets, conditions, and actions. If workflow implemented a few 'move to next stage' actions, it would be relatively easy to create a consensus-based editorial process. If at least 3 editors approve a proposal, it gets promoted to 'draft', and the author can submit subsequent revisions. If those are voted down, it goes back to 'proposal', but if they're voted up, it gets added to a node_queue.... and so on.
One thing that's not mentioned in the example above is that each action set can ALSO have a collection of sub-action-sets. One useful example might be: a parent set has two 'branches' of conditions and actions, depending on the node->type. I haven't mentioned that, as I'm not sure how to design the administration interface for infinitely nested action sets... ;) But the cod to save, process, and ACT on those nested sets is already in place.
decions
your decisions funcytionality is planned for workflow. please see the SoC proposal for workflow improvements - on this site somewhere.
A side note...
...The CVS version of VotingAPI now includes a working votingapi_actions.module. It allows you to examine any user-defined and module-defined voting action sets, and import/export action sets via copy/paste. So, no user-friendly forms to edit the action sets yet, but for developers and tinkerers, it's now possible to define action sets by hand and 'paste' them straight into the system.
Further updates as events warrant...
tweaking this for a different voting system
Hi! I'm trying to adapt the actions set for a +1/-1 voting system so that, say, a node rated lower than -2 gets demoted from the frontpage. How do I change this bit of your code?
'min_vote' => array(
'description' => 'Average higher than 75',
'handler' => 'votingapi_vote_result_handler',
'data' => array(
'function' => 'average', //should this be 'sum'?
'tag' => 'vote',
'comparison' => '>', //ok, here I just change it to '<'
'value' => 75, //and this is changed to '-2'
Is this correct?