Всего 125 524 комментария

Николай Савин
20 января 2026, 10:24
0
Подготовил релиз 1.2.1 — можешь проверить. Лучше с нуля поставить, если часть минишопа установилась — удали.
FastDevLab
20 января 2026, 07:44
0
А зачем модифицировать Modx, если проксю на ПК поставить, можно спокойно устанавливать пакеты) ну и ВПН пока никто не отменял)
Руслан
20 января 2026, 01:33
0
То же «не пройду мимо». )
Создал на sweb.ru (ex. modhost.pro) чистый тестовый аккаунт «Старт» и установил там чистый MODX 3 (и сразу накатил pdoTools и vueTools), при установке и переустановке MiniShop3 возникают такие ошибки:

Консоль запущена...
PHP deprecated: Creation of dynamic property MODX\Revolution\modConnectorResponse::$response is deprecated
Пробуем установить пакет с подписью: minishop3-1.2.0-beta1
Пакет найден... сейчас идёт подготовка к его установке.
Загрузка рабочего пространства пакета...
Рабочее пространство загружено, сейчас устанавливаем пакет...
PHP deprecated: Creation of dynamic property MODX\Revolution\modConnectorResponse::$response is deprecated
[MiniShop3] Starting database migrations...
[MiniShop3] Migration execution failed: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'keltopartu.modx_ms3_grid_fields' doesn't exist
[MiniShop3] Migration error: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'keltopartu.modx_ms3_grid_fields' doesn't exist
[MiniShop3] Stack trace: #0 /home/k/keltopartu/core/components/minishop3/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.php(462): PDOStatement->execute(Array) #1 /home/k/keltopartu/core/components/minishop3/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php(186): Phinx\Db\Adapter\PdoAdapter->bulkinsert(Object(Phinx\Db\Table\Table), Array) #2 /home/k/keltopartu/core/components/minishop3/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TimedOutputAdapter.php(103): Phinx\Db\Adapter\AdapterWrapper->bulkinsert(Object(Phinx\Db\Table\Table), Array) #3 /home/k/keltopartu/core/components/minishop3/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php(186): Phinx\Db\Adapter\TimedOutputAdapter->bulkinsert(Object(Phinx\Db\Table\Table), Array) #4 /home/k/keltopartu/core/components/minishop3/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php(373): Phinx\Db\Adapter\AdapterWrapper->bulkinsert(Object(Phinx\Db\Table\Table), Array) #5 /home/k/keltopartu/core/components/minishop3/vendor/robmorgan/phinx/src/Phinx/Db/Table.php(662): Phinx\Db\Adapter\TablePrefixAdapter->bulkinsert(Object(Phinx\Db\Table\Table), Array) #6 /home/k/keltopartu/core/components/minishop3/migrations/20251127000002_seed_customers_grid_config.php(148): Phinx\Db\Table->saveData() #7 /home/k/keltopartu/core/components/minishop3/vendor/robmorgan/phinx/src/Phinx/Migration/Manager/Environment.php(112): SeedCustomersGridConfig->up() #8 /home/k/keltopartu/core/components/minishop3/vendor/robmorgan/phinx/src/Phinx/Migration/Manager.php(413): Phinx\Migration\Manager\Environment->executeMigration(Object(SeedCustomersGridConfig), 'up', false) #9 /home/k/keltopartu/core/components/minishop3/vendor/robmorgan/phinx/src/Phinx/Migration/Manager.php(384): Phinx\Migration\Manager->executeMigration('production', Object(SeedCustomersGridConfig), 'up', false) #10 /home/k/keltopartu/core/packages/minishop3-1.2.0-beta1/MODX/Revolution/modCategory/28e69eb7abbb2be48e7c3f8899a1d2af.resolver_02_migrations.resolver(76): Phinx\Migration\Manager->migrate('production') #11 /home/k/keltopartu/core/vendor/xpdo/xpdo/src/xPDO/Transport/xPDOVehicle.php(216): include('/home/k/keltopa...') #12 /home/k/keltopartu/core/vendor/xpdo/xpdo/src/xPDO/Transport/xPDOObjectVehicle.php(218): xPDO\Transport\xPDOVehicle->resolve(Object(xPDO\Transport\xPDOTransport), Object(MODX\Revolution\mysql\modCategory), Array) #13 /home/k/keltopartu/core/vendor/xpdo/xpdo/src/xPDO/Transport/xPDOObjectVehicle.php(77): xPDO\Transport\xPDOObjectVehicle->_installObject(Object(xPDO\Transport\xPDOTransport), Array, Array, NULL, NULL) #14 /home/k/keltopartu/core/vendor/xpdo/xpdo/src/xPDO/Transport/xPDOTransport.php(263): xPDO\Transport\xPDOObjectVehicle->install(Object(xPDO\Transport\xPDOTransport), Array) #15 /home/k/keltopartu/core/src/Revolution/Transport/modTransportPackage.php(346): xPDO\Transport\xPDOTransport->install(Array) #16 /home/k/keltopartu/core/src/Revolution/Processors/Workspace/Packages/Install.php(73): MODX\Revolution\Transport\modTransportPackage->install(Array) #17 /home/k/keltopartu/core/src/Revolution/Processors/Processor.php(189): MODX\Revolution\Processors\Workspace\Packages\Install->process() #18 /home/k/keltopartu/core/src/Revolution/modX.php(1771): MODX\Revolution\Processors\Processor->run() #19 /home/k/keltopartu/core/src/Revolution/modConnectorResponse.php(151): MODX\Revolution\modX->runProcessor('Workspace/Packa...', Array, Array) #20 /home/k/keltopartu/core/src/Revolution/modConnectorRequest.php(89): MODX\Revolution\modConnectorResponse->outputContent(Array) #21 /home/k/keltopartu/core/src/Revolution/modConnectorRequest.php(77): MODX\Revolution\modConnectorRequest->prepareResponse(Array) #22 /home/k/keltopartu/public_html/connectors/index.php(79): MODX\Revolution\modConnectorRequest->handleRequest() #23 {main}
PHP warning: mkdir(): File exists
PHP warning: mkdir(): File exists
✅ [MiniShop3] Manager API custom routes file exists (preserved): core/config/ms3_routes_manager.custom.php
[MiniShop3] Could not create Web API custom routes example (optional)
📁 [MiniShop3] System routes are in: core/components/minishop3/config/routes/
PHP deprecated: Creation of dynamic property MODX\Revolution\modConnectorResponse::$response is deprecated
[MiniShop3] Scheduler tasks: 0 registered, 4 updated
[MiniShop3] Recurring tasks enabled: ms3_cleanup_drafts (daily), ms3_cleanup_tokens (weekly).
Успешно установлен пакет minishop3-1.2.0-beta1
Ivan K.
19 января 2026, 19:15
0
За cloudflare находится официальный репозиторий MODX (rest.modx.com) сам недавно столкнулся с «замедлением», на одном из хостингов. На одном работало обновление на втором нет. (Оба в РФ)
ip адреса от cloudflare «замедляют» и-за этого не обновить компоненты и не скачать их, хотя список видно.
Я вручную прописал в файле host на VDS, ip адрес, до репозитория, тот адрес, который не замедляется, пропинговал с рабочего хостинга репозиторий и узнал ip адрес, который не замедляется.
steve.kon
19 января 2026, 18:57
0
Получилось так:
&sortbyTV=`FIELD(availability, 1,0,3,2 )`
Юрий
19 января 2026, 10:56
0
Может дело в этом?
12.04.2024
Роскомнадзор заблокировал сайты DigitalOcean, GoDaddy и ещё семи хостинг-провайдеров по закону о «приземлении»
vc.ru/services/1124251-roskomnadzor-zablokiroval-saity-digitalocean-godaddy-i-eshe-semi-hosting-provaiderov-po-zakonu-o-prizemlenii
Наумов Алексей
19 января 2026, 09:29
0
зайти в настройки компонента, убрать значение у «yasmartcaptcha_service_js», а скрипт подключить вручную
FastDevLab
18 января 2026, 22:14
0
Что это. Я к примеру давненько пользуюсь (для сайтов без коммерции / без minishop). Правда вместо этого плагина используется обычно easyComm
FastDevLab
18 января 2026, 21:56
0
Замените в /core/components/fetchit/src/FetchIt.php 81-83 строки:
if (!$_SESSION['fetchit_called']) {
    return;
}
на
if (empty($_SESSION['fetchit_called'])) {
    return;
}
Или альтернативный вариант:
if (!isset($_SESSION['fetchit_called']) || !$_SESSION['fetchit_called']) {
    return;
}
Николай Савин
18 января 2026, 15:22
+1
Ты еще в документацию загляни. В описание сниппета msCustomer и страницу адреса клиента
Это все было сделано изначально, до первого релиза
Сергей
18 января 2026, 12:21
0
Я посмотрел информация по обновлению 3 версии магазина, но не тестировал еще. Если такое можно, сделать (может уже реализовано) как у всех крупных маркетов, сделать сохранение и выбора адресов и данных пользователя, что бы пользователем было проще делать повторные заказы.
Николай Савин
17 января 2026, 11:42
+3
Новый mFilter уже объединен с SeoFilter и содержит его SEO функционал. Компонент базово готов, уже на стадии тестирования.

