Drupal 8: Tour API

Drupal 8 Tour API позволяет создавать интерактивные туры по административным страницам, предоставляя редакторам наглядные обучающие подсказки.

04.11.2015
1 комментарий
6 мин.

В Drupal 8 появился новый Tour API. Это совершенно новый функционал для Drupal, следовательно, в 7-й версии его не было, но были варианты реализации с помощью контрибов, но их подход в корне отличается от того что мы видем в 8-ке.

Tour API - это небольшой фукнционал для создания "туров" по страницам, в основном административным. Другими словами, этот API позволяет нам создавать обучающие подсказки на необходимых страницах, тем самым избавляя от необходимости где-то указывать это отдельно, если это можно описать в двух словах. Это и наглядно, и быстро. При этом не нужно искать в интернете, достаточно нажать соответствующую кнопку.

Вот пример тура из Views.

Сделано это всё при помощи jQuery плагина под названием Joyride. Но вся прелесть в том, что нам не нужно использовать его API, подключать js и т.д. Библиотека находится в ядре друпала и обёрнута в соответствующий API, который и называется Tour. Всё это смешали с повсеместным использованием yaml файлов, и получилось достаточно просто и удобно. Создать свои туры можно совершенно не владея навыками программирования, достаточно лишь следовать инструкции и немного понимать устройство HTML (что такое id).

Теория

Как я уже сказал выше, туры объявляются в yaml файлах и конфигурации данного API имеют следующий паттерн: module.type.id.yml . Стоит обратить внимание, что это паттерн Tour, а не вашего модуля, следовательно название файла тура должно быть следующим: tour.tour.MY-CUSTOM-TOUR-NAME.yml . Т.е. мы лишь указываем название нашего тура, и всё, этого достаточно. Особое внимание обратите на то, что допускаются дифисы, но не нижнее подчеркивание.

Листинг у туров следующий:

# Идентификатор тура.
id: MY-CUSTOM-TOUR-NAME
# Модуль, который объявляет тур.
module: mymodule
# Небольшой заголовок (название) нашего тура. Фигуриует лишь в данном файле.
# В дальнейшем планируется появление Tour UI, следовательно там оно будет
# задействовано.
label: 'My custom tour'
# Язык тура. В случае 'en' всё содержимое шагов тура будет переводимо.
langcode: en
# Маршруты на которых будет доступен наш тур.
routes:
  - route_name: mymodule.foo
  - route_name: mymodule.bar
# Зависимости. Здесь указываются модули которые необходимы для данного тура.
dependencies:
  module:
    - myanothermodule
# Непосредственно наши подсказки.
tips:
  # Уникальный идентификатор подсказки в пределах данного тура.
  my-tip-1:
    # То же самое что и выше, дублируется.
    id: my-tip-1
    # Указывает на то, что данная подсказка имеет тип "текст" (html).
    # В ядре находится только данный тип подсказки, но по всей видимости оно
    # расширяется или будет расширяться через кастом.
    plugin: text
    # Заголовок для подсказки. Будет выведено в виде h3 элемента подсказки.
    # Обратите внимание что строковые значения оборачиваются одинарными (!)
    # кавычками, а также то, что эта строка будет переводима, не нужно писать
    # на русском языке, или укажите langcode: ru.
    label: 'My custom tip'
    # Содержимое подсказки. Не нужно оборачивать его тегом <p>, так как он
    # добавляется автоматически и вложенность может вызывать проблемы с
    # отображением.
    body: 'Hello <strong>World</strong>!'
    # Позиция подсказки относительно элемента к которому применяется.
    # Доступные значения: left, right, top, bottom (по-умолчанию bottom)
    location: left
    # Вес подсказки. Так как они выводятся поочередно, то вес и создает данную
    # очередь.
    weight: 1
    # Аттрибуты подсказки. В базовом плагине имеются всего два аттрибута:
    # data-id и data-class, они отвечают за выборку для тултипа. Если ни один
    # из них не указать (т.е. вообще не указывать attributes), то будет показана
    # модальная подсказка (по центру страницы).
    attributes:
      # HTML ID элемента к которому будет применена подсказка, знак #
      # опускается. В данном случае подсказка будет показана к элементу
      # id="edit-button".
      data-id: edit-button
      # То же самое что и с id. Единственное отличие, что тут допустм ввод
      # более сложных селекторов, например a[href^="google"]
      data-class: my-button-class
  my-tip-2:
    ...
  my-tip-3:
    ...

Практика

Чтобы нам особо не мучиться с подготовкой, давайте возьмем заготовку для модуля, который будет добавлять тур на страницу '/admin/config/system/site-information' (Информация о сайте). Эта страничка из ядра, и на ней достаточно обьемная форма, поэтому она удачно сойдет за пример, так как на ней отсутствует тур.

