Периодически возникала потребность, чтобы в композитном режиме можно было учитывать дополнительные переменные, которых нет в URL.
Основные случаи, которые требовалось учитывать при построении ключа композитного кеша, и следовательно отдавать разный html:
HTTP_USER_AGENT - для отличия мобильных и десктопных браузеров
HTTP_ACCEPT - проверять поддержку WEBP
COOKIE - в зависимости от кук
Есть метод, на который ссылается поддержка и он описан нашим коллегой. Он экспериментальный и не документированный и для меня там не всё понятно.
Я реализовал небольшой хак через rewrite в Nginx. Мой метод основан на добавлении в $_SERVER["REQUEST_URI"] дополнительных переменных через Nginx. Далее привожу 2 примера.
Первый пример для HTTP_USER_AGENT:
В секции для нужного хоста в Nginx добавляю:
if ($http_user_agent ~* "mobile")
{
rewrite ^/composite_test/$ /composite_test/?MOBILE=Y break;
}
В случае с мобильным браузером в Apache уходит url с дополнительным параметром MOBILE=Y и в $_SERVER["REQUEST_URI"] будет /composite_test/?MOBILE=Y, при этом url в браузере останется /composite_test/.
Важно делать это через Nginx, а не Apache. Битрикс использует $_SERVER["REQUEST_URI"] для построения ключа композитного кеша и не использует $_SERVER["QUERY_STRING"].
Если использовать mod_rewrite в Apache, то MOBILE=Y попадёт в $_SERVER["QUERY_STRING"], а $_SERVER["REQUEST_URI"] останется таким же.
Второй пример для COOKIE:
Nginx поддерживает cookie и можно написать условие вида:
if ($cookie_VARNAME = 1)
{
rewrite ^/composite_test/$ /composite_test/?VARNAME=1 break;
}
С cookie правда есть нюанс, что их ещё нужно установить, для этого возможно потребуется сделать страницу, без композитного кеша, которая будет эти куки ставить. Это зависит от требуемой логики.
В целом можно использовать любые переменные доступные в Nginx и таким образом формировать разные url для Apache.
Я привел базовые примеры, которые можно адаптировать под нужды конкретного проекта. Правда замечу, что необходимо иметь доступ к настройкам Nginx на сервере.
Данная методика подходит для простейших условий. Если требуется полноценная логика и расчёты, то нужно использовать композит с динамическими областями, как это и задумано логикой продукта.
Добрый день! Делаю выгрузку для Яндекс Маркета на сайте. Есть инфоблок с товарами на сайте в нем поле Наличие в виде списка со значениями: Шоу-рум, На складе, Под заказ. Для товаров у которых статус Под заказ в админке, нужно для тега в выгрузке для яндекс маркета чтобы подставлялось, то есть вот так:<pickup>false</pickup>, а для тега <store>false</store>. А если статус Шоу-рум, то <pickup>true</pickup> <store>false</store>. Нужно для каждого товара сделать обработчик наборов в false, true, например, если статус Под заказ, то свойство false и уже в прайс нужно подтягивать это свойство. Пробовал добавить вывод нового тестового тега, прописать обработчик в файле yandexrun.php в папке /bitrix/modules/catalog/load, копировал его в другое место /bitrix/php_interface/include/catalog_export, но ничего не изменяется и протестировать не получается работают изменения или нет, проверял и другие файлы связанные с модулем яндекс маркета тоже самое, как мне внести и проверить изменения и в каком файле модуля их надо вносить?
Привет! Подскажите, как быть с обустройством репозитория для проекта, если ядро к нему прилинковано? ( т.е имеем несколько проектов на одном сервере с одним ядром). Мысль была только в виде submodule в отдельном репозитории.
Добрый день! Хочу поделиться реализацией пользовательского поля с типом "Привязка к складу" Битрикса. Тип создавался для HL-блоков
Структура файлов, участвующих в построении поля такая: Сейчас разберем содержимое каждого файла. Я опущу их расположение в проекте, т.к. это не важно: разместить их можно в любом удобном месте (программисты поймут) Начнем с главного файла с классом, описывающим новый тип поля - CUserTypeStoreLink.php
<?php
namespace Project\UserTypes\StoreLink;
use Bitrix\Catalog\StoreTable;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\Loader;
use Bitrix\Main\LoaderException;
use Bitrix\Main\ObjectPropertyException;
use Bitrix\Main\SystemException;
use CJSCore;
/**
* Class CUserTypeStoreLink
*
* Класс описывает пользовательское свойство привязки к складу
*
* @package Project\UserTypes\StoreLink
*/
class CUserTypeStoreLink
{
const USER_TYPE = "mercool_store_link";
/**
* Формируем массив значение
*
* @param $mxValue
* @return array
*/
private static function getValuesArray($mxValue)
{
$arValues = array();
if (!empty($mxValue)) {
if (is_array($mxValue)) {
$arValues = $mxValue;
} else {
$arValues = [$mxValue];
}
}
return $arValues;
}
/**
* Функция регистрируется в качестве обработчика события OnUserTypeBuildList
* @return array
*/
public function GetUserTypeDescription()
{
return [
"USER_TYPE_ID" => self::USER_TYPE,
"CLASS_NAME" => __CLASS__,
"DESCRIPTION" => "Привязка к складу",
"BASE_TYPE" => "int",
];
}
/**
* Функция вызывается при добавлении нового свойства для конструирования SQL запроса
* создания столбца значений свойства
*
* @param $arUserField
* @return string
*/
public function GetDBColumnType($arUserField)
{
$sNum = "";
switch (strtolower($GLOBALS["DB"]->type)) {
case "mysql":
$sNum = "int(18)";
break;
case "oracle":
$sNum = "number(18)";
break;
case "mssql":
$sNum = "int";
break;
}
return $sNum;
}
/**
* Функция вызывается перед сохранением метаданных (настроек) свойства в БД
*
* @param $arUserField
* @return array
*/
public function PrepareSettings($arUserField)
{
return [];
}
/**
* Функция вызывается при выводе формы метаданных (настроек) свойства
*
* @param bool $arUserField
* @param $arHtmlControl
* @param $bVarsFromForm - флаг отправки формы
* @return string - HTML для вывода
*/
public function GetSettingsHTML($arUserField = false, $arHtmlControl, $bVarsFromForm)
{
return "";
}
/**
* Функция валидатор значений свойства вызвается в $GLOBALS["USER_FIELD_MANAGER"]->CheckField()
* при добавлении/изменении
*
* @param $arUserField
* @param $value - начение для проверки на валидность
* @return array - массив массивов ("id","text") ошибок
*/
public function CheckFields($arUserField, $value)
{
return [];
}
/**
* Получение склада по ID
*
* @param int $iStoreId
* @return array
* @throws ArgumentException
* @throws LoaderException
* @throws ObjectPropertyException
* @throws SystemException
*/
private static function getStore(int $iStoreId)
{
Loader::includeModule("sale");
$arFilter["=ACTIVE"] = "Y";
$arFilter["=ID"] = $iStoreId;
$arStore = StoreTable::getList(array(
"filter" => $arFilter,
"select" => array(
"ID",
"TITLE",
"ADDRESS"
),
"limit" => 1,
"cache" => array(
"ttl" => TTL,
"cache_joins" => true
)
))->fetch();
return self::makeStoreArray($arStore);
}
/**
* формируем нужный формат массива Склада
*
* @param array $arStore
* @return array
*/
public static function makeStoreArray(array $arStore)
{
return array(
"ID" => $arStore["ID"],
"TITLE" => $arStore["TITLE"],
"ADDRESS" => $arStore["ADDRESS"]
);
}
/**
* Формы редактирования значения свойства
*
* @param $arUserField
* @param $arHtmlControl
* @return string - HTML для вывода
*/
public function GetEditFormHTML($arUserField, $arHtmlControl)
{
self::registrExt();
return self::getItemFieldHtml($arHtmlControl["NAME"], $arHtmlControl["VALUE"]);
}
/**
* Вывод в форме редактирования множественного поля
*
* @param $arUserField
* @param $arHtmlControl
* @return string
* @throws ArgumentException
* @throws LoaderException
* @throws ObjectPropertyException
* @throws SystemException
*/
public function GetEditFormHTMLMulty($arUserField, $arHtmlControl)
{
self::registrExt();
return self::getItemFieldHtml($arHtmlControl["NAME"], $arHtmlControl["VALUE"], true);
}
/**
* Генерируем HTML код экземпляра поля
*
* @param string $sFieldName
* @param $mxValue
* @param bool $bIsMultiple
* @return string
* @throws ArgumentException
* @throws LoaderException
* @throws ObjectPropertyException
* @throws SystemException
*/
private function getItemFieldHtml(string $sFieldName, $mxValue, bool $bIsMultiple = false)
{
$sFieldName = str_replace('[]', '', $sFieldName);
$sPathFromContentRoot = str_replace($_SERVER["DOCUMENT_ROOT"], "", __DIR__);
$arValues = self::getValuesArray($mxValue);
list($sReturn, $iKey) = self::generateListHtml($arValues, $sFieldName, $bIsMultiple);
$sReturn .= "<div class='".self::USER_TYPE."-filter' data-code='".$sFieldName."' data-ajaxurl='".$sPathFromContentRoot."/ajax.php' >";
$sReturn .= "<input class='".self::USER_TYPE."' type='text' data-code='".$sFieldName."' name='".$sFieldName."_TITLE' value='' placeholder='Название'>";
$sReturn .= "<input class='".self::USER_TYPE."' type='text' data-code='".$sFieldName."' name='".$sFieldName."_ADDRESS' value='' placeholder='Адрес склада'>";
$sReturn .= "</div>";
$sReturn .= "<div class='store-search-result' data-count='".($iKey + 1)."' data-multiple='".($bIsMultiple ? "true" : "false")."' data-code='".$sFieldName."'></div>";
return $sReturn;
}
/**
* Функция вызывается при выводе фильтра на странице списка
*
* @param $arUserField
* @param $arHtmlControl
* @return string - HTML для вывода
*/
public function GetFilterHTML($arUserField, $arHtmlControl)
{
return self::GetEditFormHTML($arUserField, $arHtmlControl);
}
/**
* Функция должна вернуть представление значения поля для поиска
*
* @param $arUserField
* @return string - посковое содержимое
*/
public function OnSearchIndex($arUserField)
{
if (is_array($arUserField["VALUE"])) {
return implode("\r\n", $arUserField["VALUE"]);
} else {
return $arUserField["VALUE"];
}
}
/**
* Функция вызывается перед сохранением значений в БД
*
* @param $arUserField
* @param $iValue
* @return int - значение для вставки в БД
*/
public function OnBeforeSave($arUserField, $iValue)
{
if (intval($iValue) > 0) {
return intval($iValue);
}
}
/**
* Функция вызывается при выводе значения свойства в списке элементов
*
* @param $arUserField
* @param $arHtmlControl
* @return string - HTML для вывода
*/
public function GetAdminListViewHTML($arUserField, $arHtmlControl)
{
$sReturn = "";
if (!empty($arHtmlControl["VALUE"])) {
$arStore = self::getStore($arHtmlControl["VALUE"]);
$sReturn .= "<p>[".$arStore["ID"]."] ".$arStore["TITLE"]." | ".$arStore["ADDRESS"]."</p>";
}
return $sReturn;
}
/**
* Подключаем стили и скрипты к полю
*/
private function registrExt()
{
$sPathFromContentRoot = str_replace($_SERVER["DOCUMENT_ROOT"], "", __DIR__);
CJSCore::RegisterExt("store_link", [
"js" => $sPathFromContentRoot."/js/script.js",
"css" => $sPathFromContentRoot."/css/style.css",
"rel" => ["jquery"]
]);
CJSCore::Init(["jquery", "store_link"]);
}
/**
* Генерируем HTML списка значений
*
* @param array $arValues
* @param $sFieldName
* @param bool $bIsMultiple
* @return array
* @throws ArgumentException
* @throws LoaderException
* @throws ObjectPropertyException
* @throws SystemException
*/
private function generateListHtml(array $arValues, $sFieldName, bool $bIsMultiple): array
{
$iKey = 0;
$sReturn = "<ul class='".self::USER_TYPE."-list".(empty($arValues) ? " hidden" : "")."' data-code='".$sFieldName."'>";
if (!empty($arValues)) {
foreach ($arValues as $iKey => $sVal) {
if ($arStore = self::getStore($sVal)) {
$sReturn .= "<li>[".$arStore["ID"]."] ".$arStore["TITLE"]." | ".
$arStore["ADDRESS"]." <a class=\"delete\" href=\"#\">[удалить]</a>";
if ($bIsMultiple) {
$sReturn .= "<input type='hidden' name='".$sFieldName."[".$iKey."]' value='".(!empty($sVal) ? $sVal : "")."'></li>";
} else {
$sReturn .= "<input type='hidden' name='".$sFieldName."' value='".(!empty($sVal) ? $sVal : "")."'></li>";
}
}
}
}
$sReturn .= "</ul>";
return array($sReturn, $iKey);
}
}
Главная особенность - формирование списка значений и фильтра для поиска. Обратите внимание на метод getItemFieldHtml. Именно в нем формируется HTML с фильтром и выводом значений
AJAX запросы обрабатывает файл ajax.php. В нем содержится класс CUserTypeStoreLinkAction по обработке AJAX запросов на формирование списка вариантов складов, удовольтворяющих условиям фильтра. Его код представлен ниже:
<?
namespace Project\UserTypes\StoreLink;
require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
use Bitrix\Catalog\StoreTable;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\Loader;
use Bitrix\Main\Application;
use Bitrix\Main\LoaderException;
use Bitrix\Main\ObjectPropertyException;
use Bitrix\Main\SystemException;
use Bitrix\Main\Web\Json;
final class CUserTypeStoreLinkAction
{
private $sTitle;
private $sAddress;
/**
* CUserTypeStoreLinkAction constructor.
* @throws LoaderException
* @throws SystemException
*/
public function __construct()
{
Loader::includeModule("sale");
$obRequest = Application::getInstance()->getContext()->getRequest();
$this->sTitle = $obRequest->get("title");
$this->sAddress = $obRequest->get("address");
}
/**
* Получить список складов
*
* @return array
* @throws SystemException
* @throws ArgumentException
* @throws ObjectPropertyException
*/
public function getStores()
{
$arStores = array();
$arFilter["=ACTIVE"] = "Y";
if (!empty($this->sTitle)) {
$arFilter["%TITLE"] = $this->sTitle;
}
if (!empty($this->sAddress)) {
$arFilter["%ADDRESS"] = $this->sAddress;
}
$rsStores = StoreTable::getList(array(
"filter" => $arFilter,
"select" => array(
"ID",
"TITLE",
"ADDRESS"
),
"cache" => array(
"ttl" => TTL,
"cache_joins" => true
)
));
while ($arStore = $rsStores->fetch()) {
$arStores[] = CUserTypeStoreLink::makeStoreArray($arStore);
}
return $arStores;
}
}
header("Content-Type: application/json");
$obUserTypeStoreLinkAction = new CUserTypeStoreLinkAction;
$arStores = $obUserTypeStoreLinkAction->getStores();
echo Json::encode($arStores);
?>
Весь JS расположен в файле script.js
$(document).on("click", ".hover-variant", function () {
var sFieldCode = $(this).attr("data-code");
var iStoreId = $(this).attr("data-id");
var obList = $("ul[data-code='" + sFieldCode + "']");
var obSearchResults = $(".store-search-result[data-code='" + sFieldCode + "']");
var bIsMultilple = (obSearchResults.attr("data-multiple") === "true");
var sHiddenValueField = "<input type='hidden' name='" + sFieldCode + "' value='" + iStoreId + "' >";
var iKey = Number.parseInt($(this).parent().attr("data-count"));
var obFilter = $(".mercool_store_link-filter[data-code='" + sFieldCode + "']");
if (bIsMultilple) {
sHiddenValueField = "<input type='hidden' name='" + sFieldCode + "[" + iKey + "]' value='" + iStoreId + "' >";
}
var sValue = "<li>" + $(this).html().replace(/<[^>]+>/g, '') + sHiddenValueField + " <a class='delete' href='#'>[удалить]</a></li>";
if (bIsMultilple) {
obList.append(sValue);
$(this).parent().attr("data-count", iKey + 1);
} else {
obList.html(sValue);
}
obList.removeClass("hidden");
obSearchResults.hide();
obFilter.find("input").val("");
});
$(document).on("click", ".mercool_store_link-list .delete", function () {
var obListItem = $(this).parent();
obListItem.hide();
obListItem.find("input").val("");
return false;
});
/**
* AJAX-поиск на сервере
*
* @param {string} sFieldCode
*/
var searchStores = function (sFieldCode) {
var obSearchResults = $(".store-search-result[data-code='" + sFieldCode + "']");
var sTitle = $('input[name="' + sFieldCode + '_' + 'TITLE' + '"').val();
var sAddress = $('input[name="' + sFieldCode + '_' + 'ADDRESS' + '"').val();
var sAjaxUrl = $(".mercool_store_link-filter[data-code='" + sFieldCode + "']").attr("data-ajaxurl");
obSearchResults.html("Загрузка...");
obSearchResults.show();
BX.ajax.post(
sAjaxUrl,
{
"title": sTitle,
"address": sAddress
},
function (obData) {
obData = JSON.parse(obData);
var sVariants = "";
$.each(obData, function (iIndex, obStore) {
sVariants = sVariants + "<div class='hover-variant' data-code='" + sFieldCode + "' data-id='" + obStore.ID + "'>[" + obStore.ID + "] " + displayMatches(obStore.TITLE, sTitle) + " | " + displayMatches(obStore.ADDRESS, sAddress) + "</div>";
});
if (sVariants.length == 0) {
sVariants = "Ничего не найдено. Попробуйте изменить параметры поиска.";
}
obSearchResults.html(sVariants);
obSearchResults.removeClass("preload");
}
);
}
/**
* Отложенный вызов
*/
var delayExec = (function () {
var iTimer = 0;
return function (callback, ms) {
clearTimeout(iTimer);
iTimer = setTimeout(callback, ms);
};
})();
$(document).on("keyup", ".mercool_store_link", function () {
if ($(this).val().length >= 3) {
var sFieldCode = $(this).attr("data-code");
delayExec(function () {
searchStores(sFieldCode);
}, 600);
}
});
/**
* Выделение подстроки в строке
*
* @param {string} sFullStr
* @param {string} sSearchSubStr
*/
var displayMatches = function (sFullStr, sSearchSubStr) {
var obRegex = new RegExp(sSearchSubStr, 'gi')
var sStr = sFullStr.replace(obRegex, function (sSearchSubStr) {
return "<b>" + sSearchSubStr + "</b>"
})
return sStr;
}
Файл в основном шлет запросы на ajax.php поиск складов и формирует список вариантов. А также небольшие правки для удобства пользования
Есть файл style.css со стилями для поля. Содержит минимальный набор стилей для корректной работы
Дорогие друзья. Я не знаю, смогу ли я написать это здесь. Мне нужна помощь. Мне нужен программист Битрикс (человек, а не агентство) в Москве, который имеет навыки UX для нового проекта. Пожалуйста, напишите, если можете. Агентства просьба не беспокоить. спасибо большое
{"error":"ERROR_CORE","error_description":"TASKS_ERROR_EXCEPTION_#256; 5f1034a72fbac4.87134003: Param #1 (itemId) for method ctaskelapseditem::update() expected to be of type \u0022integer\u0022, but given something else.; 256\/TE\/WRONG_ARGUMENTS\u003Cbr\u003E"}
Что случилось: При экспорте диалогов открытых линий в эксель выдаёт вот такое исключение
Причина
Вся причина в очень интересном подходе подключения кастомных обработчиков ajax запросов.
Ошибка вызвана отсутствием имени модуля в переменной module класса обработчика /bitrix/components/bitrix/imopenlines.statistics.detail/ajax.php, открываем файл: Переменная есть, так почему вылазит ошибка? Кто более внимательный, заметит, что в трассировке нет вообще класса ImOpenLinesStatisticsDetailAjaxController, но есть его предок Main\Controller\Export, так почему же обработку производит предок?
А потому что вот как в ядре подключаются классы обработки ajax: /bitrix/www/bitrix/modules/main/lib/engine/router.php:222
Такое решение по выявлению подключенных классов описано в комментариях к функции . И он бы работал не используя разработчики Битрикс автозагрузку классов spl_autoload_register(). В этом случае php подключает класс только после его использования, вот и получается, что в файле /bitrix/components/bitrix/imopenlines.statistics.detail/ajax.php порядок подключения классов такой:
[506] => CComponentEngine [507] => ImOpenLinesStatisticsDetailAjaxController - находится в файле /bitrix/components/bitrix/imopenlines.statistics.detail/ajax.php [508] => Bitrix\Main\Controller\Export - является предком ImOpenLinesStatisticsDetailAjaxController, и к нему обращаются первый раз [509] => Bitrix\Main\Engine\Controller - является предком Bitrix\Main\Controller\Export, и к нему обращаются первый раз
Далее в переборе этого списка проверяется Bitrix\Main\Controller\Export, который является наследником Bitrix\Main\Engine\Controller, и возвращается в качестве кастомного обработчика вместо класса ImOpenLinesStatisticsDetailAjaxController.
Вот такой вот примитивный баг, как он прошел тестировщиков я не представляю.
Решение Самое банальное решение, пока не выйдет обновление, сделать вызов Bitrix\Main\Controller\Export, где-нибудь в init.php,например:
class kostyliforever extends \Bitrix\Main\Controller\Export{}
Чат виджет подключенный на сайт выдает ошибку К сожалению, загрузить онлайн-чат не удалось :(Пожалуйста, воспользуйтесь другими каналами связи или попробуйте открыть чат позже
в консоле браузера такое LiveChatWidget.error: ERROR_CORE (Проверка сохраненной авторизации не пройдена.) t @ script.js?r=1593511923-27:260
В БУС нужно сделать персональный доступ покупателя к файлам или странице с файлами без возможности просмотра другими покупателями.
Идея такая:
Покупатель заходит в ЛК и переходит на страницу Мои документы. Там выложены договоры, акты, счета, которые туда поместил администратор/менеджер. Покупатель может открыть эти документы, скачать. В идеале, может сам помещать свои документы, но с ограничением по размерам и формату. Помещать документы в "папку покупателя" могут лишь пользователи определённой группы. Без такого доступа каждый может видеть лишь свои документы в своей ЛК или на специальной странице.
По реализации: 1. Есть такая возможность в принципе на уровне ядра? Или это нужно разрабатывать с 0? 2. Вижу 2 варианта реализации: 1) доступ к файлам на диске, 2) хранение файлов в базе в специальной таблице/цах и динамическая генерация доступа с выгрузкой документа из базы
Что нужно: А. Для начала определить, есть ли примеры реализации в БУС, есть ли потенциальная возможность в БУС реализовать это на уровне ядра, объем и трудоёмкость работ по внедрению такого решения Б. Найти/выбрать разработчика для такого решения.
Обращаюсь к сообществу, чтобы не изобретать велосипед. Вдруг кто-то подобное делал?
В Техподдержку обращался. У них предложений нет, кроме как "всё сами" и обращайтесь на форум.
Не так давно начали использовать открытые линии, и пришлось столкнуться с некоторыми неудобствами.
Инструкция WhatsApp Начали использование с подключения вотсап, и первое с чем столкнулся, с бесполезной . Я был вынужден искать ответы на многие вопросы вне этой инструкции:
Нигде не написано, что требуется создать бизнес аккаунт в Facebook
Бизнес аккаунт требуется верифицировать, об этом соответственно тоже, хотя там есть подводный камень в виде за дизейбленой кнопки.
О том что необходимо предоставить права для твилио на управление вотсап в созданном бизнес аккаунте.
В общем нет описания что есть Тивилио, а это лишь провайдер, у которого до сих пор интеграция с вотсап в бетта версии, и есть много недочётов к интерфейсу администратора, например нет возможности создавать шаблоны сообщений с изображениями или видео, а только текст. Хотя это уже возможно в вотсап.
Нет предупреждения, что через вотсап не получится делать рассылки (этот функционал допиливать пришлось, использовал возможность подключения смс провайдера по rest, если кому интересно могу скинуть в подробностях как это сделать и какие там трудности ))) )
Ну и самое главное, стоило сделать сноску в начале инструкции, что при прямых руках вы можете с российским номером использовать оригинальный апи вотсап, со всеми его возможностями. Об этом узнаёшь как только открываешь доки по интеграции с вотсап на фейсбуке, но ты это делаешь уже когда нужно готовое решение здесь и сейчас, и нет времени на написание модуля.
В общем инструкция возможно просто давно не актуализировалась, хотя дата стоит 08 июня 2020.
im.bitrix.info Тут кстати отвечу и на вопрос о том, почему я не пишу в техподдержку Bitrix. Потребовалось 6 рабочих дней, что бы дождаться ответа от техподдержки что такое im.bitrix.info. И техподдержка не смогла объяснить что такое im.bitrix.info, и зачем он нужен, например коробке (ответ поражает информативностью): Вопрос задал больше из интереса какой будет ответ, так как о функциях сервиса можно догадаться:
Для удобства обновления апи каналов, если что-то изменится в апи одного из каналов не потребуется выпускать обновление
Для упрощения подключения каналов использующих OAuth пользователями, не требуется создавать приложение в аккаунте сервиса
Для распределения нагрузки потока сообщений в облачные порталы
....
И вроде бы всё хорошо, если бы из-за этого сервиса не возникала задержка в получении сообщений с каналов, особенно было ощутимо в первый день (совпадение?) в середине дня сообщения приходили на портал с задержкой в 40 минут, и в доступности нашего портала не было проблемы, это подтверждало изменение веб-хука для входящих сообщений в сервисе твилио, направляешь напрямую на портал, сообщения доходят без задержки секунда в секунду. Ответ поддержки был из разряда "перезагрузите компьютер":
На следующий день вроде всё нормализовалось, но задержка осталась от 5 секунд, она присутствовала на всех каналах связи кроме виджета на сайт, что логично, ведь виджет работает напрямую с сервером. И обнаружилась другая проблема, некоторые сообщения не доходят до открытых линий. Уже рассчитывать на поддержку не стал полез в ядро, благо сделали возможность писать лог коннектора открытых линий, он помог найти причину:
Скрипт получения сообщения останавливался из-за выбрасываемого исключения =(! Выяснять причину почему файл терялся я не стал, полез сразу по пути где было получено исключение, и немного покопав вышел на базовый класс коннекторов, причиной всему было скачивание аватарки писавшего клиента, по каким-то причинам она не всегда скачивалась. Но это бы не беда, но никто не позаботился отлавливать это исключение, что бы доставка сообщения не прерывалась!
И это ещё не всё, эта ошибка привела меня к тому, что создаёт часть задержки в доставке сообщений. Аватарка скачивалась при каждом входящем сообщении! По крайней мере для VK точно. Всё дело в том, что для сравнения данных используется MD5 хэш массива данных о пользователе полученных из коннектора, кто лажал было не понятно. Решение было топорным, отключить в коде вообще обработку изображения из каналов, это уменьшило задержку в доставке сообщений, и исключило ошибку, когда сообщение не доходило.
Итого почему мне не нравится использование im.bitrix.info:
нестабильность, да я не уверен в стабильности сервисов Битрикс, даже этот портал для разработчиков периодически падает. Ну и никто не исключает что между моим порталом и сервисом на магистрали будет авария, и я лишусь открытых линий полностью, хотя сервера каналов будут в полне доступны.
централизованность, упал сервис, недоступны все каналы кроме виджета.
зависимость от других порталов, которые используют этот сервис, например мои сообщения могу просто встать в очередь, из за того что сервер не успевает их обработать.
задержка доставки сообщений, при работе сервиса в штатном режиме.
конфиденциальность, что происходит с сообщениями не известно.
Всё это можно описать одним словом - зависимость, это дополнительное звено в доставке сообщений, которое находится вне моей ответственности, я не могу повлиять на решение проблем с этим сервисом, только писать в поддержку и ждать, ждать, ждать
ПС Данные по ошибке возникающей со скачиванием аватара передал в поддержку 4 июня, и пока не получал комментарием, только вопрос "можно ли закрыть чат?"
Турецкие сериалы на любой вкус только для вас Естественно, каждый человек желает смотреть только качественные и интересные турецкие фильмы и сериалы. К сожалению, на этот момент в мире существует минимальное количество хороших интернет ресурсов, где можно было бы посмотреть отличный турецкий сериал без регистрации и совершенно бесплатно. Множество людей привыкли смотреть многосерийные турецкие фильмы только с экранов своих ТВ, но эти времена прошли, и прямо сейчас вы сможете насладиться просмотром отличных работ на любой вкус. С радостью приглашаем всех фанатов турецких сериалов на интернет-ресурс turkserial.online, которых можно с легкостью назвать сериаломанами, ведь по всему миру существует множество людей, обожающих смотреть многосерийные турецкие фильмы. можно с легкостью и большим удовольствием на turkserial.online, где вы сможете насладиться огромной подборкой новых и качественных фильмов и сериалов турецкого производства.
Добрый день! Столкнулся с необходимостью реализовать REST API в Батрикс - управление сайтом, есть какие то полезные материалы по работе с рестом в бусе? Документация как я понял только для битр 24, гугл выдавал только какие то костыли самописные, хотелось бы понимать какой подход правильно применять при реализации REST API в БУС.
Хотел обратить внимание на одну новую особенность пользовательских полей и работы с ними через ORM. Конкретно если создать множественное поле типа Строка, и вы полнить запрос с фильтром по нему:
$q = new \Bitrix\Main\Entity\Query(FAQArticleTable::getEntity());
$q->setSelect(['ID', 'UF_NAME'])
->setFilter(['UF_THEME' => ['MAIN'],]);
то мы получим такой запрос:
SEL ECT
`f_aq_article`.`ID` AS `ID`,
`f_aq_article`.`UF_NAME` AS `UF_NAME`
FR OM `b_faq` `f_aq_article`
WHERE (UPPER(`f_aq_article`.`UF_THEME`) like upper('MAIN'))
который не принесёт результата, так как данные в поле таблицы хранятся с использованием serialize:
Такой запрос стал формироваться в последнем обновлении, которое я установил 04.06.2020 До этого формировался запрос с использованием отдельной таблицы для значений множественного поля:
SELECT `f_aq_article`.`ID` AS `ID`,
`f_aq_article`.`UF_NAME` AS `UF_NAME`
FR OM `b_faq` `f_aq_article`
WH ERE `f_aq_article`.`ID` IN (SELECT
`f_aq_article_tmp880275262`.`ID` AS `ID`
FR OM `b_faq` `f_aq_article_tmp880275262`
LEFT JOIN `b_faq_uf_theme` `f_aq_article_f_aq_article_utm_uf_theme_object_tmp880275262` ON `f_aq_article_f_aq_article_utm_uf_theme_object_tmp880275262`.`ID` = `f_aq_article_tmp880275262`.`ID`
WH ERE (UPPER(`f_aq_article_f_aq_article_utm_uf_theme_object_tmp880275262`.`VALUE`) like upper('MAIN')))
Согласен запрос не ахти, и работает медленно, но об этом ниже.
Решение
Первое что приходит в голову, добавить в фильтр маску:
$q = new \Bitrix\Main\Entity\Query(FAQArticleTable::getEntity());
$q->setSelect(['ID', 'UF_NAME'])
->setFilter(['UF_THEME' => ['%MAIN%'],]);
и это работает, но есть две проблемы:
Менять это во всех местах использования множественных строк.
Но главная проблема - это получение не верных результатов, этот запрос отберёт все строки таблицы, в которых поле содержит заданную маску.
И это очень всё портит, раньше запрос работал без использования маски и отбирал только соответствующие значения. В итоге получается, что решением этой проблемы остаётся вынос значений в отдельный hlblock, и создания связи с ним, то есть то, что раньше делалось на уровне ядра. И это решение тянет за собой реализацию в интерфейсе заполнения множественных значений значений и запись этих значений в этот отдельный hlblock, а в элемент с которым он связан идентификаторов.
ПС А использование hlblock тоже проблематично, если у вас в нем несколько десятков тысяч строк, то открытие формы элемента у которого есть поле связанное с этим hlblockом вызовет долгий рендеринг страницы, а на слабом компьютере выскочит:
Начиная с момента введения известным удостоверяющим центром "Lets'Encrypt" лимитов на количество получаемых сертификатов, у всех клиентов, имеющих на одном сервере с Битрикс окружением более 5 сайтов, начались постоянные проблемы с получением новых сертификатов при добавлении к окружению нового сайта. Беглый анализ логов dehydrated показал следующую картину - при добавлении сертификата к каждому новому домену пере выпускаются сертификаты ВСЕХ доменов окружения, полученные в LE, даже те которые получены 5 минут назад и прекрасно работают. Далее ладно бы скрипт dehydrated если для какого-то домена нельзя получить сертификат - писал в лог ошибку и переходил к следующему домену. Если вывалилась ошибка лимита - процесс ПОЛНОСТЬЮ прерывается и не запускается даже для тех доменов, у которых лимит не исчерпан. Данный вопрос был задан пользователем в теме форума Ответ администрации не заставил себя ждать и был - "мы ничего не значем, так работает dehydrated, ждите дня когда лимит спадет". Феерично, не правда ли! То есть нужно тебе перенести больше 20 сайтов с сервера на сервер - ТЫ БУДЕШЬ ПЕРЕНОСИТЬ ИХ МЕСЯЦАМИ, СТРАДАТЬ, и молиться на LE чтобы лимит не закончился в неподходящий момент. При этом не забывая платить за 2 сервера и отмазываясь перед клиентом почему простая операция заняла такой дикий срок!
Как я решал данную проблему:из папки /home/bitrix/dehydrated/domains переносим куда-нибудь в другое место текстовые файлы всех доменов, которые уже получили сертификаты. Далее перезапускаем через меню окружения получение сертификата для нужного сайта. dehydrated отрабатывает только для нужного домена. После получения сертификата таким способом текстовики перенесенных из папки доменов нужно вернуть, чтобы обновление сертификатов проходило штатно. Надеюсь разработчики окружения все-таки посмотрят в сторону клиентов, и доработают логику получения сертификатов. Там не такие большие изменения нужны чтобы решить данную проблему.
Столкнулся с тем, что при изменении параметров веб-формы, форма на странице сама не обновлялась. Я посмотрел код компонента form.result.new и обнаружил, что используется тегированный управляемый кэш, как описано тут: Кэш успешно принудительно сбрасывается функцией типа $CACHE_MANAGER->ClearByTag('form_'.$ID_формы) Однако при сохранении параметров формы эта функция для сброса кэша почему-то не вызывается. В списке событий найти событие на изменение данных самой веб-формы найти не смог:
Ограничился выставлением небольшого времени кэширования компонента. Написал в Битрикс, завели тикет на доработку.
Веб-форму можно привязать к одному или нескольким сайтам. В фильтрах функции CForm::GetList можно указать, привязанные к каким сайтам выбирать формы. Но результат, возвращаемый этой функцией не содержит информации о привязке найденных форм к сайтам. Если Вам нужно узнать, к каким сайтам привязана форма, можно воспользоваться функцией CForm::GetSiteArray(ID_формы) (См. ) Решение подсказано службой поддержки Битрикс.