summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2009-09-19 11:07:37 +0000
committerDries Buytaert <dries@buytaert.net>2009-09-19 11:07:37 +0000
commitfd1c63b5c0ce91849d6088b7aad40b27ea7bb96f (patch)
tree7742fc7fdc55f819c066be08a3419995b591439b
parenta557b0de2ac5d0b2048a456f94f9b8047afa71b9 (diff)
downloadbrdo-fd1c63b5c0ce91849d6088b7aad40b27ea7bb96f.tar.gz
brdo-fd1c63b5c0ce91849d6088b7aad40b27ea7bb96f.tar.bz2
- Patch ##525540 by jvandyk, sun, jhodgdon, fago | webchick, TheRec, Dave Reid, brianV, sun.core, cweagans, Dries: gave trigger.module and includes/actions.inc an API overhaul. Simplified definitions of actions and triggers and removed dependency on the combination of hooks and operations. Triggers now directly map to module hooks.
-rw-r--r--CHANGELOG.txt4
-rw-r--r--includes/actions.inc180
-rw-r--r--modules/comment/comment.module36
-rw-r--r--modules/node/node.module101
-rw-r--r--modules/simpletest/tests/actions.test18
-rw-r--r--modules/simpletest/tests/actions_loop_test.module20
-rw-r--r--modules/system/system.admin.inc273
-rw-r--r--modules/system/system.api.php84
-rw-r--r--modules/system/system.install13
-rw-r--r--modules/system/system.module316
-rw-r--r--modules/taxonomy/taxonomy.module21
-rw-r--r--modules/trigger/tests/trigger_test.module74
-rw-r--r--modules/trigger/trigger.admin.inc185
-rw-r--r--modules/trigger/trigger.api.php166
-rw-r--r--modules/trigger/trigger.install24
-rw-r--r--modules/trigger/trigger.module384
-rw-r--r--modules/trigger/trigger.test164
-rw-r--r--modules/user/user.module34
18 files changed, 1085 insertions, 1012 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 47914f0b1..40d886941 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -182,6 +182,10 @@ Drupal 7.0, xxxx-xx-xx (development version)
them complete access to the site.
* Access control affects both published and unpublished nodes.
* Numerous other improvements to the node access system.
+- Actions system
+ * Simplified definitions of actions and triggers.
+ * Removed dependency on the combination of hooks and operations. Triggers
+ now directly map to module hooks.
Drupal 6.0, 2008-02-13
----------------------
diff --git a/includes/actions.inc b/includes/actions.inc
index 5650bc64f..65089b6ac 100644
--- a/includes/actions.inc
+++ b/includes/actions.inc
@@ -7,35 +7,31 @@
*/
/**
- * Perform a given list of actions by executing their callback functions.
+ * Performs a given list of actions by executing their callback functions.
*
- * Given the IDs of actions to perform, find out what the callbacks
- * for the actions are by querying the database. Then call each callback
- * using the function call $function($object, $context, $a1, $a2)
- * where $function is the name of a function written in compliance with
- * the action specification; that is, foo($object, $context).
+ * Given the IDs of actions to perform, this function finds out what the
+ * callback functions for the actions are by querying the database. Then
+ * it calls each callback using the function call $function($object, $context,
+ * $a1, $a2), passing the input arguments of this function (see below) to the
+ * action function.
*
* @param $action_ids
- * The ID of the action to perform. Can be a single action ID or an array
- * of IDs. IDs of instances will be numeric; IDs of singletons will be
- * function names.
+ * The IDs of the actions to perform. Can be a single action ID or an array
+ * of IDs. IDs of configurable actions must be given as numeric action IDs;
+ * IDs of non-configurable actions may be given as action function names.
* @param $object
- * Parameter that will be passed along to the callback. Typically the
- * object that the action will act on; a node, user or comment object.
+ * The object that the action will act on: a node, user, or comment object.
* @param $context
- * Parameter that will be passed along to the callback. $context is a
- * keyed array containing extra information about what is currently
- * happening at the time of the call. Typically $context['hook'] and
- * $context['op'] will tell which hook-op combination resulted in this
- * call to actions_do().
+ * Associative array containing extra information about what triggered
+ * the action call, with $context['hook'] giving the name of the hook
+ * that resulted in this call to actions_do().
* @param $a1
- * Parameter that will be passed along to the callback.
+ * Passed along to the callback.
* @param $a2
- * Parameter that will be passed along to the callback.
- *
+ * Passed along to the callback.
* @return
- * An associative array containing the result of the function that
- * performs the action, keyed on action ID.
+ * An associative array containing the results of the functions that
+ * perform the actions, keyed on action ID.
*/
function actions_do($action_ids, $object = NULL, $context = NULL, $a1 = NULL, $a2 = NULL) {
// $stack tracks the number of recursive calls.
@@ -109,49 +105,20 @@ function actions_do($action_ids, $object = NULL, $context = NULL, $a1 = NULL, $a
}
/**
- * Discover all action functions by invoking hook_action_info().
- *
- * @code
- * mymodule_action_info() {
- * return array(
- * 'mymodule_functiondescription_action' => array(
- * 'type' => 'node',
- * 'description' => t('Save node'),
- * 'configurable' => FALSE,
- * 'hooks' => array(
- * 'node' => array('delete', 'insert', 'update', 'view'),
- * 'comment' => array('delete', 'insert', 'update', 'view'),
- * )
- * )
- * );
- * }
- * @endcode
+ * Discovers all available actions by invoking hook_action_info().
*
- * The description is used in presenting possible actions to the user for
- * configuration. The type is used to present these actions in a logical
- * grouping and to denote context. Some types are 'node', 'user', 'comment',
- * and 'system'. If an action is configurable it will provide form,
- * validation and submission functions. The hooks the action supports
- * are declared in the 'hooks' array.
+ * This function contrasts with actions_get_all_actions(); see the
+ * documentation of actions_get_all_actions() for an explanation.
*
* @param $reset
* Reset the action info static cache.
- *
* @return
- * An associative array keyed on function name. The value of each key is
- * an array containing information about the action, such as type of
- * action and description of the action, e.g.:
- * @code
- * $actions['node_publish_action'] = array(
- * 'type' => 'node',
- * 'description' => t('Publish post'),
- * 'configurable' => FALSE,
- * 'hooks' => array(
- * 'node' => array('presave', 'insert', 'update', 'view'),
- * 'comment' => array('delete', 'insert', 'update', 'view'),
- * ),
- * );
- * @endcode
+ * An associative array keyed on action function name, with the same format
+ * as the return value of hook_action_info(), containing all
+ * modules' hook_action_info() return values as modified by any
+ * hook_action_info_alter() implementations.
+ *
+ * @see hook_action_info()
*/
function actions_list($reset = FALSE) {
static $actions;
@@ -165,19 +132,24 @@ function actions_list($reset = FALSE) {
}
/**
- * Retrieve all action instances from the database.
+ * Retrieves all action instances from the database.
*
- * Compare with actions_list() which gathers actions by invoking
- * hook_action_info(). The two are synchronized by visiting
- * /admin/structure/actions (when actions.module is enabled) which runs
- * actions_synchronize().
+ * This function differs from the actions_list() function, which gathers
+ * actions by invoking hook_action_info(). The actions returned by this
+ * function and the actions returned by actions_list() are partially
+ * synchronized. Non-configurable actions from hook_action_info()
+ * implementations are put into the database when actions_synchronize() is
+ * called, which happens when admin/config/system/actions is visited. Configurable
+ * actions are not added to the database until they are configured in the
+ * user interface, in which case a database row is created for each
+ * configuration of each action.
*
* @return
- * Associative array keyed by action ID. Each value is an associative array
- * with keys 'callback', 'description', 'type' and 'configurable'.
+ * Associative array keyed by numeric action ID. Each value is an associative
+ * array with keys 'callback', 'label', 'type' and 'configurable'.
*/
function actions_get_all_actions() {
- $actions = db_query("SELECT aid, type, callback, parameters, description FROM {actions}")->fetchAllAssoc('aid', PDO::FETCH_ASSOC);
+ $actions = db_query("SELECT aid, type, callback, parameters, label FROM {actions}")->fetchAllAssoc('aid', PDO::FETCH_ASSOC);
foreach ($actions as &$action) {
$action['configurable'] = (bool) $action['parameters'];
unset($action['parameters']);
@@ -187,28 +159,26 @@ function actions_get_all_actions() {
}
/**
- * Create an associative array keyed by md5 hashes of function names.
+ * Creates an associative array keyed by md5 hashes of function names or IDs.
*
* Hashes are used to prevent actual function names from going out into HTML
* forms and coming back.
*
* @param $actions
- * An associative array with function names as keys and associative arrays
- * with keys 'description', 'type', etc. as values. Generally the output of
- * actions_list() or actions_get_all_actions() is given as input to this
- * function.
- *
+ * An associative array with function names or action IDs as keys
+ * and associative arrays with keys 'label', 'type', etc. as values.
+ * This is usually the output of actions_list() or actions_get_all_actions().
* @return
- * An associative array keyed on md5 hash of function names. The value of
- * each key is an associative array of function, description, and type for
- * the action.
+ * An associative array whose keys are md5 hashes of the input array keys, and
+ * whose corresponding values are associative arrays with components
+ * 'callback', 'label', 'type', and 'configurable' from the input array.
*/
function actions_actions_map($actions) {
$actions_map = array();
foreach ($actions as $callback => $array) {
$key = md5($callback);
$actions_map[$key]['callback'] = isset($array['callback']) ? $array['callback'] : $callback;
- $actions_map[$key]['description'] = $array['description'];
+ $actions_map[$key]['label'] = $array['label'];
$actions_map[$key]['type'] = $array['type'];
$actions_map[$key]['configurable'] = $array['configurable'];
}
@@ -216,17 +186,19 @@ function actions_actions_map($actions) {
}
/**
- * Given an md5 hash of a function name, return the function name.
+ * Given an md5 hash of an action array key, returns the key (function or ID).
*
- * Faster than actions_actions_map() when you only need the function name.
+ * Faster than actions_actions_map() when you only need the function name or ID.
*
* @param $hash
- * MD5 hash of a function name.
- *
+ * MD5 hash of a function name or action ID array key. The array key
+ * is a key into the return value of actions_list() (array key is the action
+ * function name) or actions_get_all_actions() (array key is the action ID).
* @return
- * The corresponding function name or FALSE if none is found.
+ * The corresponding array key, or FALSE if no match is found.
*/
function actions_function_lookup($hash) {
+ // Check for a function name match.
$actions_list = actions_list();
foreach ($actions_list as $function => $array) {
if (md5($function) == $hash) {
@@ -234,26 +206,26 @@ function actions_function_lookup($hash) {
}
}
- // Must be an instance; must check database.
+ // Must be a configurable action; check database.
return db_query("SELECT aid FROM {actions} WHERE MD5(aid) = :hash AND parameters <> ''", array(':hash' => $hash))->fetchField();
}
/**
- * Synchronize actions that are provided by modules.
+ * Synchronizes actions that are provided by modules in hook_action_info().
*
- * Actions provided by modules are synchronized with actions that are stored in
- * the actions table. This is necessary so that actions that do not require
- * configuration can receive action IDs. This is not necessarily the best
- * approach, but it is the most straightforward.
+ * Actions provided by modules in hook_action_info() implementations are
+ * synchronized with actions that are stored in the actions database table.
+ * This is necessary so that actions that do not require configuration can
+ * receive action IDs.
*
* @param $delete_orphans
- * Boolean if TRUE, any actions that exist in the database but are no longer
+ * If TRUE, any actions that exist in the database but are no longer
* found in the code (for example, because the module that provides them has
* been disabled) will be deleted.
*/
function actions_synchronize($delete_orphans = FALSE) {
$actions_in_code = actions_list(TRUE);
- $actions_in_db = db_query("SELECT aid, callback, description FROM {actions} WHERE parameters = ''")->fetchAllAssoc('callback', PDO::FETCH_ASSOC);
+ $actions_in_db = db_query("SELECT aid, callback, label FROM {actions} WHERE parameters = ''")->fetchAllAssoc('callback', PDO::FETCH_ASSOC);
// Go through all the actions provided by modules.
foreach ($actions_in_code as $callback => $array) {
@@ -272,10 +244,10 @@ function actions_synchronize($delete_orphans = FALSE) {
'type' => $array['type'],
'callback' => $callback,
'parameters' => '',
- 'description' => $array['description'],
+ 'label' => $array['label'],
))
->execute();
- watchdog('actions', "Action '%action' added.", array('%action' => filter_xss_admin($array['description'])));
+ watchdog('actions', "Action '%action' added.", array('%action' => filter_xss_admin($array['label'])));
}
}
}
@@ -285,10 +257,10 @@ function actions_synchronize($delete_orphans = FALSE) {
$orphaned = array_keys($actions_in_db);
if ($delete_orphans) {
- $actions = db_query('SELECT aid, description FROM {actions} WHERE callback IN (:orphaned)', array(':orphaned' => $orphaned))->fetchAll();
+ $actions = db_query('SELECT aid, label FROM {actions} WHERE callback IN (:orphaned)', array(':orphaned' => $orphaned))->fetchAll();
foreach ($actions as $action) {
actions_delete($action->aid);
- watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => filter_xss_admin($action->description)));
+ watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => filter_xss_admin($action->label)));
}
}
else {
@@ -301,7 +273,7 @@ function actions_synchronize($delete_orphans = FALSE) {
}
/**
- * Save an action and its associated user-supplied parameter values to the database.
+ * Saves an action and its user-supplied parameter values to the database.
*
* @param $function
* The name of the function to be called when this action is performed.
@@ -311,16 +283,15 @@ function actions_synchronize($delete_orphans = FALSE) {
* @param $params
* An associative array with parameter names as keys and parameter values as
* values.
- * @param $desc
- * A user-supplied description of this particular action, e.g., 'Send e-mail
+ * @param $label
+ * A user-supplied label of this particular action, e.g., 'Send e-mail
* to Jim'.
* @param $aid
* The ID of this action. If omitted, a new action is created.
- *
* @return
* The ID of the action.
*/
-function actions_save($function, $type, $params, $desc, $aid = NULL) {
+function actions_save($function, $type, $params, $label, $aid = NULL) {
// aid is the callback for singleton actions so we need to keep a separate
// table for numeric aids.
if (!$aid) {
@@ -333,29 +304,28 @@ function actions_save($function, $type, $params, $desc, $aid = NULL) {
'callback' => $function,
'type' => $type,
'parameters' => serialize($params),
- 'description' => $desc,
+ 'label' => $label,
))
->execute();
- watchdog('actions', 'Action %action saved.', array('%action' => $desc));
+ watchdog('actions', 'Action %action saved.', array('%action' => $label));
return $aid;
}
/**
- * Retrieve a single action from the database.
+ * Retrieves a single action from the database.
*
* @param $aid
* The ID of the action to retrieve.
- *
* @return
* The appropriate action row from the database as an object.
*/
function actions_load($aid) {
- return db_query("SELECT aid, type, callback, parameters, description FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetchObject();
+ return db_query("SELECT aid, type, callback, parameters, label FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetchObject();
}
/**
- * Delete a single action from the database.
+ * Deletes a single action from the database.
*
* @param $aid
* The ID of the action to delete.
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index 690c5db03..9aaba268f 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -2296,49 +2296,21 @@ function vancode2int($c = '00') {
}
/**
- * Implement hook_hook_info().
- */
-function comment_hook_info() {
- return array(
- 'comment' => array(
- 'comment' => array(
- 'insert' => array(
- 'runs when' => t('After saving a new comment'),
- ),
- 'update' => array(
- 'runs when' => t('After saving an updated comment'),
- ),
- 'delete' => array(
- 'runs when' => t('After deleting a comment')
- ),
- 'view' => array(
- 'runs when' => t('When a comment is being viewed by an authenticated user')
- ),
- ),
- ),
- );
-}
-
-/**
* Implement hook_action_info().
*/
function comment_action_info() {
return array(
'comment_unpublish_action' => array(
- 'description' => t('Unpublish comment'),
+ 'label' => t('Unpublish comment'),
'type' => 'comment',
'configurable' => FALSE,
- 'hooks' => array(
- 'comment' => array('insert', 'update'),
- )
+ 'triggers' => array('comment_insert', 'comment_update'),
),
'comment_unpublish_by_keyword_action' => array(
- 'description' => t('Unpublish comment containing keyword(s)'),
+ 'label' => t('Unpublish comment containing keyword(s)'),
'type' => 'comment',
'configurable' => TRUE,
- 'hooks' => array(
- 'comment' => array('insert', 'update'),
- )
+ 'triggers' => array('comment_insert', 'comment_update'),
)
);
}
diff --git a/modules/node/node.module b/modules/node/node.module
index 46cf0a91c..719e392bc 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -2754,123 +2754,90 @@ function node_forms() {
}
/**
- * Implement hook_hook_info().
- */
-function node_hook_info() {
- return array(
- 'node' => array(
- 'node' => array(
- 'presave' => array(
- 'runs when' => t('When either saving a new post or updating an existing post'),
- ),
- 'insert' => array(
- 'runs when' => t('After saving a new post'),
- ),
- 'update' => array(
- 'runs when' => t('After saving an updated post'),
- ),
- 'delete' => array(
- 'runs when' => t('After deleting a post')
- ),
- 'view' => array(
- 'runs when' => t('When content is viewed by an authenticated user')
- ),
- ),
- ),
- );
-}
-
-/**
* Implement hook_action_info().
*/
function node_action_info() {
return array(
'node_publish_action' => array(
'type' => 'node',
- 'description' => t('Publish post'),
+ 'label' => t('Publish content'),
'configurable' => FALSE,
'behavior' => array('changes_node_property'),
- 'hooks' => array(
- 'node' => array('presave'),
- 'comment' => array('insert', 'update'),
- ),
+ 'triggers' => array('node_presave', 'comment_insert', 'comment_update'),
),
'node_unpublish_action' => array(
'type' => 'node',
- 'description' => t('Unpublish post'),
+ 'label' => t('Unpublish content'),
'configurable' => FALSE,
'behavior' => array('changes_node_property'),
- 'hooks' => array(
- 'node' => array('presave'),
- 'comment' => array('delete', 'insert', 'update'),
+ 'triggers' => array(
+ 'node_presave',
+ 'comment_insert',
+ 'comment_update',
+ 'comment_delete'
),
),
'node_make_sticky_action' => array(
'type' => 'node',
- 'description' => t('Make post sticky'),
+ 'label' => t('Make content sticky'),
'configurable' => FALSE,
'behavior' => array('changes_node_property'),
- 'hooks' => array(
- 'node' => array('presave'),
- 'comment' => array('insert', 'update'),
- ),
+ 'triggers' => array('node_presave', 'comment_insert', 'comment_update'),
),
'node_make_unsticky_action' => array(
'type' => 'node',
- 'description' => t('Make post unsticky'),
+ 'label' => t('Make content unsticky'),
'configurable' => FALSE,
'behavior' => array('changes_node_property'),
- 'hooks' => array(
- 'node' => array('presave'),
- 'comment' => array('delete', 'insert', 'update'),
+ 'triggers' => array(
+ 'node_presave',
+ 'comment_insert',
+ 'comment_update',
+ 'comment_delete'
),
),
'node_promote_action' => array(
'type' => 'node',
- 'description' => t('Promote post to front page'),
+ 'label' => t('Promote content to front page'),
'configurable' => FALSE,
'behavior' => array('changes_node_property'),
- 'hooks' => array(
- 'node' => array('presave'),
- 'comment' => array('insert', 'update'),
- ),
+ 'triggers' => array('node_presave', 'comment_insert', 'comment_update'),
),
'node_unpromote_action' => array(
'type' => 'node',
- 'description' => t('Remove post from front page'),
+ 'label' => t('Remove content from front page'),
'configurable' => FALSE,
'behavior' => array('changes_node_property'),
- 'hooks' => array(
- 'node' => array('presave'),
- 'comment' => array('delete', 'insert', 'update'),
+ 'triggers' => array(
+ 'node_presave',
+ 'comment_insert',
+ 'comment_update',
+ 'comment_delete'
),
),
'node_assign_owner_action' => array(
'type' => 'node',
- 'description' => t('Change the author of a post'),
+ 'label' => t('Change the author of content'),
'configurable' => TRUE,
'behavior' => array('changes_node_property'),
- 'hooks' => array(
- 'any' => TRUE,
- 'node' => array('presave'),
- 'comment' => array('delete', 'insert', 'update'),
+ 'triggers' => array(
+ 'node_presave',
+ 'comment_insert',
+ 'comment_update',
+ 'comment_delete',
),
),
'node_save_action' => array(
'type' => 'node',
- 'description' => t('Save post'),
+ 'label' => t('Save content'),
'configurable' => FALSE,
- 'hooks' => array(
- 'comment' => array('delete', 'insert', 'update'),
- ),
+ 'triggers' => array('comment_delete', 'comment_insert', 'comment_update'),
),
'node_unpublish_by_keyword_action' => array(
'type' => 'node',
- 'description' => t('Unpublish post containing keyword(s)'),
+ 'label' => t('Unpublish content containing keyword(s)'),
'configurable' => TRUE,
- 'hooks' => array(
- 'node' => array('presave', 'insert', 'update'),
- ),
+ 'triggers' => array('node_presave', 'node_insert', 'node_update'),
),
);
}
diff --git a/modules/simpletest/tests/actions.test b/modules/simpletest/tests/actions.test
index a36684047..12a97cc5f 100644
--- a/modules/simpletest/tests/actions.test
+++ b/modules/simpletest/tests/actions.test
@@ -26,27 +26,27 @@ class ActionsConfigurationTestCase extends DrupalWebTestCase {
// Make a POST request to the individual action configuration page.
$edit = array();
- $action_description = $this->randomName();
- $edit['actions_description'] = $action_description;
+ $action_label = $this->randomName();
+ $edit['actions_label'] = $action_label;
$edit['url'] = 'admin';
$this->drupalPost('admin/config/system/actions/configure/' . md5('system_goto_action'), $edit, t('Save'));
// Make sure that the new complex action was saved properly.
$this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully saved the complex action."));
- $this->assertText($action_description, t("Make sure the action description appears on the configuration page after we've saved the complex action."));
+ $this->assertText($action_label, t("Make sure the action label appears on the configuration page after we've saved the complex action."));
// Make another POST request to the action edit page.
$this->clickLink(t('configure'));
$edit = array();
- $new_action_description = $this->randomName();
- $edit['actions_description'] = $new_action_description;
+ $new_action_label = $this->randomName();
+ $edit['actions_label'] = $new_action_label;
$edit['url'] = 'admin';
$this->drupalPost('admin/config/system/actions/configure/1', $edit, t('Save'));
// Make sure that the action updated properly.
$this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully updated the complex action."));
- $this->assertNoText($action_description, t("Make sure the old action description does NOT appear on the configuration page after we've updated the complex action."));
- $this->assertText($new_action_description, t("Make sure the action description appears on the configuration page after we've updated the complex action."));
+ $this->assertNoText($action_label, t("Make sure the old action label does NOT appear on the configuration page after we've updated the complex action."));
+ $this->assertText($new_action_label, t("Make sure the action label appears on the configuration page after we've updated the complex action."));
// Make sure that deletions work properly.
$this->clickLink(t('delete'));
@@ -54,9 +54,9 @@ class ActionsConfigurationTestCase extends DrupalWebTestCase {
$this->drupalPost('admin/config/system/actions/delete/1', $edit, t('Delete'));
// Make sure that the action was actually deleted.
- $this->assertRaw(t('Action %action was deleted', array('%action' => $new_action_description)), t('Make sure that we get a delete confirmation message.'));
+ $this->assertRaw(t('Action %action was deleted', array('%action' => $new_action_label)), t('Make sure that we get a delete confirmation message.'));
$this->drupalGet('admin/config/system/actions/manage');
- $this->assertNoText($new_action_description, t("Make sure the action description does not appear on the overview page after we've deleted the action."));
+ $this->assertNoText($new_action_label, t("Make sure the action label does not appear on the overview page after we've deleted the action."));
$exists = db_query('SELECT aid FROM {actions} WHERE callback = :callback', array(':callback' => 'drupal_goto_action'))->fetchField();
$this->assertFalse($exists, t('Make sure the action is gone from the database after being deleted.'));
}
diff --git a/modules/simpletest/tests/actions_loop_test.module b/modules/simpletest/tests/actions_loop_test.module
index 58b47fd8b..a31415891 100644
--- a/modules/simpletest/tests/actions_loop_test.module
+++ b/modules/simpletest/tests/actions_loop_test.module
@@ -2,15 +2,13 @@
// $Id$
/**
- * Implement hook_hook_info().
+ * Implement hook_trigger_info().
*/
-function actions_loop_test_hook_info() {
+function actions_loop_test_trigger_info() {
return array(
'actions_loop_test' => array(
'watchdog' => array(
- 'run' => array(
- 'runs when' => t('When a message is logged'),
- ),
+ 'label' => t('When a message is logged'),
),
),
);
@@ -26,13 +24,11 @@ function actions_loop_test_watchdog(array $log_entry) {
}
// Get all the action ids assigned to the trigger on the watchdog hook's
// "run" event.
- $aids = _trigger_get_hook_aids('watchdog', 'run');
+ $aids = trigger_get_assigned_actions('watchdog');
// We can pass in any applicable information in $context. There isn't much in
- // this case, but we'll pass in the hook name and the operation name as the
- // bare minimum.
+ // this case, but we'll pass in the hook name as the bare minimum.
$context = array(
'hook' => 'watchdog',
- 'op' => 'run',
);
// Fire the actions on the associated object ($log_entry) and the context
// variable.
@@ -54,12 +50,10 @@ function actions_loop_test_init() {
function actions_loop_test_action_info() {
return array(
'actions_loop_test_log' => array(
- 'description' => t('Write a message to the log.'),
+ 'label' => t('Write a message to the log.'),
'type' => 'system',
'configurable' => FALSE,
- 'hooks' => array(
- 'any' => TRUE,
- )
+ 'triggers' => array('any'),
),
);
}
diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc
index d7c380f9d..48806cdb4 100644
--- a/modules/system/system.admin.inc
+++ b/modules/system/system.admin.inc
@@ -2296,3 +2296,276 @@ function theme_system_themes_form($form) {
$output .= drupal_render_children($form);
return $output;
}
+
+/**
+ * Menu callback; Displays an overview of available and configured actions.
+ */
+function system_actions_manage() {
+ actions_synchronize();
+ $actions = actions_list();
+ $actions_map = actions_actions_map($actions);
+ $options = array(t('Choose an advanced action'));
+ $unconfigurable = array();
+
+ foreach ($actions_map as $key => $array) {
+ if ($array['configurable']) {
+ $options[$key] = $array['label'] . '...';
+ }
+ else {
+ $unconfigurable[] = $array;
+ }
+ }
+
+ $row = array();
+ $instances_present = db_query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchField();
+ $header = array(
+ array('data' => t('Action type'), 'field' => 'type'),
+ array('data' => t('Label'), 'field' => 'label'),
+ array('data' => $instances_present ? t('Operations') : '', 'colspan' => '2')
+ );
+ $query = db_select('actions')->extend('PagerDefault')->extend('TableSort');
+ $result = $query
+ ->fields('actions')
+ ->limit(50)
+ ->orderByHeader($header)
+ ->execute();
+
+ foreach ($result as $action) {
+ $row[] = array(
+ array('data' => $action->type),
+ array('data' => $action->label),
+ array('data' => $action->parameters ? l(t('configure'), "admin/config/system/actions/configure/$action->aid") : ''),
+ array('data' => $action->parameters ? l(t('delete'), "admin/config/system/actions/delete/$action->aid") : '')
+ );
+ }
+
+ if ($row) {
+ $pager = theme('pager', NULL);
+ if (!empty($pager)) {
+ $row[] = array(array('data' => $pager, 'colspan' => '3'));
+ }
+ $build['system_actions_header'] = array('#markup' => '<h3>' . t('Actions available to Drupal:') . '</h3>');
+ $build['system_actions_table'] = array('#markup' => theme('table', $header, $row));
+ }
+
+ if ($actions_map) {
+ $build['system_actions_manage_form'] = drupal_get_form('system_actions_manage_form', $options);
+ }
+
+ return $build;
+}
+
+/**
+ * Define the form for the actions overview page.
+ *
+ * @param $form_state
+ * An associative array containing the current state of the form; not used.
+ * @param $options
+ * An array of configurable actions.
+ * @return
+ * Form definition.
+ *
+ * @ingroup forms
+ * @see system_actions_manage_form_submit()
+ */
+function system_actions_manage_form($form, &$form_state, $options = array()) {
+ $form['parent'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Make a new advanced action available'),
+ '#prefix' => '<div class="container-inline">',
+ '#suffix' => '</div>',
+ );
+ $form['parent']['action'] = array(
+ '#type' => 'select',
+ '#default_value' => '',
+ '#options' => $options,
+ '#description' => '',
+ );
+ $form['parent']['buttons']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Create'),
+ );
+ return $form;
+}
+
+/**
+ * Process system_actions_manage form submissions.
+ *
+ * @see system_actions_manage_form()
+ */
+function system_actions_manage_form_submit($form, &$form_state) {
+ if ($form_state['values']['action']) {
+ $form_state['redirect'] = 'admin/config/system/actions/configure/' . $form_state['values']['action'];
+ }
+}
+
+/**
+ * Menu callback; Creates the form for configuration of a single action.
+ *
+ * We provide the "Description" field. The rest of the form is provided by the
+ * action. We then provide the Save button. Because we are combining unknown
+ * form elements with the action configuration form, we use an 'actions_' prefix
+ * on our elements.
+ *
+ * @param $action
+ * md5 hash of an action ID or an integer. If it is an md5 hash, we are
+ * creating a new instance. If it is an integer, we are editing an existing
+ * instance.
+ * @return
+ * A form definition.
+ *
+ * @see system_actions_configure_validate()
+ * @see system_actions_configure_submit()
+ */
+function system_actions_configure($form, &$form_state, $action = NULL) {
+ if ($action === NULL) {
+ drupal_goto('admin/config/system/actions');
+ }
+
+ $actions_map = actions_actions_map(actions_list());
+ $edit = array();
+
+ // Numeric action denotes saved instance of a configurable action.
+ if (is_numeric($action)) {
+ $aid = $action;
+ // Load stored parameter values from database.
+ $data = db_query("SELECT * FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetch();
+ $edit['actions_label'] = $data->label;
+ $edit['actions_type'] = $data->type;
+ $function = $data->callback;
+ $action = md5($data->callback);
+ $params = unserialize($data->parameters);
+ if ($params) {
+ foreach ($params as $name => $val) {
+ $edit[$name] = $val;
+ }
+ }
+ }
+ // Otherwise, we are creating a new action instance.
+ else {
+ $function = $actions_map[$action]['callback'];
+ $edit['actions_label'] = $actions_map[$action]['label'];
+ $edit['actions_type'] = $actions_map[$action]['type'];
+ }
+
+ $form['actions_label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Label'),
+ '#default_value' => $edit['actions_label'],
+ '#maxlength' => '255',
+ '#description' => t('A unique label for this advanced action. This label will be displayed in the interface of modules that integrate with actions, such as Trigger module.'),
+ '#weight' => -10
+ );
+ $action_form = $function . '_form';
+ $form = array_merge($form, $action_form($edit));
+ $form['actions_type'] = array(
+ '#type' => 'value',
+ '#value' => $edit['actions_type'],
+ );
+ $form['actions_action'] = array(
+ '#type' => 'hidden',
+ '#value' => $action,
+ );
+ // $aid is set when configuring an existing action instance.
+ if (isset($aid)) {
+ $form['actions_aid'] = array(
+ '#type' => 'hidden',
+ '#value' => $aid,
+ );
+ }
+ $form['actions_configured'] = array(
+ '#type' => 'hidden',
+ '#value' => '1',
+ );
+ $form['buttons']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ '#weight' => 13
+ );
+
+ return $form;
+}
+
+/**
+ * Validate system_actions_configure() form submissions.
+ */
+function system_actions_configure_validate($form, &$form_state) {
+ $function = actions_function_lookup($form_state['values']['actions_action']) . '_validate';
+ // Hand off validation to the action.
+ if (function_exists($function)) {
+ $function($form, $form_state);
+ }
+}
+
+/**
+ * Process system_actions_configure() form submissions.
+ */
+function system_actions_configure_submit($form, &$form_state) {
+ $function = actions_function_lookup($form_state['values']['actions_action']);
+ $submit_function = $function . '_submit';
+
+ // Action will return keyed array of values to store.
+ $params = $submit_function($form, $form_state);
+ $aid = isset($form_state['values']['actions_aid']) ? $form_state['values']['actions_aid'] : NULL;
+
+ actions_save($function, $form_state['values']['actions_type'], $params, $form_state['values']['actions_label'], $aid);
+ drupal_set_message(t('The action has been successfully saved.'));
+
+ $form_state['redirect'] = 'admin/config/system/actions/manage';
+}
+
+/**
+ * Create the form for confirmation of deleting an action.
+ *
+ * @see system_actions_delete_form_submit()
+ * @ingroup forms
+ */
+function system_actions_delete_form($form, &$form_state, $action) {
+ $form['aid'] = array(
+ '#type' => 'hidden',
+ '#value' => $action->aid,
+ );
+ return confirm_form($form,
+ t('Are you sure you want to delete the action %action?', array('%action' => $action->label)),
+ 'admin/config/system/actions/manage',
+ t('This cannot be undone.'),
+ t('Delete'),
+ t('Cancel')
+ );
+}
+
+/**
+ * Process system_actions_delete form submissions.
+ *
+ * Post-deletion operations for action deletion.
+ */
+function system_actions_delete_form_submit($form, &$form_state) {
+ $aid = $form_state['values']['aid'];
+ $action = actions_load($aid);
+ actions_delete($aid);
+ $label = check_plain($action->label);
+ watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $label));
+ drupal_set_message(t('Action %action was deleted', array('%action' => $label)));
+ $form_state['redirect'] = 'admin/config/system/actions/manage';
+}
+
+/**
+ * Post-deletion operations for deleting action orphans.
+ *
+ * @param $orphaned
+ * An array of orphaned actions.
+ */
+function system_action_delete_orphans_post($orphaned) {
+ foreach ($orphaned as $callback) {
+ drupal_set_message(t("Deleted orphaned action (%action).", array('%action' => $callback)));
+ }
+}
+
+/**
+ * Remove actions that are in the database but not supported by any enabled module.
+ */
+function system_actions_remove_orphans() {
+ actions_synchronize(TRUE);
+ drupal_goto('admin/config/system/actions/manage');
+}
+
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index 1251187ab..6a79445ac 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -2211,5 +2211,89 @@ function hook_file_mimetype_mapping_alter(&$mapping) {
}
/**
+ * Declares information about actions.
+ *
+ * Any module can define actions, and then call actions_do() to make those
+ * actions happen in response to events. The trigger module provides a user
+ * interface for associating actions with module-defined triggers, and it makes
+ * sure the core triggers fire off actions when their events happen.
+ *
+ * An action consists of two or three parts:
+ * - an action definition (returned by this hook)
+ * - a function which performs the action (which by convention is named
+ * MODULE_description-of-function_action)
+ * - an optional form definition function that defines a configuration form
+ * (which has the name of the action function with '_form' appended to it.)
+ *
+ * The action function takes two to four arguments, which come from the input
+ * arguments to actions_do().
+ *
+ * @return
+ * An associative array of action descriptions. The keys of the array
+ * are the names of the action functions, and each corresponding value
+ * is an associative array with the following key-value pairs:
+ * - 'type': The type of object this action acts upon. Core actions have types
+ * 'node', 'user', 'comment', and 'system'.
+ * - 'label': The human-readable name of the action, which should be passed
+ * through the t() function for translation.
+ * - 'configurable': If FALSE, then the action doesn't require any extra
+ * configuration. If TRUE, then your module must define a form function with
+ * the same name as the action function with '_form' appended (e.g., the
+ * form for 'node_assign_owner_action' is 'node_assign_owner_action_form'.)
+ * This function takes $context as its only parameter, and is paired with
+ * the usual _submit function, and possibly a _validate function.
+ * - 'triggers': An array of the events (that is, hooks) that can trigger this
+ * action. For example: array('node_insert', 'user_update'). You can also
+ * declare support for any trigger by returning array('any') for this value.
+ * - 'behavior': (optional) machine-readable array of behaviors of this
+ * action, used to signal additional actions that may need to be triggered.
+ * Currently recognized behaviors by Trigger module:
+ * - 'changes_node_property': If an action with this behavior is assigned to
+ * a trigger other than 'node_presave', any node save actions also
+ * assigned to this trigger are moved later in the list. If a node save
+ * action is not present, one will be added.
+ */
+function hook_action_info() {
+ return array(
+ 'comment_unpublish_action' => array(
+ 'type' => 'comment',
+ 'label' => t('Unpublish comment'),
+ 'configurable' => FALSE,
+ 'triggers' => array('comment_insert', 'comment_update'),
+ ),
+ 'comment_unpublish_by_keyword_action' => array(
+ 'type' => 'comment',
+ 'label' => t('Unpublish comment containing keyword(s)'),
+ 'configurable' => TRUE,
+ 'triggers' => array('comment_insert', 'comment_update'),
+ ),
+ );
+}
+
+/**
+ * Executes code after an action is deleted.
+ *
+ * @param $aid
+ * The action ID.
+ */
+function hook_actions_delete($aid) {
+ db_delete('actions_assignments')
+ ->condition('aid', $aid)
+ ->execute();
+}
+
+/**
+ * Alters the actions declared by another module.
+ *
+ * Called by actions_list() to allow modules to alter the return values from
+ * implementations of hook_action_info().
+ *
+ * @see trigger_example_action_info_alter().
+ */
+function hook_action_info_alter(&$actions) {
+ $actions['node_unpublish_action']['label'] = t('Unpublish and remove from public view.');
+}
+
+/**
* @} End of "addtogroup hooks".
*/
diff --git a/modules/system/system.install b/modules/system/system.install
index 85423a0a4..87c4de592 100644
--- a/modules/system/system.install
+++ b/modules/system/system.install
@@ -574,8 +574,8 @@ function system_schema() {
'not null' => TRUE,
'size' => 'big',
),
- 'description' => array(
- 'description' => 'Description of the action.',
+ 'label' => array(
+ 'description' => 'Label of the action.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
@@ -2493,6 +2493,15 @@ function system_update_7037() {
}
/**
+ * Rename action description to label.
+ */
+function system_update_7038() {
+ $ret = array();
+ db_change_field($ret, 'actions', 'description', 'label', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '0'));
+ return $ret;
+}
+
+/**
* @} End of "defgroup updates-6.x-to-7.x"
* The next series of updates should start at 8000.
*/
diff --git a/modules/system/system.module b/modules/system/system.module
index 867ec7213..ab3c966b5 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -2403,336 +2403,38 @@ function system_cron() {
}
/**
- * Implement hook_hook_info().
- */
-function system_hook_info() {
- return array(
- 'system' => array(
- 'cron' => array(
- 'run' => array(
- 'runs when' => t('When cron runs'),
- ),
- ),
- ),
- );
-}
-
-/**
* Implement hook_action_info().
*/
function system_action_info() {
return array(
'system_message_action' => array(
'type' => 'system',
- 'description' => t('Display a message to the user'),
+ 'label' => t('Display a message to the user'),
'configurable' => TRUE,
- 'hooks' => array(
- 'node' => array('view', 'insert', 'update', 'delete'),
- 'comment' => array('view', 'insert', 'update', 'delete'),
- 'user' => array('view', 'insert', 'update', 'delete', 'login'),
- 'taxonomy' => array('insert', 'update', 'delete'),
- ),
+ 'triggers' => array('any'),
),
'system_send_email_action' => array(
- 'description' => t('Send e-mail'),
'type' => 'system',
+ 'label' => t('Send e-mail'),
'configurable' => TRUE,
- 'hooks' => array(
- 'node' => array('view', 'insert', 'update', 'delete'),
- 'comment' => array('view', 'insert', 'update', 'delete'),
- 'user' => array('view', 'insert', 'update', 'delete', 'login'),
- 'taxonomy' => array('insert', 'update', 'delete'),
- 'cron' => array('run'),
- )
+ 'triggers' => array('any'),
),
'system_block_ip_action' => array(
- 'description' => t('Ban IP address of current user'),
'type' => 'user',
+ 'label' => t('Ban IP address of current user'),
'configurable' => FALSE,
- 'hooks' => array(),
+ 'triggers' => array(),
),
'system_goto_action' => array(
- 'description' => t('Redirect to URL'),
'type' => 'system',
+ 'label' => t('Redirect to URL'),
'configurable' => TRUE,
- 'hooks' => array(
- 'node' => array('view', 'insert', 'update', 'delete'),
- 'comment' => array('view', 'insert', 'update', 'delete'),
- 'user' => array('view', 'insert', 'update', 'delete', 'login'),
- )
- )
- );
-}
-
-/**
- * Menu callback. Display an overview of available and configured actions.
- */
-function system_actions_manage() {
- actions_synchronize();
- $actions = actions_list();
- $actions_map = actions_actions_map($actions);
- $options = array(t('Choose an advanced action'));
- $unconfigurable = array();
-
- foreach ($actions_map as $key => $array) {
- if ($array['configurable']) {
- $options[$key] = $array['description'] . '...';
- }
- else {
- $unconfigurable[] = $array;
- }
- }
-
- $row = array();
- $instances_present = db_query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchField();
- $header = array(
- array('data' => t('Action type'), 'field' => 'type'),
- array('data' => t('Description'), 'field' => 'description'),
- array('data' => $instances_present ? t('Operations') : '', 'colspan' => '2')
- );
- $query = db_select('actions')->extend('PagerDefault')->extend('TableSort');
- $result = $query
- ->fields('actions')
- ->limit(50)
- ->orderByHeader($header)
- ->execute();
-
- foreach ($result as $action) {
- $row[] = array(
- array('data' => $action->type),
- array('data' => $action->description),
- array('data' => $action->parameters ? l(t('configure'), "admin/config/system/actions/configure/$action->aid") : ''),
- array('data' => $action->parameters ? l(t('delete'), "admin/config/system/actions/delete/$action->aid") : '')
- );
- }
-
- if ($row) {
- $pager = theme('pager', NULL);
- if (!empty($pager)) {
- $row[] = array(array('data' => $pager, 'colspan' => '3'));
- }
- $build['system_actions_header'] = array('#markup' => '<h3>' . t('Actions available to Drupal:') . '</h3>');
- $build['system_actions_table'] = array('#markup' => theme('table', $header, $row));
- }
-
- if ($actions_map) {
- $build['system_actions_manage_form'] = drupal_get_form('system_actions_manage_form', $options);
- }
-
- return $build;
-}
-
-/**
- * Define the form for the actions overview page.
- *
- * @see system_actions_manage_form_submit()
- * @ingroup forms
- * @param $form_state
- * An associative array containing the current state of the form; not used.
- * @param $options
- * An array of configurable actions.
- * @return
- * Form definition.
- */
-function system_actions_manage_form($form, $form_state, $options = array()) {
- $form['parent'] = array(
- '#type' => 'fieldset',
- '#title' => t('Make a new advanced action available'),
- '#prefix' => '<div class="container-inline">',
- '#suffix' => '</div>',
- );
- $form['parent']['action'] = array(
- '#type' => 'select',
- '#default_value' => '',
- '#options' => $options,
- '#description' => '',
- );
- $form['parent']['buttons']['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Create'),
- );
- return $form;
-}
-
-/**
- * Process system_actions_manage form submissions.
- */
-function system_actions_manage_form_submit($form, &$form_state) {
- if ($form_state['values']['action']) {
- $form_state['redirect'] = 'admin/config/system/actions/configure/' . $form_state['values']['action'];
- }
-}
-
-/**
- * Menu callback. Create the form for configuration of a single action.
- *
- * We provide the "Description" field. The rest of the form
- * is provided by the action. We then provide the Save button.
- * Because we are combining unknown form elements with the action
- * configuration form, we use actions_ prefix on our elements.
- *
- * @see system_actions_configure_validate()
- * @see system_actions_configure_submit()
- * @param $action
- * md5 hash of action ID or an integer. If it's an md5 hash, we
- * are creating a new instance. If it's an integer, we're editing
- * an existing instance.
- * @return
- * Form definition.
- */
-function system_actions_configure($form, &$form_state, $action = NULL) {
- if ($action === NULL) {
- drupal_goto('admin/config/system/actions');
- }
-
- $actions_map = actions_actions_map(actions_list());
- $edit = array();
-
- // Numeric action denotes saved instance of a configurable action;
- // else we are creating a new action instance.
- if (is_numeric($action)) {
- $aid = $action;
- // Load stored parameter values from database.
- $data = db_query("SELECT * FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetch();
- $edit['actions_description'] = $data->description;
- $edit['actions_type'] = $data->type;
- $function = $data->callback;
- $action = md5($data->callback);
- $params = unserialize($data->parameters);
- if ($params) {
- foreach ($params as $name => $val) {
- $edit[$name] = $val;
- }
- }
- }
- else {
- $function = $actions_map[$action]['callback'];
- $edit['actions_description'] = $actions_map[$action]['description'];
- $edit['actions_type'] = $actions_map[$action]['type'];
- }
-
- $form['actions_description'] = array(
- '#type' => 'textfield',
- '#title' => t('Description'),
- '#default_value' => $edit['actions_description'],
- '#maxlength' => '255',
- '#description' => t('A unique description for this advanced action. This description will be displayed in the interface of modules that integrate with actions, such as Trigger module.'),
- '#weight' => -10
- );
- $action_form = $function . '_form';
- $form = array_merge($form, $action_form($edit));
- $form['actions_type'] = array(
- '#type' => 'value',
- '#value' => $edit['actions_type'],
- );
- $form['actions_action'] = array(
- '#type' => 'hidden',
- '#value' => $action,
- );
- // $aid is set when configuring an existing action instance.
- if (isset($aid)) {
- $form['actions_aid'] = array(
- '#type' => 'hidden',
- '#value' => $aid,
- );
- }
- $form['actions_configured'] = array(
- '#type' => 'hidden',
- '#value' => '1',
- );
- $form['buttons']['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Save'),
- '#weight' => 13
- );
-
- return $form;
-}
-
-/**
- * Validate system_actions_configure form submissions.
- */
-function system_actions_configure_validate($form, $form_state) {
- $function = actions_function_lookup($form_state['values']['actions_action']) . '_validate';
- // Hand off validation to the action.
- if (function_exists($function)) {
- $function($form, $form_state);
- }
-}
-
-/**
- * Process system_actions_configure form submissions.
- */
-function system_actions_configure_submit($form, &$form_state) {
- $function = actions_function_lookup($form_state['values']['actions_action']);
- $submit_function = $function . '_submit';
-
- // Action will return keyed array of values to store.
- $params = $submit_function($form, $form_state);
- $aid = isset($form_state['values']['actions_aid']) ? $form_state['values']['actions_aid'] : NULL;
-
- actions_save($function, $form_state['values']['actions_type'], $params, $form_state['values']['actions_description'], $aid);
- drupal_set_message(t('The action has been successfully saved.'));
-
- $form_state['redirect'] = 'admin/config/system/actions/manage';
-}
-
-/**
- * Create the form for confirmation of deleting an action.
- *
- * @ingroup forms
- * @see system_actions_delete_form_submit()
- */
-function system_actions_delete_form($form, $form_state, $action) {
-
- $form['aid'] = array(
- '#type' => 'hidden',
- '#value' => $action->aid,
- );
- return confirm_form($form,
- t('Are you sure you want to delete the action %action?', array('%action' => $action->description)),
- 'admin/config/system/actions/manage',
- t('This cannot be undone.'),
- t('Delete'), t('Cancel')
+ 'triggers' => array('any'),
+ ),
);
}
/**
- * Process system_actions_delete form submissions.
- *
- * Post-deletion operations for action deletion.
- */
-function system_actions_delete_form_submit($form, &$form_state) {
- $aid = $form_state['values']['aid'];
- $action = actions_load($aid);
- actions_delete($aid);
- $description = check_plain($action->description);
- watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $description));
- drupal_set_message(t('Action %action was deleted', array('%action' => $description)));
- $form_state['redirect'] = 'admin/config/system/actions/manage';
-}
-
-/**
- * Post-deletion operations for deleting action orphans.
- *
- * @param $orphaned
- * An array of orphaned actions.
- */
-function system_action_delete_orphans_post($orphaned) {
- foreach ($orphaned as $callback) {
- drupal_set_message(t("Deleted orphaned action (%action).", array('%action' => $callback)));
- }
-}
-
-/**
- * Remove actions that are in the database but not supported by any enabled module.
- */
-function system_actions_remove_orphans() {
- actions_synchronize(TRUE);
- drupal_goto('admin/config/system/actions/manage');
-}
-
-/**
* Return a form definition so the Send email action can be configured.
*
* @see system_send_email_action_validate()
diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module
index 7bf15935a..ed8aa2c98 100644
--- a/modules/taxonomy/taxonomy.module
+++ b/modules/taxonomy/taxonomy.module
@@ -1703,27 +1703,6 @@ function taxonomy_implode_tags($tags, $vid = NULL) {
}
/**
- * Implement hook_hook_info().
- */
-function taxonomy_hook_info() {
- return array(
- 'taxonomy' => array(
- 'taxonomy' => array(
- 'insert' => array(
- 'runs when' => t('After saving a new term to the database'),
- ),
- 'update' => array(
- 'runs when' => t('After saving an updated term to the database'),
- ),
- 'delete' => array(
- 'runs when' => t('After deleting a term')
- ),
- ),
- ),
- );
-}
-
-/**
* Implement hook_field_info().
*
* Field settings:
diff --git a/modules/trigger/tests/trigger_test.module b/modules/trigger/tests/trigger_test.module
index bb973ba11..4793ad05f 100644
--- a/modules/trigger/tests/trigger_test.module
+++ b/modules/trigger/tests/trigger_test.module
@@ -10,22 +10,64 @@
* Implementation of hook_action_info().
*/
function trigger_test_action_info() {
- // Register an action that can be assigned to the trigger "cron run".
+ // Register an action that can be assigned to the trigger "cron".
return array(
'trigger_test_system_cron_action' => array(
'type' => 'system',
- 'description' => t('Cron test action'),
+ 'label' => t('Cron test action'),
'configurable' => FALSE,
- 'hooks' => array(
- 'cron' => array('run'),
- ),
+ 'triggers' => array('cron'),
),
'trigger_test_system_cron_conf_action' => array(
'type' => 'system',
- 'description' => t('Cron test configurable action'),
+ 'label' => t('Cron test configurable action'),
'configurable' => TRUE,
- 'hooks' => array(
- 'cron' => array('run'),
+ 'triggers' => array('cron'),
+ ),
+ 'trigger_test_generic_action' => array(
+ 'type' => 'system',
+ 'label' => t('Generic test action'),
+ 'configurable' => FALSE,
+ 'triggers' => array(
+ 'taxonomy_term_insert',
+ 'taxonomy_term_update',
+ 'taxonomy_delete',
+ 'comment_insert',
+ 'comment_update',
+ 'comment_delete',
+ 'user_insert',
+ 'user_update',
+ 'user_delete',
+ 'user_login',
+ 'user_logout',
+ 'user_view',
+ ),
+ ),
+ 'trigger_test_generic_any_action' => array(
+ 'type' => 'system',
+ 'label' => t('Generic test action for any trigger'),
+ 'configurable' => FALSE,
+ 'triggers' => array('any'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_trigger_info().
+ */
+function trigger_test_trigger_info() {
+ // Register triggers that this module provides. The first is an additional
+ // node trigger and the second is our own, which should create a new tab
+ // on the trigger assignment page.
+ return array(
+ 'node' => array(
+ 'node_triggertest' => array(
+ 'runs when' => t('A test trigger is fired'),
+ ),
+ ),
+ 'trigger_test' => array(
+ 'trigger_test_triggertest' => array(
+ 'runs when' => t('Another test trigger is fired'),
),
),
);
@@ -74,3 +116,19 @@ function trigger_test_system_cron_conf_action_submit($form, $form_state) {
);
return $params;
}
+
+/**
+ * Action fired during the "taxonomy", "comment", and "user" trigger tests.
+ */
+function trigger_test_generic_action($context) {
+ // Indicate successful execution by setting a persistent variable.
+ variable_set('trigger_test_generic_action', TRUE);
+}
+
+/**
+ * Action fired during the additional trigger tests.
+ */
+function trigger_test_generic_any_action($context) {
+ // Indicate successful execution by setting a persistent variable.
+ variable_set('trigger_test_generic_any_action', TRUE);
+}
diff --git a/modules/trigger/trigger.admin.inc b/modules/trigger/trigger.admin.inc
index 5e4c3f408..9db7a928f 100644
--- a/modules/trigger/trigger.admin.inc
+++ b/modules/trigger/trigger.admin.inc
@@ -7,29 +7,29 @@
*/
/**
- * Build the form that allows users to assign actions to hooks.
+ * Builds the form that allows users to assign actions to triggers.
*
- * @param $type
- * Name of hook.
+ * @param $module_to_display
+ * Which tab of triggers to display. E.g., 'node' for all
+ * node-related triggers.
* @return
* HTML form.
*/
-function trigger_assign($type = NULL) {
+function trigger_assign($module_to_display = NULL) {
// If no type is specified we default to node actions, since they
// are the most common.
- if (!isset($type)) {
+ if (!isset($module_to_display)) {
drupal_goto('admin/structure/trigger/node');
}
$build = array();
- $hooks = module_invoke_all('hook_info');
- foreach ($hooks as $module => $module_hooks) {
- if ($module == $type) {
- foreach ($module_hooks as $hook => $data) {
- foreach ($data as $op => $description) {
- $form_id = 'trigger_' . $hook . '_' . $op . '_assign_form';
- $build[$form_id] = drupal_get_form($form_id, $hook, $op, $description['runs when']);
- }
+ $trigger_info = module_invoke_all('trigger_info');
+ drupal_alter('trigger_info', $trigger_info);
+ foreach ($trigger_info as $module => $hooks) {
+ if ($module == $module_to_display) {
+ foreach ($hooks as $hook => $description) {
+ $form_id = 'trigger_' . $hook . '_assign_form';
+ $build[$form_id] = drupal_get_form($form_id, $module, $hook, $description['label']);
}
}
}
@@ -39,25 +39,27 @@ function trigger_assign($type = NULL) {
/**
* Confirm removal of an assigned action.
*
+ * @param $module
+ * The tab of triggers the user will be directed to after successful
+ * removal of the action, or if the confirmation form is cancelled.
* @param $hook
- * @param $op
* @param $aid
* The action ID.
* @ingroup forms
* @see trigger_unassign_submit()
*/
-function trigger_unassign($form, $form_state, $hook = NULL, $op = NULL, $aid = NULL) {
- if (!($hook && $op && $aid)) {
- drupal_goto('admin/structure/trigger/assign');
+function trigger_unassign($form, $form_state, $module, $hook = NULL, $aid = NULL) {
+ if (!($hook && $aid)) {
+ drupal_goto('admin/structure/trigger');
}
$form['hook'] = array(
'#type' => 'value',
'#value' => $hook,
);
- $form['operation'] = array(
+ $form['module'] = array(
'#type' => 'value',
- '#value' => $op,
+ '#value' => $module,
);
$form['aid'] = array(
'#type' => 'value',
@@ -67,30 +69,31 @@ function trigger_unassign($form, $form_state, $hook = NULL, $op = NULL, $aid = N
$action = actions_function_lookup($aid);
$actions = actions_get_all_actions();
- $destination = 'admin/structure/trigger/' . ($hook == 'node' ? 'node' : $hook);
+ $destination = 'admin/structure/trigger/' . $module;
return confirm_form($form,
- t('Are you sure you want to unassign the action %title?', array('%title' => $actions[$action]['description'])),
+ t('Are you sure you want to unassign the action %title?', array('%title' => $actions[$action]['label'])),
$destination,
t('You can assign it again later if you wish.'),
t('Unassign'), t('Cancel')
);
}
+/**
+ * Submit callback for trigger_unassign() form.
+ */
function trigger_unassign_submit($form, &$form_state) {
$form_values = $form_state['values'];
if ($form_values['confirm'] == 1) {
$aid = actions_function_lookup($form_values['aid']);
db_delete('trigger_assignments')
->condition('hook', $form_values['hook'])
- ->condition('op', $form_values['operation'])
->condition('aid', $aid)
->execute();
$actions = actions_get_all_actions();
- watchdog('actions', 'Action %action has been unassigned.', array('%action' => check_plain($actions[$aid]['description'])));
- drupal_set_message(t('Action %action has been unassigned.', array('%action' => $actions[$aid]['description'])));
- $hook = $form_values['hook'] == 'node' ? 'node' : $form_values['hook'];
- $form_state['redirect'] = 'admin/structure/trigger/' . $hook;
+ watchdog('actions', 'Action %action has been unassigned.', array('%action' => check_plain($actions[$aid]['label'])));
+ drupal_set_message(t('Action %action has been unassigned.', array('%action' => $actions[$aid]['label'])));
+ $form_state['redirect'] = 'admin/structure/trigger/' . $form_values['module'];
}
else {
drupal_goto('admin/structure/trigger');
@@ -98,30 +101,27 @@ function trigger_unassign_submit($form, &$form_state) {
}
/**
- * Create the form definition for assigning an action to a hook-op combination.
+ * Returns the form for assigning an action to a trigger.
*
- * @param $form_state
- * Information about the current form.
+ * @param $module
+ * The name of the trigger group, e.g., 'node'.
* @param $hook
- * The name of the hook, e.g., 'node'.
- * @param $op
- * The name of the hook operation, e.g., 'insert'.
- * @param $description
- * A plain English description of what this hook operation does.
- * @return
+ * The name of the trigger hook, e.g., 'node_insert'.
+ * @param $label
+ * A plain English description of what this trigger does.
*
* @ingoup forms
* @see trigger_assign_form_validate()
* @see trigger_assign_form_submit()
*/
-function trigger_assign_form($form, $form_state, $hook, $op, $description) {
- $form['hook'] = array(
+function trigger_assign_form($form, $form_state, $module, $hook, $label) {
+ $form['module'] = array(
'#type' => 'hidden',
- '#value' => $hook,
+ '#value' => $module,
);
- $form['operation'] = array(
+ $form['hook'] = array(
'#type' => 'hidden',
- '#value' => $op,
+ '#value' => $hook,
);
// All of these forms use the same validate and submit functions.
$form['#validate'][] = 'trigger_assign_form_validate';
@@ -129,53 +129,53 @@ function trigger_assign_form($form, $form_state, $hook, $op, $description) {
$options = array();
$functions = array();
- // Restrict the options list to actions that declare support for this hook-op
- // combination.
+ // Restrict the options list to actions that declare support for this hook.
foreach (actions_list() as $func => $metadata) {
- if (isset($metadata['hooks']['any']) || (isset($metadata['hooks'][$hook]) && is_array($metadata['hooks'][$hook]) && (in_array($op, $metadata['hooks'][$hook])))) {
+ if (in_array('any', $metadata['triggers']) || in_array($hook, $metadata['triggers'])) {
$functions[] = $func;
}
}
foreach (actions_actions_map(actions_get_all_actions()) as $aid => $action) {
if (in_array($action['callback'], $functions)) {
- $options[$action['type']][$aid] = $action['description'];
+ $options[$action['type']][$aid] = $action['label'];
}
}
- $form[$op] = array(
+ $form[$hook] = array(
'#type' => 'fieldset',
- '#title' => t('Trigger: ') . $description,
+ '#title' => t('Trigger: ') . $label,
'#theme' => 'trigger_display'
- );
- // Retrieve actions that are already assigned to this hook-op combination.
- $actions = _trigger_get_hook_actions($hook, $op);
- $form[$op]['assigned']['#type'] = 'value';
- $form[$op]['assigned']['#value'] = array();
- foreach ($actions as $aid => $description) {
- $form[$op]['assigned']['#value'][$aid] = array(
- 'description' => $description,
- 'link' => l(t('unassign'), "admin/structure/trigger/unassign/$hook/$op/" . md5($aid))
+ );
+
+ // Retrieve actions that are already assigned to this hook combination.
+ $actions = trigger_get_assigned_actions($hook);
+ $form[$hook]['assigned']['#type'] = 'value';
+ $form[$hook]['assigned']['#value'] = array();
+ foreach ($actions as $aid => $info) {
+ $form[$hook]['assigned']['#value'][$aid] = array(
+ 'label' => $info['label'],
+ 'link' => l(t('unassign'), "admin/structure/trigger/unassign/$module/$hook/" . md5($aid)),
);
}
- $form[$op]['parent'] = array(
+ $form[$hook]['parent'] = array(
'#prefix' => "<div class='container-inline'>",
'#suffix' => '</div>',
);
// List possible actions that may be assigned.
if (count($options) != 0) {
array_unshift($options, t('Choose an action'));
- $form[$op]['parent']['aid'] = array(
+ $form[$hook]['parent']['aid'] = array(
'#type' => 'select',
'#options' => $options,
);
- $form[$op]['parent']['submit'] = array(
+ $form[$hook]['parent']['submit'] = array(
'#type' => 'submit',
'#value' => t('Assign')
);
}
else {
- $form[$op]['none'] = array(
+ $form[$hook]['none'] = array(
'#markup' => t('No actions available for this trigger. <a href="@link">Add action</a>.', array('@link' => url('admin/config/system/actions/manage')))
);
}
@@ -191,13 +191,12 @@ function trigger_assign_form_validate($form, $form_state) {
$form_values = $form_state['values'];
if (!empty($form_values['aid'])) {
$aid = actions_function_lookup($form_values['aid']);
- $aid_exists = db_query("SELECT aid FROM {trigger_assignments} WHERE hook = :hook AND op = :op AND aid = :aid", array(
+ $aid_exists = db_query("SELECT aid FROM {trigger_assignments} WHERE hook = :hook AND aid = :aid", array(
':hook' => $form_values['hook'],
- ':op' => $form_values['operation'],
':aid' => $aid,
))->fetchField();
if ($aid_exists) {
- form_set_error($form_values['operation'], t('The action you chose is already assigned to that trigger.'));
+ form_set_error($form_values['hook'], t('The action you chose is already assigned to that trigger.'));
}
}
}
@@ -210,54 +209,51 @@ function trigger_assign_form_submit($form, $form_state) {
if (!empty($form_values['aid'])) {
$aid = actions_function_lookup($form_values['aid']);
- $weight = db_query("SELECT MAX(weight) FROM {trigger_assignments} WHERE hook = :hook AND op = :op", array(
+ $weight = db_query("SELECT MAX(weight) FROM {trigger_assignments} WHERE hook = :hook", array(
':hook' => $form_values['hook'],
- ':op' => $form_values['operation'],
))->fetchField();
db_insert('trigger_assignments')
->fields(array(
- 'hook' => $form_values['hook'],
- 'op' => $form_values['operation'],
- 'aid' => $aid,
+ 'hook' => $form_values['hook'],
+ 'aid' => $aid,
'weight' => $weight + 1,
))
->execute();
// If this action changes a node property, we need to save the node
// so the change will persist.
+
$actions = actions_list();
- if (isset($actions[$aid]['behavior']) && in_array('changes_node_property', $actions[$aid]['behavior']) && ($form_values['operation'] != 'presave')) {
- // Delete previous node_save_action if it exists, and re-add a new one at a higher weight.
- $save_post_action_assigned = db_query("SELECT aid FROM {trigger_assignments} WHERE hook = :hook AND op = :op AND aid = :aid", array(
+ if (isset($actions[$aid]['behavior']) && in_array('changes_node_property', $actions[$aid]['behavior']) && ($form_values['hook'] != 'node_presave') && ($form_values['hook'] != 'comment_presave')) {
+ // Delete previous node_save_action if it exists, and re-add a new one at
+ // a higher weight.
+ $save_post_action_assigned = db_query("SELECT aid FROM {trigger_assignments} WHERE hook = :hook AND aid = :aid", array(
':hook' => $form_values['hook'],
- ':op' => $form_values['operation'],
':aid' => 'node_save_action',
))->fetchField();
if ($save_post_action_assigned) {
db_delete('trigger_assignments')
->condition('hook', $form_values['hook'])
- ->condition('op', $form_values['operation'])
->condition('aid', 'node_save_action')
->execute();
}
db_insert('trigger_assignments')
->fields(array(
- 'hook' => $form_values['hook'],
- 'op' => $form_values['operation'],
- 'aid' => 'node_save_action',
+ 'hook' => $form_values['hook'],
+ 'aid' => 'node_save_action',
'weight' => $weight + 2,
))
->execute();
if (!$save_post_action_assigned) {
- drupal_set_message(t('You have added an action that changes a the property of a post. A Save post action has been added so that the property change will be saved.'));
+ drupal_set_message(t("You have added an action that changes a the property of some content. Your 'Save content' action has been moved later in the list so that the property change will be saved."));
}
}
}
}
/**
- * Display actions assigned to this hook-op combination in a table.
+ * Displays actions assigned to this hook in a table.
*
* @param array $element
* The fieldset including all assigned actions.
@@ -269,12 +265,12 @@ function trigger_assign_form_submit($form, $form_state) {
function theme_trigger_display($element) {
$header = array();
$rows = array();
- if (count($element['assigned']['#value'])) {
+ if (isset($element['assigned']) && count($element['assigned']['#value'])) {
$header = array(array('data' => t('Name')), array('data' => t('Operation')));
$rows = array();
foreach ($element['assigned']['#value'] as $aid => $info) {
$rows[] = array(
- $info['description'],
+ $info['label'],
$info['link']
);
}
@@ -289,34 +285,3 @@ function theme_trigger_display($element) {
return $output;
}
-
-/**
- * Get the actions that have already been defined for this
- * type-hook-op combination.
- *
- * @param $type
- * One of 'node', 'user', 'comment'.
- * @param $hook
- * The name of the hook for which actions have been assigned,
- * e.g. 'node'.
- * @param $op
- * The hook operation for which the actions have been assigned,
- * e.g., 'view'.
- * @return
- * An array of action descriptions keyed by action IDs.
- */
-function _trigger_get_hook_actions($hook, $op, $type = NULL) {
- if ($type) {
- return db_query("SELECT h.aid, a.description FROM {trigger_assignments} h LEFT JOIN {actions} a on a.aid = h.aid WHERE a.type = :type AND h.hook = :hook AND h.op = :op ORDER BY h.weight", array(
- ':type' => $type,
- ':hook' => $hook,
- ':op' => $op,
- ))->fetchAllKeyed();
- }
- else {
- return db_query("SELECT h.aid, a.description FROM {trigger_assignments} h LEFT JOIN {actions} a on a.aid = h.aid WHERE h.hook = :hook AND h.op = :op ORDER BY h.weight", array(
- ':hook' => $hook,
- ':op' => $op,
- ))->fetchAllKeyed();
- }
-}
diff --git a/modules/trigger/trigger.api.php b/modules/trigger/trigger.api.php
index cc63d5c1a..0f7834e80 100644
--- a/modules/trigger/trigger.api.php
+++ b/modules/trigger/trigger.api.php
@@ -12,142 +12,50 @@
*/
/**
- * Declare information about one or more Drupal actions.
+ * Declares triggers (events) for users to assign actions to.
*
- * Any module can define any number of Drupal actions. The trigger module is an
- * example of a module that uses actions. An action consists of two or three
- * parts: (1) an action definition (returned by this hook), (2) a function which
- * does the action (which by convention is named module + '_' + description of
- * what the function does + '_action'), and an optional form definition
- * function that defines a configuration form (which has the name of the action
- * with '_form' appended to it.)
+ * This hook is used by the trigger module to create a list of triggers (events)
+ * that users can assign actions to. Your module is responsible for detecting
+ * that the events have occurred, calling trigger_get_assigned_actions() to find
+ * out which actions the user has associated with your trigger, and then calling
+ * actions_do() to fire off the actions.
*
- * @return
- * - An array of action descriptions. Each action description is an associative
- * array, where the key of the item is the action's function, and the
- * following key-value pairs:
- * - 'type': (required) the type is determined by what object the action
- * acts on. Possible choices are node, user, comment, and system. Or
- * whatever your own custom type is. So, for the nodequeue module, the
- * type might be set to 'nodequeue' if the action would be performed on a
- * nodequeue.
- * - 'description': (required) The human-readable name of the action.
- * - 'configurable': (required) If FALSE, then the action doesn't require
- * any extra configuration. If TRUE, then you should define a form
- * function with the same name as the key, but with '_form' appended to
- * it (i.e., the form for 'node_assign_owner_action' is
- * 'node_assign_owner_action_form'.)
- * This function will take the $context as the only parameter, and is
- * paired with the usual _submit function, and possibly a _validate
- * function.
- * - 'hooks': (required) An array of all of the operations this action is
- * appropriate for, keyed by hook name. The trigger module uses this to
- * filter out inappropriate actions when presenting the interface for
- * assigning actions to events. If you are writing actions in your own
- * modules and you simply want to declare support for all possible hooks,
- * you can set 'hooks' => array('any' => TRUE). Common hooks are 'user',
- * 'node', 'comment', or 'taxonomy'. Any hook that has been described
- * to Drupal in hook_hook_info() will work is a possiblity.
- * - 'behavior': (optional) Human-readable array of behavior descriptions.
- * The only one we have now is 'changes node property'. You will almost
- * certainly never have to return this in your own implementations of this
- * hook.
- *
- * The function that is called when the action is triggered is passed two
- * parameters - an object of the same type as the 'type' value of the
- * hook_action_info array, and a context variable that contains the context
- * under which the action is currently running, sent as an array. For example,
- * the actions module sets the 'hook' and 'op' keys of the context array (so,
- * 'hook' may be 'node' and 'op' may be 'insert').
- */
-function hook_action_info() {
- return array(
- 'comment_unpublish_action' => array(
- 'description' => t('Unpublish comment'),
- 'type' => 'comment',
- 'configurable' => FALSE,
- 'hooks' => array(
- 'comment' => array('insert', 'update'),
- )
- ),
- 'comment_unpublish_by_keyword_action' => array(
- 'description' => t('Unpublish comment containing keyword(s)'),
- 'type' => 'comment',
- 'configurable' => TRUE,
- 'hooks' => array(
- 'comment' => array('insert', 'update'),
- )
- )
- );
-}
-
-/**
- * Execute code after an action is deleted.
- *
- * @param $aid
- * The action ID.
- */
-function hook_actions_delete($aid) {
- db_delete('actions_assignments')
- ->condition('aid', $aid)
- ->execute();
-}
-
-/**
- * Alter the actions declared by another module.
- *
- * Called by actions_list() to allow modules to alter the return
- * values from implementations of hook_action_info().
- *
- * @see trigger_example_action_info_alter().
- */
-function hook_action_info_alter(&$actions) {
- $actions['node_unpublish_action']['description'] = t('Unpublish and remove from public view.');
-}
-
-/**
- * Expose a list of triggers (events) that your module is allowing users to
- * assign actions to.
- *
- * This hook is used by the Triggers API to present information about triggers
- * (or events) that your module allows users to assign actions to.
- *
- * See also hook_action_info().
+ * @see hook_action_info().
*
* @return
- * - A nested array. The outermost key defines the module that the triggers
- * are from. The menu system will use the key to look at the .info file of
- * the module and make a local task (a tab) in the trigger UI.
- * - The next key defines the hook being described.
- * - Inside of that array are a list of arrays keyed by hook operation.
- * - Each of those arrays have a key of 'runs when' and a value which is
- * an English description of the hook.
- *
- * For example, the node_hook_info implementation has 'node' as the outermost
- * key, as that's the module it's in. Next it has 'node' as the next key,
- * as hook_node() is what applies to changes in nodes. Finally the keys
- * after that are the various operations for hook_node() that the node module
- * is exposing as triggers.
+ * A nested associative array.
+ * - The outermost key is the name of the module that is defining the triggers.
+ * This will be used to create a local task (tab) in the trigger module's
+ * user interface. A contrib module may supply a trigger for a core module by
+ * giving the core module's name as the key. For example, you could use the
+ * 'node' key to add a node-related trigger.
+ * - Within each module, each individual trigger is keyed by a hook name
+ * describing the particular trigger (this is not visible to the user, but
+ * can be used by your module for identification).
+ * - Each trigger is described by an associative array. Currently, the only
+ * key-value pair is 'label', which contains a translated human-readable
+ * description of the triggering event.
+ * For example, the trigger set for the 'node' module has 'node' as the
+ * outermost key and defines triggers for 'node_insert', 'node_update',
+ * 'node_delete' etc. that fire when a node is saved, updated, etc.
*/
-function hook_hook_info() {
+function hook_trigger_info() {
return array(
'node' => array(
- 'node' => array(
- 'presave' => array(
- 'runs when' => t('When either saving a new post or updating an existing post'),
- ),
- 'insert' => array(
- 'runs when' => t('After saving a new post'),
- ),
- 'update' => array(
- 'runs when' => t('After saving an updated post'),
- ),
- 'delete' => array(
- 'runs when' => t('After deleting a post')
- ),
- 'view' => array(
- 'runs when' => t('When content is viewed by an authenticated user')
- ),
+ 'node_presave' => array(
+ 'label' => t('When either saving new content or updating existing content'),
+ ),
+ 'node_insert' => array(
+ 'label' => t('After saving new content'),
+ ),
+ 'node_update' => array(
+ 'label' => t('After saving updated content'),
+ ),
+ 'node_delete' => array(
+ 'label' => t('After deleting content'),
+ ),
+ 'node_view' => array(
+ 'label' => t('When content is viewed by an authenticated user'),
),
),
);
diff --git a/modules/trigger/trigger.install b/modules/trigger/trigger.install
index 2e014d071..b8553e48c 100644
--- a/modules/trigger/trigger.install
+++ b/modules/trigger/trigger.install
@@ -26,14 +26,7 @@ function trigger_schema() {
'length' => 32,
'not null' => TRUE,
'default' => '',
- 'description' => 'Primary Key: The name of the internal Drupal hook upon which an action is firing; for example, node.',
- ),
- 'op' => array(
- 'type' => 'varchar',
- 'length' => 32,
- 'not null' => TRUE,
- 'default' => '',
- 'description' => 'Primary Key: The specific operation of the hook upon which an action is firing: for example, presave.',
+ 'description' => 'Primary Key: The name of the internal Drupal hook; for example, node_insert.',
),
'aid' => array(
'type' => 'varchar',
@@ -49,7 +42,7 @@ function trigger_schema() {
'description' => 'The weight of the trigger assignment in relation to other triggers.',
),
),
- 'primary key' => array('hook', 'op', 'aid'),
+ 'primary key' => array('hook', 'aid'),
'foreign keys' => array(
'aid' => array('actions' => 'aid'),
),
@@ -57,4 +50,17 @@ function trigger_schema() {
return $schema;
}
+/**
+ * Adds operation names to the hook names and drops the "op" field.
+ */
+function trigger_update_7000() {
+ $ret = array();
+ $result = db_query("SELECT hook, op, aid FROM {trigger_assignments} WHERE op <> ''");
+ while ($row = db_fetch_object($result)) {
+ $ret[] = update_sql("UPDATE {trigger_assignments} SET hook = '%s' WHERE hook = '%s' AND op = '%s' AND aid = '%s'", $row->hook . '_' . $row->op, $row->hook, $row->op, $row->aid);
+ }
+ $ret[] = update_sql("ALTER TABLE {trigger_assignments} DROP op");
+
+ return $ret;
+}
diff --git a/modules/trigger/trigger.module b/modules/trigger/trigger.module
index 281400fe5..d889007e4 100644
--- a/modules/trigger/trigger.module
+++ b/modules/trigger/trigger.module
@@ -11,13 +11,13 @@
* Implement hook_help().
*/
function trigger_help($path, $arg) {
- $explanation = '<p>' . t('Triggers are system events, such as when new content is added or when a user logs in. Trigger module combines these triggers with actions (functional tasks), such as unpublishing content or e-mailing an administrator. The <a href="@url">Actions settings page</a> contains a list of existing actions and provides the ability to create and configure additional actions.', array('@url' => url('admin/config/system/actions'))) . '</p>';
+ $explanation = '<p>' . t('Triggers are system events, such as when new content is added or when a user logs in. The trigger module associates these triggers with actions (functional tasks), such as unpublishing content or e-mailing an administrator. The <a href="@url">Actions settings page</a> contains a list of existing actions and provides the ability to create and configure additional actions.', array('@url' => url('admin/config/system/actions'))) . '</p>';
switch ($path) {
case 'admin/structure/trigger/comment':
return $explanation . '<p>' . t('Below you can assign actions to run when certain comment-related triggers happen. For example, you could promote a post to the front page when a comment is added.') . '</p>';
case 'admin/structure/trigger/node':
- return $explanation . '<p>' . t('Below you can assign actions to run when certain content-related triggers happen. For example, you could send an e-mail to an administrator when a post is created or updated.') . '</p>';
- case 'admin/structure/trigger/cron':
+ return $explanation . '<p>' . t('Below you can assign actions to run when certain content-related triggers happen. For example, you could send an e-mail to an administrator when content is created or updated.') . '</p>';
+ case 'admin/structure/trigger/system':
return $explanation . '<p>' . t('Below you can assign actions to run during each pass of a <a href="@cron">cron maintenance task</a>.', array('@cron' => url('admin/reports/status'))) . '</p>';
case 'admin/structure/trigger/taxonomy':
return $explanation . '<p>' . t('Below you can assign actions to run when certain taxonomy-related triggers happen. For example, you could send an e-mail to an administrator when a term is deleted.') . '</p>';
@@ -37,46 +37,36 @@ function trigger_help($path, $arg) {
function trigger_menu() {
$items['admin/structure/trigger'] = array(
'title' => 'Triggers',
- 'description' => 'Tell Drupal when to execute actions.',
+ 'description' => 'Configure when to execute actions.',
'page callback' => 'trigger_assign',
'access arguments' => array('administer actions'),
'file' => 'trigger.admin.inc',
);
- // Explicitly define the system menu item so we can label it "cron" rather
- // than "system".
- $items['admin/structure/trigger/cron'] = array(
- 'title' => 'Cron',
- 'page callback' => 'trigger_assign',
- 'page arguments' => array('system'),
- 'access arguments' => array('administer actions'),
- 'type' => MENU_LOCAL_TASK,
- 'file' => 'trigger.admin.inc',
- );
-
// We want contributed modules to be able to describe
// their hooks and have actions assignable to them.
- $hooks = module_invoke_all('hook_info');
- foreach ($hooks as $module => $hook) {
- // We've already done system.module.
- if ($module == 'system') {
- continue;
- }
+ $trigger_info = module_invoke_all('trigger_info');
+ drupal_alter('trigger_info', $trigger_info);
+
+ foreach ($trigger_info as $module => $hooks) {
$info = db_select('system')
->fields('system', array('info'))
->condition('name', $module)
+ ->condition('status', 1)
->execute()
->fetchField();
- $info = unserialize($info);
- $nice_name = $info['name'];
- $items["admin/structure/trigger/$module"] = array(
- 'title' => $nice_name,
- 'page callback' => 'trigger_assign',
- 'page arguments' => array($module),
- 'access arguments' => array('administer actions'),
- 'type' => MENU_LOCAL_TASK,
- 'file' => 'trigger.admin.inc',
- );
+ if ($info) {
+ $info = unserialize($info);
+ $nice_name = $info['name'];
+ $items["admin/structure/trigger/$module"] = array(
+ 'title' => $nice_name,
+ 'page callback' => 'trigger_assign',
+ 'page arguments' => array($module),
+ 'access arguments' => array('administer actions'),
+ 'type' => MENU_LOCAL_TASK,
+ 'file' => 'trigger.admin.inc',
+ );
+ }
}
$items['admin/structure/trigger/unassign'] = array(
'title' => 'Unassign',
@@ -92,21 +82,96 @@ function trigger_menu() {
}
/**
- * Get the aids of actions to be executed for a hook-op combination.
+ * Implement hook_trigger_info().
+ *
+ * Defines all the triggers that this module implements triggers for.
+ */
+function trigger_trigger_info() {
+ return array(
+ 'node' => array(
+ 'node_presave' => array(
+ 'label' => t('When either saving new content or updating existing content'),
+ ),
+ 'node_insert' => array(
+ 'label' => t('After saving new content'),
+ ),
+ 'node_update' => array(
+ 'label' => t('After saving updated content'),
+ ),
+ 'node_delete' => array(
+ 'label' => t('After deleting content'),
+ ),
+ 'node_view' => array(
+ 'label' => t('When content is viewed by an authenticated user'),
+ ),
+ ),
+ 'comment' => array(
+ 'comment_insert' => array(
+ 'label' => t('After saving a new comment'),
+ ),
+ 'comment_update' => array(
+ 'label' => t('After saving an updated comment'),
+ ),
+ 'comment_delete' => array(
+ 'label' => t('After deleting a comment'),
+ ),
+ 'comment_view' => array(
+ 'label' => t('When a comment is being viewed by an authenticated user'),
+ ),
+ ),
+ 'taxonomy' => array(
+ 'taxonomy_term_insert' => array(
+ 'label' => t('After saving a new term to the database'),
+ ),
+ 'taxonomy_term_update' => array(
+ 'label' => t('After saving an updated term to the database'),
+ ),
+ 'taxonomy_term_delete' => array(
+ 'label' => t('After deleting a term'),
+ ),
+ ),
+ 'system' => array(
+ 'cron' => array(
+ 'label' => t('When cron runs'),
+ ),
+ ),
+ 'user' => array(
+ 'user_insert' => array(
+ 'label' => t('After a user account has been created'),
+ ),
+ 'user_update' => array(
+ 'label' => t("After a user's profile has been updated"),
+ ),
+ 'user_delete' => array(
+ 'label' => t('After a user has been deleted'),
+ ),
+ 'user_login' => array(
+ 'label' => t('After a user has logged in'),
+ ),
+ 'user_logout' => array(
+ 'label' => t('After a user has logged out'),
+ ),
+ 'user_view' => array(
+ 'label' => t("When a user's profile is being viewed"),
+ ),
+ ),
+ );
+ }
+
+/**
+ * Gets the action IDs of actions to be executed for a hook.
*
* @param $hook
* The name of the hook being fired.
- * @param $op
- * The name of the operation being executed. Defaults to an empty string
- * because some hooks (e.g., hook_cron()) do not have operations.
* @return
- * An array of action IDs.
+ * An array whose keys are action IDs that the user has associated with
+ * this trigger, and whose values are arrays containing the action type and
+ * label.
*/
-function _trigger_get_hook_aids($hook, $op = '') {
- return db_query("SELECT ta.aid, a.type FROM {trigger_assignments} ta LEFT JOIN {actions} a ON ta.aid = a.aid WHERE ta.hook = :hook AND ta.op = :op ORDER BY ta.weight", array(
+function trigger_get_assigned_actions($hook) {
+ return db_query("SELECT ta.aid, a.type, a.label FROM {trigger_assignments} ta LEFT JOIN {actions} a ON ta.aid = a.aid WHERE ta.hook = :hook ORDER BY ta.weight", array(
':hook' => $hook,
- ':op' => $op,
- ))->fetchAllKeyed();
+ ))->fetchAllAssoc( 'aid', PDO::FETCH_ASSOC);
}
/**
@@ -122,16 +187,16 @@ function trigger_theme() {
}
/**
- * Implement hook_forms(). We reuse code by using the
- * same assignment form definition for each node-op combination.
+ * Implement hook_forms().
+ *
+ * We re-use code by using the same assignment form definition for each hook.
*/
function trigger_forms() {
- $hooks = module_invoke_all('hook_info');
- foreach ($hooks as $module => $info) {
- foreach ($hooks[$module] as $hook => $ops) {
- foreach ($ops as $op => $description) {
- $forms['trigger_' . $hook . '_' . $op . '_assign_form'] = array('callback' => 'trigger_assign_form');
- }
+ $trigger_info = _trigger_get_all_info();
+ $forms = array();
+ foreach ($trigger_info as $module => $hooks) {
+ foreach ($hooks as $hook => $description) {
+ $forms['trigger_' . $hook . '_assign_form'] = array('callback' => 'trigger_assign_form');
}
}
@@ -139,26 +204,26 @@ function trigger_forms() {
}
/**
- * When an action is called in a context that does not match its type,
- * the object that the action expects must be retrieved. For example, when
- * an action that works on users is called during the node hook, the
- * user object is not available since the node hook doesn't pass it.
- * So here we load the object the action expects.
+ * Loads associated objects for node triggers.
+ *
+ * When an action is called in a context that does not match its type, the
+ * object that the action expects must be retrieved. For example, when an action
+ * that works on users is called during a node hook implementation, the user
+ * object is not available since the node hook call doesn't pass it. So here we
+ * load the object the action expects.
*
* @param $type
* The type of action that is about to be called.
* @param $node
* The node that was passed via the node hook.
+ *
* @return
* The object expected by the action that is about to be called.
*/
function _trigger_normalize_node_context($type, $node) {
+ // Note that comment-type actions are not supported in node contexts,
+ // because we wouldn't know which comment to choose.
switch ($type) {
- // If an action that works on comments is being called in a node context,
- // the action is expecting a comment object. But we do not know which comment
- // to give it. The first? The most recent? All of them? So comment actions
- // in a node context are not supported.
-
// An action that works on users is being called in a node context.
// Load the user object of the node's author.
case 'user':
@@ -167,33 +232,41 @@ function _trigger_normalize_node_context($type, $node) {
}
/**
- * Simple wrapper function to make user hooks work with new entry points.
+ * Calls action functions for node triggers.
*
- * @TODO: Take advantage of the new API and reorganise/remove this function.
+ * @param $node
+ * Node object.
+ * @param $op
+ * Operation to trigger.
+ * @param $a3
+ * Additional argument to action function.
+ * @param $a4
+ * Additional argument to action function.
*/
-function _trigger_node($node, $op, $a3 = NULL, $a4 = NULL) {
+function _trigger_node($node, $hook, $a3 = NULL, $a4 = NULL) {
// Keep objects for reuse so that changes actions make to objects can persist.
static $objects;
// Prevent recursion by tracking which operations have already been called.
static $recursion;
- if (isset($recursion[$op])) {
+ if (isset($recursion[$hook])) {
return;
}
- $recursion[$op] = TRUE;
+ $recursion[$hook] = TRUE;
- $aids = _trigger_get_hook_aids('node', $op);
+ $aids = trigger_get_assigned_actions($hook);
if (!$aids) {
return;
}
$context = array(
- 'hook' => 'node',
- 'op' => $op,
+ 'group' => 'node',
+ 'hook' => $hook,
);
// We need to get the expected object if the action's type is not 'node'.
// We keep the object in $objects so we can reuse it if we have multiple actions
// that make changes to an object.
- foreach ($aids as $aid => $type) {
+ foreach ($aids as $aid => $info) {
+ $type = $info['type'];
if ($type != 'node') {
if (!isset($objects[$type])) {
$objects[$type] = _trigger_normalize_node_context($type, $node);
@@ -212,48 +285,51 @@ function _trigger_node($node, $op, $a3 = NULL, $a4 = NULL) {
* Implement hook_node_view().
*/
function trigger_node_view($node, $build_mode) {
- _trigger_node($node, 'view', $build_mode);
+ _trigger_node($node, 'node_view', $build_mode);
}
/**
* Implement hook_node_update().
*/
function trigger_node_update($node) {
- _trigger_node($node, 'update');
+ _trigger_node($node, 'node_update');
}
/**
* Implement hook_node_presave().
*/
function trigger_node_presave($node) {
- _trigger_node($node, 'presave');
+ _trigger_node($node, 'node_presave');
}
/**
* Implement hook_node_insert().
*/
function trigger_node_insert($node) {
- _trigger_node($node, 'insert');
+ _trigger_node($node, 'node_insert');
}
/**
* Implement hook_node_delete().
*/
function trigger_node_delete($node) {
- _trigger_node($node, 'delete');
+ _trigger_node($node, 'node_delete');
}
/**
- * When an action is called in a context that does not match its type,
- * the object that the action expects must be retrieved. For example, when
- * an action that works on nodes is called during the comment hook, the
- * node object is not available since the comment hook doesn't pass it.
- * So here we load the object the action expects.
+ * Loads associated objects for comment triggers.
+ *
+ * When an action is called in a context that does not match its type, the
+ * object that the action expects must be retrieved. For example, when an action
+ * that works on nodes is called during the comment hook, the node object is not
+ * available since the comment hook doesn't pass it. So here we load the object
+ * the action expects.
*
* @param $type
* The type of action that is about to be called.
* @param $comment
* The comment that was passed via the comment hook.
+ *
* @return
* The object expected by the action that is about to be called.
*/
@@ -273,51 +349,51 @@ function _trigger_normalize_comment_context($type, $comment) {
* Implement hook_comment_insert().
*/
function trigger_comment_insert($comment) {
- _trigger_comment($comment, 'insert');
+ _trigger_comment($comment, 'comment_insert');
}
/**
* Implement hook_comment_update().
*/
function trigger_comment_update($comment) {
- _trigger_comment($comment, 'update');
+ _trigger_comment($comment, 'comment_update');
}
/**
* Implement hook_comment_delete().
*/
function trigger_comment_delete($comment) {
- _trigger_comment($comment, 'delete');
+ _trigger_comment($comment, 'comment_delete');
}
/**
* Implement hook_comment_view().
*/
function trigger_comment_view($comment) {
- _trigger_comment($comment, 'view');
+ _trigger_comment($comment, 'comment_view');
}
/**
- * Helper function for implementations of hook_comment_op().
+ * Calls action functions for comment triggers.
*
* @param $a1
- * Argument, which will be passed to the action.
- * It can be a array of form values or a comment.
+ * Comment object or array of form values.
* @param $op
- * What kind of action is being performed.
+ * Operation to trigger.
*/
-function _trigger_comment($a1, $op) {
+function _trigger_comment($a1, $hook) {
// Keep objects for reuse so that changes actions make to objects can persist.
static $objects;
- $aids = _trigger_get_hook_aids('comment', $op);
+ $aids = trigger_get_assigned_actions($hook);
$context = array(
- 'hook' => 'comment',
- 'op' => $op,
+ 'group' => 'comment',
+ 'hook' => $hook,
);
// We need to get the expected object if the action's type is not 'comment'.
- // We keep the object in $objects so we can reuse it if we have multiple actions
- // that make changes to an object.
- foreach ($aids as $aid => $type) {
+ // We keep the object in $objects so we can reuse it if we have multiple
+ // actions that make changes to an object.
+ foreach ($aids as $aid => $info) {
+ $type = $info['type'];
if ($type != 'comment') {
if (!isset($objects[$type])) {
$objects[$type] = _trigger_normalize_comment_context($type, $a1);
@@ -338,10 +414,10 @@ function _trigger_comment($a1, $op) {
* Implement hook_cron().
*/
function trigger_cron() {
- $aids = _trigger_get_hook_aids('cron', 'run');
+ $aids = trigger_get_assigned_actions('cron');
$context = array(
+ 'group' => 'cron',
'hook' => 'cron',
- 'op' => 'run',
);
// Cron does not act on any specific object.
$object = NULL;
@@ -349,11 +425,13 @@ function trigger_cron() {
}
/**
- * When an action is called in a context that does not match its type,
- * the object that the action expects must be retrieved. For example, when
- * an action that works on nodes is called during the user hook, the
- * node object is not available since the user hook doesn't pass it.
- * So here we load the object the action expects.
+ * Loads associated objects for user triggers.
+ *
+ * When an action is called in a context that does not match its type, the
+ * object that the action expects must be retrieved. For example, when an action
+ * that works on nodes is called during the user hook, the node object is not
+ * available since the user hook doesn't pass it. So here we load the object the
+ * action expects.
*
* @param $type
* The type of action that is about to be called.
@@ -363,12 +441,9 @@ function trigger_cron() {
* The object expected by the action that is about to be called.
*/
function _trigger_normalize_user_context($type, $account) {
+ // Note that comment-type actions are not supported in user contexts,
+ // because we wouldn't know which comment to choose.
switch ($type) {
- // If an action that works on comments is being called in a user context,
- // the action is expecting a comment object. But we have no way of
- // determining the appropriate comment object to pass. So comment
- // actions in a user context are not supported.
-
// An action that works with nodes is being called in a user context.
// If a single node is being viewed, return the node.
case 'node':
@@ -376,35 +451,36 @@ function _trigger_normalize_user_context($type, $account) {
if ((arg(0) == 'node') && is_numeric(arg(1)) && (arg(2) == NULL)) {
return node_load(array('nid' => arg(1)));
}
+ break;
}
}
/**
- * trigger_user_login
+ * Implement hook_user_login().
*/
function trigger_user_login(&$edit, $account, $category) {
- _trigger_user('login', $edit, $account, $category);
+ _trigger_user('user_login', $edit, $account, $category);
}
/**
* Implement hook_user_logout().
*/
function trigger_user_logout($account) {
- _trigger_user('logout', $edit = NULL, $account);
+ _trigger_user('user_logout', $edit = NULL, $account);
}
/**
* Implement hook_user_insert().
*/
function trigger_user_insert(&$edit, $account, $category) {
- _trigger_user('insert', $edit, $account, $category);
+ _trigger_user('user_insert', $edit, $account, $category);
}
/**
* Implement hook_user_update().
*/
function trigger_user_update(&$edit, $account, $category) {
- _trigger_user('update', $edit, $account, $category);
+ _trigger_user('user_update', $edit, $account, $category);
}
/**
@@ -414,7 +490,7 @@ function trigger_user_cancel($edit, $account, $method) {
switch ($method) {
case 'user_cancel_reassign':
case 'user_cancel_delete':
- _trigger_user('delete', $edit, $account, $method);
+ _trigger_user('user_delete', $edit, $account, $method);
break;
}
}
@@ -423,24 +499,23 @@ function trigger_user_cancel($edit, $account, $method) {
* Implement hook_user_view().
*/
function trigger_user_view($account) {
- _trigger_user('view', $edit = NULL, $account, NULL);
+ _trigger_user('user_view', $edit = NULL, $account, NULL);
}
/**
- * Simple wrapper function to make user hooks work with new entry points.
- *
- * @TODO: Take advantage of the new API and reorganise/remove this function.
+ * Calls action functions for user triggers.
*/
-function _trigger_user($op, &$edit, $account, $category = NULL) {
+function _trigger_user($hook, &$edit, $account, $category = NULL) {
// Keep objects for reuse so that changes actions make to objects can persist.
static $objects;
- $aids = _trigger_get_hook_aids('user', $op);
+ $aids = trigger_get_assigned_actions($hook);
$context = array(
- 'hook' => 'user',
- 'op' => $op,
+ 'group' => 'user',
+ 'hook' => $hook,
'form_values' => &$edit,
);
- foreach ($aids as $aid => $type) {
+ foreach ($aids as $aid => $info) {
+ $type = $info['type'];
if ($type != 'user') {
if (!isset($objects[$type])) {
$objects[$type] = _trigger_normalize_user_context($type, $account);
@@ -455,50 +530,67 @@ function _trigger_user($op, &$edit, $account, $category = NULL) {
}
/**
- * Implement hook_taxonomy().
+ * Calls action functions for taxonomy triggers.
+ *
+ * @param $hook
+ * Hook to trigger actions for taxonomy_term_insert(),
+ * taxonomy_term_update(), and taxonomy_term_delete().
+ * @param $array
+ * Item on which operation is being performed, either a term or
+ * form values.
*/
-function trigger_taxonomy($op, $type, $array) {
- if ($type != 'term') {
- return;
- }
- $aids = _trigger_get_hook_aids('taxonomy', $op);
+function _trigger_taxonomy($hook, $array) {
+ $aids = trigger_get_assigned_actions($hook);
$context = array(
- 'hook' => 'taxonomy',
- 'op' => $op
+ 'group' => 'taxonomy',
+ 'hook' => $hook
);
actions_do(array_keys($aids), (object) $array, $context);
}
/**
- * Often we generate a select field of all actions. This function
- * generates the options for that select.
- *
- * @param $type
- * One of 'node', 'user', 'comment'.
- * @return
- * Array keyed by action ID.
+ * Implement hook_taxonomy_term_insert().
*/
-function trigger_options($type = 'all') {
- $options = array(t('Choose an action'));
- foreach (actions_actions_map(actions_get_all_actions()) as $aid => $action) {
- $options[$action['type']][$aid] = $action['description'];
- }
+function trigger_taxonomy_term_insert($term) {
+ _trigger_taxonomy('taxonomy_term_insert', (array) $term);
+}
- if ($type == 'all') {
- return $options;
- }
- else {
- return $options[$type];
- }
+/**
+ * Implement hook_taxonomy_term_update().
+ */
+function trigger_taxonomy_term_update($term) {
+ _trigger_taxonomy('taxonomy_term_update', (array) $term);
+}
+
+/**
+ * Implement hook_taxonomy_term_delete().
+ */
+function trigger_taxonomy_term_delete($term) {
+ _trigger_taxonomy('taxonomy_term_delete', (array) $term);
}
/**
* Implement hook_actions_delete().
*
- * Remove all trigger entries for the given action, when deleted.
+ * Removes all trigger entries for the given action, when an action is deleted.
*/
function trigger_actions_delete($aid) {
db_delete('trigger_assignments')
->condition('aid', $aid)
->execute();
}
+
+/**
+ * Retrieves and caches information from hook_trigger_info() implementations.
+ */
+function _trigger_get_all_info() {
+ static $triggers = NULL;
+ if( $triggers ) {
+ return $triggers;
+ }
+
+ $triggers = module_invoke_all('trigger_info');
+ drupal_alter('trigger_info', $triggers);
+ return $triggers;
+}
+
diff --git a/modules/trigger/trigger.test b/modules/trigger/trigger.test
index 884982f83..4889bc59e 100644
--- a/modules/trigger/trigger.test
+++ b/modules/trigger/trigger.test
@@ -59,7 +59,7 @@ class TriggerContentTestCase extends DrupalWebTestCase {
$this->assertRaw(t('The action you chose is already assigned to that trigger.'), t('Check to make sure an error occurs when assigning an action to a trigger twice.'));
// Test 3: The action should be able to be unassigned from a trigger.
- $this->drupalPost('admin/structure/trigger/unassign/node/presave/' . $hash, array(), t('Unassign'));
+ $this->drupalPost('admin/structure/trigger/unassign/node/node_presave/' . $hash, array(), t('Unassign'));
$this->assertRaw(t('Action %action has been unassigned.', array('%action' => ucfirst($info['name']))), t('Check to make sure the @action action can be unassigned from the trigger.', array('@action' => $info['name'])));
$assigned = db_query("SELECT COUNT(*) FROM {trigger_assignments} WHERE aid IN (:keys)", array(':keys' => $content_actions))->fetchField();
$this->assertFalse($assigned, t('Check to make sure unassign worked properly at the database level.'));
@@ -79,32 +79,32 @@ class TriggerContentTestCase extends DrupalWebTestCase {
'node_publish_action' => array(
'property' => 'status',
'expected' => 1,
- 'name' => t('publish post'),
+ 'name' => t('publish content'),
),
'node_unpublish_action' => array(
'property' => 'status',
'expected' => 0,
- 'name' => t('unpublish post'),
+ 'name' => t('unpublish content'),
),
'node_make_sticky_action' => array(
'property' => 'sticky',
'expected' => 1,
- 'name' => t('make post sticky'),
+ 'name' => t('make content sticky'),
),
'node_make_unsticky_action' => array(
'property' => 'sticky',
'expected' => 0,
- 'name' => t('make post unsticky'),
+ 'name' => t('make content unsticky'),
),
'node_promote_action' => array(
'property' => 'promote',
'expected' => 1,
- 'name' => t('promote post to front page'),
+ 'name' => t('promote content to front page'),
),
'node_unpromote_action' => array(
'property' => 'promote',
'expected' => 0,
- 'name' => t('remove post from front page'),
+ 'name' => t('remove content from front page'),
),
);
return $info[$action];
@@ -138,41 +138,161 @@ class TriggerCronTestCase extends DrupalWebTestCase {
// Create an administrative user.
$test_user = $this->drupalCreateUser(array('administer actions'));
$this->drupalLogin($test_user);
-
+
// Assign a non-configurable action to the cron run trigger.
$edit = array('aid' => md5('trigger_test_system_cron_action'));
- $this->drupalPost('admin/structure/trigger/cron', $edit, t('Assign'));
-
+ $this->drupalPost('admin/structure/trigger/system', $edit, t('Assign'));
+
// Assign a configurable action to the cron trigger.
$hash = md5('trigger_test_system_cron_conf_action');
- $action_description = $this->randomName();
+ $action_label = $this->randomName();
$edit = array(
- 'actions_description' => $action_description,
- 'subject' => $action_description,
+ 'actions_label' => $action_label,
+ 'subject' => $action_label,
);
$this->drupalPost('admin/config/system/actions/configure/' . $hash, $edit, t('Save'));
$edit = array('aid' => md5('1'));
- $this->drupalPost('admin/structure/trigger/cron', $edit, t('Assign'));
-
+ $this->drupalPost('admin/structure/trigger/system', $edit, t('Assign'));
+
// Add a second configurable action to the cron trigger.
- $action_description = $this->randomName();
+ $action_label = $this->randomName();
$edit = array(
- 'actions_description' => $action_description,
- 'subject' => $action_description,
+ 'actions_label' => $action_label,
+ 'subject' => $action_label,
);
$this->drupalPost('admin/config/system/actions/configure/' . $hash, $edit, t('Save'));
$edit = array('aid' => md5('2'));
- $this->drupalPost('admin/structure/trigger/cron', $edit, t('Assign'));
-
+ $this->drupalPost('admin/structure/trigger/system', $edit, t('Assign'));
+
// Force a cron run.
drupal_cron_run();
-
+
// Make sure the non-configurable action has fired.
$action_run = variable_get('trigger_test_system_cron_action', FALSE);
$this->assertTrue($action_run, t('Check that the cron run triggered the test action.'));
-
+
// Make sure that both configurable actions have fired.
$action_run = variable_get('trigger_test_system_cron_conf_action', 0) == 2;
$this->assertTrue($action_run, t('Check that the cron run triggered both complex actions.'));
}
}
+
+/**
+ * Test other triggers.
+ */
+class TriggerOtherTestCase extends DrupalWebTestCase {
+ var $_cleanup_roles = array();
+ var $_cleanup_users = array();
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Trigger other actions',
+ 'description' => 'Test triggering of user, comment, taxonomy actions.' ,
+ 'group' => 'Trigger',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('trigger', 'trigger_test');
+ }
+
+ /**
+ * Test triggering on user create.
+ */
+ function testActionsUser() {
+ // Assign an action to the create user trigger.
+ $test_user = $this->drupalCreateUser(array('administer actions'));
+ $this->drupalLogin($test_user);
+ $action_id = 'trigger_test_generic_action';
+ $hash = md5($action_id);
+ $edit = array('aid' => $hash);
+ $this->drupalPost('admin/structure/trigger/user', $edit, t('Assign'));
+
+ // Set action variable to FALSE.
+ variable_set( $action_id, FALSE );
+
+ // Create an unblocked user
+ $web_user = $this->drupalCreateUser(array('administer users'));
+ $this->drupalLogin($web_user);
+ $name = $this->randomName();
+ $pass = user_password();
+ $edit = array();
+ $edit['name'] = $name;
+ $edit['mail'] = $name . '@example.com';
+ $edit['pass[pass1]'] = $pass;
+ $edit['pass[pass2]'] = $pass;
+ $edit['status'] = 1;
+ $this->drupalPost('admin/people/create', $edit, t('Create new account'));
+
+ // Verify that the action variable has been set.
+ $this->assertTrue(variable_get($action_id, FALSE), t('Check that creating a user triggered the test action.'));
+
+ // Reset the action variable.
+ variable_set( $action_id, FALSE );
+ }
+
+ /**
+ * Test triggering on comment save.
+ */
+ function testActionsComment() {
+ // Assign an action to the comment save trigger.
+ $test_user = $this->drupalCreateUser(array('administer actions'));
+ $this->drupalLogin($test_user);
+ $action_id = 'trigger_test_generic_action';
+ $hash = md5($action_id);
+ $edit = array('aid' => $hash);
+ $this->drupalPost('admin/structure/trigger/comment', $edit, t('Assign'));
+
+ // Set action variable to FALSE.
+ variable_set( $action_id, FALSE );
+
+ // Create a node and add a comment to it.
+ $web_user = $this->drupalCreateUser(array('create article content', 'access content', 'post comments without approval', 'post comments'));
+ $this->drupalLogin($web_user);
+ $node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
+ $edit = array();
+ $edit['subject'] = $this->randomName(10);
+ $edit['comment'] = $this->randomName(10) . ' ' . $this->randomName(10);
+ $this->drupalGet('comment/reply/' . $node->nid);
+ $this->drupalPost(NULL, $edit, t('Save'));
+
+ // Verify that the action variable has been set.
+ $this->assertTrue(variable_get($action_id, FALSE), t('Check that creating a comment triggered the action.'));
+ }
+
+ /**
+ * Test triggering on taxonomy new term.
+ */
+ function testActionsTaxonomy() {
+ // Assign an action to the taxonomy term save trigger.
+ $test_user = $this->drupalCreateUser(array('administer actions'));
+ $this->drupalLogin($test_user);
+ $action_id = 'trigger_test_generic_action';
+ $hash = md5($action_id);
+ $edit = array('aid' => $hash);
+ $this->drupalPost('admin/structure/trigger/taxonomy', $edit, t('Assign'));
+
+ // Set action variable to FALSE.
+ variable_set( $action_id, FALSE );
+
+ // Create a taxonomy vocabulary and add a term to it.
+
+ // Create a vocabulary.
+ $vocabulary = new stdClass();
+ $vocabulary->name = $this->randomName();
+ $vocabulary->description = $this->randomName();
+ $vocabulary->machine_name = drupal_strtolower($this->randomName());
+ $vocabulary->help = '';
+ $vocabulary->nodes = array('article' => 'article');
+ $vocabulary->weight = mt_rand(0, 10);
+ taxonomy_vocabulary_save($vocabulary);
+
+ $term = new stdClass();
+ $term->name = $this->randomName();
+ $term->vid = $vocabulary->vid;
+ taxonomy_term_save($term);
+
+ // Verify that the action variable has been set.
+ $this->assertTrue(variable_get($action_id, FALSE), t('Check that creating a taxonomy term triggered the action.'));
+ }
+}
diff --git a/modules/user/user.module b/modules/user/user.module
index 2c2c8896d..9cb594c73 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -2925,45 +2925,15 @@ function user_image_style_save($style) {
}
/**
- * Implement hook_hook_info().
- */
-function user_hook_info() {
- return array(
- 'user' => array(
- 'user' => array(
- 'insert' => array(
- 'runs when' => t('After a user account has been created'),
- ),
- 'update' => array(
- 'runs when' => t("After a user's profile has been updated"),
- ),
- 'delete' => array(
- 'runs when' => t('After a user has been deleted')
- ),
- 'login' => array(
- 'runs when' => t('After a user has logged in')
- ),
- 'logout' => array(
- 'runs when' => t('After a user has logged out')
- ),
- 'view' => array(
- 'runs when' => t("When a user's profile is being viewed")
- ),
- ),
- ),
- );
-}
-
-/**
* Implement hook_action_info().
*/
function user_action_info() {
return array(
'user_block_user_action' => array(
- 'description' => t('Block current user'),
+ 'label' => t('Block current user'),
'type' => 'user',
'configurable' => FALSE,
- 'hooks' => array(),
+ 'triggers' => array(),
),
);
}