Хуки-хухуки: Исключаем неактивных пользователей из списков 2.X

471
Как иногда начинают свой монолог неопытные стендаперы: «У всех в жизни было такое …», когда вывел на главную страницу сайта список пользователей, а там половина – серые тени новичков, не подтвердивших регистрацию. Давайте в качестве ещё одного примера по использованию хуков уберём их оттуда, оставив только реальных пользователей.

Хуки-хухуки: Исключаем неактивных пользователей из списков


Есть два способа это сделать по-простому.
1. Добавить одну строку в код дефолтного виджета «Новые пользователи» для фильтрации по признаку блокирования пользователя. Но это хак, который слетит при обновлении движка. Помнить об этом хаке и следить за обновлением файла очень не хочется.
2. Создать копию этого виджета и внести правку туда. Новый виджет при обновлении не слетит, но и обновлять его придётся вручную. Тоже не подходит.

Попробуем сделать всё без хаков движка.

Подготовка

Чтобы использовать хуки нужно иметь свой компонент для них. Если у вас уже такой есть – отлично. Если нет, можете себе создать компонент для хуков - мы всё будем делать на примере компонента из этого поста.

Включим «Требовать подтверждения e-mail при регистрации», зарегистрируем нового пользователя с ником «Новенький». Переключим стиль виджета на «Список». К Администратору добавился неподтверждённый Новенький, что логично, но нам бы хотелось его скрыть.
Хуки-хухуки: Исключаем неактивных пользователей из списков


Ищем событие для изменения списка пользователей

Начнём с выяснения, откуда в виджете берётся список пользователей и есть ли подходящее событие, чтобы его изменить.
Если вы ещё не знаете что такое события/хуки, для чего они нужны и как работают, то перед тем как читать этот пост дальше, прочитайте Что такое события и хуки (без PHP и с картинками)

Смотрим события отладкой

Используем простой и наглядный вариант поиска событий. В « Расширенной отладке» включим вывод в лог виджетов и в их фильтре напишем имя виджета «Новые пользователи». Также включим вывод событий в лог.
(В этом месте можете почитать Что такое фильтры в отладке и как ими пользоваться))
Фильтр по заголовку виджета
После обновления главной страницы демо увидим, во-первых, что из всех виджетов в логе остался только один наш, во-вторых, внутри виджета есть три события ‘profiles_list_filter’, ‘profiles_before_list’ и ‘render_widget_users_list_list’.
События в виджете
Третье событие нам не интересно – по названию видно, что там происходит рендеринг html-кода виджета. А вот первые два надо бы изучить…

Парой кликов включим трассировку событий хотя бы на три уровня и сразу увидим, что эти события возникают в строках 20 и 67 нашего виджета /system/controllers/users/widgets/list/widget.php.
Трассировка событий
Теперь нужно узнать, передаётся ли в этих событиях что-то, что мы можем использовать?
В строке 20 файла виджета видим событие, которое передаёт в хук поля профиля и модель компонента пользователей. Это уже хорошо. Да и по названию события видно, что это для фильтра списка пользователей.
Код PHP:
  1. list($fields, $model) = cmsEventsManager::hook('profiles_list_filter', array($fields, $model));
В событии в строке 67 этого же файла передаются уже готовый список пользователей и список выводимых в виджете полей.
Код PHP:
  1. list($profiles, $show_fields) = cmsEventsManager::hook('profiles_before_list', [$profiles, $show_fields]);
Нам лучше подойдёт первый хук, в котором к модели списка пользователей можно будет применить дополнительный фильтр по отключённым пользователям и сразу получить нужное количество активных пользователей. При использовании второго хука нам придётся в цикле перебрать отобранных пользователей и оставить только подтверждённых – их суммарное количество может измениться, а нам это не нужно.

Ищем события в коде

Самый главный вопрос при любом поиске: "Где искать?". Если вы читали « официальную документацию по виджетам» или мой пост « Как работают виджеты (без PHP и с картинками)», то ответите на него легко. Виджет (widget) «Новые пользователи» принадлежит контроллеру (controllers) "Пользователи" (users), а значит находится в его папке /system/controllers/users/widgets. Там мы видим только три виджета: avatar, list и online. Понятно, что нас интересует list - список. Итого получаем имя файла /system/controllers/users/widgets/list/widget.php

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

Событие ‘profiles_list_filter’

На всякий случай посмотрим, что реально передаётся в первом событии. Включим «Выводить в лог данные событий». И впишем в строку фильтра по имени события ‘profiles_list_filter’. В выпадающем списке его нет, так как там только события от зарегистрированных хуков, а это событие никто не обрабатывает, хука нет.
Фильтр по названию события и вывод данных
Смотрим, что передаётся в хуки в первом событии:
Данные события profiles_list_filter

Первое, что видно, в событие действительно передаётся массив описаний полей и объект modelUsers. Это нам подходит.

