УрокПишем плагин к Panels с помощью Ctools

Не так давно я очень плотно подружился связкой модулей Views + Panels + Page manager (входит в Ctools). Как оказалось, с их помощью можно много времени сэкономить на многих задачах. В добавок ко всему производительность отличная (выяснилось, что Panels работает быстрее, чем стандартные блоки ядра). Вдохновиться можно посмотрев скринкасты по работе с этой связкой. Живой пример могу показать на примере своей работы - footballtop.ru. Около 90% страниц построено именно на этой связке.

В общем, ближе к теме разговора. Передо мной стояла задача написать не совсем обычную форму голосования, со своими плюшками. Далее эту форму надо было закинуть через Panels в материалы, т.к. все ноды были уже переделаны с помощью панелей, и hook_node_view() уже не срабатывал. Саму реализацию голосования я оставлю за рамками статьи, а вот на написании плагина остановлюсь подробнее.

Первое, на что я бы хотел обратить внимание - это то, что для Panels плагины не пишутся. Они пишутся с помощью API модуля Ctools. Поэтому по сути, плагин пишется именно к Ctools (или с его помощью, как вам угодно), а далее он нам будет автоматически доступен при работе с Panels через веб интерфейс.

Итак, давайте пошагово пройдёмся по этапам создания плагина.

Шаг первый. Описываем папку с плагином.

Первым делом надо указать в какой папке будут храниться плагины. По традиции они хранятся в папке plugins внутри модуля. Папка указывается с помощью имплементирования в модуле хука hook_ctools_plugin_directory(). После описания папки, Ctools автоматически подхватит все плагины из неё.

/**
 * Implements hook_ctools_plugin_directory().
 */
function ИМЯМОДУЛЯ_ctools_plugin_directory($owner, $plugin_type) {
  if ($owner == 'ctools') {
    return 'plugins/' . $plugin_type;
  }
}

Здесь надо отметить, что плагины хранятся не сразу в директории plugins, а они ещё и по категориям разбиты разбиты. Вот список категорий:

  • access - плагины к уровням доступа. Позволяют указывать проверки, при которых пользователь получит или не получит доступ к содержимому.
  • arguments - плагины, описывающие возможные аргументы, которые можно получить из урла страницы.
  • cache - плагины, описывающие возможные механизмы кэширования.
  • content_types - плагины, которые выводят содержимое. Например, элементы материала, формы, произвольно заданное содержимое и т.д.
  • context - плагины, описывающих возможные контексты, которые могут быть использованы в настройках панелей или передаче их в другие сущности.
  • relationships - плагины, описывающие возможные взаимосвязи между сущностями.

Так как в моём случае надо было писать плагин по выводу содержимого, то он у меня хранился по адресу plugins/content_types/ftop_vote_widget.inc, где ftop_vote_widget - название плагина.

Шаг второй. Предоставляем информацию о плагине.

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

if (module_exists('ftop_vote')) {
  $plugin = array(
    'single' => TRUE,
    'title' => t('FootballTop Vote Widget'),
    'description' => t('Voting widget for footballtop.ru.'),
    'category' => t('Node'),
    'render callback' => 'ftop_vote_widget_content_type_render',
    'required context' => new ctools_context_required(t('Node'), 'node'),
  );
}

Таким образом я смог сказать Ctools, что мой плагин должен быть помещён в категорию Node, и для его подключения ему необходимо передать объект ноды из контекста (если в контекстах ноды не будет, то я не смогу выбрать этот плагин). Однако я использовал не все параметры в описании, которые доступны. Вот более полный список:

