Всего 125 096 комментариев

Артур Шевченко
18 сентября 2025, 19:00
0
Других вариантов нет
Кирилл
18 сентября 2025, 18:58
0
Не совсем подходит, т.к. меняет вообще у всех ресурсов в дереве
pagetitle, tv_vin
или pagetitle >br> tv_vin
не сработало
Артур Шевченко
18 сентября 2025, 18:51
+1
resource_tree_node_name
Олег
18 сентября 2025, 17:14
0
так, а в чем проблема во втором абзаце, не очень понял?
$modx->addEventListenerClosure(
    'OnWebPagePrerender',
    function (\MODX\Revolution\modX $modx) {
        (new \Namespace\YourClass($modx))->doSomething();
        // $class = $modx->services->get('yourservice'); $class->doSomething();
        // \Namespace\YourClass::doSomething();
        // my_global_function($modx);
        return null;
    },
    priority: 10,
    name: 'appendTestComment',
    replace: false
);
Олег
18 сентября 2025, 17:07
0
с обычными плагинами — я пока это не учитывал, но вопрос логичный. НО это не учитывается и в оригинальном методе addEventListener, так что текущая логика по сути не меняется. Оригинал:
public function addEventListener($event, $pluginId, $propertySetName = '') {
        $added = false;
        $pluginId = intval($pluginId);
        if ($event && $pluginId) {
            if (!isset($this->eventMap[$event]) || empty ($this->eventMap[$event])) {
                $this->eventMap[$event]= [];
            }
            $this->eventMap[$event][$pluginId]= $pluginId . (!empty($propertySetName) ? ':' . $propertySetName : '');
            $added = true;
        }
        return $added;
    }
Проблема в том что карта у них строится без какой либо доп информации о плагинах, просто событие->список ID плагинов, уже отсортированных в sql запросе. То есть пересортировать ее без дополнительного запроса к базе не получится (ну или надо сохранять расширенную карту из базы при инициализации и потом при добавлении нового плагина пересобирать итоговую карту). Сейчас реализовано так, что сначала выполняются плагины из бд — и подвязанные к событию в админке, и подвязанные к событию в коде, потом все closure плагины. Вообще это больше не для того чтобы кто-то в админке вручную тыкал, а чтобы например при разработке пакета вы спокойно свой функционал раскидали по нужным событиям и не парились с созданием плагинов и привязкой/отвязкой событий при обновлении например.
Олег
18 сентября 2025, 16:50
0
Да, но это приоритет выполнения именно Closure слушателей — они выполняются уже после плагинов из бд
alexwolf2006
18 сентября 2025, 16:23
0
тоже интересно, как настроить s3 от Selectel для MODX

Дмитрий, если у тебя получилось — поделись мануалом — скриншотом
Артур Шевченко
18 сентября 2025, 13:45
0
Я имел ввиду как управлять порядком выполнения, если плагины принадлежат разным компонентам? Сейчас открыл список плагинов и перетасовал приоритеты как надо, а тут списка плагинов не будет. Как искать какие плагины срабатывают на то или иное событие? Как менять их порядок?

И после просмотра примера возник новый вопрос: а если у меня будет большая функция или несколько функций или я хочу использовать целый класс в плагине, как тогда?
Наумов Алексей
18 сентября 2025, 12:05
0
Но вообще выглядит прикольно. Прям клево, взять и подписаться на событие в своем коде без создания плагинов — удобно должно быть.
Наумов Алексей
18 сентября 2025, 12:04
0
Вижу в примерах на git что есть параметр priority:
$modx->addEventListenerClosure(
    'OnWebPagePrerender',
    function (array $params, \MODX\Revolution\modX $modx) {
        $output = &$modx->resource->_output;
        $output .= "\n<!-- TEST CLOSURE: " . date('Y-m-d H:i:s') . " -->";
        return null;
    },
    priority: 5,
    name: 'appendTestComment'
);
видимо, управлять приоритетом можно.
Артур Шевченко
18 сентября 2025, 11:26
0
Что делать если нужно изменить порядок выполнения плагинов? Если будет использоваться смешанный режим плагинов: твой + стандартный — в каком порядке будут выполняться плагины, особенно если у них одно событие?
Николай Савин
18 сентября 2025, 11:07
0
Ну логично. Скрипт делает выборку по таблице modResource, и там такого поля нет. Оно лежит в другой таблице msProductOption.
Попробуй писать msProductOption.flat_area вместо простого flat_area
Александр Туниеков
18 сентября 2025, 01:09
0
Что РКН с инетом делает?? гитхаб не открывается, а впн помогло :-(
Артур Шевченко
18 сентября 2025, 00:09
0
Вставлять сырые данные с фронта в запрос небезопасно, именно на этот случай мной и была добавлена принудительная проверка. Сам в начале этим грешил. А еще в pdo есть методы для подготовки запроса, это делает их чуть более безопасными. Тут можно почитать
Александр Туниеков
17 сентября 2025, 22:53
0
К сожалению, не понимая думаю можно наделать других дырок. Попробовал проверить на уязвимость из https://habr.com/ru/articles/130826/ .

Вроде не уязвимо :-). Почти все через pdoFetch идет. Только иногда использую сложное условие вроде (id IN (2,3) and (parent = 4 or parent IS NULL)) в where. Тогда… хм. проверю :-).

