Для компонента Элементы раздела (catalog.section) возможность редактировать товары над сайтом (интерфейс Эрмитаж) реализуется несколько иначе, чем для news.list.
Для примера беру news.list. В component.php код, который расположен до и после блока с startResultCache, выполняется на каждом хите. А код, который начинается от
При желании через SetResultCacheKeys этот набор можно дополнить. Значения из $aResult с этими ключами попадут в кэш и будут доступны в component_epilog . Код в result_modifier выполняется только на первом хите после сброса кэша, на дальнейших хитах компонент как-бы "перепрыгивает" через result_modifier и выдаёт html код шаблона уже из кэша.
Я поверхностно вскопал исходники startResultCache : - проверяем, есть ли кэш, или его нет. Если кэша нет, прыгаем глубже:
if ($this->__cache->startDataCache($cacheTime, $this->__cacheID, $this->__cachePath))
{
$this->__NavNum = $GLOBALS["NavNum"];
$this->__currentCounters = self::$__componentCounter;
if (defined("BX_COMP_MANAGED_CACHE") && $this->__cache->isStarted())
$CACHE_MANAGER->startTagCache($this->__cachePath);
return true;
}
- производится ещё несколько проверок и подключается component_epilog
Кэш компонента зависит от следующих параметров: - название компонента - название шаблона - $arParams - дополнительные параметры, например группа текущего пользователя.
Имеется такой кейс. Компонент news.list выводит список новостей из инфоблока. Внешний вид стандартный - ссылка на детальную, дата публикации, изображение, текст анонса. Компонент news.detail - это детальная новости. Нужно сделать следующее: если пользователь заходил на детальную новости, то у этой новости в списке выводить не ссылку, а просто название. Т.е. второй раз пользователя на детальную не пускать (ну если будет прямой заход, то ничего страшного).
Задача подразделяется на две подзадачи: 1) Обработка захода пользователя на детальную новости. 2) Показ пользователю списка новостей.
Пользователь может быть как авторизованным, так и гостем.
В переменной $_SESSION['viewed_news'] будут лежать id просмотренных новостей. 1) component_epilog у news.detail. В него ложим код, который добавляет в $_SESSION['viewed_news'] id текущей новости. Если в component_epilog $arResult["ID"] не доступна (что наврятли, $arResult["ID"] по умолчанию сохранён в кэше), то протянуть её туда через SetResultCacheKeys 2) Тут сложнее. Варианты: 1. Скормить $_SESSION['viewed_news'] как параметр компоненту news.list . С точки зрения трудозатрат это самый простой вариант, но кэш компонента рискует опухнуть до космических размеров. 2. В component_epilog у news.list достать из $_SESSION['viewed_news'] id -шники и чисто средствами js повырубать нужные ссылки. Очень некрасивый вариант, но рабочим может быть. 3. В component_epilog делать ajax запрос специально подготовленному скрипту, который будет выдавать нам html код блока со списком новостей с неактивными где нужно ссылками и заменять содержимое блока этим куском. Такой же корявый вариант, как и предыдущий. 4. Разбить список новостей на части следующим образом: каждый блок с новостью выводится компонентом типа news.detail , на вход компонент будет принимать ID новости, ID инфоблока и прочие необходимые параметры и + статус новости для текущего пользователя (просмотрено или не просмотрено). У news.detail кэширование будет включено, а в самом news.list кэширование можно отрубить. Этот вариант меня тоже немножко коробит, но я чувствую, что здесь истина где-то рядом. Кстати, что-то подобное реализовано у blog.new_posts.list . У него в шаблоне вызывается rating.vote , ему из blog.new_posts.list скармливаются нужные параметры. В blog.new_posts.list работает обычный тегированный CPHPCache , который зависит от $arParams , id группы текущего пользователя и ещё пары доп. параметров. Но мне пока не совсем понятно, как это увязано в кэшированием в blog.new_posts.list .
Для справки : OnEpilog - обработка html перед выдачей в браузер.
AddBufferContent:
$APPLICATION->AddBufferContent('ShowCondTitle');
function ShowCondTitle()
{
global $APPLICATION;
if (!$APPLICATION->GetTitle())
return "Стандартная страница";
else
return $APPLICATION->GetTitle();
}
AddBufferContent на вход принимает имя функции, результат выполнения которой нужно положить в месте вызова (в месте вызова AddBufferContent).
Методы модуля , которые обеспечивают откладывание ( , , , , , ) являются по своей сути обёртками над .
public function ShowProperty($PROPERTY_ID, $default_value = false)
{
$this->AddBufferContent(array(&$this, "GetProperty"), $PROPERTY_ID, $default_value);
}
их методы-"партнёры" ( , , , , , ) достают нужное значение и возвращают его :
<?
use \Bitrix\Main\Loader;
define(NO_KEEP_STATISTIC, true); //запрет сбора статистики
define(NOT_CHECK_PERMISSIONS, true); //отключение проверки прав на доступ к файлам и каталогам
define(BX_BUFFER_USED, true); // сбросит уровень буферизации CMain::EndBufferContent
define(LID, "s1");
if (empty($_SERVER["DOCUMENT_ROOT"])) {//DOCUMENT_ROOT может быть не определён, поэтому определим его сами
$_SERVER["DOCUMENT_ROOT"] = '../..';
}
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php"); //подключаем ядро
set_time_limit(0);
while (ob_get_level()) { /* цикл, который сбросит все буферы */
ob_end_flush();
}
Loader::includeModule("iblock"); //подключаем нужные нам модули
Loader::includeModule("catalog");
Loader::includeModule("sale");
Сброс буферов может быть нужен в том случае, если в консоли нужно выводить какие-либо промежуточные значения из выполняемого скрипта.
Кейс таков. У нас есть метод, который формирует ассоциативный массив из символьных кодов и id инфоблоков.
public static function getIBlocksCodeIDMap()
{
if (!\Bitrix\Main\Loader::includeModule('iblock'))
return;
$arIBlocksIDsByCode = array();
$cache = new CPHPCache();
$cache_time = 86400;
$cache_id = 'getIBlocksCodeIDMap' . SITE_ID;
$cache_path = '/getIBlocksCodeIDMap/';
if ($cache_time > 0 && $cache->InitCache($cache_time, $cache_id, $cache_path))
{
$res = $cache->GetVars();
if (is_array($res["IBlocksCodeIDMap"]) && (count($res["IBlocksCodeIDMap"]) > 0))
$arIBlocksCodeIDMap = $res["IBlocksCodeIDMap"];
}
if (empty($arIBlocksCodeIDMap))
{
$rsIBlocks = \CIBlock::GetList(
Array(),
Array(
"SITE_ID" => SITE_ID
)
);
global $CACHE_MANAGER;
$CACHE_MANAGER->StartTagCache($cache_path);
while ($arIBlock = $rsIBlocks->Fetch()) {
$CACHE_MANAGER->RegisterTag("iblock_id_".$arIBlock["ID"]);
$arIBlocksCodeIDMap[$arIBlock['CODE']] = $arIBlock['ID'];
}
$CACHE_MANAGER->RegisterTag("getIBlocksCodeIDMap");
$CACHE_MANAGER->RegisterTag("iblock_id_new");
$CACHE_MANAGER->RegisterTag("getIBlocksCodeIDMap");
$CACHE_MANAGER->EndTagCache();
if ($cache_time > 0) {
$cache->StartDataCache($cache_time, $cache_id, $cache_path);
$cache->EndDataCache(array("IBlocksCodeIDMap" => $arIBlocksCodeIDMap));
}
}
return $arIBlocksCodeIDMap;
}
В каких случаях нам нужно обновлять кэш в папке /getIBlocksCodeIDMap/ ? 1 - при добавлении инфоблока. Произведётся сброс по тэгу iblock_id_new в методе CIBlock::Add 2 - изменение символьного кода инфоблока (ну т.е. обновление инфоблока) и удаление инфоблока. Произведётся сброс по тэгу iblock_id_ в методах CIBlock::Delete и CIBlock::Update.
Т.е. всё хорошо, у нас всегда актуальные данные. Но есть один ньюанс: Сброс по тэгу iblock_id_ будет производиться и при обновлении/удалении/добавлении элемента/секции этого инфоблока (CIBlockSection, CIBlockElement). Т.е. наш кэш будет сбрасываться вообще по каждому чиху, связанному с хотя бы одним из инфоблоков. Хотя нам, в данное методе, абсолютно не нужно реагировать на какие-бы то ни было изменения в элементах или секциях инфоблоков. Нас интересуют только само наличие инфоблоков и их символьные коды. Получается избыточность.
Почему бы штатным методам не добавить более узкоспециализированные тэги и сброс кэша по ним. Т.е. , например , в методе CIBlockElement::Update добавить ClearByTag("CIBlockElement") и ClearByTag("CIBlockElement_Update"), при этом оставив на месте ClearByTag("iblock_id_" . .... ).
И когда разработчик пишет свой метод, то он может более гибко управлять сбросом кэша с результатами работы своего метода. Т.е. если нужно, например, реагировать только на изменения в элементах, в метод добавляется $CACHE_MANAGER->RegisterTag("CIBlockElement"); и всё.
Или, в случае моего примера: $CACHE_MANAGER->RegisterTag("CIBlock_Update"); $CACHE_MANAGER->RegisterTag("CIBlock_Add");
Это будет совсем маленькая заметка. Цель: сбросить кэш всех методов класса.
Идея состоит в том, что класс будет иметь собственную папку с тэгированным кэшем, а папки методов будут подпапками. Например класс iBlockData имеет методы getIBlocksCodeIDMap , getIBlocksData , getIBlocksIDNameMap . Каждый метод заводит свой $cache_path: '/iBlockData/getIBlocksCodeIDMap' , '/iBlockData/getIBlocksData' , '/iBlockData/getIBlocksIDNameMap' . И в каждом методе на свою папку навешан тэг (помимо штатного iblock_id_): $CACHE_MANAGER->RegisterTag("getIBlocksCodeIDMap"); $CACHE_MANAGER->RegisterTag("getIBlocksData"); $CACHE_MANAGER->RegisterTag("getIBlocksIDNameMap");
и ещё один тэг у всех трёх методов: $CACHE_MANAGER->RegisterTag("iBlockData");
Теперь, если нам понадобится уронить кэш сразу всех трёх методов мы можем сделать либо так $CACHE_MANAGER->ClearByTag("iBlockData"); , либо так ::("/iBlockData");
В старом ядре методы работают по принципу "кто на что горазд". Какие-то методы вызывают , какие-то используют обёртки типа , кто-то вызывает (вычисляется директория с кэшем, которая соответствует таблице и очищается эта директория. Директория имеет вид типа $dbType."/".$tableId, где tableId- это, например, b_iblock_type) , некоторые классы используют собственные методы для очистки кэша. Ещё нет единого стандарта именования тэгов: iblock_id_new , sale-location-data , LEARNING_GROUP - snake_case, kebab-case, SCREAMING_SNAKE_CASE
public static function getIBlocksCodeIDMap()
{
if (!\Bitrix\Main\Loader::includeModule('iblock'))
return;
$arIBlocksIDsByCode = array();
$cache = new CPHPCache();
$cache_time = 86400;
$cache_id = 'getIBlocksCodeIDMap' . SITE_ID;
$cache_path = '/getIBlocksCodeIDMap/';
if ($cache_time > 0 && $cache->InitCache($cache_time, $cache_id, $cache_path))
{
$res = $cache->GetVars();
if (is_array($res["IBlocksCodeIDMap"]) && (count($res["IBlocksCodeIDMap"]) > 0))
$arIBlocksCodeIDMap = $res["IBlocksCodeIDMap"];
}
if (empty($arIBlocksCodeIDMap))
{
$rsIBlocks = \CIBlock::GetList(
Array(),
Array(
"SITE_ID" => SITE_ID
)
);
global $CACHE_MANAGER;
$CACHE_MANAGER->StartTagCache($cache_path);
while ($arIBlock = $rsIBlocks->Fetch()) {
$CACHE_MANAGER->RegisterTag("iblock_id_".$arIBlock["ID"]);
$arIBlocksCodeIDMap[$arIBlock['CODE']] = $arIBlock['ID'];
}
$CACHE_MANAGER->RegisterTag("getIBlocksCodeIDMap");
$CACHE_MANAGER->RegisterTag("iblock_id_new");
$CACHE_MANAGER->RegisterTag("getIBlocksCodeIDMap");
$CACHE_MANAGER->EndTagCache();
if ($cache_time > 0) {
$cache->StartDataCache($cache_time, $cache_id, $cache_path);
$cache->EndDataCache(array("IBlocksCodeIDMap" => $arIBlocksCodeIDMap));
}
}
return $arIBlocksCodeIDMap;
}
данный метод выдаёт массив типа "Символьный код инфоблока" => "id инфоблока". В идентификатор массива $cache_id ложатся параметры, от которых зависит результат работы кода, находящийся внутри if (empty($arIBlocksCodeIDMap)) { } . В данном примере параметр только один - SITE_ID . Но в зависимости от решаемой задачи, их может быть несколько. При изменении хотя бы одного параметра, создаётся новый вариант кэша. Папка с кэшем после выполнения метода:
Для демонстрации я добавлю идентификатору кэша новый параметр, значения которого будут несколько раз меняться:
я несколько раз выполнил метод и папка с кэшем изменила своё содержимое: Каждому идентификатору $cache_id в папке $cache_path соответствует подпапка (08, 11, 6f, 9с и т.д.). Через $CACHE_MANAGER->RegisterTag папке $cache_path назначаются тэги. При сбросе хотя бы по одному из назначенных тэгов (ClearByTag) содержимое папки $cache_path полностью очистится. getIBlocksCodeIDMap - это созданный тэг, который будет назначен только папке с кэшем для этого метода. iblock_id_ - это штатный тэг. Так выглядит сброс кэша по штатному тегу:
iblock_id_new - тэг, по которому делается сброс при добавлении элемента Сброс по iblock_id_new делается в методах: CAllIBlock::Add CSocNetLogTools::SetUFRights
Зачастую используется метод clearIblockTagCache($iblock_id) , в котором, в свою очередь вызывается $CACHE_MANAGER->ClearByTag('iblock_id_'.$iblock_id); CIBlock::clearIblockTagCache вызывается в : CAllPrice::ReCalculate CAllCatalogProduct::QuantityTracer CCatalogProductProvider::clearPublicCache CCatalogDocsTypes::clearPublicCache CatalogProvider::clearPublicCache CIBlockRights::SetRights CAllIBlockElement::Add CAllIBlockElement::Delete CAllIBlockSection::Add CAllIBlockSection::Update CAllIBlockSection::Delete CIBlockElement::Update
CAllIBlock имеет собственный clearIblockTagCache и вызывается он в методах: Update Delete
$DB->StartTransaction();
CIBlockElement::SetPropertyValuesEx($ELEMENT_ID, $arParams["IBLOCK_ID"], array( //сброс кэша по "iblock_id_" . $arParams["IBLOCK_ID"] сам не делает
public static function getIBlocksData()
{
if (!\Bitrix\Main\Loader::includeModule('iblock'))
return;
$arIBlocksData = array();
$cache = new CPHPCache();
$cache_time = 86400;
$cache_id = 'getIBlocksData' . SITE_ID;
$cache_path = '/getIBlocksData/';
if ($cache_time > 0 && $cache->InitCache($cache_time, $cache_id, $cache_path))
{
$res = $cache->GetVars();
if (is_array($res["IBlocksData"]) && (count($res["IBlocksData"]) > 0))
$arIBlocksData = $res["IBlocksData"];
}
if (empty($arIBlocksData))
{
$rsIBlocks = \CIBlock::GetList(
Array(),
Array(
"SITE_ID" => SITE_ID
)
);
global $CACHE_MANAGER;
$CACHE_MANAGER->StartTagCache($cache_path);
while ($arIBlock = $rsIBlocks->Fetch()) {
$CACHE_MANAGER->RegisterTag("iblock_id_".$arIBlock["ID"]);
$arIBlocksData[$arIBlock['ID']] = $arIBlock;
}
$CACHE_MANAGER->RegisterTag("getIBlocksData");
$CACHE_MANAGER->RegisterTag("iblock_id_new");
$CACHE_MANAGER->EndTagCache();
if ($cache_time > 0) {
$cache->StartDataCache($cache_time, $cache_id, $cache_path);
$cache->EndDataCache(array("IBlocksData" => $arIBlocksData));
}
}
return $arIBlocksData;
}
Метод 3:
public static function getIBlocksIDNameMap()
{
if (!\Bitrix\Main\Loader::includeModule('iblock'))
return;
$arIBlocksIDNameMap = array();
$cache = new CPHPCache();
$cache_time = 86400;
$cache_id = 'getIBlocksIDNameMap' . SITE_ID;
$cache_path = '/getIBlocksIDNameMap/';
if ($cache_time > 0 && $cache->InitCache($cache_time, $cache_id, $cache_path))
{
$res = $cache->GetVars();
if (is_array($res["IBlocksIDNameMap"]) && (count($res["IBlocksIDNameMap"]) > 0))
$arIBlocksIDNameMap = $res["IBlocksIDNameMap"];
}
if (empty($arIBlocksIDNameMap))
{
$rsIBlocks = \CIBlock::GetList(
Array(),
Array(
"SITE_ID" => SITE_ID
)
);
global $CACHE_MANAGER;
$CACHE_MANAGER->StartTagCache($cache_path);
while ($arIBlock = $rsIBlocks->Fetch()) {
$CACHE_MANAGER->RegisterTag("iblock_id_".$arIBlock["ID"]);
$arIBlocksIDNameMap[$arIBlock['ID']] = $arIBlock["NAME"];
}
$CACHE_MANAGER->RegisterTag("getIBlocksIDNameMap");
$CACHE_MANAGER->RegisterTag("iblock_id_new");
$CACHE_MANAGER->EndTagCache();
if ($cache_time > 0) {
$cache->StartDataCache($cache_time, $cache_id, $cache_path);
$cache->EndDataCache(array("IBlocksIDNameMap" => $arIBlocksIDNameMap));
}
}
return $arIBlocksIDNameMap;
}
Мы можем сделать сброс по тэгу getIBlocksData $CACHE_MANAGER->ClearByTag("getIBlocksData"); и у нас очистится папка getIBlocksData , т.е. сбросится кэш, с которым работает второй метод. Все три метода работают с одним и тем же набором инфоблоков, и если в системе, например, есть инфоблок с id = 4, то на все три папки - /getIBlocksCodeIDMap/ , /getIBlocksData/ , /getIBlocksIDNameMap/ - будет назначен тэг "iblock_id_4". И при редактировании инфоблока с id = 4 через админку вызовется метод ::() , который вызовет (4), который вызовет для "iblock_id_4" и все три папки очистятся (точнее не совсем очистятся - произведётся отложенное удаление папок - но об этом я напишу позже, сейчас - в данном контексте - это не важно), т.е. для всех трёх методов произведётся сброс кэша.
( ) хранит данные о соответствии тэгов кэша папкам в таблице b_cache_tag , из таблицы извлекается список папок и для каждой папки выполняется cleanDir. - кстати метод очень интересный, его алгоритм я детальнее разберу в одной из следующих публикаций.
В блогах иногда можно встретить такое понятие, как "Кэш инфоблока". На самом деле у инфобока кэша, как такового, не существует, есть просто штатный тэг iblock_id_ , который вызывают штатные функции Add, Update, Delete. Кстати iblock_id_ - это не единственный штатный тэг, полный список штатных тэгов и методов, которые делают по ним сброс, я предоставлю в следующей публикации.
На странице рекомендованных хостингов указана в качестве одного из вариантов компания Ru-Center (nic.ru). Также указано, что данная компания имеет статус рекомендуемого хостинг-партнёра и компетенцию «Хостинг PHP»
Я обратился в данную компанию с просьбой подсказать, как выбрать тариф из представленных данной компанией для размещения сайта на 1С-Битрикс. На свой запрос я получил ответ, что параметры хостинга должны определять разработчики и отказ предоставить любую дополнительную информацию. На вопрос, как же всё-таки выбрать, была получена рекомендация купить минимальный тариф и перейти на больший, если не устроит.
На странице указано, что компетенцию «Хостинг PHP», партнёры получают, если
предоставляют услуги хостинга для проектов, работающих на ;
предлагают тарифные планы, которые соответствуют продукта «1С-Битрикс: Управление сайтом»;
выполняют ;
сотрудники партнера сертифицированы и готовы оказать квалифицированную помощь пользователям в вопросах установки и конфигурирования сервера для оптимальной работы.
С чем я столкнулся при обращении к технической поддержке компании RU-Center
1) От технической поддержки ничего не было сообщено, какие из их тарифных планов удовлетворяют требованиям CMS 1С-Битрикс, это предлагалось делать на основании информации от разработчиков сайта (т.е. от компании 1С-Битрикс, разработавшей CMS и компании, делавшей разработку самого сайта на базе CMS). Таким образом не выполняется п. 2 из перечисленных выше
2) Предлагаемый виртуальный хостинг настраивать должен клиент, таким образом п. 3 также не выполняется. Сообщить какие-либо настройки необходимые для оптимальной работы их оборудования мне также отказали
3) Я не знаю, имели ли сотрудники, общавшиеся со мной какие-либо сертификаты, но все их ответы сводились к одному, что клиент сам своими силами должен делать установку и это не их дело, как либо помогать с настройкой операционной системы и системных программ на предоставляемом ими оборудовании, а также, что оптимальная работа определяется разработчиками и именно они, а не сотрудники компании-хостера должны что-то предлагать и осуществлять консультации по выбору и настройке хостинга. Таким образом и п. 4 полностью отсутствует у данной компании.
В итоге, 3 пункта из 4, требующихся для получения компетенции Хостинг PHP не выполняются, специалисты данной компании абсолютно никак не соответствуют квалификации хостинг-партнёра Битрикс. Всё что они умеют - это отправлять клиентам ссылку на описание тарифов, кроме которой им нечего добавить.
Какое отношение данная компания имеет к Битриксу - непонятно.
Есть следующий кейс: Компонент news.list выводит элементы из инфоблока. У элементов инфоблока есть множественное свойство типа "Привязка к пользователю" под названием "Пользователи, которым понравилась новость". Задача: выводить логины пользователей, которым понравилась новость. Если пользователь авторизован, и он поставил "лайк", то выводить кнопку "Не нравится", если он не ставил "лайк", то выводить кнопку "Нравится". Ну т.е. типичный функционал оценки контента. Мне пришло в голову несколько вариантов того, как можно организовать этот функционал в рамках штатного news.list и увязать его работу с управляемым кэшем компонента, некоторые варианты мне показались вполне приемлемыми, некоторые - не очень. 1) Добавить к news.list параметр "USER_ID" и скармливать ему id авторизованного пользователя. А в template уже работаем с $arParams["USER_ID"] . Серьёзный недостаток в том, что управляемый кэш зависит от id пользователя, что является очень нежелательной ситуацией, ибо для каждого авторизованного пользователя будет создаваться свой экземпляр кэша. Вес файлов кэша будет очень большой.
2) Добавить к news.list параметр "LIKED_NEWS_IDS" , который будет содержать id статей, лайкнутых авторизованным пользователем. Т.к. возможен такой вариант, когда несколько разных пользователей полайкали один и тот-же набор статей, то объём кэша должен быть меньше (ну по крайней мере не больше), чем в реализации, описанной в первом варианте. Вариант считаю неидеальным, зависимость управляемого кэша от id пользователя всё-равно присутствует, но на этот раз косвенная. Нутром чую, что можно сделать как-то проще, но пока остановился на этом варианте. Вот код:
$(function () {
(function () {
$(document).on("click", ".js-like", function () {
var $button = $(this);
var $parent = $button.closest(".js-news-wrap__item");
var $blockUsers = $parent.find(".js-news-item__line");
$.post($button.data("handler-path"), { newId: $button.data("new-id"), iblockId: $button.data("iblock-id") }, function (data) {
if (data.content){ $blockUsers.html(data.content); }
if (data.action === "like") {
$button.addClass("btn_red");
$button.removeClass("btn_green");
$button.html($button.data("dislike-label"));
} else if (data.action === "dislike") {
$button.removeClass("btn_red");
$button.addClass("btn_green");
$button.html($button.data("like-label"));
}
}, "json");
});
})();
});
в обработчике меняем цвет и надпись кнопки , а так-же содержимое блока, в котором выведены логины пользователей, лайкнувших новость.
like.php. обработчик лайка, который вызываем ajax-ом :
<?require_once($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");
CModule::IncludeModule('iblock');
$newId = intVal(htmlspecialchars($_POST['newId']));
$iblockId = intVal(htmlspecialchars($_POST['iblockId']));
if(empty($newId)) die();
if(empty($iblockId)) die();
global $USER;
if ($USER->IsAuthorized()) {
$arAllRegisteredUsersLogins = array();
$arAllRegisteredUsersLogins = HelperUsers::getAllRegisteredUsers();
$arFilter = array(
"IBLOCK_ID" => $iblockId,
"ID" => $newId
);
$rsElement = CIBlockElement::GetList(
array(),
$arFilter,
false,
false,
array("ID", "IBLOCK_ID", "PROPERTY_LIKED_USERS")
);
$PROPERTY_LIKED_USERS = array();
while ($arElement = $rsElement->GetNext()) {
/** PROPERTY_LIKED_USERS - это множественное свойство, поэтому вместо if
* используется while */
$PROPERTY_LIKED_USERS[] = $arElement["PROPERTY_LIKED_USERS_VALUE"];
}
sort($PROPERTY_LIKED_USERS);
$strActionType = "like";
if (in_array($USER->GetID(), $PROPERTY_LIKED_USERS)) {
$PROPERTY_LIKED_USERS = array_diff($PROPERTY_LIKED_USERS, array($USER->GetID()));
$strActionType = "dislike";
} else {
$PROPERTY_LIKED_USERS = array_merge($PROPERTY_LIKED_USERS, array($USER->GetID()));
}
sort($PROPERTY_LIKED_USERS);
CIBlockElement::SetPropertyValueCode($newId, "LIKED_USERS", $PROPERTY_LIKED_USERS);
if (defined('BX_COMP_MANAGED_CACHE')) {
$GLOBALS['CACHE_MANAGER']->ClearByTag('iblock_id_' . $iblockId);
$GLOBALS['CACHE_MANAGER']->ClearByTag('LikedNewID_' . $newId); // подозреваю, что эта строка здесь избыточна, но не стал проверять, оставил на всякий случай.
//Сброса кэша по стандартному тэгу iblock_id_ должно быть вполне достаточно.
}
$arLikedUsersLogins = array();
foreach ($PROPERTY_LIKED_USERS as $intLikedUserID) {
$arLikedUsersLogins[] = $arAllRegisteredUsersLogins[$intLikedUserID];
}
$VALUE_LOGINS_STR = implode(", ", $arLikedUsersLogins);
$result = array(
'action' => $strActionType,
'content' => $VALUE_LOGINS_STR
);
echo json_encode($result);
}else{
die();
}
Вот собственно и всё.
3) Ещё один вариант, который я спроектировал, но на практике не реализовывал. Отказывается от параметра LIKED_NEWS_IDS и задействуем component_epilog. В нём через HelperIblock::getLikedNews получаем список id новостей, которые лайкнул текущий пользователь. Далее js-ом меняем цвет и надпись кнопок. js обработчик и ajax-обработчик лайка остаются такими-же, как в предыдущем варианте.
4) Посмотреть, как реализованы лайки в forum.topic.list
Выкладывать модули в маркеплейс, как оказалось не простая задача, начиная от кодировки файлов, до галочки с Loc::getMessage. В установщике /install/index.php можно использовать, а если этот файл находится в обновлении, то нельзя, только GetMessage.
Может повлияло не желание разобраться с немного странный конструктором, может то, что бывший коллега решил тоже выложить свой модуль. Натолкнуло на мысль наконец подвинуть планы изучить npm с gulp.
Если коротко, переделать скрипт под себя, заодно изучив язык, а потом переписать на новый gulp 4 оказалось хорошей идеей.
Модулем пользуются и даже кто то активно качает обновления, надеюсь кому то пригодился.
Выкладываю что он умеет на текущий момент:
делать архивы с последней версией .last_varsion и собирать обновления (главное в гите указать теги вида 1.0.0 для сборки)
делать архивы для ручной установки в utf8 и cp1251 кодировках
кодировать подмодули в уникальный namespace (очень удобно, имея один системный модуль, тянуть по всем проектам, и не беспокоится с несовместимостью, для каждого модуля будет своя версия)
офбускация кода (нужно ли вопрос спорный, но пускай будет)
Небольшие правила для сборки обновлений, в папке /dist/version/1.1.0 должны быть:
/description.* (обязательный) - содержит описание обновления, где * - идентификатор языка в системе
/updater.php – файл запускается при установке обновления
/version_control.php - служит для организации связи между версиями модулей.
Надеюсь кому то пригодится, если будут пожелания или замечания, посмотрим как можно их решить. В планах пока, только добавить тесты.
Что делать разработчику, когда боевой сайт находится на https-протоколе, а сервер разработки на http? Попытки отредактировать что-либо в админке проваливаются из-за перенаправления на https адрес сервера разработки, который, понятно, не существует. Полчаса поисков привели к простому решению: нужно в bitrix/php_interface/dbconn.php заменить директиву $_SERVER["HTTPS"] = "On" на $_SERVER["HTTPS"] = "Off".
Привет. Есть такой неприятный момент. Использую вебхук из 1с. Загрузка файла картинки с пользовательского поля компании.
Получая данные пользовательского поля типа Файл, мне приходит downloadUrl и урл такой, как при использование не веб хуков а полноценного REST API с oauth авторизацией. Считаю это ошибкой. Можно ли получить данный файла, например в base64?
В работе возникла такая задачка: необходимо, чтобы для случаев, когда задание бизнес-процесса (БП) ставится на группу пользователей, оно автоматически ставилось и на пользователей, которые добавились в группу после того, как задание БП уже было создано. Например, есть на сайте группа пользователей "редакторы" и в ходе БП им ставится какая-то задача. Нужно, чтобы при добавлении пользователя в группу "редакторы", он получал доступ ко всем уже существующим задачам БП, поставленным на редакторов.
Создаем обработчики, добавляющие задачу пользователю в момент изменения группы. Ниже в примере показан обработчик при создании пользователя. Аналогичные обработчики надо сделать для события изменения пользователя и события изменения групп пользователя:
define('EDITOR_GROUP_ID', 10);
AddEventHandler("main", "OnAfterUserAdd", "OnBeforeUserUpdateHandler");
function OnAfterUserAdd($arFields) {
if ($arFields['ID'] && in_array(EDITOR_GROUP_ID, $arFields['GROUP_ID'])) {
$userIds = array(); // другие пользователи-редаткоры
$rsUser = CUser::GetList($b = 'id', $o = 'asc', array('GROUPS_ID' => EDITOR_GROUP_ID), array('FIELDS' => array('ID')));
while ($arUser = $rsUser->Fetch()) {
$userIds[] = $arUser['ID'];
}
if (!empty($userIds)) {
\CModule::IncludeModule('bizproc');
$rsTasks = \CBPTaskService::GetList([], ['USER_ID' => $userIds, 'STATUS' => CBPTaskUserStatus::Waiting], false, false, array('ID', 'USER_ID'));
while ($arTask = $rsTasks->Fetch()) {
// увы, штатного апи метода для добавления задачи пользователю не существует
$GLOBALS['DB']->Query(
"INS ERT IN TO b_bp_task_user (USER_ID, TASK_ID, ORIGINAL_USER_ID) " .
"VALUES (" . intval($arFields['ID']) . ", " . intval($arFields['ID']) . ", " . intval($arFields['ID']) . ") "
);
CUserCounter::Increment($arFields['ID'], 'bp_tasks', '**');
}
}
}
}
И казалось бы, что мы решили проблему. Однако это не так. Дело в том, что помимо собственно записи в таблице b_bp_task_user, информация о том, кто должен выполнить задачу, хранится и в самом бизнес процессе. Поэтому необходимо доработать исходный код бизнес процесса, чтобы изменить эту логику.
Сделаем это на примере действия "Запрос дополнительной информации". Активити этого действия называется RequestInformationActivity и находится в папке /bitrix/activities/bitrix/requestinformationactivity
Чтобы наши изменения не были затерты обновлением, нужно скопировать эту папку в собственное пространство имен, т.е. либо в bitrix/activities/custom/requestinformationactivity либо в /local/activities/requestinformationactivity
Далее нужно найти строчку в файле активити, .т.е. в requestinformationactivity.php, которая проверяет, соответствует ли текущий пользователь списку пользователей, имеющих право на выполнение данной задачи. Эта проверка осуществляется в методе \CBPRequestInformationActivity::OnExternalEvent:
Мне иногда на полном серьезе кажется, что в крупных компаниях есть специальные отделы ухудшения качества. То есть специальные люди сидят и думают, куда в постели насыпать крошек, чтобы спать было все еще возможно, но не идеально удобно.
Может быть, это как-то психологами обосновывается? Типа, если юзеру совсем хорошо, то он недостаточно лоялен. Но когда есть шероховатости, он про них знает и осознанно с ними мирится, то его вовлеченность выше?
Иногда вещь сделана как будто специально так, чтобы ты об нее мизинчиком ударялся, каждый раз, когда проходишь мимо.
Это относится ко всему на свете, к предметам, которые нас окружают, смартфонам, которые, как будто специально сделаны стеклянными и скользкими, к интерфейсам приложений и CMS. Понятное дело, что речь сейчас о Битриксе.
Вполне возможно, что на большой скорости, когда ты летишь вперед, выпуская по 2 больших апдейта в год, когда, кроме БУС, есть еще Б24, мелочи остаются незамеченными.
Но, коллеги, вы говорите про оптимизацию бизнес-процессов, про оптимизацию продаж, про личную эффективность сотрудников компаний.
Начните говорить про эффективность контент-менеджеров в вашей собственной админке. Хотя бы 5 минут, на каждой презентации новой версии БУС.
Модуль управления структурой устарел в 2010-м. «Новый» визуальный редактор был от рождения корявым уродцем. Одно только это суммарно у пользователей битрикса сжигает ежедневно целые жизни в оплаченных человекочасах!
Проблема со скриншота - не столько в криворукости разработчика, сколько в отсутствии нормального инструмента от Битрикса, который позволил бы сделать управление страницами со сложным дизайном простым и понятным. Это как если бы для списков (новостей, статей, каталогов) в Битриксе отсутствовал модуль Инфоблоки.
И ведь есть же в компании тестировщики, есть «владелец продукта», собирается обратная связь от клиентов и партнеров.
Не поверю, что все эти рекомендации, пожелания и мольбы вот прям вообще никто не слышит. Не иначе, кто-то в компании Битрикс имеет право класть болт наложить вето, на часть замечаний от тестировщиков? Типа эти мелочи не являются достаточной проблемой, чтобы их обсуждать они не дают ошибку - значит все ок.
Ошибка, это не только когда что-то явно неработает. Ошибкой может быть и отсутствие какого-то очевидного и нужного функционала (например, сортировка разделов в Медиабилиотеке) или когда работа имеющегося функционала создает проблемы (Помощник да и )
Притормозите ненадолго возле кафе, пусть пользователи продукта передохнут, перекусят, сходят в туалет. Соберите список мелочей, которые давно ждут своего часа, выпустите cosmetic update. И после снова по газам.
Или, другой пример работы Отдела по ухудшению качества. Вот разместит Сергей Рыжиков в своем Инстаграм фотку с Айфона, или в Твиттере сделает пост, поставит там смайлик-эмодзи. Любой пользователь Битрикса, который решит поделиться этой фоткой в новостях на своем сайте на Битриксе не сможет этого сделать!
И не только потому, что в визуальном редакторе из Инстаграма и Твиттера. Даже в режиме html запихать эту вставку не получится.
Потому что Битрикс при сохранении обрежет код по первому символу эмодзи. То же относится и к видео с Ютуба, если в его названии есть эмодзи.
Для наглядности записал небольшое видео (1 мин. 40 сек.):
Логично предположить, что и формы обратной связи не будут корректно работать, если пользователь запихает в текст эмодзи. Как минимум те, что сделаны на инфоблоках.
Русские улыбаются редко, зачем им эмодзи? Зачем поддерживать в визуальном редакторе вставки контента, из сервисов в которых у каждого второго пользователя интернета есть аккаунт? Разве вставка в новость на сайте твита или фотки из Инстаграм - это довольно популярный кейс, чтобы отклониться от намеченного курса, притормозить и пофиксить баг?
Если кратко подвести итог:
Пофиксите огрехи в юзабильности в админке, на это у вас вряд ли уйдет много времени.
Пофиксите ошибку с сохранением эмодзи в инфоблоках.
Начните думать про «Управление структурой 2.0.». Дизайн страниц сайтов давно уже стал сложным и разнообразным, верстка тоже. Нужен инструмент, в котором пользователь сможет легко работать с такими страницам не ломая их. Сейчас приходится или идти на компромисс с совестью, фигачить в область контента разметку и стили, которые легко сломать, открыв в виз.редакторе, или костылить, делая это на инфоблоках.
В проекте используется простой компонент, задача выводить кол-во элементов. Компонент выводится на всех детальных страницах с информацией по элементу. Это все решил повесить на стандартный механизм кеша:
if ($this->StartResultCache())
{
$arResult = []; // Тут логика выборки из базы
$this->IncludeComponentTemplate();
}
Под Apache, все работает корректно. На сервере под nginx, вызов компонента на разных страницах элементов, генерировал новый кеш, который используется только на текущей странице элемента. В результате папка с данными по кешу растет пропорционально посещению страниц элементов.
После дебага, выявилось, что переменная $_SERVER['SCRIPT_NAME'] содержит url страницы, а не "/bitrix/urlrewrite.php". Как решение в настройках конфига nginx добавил запись: "fastcgi_param SCRIPT_NAME /bitrix/urlrewrite.php;", получилось следующее:
Больной вопрос для сисадминов - это NTLM авторизация Bitrix24 Desktop в доменной сети,. XMPP на битриксе не удобно, глючно и не собирается меняться:
Миранда хоть и мощный инструмент, но в эпоху мессeнжеров, она кажется калькулятором среди айфонов. А ведь у битрикса чат хорошеет от версии к версии.
Но есть решение. Есть в такая программа , по сути это хром в котором можно писать приложение используя JS, HTML5 и CSS. Подробнее в .
Быстрый старт
Скачиваем nw.js, я приложил к сообщению уже проверенную стабильную версию, но вы можете . Программа не имеет инсталлятора, поэтому распаковываем, и ищем в папке файл package.json, это файл конфигурации программы. Если вы качали программу из сообщения, то он будет уже заполнен, вам останется только открыть блокнот и "Найти и заменить" SITE_SERVER_NAME на доменное имя своего портала, например intranet.google.local
Для тех кто скачал с офф сайта программу, выкладываю настройки ниже:
На этом настройка программы завершена, если почитаете доку, то найдете много полезных настроек, но для старта нам пока этого достаточно:
Следующим этапом нам потребуется внести изменения в файл /desktop_app/index.php на сервере битрикса:
//Найти и заменить
location.href = '/';
//на
location.href = '/auth/?backurl=/desktop_app/';
//Таких строк должно быть две. 17 и 44, номер может отличаться в зависимости от версии Б
//Далее меняем на 31 строке
$APPLICATION->IncludeComponent("bitrix:im.messenger", "fullscreen", Array(
"CONTEXT" => "FULLSCREEN",
"DESIGN" => "DESKTOP",
), false, Array("HIDE_ICONS" => "Y"));
//на
$APPLICATION->IncludeComponent("bitrix:im.messenger", "", Array(
"CONTEXT" => "DESKTOP",
//"DESIGN" => "DESKTOP",
), false, Array("HIDE_ICONS" => "Y"));
Готово. Можно запускать nw.js и проверять, если что писать вопросы в комменты. Далее отдайте папку c nw.js своему сисадмину, он раскидает её по компам и добавит в автозапуск.
Подробнее
Конечно если использовать только быстрый старт, то возникает много неудобств, например нет иконки в трее, при нажатии на закрыть прога выключается, а хочется что бы свернулась. Иконка не мигает, не понятно когда пришло сообщение. Всё это решаемо, но об этом напишу если данная тема вообще заинтересует кого-нибудь.
Задача: дать возможность пользователю в любой момент оплатить заказ банковской картой, независимо от выбранного на этапе оформления способа оплаты. Многие пользователи выбирают способ оплаты по умолчанию (это наличный расчёт), а потом звонят и спрашивают, как оплатить картой. Возможность смены способа оплаты самим пользователем на сайте сделана не была. В итоге мненеджер должен в админке менять способ оплаты, после этого появляется кнопка оплаты картой в ЛК пользователя в списке заказов. Решение: я использовал новую возможность Битрикса в одном заказе иметь несколько оплат. Через ядро D7 создаю принудительно дополнительную оплату - "оплата банковской картой".
Достоинства: пользователю не надо ничего менять, сразу работает кнопка оплаты Недостатки: в админке висят две оплаты на полную сумму заказа, оплату картой приходится ставить первой, чтобы срабатывал стандартный компонент bitrix:sale.order.payment, который видит только первую оплату
В типовой сборке БУС есть обработчик платежной системы LiqPay. К сожалению, в форме настроек, нет возможности включить тестовый режим для проверки, хотя апи LiqPay позволяет:
Для включения тестового режима, необходимо в передаваемых параметрах добавить значение sandbox 1.
Из документации
sandbox - Включает тестовый режим. Средства с карты плательщика не списываются. Для включения тестового режима необходимо передать значение 1. Все тестовые платежи будут иметь статус sandbox - успешный тестовый платеж.
Для проверки работы системы оплаты и возврата статуса платежа, можно ВРЕМЕННО (исключительно на время проверки - потом обязательно убрать) добавить этот параметр.
В 17.8.9 версии модуля Интернет-магазин, файл с параметрами запроса расположен по адресу: /bitrix/modules/sale/payment.php
1. Добавляем параметр sandbox для передачи его на сервер LiqPay
2. При использовании тестовых платежей, LiqPay возвращает статус оплаты sandbox вместо success. Для того, чтобы Битрикс воспринял правильно статус и обновил статус документа Оплаты, добавляем и его:
payment.php:140, функция processRequest
Добавляем в условие наш статус
if ($this->isCorrectHash($payment, $request))
{
if ($status == 'success' || $status == 'sandbox')
{
return $this->processNoticeAction($payment, $request);
}
else if ($status == 'wait_secure')
{
return new PaySystem\ServiceResult();
}
}
Теперь можно протестировать систему оплаты - деньги с карты в тестовых платежах не снимаются. По окончанию тестирования, не забываем забрать наш код.
Статус "Другая ошибка" при проверке мобильной юзабилити Googlebot-Mobile Господа битриксоводы и программисты, кто сталкивался, с чем может быть связано? Имеется адаптивный шаблон сайта на битрикс. При проверке на юзабилити, Google ругается на скрипты из шаблона и картинки, хотя они вполне себе открываются. Статус ошибки пишет "Другая ошибка". В robots нет Disallow директив, которые относились бы к этим скриптам. Ни одной разумной причины для вывода таких ошибок не видно. Битрикс умеет удивлять даже видавших виды, этого у него не отнять. Где искать? Подскажите. Заранее благодарю!
В документации метода CCatalogProduct::Add(), есть непонятные моменты.
Начнем обсуждение с ключа TYPE
TYPE - тип товара (значения по умолчанию зависят от вида торгового каталога);
А какие же типы типы продукта можно использовать?
Обычный продукт (простой)
В ключе TYPE укажите \Bitrix\Catalog\ProductTable::TYPE_PRODUCT
Ключи у этого типа, описаны в документации
Комплект или набор
В ключе TYPE укажите \Bitrix\Catalog\ProductTable::TYPE_SET
Чем отличается, в коде, комплект от набора не удалось понять, если знаете - напишите. Какие ключи надо использовать у этого типа, также нигде не смог найти. Если знаете, пишите в коментах.
Товар с торговыми предложениями
В ключе TYPE укажите \Bitrix\Catalog\ProductTable::TYPE_SKU
Ключи также НЕ описаны в документации.
Торговое предложение
В ключе TYPE укажите \Bitrix\Catalog\ProductTable::TYPE_OFFER
Ключи у этого типа, описаны в документации
В коде есть еще 2 типа TYPE_FREE_OFFER и TYPE_EMPTY_SKU, но когда их лучше всего использовать не понятно. Знаете - пишите!
@darkfriend: Битрикс сохраняет свой пофигизм к разработчикам. Если вы хотите, чтоб за продукт платили, в первую очередь ДОЛЖНА быть качественная полная документация. Битрикс на протяжении всего своего существования второстепенно относится к этому.
У элементов инфоблока есть свойства rating и vote_count . Но при подобной выборке к ним надо обращаться через верхний регистр. Подобный ньюанс был описан где-то в комментариях к документации, но я повторил на всякий случай. Вывод: назначайте добавляемым свойствам символьный коды в верхнем регистре, пусть они на вас орут (в смысле символьные коды). и не придётся разбираться в подобных ньюансах.