Когда-то давно я наткнулся на статью "Класс для файла настроек модуля options.php" который помогал формировать страницы опций. С тех пор он прочно осел в проектах, значительно упрощая работу. В том числе файл встречался различных коммерческих модулях что показало актуальность его существования.
К сожалению, оригинальная статья датирована 2012 годом и код несколько устарел. Стали видны слабые момент в виду сложности добавления новых типов полей в классе, да и в плане внешнего вида UI Bitrix продвинулся вперёд.
С учетом этих проблем сделана более улучшенная версия, позволяющая легко добавлять новые типы полей, и испольщую библиотеку интерфейсов битрикс. Создан пакет для composer и, если кому-то зачем-то нужно - был опубликован модуль для маркетплейса. Проблема добавления новых типов полей закрыта, внешний вид значительно улчшился (по моему мнению).
Как это выглядит в админке:
Уже реализованы поля типов: строка, число, текст, чекбокс, выпадающий и множественный списки. Можно вешать различные модификаторы на поля, добавлять теги. В будущем планирую добавить добавление файлов, размещение кнопок на которые можно вешать свои скрипты.
Не буду описывать особенности установки и настройки - они детально описаны как на гитхабе, так и в маркетплейсе. Буду рад, если решение будет полезно, проект открыт для доработок как по самому коду, так и по добавлению новых типов полей.
Отдельное спасибо Андрею Новикову, за класс, служивший до этого долгие годы.
В коде происходит выборка 30 элементов, первыми из которых пойдут элементы у которых есть совпадения тегов "Personal" и "2020", далее хотя бы одного из этих двух Тегов, и в конце статьи, не имеющие совпадений.
По желанию можно добавить условие фильтра для выборки только тех элементов, которые содержат хоть один Тег. Например, условие по одному Тегу будет выглядеть так:
array(
"LOGIC" => "OR",
array(
'%TAGS' => " ".$sTagName."," // строка целиком в середине
),
array(
'TAGS' => $sTagName.",%" // строка целиком в начале
),
array(
'TAGS' => "% ".$sTagName // строка целиком в конце
),
array(
'TAGS' => $sTagName // единственная строка в тегах
)
)
Пример запроса SQL:
select ((case when LOCATE('Personal', TAGS)>0 then 1 else 0 end) + (case when LOCATE('2020', TAGS)>0 then 1 else 0 end)) as SORT,
TAGS, ID fr om b_iblock_element WHERE IBLOCK_ID=18 ORDER BY SORT DESC LIMIT 0, 30;
Представим, что нам надо отправлять сообщения в Битрикс24 на события из bitbucket, например Тимлиду ссылку на пулреквест для кодревью.
Bitbucket webhook Вебхуки bitbucket имеют события на пулреквесты, пуши и ишью. Полный перечень тут. Добавляется вебхука в настройках репозитория:
Укажите путь, куда будет отправлен запрос и выберите какие события вы хотите обрабатывать:
Обработка событий и отправка сообщения в чат Следующий скрипт, принимает запрос от bitbucket и отправляет ссылку на пулреквест Тимлиду на портал. Отправленное сообщение состоит из названия и ссылки на пулреквест.
Вот так просто. Логику поиска получателей или отправителей вы уже должны реализовать самостоятельно, в зависимости от ваших процессов и замысла. В нашем примере, для упрощения, ID пользователей прописаны явно.
Для обработки вебхуков bitbucket используется библиотека. Библиотека понимает большинство событий и комплектует объекты данными ответа в виде методов для получения ника автора, ссылки на пулреквест, комментарии коммитов и так далее.
Стандартное автоопределение клавиатуры почти всегда справляется, но некоторые русские слова всё-таки перекидывает на английскую раскладку (например: "шкаф" превращается в "irfa", а "кувшин" в "rediby", очень неприятно, если это ищут). Ну и специфические потребности никто не отменял.
Как ни странно, но отвечающий за эту радость класс CSearchLanguage поддерживает какую-никакую кастомизацию. Копируем всё из папки /bitrix/modules/search/tools/ru/ в папку /bitrix/php_interface/ru/search/ (и аналогично с en версией). Папка local не поддерживается.
По этому пути ищется кастомный класс для этой петрушки и теперь он есть, но требует небольшой доработки. Добавляем в ru версию в файл language.php функцию
function PreGuessLanguage($text, $lang=false)
{
$stop_list = array(
//сюда вписываем любые слова, которым не требуется автоопреление. Ключи важны, значения - любые, но мне так удобнее
"шкаф" => "irfa",
"шкафы" => "irfas",
);
//Indicates that there is no own guess
if(isset($stop_list[$text]))
return true;
else
return false;
//In subclasses you should return array("from" => lang, "to" => lang) to translate
//or return true when no translation nedded
//or parent::GuessLanguage for futher processing
}
Теперь слово "шкаф" не заменяется на "irfa". Класс.
А в en версию аналогичный, но чуть другой:
function PreGuessLanguage($text, $lang=false)
{
$stop_list = array(
"irfa" => "шкаф",
"irfas" => "шкафы",
);
//Indicates that there is no own guess
if(isset($stop_list[$text]))
return array("from" => "en", "to" => "ru");
else
return false;
//In subclasses you should return array("from" => lang, "to" => lang) to translate
//or return true when no translation nedded
//or parent::GuessLanguage for futher processing
}
Теперь и "irfa" вполне себе меняется на "шкаф".
Собственно вот и всё. Можно вписывать любые другие слова и наслаждаться жизнью.
(А ещё можно вынести словарь в отдельный файл и подтягивать значения из него, но ради пары слов я бы таким не занимался.)
Необходимо выполнить доработки по сайту на Битриксе. Было куплено шаблонное решение, сейчас наполняем сайт для сео, нужны доработки. Задачи по сайту будут постоянные в дальнейшем.
На данный момент есть задачи по ТЗ, посмотрите их, оцените объем, напишите ваши условия.
Хочу поделиться способом автоматической минификации стилей и скриптов. Решение было сделано на БУС, но подойдет для других CMS. Описанное решение может оказаться полезным тем, у кого стили распределены по компонентам и не настроены автоматические сборщики типа gulp или webpack и д.р.
Думаю все знают про настройки Оптимизации CSS и JS в главном модуле, а именно "Подключать минифицированные версии CSS и JS файлов". К сожалению в Битриксе не заложена функция создания минифицированных файлов (*.min.css и *.min.js), но зато заложена опция подключения таких файлов.
CSS
Давайте начнем с CSS. Решение базируется на модуле uglifycss Node.js (https://www.npmjs.com/package/uglifycss) . Устанавливаете uglifycss для использования в командной строке (For a command line usage), это очень важно! Заранее позаботьтесь о необходимых доступах к серверу. Выполните к консоле команду:
$ npm install uglifycss -g
После успешной установки узнайте путь нахождения установленного модуля, выполнив команду. К примеру у меня, на боевом сервере был свой путь, а на серверах разработки другой.
whereis uglifycss
В ответе будет строка с путём до uglifycss. В моём случае - /usr/local/bin/uglifycss. Далее мы этот путь будем использовать в классе.
Сам класс:
<?
/*
* Класс по минификации CSS-файлов. Использует NodeJS плагин uglifycss
*/
class CCssMinify
{
const DIRS_FOR_CSS_MINIFY = [
'/local/admin/css/',
'/local/components/',
'/local/templates/main/components/',
'/local/templates/main/theme/build/styles/'
];
const FILES_FOR_CSS_MINIFY = [
'/local/templates/main/styles.css',
'/local/templates/main/template_styles.css'
];
private static $bNeedCacheRefresh = false;
/**
* Агент по минификации стилей
*
* @return string
*/
public static function minifyAgent(): string
{
try {
self::minify();
} catch (Exception $obException) {
CEventLog::Add(array(
"SEVERITY" => "ERROR",
"AUDIT_TYPE_ID" => "MINIFY_CSS",
"MODULE_ID" => "main",
"ITEM_ID" => "",
"DESCRIPTION" => $obException->getMessage()
));
}
return __METHOD__.'();';
}
/**
* Минификация стилей, находящихся внутри директорий, обозначенных в константе \dh\CCssMinify::DIRS_FOR_CSS_MINIFY,
* и в файлах в константе \dh\Seo\CCssMinify::FILES_FOR_CSS_MINIFY
*/
public static function minify()
{
foreach (self::DIRS_FOR_CSS_MINIFY as $sDir) {
$arFilePaths = self::findCssFilesInDir($sDir);
if (!empty($arFilePaths)) {
foreach ($arFilePaths as $sFilePath) {
self::execUglifycss($sFilePath);
}
}
}
foreach (self::FILES_FOR_CSS_MINIFY as $sFilePath) {
self::execUglifycss($sFilePath, true);
}
if (self::$bNeedCacheRefresh && BXClearCache(true, "/css/")) {
CEventLog::Add(array(
"SEVERITY" => "INFO",
"AUDIT_TYPE_ID" => "MINIFY_CSS",
"MODULE_ID" => "main",
"ITEM_ID" => "",
"DESCRIPTION" => "Clear cache"
));
}
}
/**
* Получим путь до UglifyCss
* @return string
*/
private static function getUglifyCssPath(): string
{
return '/usr/local/bin/uglifycss';
}
/**
* Поиск Не минифицированных css файлов в Директории (и её поддиректориях)
*
* @param $sDirPath
* @return array
*/
public static function findCssFilesInDir($sDirPath): array
{
$arFilePaths = [];
$obDirectory = new RecursiveDirectoryIterator($_SERVER["DOCUMENT_ROOT"].$sDirPath);
$obIterator = new RecursiveIteratorIterator($obDirectory);
foreach ($obIterator as $obInfo) {
$file_formal = substr($obInfo->getfileName(), strrpos($obInfo->getfileName(), ".") + 1);
$name_search = array("css"); // Список форматов
foreach ($name_search as $key_name) {
if (
$file_formal == $key_name &&
!stristr($obInfo->getfileName(), '.min.css')
) {
$arFilePaths[] = $obInfo->getPathname();
}
}
}
return $arFilePaths;
}
/**
* Выполнить минификацию
*
* @param $sFilePath
* @param bool $bAddDocumentRoot
*/
private static function execUglifycss($sFilePath, bool $bAddDocumentRoot = false)
{
if ($bAddDocumentRoot) {
$sFilePath = $_SERVER["DOCUMENT_ROOT"].$sFilePath;
}
$sMinFilePath = str_replace('.css', '.min.css', $sFilePath);
if (
!file_exists($sMinFilePath) ||
(
file_exists($sMinFilePath) &&
filectime($sFilePath) > filectime($sMinFilePath)
)
) {
/*
* https://www.npmjs.com/package/uglifycss
--output f puts the result in f file
*/
shell_exec(self::getUglifyCssPath().' '.$sFilePath.' --output '.$sMinFilePath);
self::$bNeedCacheRefresh = true;
}
}
}
В приведенном выше классе есть метод \CCssMinify::getUglifyCssPath, в котором как раз мы возвращаем путь до uglifycss, полученный через whereis uglifycss. Мы не используем специфических настроек запуска uglifycss, только путь до минифицированной версии, которая будет находится рядом с основной.
Сам Агент реализован методом \CCssMinify::minifyAgent(); Как видно из кода, метод автоматически очищает CSS КЕШ сайта. Пересозданию минификаций подвержены только те файлы css, время изменения которых позже созданных для них минификаций. В Административном разделе агент имеет следующие настройки:
Среднее время выполнения агента на моём проекте: 7-8 секунд, для ПОЛНОГО пересоздания. В режиме обычно работы, при пересоздании только изменённых файлов, не превышает 0.1 секунды. Не берусь говорить точно об объёмах, но проект довольно крупный, так что не сильно беспокойтесь, что всё повиснет. Хотя перестраховаться не мешает.
Пример результата работы агента:
C CSS мы закончили. давай перейдем к JS.
JS
Решение базируется на модуле uglifyjs Node.js (https://www.npmjs.com/package/uglify-js). Устанавливаете uglifyjs для использования в командной строке (From NPM for use as a command line app), это очень важно! Заранее позаботьтесь о необходимых доступах к серверу. Выполните к консоле команду:
npm install uglify-js -g
После успешной установки узнайте путь нахождения установленного модуля, выполнив команду.
whereis uglifyjs
В ответе будет строка с путём до uglifyjs. В моём случае - /usr/local/bin/uglifyjs. Далее мы этот путь будем использовать в классе.
Сам класс:
<?
/*
* Класс по минификации JS-файлов. Использует NodeJS плагин uglifyjs
*/
class CJsMinify
{
const DIRS_FOR_JS_MINIFY = [
'/local/admin/js/',
'/local/templates/main/js/',
'/local/components/',
'/local/templates/main/components/'
];
/**
* Агент по минификации скриптов
*
* @return string
*/
public static function minifyAgent()
{
try {
self::minify();
} catch (Exception $obException) {
CEventLog::Add(array(
"SEVERITY" => "ERROR",
"AUDIT_TYPE_ID" => "MINIFY_JS",
"MODULE_ID" => "main",
"ITEM_ID" => "",
"DESCRIPTION" => $obException->getMessage()
));
}
return __METHOD__.'();';
}
/**
* Минификация скриптов, находящихся внутри директорий, обозначенных в константе \dh\CJsMinify::DIRS_FOR_JS_MINIFY
*/
public static function minify()
{
$bNeedCacheRefresh = false;
foreach (self::DIRS_FOR_JS_MINIFY as $sDir) {
$arFilePaths = self::findJsFilesInDir($sDir);
if (!empty($arFilePaths)) {
foreach ($arFilePaths as $sFilePath) {
$sMinFilePath = str_replace('.js', '.min.js', $sFilePath);
if (
!file_exists($sMinFilePath) ||
(
file_exists($sMinFilePath) &&
filectime($sFilePath) > filectime($sMinFilePath)
)
) {
/*
* https://www.npmjs.com/package/uglify-js
-c, --compress [options] Enable compressor/specify compressor options
-m, --mangle [options] Mangle names/specify mangler options
-o, --output <file> Output file path (default STDOUT)
*/
shell_exec(self::getUglifyJsPath().' '.$sFilePath.' -c -m -o '.$sMinFilePath);
$bNeedCacheRefresh = true;
}
}
}
}
if ($bNeedCacheRefresh && BXClearCache(true, "/js/")) {
CEventLog::Add(array(
"SEVERITY" => "INFO",
"AUDIT_TYPE_ID" => "MINIFY_JS",
"MODULE_ID" => "main",
"ITEM_ID" => "",
"DESCRIPTION" => "Clear cache"
));
}
}
/**
* Получим путь до UglifyJS
* @return string
*/
private static function getUglifyJsPath() {
return '/usr/local/bin/uglifyjs';
}
/**
* Поиск Не минифицированных js файлов в Директории (и её поддиректориях)
*
* @param $sDirPath
* @return array
*/
public static function findJsFilesInDir($sDirPath)
{
$arFilePaths = [];
$obDirectory = new RecursiveDirectoryIterator($_SERVER["DOCUMENT_ROOT"].$sDirPath);
$obIterator = new RecursiveIteratorIterator($obDirectory);
foreach ($obIterator as $obInfo) {
$file_formal = substr($obInfo->getfileName(), strrpos($obInfo->getfileName(), ".") + 1);
$name_search = array("js"); // Список форматов
foreach ($name_search as $key_name) {
if (
$file_formal == $key_name &&
!stristr($obInfo->getfileName(), '.min.js') &&
!stristr($obInfo->getfileName(), '.map.js')
) {
$arFilePaths[] = $obInfo->getPathname();
}
}
}
return $arFilePaths;
}
}
В приведенном выше классе есть метод CJsMinify::getUglifyJsPath, в котором как раз мы возвращаем путь до uglifyjs, полученный через whereis uglifyjs. В коде имеются пояснения выставленных опций, с которыми подробнее можете ознакомится на странице https://www.npmjs.com/package/uglify-js
Сам Агент реализован методом CJsMinify::minifyAgent(). Как видно из кода, метод автоматически очищает JS КЕШ сайта. Пересозданию минификаций подвержены только те файлы js, время изменения которых позже созданных для них минификаций. В Административном разделе агент имеет следующие настройки:
Среднее время выполнения агента на моём проекте: 30-32 секундs, для ПОЛНОГО пересоздания. В режиме обычной работы, при пересоздании только изменённых файлов, не превышает 0.1 секунды.
Пример результата работы:
Бонус:
В качестве бонуса поделюсь примером .gitignore, чтобы не засорять репозиторий минификациями:
Проблема: у нас, например, есть 10 отдельных кешируемых страниц, нам надо сбрасывать разом весь их кеш. Без тегированного кеша надо знать все ключи и перебирая их сбрасывать. Добавляя 11 страницу, нам надо не забыть добавить его ключ в сбрасыватель кеша, что легко забыть, или сайт начнет выдавать баги.
Тут нам помогает тегированный кеш. При генерации страниц, мы помечаем данные одним общим тегом и по нему сбрасываем кеш. Добавляя 11 страницу, мы добавляем на странице общий тег и эта страница начинает сбрасываться автоматически.
use \Bitrix\Main\Data\Cache;
use \Bitrix\Main\Application;
$cache = Cache::createInstance(); // Служба кеширования
$taggedCache = Application::getInstance()->getTaggedCache(); // Служба пометки кеша тегами
/*
* Чтобы тегированный кеш нашел что ему сбрасывать, необходим
* одинаковый путь в $cache->initCache() и $taggedCache->startTagCache()
* У нас путь указан в $cachePath
*/
$cachePath = 'mycachepath';
$cacheTtl = 3600;
$cacheKey = 'mycachekey';
if ($cache->initCache($cacheTtl, $cacheKey, $cachePath))
{
$vars = $cache->getVars();
/*
* Еще тут можно вывести данные в браузер, через $cache->output();
* Тогда получится замена классу CPageCache
*/
}
elseif ($cache->startDataCache())
{
// Начинаем записывать теги
$taggedCache->startTagCache($cachePath);
$vars = [
'date' => date('r'),
'rand' => rand(0, 9999), // Если данные закешированы - число не будет меняться
];
// Добавляем теги
// Кеш сбрасывать при изменении данных в инфоблоке с ID 1
$taggedCache->registerTag('iblock_id_1');
// Кеш сбрасывать при изменении данных в инфоблоке с ID 2
$taggedCache->registerTag('iblock_id_2');
// Если что-то пошло не так и решили кеш не записывать
$cacheInvalid = false;
if ($cacheInvalid)
{
$taggedCache->abortTagCache();
$cache->abortDataCache();
}
// Всё хорошо, записываем кеш
$taggedCache->endTagCache();
$cache->endDataCache($vars);
}
// Данные будут обновляться раз в час или при обновлении данных в инфоблоках 1 и 2
print_r($vars);
Когда необходимо сбросить кеш по тегу:
use \Bitrix\Main\Application;
$taggedCache = Application::getInstance()->getTaggedCache(); // Служба пометки кеша тегами
/*
* Где-то на отдельной странице чистим кеш по тегу
*/
$taggedCache->clearByTag('iblock_id_28');
Как сделать чтобы мой кеш сбрасывался автоматически, при изменении данных в инфоблоке? Использовать готовые теги: iblock_id_{ID} - это зарезервированные теги битрикса для инфоблоков, сброс кеша по этим тегам уже есть в классах работы с инфоблоками. iblock_id_new - по этому тегу кеш сбрасывается при создании инфоблоков. Списка используемых тегов в битрикс я не нашел.
Для себя вы можете придумать свои теги.
Везде пишут что, чтобы работало всё это чудо, надо добавить в dbconn.php строку: define("BX_COMP_MANAGED_CACHE", true); Но у меня и без нее всё работает
Сталкивались ли вы с тем, что клиенты используют обновления битрикс как манипулятивный рычаг давления, чтобы спихнуть ошибки предыдущих разработчиков на тех, кто произвел обновление ядра (ядро не модифицировалось предыдущим разработчиком)?
(доводы клиента: до обновления все работало и прочее, верните все как было)
или это классическая ментальная ошибка, когда произошедшие одновременно вещи считают связанными?
или все-таки есть риски и большие при обновлении ядра?
хотя обновления ядра объективно - это ресурс, причем платный.
Всё более актуальной становится проблема недоставки писем, отправляемых через phpmailer. Не доверяют почтовые сервисы таким сообщениям... Особенно их не любит iCloud. Тут даже SPF не помогает. Помогает только DKIM. А ещё круче, когда письма отсылает платный сервис. Например, SendPulse. Ну, надо же всем зарабатывать, ведь правда?
Короче, нам было необходимо обеспечить 100% доставку писем о результатах тестов на ковид. В итоге, вот что получилось.
1. Регистрируемся на Sendpulse 2. Скачиваем в php_interface Битрикса библиотеку (враппер) их API 3. Прописываем в init.php следующее:
Таким образом, мы сохранили отправку всех сообщений, на базе почтовых шаблонов Битрикса, с поддержкой прикрепленных файлов (в CEvent::Send теперь есть массивчик под них). Фишка в том, что мы аннулируем отправку стандартных писем Битрикса, добавив функцию custom_mail. При этом перехватываем её через обработчик события, и шлём нужные нам письма через Сендпульс.
Единственный момент - штука не поддерживает спецслова в шаблонах типа DEFAULT_EMAIL_FROM, SITE_NAME и т.д. Только поля почтового события. Ну, это ерунда. Главное, что теперь почта ходит лучше, чем DHL.
После отключения mbstring.func_overload для установки обновлений Битрикса, перестали работать строковые функции типа strtoupper(). Техподдержка поделилась секретной ссылкой, как заставить работать: https://helpdesk.bitrix24.ru/open/11473408
Добрый день. В файле init.php объявлен обработчик события OnProlog, в нем реализована логика добавления js-скрипта в хэдер сайта, но скрипт не добавляется. Функция addJs возвращает true. Скажите, пожалуйста, в чем может быть причина?
Добрый день! Подскажите, пожалуйста! Недавно начал работать с битрикс, достался готовый магазин. При уходе с сайта выскакивает всплывающие окно. Шаблон аспро стоит. перелопатил всю админку, готовых решений не установлено по всплывающим окнам. Можете подсказать в каком направлении искать? Нужно убрать это окно.
Не устанавливается значение свойства. А если перед выполнением в SALE было Y, то после выполнения удаляет значения свойства. То есть при выполнении просто происходит удаление значения свойства.
Здравствуёте, Хотел спрость на тему вот этого вопроса https://dev.1c-bitrix.ru/community/forums/messages/forum6/topic84573/message470217/#message470217. У меня похожая проблема, должно сначало приходить письмо оформлемления заказа, потом после подтвердение заказа магазином, приходит письмо с сылкой на оплату, после оплаты приходит письмо о успешной оплате, но вот проблема на этом моменте опять приходит письмо со ссылкой об оплате(ссылка уже не действующая). Я думаю мой слечай. Подскажите пожалуйста по подробей, в каком файле вставить \Bitrix\Sale\Notify::setNotifyDisable(true); перед вызовом DoSaveOrder.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Если возникли проблемы с очисткой cookies в браузере и не получается авторизоваться, можно инициировать удаление лишней куки со стороны сервера:
1. Если не используется многосайтовость, а поле «Доменное имя» очищено, то надо удалить куку с точкой в начале. Для этого впишите в любую строку файла dbconn.php следующий код: setcookie("PHPSESSID", "", 777, '/', '.site.ru'); где site.ru - имя вашего домена.
2. Если используется многосайтовость или не очищено поле «Доменное имя», то впишите код: setcookie("PHPSESSID", "", 777, '/'); строго без имени домена.
Не добавляются платежные системы после переноса сайта. Раньше при добавлении платежной системы было выпадающее меню с различными типами платежных систем, а теперь их нет. Подскажите, где начинать искать решение этой проблемы.
Добрый день, друзья! На дворе 2021 год, а проблема до сих пор не решена со стороны разработчика. Вот и я столкнулся с этой проблемой. Предисловие: был перенос хостинга (c VPS на другую VPS). Перенос прошел почти гладко, но вот с почтой загвоздки. Итак, что сейчас сделано: настроена отправка почты через Postfix, агенты на кроне, все тесты проходят хорошо (все зелененькие галочки). В терминале почта нормально отправляется и доставляется через юзеров root и bitrix. Но при выполнении заказов, при регистрации пользователя и др. событий с сайта, почта отправляется, но не доходит до пользователя. Симлинк на /ets/msmtp сделан. В crontab от bitrix прописан PATH. Крон срабатывает нормально (через минуту после события на сайте появляется в логах msmtp). Лог msmtp
Mar 08 09:11:01 host=127.0.0.1 tls=on auth=off from=noreply@binpro.ru recipients=alex-00@mail.ru mailsize=3749 smtpstatus=250 smtpmsg='250 2.0.0 Ok: queued as A6E0723B4D' exitcode=EX_OK Mar 08 09:40:05 host=127.0.0.1 tls=on auth=off from=noreply@binpro.ru recipients=velobox@inbox.ru mailsize=36938 smtpstatus=250 smtpmsg='250 2.0.0 Ok: queued as 8FC7E23B4C' exitcode=EX_OK Mar 08 10:09:03 host=127.0.0.1 tls=on auth=off from=noreply@binpro.ru recipients=alex-00@mail.ru mailsize=3749 smtpstatus=250 smtpmsg='250 2.0.0 Ok: queued as D129E23B51' exitcode=EX_OK
Настройки msmtp
# smtp account configuration for default account default logfile /home/bitrix/msmtp_default.log host 127.0.0.1 port 25 from noreply@binpro.ru keepbcc off auth off tls on tls_certcheck off
echo "hello from mail.com.ru." | msmtp --debug -a site.ru test@site.ruгде site.ru - значение account в конфиге .msmtprc
пример конфига для яндекса
# smtp account configuration for site.ru account site.ru logfile /home/bitrix/msmtp_site.ru.log host smtp.yandex.ru port 587 from noreply@site.ru keepbcc on auth on user noreply@site.ru password *********** tls on tls_starttls on tls_certcheck off
Не помогают вариации настроек главного модуля для отправки почты и т.д.
Укажите тут /bitrix/admin/settings.php?lang=ru&mid=main&tabControl_active_tab=tab_mail&back_url_settings= "Дополнительный параметр для передачи функции mail:" -a site.ru (см. скрин)
Чтобы гуглю было проще искать: SUCCESS_EXEC='F' , b_event, не работает почта msmtprc
После восстановления пароля, пользователь нажимает на кнопку авторизации, вводит свой логин и новый пароль, но авторизация не происходит. Ошибок не выдает, просто обновляется страница и на этом все. После повторного нажатия на кнопку авторизации - аторизует. В чем может быть проблема?
Всем привет! У меня вот такой вопрос, какие можете посоветовать курсы по битриксу? И вообще что думаете по перспективам развития битрикса в Казахстане? Стоит ли сейчас ставить фокус на его изучение, либо работать лучше с Российскими партнерами? все таки если быть откровенным в России разработчики битрикса куда более продвинутые и фрилансера найти гораздо легче? Спасибо заранее за ответ)
Иногда плавающая "панель управления Битрикс" вызывает трудности. Это бывает, когда на сайте сделана плавающая шапка. Тогда либо (чаще всего) панель Битрикса перекрывает шапку либо наоборот и доступ другой панели становится затруднительным. Да, для неавторизованных и других пользователей, которым не доступна панель администрирования - проблем нет. Вот и приходится заниматься изобретением колеса! Вот если бы была возможность "прятать за край" панель администратора Битрикс и показывать при подводе мышки к краю экрана - вот это было бы очень удобно! Помните была подобная функция в Windows, позволяющая прятать за край экрана "Панель задач"?
Я так понял, пока гуглил, что CUSTOM_PRICE очень "любим" многими за то, что ведёт себя как FINAL_PRICE (которого нет, но суть передаёт лучше). Знаю ситуации, когда такое поведение необходимо. Но так получилось, что чаще мне надо добавить кастомную цену на товар не отключая при этом последующие правила работы с корзиной. И вот я нашёл способ добавлять заранее известную наценку (так можно и понижать цену, но я только про наценку буду говорить) не используя CUSTOM_PRICE. Пропишу основные моменты.
Раз у меня все такие товары всё равно добавляются через кастомный ajax запрос, и наценка известна в момент добавления, то могу спокойно её передавать свойством товара в корзине.
Просто в шаблоне корзины её не выводим для пользователя отдельно, раз она уже считается, тогда никого она смущать не будет. Будем использовать кастомный CatalogProvider (это слишком легко гуглится, чтобы дополнительно расписывать) и слегка дополним getProductData.
/**
* @param array $products
*
* @return Sale\Result
*/
public function getProductData(array $products)
{
$result = parent::getProductData($products);
// Во избежании казусов при работе с командной строкой PHP
if(!Bitrix\Main\Context::getCurrent()->getSite())
return $result;
$basket = Sale\Basket::loadItemsForFUser(Sale\Fuser::getId(), Bitrix\Main\Context::getCurrent()->getSite());
$productDataList = array();
foreach($result->getData()['PRODUCT_DATA_LIST'] as $productId => $arProduct)
{
foreach($arProduct['PRICE_LIST'] as $basketItemId => &$arItem)
{
// При добавлении в корзину нет у товара ID, мы не сможем его свойства тут посмотреть
if((int)$basketItemId !== 0)
{
if($basketItem = $basket->getItemById($basketItemId))
{
$properties = $basketItem->getPropertyCollection()->getPropertyValues();
// Если есть наценка, добавляем её к цене
if(!empty($properties['PRICE_ADD']) && isset($properties['PRICE_ADD']['VALUE']))
{
$arItem['BASE_PRICE'] = $arItem['BASE_PRICE'] + (float)$properties['PRICE_ADD']['VALUE'];
$arItem['PRICE'] = $arItem['PRICE'] + (float)$properties['PRICE_ADD']['VALUE'];
}
}
}
}
$productDataList[$productId] = $arProduct;
unset($arItem);
}
$result->setData(['PRODUCT_DATA_LIST' => $productDataList]);
return $result;
}
Тут всё просто. При вызове обработчика, в корзине находятся товары со свойством наценки, их цена обновляется. Но если товар только добавили, он игнорируется (не достать его свойства пока что в этой функции, их вообще может ещё не быть у товара). На этом моменте компонент bitrix:sale.basket.basket при загрузке страницы уже работает как надо. И если раньше (у вас на проекте) обновление количества товара в корзине происходило через
$item->setField('QUANTITY', $value);
придётся его поменять на
$item->setFieldNoDemand('QUANTITY', $value);
(у меня переделанный шаблон, скопированный ещё тогда, когда цена пересчитывалась при нажатии кнопки "Пересчитать", и ajax обновление количества ручками писанное)
К сожалению, у меня не получилось заставить компонент плавающей корзины bitrix:sale.basket.basket.line обновлять цену при добавлении. Но если копировать его в своё пространство имён и наследовать, то в ajax.php перед вызовом компонента добавляем пересчёт корзины
// Не надо нам при удалении ничего пересчитывать здесь. Таков путь.
if(!$_POST['sbblRemoveItemFromCart'])
{
$basket = \Bitrix\Sale\Basket::loadItemsForFUser(\Bitrix\Sale\Fuser::getId(), SITE_ID);
$refreshStrategy = \Bitrix\Sale\Basket\RefreshFactory::create(\Bitrix\Sale\Basket\RefreshFactory::TYPE_FULL);
$result = $basket->refresh($refreshStrategy);
$basket->save();
}
И где-нибудь повыше модуль sale подключаем (чего стесняться в компоненте корзины, он всё равно там будет). В стандартном шаблоне SITE_ID задаётся выше, так что он определён. И у меня нельзя в плавающей корзине менять количество, только удалять. Не знаю, как себя поведёт при другом раскладе, но теоретически никаких проблем быть не должно.
Собственно всё. Теперь наша наценка - полноценная часть цены. Без всяких блокирующих скидки CUSTOM_PRICE'ов. Единственное, что не делает этот способ совсем незаметным, так это вывод свойства наценки в заказе в админке менеджерам. Но лично я могу этим пренебречь.