$plugin = array(
 
  // Название плагина, которое будет отображено при его выборе.
  'title' => t('Simplecontext content type'),
 
  // Описание плагина, которое будет отображено при его выборе.
  'description' => t('Simplecontext content type - works with a simplecontext context.'),
 
  // Конструктор.
  'content_types' => 'simplecontext_content_type',
 
  // Показывает, является ли плагин подтипом другого. В данном случае - не является.
  'single' => TRUE,
 
  // Название функции, которая будет создавать блок с данными.
  'render callback' => 'simplecontext_content_type_render',
 
  // Иконка, отображаемая при выборе плагина.
  'icon' => 'icon_example.png',
 
  // Контекст, который необходимо передать плагину для его работы.
  'required context' => new ctools_context_required(t('Simplecontext'), 'simplecontext'),
 
  // Форма с дополнительными настройками плагина.
  'edit form' => 'simplecontext_content_type_edit_form',
 
  // Административный заголовок модуля, который может быть изменён через веб интерфейс.
  'admin title' => 'ctools_plugin_example_simplecontext_content_type_admin_title',
 
  // Создаёт блок предпросмотра результата в Panels.
  'admin info' => 'ctools_plugin_example_simplecontext_content_type_admin_info',
 
  // Категория, в которую будет помещён плагин, и его вес.
  'category' => array(t('CTools Examples'), -9),
 
  // Контексты, передаваемые по умолчанию.
  'defaults' => array(),
);

Шаг третий. Реализуем функции из описания плагина.

В моём это только одна функция - ftop_vote_widget_content_type_render(), которая с помощью полученных в Panels данных подгружает форму с голосованием:

/**
 * Render the custom content type.
 */
function ftop_vote_widget_content_type_render($subtype, $conf, $panel_args, $context) {
 
  // На всякий случай, если вдруг контекст был, плагин включили, а потом контекст убрали.
  if (empty($context) || empty($context->data)) {
    return;
  }
 
  // Забираем нашу ноду из контекста.
  $node= $context->data;
 
  // Создаём блок с формой и передаём ей ноду.
  $block = new stdClass();
  $block->module  = 'ftop_vote';
  $block->content = drupal_get_form('ftop_vote_form', $context->data);
  return $block;
}

Эта функция получает следующие параметры:

  • $subtype - используемый подтип. В моём случае его нет, поэтому будет передано 'ftop_vote_widget'.
  • $conf - массив с пользовательскими настройками плагина. По умолчанию здесь может быть только заголовок блока, но если вы описывали форму с дополнительными настройками - то они окажутся в этом массиве.
  • $panel_args - аргументы, которые были переданы в Panels в их необработанном виде.
  • $context - контексты, который (или которые) выбрал пользователь для передачи в плагин.

Вот, собственно, и всё. В итоге плагин с описанием директории и его самого у меня занял ровно 36 строк кода (и это вместе с комментариями и отступами!). Совсем немного, для подключения модуля к такой машине, как Panels, не так ли ? :)

Комментарии

Аватар пользователя itsanti
itsanti написал:

хорошо было бы почитать про связку Views + Panels + Page manager подробнее. так сказать "на пальцах". всегда делал шаблоны через regions+block. скринкаст на node 1 смотрел, как раз по ним и понял, что связка довольно мощная. не планируешь написать парочку статей по теме? может даже на примере сайта footballtop.ru

02.05.2012 01:36
Аватар пользователя Spleshka
Spleshka написал:

Так а что писать-то про неё? nodeone шикарно раскрывают всё в скринкастах. Можно только рабочие примеры показывать, не более того. Или есть особые пожелания?

02.05.2012 01:45
Аватар пользователя itsanti
itsanti написал:

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

02.05.2012 18:49
Аватар пользователя Sava
Sava написал:

Скринкаст больше не доступен :(
Может переложили куда? Есть ссылки?
Буду признателен :)

26.04.2013 14:23
Аватар пользователя Sava
Sava написал:

Прошу прощения, был не совсем внимателен.
Рабочие ссылки на скринкаст есть в блоке слева, а в теле новости ссылки таки битые.

26.04.2013 14:30
Аватар пользователя xandeadx
xandeadx написал:

> Как оказалось, с их помощью можно много времени сэкономить на многих задачах

например?

> выяснилось, что Panels работает быстрее, чем стандартные блоки ядра

цифры?

02.05.2012 01:47
Аватар пользователя Spleshka
Spleshka написал:

Пример - http://www.footballtop.ru/players/lionel-messi. На этой связке не только вывод всего содержимого ноды, но и содержимое остальных вкладок, которые к этой ноде относятся. Настраивается за час совместно с настройкой вьюсов для этих вкладок и хлебных крошек к странацам.

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

