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

Наумов Алексей
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. Ресурсы не кешируются.

Доп. учесть:
— Конфликт сессий
Функционал:
Код / Действие:
Примечания:?
Александр Туниеков
17 сентября 2025, 13:13
+3
Сложный вопрос. Застали в расплох. Как правильно ответить долго думать. Попробую ответить как смогу. Не обессудте.
Как правильно заметил @Futuris, у нас сложный технологический процесс, и запрограммировать множество технических параметрев деталей на extJs очень долго. Для примера, я в начале года пытался перевести тикеты на modx3. Так в них 50 процессоров для разных действий (https://github.com/tuniekov/Tickets/tree/master/core/components/tickets2/src/Processors). gtsAPI устроен так, что ему не нужны куча контролеров. Прописываешь настройки доступа и отображения для таблицы и всем процессом управляет 1 уже написанный контроллер. Это в разы сокращает время написания приложения. Хотя и вносит некоторые ограничения :-). Так вот, чтобы прописать нужный нам функционал на extJs, наверно бы потребовалось написать 500-1000 процессоров.
В примере админки на Vue, PVAdmin, я написал в основном, только нужный нам функционал для менеджеров и инженеров ИТР. Полностью повторять админку MODX у меня задача не стояла. Для стандартных целей админка MODX меня полностью устраивает. Админка PVAdmin, в текущей реализации, нужна только если програмировать сложный функционал или нет желания пускать пользователей в стандартную админку MODX.
Но программировать на Vue с gtsAPI, по моему, гораздо проще, чем на extJs. Если писать приложения вроде Tickets для PVAdmin, то может быть, со временем админка на Vue будет удобнее для обычных сайтов. Пока Tickets для PVAdmin не планирую, но есть некоторый соблазн :-).

Как ими воспользоваться?
Я пишу документацию временами и почти не получаю обратную связь от других разработчиков и пользователей. Так что сейчас документация не слишком хороша. Надеюсь, это исправиться. Преимущества Vue с gtsAPI в том что можно быстрее и проще програмировать компоненты для PVAdmin.
Мои заметки по gtsAPI:
UniTree новые возможности gtsAPI-PVTables
Кейс gtsAPI. CRUD пользователей на фронте
gtsAPI — Универсальное API для MODX
Введение в PVTables
Компонеты для gtsAPI проще писать на основе https://github.com/tuniekov/PVExtra. В readme инструкция.
Сейчас изучать gtsAPI наверно проще по примерам:
https://github.com/tuniekov/OrgStructure
https://github.com/tuniekov/PVAdmin/.
Основные файлы:
В файле _build\configs\gtsapipackages.js настройки таблиц для gtsAPI. По ним gtsAPI и PVTables формируют доступное API и таблицы, деревья, формы на фронте.
В файле core\components\pvextra\model\schema\pvextra.mysql.schema.xml MODX схема базы данных
В файле core\components\pvextra\model\pvextra\pvextra.class.php класс MODX компонента. Триггеры и кастомные действия обращаются в него.
src/App.vue код приложения на vue.

В папке https://github.com/tuniekov/PVExtra/tree/main/docs документация ИИ для ИИ. Часто в нейросеть пишу промпт: Прочитай docs/use_gtsapipackages.md и напиши конфиг для таблицы tableName (например, modUser или modUserProfile).

Надеюсь хорошо ответил… Буду рад если gtsAPI вас заинтересует.
Futuris
17 сентября 2025, 10:47
0
Как понял из предыдущих постов автора — это не для обычных сайтов. У них там сложный технологический процесс, и нужно, чтобы участники этого процесса могли загружать множество технических параметров для своих деталей — видимо обычная админка этого не позволяет. Но вот что это дает разработчикам обычных сайтов — пока не понятно.
Артур Шевченко
17 сентября 2025, 09:48
0
Переформулирую вопрос. Какую проблему или проблемы ты решал, когда делал свою админку? Какие возможности даёт другим разработчикам твоя админка? Как ими воспользоваться?
Александр Туниеков
17 сентября 2025, 01:42
0
А чем она должна быть лучше??? Просто тупо програмирую интерфейс пользователя без extjs. У меня там много всякого. На extjs гемор это делать. Vue удобней.
Артур Шевченко
16 сентября 2025, 22:10
0
А чем эта админка лучше стандартной?
Сергей Карпович
15 сентября 2025, 19:50
0
Подскажите, как включить поле title в окошке добавления изображения в редактор?
Андрей
15 сентября 2025, 18:08
0
Будет ли поддержка MySQL 8? Сейчас компонент не работает с этой версией, приходится откатывать mysql
Артур Шевченко
12 сентября 2025, 20:15
0
Можно обновить, но по инструкции автора
Николай Савин
12 сентября 2025, 18:55
0
Твой код верный. Должен работать. Я бы его немного вот так переделал, чтобы фильтровать GET и проверить существование заголовка HTTP_ACCEPT_LANGUAGE
<?php
  /* Запускаем плагин только на фронтенде и с включенными sef-url */
  if ($modx->context->key == 'mgr' || !$modx->getOption('friendly_urls') || $modx->event->name != 'OnHandleRequest') {
      return;
  }

HTTP_ACCEPT_LANGUAGE есть в 95% случаев, но обычно не гарантия что он обязательно будет
  /* Разрешенные контексты */
  $allowedContexts = ['en' => 'en', 'ru' => 'web'];

  /* Проверяем GET-параметр cultureKey */
  $cultureKey = filter_input(INPUT_GET, 'cultureKey', FILTER_SANITIZE_STRING);
  if (!empty($cultureKey)) {
      $targetContext = isset($allowedContexts[$cultureKey]) ? $allowedContexts[$cultureKey] : 'web';
      $modx->switchContext($targetContext);
  } else {
      /* Если cultureKey не указан, проверяем язык браузера */
      $acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '';
      if (!empty($acceptLanguage)) {
          $browserLang = strtolower(substr($acceptLanguage, 0, 2));
          if ($browserLang === 'en') {
              $modx->switchContext('en');
          }
      }
      /* В остальных случаях оставляем текущий контекст */
  }
Олег
12 сентября 2025, 18:55
0
Нашел информацию, автор модуля добавил такую возможность modx.pro/components/21775
Только не понимаю, мне надо заново этот модуль покупать? Или можно его как то обновить?