Уже достаточно давно в продукте появилось поддержка прикрепления файлов к почтовым событиям.
Прикрепить файл просто: вызываем метод Bitrix\Main\Mail\Event::send, в ключе FILE передаем либо массив ID из базы, либо массив путей. В старом методе CEvent::Send, который по факту уже является оберткой для нового, указываем параметр $files (описан в документации).
При передаче путей, Битрикс предварительно сохраняет файлы в таблицу b_file с помощью CFile::SaveFile. Общая логика такая:
Сохраняются файлы в таблицу b_file (в случае указания путей)
Добавляется запись в таблицу b_event_attachment, которая содержит ID события (EVENT_ID) и ID файла (FILE_ID).
Агент рассылает письма, выбирая эти данные.
Пусть у нас есть:
почтовый шаблон для события SOME_EVENT с заранее заполненным полем «Кому»
два файла в корне file.jpg и file.jpg.
Следующий код успешно отправит письмо с 2 вложениями, но только на кодировке utf-8:
use Bitrix\Main\Mail\Event; Event::send([ "EVENT_NAME" => "SOME_EVENT", "LID" => "s1", "C_FIELDS" => [], "FILE" => [ $_SERVER["DOCUMENT_ROOT"]."/файл.jpg", $_SERVER["DOCUMENT_ROOT"]."/file.jpg" ] ); |
С windows-1251, в лучшем случае, в письме придет только один файл.
Необходимо правильно указать путь, так как кодировка сайта у нас windows-1251, а кодировка файловой системы – utf-8.
Полный код будет выглядеть следующим образом:
use \Bitrix\Main\Mail\Event; use \Bitrix\Main\IO; Event::send([ "EVENT_NAME" => "SOME_EVENT", "LID" => "s1", "C_FIELDS" => [], "FILE" => [ IO\Path::ConvertLogicalToPhysical($_SERVER["DOCUMENT_ROOT"]."/файл.jpg"), $_SERVER["DOCUMENT_ROOT"]."/file.jpg" ] ]); |
Стоит отметить, что Битрикс не выдает вообще никакой ошибки в первом варианте (когда CFile::SaveFile отработал некорректно), а просто заносит в таблицу b_event_attachment в поле FILE_ID значение 0.
C IO\Path::ConvertLogicalToPhysical файл сохраняется корректно.
Вроде бы задача решена. Но если мы попробуем сейчас выполнить этот код, то придет 2 файла:
«file.jpg» - нормальный, а «файл.jpg» с нулевой длиной.
Происходит это из-за бага, который есть внутри ядра. Тикет создан в феврале 2017 с категорией «Ошибки», пока исправления нет. Суть в том, что агент, который отправляет письма производит двойную конвертацию имени файла. Сначала в CFile::MakeFileArray, а затем в классе Bitrix\Main\Mail\Mail:: setAttachment при вызове Bitrix\Main\IO\File::getFileContents
«Исправляем» это:
И вносим правки в ядро
, добавляя в метод setAttachment блок исключения:
Было:
try {
$fileContent = File::getFileContents($attachment["PATH"]);
} catch (\Exception $exception) {
$fileContent = '';
} |
Стало:
try {
$fileContent = File::getFileContents($attachment["PATH"]);
} catch (\Bitrix\Main\IO\FileNotFoundException $exception) {
try {
$fileContent = File::getFileContents(\Bitrix\Main\IO\Path::convertPhysicalToLogical($attachment["PATH"]));
} catch (\Exception $exception) {
$fileContent = '';
}
} catch (\Exception $exception) {
$fileContent = '';
} |
Т.е. если при попытке получить содержимое файла вывалилось исключение «Файл не найден», пробуем получить содержимое по сконвертированному в обратную сторону имени файла. Теперь все файлы отправляются
| Прочитал - передай другому |
Версионность кода;
Версионность БД;
Автоматические сборки новых версий проекта.
Один и тот же код на production и develop версиях сайта;
Одна и та же структура БД на production и develop;
Автоматический перенос изменений на production (желательно вообще без участия специалиста) по расписанию или по событию.
Неприкосновенность ядра;
Неприкосновенность структуры БД;
Возможность обновления ядра без последствий;
Технология «эрмитаж», позволяющая администратору сайта создавать разделы/страницы на бою с помощью визуального редактора;
Настройки компонентов (читай разделов сайта) хранятся в файлах.

/nbproject/ *.swp *.swo .idea /bitrix /upload /sitemap* *.tst *.zip *.log *.pdf *.xls *.txt *.html *.old gitignore gitignore/* |
Отслеживать изменения в репозитории;
Делать git pull нужной ветки;
Архивировать файлы;
ssh.
"SEF_URL_TEMPLATES" => array( "sections" => "", "section" => "#SECTION_CODE_PATH#/", "element" => "#SECTION_CODE_PATH#/#ELEMENT_CODE#/", "compare" => "", "smart_filter" => "#SECTION_CODE_PATH#/#ELEMENT_CODE#/#SMART_FILTER_PATH#/", ) |
array( "CONDITION" => "#^/#", "RULE" => "", "ID" => "bitrix:catalog", "PATH" => "/pages/index.php", ), |


public static function Definders() {
\Bitrix\Main\Loader::includeModule('iblock');
$result = \Bitrix\Iblock\IblockTable::getList(array(
'select' => array('ID', 'IBLOCK_TYPE_ID', 'CODE'),
));
while ($row = $result->fetch()) {
$CONSTANT = ToUpper(implode('_', array('IBLOCK', $row['IBLOCK_TYPE_ID'],
$row['CODE'])));
if (!defined($CONSTANT)) {
define($CONSTANT, $row['ID']);
}
}
} |
| /bitrix/modules/main/tools/backup.php |
//добавляем обработчик на оба события
AddEventHandler("main", "OnAutoBackupError", "OnAutoBackupError");
AddEventHandler("main", "OnAutoBackupUnknownError", "OnAutoBackupError");
//сам обработчик
function OnAutoBackupError($error){
//какую информацию передадим в уведомление
$arEventFields = array(
"DATE_CREATE"=> date("d.m.Y H:i:s"),
"ERROR"=> $error['ERROR'],
"ERROR_CODE"=> $error['ERROR_CODE'],
"ITEM_ID"=> $error['ITEM_ID'],
);
//создаем почтовое событие
CEvent::Send("BACKUP_UNKNOWN_ERROR", SITE_ID, $arEventFields);
} |
/bitrix/admin/partner_modules.php?id=#MODULE_ID#&lang=ru&uninstall=Y&sessid=#SESSID# |
BX.adminPanel.Redirect([], '/bitrix/admin/partner_modules.php?id=#MODULE_ID#&lang=ru&uninstall=Y&sessid=#SESSID#', event) |

. Всем нам вдалбливали в голову, что при разработке web-приложений нужно обязательно следовать MVC. Да, я согласен
. MVC в битрикс - это компоненты 2.0. Компонент - это контроллер, шаблон - представление, а модули - это модель.
. В битрикс его нет, но если предположить, что есть некий глобальный комплексный компонент для всего сайта, то его можно будет назвать Front controller.



<?php
foreach ($arResult['JS_OFFERS'] as $ind => $jsOffer)
{
if ($jsOffer['ID']==$_GET['pid'] && !empty($jsOffer['TREE']) && is_array($jsOffer['TREE']))
{
$arResult['OFFERS_SELECTED'] = $ind;
}
}
$jsParams = array( //.......
'OFFER_SELECTED' => $arResult['OFFERS_SELECTED'],
//.......
);
|
Добрый день!
Ищем партнера (разработчика) на долевой основе в проект. Санкт-Петербург.
Проект на Битриксе, для того, чтобы моб. приложение работало на iOS и Android, это механика увеличения объема продаж в ресторанах.
Описание проекта и кого мы ищем отправим на почту (тут не прикрепить файлы).
1) Решаем невыдуманные и актуальные проблемы. Проверили спрос, бизнес готов тестировать.
2) Развитие экосистемы для масштабирования подобных решений в РФ (Plazius, Эвотор) и по миру. Быстрый рост, не нужно продавать предприятию адресно и интегрироваться с каждой версией ПО в POS.
3) Возможность применения в механики технологии блокчейн (безопаснее, ниже операционные издержки, добавочная ценность при выпуске токенов, возможность привлечения средств через ICO).
4) Наличие квалифицированного юриста для получения патента классическим способом и через сервис на блокчейне.
P.S. Если по правилам форума, такое сообщение является рекламным, то посоветуйте, пожалуйста, где корректнее его разместить. Спасибо.
switch (this.productType)
{
case 1: // product
case 2: // set
console.log(this.product.id.toString());
break;
case 3: // sku
console.log(this.offers[this.offerNum].ID);
break;
} |
$arFavorite = &$_SESSION['FAVORITES_BY_RUSLAN']; $arFavorite[] = ['1']; |
// у товара смотрим его ТП и берем артикулы, вносим их в поле, чтобы искалось по Артикулу ТП
AddEventHandler("catalog", "OnProductAdd","addIDsFromSKUs");
AddEventHandler("catalog", "OnProductUpdate","addIDsFromSKUs");
function addIDsFromSKUs($id, $arFields)
{
$TARGET_IBLOCK_ID = 2;
$artnumbers = "";
if (CModule::IncludeModule('catalog') && CModule::IncludeModule('iblock')) {
$arProductInfo = CCatalogSKU::GetProductInfo($id);
if (is_array($arProductInfo) && $arProductInfo["IBLOCK_ID"]==$TARGET_IBLOCK_ID ) {
$arOffersInfo = CCatalogSKU::GetInfoByProductIBlock($arProductInfo['IBLOCK_ID']);
$arFilter = array(
'IBLOCK_ID' => $arOffersInfo['IBLOCK_ID'],
"PROPERTY_CML2_LINK" => $arProductInfo['ID'],
//"!ID" => $id,
);
$arSelect = Array("PROPERTY_ARTNUMBER");
$obOffersList = CIBlockElement::GetList(array("SORT"=>"ASC"), $arFilter, false, false, $arSelect);
while ($arOffers = $obOffersList->Fetch()) {
$artnumbers .= $arOffers["PROPERTY_ARTNUMBER_VALUE"]. ' ';
}
$PROPERTY_CODE = "SKU_ARTNUMBERS";
$PROPERTY_VALUE = $artnumbers;
$el_id = $arProductInfo['ID'];
$iblock_id = $arProductInfo["IBLOCK_ID"];
$prop[$PROPERTY_CODE] = array('VALUE'=>array('TYPE'=>'TEXT', 'TEXT'=>$PROPERTY_VALUE));
CIBlockElement::SetPropertyValuesEx($el_id, $iblock_id, $prop);
$res = CIBlockElement::GetByID($el_id);
$ar_res = $res->GetNext();
//переиндексируем элемент, чтобы находился в поиске
CSearch::Index(
"iblock",
$el_id,
Array(
"DATE_CHANGE"=>$ar_res["DATE_CHANGE"],
"TITLE"=>$ar_res["NAME"],
"SITE_ID"=>array('s1'),
"PARAM1"=>'catalog',//$arFields["IBLOCK_TYPE_ID"],
"PARAM2"=>'2', //$arFields['IBLOCK_ID'],
"PERMISSIONS"=>array("2"),
"URL"=>str_replace("#ID#", $ar_res["ID"], $ar_res["DETAIL_PAGE_URL"]),
"BODY"=>$ar_res["DETAIL_TEXT"].' '.$artnumbers,
"TAGS"=>$artnumbers,
),
true
);
}
}
}
AddEventHandler("iblock", "OnAfterIBlockElementUpdate", "addIDsFromElementSKUs");
AddEventHandler("iblock", "OnAfterIBlockElementAdd", "addIDsFromElementSKUs");
function addIDsFromElementSKUs(&$arFields)
{
if ($arFields['IBLOCK_ID']==2 ) {
$arOffersInfo = CCatalogSKU::GetInfoByProductIBlock($arFields['ID']);
$arFilter = array(
'IBLOCK_ID' => $arOffersInfo['IBLOCK_ID'],
"PROPERTY_CML2_LINK" => $arFields['ID'],
//"!ID" => $id,
);
$arSelect = Array("PROPERTY_ARTNUMBER");
$obOffersList = CIBlockElement::GetList(array("SORT"=>"ASC"), $arFilter, false, false, $arSelect);
while ($arOffers = $obOffersList->Fetch()) {
$artnumbers .= $arOffers["PROPERTY_ARTNUMBER_VALUE"]. ' ';
}
$PROPERTY_CODE = "SKU_ARTNUMBERS";
$PROPERTY_VALUE = $artnumbers;
$el_id = $arFields['ID'];
$iblock_id = $arFields["IBLOCK_ID"];
$prop[$PROPERTY_CODE] = array('VALUE'=>array('TYPE'=>'TEXT', 'TEXT'=>$PROPERTY_VALUE));
CIBlockElement::SetPropertyValuesEx($el_id, $iblock_id, $prop);
//переиндексируем элемент, чтобы находился в поиске
CSearch::Index(
"iblock",
$el_id,
Array(
"DATE_CHANGE"=>$arFields["DATE_CHANGE"],
"TITLE"=>$arFields["NAME"],
"SITE_ID"=>array('s1'),
"PARAM1"=>'catalog',//$arFields["IBLOCK_TYPE_ID"],
"PARAM2"=>'2', //$arFields['IBLOCK_ID'],
"PERMISSIONS"=>array("2"),
"URL"=>str_replace("#ID#", $arFields["ID"], $arFields["URL"]),
"BODY"=>$arFields["DETAIL_TEXT"].' '.$artnumbers,
"TAGS"=>$artnumbers,
),
true
);
}
}
|