• Как исправить проблему с AJAX bitrix?

    @PetrPo
    breadcrumbs тут ни при чем конечно, если просто из корня каталога в раздел перейти такая дичь происходит. Ну и да действительно косяк есть.
    1. Для компонента каталога должен быть выставлен параметр AJAX_OPTION_STYLE = Y
    2. Ну и собственно косяк в самом ядре, на скрине у тебя первая ошибка указвает на функцию в core.js, которая меняет заголовок h1 после ajax. Если посмотришь, то в корне каталога нет заголовка, то есть тебе надо у страницы каталога установить заголовок (например, через панель в публичке)
    Ответ написан
    Комментировать
  • Как обработать ответ от платежной системы?

    @PetrPo
    Вопрос решен в комментариях
    Ответ написан
    Комментировать
  • Почему не работает ссылки на битрикс?

    @PetrPo
    Файл .htaccess был пустым. Здесь есть пример стандартного файла
    Ответ написан
    Комментировать
  • Как заменить привязку "Предложение" hl-block, на обычную картинку?

    @PetrPo
    Помог разобраться в комментариях.
    Ответ написан
    Комментировать
  • Как в sale.order.ajax запретить переход к следующему шагу если не выбран пункт выдачи в Почте России?

    @PetrPo
    Либо шаблон компонента кастомить и дописывать order_ajax.js, либо можно в своем js файле частично переопределить функции из order_ajax.js
    Первый вариант хорош тем, что там буквально пару функций дописать и добавить выводы в несколько мест, но кастомный шаблон.
    При втором варианте побольше кода и как бы оторванный от самого компонента, но большая совместимость при обновлении битрикса.

    По второму варианту, переопредлить можно так
    spoiler

    var saleOrderAjaxComponentCustom = function() {
    	// чтобы добавить валидацию для свернутого блока доставки
    	BX.Sale.OrderAjaxComponent.editFadeDeliveryBlock = function() {
    		var deliveryContent = this.deliveryBlockNode.querySelector('.bx-soa-section-content'), newContent;
    
    		if (this.initialized.delivery)
    		{
    			this.deliveryHiddenBlockNode.appendChild(deliveryContent);
    		}
    		else
    		{
    			this.editActiveDeliveryBlock(false);
    			BX.remove(BX.lastChild(this.deliveryBlockNode));
    		}
    
    		newContent = this.getNewContainer(true);
    		this.deliveryBlockNode.appendChild(newContent);
    
    		this.editFadeDeliveryContent(newContent);
    
    		if (this.params.SHOW_COUPONS_DELIVERY == 'Y')
    			this.editCouponsFade(newContent);
    		
    		// валидация доставки и вывод плашки с ошибкой
    		var deliveryErrors = this.validateDelivery();
    	}
    	
    	// чтобы добавить валидацию для активного блока доставки
    	BX.Sale.OrderAjaxComponent.editActiveDeliveryBlock = function(activeNodeMode) {
    		var node = activeNodeMode ? this.deliveryBlockNode : this.deliveryHiddenBlockNode,
    			deliveryContent, deliveryNode;
    
    		if (this.initialized.delivery)
    		{
    			BX.remove(BX.lastChild(node));
    			node.appendChild(BX.firstChild(this.deliveryHiddenBlockNode));
    		}
    		else
    		{
    			deliveryContent = node.querySelector('.bx-soa-section-content');
    			if (!deliveryContent)
    			{
    				deliveryContent = this.getNewContainer();
    				node.appendChild(deliveryContent);
    			}
    			else
    				BX.cleanNode(deliveryContent);
    
    			this.getErrorContainer(deliveryContent);
    
    			deliveryNode = BX.create('DIV', {props: {className: 'bx-soa-pp row'}});
    			this.editDeliveryItems(deliveryNode);
    			deliveryContent.appendChild(deliveryNode);
    			this.editDeliveryInfo(deliveryNode);
    
    			if (this.params.SHOW_COUPONS_DELIVERY == 'Y')
    				this.editCoupons(deliveryContent);
    
    			this.getBlockFooter(deliveryContent);
    		}
    		
    		// валидация доставки и вывод плашки с ошибкой
    		if(this.deliveryBlockNode.getAttribute('data-visited') === 'true') {
    			var deliveryErrors = this.validateDelivery();
    		}
    	}
    	
    	// чтобы добавить валидацию по нажатию кнопки "Оформить заказ"
    	BX.Sale.OrderAjaxComponent.isValidForm = function() {
    		if (!this.options.propertyValidation)
    			return true;
    
    		var regionErrors = this.isValidRegionBlock(),
    			propsErrors = this.isValidPropertiesBlock(),
    			navigated = false, tooltips, i;
    
    		if (regionErrors.length)
    		{
    			navigated = true;
    			this.animateScrollTo(this.regionBlockNode, 800, 50);
    		}
    
    		if (propsErrors.length && !navigated)
    		{
    			if (this.activeSectionId == this.propsBlockNode.id)
    			{
    				tooltips = this.propsBlockNode.querySelectorAll('div.tooltip');
    				for (i = 0; i < tooltips.length; i++)
    				{
    					if (tooltips[i].getAttribute('data-state') == 'opened')
    					{
    						this.animateScrollTo(BX.findParent(tooltips[i], {className: 'form-group bx-soa-customer-field'}), 800, 50);
    						break;
    					}
    				}
    			}
    			else
    				this.animateScrollTo(this.propsBlockNode, 800, 50);
    		}
    		
    		if (regionErrors.length)
    		{
    			this.showError(this.regionBlockNode, regionErrors);
    			BX.addClass(this.regionBlockNode, 'bx-step-error');
    		}
    
    		if (propsErrors.length)
    		{
    			if (this.activeSectionId !== this.propsBlockNode.id)
    				this.showError(this.propsBlockNode, propsErrors);
    
    			BX.addClass(this.propsBlockNode, 'bx-step-error');
    		}
    		
    		// валидация доставки и вывод плашки с ошибкой
    		var deliveryErrors = this.validateDelivery();
    		if(deliveryErrors) {
    			this.animateScrollTo(this.deliveryBlockNode, 800, 50);
    		}
    
    		return !(regionErrors.length + propsErrors.length + deliveryErrors.length);
    	}
    	
    	// валидация доставки и вывод плашки с ошибкой
    	BX.Sale.OrderAjaxComponent.validateDelivery = function() {
    		var deliveryErrors = this.isValidDeliveryBlock();
    		this.showDeliveryErrors(deliveryErrors);
    		
    		return deliveryErrors;
    	}
    	
    	// валидация доставки
    	BX.Sale.OrderAjaxComponent.isValidDeliveryBlock = function() {
    		var selectedDelivery = this.getSelectedDelivery(),
    			deliveryProps = this.orderBlockNode.querySelectorAll('[name=DELIVERY_ID]'),
    			deliveryErrors = [];
    		
    		// ПРИМЕР, ЗДЕСЬ ПИШЕШЬ СВОЮ ВАЛИДАЦИЮ
    		if(selectedDelivery.ID == 1) {
    			deliveryErrors.push('ТЕСТОВАЯ ОШИБКА');
    		}
    		// --------------------------------------
    
    		return deliveryErrors;
    	}
    	
    	// вывод плашки с ошибкой
    	BX.Sale.OrderAjaxComponent.showDeliveryErrors = function(deliveryErrors) {
    		if (deliveryErrors.length) {
    			this.showError(this.deliveryBlockNode, deliveryErrors);
    			BX.addClass(this.deliveryBlockNode, 'bx-step-error');
    		}
    	}
    }
    
    BX.ready(function(){
    	if(BX.Sale && BX.Sale.OrderAjaxComponent) {
    		// при загрузке страницы
    		saleOrderAjaxComponentCustom();
    		BX.Sale.OrderAjaxComponent.validateDelivery();
    		
    		// для ajax
    		BX.addCustomEvent(window, 'onAjaxSuccess', function(e) {
    			saleOrderAjaxComponentCustom();
    			BX.Sale.OrderAjaxComponent.validateDelivery();
    		});
    	}
    });


    Там коммент // ПРИМЕР, ЗДЕСЬ ПИШЕШЬ СВОЮ ВАЛИДАЦИЮ
    Саму валидацию придется тебе придумать по каким полям ты будешь валидировать. Как вариант, я посмотрел там инпуты добавляются, можно проверить, что если они есть и не заполнены, тогда выводить ошибку.
    Ответ написан
  • Как проверить нахождение юзера на определённом url с помощью php?

    @PetrPo
    Будто это все знают, кроме меня.

    Так и есть)

    CSite::InDir();
    $APPLICATION->GetCurDir();
    $APPLICATION->GetCurPage();

    Гугли, выбирай, что тебе больше подходит
    Ответ написан
  • Как добавить модальное окно на сайт bitrix?

    @PetrPo
    Вопрос решен, см. комментарии.
    Ответ написан
    Комментировать
  • Bitrix Триггерные рассыкилки как персонализировать по группам пользователя?

    @PetrPo
    Вариант, если нужно ограничить все триггерные рассылки по принадлежности юзера к группе пользователей
    $eventManager = \Bitrix\Main\EventManager::getInstance();
    $eventManager->addEventHandler('sender', 'OnBeforePostingSendRecipient', ['SenderEvents', 'OnBeforePostingSendRecipient']);
    
    class SenderEvents {
    	const USER_GROUP_DILLER_ID = 1;
    	
    	public static function OnBeforePostingSendRecipient($event) {
    		$parameters = $event->getParameters()[0];
    		$isUserDiller = self::userInGroup($parameters['FIELDS']['USER_ID'], self::USER_GROUP_DILLER_ID);
    		
    		if($isUserDiller) {
    			$parameters['FIELDS']['EMAIL_TO'] = '';
    		}
    		
    		return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS, $parameters);
    	}
    	
    	private static function userInGroup(int $userId = 0, $userGroupsId = []) {
    		if($userId && $userGroupsId) {
    			$userGroupsId = (array) $userGroupsId;
    			$userGroups = CUser::GetUserGroup($userId);
    			
    			return array_intersect($userGroups, $userGroupsId) ? true : false;
    		}
    		else {
    			return false;
    		}
    	}
    }


    Вариант, если нужно ограничить определенные триггеры, например "Забытая корзина"
    class SenderEvents {
    	const USER_GROUP_DILLER_ID = 1;
    	
    	public static function OnBeforePostingSendRecipient($event) {
    		$parameters = $event->getParameters()[0];
    		
    		$mailingChainId = $parameters['MAILING_CHAIN_ID'];
    		$isUserDiller = self::userInGroup($parameters['FIELDS']['USER_ID'], self::USER_GROUP_DILLER_ID);
    		
    		if($mailingChainId && $isUserDiller) {
    			$mailingTrigger = new \MailingTrigger($mailingChainId);
    			
    			if($mailingTrigger->isTriggerBasketForgotten()) {
    				$parameters['FIELDS']['EMAIL_TO'] = '';
    			}
    		}
    
    		return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS, $parameters);
    	}
    	
    	private static function userInGroup(int $userId = 0, $userGroupsId = []) {
    		if($userId && $userGroupsId) {
    			$userGroupsId = (array) $userGroupsId;
    			$userGroups = CUser::GetUserGroup($userId);
    			
    			return array_intersect($userGroups, $userGroupsId) ? true : false;
    		}
    		else {
    			return false;
    		}
    	}
    }
    
    class MailingTrigger {
    	private $trigger;
    	
    	public function __construct(int $mailingChainId) {
    		$this->setTrigger($mailingChainId);
    	}
    	
    	public function getTrigger(int $mailingChainId) {
    		return $this->trigger;
    	}
    	
    	private function setTrigger(int $mailingChainId) {
    		if(!isset($this->trigger)) {
    			$this->trigger = \Bitrix\Sender\MailingTriggerTable::getList([
    				'select' => ['ENDPOINT'],
    				'filter' => ['MAILING_CHAIN_ID' => $mailingChainId, 'IS_TYPE_START' => 1]
    			])->fetch();
    		}
    	}
    	
    	public function getCode() {
    		return $this->trigger['ENDPOINT']['CODE'];
    	}
    	
    	public function isTriggerBasketForgotten() {
    		$triggerBasketForgottenCode =  \Bitrix\Sale\Sender\TriggerBasketForgotten::getCode();
    		
    		return $this->getCode() == $triggerBasketForgottenCode ? true : false;
    	}
    }
    Ответ написан
  • Как сделать фильтр только на последнем уровне каталога товаров в 1с Битрикс?

    @PetrPo
    Надо обернуть вызов компонента фильтра в условие.
    if($section['RIGHT_MARGIN'] - $section['LEFT_MARGIN'] == 1)  {
        // компонент фильтра
    }

    По битриксовой логике такое условие всегда расскажет тебе о том, что подраздел последненго уровня вложенности
    Ответ написан
    Комментировать
  • Bitrix\Sale\DiscountCouponsManager почему не срабатывает?

    @PetrPo
    Метод \Bitrix\Sale\DiscountCouponsManager::get возвращает примененные купоны

    Используй \Bitrix\Sale\Internals\DiscountCouponTable::getList
    Как у тебя в примере, без фильтра, селекта и т.д.
    $arCoupon = \Bitrix\Sale\Internals\DiscountCouponTable::getList()->fetchAll();
    Ответ написан
    Комментировать
  • Как изменить порядок наименований сортировки?

    @PetrPo
    Видимо как-то так
    spoiler

    <?if($itemsCnt):?>
      <!-- noindex -->
      <div class="row filters-wrap">
        <?
        if($arResult['VARIABLES']['SECTION_ID']){
          $arSectiontmp = CIBlockSection::GetList(array(), array('IBLOCK_ID' => $arParams['IBLOCK_ID'], 'ID' => $arResult['VARIABLES']['SECTION_ID']), false, array('ID',  'UF_VIEWTYPE'))->GetNext();
        }
        elseif($arResult['VARIABLES']['SECTION_CODE']){		 
           $arSectiontmp = CIBlockSection::GetList(array(), array('IBLOCK_ID' => $arParams['IBLOCK_ID'], 'CODE' => $arResult['VARIABLES']['SECTION_CODE']), false, array('ID', 'UF_VIEWTYPE'))->GetNext();
        }
          
        if($_SESSION['UF_VIEWTYPE_'.$arParams['IBLOCK_ID']] === NULL){
          $arUserFieldViewType = CUserTypeEntity::GetList(array(), array('ENTITY_ID' => 'IBLOCK_'.$arParams['IBLOCK_ID'].'_SECTION', 'FIELD_NAME' => 'UF_VIEWTYPE'))->Fetch();
          $resUserFieldViewTypeEnum = CUserFieldEnum::GetList(array(), array('USER_FIELD_ID' => $arUserFieldViewType['ID']));
          while($arUserFieldViewTypeEnum = $resUserFieldViewTypeEnum->GetNext()){
            $_SESSION['UF_VIEWTYPE_'.$arParams['IBLOCK_ID']][$arUserFieldViewTypeEnum['ID']] = $arUserFieldViewTypeEnum['XML_ID'];
          }
        }
        
        $sort_default = $arParams['SORT_PROP_DEFAULT'] ? $arParams['SORT_PROP_DEFAULT'] : 'name';
        $order_default = $arParams['SORT_DIRECTION'] ? $arParams['SORT_DIRECTION'] : 'asc';
        $arPropertySortDefault = array('name', 'sort');
        
    	$arAvailableSort = [];
    	
        if($arParams['SORT_PROP']){
          if(!isset($_SESSION[$arParams['IBLOCK_ID'].md5(serialize((array)$arParams['SORT_PROP']))])){
            foreach($arParams['SORT_PROP'] as $prop){
              if(!isset($arAvailableSort[$prop])){
                $dbRes = CIBlockProperty::GetList(array(), array('ACTIVE' => 'Y', 'IBLOCK_ID' => $arParams['IBLOCK_ID'], 'CODE' => $prop));
                while($arPropperty = $dbRes->Fetch()){
                  $arAvailableSort[$prop] = array(
                    'SORT' => 'PROPERTY_'.$prop,
                    'ORDER_VALUES' => array(),
                  );
    
                  if($prop == 'PRICE' || $prop == 'FILTER_PRICE'){
                    $arAvailableSort[$prop]['ORDER_VALUES']['asc'] = GetMessage('sort_title').GetMessage('sort_PRICE_asc');
                    $arAvailableSort[$prop]['ORDER_VALUES']['desc'] = GetMessage('sort_title').GetMessage('sort_PRICE_desc');
                  }
                  else{
                    $arAvailableSort[$prop]['ORDER_VALUES'][$order_default] = GetMessage('sort_title_property', array('#CODE#' => $arPropperty['NAME']));
                  }
                }
              }
            }
            $_SESSION[$arParams['IBLOCK_ID'].md5(serialize((array)$arParams['SORT_PROP']))] = $arAvailableSort;
          }
          else{
            $arAvailableSort = $_SESSION[$arParams['IBLOCK_ID'].md5(serialize((array)$arParams['SORT_PROP']))];
          }
        }
    	
        $arAvailableSort['name'] = array(
            'SORT' => 'NAME',
            'ORDER_VALUES' => array(
              'asc' => GetMessage('sort_title').GetMessage('sort_name_asc'),
              'desc' => GetMessage('sort_title').GetMessage('sort_name_desc'),
            ),
          );
    	  
        $arAvailableSort['sort'] = array(
            'SORT' => 'SORT',
            'ORDER_VALUES' => array(
              $order_default => GetMessage('sort_title').GetMessage('sort_sort'),
            )
        );
        
        foreach($arAvailableSort as $prop => $arProp){
          if(!in_array($prop, $arParams['SORT_PROP']) && $sort_default !== $prop){
            unset($arAvailableSort[$prop]);
          }
        }
    
    
        if(array_key_exists('display', $_REQUEST) && !empty($_REQUEST['display'])){
          setcookie('catalogViewMode', $_REQUEST['display'], 0, SITE_DIR);
          $_COOKIE['catalogViewMode'] = $_REQUEST['display'];
        }
        if(array_key_exists('sort', $_REQUEST) && !empty($_REQUEST['sort'])){
          setcookie('catalogSort', $_REQUEST['sort'], 0, SITE_DIR);
          $_COOKIE['catalogSort'] = $_REQUEST['sort'];
        }
        if(array_key_exists('order', $_REQUEST) && !empty($_REQUEST['order'])){
          setcookie('catalogOrder', $_REQUEST['order'], 0, SITE_DIR);
          $_COOKIE['catalogOrder'] = $_REQUEST['order'];
        }
        if(array_key_exists('show', $_REQUEST) && !empty($_REQUEST['show'])){
          setcookie('catalogPageElementCount', $_REQUEST['show'], 0, SITE_DIR);
          $_COOKIE['catalogPageElementCount'] = $_REQUEST['show'];
        }
    
        if($arSectiontmp['UF_VIEWTYPE'] && isset($_SESSION['UF_VIEWTYPE_'.$arParams['IBLOCK_ID']][$arSectiontmp['UF_VIEWTYPE']])){
          $display = $_SESSION['UF_VIEWTYPE_'.$arParams['IBLOCK_ID']][$arSectiontmp['UF_VIEWTYPE']];
        }
        else{
          $display = !empty($_COOKIE['catalogViewMode']) ? $_COOKIE['catalogViewMode'] : $arParams['VIEW_TYPE'];
        }
        $show = !empty($_COOKIE['catalogPageElementCount']) ? $_COOKIE['catalogPageElementCount'] : $arParams['PAGE_ELEMENT_COUNT'];
        $sort = !empty($_COOKIE['catalogSort']) ? $_COOKIE['catalogSort'] : $sort_default;
        $order = !empty($_COOKIE['catalogOrder']) ? $_COOKIE['catalogOrder'] : $order_default;
        ?>
        <div class="col-md-3 col-sm-5 ordering-wrap">
          <div class="select-outer">
            <i class="fa fa-angle-down"></i>
            <select class="sort">
            <?foreach($arAvailableSort as $newSort => $arSort):?>
              <?if(is_array($arSort['ORDER_VALUES'])):?>
                <?foreach($arSort['ORDER_VALUES'] as $newOrder => $sortTitle):?>
                  <?$selected = ($sort == $newSort && $order == $newOrder);?>
                  <option <?=($selected ? "selected='selected'" : "")?>  value="<?=$APPLICATION->GetCurPageParam('sort='.$newSort.'&order='.$newOrder, array('sort', 'order'))?>" class="ordering"><span><?=$sortTitle?></span></option>
                <?endforeach;?>
              <?endif;?>
            <?endforeach;?>
            </select>
          </div>
        </div>
        <?if(!$arSectiontmp['UF_VIEWTYPE']):?>
          <div class="col-md-5 col-sm-6 display-type pull-right text-right">
            <span class="label_show"><?=GetMessage('T_SORT');?>:</span>
            <a rel="nofollow" href="<?=$APPLICATION->GetCurPageParam('display=price', array('display'))?>" class="view-button view-price <?=$display == 'price' ? 'cur' : '';?>" alt="<?=GetMessage('T_PRICE_VIEW');?>" title="<?=GetMessage('T_PRICE_VIEW');?>">
              &nbsp;
            </a>
            <a rel="nofollow" href="<?=$APPLICATION->GetCurPageParam('display=table', array('display'))?>" class="view-button view-tiles <?=$display == 'table' ? 'cur' : '';?>" alt="<?=GetMessage('T_LIST_VIEW');?>" title="<?=GetMessage('T_LIST_VIEW');?>">
              &nbsp;
            </a>
            <a rel="nofollow" href="<?=$APPLICATION->GetCurPageParam('display=list', array('display'))?>" class="view-button view-list <?=$display == 'list' ? 'cur' : '';?>" alt="<?=GetMessage('T_TABLE_VIEW');?>" title="<?=GetMessage('T_TABLE_VIEW');?>">
              &nbsp;
            </a>
            
          </div>
        <?endif;?>
      </div>
      <!-- /noindex -->
    <?endif;?>

    Ответ написан
    Комментировать
  • Пропадают разделы в хлебных крошках в каталоге Bitrix-1C, что проверить?

    @PetrPo
    Потому что в компоненте catalog.section внутри комплексного компонента catalog (файл section.php) параметр ADD_SECTIONS_CHAIN = N
    Ответ написан
    2 комментария
  • Как правильно подключить свой класс через конструкцию use?

    @PetrPo
    // здесь я думаю не правильно указано имя класса ORM с которым работает DayorderHelper в методе GetElement

    Правильно думаешь))
    5f6c89996a4d8519558914.jpeg
    Ответ написан
    Комментировать
  • Почему не работает $basket->getExistsItem('catalog', $productId) в bitrix?

    @PetrPo
    Вот здесь я подробно объяснил, что и как в этом методе (комменты тоже прочитай) ссылка
    Ответ написан
    Комментировать
  • Как в битрикс настроить фильтр для вывода последних 5 новостей?

    @PetrPo
    Открыть документацию по этому компоненту и прочитать, там все написано
    Ответ написан
    Комментировать
  • Как настроить пользовательское свойство Яндекс.Карты?

    @PetrPo
    DISPLAY_VALUE = компонент map.yandex.system с дефолтным шаблоном, там и ставится zoom, по умолчанию = 10

    component.php
    5f68625f8ad5e354492893.jpeg

    tempate.php
    5f68626bdd90b399986257.jpeg

    Варианты как изменить:
    1. Использовать компонент карты, передавать туда широту и долготу со свойства и устанавливать параметтр INIT_MAP_SCALE с нужным масштабом
    2. Закинуть в свой шаблон сайта шаблон компонента map.yandex.system с именем .default и в template.php поменять zoom на нужный
    Ответ написан
    Комментировать
  • Где редактировать тест ошибок в sale.order.ajax?

    @PetrPo
    Смотря как отредактировать надо
    1. "Поле" находится в lang файле
    $MESS["SOA_FIELD"] = "Поле";

    2. "должно быть заполнено" (у меня "обязательно для заполнения") тоже в lang файле
    $MESS["SOA_REQUIRED"] = "обязательно для заполнения";

    3. "Адрес доставки" - это название свойства и админки

    В файле шаблона order_ajax.js есть такого типа функции, в которых формируется текст ошибки
    validateString: function(input, arProperty, fieldName)
    {
    //..............
    errors.push(field + ' ' + BX.message('SOA_REQUIRED'));
    //....................
    Ответ написан
  • Как побороть ошибку Call to a member function GetProperties() on boolean (0)?

    @PetrPo
    В строке
    $articul= CIBlockElement::GetByID($mxResult['ID'])->GetNextElement()->GetProperties();

    ошибка, так как метод GetNextElement возвращает false, надо добавить проверку
    $articul = [];
    
    $dbRes = CIBlockElement::GetByID($mxResult['ID']);
    if($ob = $dbRes->GetNextElement()) {
      $articul = $ob->GetProperties();
    }
    Ответ написан
    1 комментарий
  • Как в выборе местоположения Битрикс изменить плейсхолдер?

    @PetrPo
    Это отдельный компонент sale.location.selector.search
    "Введите название" в языковых файлах находится, а три точки прямо в template.php
    Ответ написан
    1 комментарий