/[drupal]/drupal/modules/trigger/trigger.module
ViewVC logotype

Contents of /drupal/modules/trigger/trigger.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


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

  ViewVC Help
Powered by ViewVC 1.1.2