Готовые решения
[СДЕЛАЙ САМ] Контексты для регионов в интернет-магазине.
Задача: сделать максимально простое добавление новых контекстов на сайт для создания региональных копий с собственными ресурсами, robots.txt и sitemap.xml, уникальными для каждого региона ресурсами и ценами на некоторые товарные позиции.
Проблемы:
1. Вывод галереи изображений товаров
2. Добавление дополнительных категорий к товарам в новом контексте.
3. Связывание товаров.
Решение:
1. Настраиваем редирект со всех поддоменов на основной домен (как это сделать уточняйте у хостера или в Google);
2. Подключаем плагин для переключения контекстов:
Логика такая: определяем по url какой контекст запросили, если это не основной контекст (web), то переключаем контекст на запрошенный, в противном случае смотрим есть ли в $_COOKIE город, если нет, то устанавливаем $_COOKIE['curCity'], проверяем есть ли в списке контекстов запрошенный, если есть, то переключаем, если нет остаемся на основном контексте. На фронте этим управляет вот такой код
2.1 если кому интересно город я определяю через сайт DaData.Код сниппета detectRegion тут.
3. Подключаем плагин для создания настроек контекста, при копировании или создании нового.
Проблемы:
1. Вывод галереи изображений товаров
2. Добавление дополнительных категорий к товарам в новом контексте.
3. Связывание товаров.
Решение:
1. Настраиваем редирект со всех поддоменов на основной домен (как это сделать уточняйте у хостера или в Google);
2. Подключаем плагин для переключения контекстов:
<?php
// Работаем только на фронтенде
if ($modx->event->name != 'OnHandleRequest' || $modx->context->key == 'mgr') {return;}
// Определяем запрашиваемый хост
$host = $_SERVER['HTTP_HOST'];
$ctx = $modx->getObject('modContextSetting', array('key' => 'http_host', 'value' => $host));
if($ctx->get('context_key') != 'web'){
$modx->switchContext($ctx->get('context_key'));
}
Логика такая: определяем по url какой контекст запросили, если это не основной контекст (web), то переключаем контекст на запрошенный, в противном случае смотрим есть ли в $_COOKIE город, если нет, то устанавливаем $_COOKIE['curCity'], проверяем есть ли в списке контекстов запрошенный, если есть, то переключаем, если нет остаемся на основном контексте. На фронте этим управляет вот такой код
let btns = document.querySelectorAll('.jsChooseBtn'), // это кнопки подтверждения ДА и НЕТ
tooltip = document.querySelector('.jsCityTooltip'); // это само окно с вопросом "ЭТО ВАШ ГОРОД"
for(let i = 0; i < btns.length; i++){
btns[i].addEventListener('click', function(e){
tooltip.classList.add('d-none');
if(e.target.classList.contains('jsCityConfirm')){ // если нажали да
document.cookie = 'curCity='+ e.target.dataset.city + '; path=/;domain=ecodecking.ru';
}
});
}
if(document.cookie.indexOf('curCity') != -1){ // проверяем есть ли город в куках
tooltip.classList.add('d-none');
}
2.1 если кому интересно город я определяю через сайт DaData.Код сниппета detectRegion тут.
<?php
$url = 'https://suggestions.dadata.ru/suggestions/api/4_1/rs/iplocate/address';
$siteIsAvaliable = $modx->runSnippet('isSiteAvailible', array('url' => $url));
if($siteIsAvaliable){
$token = $modx->getOption('dadata_api_key');
$ip = $_SERVER['REMOTE_ADDR'];
$headers = array(
'Accept: application/json',
'Authorization: Token ' . $token
);
$ch = curl_init($url.'?ip='.$ip);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HEADER, false);
$result = json_decode(curl_exec($ch),1);
curl_close($ch);
$region = $result['location']['data']['city'];
return $region;
}else{
$modx->log(1, 'detectRegion: Не возможно определить регион. Сервис DaData недоступен');
return false;
}
3. Подключаем плагин для создания настроек контекста, при копировании или создании нового.
[СДЕЛАЙ САМ] mFilter2 фильтрация по диапазону дат.
Приветствую, к сожалению не нашёл приемлемого для себя решения данной задачи поэтому «написал» своё, в кавычках потому что немного подправил. На мой взгляд очевидно, что дата это число, а в mFilter2 есть фильтрация по диапазону чисел, значит нужно превратить дату в число. Чтобы при обновлении компонента ничего не сломалось, создадим свой класс фильтрации customfilter.class.php в папке core/components/msearch2/custom/filters/, потом прописываем в системную настройку mse2_filters_handler_class = CustomFiltersHandler
[Мультиязычность] Скрипт массовой связки разных языковых версий (в разных контекстах) по URI [Babel]
Надо добавить мультиязычность на сайт через Babel. Для этого была сделана русская версия (в контексте web) и затем сделаны ее копии (en и de).
Далее нужно было связать все версии языков ресурсов между собой.
Далее нужно было связать все версии языков ресурсов между собой.
[УПУЩЕННОЕ] Настраиваем расчёт скидки в minishop2 версии 2.7.0 и выше
Приветствую, как-то так случилось, что я упустил вот этот релиз, и тут понадобилось мне сделать возможность устанавливать скидку на каждый товар, начал я писать плагин и увидел в объекте корзине два ранее не встречавшихся ключа discount_price и discount_cost, связался с @Иван Бочкарев, он меня и просветил, что расчёт скидки уже внедрён, надо только указать старую цену и всё посчитается. Это круто, но зачастую удобнее указать скидку в % от цены или просто числом, поэтому я написал небольшой плагин, который при сохранении товара рассчитывает новую цену, а старую записывает в поле old_price, если же скидка равна 0 или не указана и поле old_price заполнено, то его содержимое переносится в price, а old_price обнуляем. Уверен, что многие сами могут такой плагин написать, но кому-то возможно захочется сэкономить 5 минут, код под катом.
Платежка Stripe для miniShop2

