Артур Шевченко

Артур Шевченко

С нами с 24 мая 2019; Место в рейтинге пользователей: #11
Отправить деньги
Артур Шевченко
22 марта 2023, 19:46
+1
Всё просто. В документации к API написано куда и какие запросы слать и какой ответы ты получишь. Тебе нужно с помощью curl отправлять запросы, а потом обрабатывать полученный результат с помощью php и API Modx. Как ты оформишь код дело твое. Запускать можно или кроном или в плагинах, зависит от задачи.
Артур Шевченко
21 марта 2023, 22:56
0
Отправка писем прописана в классе minishop2, расширение которого не предусмотрено. Либо пиши свою отправку, либо правь исходники, либо отправляй ссылку на файл загруженный на сервер.
Артур Шевченко
21 марта 2023, 20:05
+1
Добавлять авторизацию и регистрацию — это не «такое себе»
Не такое себе, я этим пользуюсь, мне удобно когда это есть.

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

Именно с этим лозунгом ты сменил jquery-form на XMLHttpRequest
Нет, лозунг был другой: одна отключаемая зависимость лучше двух обязательных.

Ты проигнорировал вопрос про асинхронность в XMLHttpRequest
Потому что счёл его риторическим, логично же что никаких работ по усовершенствованию не ведётся, разработан новый API и его развивают.

А когда в XMLHttpRequest появится (спойлер — не появится, но есть библиотеки-обёртки над ним в которых реализована асинхронность) ты обратно перепишешь с Fetch API на XMLHttpRequest?
Ты меня за идиота принимаешь? Я решаю возникающие у меня задачи, удобным для меня способом. Мне нужно показывать процесс загрузки, Fetch API этого не умеет, я использовал XMLHttpRequest. По-моему всё логично.

Мне кажется ты упускаешь суть: ты писал компонент с целью внедрения Fetch API, я писал компонент с целью повысить удобство собственной работы и отказаться от использования jQuery. Не вижу причин по которым я обязан был использовать Fetch вместо XMLHttpRequest. Для пользователя компонента это вообще не важно, что там под капотом, главное это стабильное выполнение необходимых ему функций.

И наконец, всё что ты перечислил в качестве проблем AjaxFormitLogin, всего лишь твоё скромное мнение, о чем ты забыл упомянуть, отчего кому-то может показаться, что перечисленные тобой «проблемы» действительно серьезно могу усложнить жизнь пользователю моего компонента. Я думаю тот, кому понадобится больше событий и API, легко поймёт, что всё это есть в FetchIt, и выберет именно его.
Артур Шевченко
21 марта 2023, 18:40
+1
Банально — такой, такой и такой
Думаю это ни к чему в данном компоненте, просто потому что я не могу придумать для чего это может понадобиться лично мне. А добавлять что-то, чтобы было такое себе. Если кто-то предложит PR, волью без проблем.

Добавление и/или изменение значений отправляемых на сервер
Грубо говоря да, но при желании всегда можно расширить класс.

Каждый раз когда зависимость обновиться, нужно будет обновить компонент?
Нет смысла обновлять то, что и так работает.

когда разработчик sweetalert2 захочет выразить свой личный протест в OpenSource проекте, то как быть с этим?
Понятное дело не использовать подключение зависимостей через cdn.

Работа над введением этой возможности ведётся и оно точно появится
Когда такая возможность появится — перепишу.
Артур Шевченко
21 марта 2023, 15:22
+1
а в чём смысл AjaxFormitLogin сейчас
В том, что этот компонент упрощает мне разработку сайтов, там много всякой функциональности, которая нужна почти на каждом сайте. А вообще, читайте документацию, чтобы решить надо оно вам или нет.
Артур Шевченко
21 марта 2023, 15:19
+1
Отсутствие API
Какой API нужен компоненту для отправки форм ajax'ом?

Нехватка событий (есть одно afl_complete) что не позволяет решать разные задачи
Какие задачи связанные с отправкой формы нельзя решить используя AjaxFormitLogin?

Повторение недостатка AjaxForm (AjaxForm тянул зависимость ввиде jgrowl, а AjaxFormitLogin теперь тянет iziToast)
AjaxForm тянул jQuery и jGrowl, AjaxFormitLogin тянет IziToast только для того, чтобы из коробки были уведомления. В документации написано как отключить их и заменить.

