Делаем стандартную форму регистрации в Друпал 7 многошаговой на AJAX Drupal, Drupal 7.x статьи | Frantsuzzz

Hook_node_access_records()

Данный хук записывает в базу информацию о правах доступа к ноде. В нём, мы будем определять, какой уровень доступа должен быть у пользователя, чтобы получить доступ к данному содержимому. В нашем случае, к его просмотру.

Hook_node_grants()

Данный хук отвечает за выдачу прав пользователю. При просмотре ноды, пользователь в зависимости от тех условий что мы задаем, получит определенный уровень доступа к содержимому. Т.е. в данном хуке мы определим уровни доступа.

В чём плюсы

  1. В отличии от блокировки доступа средствами Rules, текущий вариант железный. Нету роли – нету доступа.
  2. Можно гнуть как угодно. Любые условия, проверки и т.д. На что хватит фантазии.
  3. В результатах Views не будут отображаться ноды, доступа к которым не имеет пользователь. Их увидят только те, кто подходит под условия.

Всё не так уж и сложно. Главное уловить идею про выдачу уровней доступа и понимание придёт. Удачи в создании закрытых разделов 😉

Глазами open-id

Казалось бы давно решенная проблема. Есть же авторизация/регистрация по OpenID. В drupal встроена в ядро. Но пользователь, который только что вылез из ВКонтакте, скорее всего даже слов таких не слышал и пройдет мимо данной возможности.

Глазами программиста

Программистов тоже можно понять. Во-первых, спамеры, во-вторых, сеошники. Лезут во все дыры сайта, суют свои поганые ссылки на «энладж ё пенис» и «чиап лоан». Вот и вырастают заборы из кептч, проверок, анкет….

И на эти заборы кроме спамеров натыкаются и те, кто, возможно, собирается что то купить на сайте, а терпения на форму регистрации у них уже может не хватить.

Модуль в drupal

Вы можете создать новый модуль для друпал или использовать существующий для размещения следующих hook-ов и объявлений. Пусть модуль называется «loginza» для определенности.

Похожее:  Как перевести деньги с МТС на МТС - пополнение телефона с помощью перевода с другого номера

Создаём раздел сайта — вход для обработки данных авторизации

далее сам обработчик.

Небольшой кусочек сервисной библиотеки. Тут только ф-ция генерации пароля.

Нюансы

Имейте ввиду, что главный админ сайта будет обходить все эти проверки. Так что дебажить эти хуки не получится при помощи devel. Либо создайте пользователя с доступом к девелу, либо включите специальный для этого модуль Devel node access и вынесите соответствующий блок в видный регион. Он будет красиво, в табличке показывать, кто что может, а кто нет.

Подготовка сайта

Это по сути пошаговый туториал для начинающих. Если вы хорошо разбираетесь в коде, то и воссоздавать среду для тестов не придётся.

Я не особо хочу заморачиваться с подготовкой сайта, поэтому всё очень просто:

  1. Устанавливаем Drupal с профилем Standart.
  2. Типу содержимого Article добавляем поле: 1. Название: “Для своих”;
  3. Машинное имя: field_members_only;
  4. Тип: boolean;
  5. Виджет: Single onoff checkbox.
  6. Добавляем новую роль для пользователей: Members и располагаем после авторизованных пользователей.

Пояснения

Чтобы ещё больше внести ясности, вот небольшое пошаговое объяснение.

Практическая часть

Собственно код со всеми комментариями.

/**
 * @file
 * Здесь мы будем писать весь код.
 */

/**
 * Для начала объявим константы для более удобного контроля доступа.
 * Так как он весь завязан на цифрах от 0 и выше, то чтобы не запутаться, проще
 * вынести их в константы. Так будет намного читабельнее и яснее что делается.
 *
 * MYMODULE_ACCESS_REALM - название нашего "реалма" внутри которого будут
 * выдаваться права. Это что-то вроде машинного имени для наших уровней доступа
 * внутри которого отрабатывают наши условия.
 *
 * А также объявляем две константы с уровнем доступа:
 *   - MYMODULE_ACCESS_PUBLIC: которая равняется нулю, что в свою очередь
 *     является уровнем доступа для просмотра публичных материалов.
 *   - MYMODULE_ACCESS_PRIVATE: равняется единице, что будет соответстовать
 *     праву на просмотр скрытого содержимого.
 *
 * Цифры могут быть любыми, задаются на усмотрение. Но для понимания мы делаем
 * их в порядке увеличения. Чем выше цифра - тем больше прав.
 */
