一个动作,实际上就是Drupal做的一件事。以下是一些例子:
• 把一个节点推荐到首页
• 把一个节点从已发布状态改变到未发布状态
• 删除一个注册用户
• 发送一封email
上面的这些,全都是清晰定义好的任务,编程人员会发现,前面的列表实际上与PHP的函数间有某种相似性。例如,你可以通过调用includes/mail.inc中的drupal_mail()这个函数,来发送一封email。动作(action),有点类似于函数,因为实际上动作本身是多个函数的集成体。也就是说,动作是由那些Drupal能够调用的或是可以与事件进行松散耦合的函数所组成的。下面,让我们来看看trigger这个模块。
Trigger模块的用户界面
浏览至Administrator > Site building > Modules,然后启动trigger模块。进到Administrator > Site building > Triggers。你应该能看到与下图类似的界面:
请注意最上方的几个标签页,它们与Drupal的钩子完全对应!在上图中,我们实际上正在看的是nodeapi这个钩子中的操作(operation)。这些操作被给予了好听的名字,比如,”delete”这个操作,被称为”after deleting a post”。因此,所有的钩子中被允许的操作都被显示出来,同时,用户还可以给这些操作关联上一个动作,比如当某个操作发生时,执行“把节点推荐到首页”这个动作。所有的可以使用的动作都在”Choose an action”这个下拉列表中。
Note: 并非所有的动作都适用于所有的触发器,因为在某些情况下,有的动作没有任何意义。例如,你在删除某个节点后,通常不会去运行“推荐到首页”这个动作,因为这根本无意义。根据你的安装,有些触发器会显示“No actions available for this trigger”。
下表显示了触发器以及它们相应的钩子和操作的组合:
你自己开发的第一个动作(Action)
如果我们想把前面所写的让机器beeping一声的动作,添加入触发器中,有个步骤:
1. 通知Drupal我们所写的动作将支持哪个触发器
2. 创建Action的函数
其中,第一步是通过实现hook_action_info()这个钩子来达到的。下面是在我们的beep模块中实现的代码:
/**
* Implementation of hook_action_info().
*/
function beep_action_info() {
$info['beep_beep_action'] = array(
'type' => 'system',
'description' => t('Beep annoyingly'),
'configurable' => FALSE,
'hooks' => array(
'nodeapi' => array('view', 'insert', 'update', 'delete'),
'comment' => array('view', 'insert', 'update', 'delete'),
'user' => array('view', 'insert', 'update', 'delete', 'login'),
'taxonomy' => array('insert', 'update', 'delete'),
),
);
return $info;
}
方法的名称是beep_action_info(),这是因为正如其它的钩子实现,我们总是使用模块名称(beep)再加上钩子名称(action_info)来构成自己的方法名。方法将会返回一个数组,数组中的每一项对应了我们模块中的一个动作。在beep这个模块中,实际上现在只有一个action,因此在我们将要返回的数组中一共也只有一项。这一项的键名,也是它的key,是使用代表Action的那个函数的名称,在本例中,就是beep_beep_action()这个函数。事实上,当浏览代码的时候,如果能知道某个方法是一个Action,这样总是很方便的。所以我们在方法的最后加上了_action这样的后缀,因此使用了方法名beep_beep_action(),而非beep_beep()。
我们再仔细看一下返回的数组中的各个键名:
• Type: 这表示着你所构建的动作的类型,Drupal使用这个信息来对各种动作进行分类,这种分类最终被体现于触发器配置的用户界面里出现的各个下拉框下。取值的范围包括system, node, user, comment, 还有taxonomy。那么,如何决定我所写的Action是何种类型的呢?基本上这取决于你所开发的新动作将和什么对象打交道。如果这个动作将会和多种类型的对象打交道,那么就使用system类型。
• Description: 顾名思义,这是关于当前这个Action的描述信息。它将会被显示于触发器配置界面的下拉选择框内。
• Configurable: 这用于决定是否当前Action需要接收参数。
• Hooks: 在这个数组中,可以看到,键名就是钩子名称,而键值实际上是遍历了一遍这个钩子下的操作,这些操作会被当前所定义的这个Action所支持。Drupal正是使用了这里所定义的内容来决定在触发器页面上是否为某个触发器显示当前这个Action。
前面我们已经描述过了我们将要添加的Action,现在让我们来完成它:
/**
* Simulate a beep. A Drupal action.
*/
function beep_beep_action() {
beep_beep();
}
好吧,这看上去也不是很难,是吧?在继续之前,让我们删掉beep_user()和beep_nodeapi(),因为我们将要使用图型界面来进行配置,而不再使用直接的钩子实现。
把Action与触发器关联
现在让我们再一次来到administrator > site building > Triggers这个页面。如果你已经把上面说的每件事都按正确的方式完成了,那么你写的动作,应该会出现在这个界面中,如下图所示:
变更一个动作所支持的触发器
如果你想改变当前动作支持哪个触发器,你应该能在界面中进行相应的选择。例如,如果你把代码改成下面那样,那么”Beep”这个action就只支持”After deleting a post”这一个触发器了。
/**
* Implementation of hook_action_info().
*/
function beep_action_info() {
$info['beep_beep_action'] = array(
'type' => 'system',
'description' => t('Beep annoyingly'),
'configurable' => FALSE,
'hooks' => array(
'nodeapi' => array('delete'),
),
);
return $info;
}
支持所有触发器的动作
如果你不想限制你的Action只支持少数几个触发器,你可以声明你的Action将支持任意的触发器,代码如下:
/**
* Implementation of hook_action_info().
*/
function beep_action_info() {
$info['beep_beep_action'] = array(
'type' => 'system',
'description' => t('Beep annoyingly'),
'configurable' => FALSE,
'hooks' => array(
'any' => TRUE,
),
);
return $info;
}
更高级的Action操作
Drupal中事实上有两种Action,一种是支持参数的,另一种不支持。前面我们所讨论的”Beep”这个Action就没有使用任何参数。当它被执行的时候,系统beep一下,然后就结束了。这是一种比较简单的情况。然而,很多时间,我们会需要更复杂一些的功能。比如,发送email这样一个动作,我们需要知道接收者的email地址,还要知道邮件的标题是啥。像这样的动作,我们需要设置一个配置表单,并且我们把这样的动作称为高级的动作(advanced action),或者也可以称为可配置的动作。
简单的动作是不需要参数的,同时也不需要配置表单,并且系统自动激活(在trigger模块被激活后)。通过把hook_action_info()实现中的configurable键值设置为true,就可让Drupal知道刚刚所写的动作是一个高级的动作,同时还需提供一个表单,一个验证表单的方法以及一个表单的submit提交函数来配置这个action。下表列出了简单动作和高级动作之间的区别:
(
我这儿简单用中文翻译一下这张表哈:
SA代表简单Action,AA代表高级Action
SA不需要参数,AA需要
SA不需要配置用的表单,AA需要
SA设置完成后,系统会自动将其发布到trigger页面中,AA必须使用动作管理页面对其进行发布。
选SA时,把configure那个键设为false, AA则设为真
)
接下来让我们创建一个高级的动作,该动作可以beep多次。我们可以使用一个配置表单来注明到底要让它beep多少次。
首先,我们需要告诉Drupal当前正在创建的这个动作是需要被配置的,也就是configurable要设为true。让我们为beep_action_info()的返回数组中添加一项新值,用来表示一个新的动作:
/**
* Implementation of hook_action_info().
*/
function beep_action_info() {
$info['beep_beep_action'] = array(
'type' => 'system',
'description' => t('Beep annoyingly'),
'configurable' => FALSE,
'hooks' => array(
'nodeapi' => array('delete'),
),
);
$info['beep_multiple_beep_action'] = array(
'type' => 'system',
'description' => t('Beep multiple times'),
'configurable' => TRUE,
'hooks' => array(
'any' => TRUE,
),
);
return $info;
}
接着让我们快速检查一下在administrator > site configuration > Actions中,是否已经有相应的内容出现。很显然,如下图所示你能看到,我们所添加的高级动作,已经出现在了这个界面中的下拉框里:
现在,我们需要提供一个表单,这样管理员才可以选择到底每次要beep多少下。我们通过Drupal的表单API来定义一个或多个的字段来实现它。同时,我们也会给出关于表单验证和提交的函数。这些函数的名字是基于所定义的action的ID,也就是hook_action_info()方法中所给出的beep_multiple_beep_action,同时,按照惯例,我们需要在这个action的ID后面加上_from来构成一个表单的方法名,也就是beep_multiple_beep_action_form。Drupal的表单验证函数名通常是由表单名称再加上_validate来构成(beep_multiple_beep_action_validate),而提交函数名通常是由表单名称再加上_submit来构成(beep_multiple_beep_action_submit)。
/**
* Form for configurable Drupal action to beep multiple times.
*/
function beep_multiple_beep_action_form($context) {
$form['beeps'] = array(
'#type' => 'textfield',
'#title' => t('Number of beeps'),
'#description' => t('Enter the number of times to beep when this action executes.'),
'#default_value' => isset($context['beeps']) ? $context['beeps'] : '1',
'#required' => TRUE,
);
return $form;
}
function beep_multiple_beep_action_validate($form, $form_state) {
$beeps = $form_state['values']['beeps'];
if (!is_numeric($beeps)) {
form_set_error('beeps', t('Please enter a numeric value.'));
}
else if ((int) $beeps > 10) {
form_set_error('beeps', t('That would be too annoying. Please choose fewer than 10 beeps.'));
}
}
function beep_multiple_beep_action_submit($form, $form_state) {
return array(
'beeps' => (int) $form_state['values']['beeps']
);
}
上面的第一个函数描述了一个Drupal的表单。在这里我们只定义了一个单文本字段,这样的话,管理员就可以输入beep的次数。当管理员选择添加高级动作”Beep multiple times”时,Drupal就会展现给用户我们所创建的配置表单,以便让用户进行配置,就如下图所示:
Drupal本身为这个配置表单添加了一个Description的字段,这里面的值是可以更改的,并且会被用于替换默认的描述信息,也就是我们在beep_action_info这个方法里所定义的那个description。这一点很有用,是因为我们有可能会创建一个高级动作,并把description命名为“响两声”,然后可能再创建一个高级动作,并把description命名为“响五声”。这样的话,当我们想把一个动作同一个触发器相关联时,我们能很清楚的知道哪个动作代表什么意义,对管理员来说也就更容易清楚的知道动作的具体含义。
Tip: 这两个高级动作,“响两声”和“响五声”,可以被看作是Beep multiple times这个Action的两个实例。
上面所写的验证方法与任何Drupal中的表单验证方法是一样的(具体内容查看章节10),在这里,我们检查了用户是否真正输入了一个合法的数字,同时我们也检查了这个输入的数字是否超过一定范围。
对于高级动作的配置表单来说,它的提交方法的返回值是有些特殊的。它应当是一个由我们所感兴趣的字段作为键名的数组。这个数组中的值会在Action真正运行时生效。由于在上面的表单中,description这个字段是由系统自动管理的,因此在这里我们只需要返回我们提供的字段,也就是beep的次数这个字段。
最后,来写一下action本身的代码:
/**
* Configurable action. Beeps a specified number of times.
*/
function beep_multiple_beep_action($object, $context) {
for ($i = 1; $i < $context['beeps']; $i++) {
beep_beep();
}
}
你会注意到,这个动作接收了两个参数。$object和$context。这正是与前面我们所写的简单action的最大不同之处,前面的简单action是没有参数的。
NOTE: 事实上,简单的Action也可以接收到那些高级的需要配置的Action所能接收的参数。因为PHP本身会忽略那些没有在方法签名中声明过的参数,所以如果我们需要了解一下当前的上下文内容话,我们只需简单的把方法beep_beep_action()改为been_beep_action($object, $context)。所有的Action实际上都是使用相同的参数来调用的,只不过在接收时,高级Action使用了参数,而简单的Action不使用而已。
Taxonomy upgrade extras