Пройдемся по параметрам: my_answer - строка - уникальный id DOM-элемента, в который будет обернуто окно. Ежли на странице будет 2 и больше окон такого плана, то у каждого должен быть свой id.
null - параметр «принадлежности окна» к какому-либо DOM-элементу на странице. Т.е. по дефолту окно всплывает в центре страницы. Хотите чтоб всплывало, на пример, рядом с какой-либо кнопкой, ставьте суда id элемента в виде $(‘#my_name_div’) или через битриксовый эквивалент BX (‘my_name_div’)
content - строка - здесь контент, который будет показан в окне. Можно прямо сюда указывать html или указать #div, в котором лежит нужный текст.
closeIcon: {right: "20px", top: "10px" } - показывает нужна ли вам иконка закрытия окна и ее положение в окне.
titleBar: {content: BX .create("span", {html: ‘Это заголовок окна’, 'props': {'className': 'access-title-bar'}})} - собссно это создание заголовка окна. BX .create - функция создания DOM-элемента. В данном случае создание <span class=’access-title-bar’>Это заголовок окна</span>. Если хотите чтоб ваше окно можно было перетаскивать по странице, то класс лучше не менять.
zIndex: 0, offsetLeft: 0, offsetTop: 0, draggable: {restrict: false} - эти параметры указывают, что окно можно передвигать по странице.
buttons - кнопки для управления содержимым окна. На каждую кнопку создаем новый объект new BX .PopupWindowButton с параметрами: text - ‘название кнопки’ className - класс на кнопке, из стандартных возможны варианты: popup-window-button-accept - зеленая кнопка как на скрине, popup-window-button-cancel - белая кнопка, popup-window-button-link-cancel - просто ссылка. Само-собой можно приклеить свой стиль.
events: {click: function(){ this.popupWindow .close(); }} Это параметр, отвечающий за действие, произошедшее при нажатии кнопки. В данном случае this.popupWindow .close(); - говорит о закрытии окна. Но тут возможны различные варианты в зависимости от фантазии и задач. На пример, если в окне форма, можно сделать ajax-отправку контента через
Здесь BX ("myForm" - id-формы, data - данные, BX ( 'ajax-add-answer').innerHTML = data; - вставка ответа сервера в окно.
Либо можно сделать что-то свое в зависимости от нужд. Эта библиотека отлично работает в сочетании с jQuery, поэтому тут можно свободно использовать ее функции.
Далее приведу пример открытия окна по ссылке, загрузку в него ajax-конетнта и отправку формы.
Не так давно в свет вышел довольно интересный и красивый загрузчик файлов, который сейчас во всю используется в блогах и живой ленте.
А вот описания к нему я не нашел. Восполним пробел.
Компонент называется main.file.input и лежит в системных компонентах. Все что он умеет - это загружать и сохранять файлы через CFile::SaveFile(), удалять через CFile::Delete() и все. Файлы по дефолту привязываются к главному модулю main и сохраняются в одноименной папке относительно /upload. Сам компонент возвращает некий универсальный идентификатор. Без него невозможна работа callback-ов при загрузки файла и после.
Сценарий работы компонента Все основные js-функции лежат в script.js. При загрузке страницы формируется js-объект с заданными параметрами (это происходит в шаблоне). Далее, либо при загрузке страницы, либо по событию (клик, аякс-загрузка контента, ховер и пр.) срабатывают кастомные js-события, они вызывают функции, которые открывают нужные дивы и таблицы, ибо, по дефолту они скрыты. При этом срабатывает функция window.BlogBFileDialogUploader.prototype.GetUploadFileName, которая формирует форму для отправки файла и фрейм.
При отправке формы срабатывает window.BlogBFileDialogUploader.prototype.CallSubmit, которая отправляет файл. По дефолту файл отправляется на ту страницу, на которой находится сам компонент. Путь берется из параметра this.uploadFileUrl, а он из параметров при создании объекта. При случае можно менять на какой угодно. В общем и целом, лучше чтоб файл загружался на страницу, где находится компонент, потому как в самом компоненте идет проверка основных параметров и там формируется объект для работы с callback-ами. Без него у вас файл может загрузиться и сохраниться нормально, но индикатор загрузки зависнет, как будто файл не загрузился и процесс завис. Но обо всем по порядку.
Параметры: INPUT_NAME // уникальный name инпута, без него выдает ошибку MULTIPLE => Y || N - позволяет или не позволяет множественную загрузку MODULE_ID => main || iblock || blog || forum и пр. имя модуля, к которому файл привязан будет и в какую папку попадет относительно upload. MAX_FILE_SIZE // максимальный размер файла (вроде в байтах) ALLOW_UPLOAD A || F || I - какой тип файлов будем грузить: F - файлы, I - картинки, A - все подряд. ALLOW_UPLOAD_EXT => «*.zip,*.rar,*.doc и пр.» // какие расширения файлов можно грузить. Работает если ALLOW_UPLOAD => F
Шаблон компонента всяко придется править, так что можно сразу копировать в шаблон .default. при этом в самом шаблоне поправьте пути к js и css файлам, которые грузятся по средствам js-функций.
После этого на странице вы не увидите ничего Для этого надо в шаблоне избавиться от некоторых мало понятных проверок и функции.
Убираем php-вставки в скрипте, в шаблоне, примерно строки 117, 154 и 171. Тут идет проверка php - callback - функции, в момент загрузки страницы, она пустая и компонент скрыт. Активируется после перетаскивания файла в дропзону. После этого компонент нормально отображается на странице при ее загрузке.
Сам загрузчик формируется объектом:
BfileFD<?=$uid?> = new BlogBFileDialog({
'mode' : variant, // simple | extended вариант отображения - простой загрузчик или драг-н-дроп
'CID' : "<?=$arResult['CONTROL_UID']?>", // контрольная строка
'upload_path' : "<?=CUtil::JSEscape(htmlspecialcharsback(POST_FORM_ACTION_URI))?>", // путь, куда грузить файлы
'multiple' : <?=( $arParams['MULTIPLE'] == 'N' ? 'false' : 'true' )?>, // загрузка нескольких файлов одновременно
'controller': <?=$controller?>, // #id - основного div-а
'inputName' : "<?=CUtil::JSEscape($controlName)?>",
'fileInput' : "file-fileUploader-<?=$uid?>",
'fileInputName' : "mfi_files[]", // массив, который формируется отправке файлов
'msg' : { // всякие сообщения
'loading' : "<?=CUtil::JSEscape(GetMessage('BFDND_FILE_LOADING'))?>",
'file_exists':"<?=CUtil::JSEscape(GetMessage('BFDND_FILE_EXISTS'))?>",
'upload_error':"<?=CUtil::JSEscape(GetMessage('BFDND_UPLOAD_ERROR'))?>",
'access_denied':"<p style='margin-top:0;'><?=CUtil::JSEscape(GetMessage('BFDND_ACCESS_DENIED'))?></p>"
}
});
Если посмотреть функцию window.BlogBFileDialogUploader.prototype.CreateElements - там формируется фрейм и форма для отправки файла. Если нужно вместе с файлом передать еще какие-либо параметры, то добавляем свои инпуты.
window.BlogBFileDialogUploader.prototype.Callback - срабатывает после загрузки и содержит массив с инфой о файле. Сюда можно впихнуть все что получится в $arResult. С этим массивом можно работать и использовать дальше после загрузки.
При использовании компонента в режиме ajax - т.е. при загрузке компонента на страницу или в popup-окно с помощью аякса, не забудьте поменять параметр "upload_path" в объекте формирования загрузчика в шаблоне компонента. И если не хочется заморачиваться файлом-приемником, то поставьте туда этот же компонент.
Рабой Андрей, так не копипастим пример, а правильно задаём параметр MODULE_ID. Вот и не удаляются, если кастомно в инфоблоки грузите, надо задавать -- iblock, а не main
Для торопыг: 1. если нужно, чтобы сразу выводилась форма, без ссылки "Загрузить файлы". Просто js проверяем display формы и делаем клик:
$(document).ready(function() {
let link = document.querySelector('.file-selectdialog-switcher');
let fileForm = document.querySelector('.file-selectdialog');
if (fileForm.style["display"] === 'none'){
link.click();
}
});
Почему проверка: без проверки на форме редактирования элемента блок начинает скакать туда сюда, если указать файлы в INPUT_VALUE.
Иначе на форме добавления файла можно просто клик:
2. если у вас форма редактирования элемента и есть список файлов => прописываем параметр INPUT_VALUE в вызове компонента. Пример есть выше в комментах:
event: 'onSelectElement' - это кастомное событие, на которое мы будем вешаться, дабы отследить, что в окошке выбрали товар. Писать там можно что угодно, хоть hrenMorjovii.
lang: 'ru' - язык сайта.
allow_select_parent: 'Y' - не экспериментировал особо с этим параметром, за что отвечает - хз. Без него тоже работает.
compileUrl - комплит урл, по которому надо получать контент окна. По сути, впихивает в урл все props, которые у нас есть.
/bitrix/admin/esd_element_search_dialog.php - кастомная страница вызова компонента, которую мы создали, и положили вызов компонента, аналогично /bitrix/modules/catalog/admin/cat_product_search_dialog.php
У этого объекта есть параметр content_url, если его указать, то контент в окно будет аяксом подтянут по этому урлу. Это нам и нужно.
И в файле, где нам это окно нужно использовать:
import ElementSearch from './ElementSearch';
const dialog = new ElementSearch({ IBLOCK_ID: 147 });
dialog.dialogSearch().getDialog().Show();
Параметр IBLOCK_ID - отвечает за тот инфоблок, с которого надо тянуть элементы.
Метод dialog.dialogSearch().getDialog().Show(); отвечает за показ окна. Я его здесь вызвал при отработке скрипта, но его можно обернуть в какой-нибудь onClick и вызывать в нем. Это уже люди сами разберутся, где и как юзать.
Для отслеживания выбора элементов повесимся на событие, которое мы указали в объекте.
BX.addCustomEvent(dialog.getEvent(), (dataEvent) => {
console.info(dataEvent); // данные о выбранном элементе.
dialog.getDialog().Close(); // закроем окно
});
По дефолту это окно выбирает только те инфоблоки, которые отмечены как торговые каталоги.
Если же нам надо выбрать вообще любой ИБ, то нужно кастомизировать компонент. Благо он сделан с использованием class.php.
Копируем компонент bitrix:catalog.product.search в свое пространство.
Пересоздаем class.php, вызываем в нем подключение родительского класса.
<?php
/** @var \CBitrixComponent $this */
/** @var array $arParams */
/** @var array $arResult */
/** @var string $componentPath */
/** @var string $componentName */
/** @var string $componentTemplate */
/** @var \CBitrixComponent $component */
/** @global \CUser $USER */
/** @global \CMain $APPLICATION */
use Bitrix\Main\Loader;
use Bitrix\Iblock;
Loader::includeModule('iblock');
CBitrixComponent::includeComponentClass('bitrix:catalog.product.search');
class AdminElementSearchComponent extends ProductSearchComponent
{
protected function checkIblockAccess()
{
return true; // это чтоб не проверять, что у нас только каталоги и разрешать все ИБ
}
protected function getIblockList()
{
if ($this->iblockList === null){
$oResult = Iblock\IblockTable::getList([
'filter' => ['=ACTIVE' => 'Y'],
'sel ect' => ['*'],
]);
while ($row = $oResult->fetch()) {
$this->iblockList[$row['ID']] = $row;
}
}
return $this->iblockList;
}
protected function getIblockId()
{
return $this->arParams['IBLOCK_ID'];
}
}
Переопределяем несколько методов.
checkIblockAccess() - чтоб не проверять, что у нас только каталоги и разрешать все ИБ.
getIblockList() - скажет компоненту, какие ИБ можно выбирать в окошке. Собственно, тут же можно сформировать свой список, если задача того требует.
getIblockId() - чтобы тоже избежать проверок на принадлежность ИБ к каталогу, и показывать ИБ из $_GET в изначальной отрисовке окна. Грубо говоря, чтоб в arParams['IBLOCK_ID'] попал ИБ, который мы в js засунули.
Далее, в админке, на своей странице меняем вызов компонента на свой. И в конструктор объекта можно передавать новый урл.
const dialog = new ElementSearch({
IBLOCK_ID: 147,
url: '/bitrix/admin/new_search_element.php'
});
Листинг вызова окошка.
import ElementSearch fr om './ElementSearch';
BX(function(){
const dialog = new ElementSearch({
IBLOCK_ID: 147,
url: '/bitrix/admin/new_search_element.php'
});
dialog.dialogSearch().getDialog().Show();
})
А как можно там слева настроить вывод всех каталогов чтобы была иерархия: ВСЕ и под ней все инфоблоки. Чтобы поиск справа в фильтре был сразу по товарам из всех инфоблоков?
Решил на днях попробовать в D7 выборку с group_by. Задача - выдрать из базы активные элементы с максимальной сортировкой, из определенного раздела и сделать группировку по NAME. Вроде ничего необычного.
Вроде все логично и должно работать, но я был удивлен тем, какой sql получился на выходе:
SEL ECT
`iblock_element`.`ID` AS `ID`,
`iblock_element`.`NAME` AS `NAME`,
COUNT(*) AS `COUNT`,
`iblock_element`.`CODE` AS `CODE`
FR OM `b_iblock_element` `iblock_element`
WHERE `iblock_element`.`IBLOCK_ID` = 114
AND UPPER(`iblock_element`.`ACTIVE`) like upper('Y')
AND `iblock_element`.`IBLOCK_SECTION_ID` = 10216
GROUP BY `iblock_element`.`NAME`, `iblock_element`.`SORT`, `iblock_element`.`ID`, `iblock_element`.`CODE`
ORDER BY `iblock_element`.`SORT` DESC
LIMIT 0, 30
Зачем в GROUP_BY пихать все поля из SELECT? Это баг, фича или я чего-то не понимаю в построении запросов? Естественно этот код выдал не тот результат.
А вот ежели просто выполнить запрос:
SELECT
`iblock_element`.`ID` AS `ID`,
`iblock_element`.`NAME` AS `NAME`,
COUNT(*) AS `COUNT`,
`iblock_element`.`CODE` AS `CODE`
FR OM `b_iblock_element` `iblock_element`
WH ERE `iblock_element`.`IBLOCK_ID` = 114
AND UPPER(`iblock_element`.`ACTIVE`) like upper('Y')
AND `iblock_element`.`IBLOCK_SECTION_ID` = 10216
GROUP BY `iblock_element`.`NAME`
ORDER BY `iblock_element`.`SORT` DESC
LIMIT 0, 30;
То все работает как надо. Стоит стабильная версия 14.9.3
От тех кто уже наступил на эти грабли тем, кто еще только собирается.
Не так давно яндекс-маркет сделал механизм с покупкой на маркете, не переходя на сайт партнера. Т.е. вы можете выбрать товар на маркете, и тут же его купить. Вернее покупать пока нельзя, можно только сформировать заказ, который упадет в магазин партнера. Далее с вами будут связываться из этого магазина и уточнять детали по заказу.
Собственно магазин должен удовлетворять требованиям маркета, и иметь SSL.
В Битриксе из коробки есть механизм интеграции с этой программой. Наблюдать его можно в админке -> магазин -> настройки -> покупки на маркете. Там формочка с настройками по каждому сайту. До нее нужно настроить форму в кабинете маркета и получить ключи. А механизм настроек со стороны Битрикс описан в курсе http://dev.1c-bitrix.ru/learning/cour....4828.5802
Пройдемся по настройкам в личном кабинете маркета.
URL ресурса партнерского API Яндекс.Маркета – оставляем как есть.
Токен для запросов Яндекса к магазину – token, который мы получали в яндексе – access_token.
Авторизационный токен (для запросов магазина к Яндексу) – см. выше.
Идентификатор приложения – то, что получили при регистрации приложения.
Остальные настройки понятны и без комментов.
Сохраняем форму и, казалось бы, наступит счастье. Однако…
Вместо счастья начинается череда засад, не зная про которые можно долго блуждать по инету и в исходниках модуля sale.
Засада номер раз. Она не критична с точки зрения разработки, но критична для юзабилити. Ежели мы, после настроек, нажмем «сохранить» или «применить», то все сработает как надо. Но ежели сделать F5, форма окажется пустой. Я раза 3 пересохранял настройки пока на залез в b_option. Там под NAME = yandex_market_purchase_settings будет лежать сериализованный массив настроек этой формы. Пройдя в /bitrix/modules/sale/admin/ymarket.php увидим примерно на 17 строке $arYMSettings = $_POST["YMSETTINGS"]; - это нормально, а не нормально, что нигде нет вызова этих настроек через getOptionString. Думаю разработчики Битрикс сие безобразие исправят.
Ну ладно, настройки-то в итоге сохраняются, значит все норм.
Однако…
Засада норме два. Идем в кабинет маркета, пробуем сделать тестовую покупку и получаем отлуп от яндекса в виде ошибки 404. Согласно доков, Яндекс ломится на страницу https://site.ru/bitrix/services/ymarket/cart, но такой страницы действительно нет на сайте. И правил под нее тоже никаких. Кроме того, у апача есть особенность подставлять слеш в конец строки если не указано расширение файла. Апач считает это разделом на сайте, подставляет в конец урла слеш и мы попадаем на 404. Выход – идем в админку сайта –> настройки -> обработка адресов -> правила обработки. И там указываем правила на три запроса от яндекса: правило - #^/bitrix/services/ymarket/cart#, файл - /bitrix/services/ymarket/index.php, так же и для запросов /order/status и /order/accept. В итоге все запросы от яндекса должны переправляться в файл /bitrix/services/ymarket/index.php.
Засада номер три. При следующей тестовой покупке сайт ничего не вернет, на что яндекс и будет материться. Идем в файл /bitrix/modules/sale/services/ymarket/index.php. Там есть интересные строчки
Однако сколько ни пытайся трейсить $_REQUEST, $_POST и $_GET – ничего не увидишь. И это логично, ибо, все данные от яндекса лежат в $HTTP_RAW_POST_DATA в виде json. Вот с ней и надо работать.
Засада номер четыре. После подстановки нужных значений в $YMHandler->processRequest() все вроде бы должно работать. Идем в это модуле в файл /general/ym_handler.php, ищем метод getDeliveryOptions, в нем foreach ($this->mapDelivery as $deliveryId => $deliveryType) В mapDelivery настройки попадают из __construct в котором получаются через getSettings().
Все бы ничего, если у вас 1 или 2 сайта на лицензии. Но когда их с десяток, то массив с настройками не полностью сохраняется в базу. Он обрезается до 2000 символов. И при unserialize получаем false и Notice: unserialize(): Error at offset 1995 of 2000 bytes. Решение – класть настройки для каждого сайта в отдельную строку в b_option.
Засада номер пять. Кроется она в методе getDeliveryOptions() По докам яндекса, если у товара имеется доставка типа PICKUP, в ответном массиве должен быть ключ outlets => array(array('id'=>1)), id в общем вроде бы не важен, должен браться из настроек магазина, из кабинета маркета, но можно передать и сразу. Но этот ключ должен быть обязательно и иметь такую структуру.
Пожалуй это пока все засады, которые я знаю про эту фичу.
Как это вылечили.
Сделали свою страничку в админке с настройками, настройки кладем по каждому сайту в отдельную строку в b_option.
Сделали свои странички php_interface: ym_buy_init.php – кастомизированный /bitrix/modules/sale/services/ymarket/index.php и ym_buy_handler.php – кастомизированный /sale/general/ym_handler.php
Перенаправили яндекс на них.
В итоге – когда это безобразие профиксят, и будет действительно работать из коробки, останется только перенаправить яндекс на дефолтную страницу и сменить правила обработки урлов.
По поводу проблемы №2 (я столкнулся только с ней) - можно сделать 1 правило #^/bitrix/services/ymarket/# с файлом /bitrix/services/ymarket/index.php , этого будет достаточно, плюс такое правило универсально.
Станислав, день добрый. Не подскажите что может быть? Тестовый заказ получаю на сайт, все приходит норм. При попытке поменять статус заказ, допустим на Выполнен в Яндексе не меняется статус, остается в обработке.
Так случилось, что модуль веб-сервисов как-то обошли стороной в документации. Вернее не совсем обошли - основные методы и классы описали, а вот пример в курсе для разработчиков весьма скудный и мало что проясняющий.
Я записал видео, в котором показал основные моменты работы с этим модулем на наглядном примере. Это не картина маслом, а только набросок карандашом, с которого можно писать свои картины. Исходники компонентов прилагаются.
Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».