annotate includes/actions.inc @ 13:134d4b2e75f6

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