Всё что нам нужно, это модуль в базовой реализации, и файлик тура. Файлы туров являются yml файлами, и по сути конфигурациями, поэтому они должны находиться в папке config, и в подпапке optional. Там существуют разные категории конфигураций, install, schema, optional, но в данном случае нам нужен именно optional (эта инфа уже не из даной темы, если интересно можете погуглить Configuration API, об этом я напишу позже). И в этой папке мы создаём файл тура по следующему шаблону: tour.tour.[tour-name].yml . Допустим наш тур будет называться site-information-help, следотвательно, должна получиться такая структура:

helloworld (модуль)
- config
-- optional
--- tour.tour.site-information-help.yml

Далее дело за малым, написать наш тур.

id: site-information-help
module: helloworld
# Обратите внимание как нужно выводить ' внутри строки.
label: 'Site''s information tour'
# В данном случае мы используем русский чтобы писать подсказки сразу ну русском
# в случае с языком en они станут переводимы - и надо будет писать на
# английском.
langcode: ru
routes:
  # /admin/config/system/site-information
  - route_name: system.site_information_settings
# Так как эту страницу добавляет модуль system (там же я и посмотрел название
# роута для страницы), то нам надо установить зависимость.
dependencies:
  module:
    - system
tips:
  greetings:
    id: greetings
    plugin: text
    label: 'Привет! Это настройки сайта.'
    body: 'В данном разделе вы можете изменить базовые настройки сайта.'
    weight: 1
  site-name:
    id: site-name
    plugin: text
    label: 'Название сайта'
    body: 'В данном поле вы указываете название вашего сайта, оно будет выводиться в шапке сайта, а также в заголовке окна браузера.'
    weight: 2
    attributes:
      data-id: edit-site-name
  site-slogan:
    id: site-slogan
    plugin: text
    label: 'Слоган сайта'
    body: 'В данном поле можно написать как слоган сайта, так и слоган компании. Поведение данного поля зависит от темы.'
    weight: 3
    attributes:
      data-id: edit-site-slogan
  site-mail:
    id: site-mail
    plugin: text
    label: 'E-mail адрес сайта'
    body: 'Данный электронный адрес будет использоваться в качестве отправителя всех писем с сайта по умолчанию. Также, на этот адрес будут высылаться оповещения об обновлениях и прочие письма.'
    weight: 4
    attributes:
      data-id: edit-site-mail
  site-frontpage:
    id: site-frontpage
    plugin: text
    label: 'Главная страница сайта'
    body: 'В данном поле вы можете указать относительный путь до страницы, которая будет использоваться в качестве главной. Вы также можете оставить данное поле пустым, чтобы главная страница была стандартной.'
    weight: 5
    attributes:
      data-id: edit-site-frontpage
  error-pages:
    id: error-pages
    plugin: text
    label: 'Страницы об ошибках'
    body: 'В этом разделе настраиваются страницы об ошибках 403 и 404. Если указаны пути, то на них будет произвдена переадресация в случае конкретной ошибки.'
    weight: 6
    attributes:
      data-id: edit-error-page
  save:
    id: save
    plugin: text
    label: 'Сохранение настроек'
    body: 'Если вы внесли какие-либо изменения на данной странице, вам необходимо сохранить их используя данную кнопку.'
    weight: 7
    attributes:
      data-id: edit-submit
  goodbye:
    id: goodbye
    plugin: text
    label: 'Спасибо за внимание'
    body: 'На этом тур завершен, спасибо за внимание.'
    weight: 8

Теперь нам надо включить наш модуль. В случае, если всё сделали правильно, на странице настроек сайта появиться кнопка тура:

Ну и при нажатии у нас запуститься наш тур:

Вот так просто делаются туры без единой строчки кода.

Возможные проблемы

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

Решение всё же имеется - Configuration Manager. Оно не идеальное, но работает. Для этого нам надо активировать наш модуль чтобы тур импортировался успешно и был внесен в конфигурации. Далее заходим на страницу экспорта одиночной (! admin/config/development/configuration/single/export) конфигурации (не всех что есть на сайте) и выбираем наш тур:

Ниже появиться экспорт нашего тура. Копируем содержимое. Оно отличается от оригинального только тем, что там добавлен UUID, поэтому удалять его не стоит. Затем правим наш тур в любом удобном редакторе, копируем содержимое файла (а не сам файл) и импортируем:

И когда всё доделаете, изменения переносим уже в оригинальный конфиг файл из config/optional.

Да, это не очень удобно, поэтому делается Tour UI. Но и надо заметить, делать туры не так сложно, и постоянно проверять что получилось тоже нет смысла. Поэтому их можно написать с первого раза и такие пляски вовсе не потребуются. Разве что на первое время, чтобы понять как это устроено.

На drupal.org был еще метод через синхронизацию конфигураций, но он не работает. Почему-то не распознает конфигурацию, даже с UUID меткой.

На этом всё, спасибо за внимание.

Ссылки

Drupal
Drupal 8
Tour API

Комментарии

Bohdan Vasiliuk   чт, 28/11/2019 - 11:58

Удобно использовать для апдейта конфігов - https://www.drupal.org/project/config_update