Задать вопрос
  • Как вывести редактор CHTMLEditor с помощью ajax?

    @dom12 Автор вопроса
    Удалось разобраться, вот что получилось:

    Скрипты и стили
    <?if (Loader::IncludeModule("fileman")){?>
    <link rel="stylesheet" type="text/css" href="/bitrix/js/fileman/html_editor/html-editor.css">
    <?CJSCore::Init(array('window','ajax','fx','uploader','ui.design-tokens', 'date', 'timer'));
    foreach(array(
    	'range.js',
    	'html-actions.js',
    	'html-views.js',
    	'html-parser.js',
    	'html-base-controls.js',
    	'html-controls.js',
    	'html-components.js',
    	'html-snippets.js',
    	'html-editor.js',
    ) as $DIST){?>
    	<script src="/bitrix/js/fileman/html_editor/<?=$DIST?>"></script>
    <?}?>
    <script src="/bitrix/js/main/dd.js"></script>
    <script>
    	BX.message(<?=CUtil::PhpToJSObject(\Bitrix\Main\Localization\Loc::loadLanguageFile($_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/fileman/classes/general/html_editor_js.php'), false);?>);
    	var configBXHtmlEditor = {
    		cssIframePath:'<?=\CUtil::GetAdditionalFileURL('/bitrix/js/fileman/html_editor/iframe-style.css')?>',
    		designTokens:'<?=json_encode(\Bitrix\Main\UI\Extension::getHtml('ui.design-tokens'),JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_PARTIAL_OUTPUT_ON_ERROR)?>',
    		spellcheck_path:'<?=\CUtil::GetAdditionalFileURL('/bitrix/js/fileman/html_editor/html-spell.js')?>'
    	};
    	(function(window) {
    		if (!window.BXHtmlEditor){
    			var BXHtmlEditor = {
    				editors: {},
    				configs: {},
    				dialogs: {},
    				Controls: {},
    				SaveConfig: (config)=>{
    					BX.ready(()=>{
    						if (config && config.id) BXHtmlEditor.configs[config.id] = config;
    					});
    				},
    				Show: (config, id)=>{
    					BX.ready(()=>{
    						if ((!config || typeof config != 'object') && id && BXHtmlEditor.configs[id]) config = BXHtmlEditor.configs[id];
    						if (config && typeof config == 'object'){
    							if (!BXHtmlEditor.editors[config.id] || !BXHtmlEditor.editors[config.id].Check()){
    								BXHtmlEditor.editors[config.id] = new window.BXEditor(config);
    							}else{
    								BXHtmlEditor.editors[config.id].CheckAndReInit();
    							}
    						}
    					});
    				},
    				Hide: (id) => {
    					if(BXHtmlEditor.editors[id]) BXHtmlEditor.editors[config.id].Hide();
    				},
    				Get: (id) => {
    					return BXHtmlEditor.editors[id] || false;
    				},
    				OnBeforeUnload: ()=>{
    					for (var id in BXHtmlEditor.editors){
    						if (BXHtmlEditor.editors.hasOwnProperty(id) &&
    							BXHtmlEditor.editors[id].config.askBeforeUnloadPage === true &&
    							BXHtmlEditor.editors[id].IsShown() &&
    							BXHtmlEditor.editors[id].IsContentChanged() &&
    							!BXHtmlEditor.editors[id].IsSubmited() &&
    							BXHtmlEditor.editors[id].beforeUnloadHandlerAllowed !== false
    						){
    							if(typeof(BX.desktopUtils) != 'undefined' && typeof(BX.desktopUtils.isChangedLocationToBx) == 'function' && BX.desktopUtils.isChangedLocationToBx()) return;
    							return BXHtmlEditor.editors[id].config.beforeUnloadMessage || BX.message('BXEdExitConfirm');
    						}
    					}
    				},
    				ReplaceNewLines: (content) => {
    					content = content.replace(/<[^<>]*br>\n/ig, '#BX_BR#');
    					var contentTmp;
    					while (true){
    						contentTmp = content.replace(/([\s|\S]+)\n([\s|\S]+)/gi, (s, s1, s2) => {
    							if (s1.match(/>\s*$/) || s2.match(/^\s*</)) return s;
    							return s1 + '#BX_BR#' + s2;
    						});
    						if (contentTmp == content){
    							break;
    						}else{
    							content = contentTmp;
    						}
    					}
    					content = content.replace(/#BX_BR#/ig, "<br>\n");
    					return content;
    				},
    				ReplaceNewLinesBack: (content) =>{
    					content = content.replace(/<[^<>]*br>\n/ig, '#BX_BR#');
    					var contentTmp;
    					while (true){
    						contentTmp = content.replace(/([\s|\S]+)#BX_BR#([\s|\S]+)/gi, (s, s1, s2) => {
    							if (s1.match(/>\s*$/) || s2.match(/^\s*</)) return s;
    							return s1 + '\n' + s2;
    						});
    						if (contentTmp == content){
    							break;
    						}else{
    							content = contentTmp;
    						}
    					}
    					content = content.replace(/#BX_BR#/ig, "<br>\n");
    					return content;
    				}
    			};
    			window.BXHtmlEditor = BXHtmlEditor;
    			window.onbeforeunload = BXHtmlEditor.OnBeforeUnload;
    		}
    		BX.onCustomEvent(window, "OnBXHtmlEditorInit");
    		top.BXHtmlEditorAjaxResponse = {};
    	})(window);
    </script>
    <?}?>


    Редактор:
    <?if (Loader::IncludeModule("fileman")){?>
    <div class="bx-html-editor" id="bx-html-editor-<?=$NAME?>" style="width:100%; height:250px;" data-name="<?=$NAME?>">
    <div class="bxhtmled-toolbar-cnt" id="bx-html-editor-tlbr-cnt-<?=$NAME?>">
    <div class="bxhtmled-toolbar" id="bx-html-editor-tlbr-<?=$NAME?>"></div>
    </div>
    <div class="bxhtmled-search-cnt" id="bx-html-editor-search-cnt-<?=$NAME?>" style="display: none;"></div>
    <div class="bxhtmled-area-cnt" id="bx-html-editor-area-cnt-<?=$NAME?>">
    <div class="bxhtmled-iframe-cnt" id="bx-html-editor-iframe-cnt-<?=$NAME?>"></div>
    <div class="bxhtmled-textarea-cnt" id="bx-html-editor-ta-cnt-<?=$NAME?>"></div>
    <div class="bxhtmled-resizer-overlay" id="bx-html-editor-res-over-<?=$NAME?>"></div>
    <div id="bx-html-editor-split-resizer-<?=$NAME?>"></div>
    </div>
    <div class="bxhtmled-nav-cnt" id="bx-html-editor-nav-cnt-<?=$NAME?>" style="display: none;"></div>
    <div class="bxhtmled-taskbar-cnt bxhtmled-taskbar-hidden" id="bx-html-editor-tskbr-cnt-<?=$NAME?>">
    <div class="bxhtmled-taskbar-top-cnt" id="bx-html-editor-tskbr-top-<?=$NAME?>"></div>
    <div class="bxhtmled-taskbar-resizer" id="bx-html-editor-tskbr-res-<?=$NAME?>">
    <div class="bxhtmled-right-side-split-border">
    <div data-bx-tsk-split-but="Y" class="bxhtmled-right-side-split-btn"></div>
    </div>
    </div>
    <div class="bxhtmled-taskbar-search-nothing" id="bxhed-tskbr-search-nothing-<?=$NAME?>">HTMLED_SEARCH_NOTHING</div>
    <div class="bxhtmled-taskbar-search-cont" id="bxhed-tskbr-search-cnt-<?=$NAME?>" data-bx-type="taskbar_search">
    <div class="bxhtmled-search-alignment" id="bxhed-tskbr-search-ali-<?=$NAME?>">
    <input type="text" class="bxhtmled-search-inp" id="bxhed-tskbr-search-inp-<?=$NAME?>" placeholder="HTMLED_SEARCH_PLACEHOLDER"/>
    </div>
    <div class="bxhtmled-search-cancel" data-bx-type="taskbar_search_cancel" title="HTMLED_SEARCH_CANCEL"></div>
    </div>
    </div>
    <div id="bx-html-editor-file-dialogs-<?=$NAME?>" style="display: none;"></div>
    </div>
    <?}else{?>
    	<textarea rows="5" name="<?=$NAME?>"></textarea>
    <?}?>

    Скрипт редактора:
    const editor = document.querySelectorAll('.bx-html-editor');
    if(editor.length){
    	for (let i = 0; i < editor.length; i++){
    		window.BXHtmlEditor.Show({
    			id: editor[i].getAttribute('data-name'),
    			inputName:editor[i].getAttribute('data-name'),
    			content:'',
    			useFileDialogs:false,
    			width:"100%",
    			height:"200",
    			allowPhp:false,
    			limitPhpAccess:false,
    			templates:[],
    			templateId:'',
    			templateParams:[],
    			componentFilter:'',
    			snippets:[],
    			placeholder:"Text here...",
    			actionUrl:"/bitrix/admin/fileman_html_editor_action.php",
    			cssIframePath:configBXHtmlEditor.cssIframePath,
    			bodyClass:'',
    			bodyId:'',
    			designTokens:configBXHtmlEditor.designTokens,
    			spellcheck_path:configBXHtmlEditor.spellcheck_path,
    			usePspell:'N',
    			useCustomSpell:'Y',
    			bbCode:false,
    			askBeforeUnloadPage:true,
    			settingsKey:'user_settings__ChangeView',
    			showComponents:false,
    			showSnippets:false,
    			view:'wysiwyg',
    			splitVertical:false,
    			splitRatio:1,
    			taskbarShown:false,
    			taskbarWidth:250,
    			lastSpecialchars:false,
    			cleanEmptySpans:true,
    			lazyLoad:false,
    			showTaskbars:false,
    			showNodeNavi:false,
    			controlsMap:[
    				{"id":"ChangeView","wrap":true,"compact":true,"sort":10},
    				{"separator":true,"compact":false,"sort":11},
    				{"id":"Undo","compact":false,"sort":12},
    				{"id":"Redo","compact":false,"sort":13},
    				{"separator":true,"compact":false,"sort":14},
    				{"id":"Bold","compact":true,"sort":15},
    				{"id":"Italic","compact":true,"sort":16},
    				{"id":"Underline","compact":true,"sort":17},
    				{"id":"Strikeout","compact":true,"sort":18},
    				{"id":"RemoveFormat","compact":true,"sort":19},
    				{"id":"Color","compact":true,"sort":20},
    				{"id":"StyleSelector","compact":false,"sort":21},
    				{"id":"FontSelector","compact":false,"sort":22},
    				{"id":"FontSize","compact":false,"sort":23},
    				{"separator":true,"compact":false,"sort":24},
    				{"id":"OrderedList","compact":true,"sort":25},
    				{"id":"UnorderedList","compact":true,"sort":26},
    				{"id":"AlignList","compact":false,"sort":27},
    				{"id":"InsertHr","compact":false,"hidden":true,"sort":29},
    				{"id":"Sub","compact":false,"hidden":true,"sort":30},
    				{"id":"Sup","compact":false,"hidden":true,"sort":31},
    				{"id":"IndentButton","compact":true,"hidden":true,"sort":32},
    				{"id":"OutdentButton","compact":true,"hidden":true,"sort":33},
    				{"separator":true,"compact":false,"hidden":true,"sort":34},
    				{"id":"InsertChar","compact":false,"hidden":true,"sort":35},
    				{"id":"PrintBreak","compact":false,"hidden":true,"sort":36},
    				{"id":"PageBreak","compact":false,"hidden":true,"sort":37},
    				{"id":"Spellcheck","compact":false,"hidden":true,"sort":38},
    				{"separator":true,"compact":false,"hidden":true,"sort":39},
    				{"id":"InsertLink","compact":true,"hidden":true,"sort":40},
    				{"id":"InsertImage","compact":false,"hidden":true,"sort":41},
    				{"id":"InsertVideo","compact":true,"hidden":true,"sort":42},
    				{"id":"InsertTable","compact":false,"hidden":true,"sort":43},
    				{"separator":true,"compact":false,"sort":44},
    				{"id":"Fullscreen","compact":false,"sort":45},
    				{"id":"Viewmode","compact":true,"sort":46},
    				{"id":"More","compact":true,"sort":47}
    			],
    			autoResize:true,
    			autoResizeOffset:40,
    			minBodyWidth:350,
    			normalBodyWidth:555,
    			components:false,
    			isCopilotEnabled:false,
    			isCopilotImageEnabledBySettings:true,
    			isCopilotTextEnabledBySettings:true,
    			copilotParams:null,
    			isMentionUnavailable:false,
    			fontSize:'14px',
    			pasteSetColors:true,
    			pasteSetBorders:true,
    			pasteSetDecor:true,
    			pasteClearTableDimen:true,
    			linkDialogType:"internal"
    		});
    	}
    }
    Ответ написан
    Комментировать
  • PHP + 1C интеграция. Что почитать?

    mrspender
    @mrspender
    Мое мнение — они все сделали так, чтобы в этом либо никто не разобрался, либо единицы, потратив кучу времени на гадание на кофейной гуще.

    Все-таки продажи 1С-Битрикс куда важнее, чем дела программистов-конкурентов.
    Ответ написан
    Комментировать
  • Как в Битриксе отключить авторизацию для внешних запросов на роуты (routing_index.php)?

    @titkov96
    в файле \bitrix\access.php нужно дать права на файл \bitrix\routing_index.php
    Ответ написан
    Комментировать
  • Как правильно работать с сторонним api?

    @Flying
    Конечно контроллер - не место для подобных действий.

    Как правильно указал Владимир Коротенко - перенос кода, отвечающего за работу с внешним API в отдельный сервис - это первое что вам нужно сделать. Это действие открывает для вас массу возможностей: подмена сервиса для разных сценариев, использование заглушек для тестов, вызовы вне контроллеров и т.д. Кроме того сам код при этом становится более простым и поддерживаемым, а также лучше соответствует принципам SOLID.

    Если после выделения сервиса вы поймёте что для вас необходима возможность изменения его реализации - важно выделить ключевые методы в интерфейс и использовать именно интерфейс для внедрения зависимостей в те же контроллеры.

    Для реализации самих запросов внутри сервиса логично использовать Symfony HTTP Client.

    После этого у вас возможны различные сценарии в зависимости от того что же этот API делает.

    Если вы забираете из API данные для подготовки своего ответа и эти данные меняются часто и должны быть всегда актуальны (к примеру какое-нибудь значение котировки акции или курса валюты) - вы вызываете ваш сервис напрямую, а он делает запрос и возвращает ответ.

    Если вы забираете из API данные для подготовки своего ответа, но сами эти данные меняются не постоянно - логично будет добавить в ваш сервис поддержку кэширования с использованием Symfony Cache.

    Если вы забираете данные из API или отправляете данные туда, но они не зависят от контекста текущего запроса - этот процесс логичнее всего вынести в консольную команду, реализованную через Symfony Console и дёргать её по cron'у.

    Альтернативно, если вы используете Symfony 4.4 или 5.x - вы можете организовать этот процесс через новый Symfony Messenger. Он же будет лучшим вариантом если, к примеру, запросов много или они тяжёлые и вам нужно организовать их распределение.
    Ответ написан
    Комментировать
  • Не отправляются файлы на сервер через BX.ajax.runComponentAction, в чем может быть проблема?

    Starina_js
    @Starina_js
    full-stack web dev
    Эту ветку смотрели?

    BX.ajax.runComponentAction('componentName', "actionName", {
        mode: 'ajax',
        data: new FormData(form)
    })


    use \Bitrix\Main\Application;
    $request = Application::getInstance()->getContext()->getRequest();
    $files = $request->getFileList()->toArray();
    Ответ написан
    4 комментария
  • PHP - как оставить только 2 знака(не округлить) после запятой?

    На сегодня единственный разумный вариант:

    bcdiv('12.345', 1, 2); // 12.34
    Ответ написан
    3 комментария
  • Как в Битриксе отключить авторизацию для внешних запросов на роуты (routing_index.php)?

    @PetrPo
    prolog_before.php не требует авторизаию.

    Авторизацию требует controller (так по умолчанию сделано), в нем должен быть метод configureActions, в котором отменяешь необходимость авторизации

    public function configureActions()
    {
      return [
        'addBook' => [
          'prefilters' => [
    
          ],
          '-prefilters' => [
            \Bitrix\Main\Engine\ActionFilter\Authentication::class
          ],
        ]
      ];
    }


    P.S.
    addBook = ...function addBookAction()
    Ответ написан
  • Как импортировать в Битрикс24 пользователей и структуру из 1С?

    gromdron
    @gromdron Куратор тега Битрикс24
    Работаю с Bitrix24
    Да, в Битрикс24 есть возможность импорта орг.структуры и пользователей из 1С, есть даже связывание этих пользователей по логину с конкретным заведенным доменом, однако на практике штатные инструменты для группы компаний не подходят. Подобные работы выполняются под ключ для каждой компании партнерами-интеграторами.
    От себя могу добавить следующее:
    1. Если хотите чтобы проблем не было, то поставщиком пользователей (профилей) и орг.структуры должна являться 1С. И это должна быть именно одна система, потому как несколько подобных систем-источников вызывают коллапс в Б24.
    2. Если хотите использовать несколько доменов AD, то оставьте только минимальный набор полей - флаг активности, логин. AD должен выступать исключительно в роли авторизационного сервера, поэтому никакого обмена с периодичностью, построения орг.структуры и создания пользователей на портале. Только активность и привязка к группам.
    3. Для привязки заранее создайте как минимум 3 группы в каждом AD: "bitrix users", куда включите всех пользователей которые должны маппится в группу сотрудников, "bitrix admins" это те пользователи из числа "bitrix users", кто должны стать админами на портале и "bitrix ignored" (учетки которые будут игнорироваться).
    - Если пользователь должен стать админом - в группы "bitrix users" и "bitrix admins" его.
    - Если учетку не нужно затягивать в Б24 то прописываем в "bitrix ignored" и соответственно ее в исключения. Помните если пользователь будет одновременно в группе ignored и users, то на портал он не попадет.

    По-поводу SSO на мультидоменные структуры (именно чтобы открыть портал и сразу авторизован) это не просто.
    Либо нужно сломать BitrixVM подключив Kerberos, либо нужно пилить свой модуль для какого-нибудь KeyCloak/ADFS или другого поставщика авторизации. Штатный NTLMv2 работает только с одним доменом (тем в который введен сервер).
    Ответ написан
    Комментировать
  • Как правильно именовать таблицы, столбцы в базе данных?

    JohnnyGat
    @JohnnyGat
    Стараюсь писать код, понятный человеку.
    SQL Style Guide - www.sqlstyle.guide
    Перевод на русский - www.sqlstyle.guide/ru
    Ответ написан
    6 комментариев
  • Как получить ID формы в которой находится кнопка?

    FLUNKEY
    @FLUNKEY
    самовар
    Я думаю прежде чем задавать такие вопросы, лучше прочитать ту же документацию по jQuery или на край загуглить.

    $('form button').click(function(){
        var $form = $(this).parents('form');
        var id = $form.attr('id');
    });
    Ответ написан
    Комментировать
  • Почему Service Locator это зло и что использовать вместо?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Все эти страшные слова - они на самом деле всегда про одно и то же - про связность. Когда ты хардкодишь внутри класса вызов какого-то конкретного сервиса - ты намертво к нему привязываешься. И чтобы поменять сервис на другой, ты будешь вынужден поменять код класса. Окей, поменял. И тут же в другом месте, где этот же класс использовался, что-то сломалось! И что теперь? Делать два класса, которые различаются одной строчкой? Нет конечно. А как тогда использовать один и тот же класс для обработки разных входящих данных (или одних и тех же данных, но разными способами)? Сделать его поведение изменяемым. То есть сделать изменяемыми те инструменты, которыми он пользуется - т.е. его зависимости.

    Поэтому все зависимости обычно передаются через конструктор (и поэтому и называются инъекция зависимостей.)

    Таким образом мы можем менять поведение класса, не меняя его код

    Но тут надо понимать, что всё это работает только при правильном применении ООП. А точнее просто при применении ООП. Потому что 98% "ООП" кода, который пишется на РНР - это голимая процедурщина, даже если она обёрнута в классы и методы. Если у тебя метод класса представляет из себя стену кода, которую ты тупо перенёс из файла, инклюдившегося в любимое похапешное спагетти - то это не ООП. Это та же процедурщина, вид сбоку. И смысл использования dependency injection ты с ним не почуствуешь. Будешь конечно применять, но в качестве карго культа - потому что тебе это на тостере написали.
    А вот когда твой код начнет становиться действительно объектным - тогда стразу станет понятнее.


    Похожим на сервис локатор является сервис- или DI-контейнер. Используемый вручную, он является тем же самым сервис локатором. Поэтому вручную его никогда не надо вызывать - что и запрещается в симфоневских конроллерах - а только для автоматического создания классов. В МВЦ у тебя ведь очень многие объекты создаются автоматом - сущности, контроллеры. И вот для того, чтобы при автоматическом создании экземпляра класса у тебя были на руках все требуемые сервисы - и нужен контейнер.

    Соотвтственно, ответ на вопрос "что использовать?" очень простой:
    - при ручном создании экземпляра объекта, все зависимости передавать в него через конструктор, а не получать "из воздуха" в коде.
    - при автоматическом создании экземпляра объекта, использовать dependency injection container

    В этим смысле очень полезно освоить Симфони - строгий фрейворк, в котором нет сервис локатора и в котором запрещено пользоваться контейнером напрямую.
    Ответ написан
    4 комментария
  • Кодировка базы данных для русскоязычного сайта Laravel?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Господи, ну нельзя же настолько буквально воспринимать вопросы.
    Ну неужели из контекста непонятно, что автор спрашивает про кодировку, а не про набор правил сравнения?

    Автор, кодировка, которая тебе нужна, называется utf8mb4. Вот её и ставь.
    А набор правил сравнения подставится автоматически, и в целом без разницы, какой - utf8mb4_general_ci или utf8mb4_unicode_ci
    Ответ написан
    Комментировать
  • Где находится в коде этот счётчик (коробочная версия)?

    gromdron
    @gromdron Куратор тега Битрикс24
    Работаю с Bitrix24
    Перед началом: да, можно вывести счетчик, но если вы хотите сделать обновление данных через push-сервер нужно немного модифицировать шаблон меню.
    И под счетчиками, я понимаю стандартный механизм использующий API: CUserCounter (таблица b_user_counter)

    Чтобы вывести цифру нужно сделать следующее: при описании меню (.left.menu.php) в 3-й параметр пункта нужно добавить параметр counter_num.
    Array(
    	'Test menu',
    	"/test", 
    	Array(), 
    	Array(
    		'counter_num' => 7,
    	), 
    	"" 
    ),


    В данном случае 7 - цифра, которая будет отображаться около номера.
    Сам же счетчик можно наполнять данными откуда угодно.

    Однако если вы хотите добавить обновляемый через push-сервер счетчик то нужно добавить еще один параметр counter_id (это идентификатор стандартного счетчика битрикса):
    Array(
    	'Test menu',
    	"/test", 
    	Array(), 
    	Array(
    		"counter_id" => "test-counter-menu",
    		'counter_num' => 7,
    	), 
    	"" 
    ),


    Далее, в шаблоне компонента меню (/bitrix/templates/bitrix24/components/bitrix/menu/top_horizontal/template.php) нужно перед кодом:
    $items[] = $newItem;

    добавить:
    if ( isset($item["PARAMS"]["counter_id"]) )
    {
    	$newItem['COUNTER_ID'] = $item["PARAMS"]["counter_id"];
    }


    Теперь когда вы будете обновлять счетчик, он будет автоматически обновляться у пользователей.
    Ответ написан
    1 комментарий
  • Какой выбрать локальный сервер?

    kentuck1213
    @kentuck1213
    Не надо на подобие денвера.
    open-server.ru
    Ответ написан
    Комментировать
  • Какой выбрать локальный сервер?

    Enapiuz
    @Enapiuz
    велосипедостроительное бюро
    На винде сам пользуюсь и всегда всем советую OpenServer. Наверное самая безгеморройная вещь. Главное запускать с правами админа, чтобы hosts правился автоматически.
    Ответ написан
    Комментировать
  • Какой выбрать локальный сервер?

    muhammad_97
    @muhammad_97
    PHP-разработчик
    OpenServer или XAMPP
    Ответ написан
    Комментировать
  • Как добавить запись в историю заказа?

    @ArmBar
    \Bitrix\Main\Loader::includeModule('sale');
    $historyEntityType = 'ORDER'; //В данном случае для заказа
    $historyType = 'ORDER_COMMENTED'; //Нужный тип можно посмотеть в классе \CSaleOrderChangeFormat в $operationTypes
    $order = \Bitrix\Sale\Order::load($orderId);
    \Bitrix\Sale\OrderHistory::addAction(
    			$historyEntityType,
    			$order->getId(),
    			$historyType,
    			$order->getId(),
    			$order,
    			['COMMENTS' => 'Новый комментарий']
    );
    $order->save();
    Ответ написан
    Комментировать
  • Как проверять на существование лидов с таким же Email. Битрикс24. Коробочная версия?

    @elov4anin Автор вопроса
    Frontend developer
    Добавлю так же решение которые по советовали в ТП Битрикса и которе применил в итоге

    CModule::IncludeModule('crm');
    $rs = CCrmFieldMulti::GetList(
       array(),
       array(
          'ENTITY_ID' => 'LEAD', // выбираем только email лида
          "VALUE" => "test@mail.ru",
          //"@ELEMENT_ID" => array(1,2), // ID выбираемых лидов
          //"TYPE_ID" => "EMAIL",
          'COMPLEX_ID' => 'EMAIL_WORK' // тип email: "Рабочий"
       )
    );
    while($ar = $rs->Fetch()) 
    {
       echo '<pre>';
       print_r($ar);
       echo '</pre>';
    }
    Ответ написан
    Комментировать
  • Как сделать ожидание выполнения функции в JavaScript?

    @vshvydky
    Если функция с колбеком надо обернуть ее в промис, чтобы работать с промисами синхронно, нужно либо использовать рекурсивный вызов промисов один за другим, либо в цикле фор но в асинк функции с прерываниями на авайт
    Ответ написан
    Комментировать
  • Человекопонятные даты в js на русском языке?

    DenZzeuro
    @DenZzeuro
    Мотоциклы, кофе
    new Date().toLocaleString('ru', {
    			  year: 'numeric',
    			  month: 'long',
    			  day: 'numeric'
    			});
    Ответ написан
    2 комментария