mFilter — будет фасетным
— так он и был фасетным всегда.
Сергей
17 января 2026, 08:38
+1
Мне как человеку, занимающемся seo, хотелось бы функционал там увидеть SeoFilter. Я думаю так будет проще, если он будет туда вшит. mFilter — будет фасетным?
Scorp Satex
16 января 2026, 19:24
0
Добрый день.

На странице в модальных окнах есть 5 форм.

В каждой форме вставлен [[!YaSmartCaptcha]] в нужное место.

Из-за этого он 5 раз закачивает два файла:

* smartcaptcha.cloud.yandex.ru/slider.xxxxxxxxxx.js
* smartcaptcha.cloud.yandex.ru/captchapgrd

Из-за этого размер страницы сильно раздувается.

Как можно решить проблему дублирования?
Николай Савин
16 января 2026, 15:33
0
Поддержал рублем и купил доп, надеюсь не забросите развитие
За мной не заржавеет.
Андрей
16 января 2026, 14:05
+1
Поддержал рублем и купил доп, надеюсь не забросите развитие
Добавите плз возможность редактирование цен из списка товаров, не заходя на саму страницу товара
кнопку применения фильтра сделал бы в одну линию фильтрации
Очень ждем шуструю фильтрацию, при больших объемах товара
Дмитрий
16 января 2026, 02:03
0
Возвращался к этому вопросу, чтобы не грузить ничего заранее, делаем просто встраивание iframe после нажатия на div и вместо него встраиваем iframe. Не буду приводить никакие стили, просто суть:

<div class="rutube" id="f1513a11d3821109ac8283a7423f8f05" data-params="autoplay=true" style="width: 100%;height: 290px;background: url(путь до собственной заглушки) no-repeat;"></div>

и на jquery так:

$(".rutube").each(function() {        
        $(this).append($('<div/>', {'class': 'play'}));
        $(document).delegate('#'+this.id, 'click', function() {
            var iframe_url = "https://rutube.ru/play/embed/" + this.id + "/?r=wd";
            if ($(this).data('params')) iframe_url+='&'+$(this).data('params');
            var iframe = "<iframe  width=" + $(this).width() +" height="+ $(this).height() + " src=" + iframe_url + " style='border: none;' allow='clipboard-write; autoplay' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>";
            $(this).replaceWith(iframe);
        });
    });
Артур Шевченко
15 января 2026, 20:23
+1
Вариант 1
Переопределить метод msOrderHandler::submit() таким образом, чтобы там перед установлением статуса «Новый» проверялся способ оплаты и, если оплата при получении, статус устанавливать методом minishop2::changeOrderStatus(), иначе через modResource::save().

Вариант 2.
Ввести для оплаты при получении отдельный статус. Написать плагин на событие msOnChangeOrderStatus, в котором проверять способ оплаты и, если это оплата при получении, вызывать метод minishop2::changeOrderStatus() и устанавливать созданный для этого способа оплаты статус. При этом отправку писем для статуса «Новый» отключить в настройках минишопа.

Вариант 3.
Отключить стандартную отправку писем. Написать свой класс отправки писем и вызывать его в плагине на событие msOnChangeOrderStatus. Вот пример реализации такого класса
<?php

namespace CustomServices\Manage;

use CustomServices\Helpers\Mailer;

class MailingOrder extends Base
{
    private Mailer $mailer;

    private array $mailingStatuses = [
        2 => [
            'user' => [
                'subject' => 'ms2_email_subject_paid_user',
                'chunk' => 'msEmailPaid.user'
            ],
            'manager' => [],
        ],
        3 => [
            'user' => [
                'subject' => 'ms2_email_subject_sent_user',
                'chunk' => 'msEmail.sent.user'
            ],
            'manager' => [],
        ],
        108 => [
            'user' => [
                'subject' => 'ms2_email_subject_sent_user',
                'chunk' => 'msEmail.sent.user'
            ],
            'manager' => [],
        ],
        117 => [
            'user' => [
                'subject' => 'ms2_email_subject_paid_user',
                'chunk' => 'msEmailPaid.user'
            ],
            'manager' => [],
        ],
        103 => [
            'user' => [
                'subject' => 'ms2_email_subject_paid_user',
                'chunk' => 'msEmailPaid.user'
            ],
            'manager' => [],
        ],
    ];

    private array $statusData;