Второе, это то, что событие ‘profiles_list_filter’ происходит на странице дважды: в нашем виджете «Новые пользователи» и в виджете «Сейчас онлайн». Поиск по коду движка говорит, что это событие возникает в пяти местах: в двух виджетах, в обработке подписок и в списках пользователей, полученных через метод renderProfilesList() компонента Users.
Поиск profiles_list_filter
Это значит, что если мы будем менять список пользователей в обработке этого события, то не подтверждённые пользователи будут исключены во всех местах, где оно происходит. Для некоторых проектов это как раз подходит, а для других придётся придумать проверку, из какого места был сделан вызов хука, и фильтровать только в нужном виджете.

Добавляем опцию

В компоненте «Хуки-хухуки» уже есть вкладка «Виджеты». Так как наш хук относится к пользователям, то добавим в файл \system\controllers\webman\backend\forms\form_options.php ещё одну вкладку «Пользователи» с опцией 'users_no_locked' «Исключать неактивных пользователей из списков».
Код PHP:
  1. 'type' => 'fieldset',
  2. 'title' => LANG_WEBMAN_OPT_TAB_USERS,
  3. 'childs' => array(
  4.  
  5. new fieldCheckbox('users_no_locked', array(
  6. 'title' => LANG_WEBMAN_OPT_USERS_NO_LOCKED,
  7. )),
  8.  
  9. )
  10. ),
Полный код файла
Добавим языковые константы в файл \system\languages\ru\controllers\webman\webman.php
Код PHP:
  1. define('LANG_WEBMAN_OPT_TAB_USERS', 'Пользователи');
  2. define('LANG_WEBMAN_OPT_USERS_NO_LOCKED', 'Исключать неактивных пользователей из списков');
Полный код файла
Новая опция

Создаём хук

Создадим новый файл с именем обрабатываемого события /system/controllers/webman/hooks/profiles_list_filter.php.
Код хука
В нём сначала присваиваем элементы массива $data отдельным переменным: описанию полей $fields и модели пользователей $model_users.
Потом проверяем включение опции 'users_no_locked'.
И если она включена, то добавляем к модели пользователей фильтр: только пользователей, у которых поле 'is_locked' равно null, то есть пользователь не заблокирован.
И в конце возвращаем объединённые в массив входные переменные $fields и $model_users, чтобы их могли обработать другие такие же хуки или код после хука в точке его вызова.

Добавляем хук в манифест /system/controllers/webman/manifest.php
Код манифеста
И разрешаем добавить наш хук в список хуков («Админка – Компоненты - Управление событиями»).
Отлично! Заблокированный пользователь исчез из списков в виджете и на странице пользователей.

Фильтр только в виджете

Если мы хотим фильтровать список пользователей только в нашем виджете, то нужно в хуке проверять, откуда сделан его вызов. Я не нашёл в движке стандартного способа получить эту информацию, поэтому используем небольшой лафхак – поищем класс нашего виджета 'widgetUsersList' в трассировке вызовов PHP из функции debug_backtrace()
Новый код хука
В этом варианте дополнительно получаем массив трассировки вызовов в переменную $backtrace. Перебираем его элементы $line и ищем в них класс 'widgetUsersList'. Если находим, то присваиваем переменной $found значение true, которое является разрешением для добавления нашего фильтра пользователей.

Проверяем. Теперь в виджете «Новые пользователи» остался только Администратор – только активные пользователи. А в списке пользователей на странице httр://site/users показывает и активных, и заблокированных пользователей. Всё как мы и хотели, наш фильтр применяется только при вызове из одного виджета.

Расширение функционала

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

Тогда строка проверки класса в хуке может выглядеть как-то так:
Код PHP:
  1. if (in_array($line['class'], explode(',', $this->options['имя_опции_со_списком']))) {

Пакет обновления компонента

Создание пакета обновления компонента описано в предыдущей части про Добавление ссылки под заголовки виджетов.

Для ленивых ссылка на готовый пакет обновления webman_update_1.2.0.zip.

Не забывайте, что имя webman везде нужно заменить на своё имя компонента с сохранением регистров символов
«Расширенная отладка» для InstantCMS 2.14.1 (v.14.1.2) – большое обновление для разработчиков
Комментарии (4)
Make 28 февраля 2021 в 10:06 +2
Спасибо, это очень полезно!
Tolya 28 февраля 2021 в 10:26 +2
Я когда вижу такие посты в блоге, я прям радуюсь. Всё расписано, всё рассказано и есть пример.
Во первых, поисковику есть что индексировать.
Во вторых новичку есть что читать, и на чем учится.

Я конечно писатель так себе laugh , и кодер тоже так себе )) поэтому не магу себе позволить писать такие статейки scratch а так бы тоже с радостью наполнил блог такими статьями.

WebMan, +++++ однозначно!!!
Scythian 7 марта 2021 в 19:19 +2
Полезно, плюсую в тему и в карму )
Inna 14 марта 2021 в 05:34 0
В темы и в карму +