Всем привет!
Вчера обратился человек, с просьбой написать платежку для системы Stripe. Я что-то загнул кажись ему цену за работу, в итоге он пропал. Ну раз просил, видать есть спрос на данную платежку.
mfilter2 - вывод значений фильтра в алфавитном порядке, с группировкой по первой букве
Добрый день, давно не писал ничего, вот делюсь рабочим вариантом группировки значений фильтра по первой букве.
Как это должно выглядеть:

Как это должно выглядеть:

[СДЕЛАЙ САМ] Личный кабинет своими руками. Редактирование персональных данных.
После того как авторизация и регистрация сделаны нужно дать возможность пользователям редактировать свои персональные данные. Для этого создаем ресурс Редактирование данных, в котором вызываем AjaxForm
Localizator, модификаторы для получения переводов
Локализатор очень хорошая штука и хотелось ее опробовать, но как то не задалось переносить уже готовые данные со всего сайта в поля локализатора. Пришлось искать решения для вывода данных и при том с учетом того что же есть в основном контенте страниц.
Итак… модификаторы для вывода данных, пошагово:
1. создаем плагин…
Итак… модификаторы для вывода данных, пошагово:
1. создаем плагин…
[СДЕЛАЙ САМ] Добавляем отображение прогресса загрузки файлов в AjaxForm
Пишу себе на память, но вдруг кому-то тоже нужно. На 17-ю строку в этом файле assets/components/ajaxform/js/default.js добавляем вот такой код
[СДЕЛАЙ САМ] Получение ЛЮБЫХ полей пользователя
Если использовать как модификатор для fenom то запись будет такой:
{идентификатор пользователя | getUserInfo: 'fieldname1,fieldname2,extended.fieldname'}
Если как сниппет, то такой:
{'getUserInfo' | snippet:[
'input' => 'идентификатор пользователя',
'options' => 'fieldname1,fieldname2,extended.fieldname'
]}
идентификатор пользователя — id;
fieldname1,fieldname2 — основные поля профиля без префикса;
extended.fieldname — дополнительные поля профиля, те что хранятся в json и редактируются на отдельной вкладке, префикс extended. обязателен.
Собственно сам код сниппета getUserInfo (input и options передавать обязательно):
{идентификатор пользователя | getUserInfo: 'fieldname1,fieldname2,extended.fieldname'}
Если как сниппет, то такой:
{'getUserInfo' | snippet:[
'input' => 'идентификатор пользователя',
'options' => 'fieldname1,fieldname2,extended.fieldname'
]}
идентификатор пользователя — id;
fieldname1,fieldname2 — основные поля профиля без префикса;
extended.fieldname — дополнительные поля профиля, те что хранятся в json и редактируются на отдельной вкладке, префикс extended. обязателен.
Собственно сам код сниппета getUserInfo (input и options передавать обязательно):
<?php
if(!$input){return false;}
if(!$options){return false;}
$sql = 'SELECT * FROM `modx_users` LEFT JOIN `modx_user_attributes` ON modx_users.id = modx_user_attributes.internalKey WHERE modx_users.id=:id';
$statement = $modx->prepare($sql);
if ( $statement->execute(array('id'=>$input)) ) {
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
}
$output = [];
$options = explode(',',$options);
if($result) {
foreach($options as $option){
$option = trim($option); // это на тот случай если полей много и захочется сделать перенос строки
if (strpos($option,'extended.') === false) {
$output[$option] = $result[0][$option];
} else {
$extended = json_decode($result[0]['extended'], 1);
$ext = explode(".", $option);
$ext = $ext[1];
if(strpos($ext,'|') === false){
$output[$ext] = $extended[$ext];
}else{
$cont = explode("|", $ext);
$key = $cont[0];
$val = $cont[1];
$output[$key][$val] = $extended[$key][$val];
}
}
}
}
if(count($output) == 1 && !$extended){
return $output[$options[0]];
}elseif(count($output) == 1 && $extended){
return $output[$ext];
}else{
return $output;
}