Самое главное, AjaxFormitLogin заменил громоздкий jquery-form на XMLHttpRequest — это нонсенс
Fetch API не позволяет отслеживать процесс загрузки файлов, а мне это нужно.
Артур Шевченко
21 марта 2023, 15:11
0
В AjaxFormitLogin нет jGrowl, там IziToast. Можно заглянуть к нему в документацию и настроить как хочется, или расширить класс уведомлений и написать свои.
Артур Шевченко
21 марта 2023, 11:55
0
Написать плагин на событие msOnAddToCart
Артур Шевченко
20 марта 2023, 22:59
+1
Ну вообще AjaxForm сам подгружает все скрипты, возможно у вас она некорректно установилась. А так, рекомендую переходить на более современные решения. Например AjaxFormitLogin, где jQuery выпилена за ненадобностью и есть много полезных фишек из коробки.
Артур Шевченко
19 марта 2023, 12:14
0
А почему нельзя в значениях ТВ написать «на английском языке», «на русском языке», «на украинском языке». В параметрах вывода разделителем указать запятую. А потом вывести вот так
Статья доступна {$_modx->resource.lang}
Артур Шевченко
17 марта 2023, 00:06
0
Ещё можешь вывести ресурсы через pdoResources и в чанке вывести значение TV
Артур Шевченко
16 марта 2023, 23:32
0
Какой-то странный у тебя кейс, обычно этого не требуется. Я бы сниппет написал, который соберут все данные из ТВ в один массив.
Артур Шевченко
15 марта 2023, 20:51
0
А сервер точно apache?
Артур Шевченко
15 марта 2023, 15:32
0
У вас включен fenom на страницах и вот такой синтаксис {{ ему не нравится. Самый простой вариант заменить {{ на [[ должно помочь.
Артур Шевченко
15 марта 2023, 15:04
0
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\.(php|html|htm)\ HTTP/
RewriteRule ^(.*)index\.(php|html|htm)$ $1 [R=301,L]
Добавить в htaccess
Артур Шевченко
15 марта 2023, 12:54
0
<?php
ini_set('memory_limit', '512M');

class ImportFeed
{
    public ModX $modx;
    public string $basepath;
    public string $filename;
    public string $feedurl;
    public string $logpath;
    public array $categoryDefaultFields;

    public function __construct($modx, $filename, $feedurl)
    {
        $this->modx = $modx;
        $this->feedurl = $feedurl;
        $this->basepath = $this->modx->getOption('base_path');
        $this->logpath = $this->basepath . 'import_log.txt';
        $this->filename = $this->basepath . $filename;
        $this->categoryDefaultFields = array(
            'parent' => 89,
            'template' => 15,
            'hidemenu' => 1,
            'published' => 1,
            'class_key' => 'msCategory'
        );

        return true;
    }

    public function start($import_categories = true, $import_products = true, $download_feed = true)
    {
        if(file_exists($this->logpath)){
            unlink($this->logpath);
        }

        if ($download_feed) {
            if ($this->downloadFeed()) {
                $this->log('[ImportFeed::start] Загружен файл фида.');
            } else {
                $this->log('[ImportFeed::start] Не удалось скачать файл фида.');
                return false;
            }
        } else {
            if (file_exists($this->filename)) {
                $this->log('[ImportFeed::start] Будет произведён импорт из имеющегося файла фида.');
            } else {
                $this->log('[ImportFeed::start] Файл фида отсутствует. Загрузите его, чтобы выполнить импорт.');
                return false;
            }
        }

        $this->log('[ImportFeed::start] Начат импорт.');
        if ($import_categories) {
            $this->getCategoriesData();
        }
        if ($import_products) {
            $this->getProductsData();
        }
    }

    function downloadFeed()
    {
        $ch = curl_init($this->feedurl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_HEADER, false);
        $html = curl_exec($ch);
        curl_close($ch);

        return file_put_contents($this->filename, $html);

    }

    private function getCategoriesData()
    {
        $this->log('[ImportFeed::getCategoriesData] Начато получение данных категорий для импорта.');
        if ($reader = $this->getXmlReader($this->filename, 'categories')) {
            $this->modx->log("[ImportFeed::getCategoriesData] Выделено памяти:" . (memory_get_usage() / 1048576).  " МБ.", 0, 1);
            if ($xml = $this->readXml($reader)) {
                $this->log('[ImportFeed::getCategoriesData] Начат импорт категорий.');
                $c = 0;
                foreach ($xml->category as $item) {
                    $c++;
                    $parentId = $item->attributes()->parentId ? $item->attributes()->parentId->__toString() : '';
                    $id = $item->attributes()->id ? $item->attributes()->id->__toString() : '';
                    if ($id && !$parentId) {
                        $categoryData = array(
                            'pagetitle' => $item->__toString(),
                            'feed_id' => $id,
                        );
                    } else {
                        $categoryData = array(
                            'feed_id' => $id,
                            'parentId' => $parentId,
                            'pagetitle' => $item->__toString(),
                        );
                    }
                    $this->manageCategories($categoryData);
                }
                $this->log('[ImportFeed::getCategoriesData] Импорт окончен. Импортировано категорий: ' . $c);
                unset($xml);
            } else {
                $this->log('[ImportFeed::getCategoriesData] Не удалось прочитать данные категорий из фида.');
            }
            unset($reader);
        } else {
            $this->log('[ImportFeed::getCategoriesData] Не удалось получить данные категорий для чтения.');
        }
    }

    private function manageCategories($categoryData)
    {
        $categoryData = array_merge($this->categoryDefaultFields, $categoryData);
        if ($categoryData['parentId']) {
            $data['feed_id'] = $categoryData['parentId'];
            $data = array_merge($this->categoryDefaultFields, $data);
            if ($parent = $this->manageObject('modResource', $data, array('class_key' => 'msCategory', 'feed_id' => $data['feed_id'], 'template' => 15))) {
                $categoryData['parent'] = $parent;
            }
        }
        $this->manageObject('modResource', $categoryData, array('class_key' => 'msCategory', 'feed_id' => $categoryData['feed_id'], 'template' => 15));
        unset($categoryData);
    }

    private function getProductsData()
    {
        $this->log('[ImportFeed::getProductsData] Начато получение данных товаров для импорта.');
        if ($reader = $this->getXmlReader($this->filename, 'offers')) {
            if ($xml = $this->readXml($reader)) {
                $this->log('[ImportFeed::getProductsData] Начат импорт товаров.');
                $c = 0;
                foreach ($xml->offer as $item) {
                    $c++;
                    $id = $item->attributes()->id ? $item->attributes()->id->__toString() : '';
                    $productData = array(
                        'pagetitle' => $item->name->__toString(),
                        'longtitle' => $item->url->__toString() . '?utm_source=petrovich_znaet',
                        'introtext' => $item->picture->__toString(),
                        'price' => $item->price->__toString(),
                        'categoryId' => $item->categoryId->__toString(),
                        'feed_id' => $id,
                        'published' => 1
                    );
                    $this->manageProducts($productData);
                }
                $this->log('[ImportFeed::getProductsData] Импорт окончен. Импортировано товаров: ' . $c);
                unset($xml);
            }
            else {
                $this->log('[ImportFeed::getProductsData] Не удалось прочитать данные товаров из фида.');
            }
            unset($reader);
        } else {
            $this->log('[ImportFeed::getProductsData] Не удалось получить данные товаров для чтения.');
        }
    }

    private function manageProducts($productData)
    {
        $productData['parent'] = 89;
        $productData['class_key'] = 'msProduct';
        $productData['show_in_tree'] = 0;
        if ($parent = $this->modx->getObject('msCategory', array('feed_id' => $productData['categoryId']))) {
            $productData['parent'] = $parent->get('id');
        }
        $this->manageObject('modResource', $productData, array('class_key' => 'msProduct', 'feed_id' => $productData['feed_id']));
        unset($productData);
    }

    private function getXmlReader($filename, $search)
    {
        //read xml file
        $reader = new XMLReader;
        $success = $reader->open($filename);
        if (!$success) {
            $this->modx->log("[ImportFeed::getXmlReader] Невозможно считать файл $filename. Возможно он содержит ошибки XML.", 0, 1);
        }

        //search categories
        while ($reader->read() && $reader->name !== $search);
        return $reader;
    }

    private function readXml($reader)
    {
        $outerXml = $reader->readOuterXML();
        return $outerXml ? new SimpleXMLElement($outerXml) : null;
    }

    private function manageObject($class, $data, $conditions)
    {
        if ($data['pagetitle']) {
            $data['description'] = $data['pagetitle'];
            $data['pagetitle'] = $this->truncate($data['pagetitle']);
            $data['pagetitle'] .= ' ' . $data['feed_id'];
        }
        if (!$obj = $this->modx->getObject($class, $conditions)) {
            if (!$data['pagetitle']) {
                $data['pagetitle'] = 'Ресурс' . $data['feed_id'];
            }
            $obj = $this->modx->newObject($class);
            if ($class === 'modResource') {
                $data['alias'] = $this->translit($data['pagetitle']);
            }
        }
        $obj->fromArray($data);
        if ($obj->save()) {
            $this->log('[ImportFeed::manageObject] Обработан ресурс: ' . $data['pagetitle'] . ' ' . print_r($data,1));
            $id = $obj->get('id');
            unset($obj);
            return $id;
        } else {
            $this->log('[ImportFeed::manageObject] Не удалось сохранить объект со следующими данными ' . print_r($data, 1));
        }
    }

    private function translit($value)
    {
        $converter = array(
            'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
            'е' => 'e', 'ё' => 'e', 'ж' => 'zh', 'з' => 'z', 'и' => 'i',
            'й' => 'y', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n',
            'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't',
            'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c', 'ч' => 'ch',
            'ш' => 'sh', 'щ' => 'sch', 'ь' => '', 'ы' => 'y', 'ъ' => '',
            'э' => 'e', 'ю' => 'yu', 'я' => 'ya',
        );

        $value = mb_strtolower($value);
        $value = strtr($value, $converter);
        $value = mb_ereg_replace('[^-0-9a-z]', '-', $value);
        $value = mb_ereg_replace('[-]+', '-', $value);
        $value = trim($value, '-');

        return $value;
    }

    private function truncate($str, $length = 90)
    {
        $arr = explode(' ', $str);
        $c = 0;
        $newArr = [];
        foreach ($arr as $r) {
            $c += mb_strlen($r);
            $newArr[] = $r;
            if ($c > $length) {
                break;
            }
        }
        return implode(' ', $newArr);
    }

    private function log($msg)
    {
        file_put_contents($this->logpath, date('d.m.Y H:i:s') . ' ' . $msg . PHP_EOL, FILE_APPEND);
    }
}
Артур Шевченко
15 марта 2023, 11:48
0
Связка с 1C обязательна, без этого запустить обработку, по словам автора, сложно. Я писал свой импорт, могу скинуть код скорректируешь под себя.