| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Enables functions to be stored and executed at a later time. 6 */ 7 8 /** 9 * Implements hook_help(). 10 */ 11 function trigger_help($path, $arg) { 12 // Generate help text for admin/structure/trigger/(module) tabs. 13 $matches = array(); 14 if (preg_match('|^admin/structure/trigger/(.*)$|', $path, $matches)) { 15 $explanation = '<p>' . t('Triggers are events on your site, such as new content being added or a user logging in. The Trigger module associates these triggers with actions (functional tasks), such as unpublishing content containing certain keywords 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 advanced actions (actions requiring configuration, such as an e-mail address or a list of banned words).', array('@url' => url('admin/config/system/actions'))) . '</p>'; 16 17 $module = $matches[1]; 18 $trigger_info = _trigger_tab_information(); 19 if (!empty($trigger_info[$module])) { 20 $explanation .= '<p>' . t('There is a tab on this page for each module that defines triggers. On this tab you can assign actions to run when triggers from the <a href="@module-help">@module-name module</a> happen.', array('@module-help' => url('admin/help/' . $module), '@module-name' => $trigger_info[$module])) . '</p>'; 21 } 22 23 return $explanation; 24 } 25 26 if ($path == 'admin/help#trigger') { 27 $output = ''; 28 $output .= '<h3>' . t('About') . '</h3>'; 29 $output .= '<p>' . t('The Trigger module provides the ability to cause <em>actions</em> to run when certain <em>triggers</em> take place on your site. Triggers are events, such as new content being added to your site or a user logging in, and actions are tasks, such as unpublishing content or e-mailing an administrator. For more information, see the online handbook entry for <a href="@trigger">Trigger module</a>.', array('@trigger' => 'http://drupal.org/documentation/modules/trigger/')) . '</p>'; 30 $output .= '<h3>' . t('Uses') . '</h3>'; 31 $output .= '<dl>'; 32 $output .= '<dt>' . t('Configuring triggers and actions') . '</dt>'; 33 $output .= '<dd>' . t('The combination of actions and triggers can perform many useful tasks, such as e-mailing an administrator if a user account is deleted, or automatically unpublishing comments that contain certain words. To set up a trigger/action combination, first visit the <a href="@actions-page">Actions configuration page</a>, where you can either verify that the action you want is already listed, or create a new <em>advanced</em> action. You will need to set up an advanced action if there are configuration options in your trigger/action combination, such as specifying an e-mail address or a list of banned words. After configuring or verifying your action, visit the <a href="@triggers-page">Triggers configuration page</a> and choose the appropriate tab (Comment, Taxonomy, etc.), where you can assign the action to run when the trigger event occurs.', array('@triggers-page' => url('admin/structure/trigger'), '@actions-page' => url('admin/config/system/actions'))) . '</dd>'; 34 $output .= '</dl>'; 35 return $output; 36 } 37 } 38 39 /** 40 * Implements hook_menu(). 41 */ 42 function trigger_menu() { 43 $items['admin/structure/trigger'] = array( 44 'title' => 'Triggers', 45 'description' => 'Configure when to execute actions.', 46 'page callback' => 'trigger_assign', 47 'access arguments' => array('administer actions'), 48 'file' => 'trigger.admin.inc', 49 ); 50 51 $trigger_info = _trigger_tab_information(); 52 foreach ($trigger_info as $module => $module_name) { 53 $items["admin/structure/trigger/$module"] = array( 54 'title' => $module_name, 55 'page callback' => 'trigger_assign', 56 'page arguments' => array($module), 57 'access arguments' => array('administer actions'), 58 'type' => MENU_LOCAL_TASK, 59 'file' => 'trigger.admin.inc', 60 ); 61 } 62 63 $items['admin/structure/trigger/unassign'] = array( 64 'title' => 'Unassign', 65 'description' => 'Unassign an action from a trigger.', 66 'page callback' => 'drupal_get_form', 67 'page arguments' => array('trigger_unassign'), 68 // Only accessible if there are any actions that can be unassigned. 69 'access callback' => 'trigger_menu_unassign_access', 70 // Only output in the breadcrumb, not in menu trees. 71 'type' => MENU_VISIBLE_IN_BREADCRUMB, 72 'file' => 'trigger.admin.inc', 73 ); 74 75 return $items; 76 } 77 78 /** 79 * Access callback: Determines if triggers can be unassigned. 80 * 81 * @return bool 82 * TRUE if there are triggers that the user can unassign, FALSE otherwise. 83 * 84 * @see trigger_menu() 85 */ 86 function trigger_menu_unassign_access() { 87 if (!user_access('administer actions')) { 88 return FALSE; 89 } 90 $count = db_select('trigger_assignments') 91 ->countQuery() 92 ->execute() 93 ->fetchField(); 94 return $count > 0; 95 } 96 97 /** 98 * Implements hook_trigger_info(). 99 * 100 * Defines all the triggers that this module implements triggers for. 101 */ 102 function trigger_trigger_info() { 103 return array( 104 'node' => array( 105 'node_presave' => array( 106 'label' => t('When either saving new content or updating existing content'), 107 ), 108 'node_insert' => array( 109 'label' => t('After saving new content'), 110 ), 111 'node_update' => array( 112 'label' => t('After saving updated content'), 113 ), 114 'node_delete' => array( 115 'label' => t('After deleting content'), 116 ), 117 'node_view' => array( 118 'label' => t('When content is viewed by an authenticated user'), 119 ), 120 ), 121 'comment' => array( 122 'comment_presave' => array( 123 'label' => t('When either saving a new comment or updating an existing comment'), 124 ), 125 'comment_insert' => array( 126 'label' => t('After saving a new comment'), 127 ), 128 'comment_update' => array( 129 'label' => t('After saving an updated comment'), 130 ), 131 'comment_delete' => array( 132 'label' => t('After deleting a comment'), 133 ), 134 'comment_view' => array( 135 'label' => t('When a comment is being viewed by an authenticated user'), 136 ), 137 ), 138 'taxonomy' => array( 139 'taxonomy_term_insert' => array( 140 'label' => t('After saving a new term to the database'), 141 ), 142 'taxonomy_term_update' => array( 143 'label' => t('After saving an updated term to the database'), 144 ), 145 'taxonomy_term_delete' => array( 146 'label' => t('After deleting a term'), 147 ), 148 ), 149 'system' => array( 150 'cron' => array( 151 'label' => t('When cron runs'), 152 ), 153 ), 154 'user' => array( 155 'user_insert' => array( 156 'label' => t('After creating a new user account'), 157 ), 158 'user_update' => array( 159 'label' => t('After updating a user account'), 160 ), 161 'user_delete' => array( 162 'label' => t('After a user has been deleted'), 163 ), 164 'user_login' => array( 165 'label' => t('After a user has logged in'), 166 ), 167 'user_logout' => array( 168 'label' => t('After a user has logged out'), 169 ), 170 'user_view' => array( 171 'label' => t("When a user's profile is being viewed"), 172 ), 173 ), 174 ); 175 } 176 177 /** 178 * Gets the action IDs of actions to be executed for a hook. 179 * 180 * @param $hook 181 * The name of the hook being fired. 182 * 183 * @return 184 * An array whose keys are action IDs that the user has associated with 185 * this trigger, and whose values are arrays containing the action type and 186 * label. 187 */ 188 function trigger_get_assigned_actions($hook) { 189 $actions = &drupal_static(__FUNCTION__, array()); 190 if (!isset($actions[$hook])) { 191 $actions[$hook] = 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( 192 ':hook' => $hook, 193 ))->fetchAllAssoc('aid', PDO::FETCH_ASSOC); 194 } 195 return $actions[$hook]; 196 } 197 198 /** 199 * Implements hook_theme(). 200 */ 201 function trigger_theme() { 202 return array( 203 'trigger_display' => array( 204 'render element' => 'element', 205 'file' => 'trigger.admin.inc', 206 ), 207 ); 208 } 209 210 /** 211 * Implements hook_forms(). 212 * 213 * We re-use code by using the same assignment form definition for each hook. 214 */ 215 function trigger_forms() { 216 $trigger_info = _trigger_get_all_info(); 217 $forms = array(); 218 foreach ($trigger_info as $module => $hooks) { 219 foreach ($hooks as $hook => $description) { 220 $forms['trigger_' . $hook . '_assign_form'] = array('callback' => 'trigger_assign_form'); 221 } 222 } 223 224 return $forms; 225 } 226 227 /** 228 * Loads associated objects for node triggers. 229 * 230 * When an action is called in a context that does not match its type, the 231 * object that the action expects must be retrieved. For example, when an action 232 * that works on users is called during a node hook implementation, the user 233 * object is not available since the node hook call doesn't pass it. So here we 234 * load the object the action expects. 235 * 236 * @param $type 237 * The type of action that is about to be called. 238 * @param $node 239 * The node that was passed via the node hook. 240 * 241 * @return 242 * The object expected by the action that is about to be called. 243 */ 244 function _trigger_normalize_node_context($type, $node) { 245 // Note that comment-type actions are not supported in node contexts, 246 // because we wouldn't know which comment to choose. 247 switch ($type) { 248 // An action that works on users is being called in a node context. 249 // Load the user object of the node's author. 250 case 'user': 251 return user_load($node->uid); 252 } 253 } 254 255 /** 256 * Calls action functions for node triggers. 257 * 258 * @param $node 259 * Node object. 260 * @param $hook 261 * Hook to trigger. 262 * @param $a3 263 * Additional argument to action function. 264 * @param $a4 265 * Additional argument to action function. 266 */ 267 function _trigger_node($node, $hook, $a3 = NULL, $a4 = NULL) { 268 // Keep objects for reuse so that changes actions make to objects can persist. 269 static $objects; 270 // Prevent recursion by tracking which operations have already been called. 271 static $recursion; 272 273 $aids = trigger_get_assigned_actions($hook); 274 if (!$aids) { 275 return; 276 } 277 278 if (isset($recursion[$hook])) { 279 return; 280 } 281 $recursion[$hook] = TRUE; 282 283 $context = array( 284 'group' => 'node', 285 'hook' => $hook, 286 ); 287 288 // We need to get the expected object if the action's type is not 'node'. 289 // We keep the object in $objects so we can reuse it if we have multiple actions 290 // that make changes to an object. 291 foreach ($aids as $aid => $info) { 292 $type = $info['type']; 293 if ($type != 'node') { 294 if (!isset($objects[$type])) { 295 $objects[$type] = _trigger_normalize_node_context($type, $node); 296 } 297 // Since we know about the node, we pass that info along to the action. 298 $context['node'] = $node; 299 $result = actions_do($aid, $objects[$type], $context, $a3, $a4); 300 } 301 else { 302 actions_do($aid, $node, $context, $a3, $a4); 303 } 304 } 305 306 unset($recursion[$hook]); 307 } 308 309 /** 310 * Implements hook_node_view(). 311 */ 312 function trigger_node_view($node, $view_mode) { 313 _trigger_node($node, 'node_view', $view_mode); 314 } 315 316 /** 317 * Implements hook_node_update(). 318 */ 319 function trigger_node_update($node) { 320 _trigger_node($node, 'node_update'); 321 } 322 323 /** 324 * Implements hook_node_presave(). 325 */ 326 function trigger_node_presave($node) { 327 _trigger_node($node, 'node_presave'); 328 } 329 330 /** 331 * Implements hook_node_insert(). 332 */ 333 function trigger_node_insert($node) { 334 _trigger_node($node, 'node_insert'); 335 } 336 337 /** 338 * Implements hook_node_delete(). 339 */ 340 function trigger_node_delete($node) { 341 _trigger_node($node, 'node_delete'); 342 } 343 344 /** 345 * Loads associated objects for comment triggers. 346 * 347 * When an action is called in a context that does not match its type, the 348 * object that the action expects must be retrieved. For example, when an action 349 * that works on nodes is called during the comment hook, the node object is not 350 * available since the comment hook doesn't pass it. So here we load the object 351 * the action expects. 352 * 353 * @param $type 354 * The type of action that is about to be called. 355 * @param $comment 356 * The comment that was passed via the comment hook. 357 * 358 * @return 359 * The object expected by the action that is about to be called. 360 */ 361 function _trigger_normalize_comment_context($type, $comment) { 362 switch ($type) { 363 // An action that works with nodes is being called in a comment context. 364 case 'node': 365 return node_load(is_array($comment) ? $comment['nid'] : $comment->nid); 366 367 // An action that works on users is being called in a comment context. 368 case 'user': 369 return user_load(is_array($comment) ? $comment['uid'] : $comment->uid); 370 } 371 } 372 373 /** 374 * Implements hook_comment_presave(). 375 */ 376 function trigger_comment_presave($comment) { 377 _trigger_comment($comment, 'comment_presave'); 378 } 379 380 /** 381 * Implements hook_comment_insert(). 382 */ 383 function trigger_comment_insert($comment) { 384 _trigger_comment($comment, 'comment_insert'); 385 } 386 387 /** 388 * Implements hook_comment_update(). 389 */ 390 function trigger_comment_update($comment) { 391 _trigger_comment($comment, 'comment_update'); 392 } 393 394 /** 395 * Implements hook_comment_delete(). 396 */ 397 function trigger_comment_delete($comment) { 398 _trigger_comment($comment, 'comment_delete'); 399 } 400 401 /** 402 * Implements hook_comment_view(). 403 */ 404 function trigger_comment_view($comment) { 405 _trigger_comment($comment, 'comment_view'); 406 } 407 408 /** 409 * Calls action functions for comment triggers. 410 * 411 * @param $a1 412 * Comment object or array of form values. 413 * @param $hook 414 * Hook to trigger. 415 */ 416 function _trigger_comment($a1, $hook) { 417 // Keep objects for reuse so that changes actions make to objects can persist. 418 static $objects; 419 $aids = trigger_get_assigned_actions($hook); 420 $context = array( 421 'group' => 'comment', 422 'hook' => $hook, 423 ); 424 // We need to get the expected object if the action's type is not 'comment'. 425 // We keep the object in $objects so we can reuse it if we have multiple 426 // actions that make changes to an object. 427 foreach ($aids as $aid => $info) { 428 $type = $info['type']; 429 if ($type != 'comment') { 430 if (!isset($objects[$type])) { 431 $objects[$type] = _trigger_normalize_comment_context($type, $a1); 432 } 433 // Since we know about the comment, we pass it along to the action 434 // in case it wants to peek at it. 435 $context['comment'] = (object) $a1; 436 actions_do($aid, $objects[$type], $context); 437 } 438 else { 439 actions_do($aid, $a1, $context); 440 } 441 } 442 } 443 444 /** 445 * Implements hook_cron(). 446 */ 447 function trigger_cron() { 448 $aids = trigger_get_assigned_actions('cron'); 449 $context = array( 450 'group' => 'cron', 451 'hook' => 'cron', 452 ); 453 // Cron does not act on any specific object. 454 $object = NULL; 455 actions_do(array_keys($aids), $object, $context); 456 } 457 458 /** 459 * Loads associated objects for user triggers. 460 * 461 * When an action is called in a context that does not match its type, the 462 * object that the action expects must be retrieved. For example, when an action 463 * that works on nodes is called during the user hook, the node object is not 464 * available since the user hook doesn't pass it. So here we load the object the 465 * action expects. 466 * 467 * @param $type 468 * The type of action that is about to be called. 469 * @param $account 470 * The account object that was passed via the user hook. 471 * 472 * @return 473 * The object expected by the action that is about to be called. 474 */ 475 function _trigger_normalize_user_context($type, $account) { 476 // Note that comment-type actions are not supported in user contexts, 477 // because we wouldn't know which comment to choose. 478 switch ($type) { 479 // An action that works with nodes is being called in a user context. 480 // If a single node is being viewed, return the node. 481 case 'node': 482 // If we are viewing an individual node, return the node. 483 if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == NULL) { 484 return node_load(array('nid' => arg(1))); 485 } 486 break; 487 } 488 } 489 490 /** 491 * Implements hook_user_login(). 492 */ 493 function trigger_user_login(&$edit, $account, $category) { 494 _trigger_user('user_login', $edit, $account, $category); 495 } 496 497 /** 498 * Implements hook_user_logout(). 499 */ 500 function trigger_user_logout($account) { 501 $edit = array(); 502 _trigger_user('user_logout', $edit, $account); 503 } 504 505 /** 506 * Implements hook_user_insert(). 507 */ 508 function trigger_user_insert(&$edit, $account, $category) { 509 _trigger_user('user_insert', $edit, $account, $category); 510 } 511 512 /** 513 * Implements hook_user_update(). 514 */ 515 function trigger_user_update(&$edit, $account, $category) { 516 _trigger_user('user_update', $edit, $account, $category); 517 } 518 519 /** 520 * Implements hook_user_cancel(). 521 */ 522 function trigger_user_cancel($edit, $account, $method) { 523 switch ($method) { 524 case 'user_cancel_reassign': 525 _trigger_user('user_delete', $edit, $account, $method); 526 break; 527 } 528 } 529 530 /** 531 * Implements hook_user_delete(). 532 */ 533 function trigger_user_delete($account) { 534 $edit = array(); 535 _trigger_user('user_delete', $edit, $account, NULL); 536 } 537 538 /** 539 * Implements hook_user_view(). 540 */ 541 function trigger_user_view($account) { 542 $edit = NULL; 543 _trigger_user('user_view', $edit, $account, NULL); 544 } 545 546 /** 547 * Calls action functions for user triggers. 548 * 549 * @param $hook 550 * The hook that called this function. 551 * @param $edit 552 * Edit variable passed in to the hook or empty array if not needed. 553 * @param $account 554 * Account variable passed in to the hook. 555 * @param $method 556 * Method variable passed in to the hook or NULL if not needed. 557 */ 558 function _trigger_user($hook, &$edit, $account, $category = NULL) { 559 // Keep objects for reuse so that changes actions make to objects can persist. 560 static $objects; 561 $aids = trigger_get_assigned_actions($hook); 562 $context = array( 563 'group' => 'user', 564 'hook' => $hook, 565 'form_values' => &$edit, 566 ); 567 foreach ($aids as $aid => $info) { 568 $type = $info['type']; 569 if ($type != 'user') { 570 if (!isset($objects[$type])) { 571 $objects[$type] = _trigger_normalize_user_context($type, $account); 572 } 573 $context['user'] = $account; 574 actions_do($aid, $objects[$type], $context); 575 } 576 else { 577 actions_do($aid, $account, $context, $category); 578 } 579 } 580 } 581 582 /** 583 * Calls action functions for taxonomy triggers. 584 * 585 * @param $hook 586 * Hook to trigger actions for taxonomy_term_insert(), 587 * taxonomy_term_update(), and taxonomy_term_delete(). 588 * @param $array 589 * Item on which operation is being performed, either a term or 590 * form values. 591 */ 592 function _trigger_taxonomy($hook, $array) { 593 $aids = trigger_get_assigned_actions($hook); 594 $context = array( 595 'group' => 'taxonomy', 596 'hook' => $hook 597 ); 598 actions_do(array_keys($aids), (object) $array, $context); 599 } 600 601 /** 602 * Implements hook_taxonomy_term_insert(). 603 */ 604 function trigger_taxonomy_term_insert($term) { 605 _trigger_taxonomy('taxonomy_term_insert', (array) $term); 606 } 607 608 /** 609 * Implements hook_taxonomy_term_update(). 610 */ 611 function trigger_taxonomy_term_update($term) { 612 _trigger_taxonomy('taxonomy_term_update', (array) $term); 613 } 614 615 /** 616 * Implements hook_taxonomy_term_delete(). 617 */ 618 function trigger_taxonomy_term_delete($term) { 619 _trigger_taxonomy('taxonomy_term_delete', (array) $term); 620 } 621 622 /** 623 * Implements hook_actions_delete(). 624 * 625 * Removes all trigger entries for the given action, when an action is deleted. 626 */ 627 function trigger_actions_delete($aid) { 628 db_delete('trigger_assignments') 629 ->condition('aid', $aid) 630 ->execute(); 631 drupal_static_reset('trigger_get_assigned_actions'); 632 } 633 634 /** 635 * Retrieves and caches information from hook_trigger_info() implementations. 636 * 637 * @return 638 * Array of all triggers. 639 */ 640 function _trigger_get_all_info() { 641 $triggers = &drupal_static(__FUNCTION__); 642 643 if (!isset($triggers)) { 644 $triggers = module_invoke_all('trigger_info'); 645 drupal_alter('trigger_info', $triggers); 646 } 647 648 return $triggers; 649 } 650 651 /** 652 * Gathers information about tabs on the triggers administration screen. 653 * 654 * @return 655 * Array of modules that have triggers, with the keys being the 656 * machine-readable name of the module, and the values being the 657 * human-readable name of the module. 658 */ 659 function _trigger_tab_information() { 660 // Gather information about all triggers and modules. 661 $trigger_info = _trigger_get_all_info(); 662 $modules = system_get_info('module'); 663 $modules = array_intersect_key($modules, $trigger_info); 664 665 $return_info = array(); 666 foreach ($modules as $name => $info) { 667 $return_info[$name] = $info['name']; 668 } 669 670 return $return_info; 671 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title