actions.inc

  1. drupal
    1. 6 includes/actions.inc
    2. 7 includes/actions.inc
    3. 8 core/includes/actions.inc

This is the actions engine for executing stored actions.

Functions & methods

NameDescription
actions_actions_mapCreates an associative array keyed by hashes of function names or IDs.
actions_deleteDeletes a single action from the database.
actions_doPerforms a given list of actions by executing their callback functions.
actions_function_lookupReturns an action array key (function or ID), given its hash.
actions_get_all_actionsRetrieves all action instances from the database.
actions_listDiscovers all available actions by invoking hook_action_info().
actions_loadRetrieves a single action from the database.
actions_saveSaves an action and its user-supplied parameter values to the database.
actions_synchronizeSynchronizes actions that are provided by modules in hook_action_info().

File

core/includes/actions.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * This is the actions engine for executing stored actions.
  5. */
  6. /**
  7. * @defgroup actions Actions
  8. * @{
  9. * Functions that perform an action on a certain system object.
  10. *
  11. * Action functions are declared by modules by implementing hook_action_info().
  12. * Modules can cause action functions to run by calling actions_do().
  13. *
  14. * Each action function takes two to four arguments:
  15. * - $entity: The object that the action acts on, such as a node, comment, or
  16. * user.
  17. * - $context: Array of additional information about what triggered the action.
  18. * - $a1, $a2: Optional additional information, which can be passed into
  19. * actions_do() and will be passed along to the action function.
  20. *
  21. * @} End of "defgroup actions".
  22. */
  23. /**
  24. * Performs a given list of actions by executing their callback functions.
  25. *
  26. * Given the IDs of actions to perform, this function finds out what the
  27. * callback functions for the actions are by querying the database. Then
  28. * it calls each callback using the function call $function($object, $context,
  29. * $a1, $a2), passing the input arguments of this function (see below) to the
  30. * action function.
  31. *
  32. * @param $action_ids
  33. * The IDs of the actions to perform. Can be a single action ID or an array
  34. * of IDs. IDs of configurable actions must be given as numeric action IDs;
  35. * IDs of non-configurable actions may be given as action function names.
  36. * @param $object
  37. * The object that the action will act on: a node, user, or comment object.
  38. * @param $context
  39. * Associative array containing extra information about what triggered
  40. * the action call, with $context['hook'] giving the name of the hook
  41. * that resulted in this call to actions_do().
  42. * @param $a1
  43. * Passed along to the callback.
  44. * @param $a2
  45. * Passed along to the callback.
  46. *
  47. * @return
  48. * An associative array containing the results of the functions that
  49. * perform the actions, keyed on action ID.
  50. *
  51. * @ingroup actions
  52. */
  53. function actions_do($action_ids, $object = NULL, $context = NULL, $a1 = NULL, $a2 = NULL) {
  54. // $stack tracks the number of recursive calls.
  55. static $stack;
  56. $stack++;
  57. if ($stack > variable_get('actions_max_stack', 35)) {
  58. watchdog('actions', 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.', array(), WATCHDOG_ERROR);
  59. return;
  60. }
  61. $actions = array();
  62. $available_actions = actions_list();
  63. $actions_result = array();
  64. if (is_array($action_ids)) {
  65. $conditions = array();
  66. foreach ($action_ids as $action_id) {
  67. if (is_numeric($action_id)) {
  68. $conditions[] = $action_id;
  69. }
  70. elseif (isset($available_actions[$action_id])) {
  71. $actions[$action_id] = $available_actions[$action_id];
  72. }
  73. }
  74. // When we have action instances we must go to the database to retrieve
  75. // instance data.
  76. if (!empty($conditions)) {
  77. $query = db_select('actions');
  78. $query->addField('actions', 'aid');
  79. $query->addField('actions', 'type');
  80. $query->addField('actions', 'callback');
  81. $query->addField('actions', 'parameters');
  82. $query->condition('aid', $conditions, 'IN');
  83. $result = $query->execute();
  84. foreach ($result as $action) {
  85. $actions[$action->aid] = $action->parameters ? unserialize($action->parameters) : array();
  86. $actions[$action->aid]['callback'] = $action->callback;
  87. $actions[$action->aid]['type'] = $action->type;
  88. }
  89. }
  90. // Fire actions, in no particular order.
  91. foreach ($actions as $action_id => $params) {
  92. // Configurable actions need parameters.
  93. if (is_numeric($action_id)) {
  94. $function = $params['callback'];
  95. if (function_exists($function)) {
  96. $context = array_merge($context, $params);
  97. $actions_result[$action_id] = $function($object, $context, $a1, $a2);
  98. }
  99. else {
  100. $actions_result[$action_id] = FALSE;
  101. }
  102. }
  103. // Singleton action; $action_id is the function name.
  104. else {
  105. $actions_result[$action_id] = $action_id($object, $context, $a1, $a2);
  106. }
  107. }
  108. }
  109. // Optimized execution of a single action.
  110. else {
  111. // If it's a configurable action, retrieve stored parameters.
  112. if (is_numeric($action_ids)) {
  113. $action = db_query("SELECT callback, parameters FROM {actions} WHERE aid = :aid", array(':aid' => $action_ids))->fetchObject();
  114. $function = $action->callback;
  115. if (function_exists($function)) {
  116. $context = array_merge($context, unserialize($action->parameters));
  117. $actions_result[$action_ids] = $function($object, $context, $a1, $a2);
  118. }
  119. else {
  120. $actions_result[$action_ids] = FALSE;
  121. }
  122. }
  123. // Singleton action; $action_ids is the function name.
  124. else {
  125. if (function_exists($action_ids)) {
  126. $actions_result[$action_ids] = $action_ids($object, $context, $a1, $a2);
  127. }
  128. else {
  129. // Set to avoid undefined index error messages later.
  130. $actions_result[$action_ids] = FALSE;
  131. }
  132. }
  133. }
  134. $stack--;
  135. return $actions_result;
  136. }
  137. /**
  138. * Discovers all available actions by invoking hook_action_info().
  139. *
  140. * This function contrasts with actions_get_all_actions(); see the
  141. * documentation of actions_get_all_actions() for an explanation.
  142. *
  143. * @param $reset
  144. * Reset the action info static cache.
  145. *
  146. * @return
  147. * An associative array keyed on action function name, with the same format
  148. * as the return value of hook_action_info(), containing all
  149. * modules' hook_action_info() return values as modified by any
  150. * hook_action_info_alter() implementations.
  151. *
  152. * @see hook_action_info()
  153. */
  154. function actions_list($reset = FALSE) {
  155. $actions = &drupal_static(__FUNCTION__);
  156. if (!isset($actions) || $reset) {
  157. $actions = module_invoke_all('action_info');
  158. drupal_alter('action_info', $actions);
  159. }
  160. // See module_implements() for an explanation of this cast.
  161. return (array) $actions;
  162. }
  163. /**
  164. * Retrieves all action instances from the database.
  165. *
  166. * This function differs from the actions_list() function, which gathers
  167. * actions by invoking hook_action_info(). The actions returned by this
  168. * function and the actions returned by actions_list() are partially
  169. * synchronized. Non-configurable actions from hook_action_info()
  170. * implementations are put into the database when actions_synchronize() is
  171. * called, which happens when admin/config/system/actions is visited.
  172. * Configurable actions are not added to the database until they are configured
  173. * in the user interface, in which case a database row is created for each
  174. * configuration of each action.
  175. *
  176. * @return
  177. * Associative array keyed by numeric action ID. Each value is an associative
  178. * array with keys 'callback', 'label', 'type' and 'configurable'.
  179. */
  180. function actions_get_all_actions() {
  181. $actions = db_query("SELECT aid, type, callback, parameters, label FROM {actions}")->fetchAllAssoc('aid', PDO::FETCH_ASSOC);
  182. foreach ($actions as &$action) {
  183. $action['configurable'] = (bool) $action['parameters'];
  184. unset($action['parameters']);
  185. unset($action['aid']);
  186. }
  187. return $actions;
  188. }
  189. /**
  190. * Creates an associative array keyed by hashes of function names or IDs.
  191. *
  192. * Hashes are used to prevent actual function names from going out into HTML
  193. * forms and coming back.
  194. *
  195. * @param $actions
  196. * An associative array with function names or action IDs as keys
  197. * and associative arrays with keys 'label', 'type', etc. as values.
  198. * This is usually the output of actions_list() or actions_get_all_actions().
  199. *
  200. * @return
  201. * An associative array whose keys are hashes of the input array keys, and
  202. * whose corresponding values are associative arrays with components
  203. * 'callback', 'label', 'type', and 'configurable' from the input array.
  204. */
  205. function actions_actions_map($actions) {
  206. $actions_map = array();
  207. foreach ($actions as $callback => $array) {
  208. $key = drupal_hash_base64($callback);
  209. $actions_map[$key]['callback'] = isset($array['callback']) ? $array['callback'] : $callback;
  210. $actions_map[$key]['label'] = $array['label'];
  211. $actions_map[$key]['type'] = $array['type'];
  212. $actions_map[$key]['configurable'] = $array['configurable'];
  213. }
  214. return $actions_map;
  215. }
  216. /**
  217. * Returns an action array key (function or ID), given its hash.
  218. *
  219. * Faster than actions_actions_map() when you only need the function name or ID.
  220. *
  221. * @param $hash
  222. * Hash of a function name or action ID array key. The array key
  223. * is a key into the return value of actions_list() (array key is the action
  224. * function name) or actions_get_all_actions() (array key is the action ID).
  225. *
  226. * @return
  227. * The corresponding array key, or FALSE if no match is found.
  228. */
  229. function actions_function_lookup($hash) {
  230. // Check for a function name match.
  231. $actions_list = actions_list();
  232. foreach ($actions_list as $function => $array) {
  233. if (drupal_hash_base64($function) == $hash) {
  234. return $function;
  235. }
  236. }
  237. $aid = FALSE;
  238. // Must be a configurable action; check database.
  239. $result = db_query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchAll(PDO::FETCH_ASSOC);
  240. foreach ($result as $row) {
  241. if (drupal_hash_base64($row['aid']) == $hash) {
  242. $aid = $row['aid'];
  243. break;
  244. }
  245. }
  246. return $aid;
  247. }
  248. /**
  249. * Synchronizes actions that are provided by modules in hook_action_info().
  250. *
  251. * Actions provided by modules in hook_action_info() implementations are
  252. * synchronized with actions that are stored in the actions database table.
  253. * This is necessary so that actions that do not require configuration can
  254. * receive action IDs.
  255. *
  256. * @param $delete_orphans
  257. * If TRUE, any actions that exist in the database but are no longer
  258. * found in the code (for example, because the module that provides them has
  259. * been disabled) will be deleted.
  260. */
  261. function actions_synchronize($delete_orphans = FALSE) {
  262. $actions_in_code = actions_list(TRUE);
  263. $actions_in_db = db_query("SELECT aid, callback, label FROM {actions} WHERE parameters = ''")->fetchAllAssoc('callback', PDO::FETCH_ASSOC);
  264. // Go through all the actions provided by modules.
  265. foreach ($actions_in_code as $callback => $array) {
  266. // Ignore configurable actions since their instances get put in when the
  267. // user adds the action.
  268. if (!$array['configurable']) {
  269. // If we already have an action ID for this action, no need to assign aid.
  270. if (isset($actions_in_db[$callback])) {
  271. unset($actions_in_db[$callback]);
  272. }
  273. else {
  274. // This is a new singleton that we don't have an aid for; assign one.
  275. db_insert('actions')
  276. ->fields(array(
  277. 'aid' => $callback,
  278. 'type' => $array['type'],
  279. 'callback' => $callback,
  280. 'parameters' => '',
  281. 'label' => $array['label'],
  282. ))
  283. ->execute();
  284. watchdog('actions', "Action '%action' added.", array('%action' => $array['label']));
  285. }
  286. }
  287. }
  288. // Any actions that we have left in $actions_in_db are orphaned.
  289. if ($actions_in_db) {
  290. $orphaned = array_keys($actions_in_db);
  291. if ($delete_orphans) {
  292. $actions = db_query('SELECT aid, label FROM {actions} WHERE callback IN (:orphaned)', array(':orphaned' => $orphaned))->fetchAll();
  293. foreach ($actions as $action) {
  294. actions_delete($action->aid);
  295. watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => $action->label));
  296. }
  297. }
  298. else {
  299. $link = l(t('Remove orphaned actions'), 'admin/config/system/actions/orphan');
  300. $count = count($actions_in_db);
  301. $orphans = implode(', ', $orphaned);
  302. watchdog('actions', '@count orphaned actions (%orphans) exist in the actions table. !link', array('@count' => $count, '%orphans' => $orphans, '!link' => $link), WATCHDOG_INFO);
  303. }
  304. }
  305. }
  306. /**
  307. * Saves an action and its user-supplied parameter values to the database.
  308. *
  309. * @param $function
  310. * The name of the function to be called when this action is performed.
  311. * @param $type
  312. * The type of action, to describe grouping and/or context, e.g., 'node',
  313. * 'user', 'comment', or 'system'.
  314. * @param $params
  315. * An associative array with parameter names as keys and parameter values as
  316. * values.
  317. * @param $label
  318. * A user-supplied label of this particular action, e.g., 'Send e-mail
  319. * to Jim'.
  320. * @param $aid
  321. * The ID of this action. If omitted, a new action is created.
  322. *
  323. * @return
  324. * The ID of the action.
  325. */
  326. function actions_save($function, $type, $params, $label, $aid = NULL) {
  327. // aid is the callback for singleton actions so we need to keep a separate
  328. // table for numeric aids.
  329. if (!$aid) {
  330. $aid = db_next_id();
  331. }
  332. db_merge('actions')
  333. ->key(array('aid' => $aid))
  334. ->fields(array(
  335. 'callback' => $function,
  336. 'type' => $type,
  337. 'parameters' => serialize($params),
  338. 'label' => $label,
  339. ))
  340. ->execute();
  341. watchdog('actions', 'Action %action saved.', array('%action' => $label));
  342. return $aid;
  343. }
  344. /**
  345. * Retrieves a single action from the database.
  346. *
  347. * @param $aid
  348. * The ID of the action to retrieve.
  349. *
  350. * @return
  351. * The appropriate action row from the database as an object.
  352. */
  353. function actions_load($aid) {
  354. return db_query("SELECT aid, type, callback, parameters, label FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetchObject();
  355. }
  356. /**
  357. * Deletes a single action from the database.
  358. *
  359. * @param $aid
  360. * The ID of the action to delete.
  361. */
  362. function actions_delete($aid) {
  363. db_delete('actions')
  364. ->condition('aid', $aid)
  365. ->execute();
  366. module_invoke_all('actions_delete', $aid);
  367. }
Login or register to post comments