    public function __construct(\modX $modx, ?array $properties = [])
    {
        parent::__construct($modx, $properties);
        if ($properties['order'] && $properties['order']->get('context') !== $this->modx->context->get('key')) {
            $this->modx->switchContext($properties['order']->get('context'));
            $this->cultureKey = $this->modx->getOption('cultureKey', '', 'en');
        }
        $this->mailer = new Mailer($this->modx);
        $this->statusData = [];
    }

    public function send(): bool
    {
        $statusId = $this->properties['statusId'];
        if (!isset($this->mailingStatuses[$statusId])) {
            return false;
        }
        $this->statusData = $this->mailingStatuses[$statusId];

        if (!$this->properties['order']) {
            $this->logging->write([
                'msg' => "Mail not sent. Order not defined",
            ]);
            return false;
        }

        $pls = $this->getEmailPls();
        if (!empty($this->statusData['manager']) && $mgrMailerProps = $this->getMailerProperties($pls, 'manager')) {
            if(!$this->mailer->send($mgrMailerProps)){
                $this->logging->write([
                    'msg' => "Order {$pls['num']} was not sent to manager email",
                    'data'=> [
                        'mailerProps' => $mgrMailerProps
                    ]
                ]);
            }
        }

        if (!empty($this->statusData['user']) && $usrMailerProps = $this->getMailerProperties($pls)) {
            if(!$this->mailer->send($usrMailerProps)){
                $this->logging->write([
                    'msg' => "Order {$pls['num']} was not sent to customer email",
                    'data'=> [
                        'mailerProps' => $usrMailerProps
                    ]
                ]);
            }
        }
        return true;
    }

    public function getEmailPls(): array
    {
        $pls = $this->properties['order']->toArray();
        $pls['cost'] = $this->miniShop2->formatPrice($pls['cost']);
        $pls['cart_cost'] = $this->miniShop2->formatPrice($pls['cart_cost']);
        $pls['delivery_cost'] = $this->miniShop2->formatPrice($pls['delivery_cost']);
        $pls['weight'] = $this->miniShop2->formatWeight($pls['weight']);
        $pls['payment_link'] = $this->properties['paymentLink'] ?: '';
        return $pls;
    }

    public function getMailerProperties(array $pls, $type = 'user'): array
    {
        $mailerProperties = [];
        $mailerProperties['subject'] = $this->modx->lexicon($this->statusData[$type]['subject'], $pls, $this->cultureKey);
        if($mailerProperties['subject'] === $this->statusData[$type]['subject']){
            $mailerProperties['subject'] = $this->modx->lexicon($this->statusData[$type]['subject'], $pls, 'en');
        }
        $mailerProperties['body'] = $this->parser->runSnippet('msGetOrder', array_merge($pls, ['tpl' => $this->statusData[$type]['chunk']]));
        if ($this->properties['invoicePath'] && $type === 'user') {
            $mailerProperties['attachment'] = $this->properties['invoicePath'];
        }

        if (!$mailerProperties['to'] = $type === 'user' ? $this->getUserEmails($pls['user_id']) : $this->getManagerEmails()) {
            return [];
        }

        return $mailerProperties;
    }

    public function getUserEmails(int $userId): array
    {
        if (!$profile = $this->modx->getObject('modUserProfile', ['internalKey' => $userId])) {
            return [];
        }
        $email = $profile->get('email');
        if (!$email) {
            if (!$user = $this->modx->getObject('modUser', $userId)) {
                return [];
            }
            if (!$email = $user->get('username')) {
                return [];
            }
        }
        return [$email];
    }

    public function getManagerEmails(): array
    {
        return array_map(
            'trim',
            explode(
                ',',
                $this->modx->getOption('ms2_email_manager', null, $this->modx->getOption('emailsender'))
            )
        );
    }
}
vectorserver
15 января 2026, 11:23
0
Вот давно для себя писал: modx.pro/solutions/18489
или через мускул (перед выполнением обязательно сделайте дамп) напрямую + потом кеш почистить
UPDATE modx_site_content t1
JOIN (
    SELECT 
        id,
        CONCAT(alias, '-', ROW_NUMBER() OVER (PARTITION BY uri ORDER BY id ASC)) AS new_alias
    FROM 
        modx_site_content 
    WHERE 
        uri IN (
            SELECT uri 
            FROM modx_site_content 
            GROUP BY uri 
            HAVING COUNT(*) > 1
        )
        AND deleted = 0
) t2 ON t1.id = t2.id
SET 
    t1.alias = t2.new_alias
WHERE 
    t2.new_alias != t1.alias;