Код для проверки:
<?php
$pdo = $modx->getService('pdoFetch');
$id = $_GET['id'];
$pdo->setConfig([
    'class'=>'modResource',
    'where'=>[
        0=>"(id = $id and (parent = 4 or parent = 0))"
    ],
    'limit'=>0,
    'return'=>'data'
]);

$ress = $pdo->run();
foreach($ress as $res){
    echo "<p>{$res['id']}</p>";
}
echo '<pre>'.$pdo->getTime().'</pre>';
На запрос gettables.ru/test.html?id=4+OR+1 выдало:
1

2

3

4

5

6

7

14

16

19

20

21

22

25

30

0.0000529: xPDO query object created
0.0000019: Processed additional conditions
0.0000720: Added where condition: 0=(id = 4 OR 1 and (parent = 4 or parent = 0))
0.0000749: Sorted by modResource.id, ASC
0.0002530: SQL prepared "SELECT * FROM `modx_site_content` AS `modResource` WHERE (id = 4 OR 1 and (parent = 4 or parent = 0)) ORDER BY modResource.id ASC "
0.0014510: SQL executed
0.0000789: Rows fetched
0.0000720: Returning raw data
0.0167792: Total time
4 194 304: Memory usage
Выдало все id страниц. То есть
'where'=>[
        0=>"(id = $id and (parent = 4 or parent = 0))"
    ],
Уязвимо для инъекций. Будем знать.
Артур Шевченко
17 сентября 2025, 21:46
0
Я тоже далеко не специалист в безопасности, но точно знаю, что надо валидировать пользовательский ввод, поэтому и добавил этот функционал в SendIt.
Александр Туниеков
17 сентября 2025, 21:41
0
Безопасность MODX для меня не слишком понятная тема. Когда то искал как безопасно писать запросы в базу. Нашел только приводить к инт все id получаемые с фронта. И плюс вырезать из получаемого с фронта теги MODX и fenom. Было бы интересно увидеть разбор безопасности MODX, при запросах в базу. И увидеть примеры атак, чтобы знать от чего защищаться.
Артур Шевченко
17 сентября 2025, 19:47
0
Делай всё, что связано с контекстами и языками и мобильной версией на событие OnHandleRequest, на OnMODXInit это просто бессмысленно. А в остальном логика верная.
Даша
17 сентября 2025, 19:21
0
Вот таблица из вопроса в текстовом виде на случай, если ее неудобно читать или открывать:

Этап:
OnMODXInit — Проверка сессии
Функционал: Проверка наличия данных в сессии
Код / Действие:
if (!session('device.type')) { ... }
if (!session('user.lang')) { ... }
Примечания: На случай, если в сессию была вручную положена смена устройства или языка.

<cut/>
Этап
OnMODXInit — MobileDetect
Функционал: Определение типа устройства
Код / Действие:
$detect = new MobileDetect(); $isDesktop = !$detect->isMobile() && !$detect->isTablet(); session('device.type', $isDesktop ? 'desktop' : 'mobile');
Примечания: Определяется при инициализации MODX, чтобы не делать это на каждой странице. Если не удалось определить — ставим «mobile» как дефолт. Значение в сессии нужно для ручного переключения.

Этап:
OnMODXInit — Язык браузера
Функционал: Получение языка из заголовка
Код / Действие:
lang=substr(_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2); session('user.lang', $lang);
Примечания: Используется тот же плагин, что и выше.

Этап:
OnMODXInit — Плейсхолдеры
Функционал: Установка плейсхолдеров
Код / Действие:
pls('device', session('device.type')); pls('lang', session('user.lang'));
Примечания: Нужно ли использовать session_write_close();?

Этап:
OnHandleRequest — Babel / Контекст
Функционал: Переключение контекста по URL
Код / Действие:
switch_context(['base_url' => request()->segment(1)]);
Примечания:
Нужно исключить mgr:
if ($modx->context->key !== 'mgr')
Применяется сразу, т.к. сессия без перезагрузки не даст данные
Событие может меняться местами с другим — нужно повесить на оба
Номер сегмента — это порядковый номер контекста в URL

Этап:
OnLoadWebDocument — Переключение шаблона
Функционал: Установка шаблона в зависимости от устройства
Код / Действие:
if (session('device.type') == 'desktop') {  $modx->resource->set('template', $newTemplateId); }
Примечания: Меняем шаблон через специальный плагин, где заданы соответствия для mobile/desktop. Ресурсы не кешируются.

Доп. учесть:
— Конфликт сессий
Функционал:
Код / Действие:
Примечания:?