02.05.2012 02:18
Аватар пользователя kalabro
kalabro написала:

Сайт по общим ощущениям действительно работает быстро!

По поводу голосований было бы интересно почитать. Я вижу Voiting API и FLAG как 2 стороны вопроса. У вас ни того, ни другого не спалила.

02.05.2012 13:23
Аватар пользователя Spleshka
Spleshka написал:

Зря не спалили) У меня голосование как раз на Voting API построено.

02.05.2012 21:45
Аватар пользователя kalabro
kalabro написала:

ну его не особо видно, форма и форма :)

02.05.2012 22:46
Аватар пользователя Spleshka
Spleshka написал:

Правильно, обёртка-то моя. А вот API чужой =)

03.05.2012 14:42
Аватар пользователя ice
ice написал:

большое спасибо за труд , последние три статьи очень вовремя вышли ).

11.05.2012 20:16
Аватар пользователя &amp;nbsp;
&nbsp; написал:
/**
 * Implements hook_ctools_plugin_directory().
 */
function ИМЯМОДУЛЯ_ctools_plugin_directory($owner, $plugin_type) {
  if ($owner == 'ctools') {
    return 'plugins/' . $plugin_type;
  }
}

Это в отдельном модуле , в all/modules ?

Пример полный , работать будет при воспроизведении ?

15.05.2012 16:11
Аватар пользователя Spleshka
Spleshka написал:

Да, в отдельном модуле.

Пример не полный, я в начале об этом писал. Но если поменять форму ftop_vote_form на любую другую, то работать будет.

16.05.2012 01:05
Аватар пользователя Ch
Ch написал:

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

22.05.2012 08:50
Аватар пользователя Spleshka
Spleshka написал:

Это не совсем те плагины, которые я имел ввиду. Но замечание ценное, а то могут решить что вообще плагинов больше нет к панелям :)

22.05.2012 10:44
Аватар пользователя chegor
chegor написал:

Небольшой офтоп.
А как, если не секрет, вы реализовывали рейтинг игрока http://www.footballtop.ru/players/lionel-messi в смысле снятия ежедневных данных по ноде и переносу на график.

11.01.2013 18:42
Аватар пользователя Валентин777
Валентин777 написал:

Сделай пожалуйста урок который научил бы делать такое же меню как на этом сайте footballtop.ru

25.08.2013 04:18
Аватар пользователя Mr.Miltonn
Mr.Miltonn написал:

Я создал плагин, все получилось. Но! Теперь я хочу создать еще одни.
Для этого создаю для него файлик $user_profile.inc, который находится в "plugins/content_types". В этом файле описываю плагин по инструкции. Но он не появляется там где должен.

То есть 1-й созданный плагин работает как нужно, 2-й созданный плагин в том же модуле вообще не появляется. Почему его может не быть?
Как правильно создавать в одном модуле несколько плагинов?

05.02.2014 18:44
Аватар пользователя Mr.Miltonn
Mr.Miltonn написал:

Уже разобрался. Проблема была в имени. Почему-то нельзя называть файл с плагином вот так - "user_profile.inc"

05.02.2014 19:32
Аватар пользователя Сергей123
Сергей123 написал:

Зашел на footballtop.ru Так как у меня заблокированы скрипты меня перенаправили на страницу http://www.footballtop.ru/badbrowser.html. ок. Временно разрешил скрипты на этом сайте. Нажимаю "обновить страничку" И... ничего не меняется Пока принудительно не догадался вырезать из адреса badbrowser.html. Автор, по моему вы теряете с таким подходом посетителей. Не многие буду разбираться почему у них не открывается сайт. А после обнвления страницы badbrowser.html сайт сам должен перенаправлять на главную в случае если js уже включили. Это просто мой совет

ps
А тут и капчу с первого раза не введешь... Как надежно все запечатано :)

pps
Дубль три при попытке ввести коммент.
Теперь оказывается нельзя потому что "Пользователь с таким именем уже есть" тяжело к вам достучаться :)

22.11.2015 23:54

Комментировать