define('MYMODULE_ACCESS_REALM', 'mymodule_access_article');
define('MYMODULE_ACCESS_PUBLIC', 0);
define('MYMODULE_ACCESS_PRIVATE', 1);

/**
 * Используем hook_node_grants().
 *
 * Данный хук срабатывает при просмотре содержимого и выдаёт пользователю
 * соответствующий уровень доступа к содержимому.
 *
 * $account - информация о пользователе, который обратился к ноде.
 * $op - операция которая выполняется (view, edit, delete).
 */
function mymodule_node_grants($account, $op) {
  // Нас интересует лишь просмотр содержимого. Поэтому права мы выдаем именно
  // в момент просмотра содержимого. Редактирование и удаление будет ограничено
  // системными правами (что в админке друпала).
  if ($op == 'view') {

    // Теперь мы проверяем, имеет ли текущий пользователь роль 'Members'.
    // Т.е. условие может быть каким угодно, но в нашем случае, мы определяем
    // будет ли иметь доступ по роли.
    if (in_array('Members', $account->roles)) {
      // Наш пользователь имеет роль 'Members' и мы выдаем ему права на
      // просмотр публичного И приватного содержимого.
      // Если указать только права на приватное содержимое, то пользователь
      // не сможет увидеть публичное.
      $grants[MYMODULE_ACCESS_REALM] = array(
        MYMODULE_ACCESS_PUBLIC,
        MYMODULE_ACCESS_PRIVATE,
      );
    }
    else {
      // Ну а если у пользователя нету роли 'Members' то мы разрешаем смотреть
      // только публичные материалы.
      $grants[MYMODULE_ACCESS_REALM] = array(
        MYMODULE_ACCESS_PUBLIC,
      );
    }

    return $grants;
  }
}

/**
 * Используем hook_node_access_records().
 *
 * В данном хуке определяется, какой уровень доступа необходим для ноды.
 * Данная запись делется при редактированиидобавлении нового материала.
 *
 * Если у вас уже есть содержимое, которому нужно "пересобрать" права, то
 * воспользуйтесь фукнцией node_access_rebuild() или в админке:
 * admin/reports/status/rebuild
 */
function mymodule_node_access_records($node) {

  // Мы задаем права доступа только для нашего типа содержимого 'Article'.
  if ($node->type == 'article') {
    // Получаем значения поля "Для своих".
    $members_only = field_get_items('node', $node, 'field_members_only');

    // Если отмечено "Для своих".
    if ($members_only[0]['value']) {
      // Указываем ноде, что смотреть её могут пользователи только с gid
      // который отвечает за просмотр приватного содержимого.
      // Также обратите внимание что у обновления и удаления у нас стоят нули
      // так как мы выдаём лишь на просмотр.
      $grants[] = array(
        'realm' => MYMODULE_ACCESS_REALM,
        'gid' => MYMODULE_ACCESS_PRIVATE,
        'grant_view' => 1,
        'grant_update' => 0,
        'grant_delete' => 0,
        'priority' => 0,
      );
    }
    else {
      // Если не отмечено "Для своих", то мы открываем материал всем желающим.
      $grants[] = array(
        'realm' => MYMODULE_ACCESS_REALM,
        'gid' => MYMODULE_ACCESS_PUBLIC,
        'grant_view' => 1,
        'grant_update' => 0,
        'grant_delete' => 0,
        'priority' => 0,
      );
    }
  }

  return $grants;
}

Теоретическая часть

Все крутится вокруг двух хуков hook_node_grants() и hook_node_access_records().

Узкие места

Еще раз опишу узкие места. Они не являются неустранимыми, обращаю на них ваше внимание.

Не уникальность имени. В данном варианте код не может зарегистрировать двух пользователей с одинаковым именем. Это не удобно. Будет выдано соответствующее сообщение.Решение: Можно придумать какую то схему уникализации.

Авторизация по существующему в базе email. Программа доверяет данным из СС, и считает, что e-mail проверен на принадлежность пользователю. Это не всегда может быть так. Тогда злоумышленник, зная email админа, к примеру, регистрируется в какой то CC.

Добивается там нужного значения email — и дело сделано, мы имеем взлом аккаунта.Решение: Тут простой путь только один — проводить проверку e-mail, т.е. высылать проверочный код, а потом уже актуализировать авторизацию. Эх, а как не хочется напрягать юзеров лишний раз что то делать!

Шаг 1. подготовка

Этот этап нет смысла описывать подробно, поэтому вкратце:

Добавим две дополнительные роли на сайте:

  1. Физическое лицо
  2. Юридическое лицо

С помощью модуля Auto Assign Role разрешим пользователям выбирать свою роль при регистрации. На форме регистрации должы появится соответствующие радиокнопки. Установим и настроим модуль Ajax Login/Register. Установим и настроим модуль Legal.

Шаг 2. магия

Для начала заварим кружку горячего кофе и зарегистрируем адрес для ajax запроса. По этому пути будем обрабатывать форму после заполнения всех шагов. Если у Вас не установлен модуль Ajax Login/Register – то можно обойтись и без этого. 

/**
 * Implements hook_menu().
 */
function multistep_register_menu() {
  $items = array();
  $items['ajax_multistep_register'] = array(
    'page callback' => '_multistep_register_ajax_callback',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
    'delivery callback' => 'ajax_deliver',
    'theme callback' => 'ajax_base_page_theme',
  );
 return $items;
}

Ну а теперь начинаем делать магию. При помощи hook_form_FORM_ID_alter модернизируем форму регистрации. 

Шаг 3. ajax-обработчик формы.

Тут даже комментировать ничего не буду.

function multistep_register_ajax_callback($form, &$form_state) {
  return $form;
}

Шаг 4. дополнительный валидатор формы

У формы регистрации есть свой набор функций для проверки введенных значений.

Есть несколько вариантов, как реализовать валидацию формы в данном случае:

  1. Удалить стандартные валидаторы и написать один свой, который будет проверять значения в зависимости от текущего шага;
  2. Написать отдельные валидаторы для каждого шага и вешать их в hook_form_FORM_ID_alter;
  3. Написать валидаторы для полей и подцеплять их к полям;
  4. Использовать стандартные валидаторы формы добавить дополнительную проверку по шагам, если вдруг стандартных Вам не хватает.

Основная проблема заключается в том, что стандартные валидаторы работают со всей формой сразу, а у нас после каждого шага заполнен только определенный набор значений, остальные поля – пустые. 

Что бы решить эту проблему, будем перед проверкой дополнять массив $form_state[‘values’] значениями с предыдущих шагов. Конечно, для нашей формы поля Имя пользователя и E-mail обязательно должны быть на первом шаге, иначе, придется отключать стандартные проверки для начальных шагов.

function multistep_register_myvalidate($form, &$form_state) {
$current_step = empty($form_state['storage']['step']) ? 1 : $form_state['storage']['step'];
if ($current_step>1) {
for($i=1;$i<$current_step;$i  ){
	if(isset($form_state['storage']['values']['step'.$i])){
		$form_state['values']=array_merge($form_state['values'], $form_state['storage']['values']['step'.$i]);
	}
}
}
  
//Теперь добавим свою проверку в зависимости от текущего шага 
switch ($current_step) {
  case 1:	
	if (!preg_match("/^[0-9a-zA-Z-_] $/i", $form_state['values']['name'])) {
	  form_set_error('name', 'Недопустимые символы в поле "Имя пользователя"');
	}
	break; 
 }    
}

Шаг 5. обработчик формы

function multistep_register_form_submit($form, &$form_state) {
//Узнаем текущий шаг
$current_step = 'step' . $form_state['storage']['step'];
		
// Если перешли на следующий шаг - то увеличиваем счётчик шагов и
// сохраняем состояние формы, полученное при переходе на новый шаг.
if (isset($form['actions']['next']['#value']) && $form_state['triggering_element']['#value'] == $form['actions']['next']['#value']) {
    $form_state['storage']['step']  ;
// для сохранения текущих значений будет написана отдельная функция
	$form_state['storage']['values'][$current_step] = _multistep_register_update_values_step($form_state['storage']['fields_step'][$current_step], $form_state['values']);
  }
  
// Если вернулись на шаг назад - уменьшаем счётчик шагов.
//
if (isset($form['actions']['prev']['#value']) && $form_state['triggering_element']['#value'] == $form['actions']['prev']['#value']) {
    $form_state['storage']['step']--;
  }
$form_state['rebuild'] = TRUE;
}

Дополнительная функция, которая формирует массив значений полей только для текущего шага. 

function _multistep_register_update_values_step($fields, $values) {
$items=array();
foreach($fields as $name){
	if(isset($values[$name])){
		$items[$name]=$values[$name];
	}
}
return $items;
}

Шаг 6. добиваем popup

Уже сейчас все должно работать как задумано. Кроме совместимости с модулем Ajax Login/Register. У меня возникало много непонятных проблем, вот самый оптимальный вариант решения – переопределить ajax обработчик для кнопки Отправить.  Основа тут – Заставляем любую форму выполняться через AJAX в Drupal 7

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *