ertttert
Books-CMS.clan.su

Использование контекста в действиях

Итак, мы знаем, что сигнатура функций для действий имеет вид example_action($object, $context). Рассмотрим каждый из этих параметров.

  • $object. Многие действия работают с одним из встроенных объектов Drupal: с нодами, пользователями, таксонометрическими терминами и т.д. При выполнении действия модулем trigger.module нужный объект передается действию в параметре $object. Например, если действие предназначено для выполнения при создании новой ноды, параметр $object будет содержать объект ноды.
  • $context. Действие может быть выполнено во многих различных контекстах. Действия объявляют поддерживаемые ими триггеры с помощью определения ключа hooks в функции hook_action_info(). Но действиям, поддерживающим несколько триггеров, нужен способ определения контекста, в котором они вызваны, чтобы по-разному выполняться в зависимости от этого контекста.

Подготовка контекста модулем триггера

Предположим, что имеется веб-сайт, на котором возможны взаимоисключающие варианты. Вот один из коммерческих примеров: пользователи проходят платную регистрацию и могут оставлять на веб-сайте только один комментарий. После помещения одного комментария они блокируются и должны снова заплатить для разблокировки. Не задумываясь над экономическими перспективами такого сайта, рассмотрим, как можно реализовать необходимые триггеры и действия. Нам понадобится действие, блокирующее текущего пользователя. Просмотрев модуль user.module, можно увидеть, в Drupal такое действие уже есть:

/**
* Реализация 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(),
 ),
);
}

Однако это действие не видно на странице назначения триггеров, т.к. оно не объявляет ни одного поддерживаемого хука: triggers представляет собой просто пустой массив. Хорошо бы найти способ изменить это. И такой способ есть.

Изменение существующих действий с помощью drupal_alter()

При выполнении хука action_info Drupal позволяет каждому модулю не только объявить предоставляемые им действия, но и изменить эту информацию — в том числе и информацию, предоставляемую другими модулями. Вот как можно сделать действие Block current user (Блокировка текущего пользователя) доступным для триггера вставки комментария:

/**
* Реализация hook_drupal_alter(). Вызывается Drupal после вызова
* hook_action_info(), чтобы модули могли изменить массив action_info.
*
* @param array $info
* Результат вызова hook_action_info() для всех модулей.
*/
function beep_action_info_alter(&$info) {
// Сделать действие Block current user доступным
// для триггера вставки комментария.
if (!in_array("comment_insert", $info['user_block_user_action']['triggers'])) {
$info['user_block_user_action']['triggers'][] = 'comment_insert';
}
}

После этого действие Block current user станет доступным для назначения, как показано на рис. 3.6.

Формирование контекста

Когда назначено действие, текущий пользователь будет блокироваться после размещения нового комментария. Рассмотрим, что при этом происходит. Мы уже знаем, что Drupal уведомляет модули о возникновении определенных событий за счет запуска хука. В нашем случае это хук комментария. Конкретная выполняемая операция — insert (вставка), т.к. нужно реагировать на добавление нового комментария. Модуль триггера реализует хук комментария. Внутри такого хука выполняется запрос к базе данных, существуют ли действия, назначенные данному триггеру. База данных выдает информацию о назначенном нами действии Block current user (Блокировка текущего пользователя). Теперь модуль триггера готов к выполнению действия, которое имеет стандартную сигнатуру функции example_action($object, $context). Но тут возникает одна проблема. Действие, которое должно быть выполнено, имеет тип user (пользователь), а не comment (комментарий). Оно ожидает, что ему будет передан объект пользователя! В контексте хука комментария нужно вызвать действие пользователя, но хуку передается информация о комментарии, а не о пользователе. И как быть? Что произойдет, если модуль триггера определит, что действие нужно выполнить для пользователя, и загрузит объект $user, необходимый нашему действию? Вот код из модуля modules/trigger/trigger.module, который демонстрирует, как это происходит:

/**
* Загрузка связанных объектов для триггеров комментария.
*
* При вызове действия в контексте, который не соответствует его типу, должен
* быть извлечен объект, ожидаемый действием. Например, если хук комментария
* вызывает действие, обрабатывающее ноды, объект ноды недоступен, т.к. он не
* передается хуку комментария. Поэтому здесь загружается необходимый
* действию объект.
*
* @param $type
* Тип действия, которое должно быть выполнено.
* @param $comment
* Комментарий, передаваемый через хук комментария.
*
* @return
* Объект, нужный для действия, которое должно быть выполнено.
*/
function _trigger_normalize_comment_context($type, $comment) {
switch ($type) {
 // Действие для работы с нодами вызвано в контексте комментария.
 case 'node':
 return node_load(is_array($comment) ? $comment['nid'] : $comment->nid);
 // Действие для работы с пользователями вызвано в контексте комментария.
 case 'user':
 return user_load(is_array($comment) ? $comment['uid'] : $comment->uid);
}
}

При выполнении этого кода для нашего действия пользователя срабатывает второй вариант: загружается объект пользователя и затем выполняется действие пользователя. Информация, известная хуку комментария (например, тема комментария), передается действию в параметре $context. Обратите внимание, что действие ищет идентификатор пользователя сначала в объекте, потом в контексте, и затем переходит к глобальной информации в $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('users')
 ->fields(array('status' => 0))
 ->condition('uid', $uid)
 ->execute();
 drupal_session_destroy_uid($uid);
 watchdog('action', 'Blocked user %name.', array('%name' => $user->name));
}

Действия должны вести себя интеллектуально, поскольку они не очень-то знают, что происходит при их вызове. Поэтому лучшие действия выглядят просто и даже являются атомарными. Модуль триггера всегда передает текущий хук и действие в контекст. Эти значения хранятся в элементах $context['hook'] и $context['op']. Такой подход предлагает стандартный способ предоставления информации действию.

Всего комментариев: 0
Имя *:
Email *:
Код *:


Бесплатный конструктор сайтов - uCoz