Создание ноды через кастомную форму с использованием модального окна CTools

Создание ноды через модальные окна CTools

Здравствуйте, товарищи! Наконец-то закончил с очередным проектом и теперь есть немного времени, чтобы поделиться опытом и рассказать о приемах, которые способствуют улучшению юзабилити сайта. Начну, пожалуй, с создания нод через собственные формы: как выводить и обрабатывать подобные формы в модальных окнах CTools, в блоках — и все это даже с использованием AJAX.

Сразу хочу сказать, что листинги кода будут представлены из реального проекта, поэтому не удивляйтесь непонятным константам и функциям с префиксом «GM_». По архитектуре: есть нода объявления ("advertisement"), на основании данных которой необходимо создать ноду сделки ("deal"). Поэтому представьте, что есть страница объявления, на которой выводится кнопка «Предложить сделку». По клику на эту кнопку инициализируется модальное окно CTools с формой создания ноды сделки. Задача ясна? Тогда поехали!

Создание Node c CTools

Я думаю, каждый разработчик ставил модуль CTools, однако далеко не все используют его API. Согласитесь, что в большинстве случаев этот модуль Drupal устанавливается потому, что этого требует Views. Сегодня же мы постараемся узнать об этом модуле чуточку больше, а именно о том, как работать с модальными окнами.

Создание CTools кнопки с AJAX

Для начала необходимо создать ссылку (или кнопку), по клику на которую будет начинаться вся магия. В моем случае эту ссылку я выводил через кастомный Views field, но об этом уже расскажу в рамках другой статьи. Сейчас достаточно знать следующее:

  1. // Подключение необходимых библиотек.
  2. ctools_include('ajax');
  3. ctools_include('modal');
  4.  
  5. // Подключение JS для модальных окон.
  6. ctools_modal_add_js();
  7.  
  8. // Создание пользовательского стиля, который будет использоваться для темизации модального окна.
  9. $sample_style = array(
  10. 'gm-offer-deal-ctools' => array(
  11. 'modalSize' => array(
  12. 'type' => 'fixed',
  13. 'width' => 500,
  14. 'height' => 'auto',
  15. 'addWidth' => 0,
  16. 'addHeight' => 0,
  17. ),
  18. 'modalOptions' => array(
  19. 'opacity' => .5,
  20. 'background-color' => '#000',
  21. ),
  22. 'closeText' => '',
  23. 'animation' => 'fadeIn',
  24. 'animationSpeed' => 500,
  25. ),
  26. );
  27.  
  28. drupal_add_js($sample_style, 'setting');
  29.  
  30. $link = ctools_modal_text_button(t('Offer a deal'), 'gm_ajax/gm_deal/nojs/offer/' . $node->nid,
  31. t('Offer a deal'), 'ctools-modal-gm-offer-deal-ctools');

Как вы уже успели заметить, данная ссылка стучится по адресу 'gm_ajax/gm_deal/nojs/offer/<NODE_ID>'. Что ж, самое время зарегистрировать данный путь в системе Drupal:

  1. /**
  2.  * Implements hook_menu().
  3.  */
  4. function gm_deal_menu() {
  5. $items['gm_ajax/gm_deal/%ctools_js/offer/%node'] = array(
  6. 'title' => 'Offer a Deal',
  7. 'page callback' => 'gm_deal_ctools_ajax_offer_deal',
  8. 'page arguments' => array(2, 4),
  9. 'access callback' => TRUE,
  10. 'file' => 'gm_deal.pages.inc',
  11. 'type' => MENU_CALLBACK,
  12. );
  13.  
  14. return $items;
  15. }

Создание ноды через собственную форму

Прежде чем перейти к описанию функции gm_deal_ctools_ajax_offer_deal() я, пожалуй, расскажу почему я тут не использовал drupal_get_form() с ID формы для создания ноды сделки. Это самая распространенная ошибка зеленых разработчиков, когда необходимо создать кастомную форму для создания ноды.

