| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: actions.inc,v 1.8.2.13 2010/08/11 20:35:47 goba Exp $ 3 4 /** 5 * @file 6 * This is the actions engine for executing stored actions. 7 */ 8 9 /** 10 * @defgroup actions Actions 11 * @{ 12 * Functions that perform an action on a certain system object. 13 * 14 * All modules should declare their action functions to be in this group and 15 * each action function should reference its configuration form, validate, and 16 * submit functions using \@see. Conversely, form, validate, and submit 17 * functions should reference the action function using \@see. For examples of 18 * this see comment_unpublish_by_keyword_action(), which has the following in 19 * its doxygen documentation: 20 * 21 * \@ingroup actions 22 * \@see comment_unpublish_by_keyword_action_form(). 23 * \@see comment_unpublish_by_keyword_action_submit(). 24 * 25 * @} End of "defgroup actions". 26 */ 27 28 /** 29 * @defgroup actions Actions 30 * @{ 31 * Functions that perform an action on a certain system object. 32 * 33 * All modules should declare their action functions to be in this group and 34 * each action function should reference its configuration form, validate, and 35 * submit functions using \@see. Conversely, form, validate, and submit 36 * functions should reference the action function using \@see. For examples of 37 * this see comment_unpublish_by_keyword_action(), which has the following in 38 * its doxygen documentation: 39 * 40 * \@ingroup actions 41 * \@see comment_unpublish_by_keyword_action_form(). 42 * \@see comment_unpublish_by_keyword_action_submit(). 43 * 44 * @} End of "defgroup actions". 45 */ 46 47 /** 48 * Perform a given list of actions by executing their callback functions. 49 * 50 * Given the IDs of actions to perform, find out what the callbacks 51 * for the actions are by querying the database. Then call each callback 52 * using the function call $function($object, $context, $a1, $a2) 53 * where $function is the name of a function written in compliance with 54 * the action specification; that is, foo($object, $context). 55 * 56 * @param $action_ids 57 * The ID of the action to perform. Can be a single action ID or an array 58 * of IDs. IDs of instances will be numeric; IDs of singletons will be 59 * function names. 60 * @param $object 61 * Parameter that will be passed along to the callback. Typically the 62 * object that the action will act on; a node, user or comment object. 63 * If the action does not act on an object, pass a dummy object. This 64 * is necessary to support PHP 4 object referencing. 65 * @param $context 66 * Parameter that will be passed along to the callback. $context is a 67 * keyed array containing extra information about what is currently 68 * happening at the time of the call. Typically $context['hook'] and 69 * $context['op'] will tell which hook-op combination resulted in this 70 * call to actions_do(). 71 * @param $a1 72 * Parameter that will be passed along to the callback. 73 * @param $a2 74 * Parameter that will be passed along to the callback. 75 * 76 * @return 77 * An associative array containing the result of the function that 78 * performs the action, keyed on action ID. 79 */ 80 function actions_do($action_ids, &$object, $context = NULL, $a1 = NULL, $a2 = NULL) { 81 // $stack tracks the number of recursive calls. 82 static $stack; 83 $stack++; 84 if ($stack > variable_get('actions_max_stack', 35)) { 85 watchdog('actions', 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.', array(), WATCHDOG_ERROR); 86 return; 87 } 88 $actions = array(); 89 $available_actions = actions_list(); 90 $result = array(); 91 if (is_array($action_ids)) { 92 $where = array(); 93 $where_values = array(); 94 foreach ($action_ids as $action_id) { 95 if (is_numeric($action_id)) { 96 $where[] = "OR aid = '%s'"; 97 $where_values[] = $action_id; 98 } 99 elseif (isset($available_actions[$action_id])) { 100 $actions[$action_id] = $available_actions[$action_id]; 101 } 102 } 103 104 // When we have action instances we must go to the database to 105 // retrieve instance data. 106 if ($where) { 107 $where_clause = implode(' ', $where); 108 // Strip off leading 'OR '. 109 $where_clause = '('. strstr($where_clause, " ") .')'; 110 $result_db = db_query('SELECT * FROM {actions} WHERE '. $where_clause, $where_values); 111 while ($action = db_fetch_object($result_db)) { 112 $actions[$action->aid] = $action->parameters ? unserialize($action->parameters) : array(); 113 $actions[$action->aid]['callback'] = $action->callback; 114 $actions[$action->aid]['type'] = $action->type; 115 } 116 } 117 118 // Fire actions, in no particular order. 119 foreach ($actions as $action_id => $params) { 120 if (is_numeric($action_id)) { // Configurable actions need parameters. 121 $function = $params['callback']; 122 if (function_exists($function)) { 123 $context = array_merge($context, $params); 124 $actions_result[$action_id] = $function($object, $context, $a1, $a2); 125 } 126 else { 127 $actions_result[$action_id] = FALSE; 128 } 129 } 130 // Singleton action; $action_id is the function name. 131 else { 132 $result[$action_id] = $action_id($object, $context, $a1, $a2); 133 } 134 } 135 } 136 // Optimized execution of single action. 137 else { 138 // If it's a configurable action, retrieve stored parameters. 139 if (is_numeric($action_ids)) { 140 $action = db_fetch_object(db_query("SELECT * FROM {actions} WHERE aid = '%s'", $action_ids)); 141 $function = $action->callback; 142 if (function_exists($function)) { 143 $context = array_merge($context, unserialize($action->parameters)); 144 $actions_result[$action_ids] = $function($object, $context, $a1, $a2); 145 } 146 else { 147 $actions_result[$action_ids] = FALSE; 148 } 149 } 150 // Singleton action; $action_ids is the function name. 151 else { 152 $result[$action_ids] = $action_ids($object, $context, $a1, $a2); 153 } 154 } 155 $stack--; 156 return $result; 157 } 158 159 160 /** 161 * Discover all action functions by invoking hook_action_info(). 162 * 163 * @code 164 * mymodule_action_info() { 165 * return array( 166 * 'mymodule_functiondescription_action' => array( 167 * 'type' => 'node', 168 * 'description' => t('Save node'), 169 * 'configurable' => FALSE, 170 * 'hooks' => array( 171 * 'nodeapi' => array('delete', 'insert', 'update', 'view'), 172 * 'comment' => array('delete', 'insert', 'update', 'view'), 173 * ) 174 * ) 175 * ); 176 * } 177 * @endcode 178 * 179 * The description is used in presenting possible actions to the user for 180 * configuration. The type is used to present these actions in a logical 181 * grouping and to denote context. Some types are 'node', 'user', 'comment', 182 * and 'system'. If an action is configurable it will provide form, 183 * validation and submission functions. The hooks the action supports 184 * are declared in the 'hooks' array. 185 * 186 * @param $reset 187 * Reset the action info static cache. 188 * 189 * @return 190 * An associative array keyed on function name. The value of each key is 191 * an array containing information about the action, such as type of 192 * action and description of the action, e.g., 193 * 194 * @code 195 * $actions['node_publish_action'] = array( 196 * 'type' => 'node', 197 * 'description' => t('Publish post'), 198 * 'configurable' => FALSE, 199 * 'hooks' => array( 200 * 'nodeapi' => array('presave', 'insert', 'update', 'view'), 201 * 'comment' => array('delete', 'insert', 'update', 'view'), 202 * ), 203 * ); 204 * @endcode 205 */ 206 function actions_list($reset = FALSE) { 207 static $actions; 208 if (!isset($actions) || $reset) { 209 $actions = module_invoke_all('action_info'); 210 drupal_alter('action_info', $actions); 211 } 212 213 // See module_implements for explanations of this cast. 214 return (array)$actions; 215 } 216 217 /** 218 * Retrieves all action instances from the database. 219 * 220 * Compare with actions_list(), which gathers actions by invoking 221 * hook_action_info(). The actions returned by this function and the actions 222 * returned by actions_list() are partially synchronized. Non-configurable 223 * actions from hook_action_info() implementations are put into the database 224 * when actions_synchronize() is called, which happens when 225 * admin/settings/actions is visited. Configurable actions are not added to 226 * the database until they are configured in the user interface, in which case 227 * a database row is created for each configuration of each action. 228 * 229 * @return 230 * Associative array keyed by action ID. Each value is an 231 * associative array with keys 'callback', 'description', 'type' and 232 * 'configurable'. 233 */ 234 function actions_get_all_actions() { 235 $actions = array(); 236 $result = db_query("SELECT * FROM {actions}"); 237 while ($action = db_fetch_object($result)) { 238 $actions[$action->aid] = array( 239 'callback' => $action->callback, 240 'description' => $action->description, 241 'type' => $action->type, 242 'configurable' => (bool) $action->parameters, 243 ); 244 } 245 return $actions; 246 } 247 248 /** 249 * Create an associative array keyed by md5 hashes of function names. 250 * 251 * Hashes are used to prevent actual function names from going out into 252 * HTML forms and coming back. 253 * 254 * @param $actions 255 * An associative array with function names as keys and associative 256 * arrays with keys 'description', 'type', etc. as values. Generally 257 * the output of actions_list() or actions_get_all_actions() is given 258 * as input to this function. 259 * 260 * @return 261 * An associative array keyed on md5 hash of function name. The value of 262 * each key is an associative array of function, description, and type 263 * for the action. 264 */ 265 function actions_actions_map($actions) { 266 $actions_map = array(); 267 foreach ($actions as $callback => $array) { 268 $key = md5($callback); 269 $actions_map[$key]['callback'] = isset($array['callback']) ? $array['callback'] : $callback; 270 $actions_map[$key]['description'] = $array['description']; 271 $actions_map[$key]['type'] = $array['type']; 272 $actions_map[$key]['configurable'] = $array['configurable']; 273 } 274 return $actions_map; 275 } 276 277 /** 278 * Given an md5 hash of a function name, return the function name. 279 * 280 * Faster than actions_actions_map() when you only need the function name. 281 * 282 * @param $hash 283 * MD5 hash of a function name 284 * 285 * @return 286 * Function name 287 */ 288 function actions_function_lookup($hash) { 289 $actions_list = actions_list(); 290 foreach ($actions_list as $function => $array) { 291 if (md5($function) == $hash) { 292 return $function; 293 } 294 } 295 296 // Must be an instance; must check database. 297 $aid = db_result(db_query("SELECT aid FROM {actions} WHERE MD5(aid) = '%s' AND parameters <> ''", $hash)); 298 return $aid; 299 } 300 301 /** 302 * Synchronize actions that are provided by modules. 303 * 304 * They are synchronized with actions that are stored in the actions table. 305 * This is necessary so that actions that do not require configuration can 306 * receive action IDs. This is not necessarily the best approach, 307 * but it is the most straightforward. 308 */ 309 function actions_synchronize($actions_in_code = array(), $delete_orphans = FALSE) { 310 if (!$actions_in_code) { 311 $actions_in_code = actions_list(TRUE); 312 } 313 $actions_in_db = array(); 314 $result = db_query("SELECT * FROM {actions} WHERE parameters = ''"); 315 while ($action = db_fetch_object($result)) { 316 $actions_in_db[$action->callback] = array('aid' => $action->aid, 'description' => $action->description); 317 } 318 319 // Go through all the actions provided by modules. 320 foreach ($actions_in_code as $callback => $array) { 321 // Ignore configurable actions since their instances get put in 322 // when the user adds the action. 323 if (!$array['configurable']) { 324 // If we already have an action ID for this action, no need to assign aid. 325 if (array_key_exists($callback, $actions_in_db)) { 326 unset($actions_in_db[$callback]); 327 } 328 else { 329 // This is a new singleton that we don't have an aid for; assign one. 330 db_query("INSERT INTO {actions} (aid, type, callback, parameters, description) VALUES ('%s', '%s', '%s', '%s', '%s')", $callback, $array['type'], $callback, '', $array['description']); 331 watchdog('actions', "Action '%action' added.", array('%action' => $array['description'])); 332 } 333 } 334 } 335 336 // Any actions that we have left in $actions_in_db are orphaned. 337 if ($actions_in_db) { 338 $orphaned = array(); 339 $placeholder = array(); 340 341 foreach ($actions_in_db as $callback => $array) { 342 $orphaned[] = $callback; 343 $placeholder[] = "'%s'"; 344 } 345 346 $orphans = implode(', ', $orphaned); 347 348 if ($delete_orphans) { 349 $placeholders = implode(', ', $placeholder); 350 $results = db_query("SELECT a.aid, a.description FROM {actions} a WHERE callback IN ($placeholders)", $orphaned); 351 while ($action = db_fetch_object($results)) { 352 actions_delete($action->aid); 353 watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => $action->description)); 354 } 355 } 356 else { 357 $link = l(t('Remove orphaned actions'), 'admin/settings/actions/orphan'); 358 $count = count($actions_in_db); 359 watchdog('actions', format_plural($count, 'One orphaned action (%orphans) exists in the actions table. !link', '@count orphaned actions (%orphans) exist in the actions table. !link'), array('@count' => $count, '%orphans' => $orphans, '!link' => $link), WATCHDOG_WARNING); 360 } 361 } 362 } 363 364 /** 365 * Save an action and its associated user-supplied parameter values to the database. 366 * 367 * @param $function 368 * The name of the function to be called when this action is performed. 369 * @param $type 370 * The type of action, to describe grouping and/or context, e.g., 'node', 371 * 'user', 'comment', or 'system'. 372 * @param $params 373 * An associative array with parameter names as keys and parameter values 374 * as values. 375 * @param $desc 376 * A user-supplied description of this particular action, e.g., 'Send 377 * e-mail to Jim'. 378 * @param $aid 379 * The ID of this action. If omitted, a new action is created. 380 * 381 * @return 382 * The ID of the action. 383 */ 384 function actions_save($function, $type, $params, $desc, $aid = NULL) { 385 $serialized = serialize($params); 386 if ($aid) { 387 db_query("UPDATE {actions} SET callback = '%s', type = '%s', parameters = '%s', description = '%s' WHERE aid = '%s'", $function, $type, $serialized, $desc, $aid); 388 watchdog('actions', 'Action %action saved.', array('%action' => $desc)); 389 } 390 else { 391 // aid is the callback for singleton actions so we need to keep a 392 // separate table for numeric aids. 393 db_query('INSERT INTO {actions_aid} VALUES (default)'); 394 $aid = db_last_insert_id('actions_aid', 'aid'); 395 db_query("INSERT INTO {actions} (aid, callback, type, parameters, description) VALUES ('%s', '%s', '%s', '%s', '%s')", $aid, $function, $type, $serialized, $desc); 396 watchdog('actions', 'Action %action created.', array('%action' => $desc)); 397 } 398 399 return $aid; 400 } 401 402 /** 403 * Retrieve a single action from the database. 404 * 405 * @param $aid 406 * integer The ID of the action to retrieve. 407 * 408 * @return 409 * The appropriate action row from the database as an object. 410 */ 411 function actions_load($aid) { 412 return db_fetch_object(db_query("SELECT * FROM {actions} WHERE aid = '%s'", $aid)); 413 } 414 415 /** 416 * Delete a single action from the database. 417 * 418 * @param $aid 419 * integer The ID of the action to delete. 420 */ 421 function actions_delete($aid) { 422 db_query("DELETE FROM {actions} WHERE aid = '%s'", $aid); 423 module_invoke_all('actions_delete', $aid); 424 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |