Chapter 03: Hooks, Actions, and Triggers
当 用Drupal干活时有一个通常的考虑那就是当一个特定的事件产生时有什么事情发生,例如,站点管理员可能想在某条消息发布时接收一个e-mail或者一 个一个用户在评论里包含了某些单词时应该被锁定。本章论述怎样钩进Drupal事件以在这些事件产生时执行你自己的代码。
理解事件和触发器
name = Beep description = Simulates a system beep. packeage = Pro Drupal Development core = 7.x files[] = beep.module
<?php /** * @file * Provide a simulated beep. */ function beep_beep() { watchdog('beep', 'Beep!'); }
/** * Implamentation of hook_user_login(). */ function beep_user_login(&$editor, $account) { beep_beep(); }
/** * Implamentation of hook_node_insert(). */ function beep_node_insert($node) { beep_beep(); }
理解动作
Trigger用户接口
Hook Trigger Name comment_insert After saving a new comment comment_update After saving an updated comment comment_delete After deleting a comment comment_view When a comment is being viewed by an authenticated user cron When cron run node_presave When either saving a nwe post or updating an existing post node_insert After saving a new post node_update After saving an updated post node_delete After deleting a post taxonomy_term_insert After saving a new term to the database taxonomy_term_update After saving an updated term to the database taxonomy_term_delete After_deleting a term user_insert After a user account has been created user_update After a user's profile has been updated user_delete After a user has been deleted user_login After a user has logged in user_logout After a user has logged out user_view When a user's profile is being wiewd
你的第一个动作
/** * Implementation of hook_action_info(). */ function beep_action_info() { return array( 'beep_beep_action' => array( 'type' => 'system', 'label' => t('Beep annoyingly'), 'configurable' => FALSE, 'triggers' => array('node_view', 'node_insert', 'node_update', 'node_delete'), ), ); }
/** * Simulate a beep. A Drupal action. */ function beep_beep_action() { beep_beep(); }
分派动作
改变一个动作支持的触发器
/** * Implementation of hook_action_info(). */ function beep_action_info() { return array( 'beep_beep_action' => array( 'type' => 'system', 'label' => t('Beep annoyingly'), 'configurable' => FALSE, 'triggers' => array('node_delete'), ), ); }
支持所有触发器的动作
/** * Implementation of hook_action_info(). */ function beep_action_info() { return array( 'beep_beep_action' => array( 'type' => 'system', 'label' => t('Beep annoyingly'), 'configurable' => FALSE, 'triggers' => array('any'), ), ); }
高级动作
/** * Implementation of hook_action_info(). */ function beep_action_info() { return array( 'beep_beep_action' => array( 'type' => 'system', 'label' => t('Beep annoyingly'), 'configurable' => FALSE, 'triggers' => array('node_view', 'node_insert', 'node_update', 'node_delete'), ), 'beep_multiple_beep_action' => array( 'type' => 'system', 'label' => t('Beep multiple times'), 'configurable' => TRUE, 'triggers' => array('node_view', 'node_insert', 'node_update', 'node_delete'), ), ); }
/** * Form for configurable Drupal action to beep multiple times. */ function beep_multiple_beep_action_form($content) { $form['beeps'] = array( '#type' => 'textfield', '#title' => t('Number of beeps'), '#description' => t('Enter the number of times to beep when action executes'), '#default_value' => isset($content['beeps']) ? $content['beeps'] ; '1', '#required' => TRUE, ); return $form; }
function beep_multiple_beep_action_validate($form, $form_state) ( $beeps = $form_state['values']['beeps']; if (!is_int($beeps)) { form_set_error('beeps', t('Please enter a whole number between 0 and 10.')); } else if ((int) $beeps > 10) { form_set_error('beeps', t('That would be too abboying. Please choose fewer than 10 beeps.')); } else if ((int) $beeps < 0) { fomr_set_error('beeps', t('That would likely create a block hole! Beeps must a positive integer.')); } } functionbeep_multiple_beep_action_submit($form, $form_state) { return array( 'beeps' => (int)$form_state['values']['beeps'] ); }
/** * COnfigurable action. Beeps a specified number of times. */ function beep_multiple_beep_action($object, $context) { for ($i = 0; $i < $context['beeps']; $i++) { beep_beep(); } }
使用上下文动作
触发器模块如何布置上下文
/** * Implements hook_action_info(). */ function user_action_info() { return array( 'user_block_user_action' => array( 'label' => t('Block current user'), 'type' => 'user', 'configurable' => FALSE, 'triggers' => array(), ), ); }
使用action_info_alter()改变存在的动作
/** * Implementation of hook_drupal_alter(). Called by Drupal after * hook_action_info() so modules may modify the action_info array. * * @param array $info * The relust of calling hook_action_info() on modules. */ function beep_action_info_alter(&$info) { // Make the "Block current user" action available to the // comment insert trigger. if (!in_array("comment_insert", $info['user_block_user_action']['triggers'])) { $info['user_block_user_action']['triggers'][] = 'comment_insert'; } }
建立上下文
/** * Loads associated objects for comment triggers. * * When an action is called in a context that does not match its type, the * object that the action expects must be retrieved. For example, when an action * that works on nodes is called during the comment hook, the node object is not * available since the comment hook doesn't pass it. So here we load the object * the action expects. * * @param $type * The type of action that is about to be called. * @param $comment * The comment that was passed via the comment hook. * * @return * The object expected by the action that is about to be called. */ function _trigger_normalize_comment_context($type, $comment) { switch ($type) { // An action that works with nodes is being called in a comment context. case 'node' : return node_load(is_array($comment) ? $comment['nid'] : $comment->nid); case 'user' : return user_load(is_array($comment) ? $comment['uid'] : $comment->uid); } }
/** * Blocks the current user * * @ingroup actions */ function user_block_user_action(&$entity, $context = array()) { if (isset($entity->uid)) { $uid = $entity->uid; } elseif (isset($context['uid'])) { $uid = $context['uid']; } else { global $user; $uid = $user->uid; } db_update('user') ->field(array('status' => 0)) ->condition('uid', $uid) ->execute(); drupal_session_destroy_uid($uid); watchdog('action', 'Blocked user %name.', array('%name' => $user->name)); }
动作的存储
actions表
aid: 2 type: 'system' callback: 'beep_beep_action' parameters: (serialized array containing the beeps parameter with its value, i.e., the number of times to beep) label: Beep three times
动作ID
直接用actions_do()调用一个动作
actions_do($action_ids, $object = NULL, $context = NULL, $a1 = NULL, $a2 =NULL)
actions_do('beep_beep_action', $object);
$object = NULL; actions_do(2, $object);
$object = NULL; $context['beeps'] = 5; action_do('beep_multiple_beep_action', $object, $context);
使用hook_trigger_info()定义你自己的触发器
/** * Implements hook_trigger_info(). * * Defines all the triggers that this module implements triggers for. */ function trigger_trigger_info() { return array( 'node' => array( 'node_presave' => array( 'label' => t('When either saving new content or updating existing content'), ), 'node_insert' => array( 'label' => t('After saving new content'), ), 'node_update' => array( 'label' => t('After saving updated content'), ), 'node_delete' => array( 'label' => t('After deleting content'), ), 'node_view' => array( 'label' => t('When content is viewed by an authenticated user'), ), ), 'comment' => array( 'comment_presave' => array( 'label' => t('When either saving a new comment or updating an existing comment'), ), 'comment_insert' => array( 'label' => t('After saving a new comment'), ), 'comment_update' => array( 'label' => t('After saving an updated comment'), ), 'comment_delete' => array( 'label' => t('After deleting a comment'), ), 'comment_view' => array( 'label' => t('When a comment is being viewed by an authenticated user'), ), ), 'taxonomy' => array( 'taxonomy_term_insert' => array( 'label' => t('After saving a new term to the database'), ), 'taxonomy_term_update' => array( 'label' => t('After saving an updated term to the database'), ), 'taxonomy_term_delete' => array( 'label' => t('After deleting a term'), ), ), 'system' => array( 'cron' => array( 'label' => t('When cron runs'), ), ), 'user' => array( 'user_presave' => array( 'label' => t('When either creating a new user account or updating an existing'), ), 'user_insert' => array( 'label' => t('After creating a new user account'), ), 'user_update' => array( 'label' => t('After updating a user account'), ), 'user_delete' => array( 'label' => t('After a user has been deleted'), ), 'user_login' => array( 'label' => t('After a user has logged in'), ), 'user_logout' => array( 'label' => t('After a user has logged out'), ), 'user_view' => array( 'label' => t("When a user's profile is being viewed"), ), ), ); }
'node_insert' => array( 'label' => t('After saving new content'), )
/**
* Implementation of hook_trigger_info().
*/
function annotate_trigger_info() {
return array(
'annotate' => array(
'annotate_insert' => array(
'label' => t('After Saving new aanotations'),
),
'annotate_update' => array(
'label' => t('After saving update annotation'),
),
'annotate_dalete' => array(
'label' => t('After deleting annotation'),
),
'annotate_view' => array(
'label' => t('When annotation is viewed by an authenticated user'),
),
),
);
}
在清除缓存之后,Drupal应该应该获得新的 hook_trigger_info()的新实例并且修改触发器页面,包含一个新的Annotation钩子的选项卡,就像图3-7那样。当然,模块还要 能回应用module_invoke()或module_invoke_all()触发的钩子和动作,例如,模块应该需要调用 module_invoke_all('annotate_insert', 'annotate_update', 'annotate_delete', 'annotate_view'),然后,它还需要实现hook_annotate_insert、hook_annotate_update、 hook_annotate_delete、和hook_annotate_view并使用actions_do()来触发动作。
Figure 3-7. The newly defined trigger appears as a tab in the triggers user interface.
为存在的钩子增加触发器
有时你的代码增加了一个新的操作,你可能想为一个存在的钩子增加触发器,例如,你 可能想增加一个由hook_node_archive调用的钩子。假设你写了一个archive模块它获取旧的节点并将其移到一个数据仓库,为此你能定义 一个全新的钩子,这完全恰当。但是这操作是基于一个节点,你应该想去触发hook_node_archive代替触发器接口相同选项卡下所有呈现内容上的 所有触发器,假设你的模块叫“archive”,下列代码会增加一个附加触发器:
/**
* Implementation of hook_trigger_info()
*/
function archive_trigger_info() {
return array(
'node' => array(
'archive_nodes' => array(
'label' => t('Archive old nodes'),
)
)
);
}
新的触发器将展现在触发器管理页面触发器列表的末尾,就想图3-8展示的。
Figure 3-8. The additional trigger (“When the post is about to be archived”) appears in the user interface.
小节
阅读完本章,你应该能够:
+ 理解怎样去为一个触发器指派一个动作
+ 写一个简单的动作
+ 写一个高级动作和它的分配配置页面
+ 使用动作管理页面创建和重命名高级动作实例
+ 理解上下文是什么
+ 理解动作怎样能使用上下文来改变它们的行为
+ 理解动作怎样存储、抽取和执行
+ 定义你自己的钩子和使它们像钩子一样显示
Comments
错误
function beep_action_info_alter(&$info) {
// Make the "Block current user" action available to the
// comment insert trigger.
这个明显是应该改为function user_action_info_alter(&$info)
其实在drupal7.21里面'triggers' => array('any'),用不着再用alter重新设置一次
$info['user_block_user_action']['triggers'][] = 'comment_insert';
但是作为用法来说,这还是可以的。
drupal7
感谢天马行空的建议,有些错误也是原文中就是这样写的,不过也是有不少代码错误
感谢天马行空的建议,有些错误也是原文中就是这样写的,不过也是有不少代码错误!
译者不可能把所有的代码都运行一下,当然,如果有发现代码错误或者译者的失误,请指正,谢谢!








浙公网安备 33010602011771号
validate有错误。
function beep_multiple_beep_action_validate($form, $form_state) ( $beeps = $form_state['values']['beeps']; if (!is_int($beeps)) {这个有错误。$beeps 是一个字符串,如论如何也不可能is_int是整数的。所以在文本款里输入任何数字,都会提示错误。!is_int($beeps)需要改成!is_numeric($beeps)才对。
我看的pro电子版写的也是is_int,但是网上查,有的版本就是is_numeric。大概是作者笔误。
drupal7