Есть два нюанса. Первый заключается в том, что необходимо подключить файл 'node.pages.inc', при чем подключить через $form_state, иначе рискуете тем, что данный файл отвалится при ошибках на валидации. Это равносильно тому, что пытаться подключить .js-файл к форме через drupal_add_js(), когда для этого есть $form[‘#attached’][‘js’] массив. В общем, для правильного подключения файла надо использовать следующую функцию:

  1. form_load_include($form_state, 'inc', 'node', 'node.pages');

Второй нюанс – это то, что для успешного создания ноды у формы в $form_state должен находиться объект этой самой ноды, который может быть передан через дополнительные аргументы в drupal_get_form(). Заглянув в эту функцию, вы поймете, что она есть не что иное, как надстройка над drupal_build_form(), которую я, собственно, и намереваюсь использовать.

Основным преимуществом использования drupal_build_form() является то, что вы можете формировать и передавать $form_state массив. Для начала листинг вспомогательной функции, которая формирует этот самый $form_state:

  1. /**
  2.  * Helper function.
  3.  *
  4.  * Вся магия использования вышеописанных колбэков находится здесь.
  5.  * Инициализируется форма добавления сделки включая данные выбранного объявления.
  6.  *
  7.  * @param $adv_node
  8.  * Нода объявления, на основе данных которого будет создана сделка.
  9.  * @return array
  10.  */
  11. function _gm_deal_custom_form_state_prepare($adv_node) {
  12. global $user;
  13. global $language;
  14.  
  15. $form_state = array();
  16.  
  17. // Тот самый нюанс #1.
  18. // Подключаем файл, который необходим для успешного создания ноды.
  19. form_load_include($form_state, 'inc', 'node', 'node.pages');
  20.  
  21. // Инициализируем объект ноды сделки.
  22. $node = new StdClass();
  23. $node->uid = $user->uid;
  24. $node->name = $user->name;
  25. $node->type = GM_DEAL_NODE_TYPE;
  26. $node->language = $language->language;
  27. $node->status = NODE_PUBLISHED;
  28. // Вы также можете сразу предопределить некоторые значения полей ноды.
  29. $node->{GM_DEAL_FIELD_STATE}[LANGUAGE_NONE][0] = array('value' => GM_DEAL_ACTIONS_STATE_AUTHOR_OFFER);
  30. $node->{GM_DEAL_FIELD_ADVERTISEMENT}[LANGUAGE_NONE][0] = array('node_vid' => $adv_node->vid);
  31.  
  32. // Тот самый нюанс #2.
  33. // Это станадртный прием. См. drupal_get_form.
  34. $form_state['build_info']['args'] = array($node);
  35.  
  36. // Мы можем добавить любые доп. данные, которые будут
  37. // использоваться в дальнейшем. Например, при альтере формы.
  38. $form_state['build_info']['advertisement'] = $adv_node;
  39. $form_state['build_info']['gm_deal_processing'] = TRUE;
  40.  
  41. return $form_state;
  42. }

Ну, а теперь самое время раскрыть весь секрет menu callback’a:

  1. /**
  2.  * Menu callback.
  3.  *
  4.  * @param $js
  5.  * @param $adv_node
  6.  * Нода объявления.
  7.  */
  8. function gm_deal_ctools_ajax_offer_deal($js, $adv_node) {
  9. // Подключаем библиотеки.
  10. ctools_include('modal');
  11. ctools_include('ajax');
  12.  
  13. // Формируем $form_state через вышеописанную функцию.
  14. $form_state = _gm_deal_custom_form_state_prepare($adv_node);
  15.  
  16. $form_state += array(
  17. 'title' => t('Offer a Deal'),
  18. 'ajax' => TRUE,
  19. );
  20.  
  21. // Обрабатываем форму. Своего рода надстройка CTools над drupal_build_form().
  22. $output = ctools_modal_form_wrapper(GM_DEAL_NODE_TYPE . '_node_form', $form_state);
  23.  
  24. // Если нода была успешно создана, выводим сообщение и доп. текст в том же модальном окне.
  25. if (!empty($form_state['executed'])) {
  26. $nid = $form_state['node']->nid;
  27. $link = ctools_ajax_text_button(t('link'), 'node/' . $nid, t('Go to page'), 'goto', 'link');
  28. $message = t('You can look it by the !link.', array('!link' => $link));
  29.  
  30. $output = array();
  31. $output[] = ctools_modal_command_display(t('Deal created'), '<div class="modal-message">' . $message . '</div>');
  32. $output[] = ajax_command_prepend('#modal-content', theme('status_messages'));
  33. }
  34.  
  35. print ajax_render($output);
  36. exit;
  37. }

AJAX-команды для работы с модальными окнами

Собственно, как это работает? Все ошибки валидации будут возвращаться вместе с формой в модальное окно. Если форма будет успешно добавлена, то вы увидите сообщение о только что созданной ноде, после чего сможете либо перейти по ссылке на страницу с нодой, либо же просто закрыть модальное окно.

CTools modal windows

Также вы можете изменить массив в AJAX-команд и добиться другого результата – например, закрытия окна и перезагрузки страницы при успешном выполнении формы:

  1. $output[] = ctools_modal_command_dismiss();
  2. $output[] = ctools_ajax_command_reload();

Вот такие возможности таит в себе модуль CTools. Согласитесь, что данная реализация выглядит «модно, современно, молодежно» с использованием AJAX’a, что должно сказаться на юзабилити вашего сайта. Еще добавлю, что модальные окна чудесно темизируются Bootstrap фреймворком, о котором писал в предыдущих материалах.

И чуть было не забыл: в упаковке CTools есть модуль 'ctools_ajax_sample', который мне в свое время и помог разобраться с API для создания вот таких вещей. Так что дерзайте!

Комментарии

Аватар пользователя Платон
Платон

Спасибо за статью и блог в целом!

Вопрос: как сделать через CTools модальное окно для подтверждения удаления материала (на странице редактирования материала). При этом редактирование материала происходит в переопределённом шаблоне (без оверлея) и доступно для пользователя, создавшего ноду?!

Нашёл кучу ссылок в гугле о том, как показать любую форму в модальном окне, но как показать не какую-то кастомную форму, а страницу удаления материала (node/[nid]/delete) — не очень понимаю.

Иными словами: пользователь нажимает "Изменить материал", кликает на кнопку (стандартную, друпаловскую) "Удалить" и вместо перехода на стандартную страницу node/[nid]/delete появляется модальное окошко с этой страницей. Жмём на "Удалить" — нода удаляется, окошко закрывается, происходит редирект на главную (или в профиль пользователя — без разницы), а если нажать на стандартную ссылку "Отмена" — то окошко просто закрывается (без редиректа).

Буду рад любой помощи :)

Аватар пользователя angarsky
angarsky
Для начала надо альтернуть форму редактирования материала и навесить AJAX на кнопку "Удалить", который будет вызывать модальное окно. В окно уже напихаете чего душе угодно. Однако может быть затык, что кнопка "Удалить" будет сабмитить форму - может проще ее будет вообще удалить и добавить текстовую ссылку вместо кнопки. Смотрите уже сами.
Аватар пользователя Вячеслав
Вячеслав

Семён, приветствую Тебя. Сделай, пожалуйста, подобный обзор темизации формы добавления нового значения для Field Collections (стоит неограниченное количество), которая появляется по адресу: ../field-collection/add/node/.. Это какой-то ужас, с этими коллекционными полями, а форма дефолтная у них убогая. Уже какой раз пытаюсь освоить и все не получает, весь буржуазный Интернет перелопачиваю каждый раз, стыкуя разрозненные знания по крупицам

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

                888                     
888
888
88888888 .d88888 888 888 .d88b.
d88P d88" 888 888 888 d88P"88b
d88P 888 888 Y88 88P 888 888
d88P Y88b 888 Y8bd8P Y88b 888
88888888 "Y88888 Y88P "Y88888
888
Y8b d88P
"Y88P"
Зарегистрируйтесь для добавления материалов без проверки.