Error install app pls install local application bitrix24

Доброго времени суток.Воспользовался примером Скачал его к себе загрузил на сервер, установил и изменил ключи как пложено.При переходе на страницу приложение index.php в результатах пишет Код [error] => no_install_app [error_information] => error install app, pls install local application Нажал "переустановить" приложение результат такой же. Не могу понять где проблема, что не работает. Подскажите куда смотреть и копать?
 

Пользователь 308489

Заглянувший

Сообщений: 2
Авторитет:

1

Рейтинг пользователя:

0

Регистрация: 23.03.2015

#1

0

17.02.2021 10:38:51

Доброго времени суток.

Воспользовался примером

https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=99&LESSON_ID=8579

Скачал его к себе загрузил на сервер, установил и изменил ключи как пложено.

При переходе на страницу приложение index.php в результатах пишет

Код
[error] => no_install_app
[error_information] => error install app, pls install local application 

Нажал «переустановить» приложение результат такой же. Не могу понять где проблема, что не работает. Подскажите куда смотреть и копать?

 

Пользователь 3045925

Заглянувший

Сообщений: 8
Авторитет:

0

Рейтинг пользователя:

0

Регистрация: 29.03.2019

#2

0

20.02.2021 14:41:31

Цитата
Максим Чувашов написал:
Доброго времени суток.

Воспользовался примером  

https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=99&LESSON_ID=8579
Скачал его к себе загрузил на сервер, установил и изменил ключи как пложено.

При переходе на страницу приложение index.php в результатах пишет  

Код
 [error] => no_install_app
[error_information] => error install app, pls install local application  

Нажал «переустановить» приложение результат такой же. Не могу понять где проблема, что не работает. Подскажите куда смотреть и копать?

Нужно проверить, корректно ли берутся данные из файла. Проверить пути к файлу settings.json

 

Пользователь 5354408

Заглянувший

Сообщений: 2
Авторитет:

0

Рейтинг пользователя:

0

Регистрация: 17.06.2021

#3

0

21.06.2021 11:12:25

Цитата
Максим Чувашов написал:
Доброго времени суток.

Воспользовался примером  

https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=99&LESSON_ID=8579
Скачал его к себе загрузил на сервер, установил и изменил ключи как пложено.

При переходе на страницу приложение index.php в результатах пишет  

Код
 [error] => no_install_app
[error_information] => error install app, pls install local application  

Нажал «переустановить» приложение результат такой же. Не могу понять где проблема, что не работает. Подскажите куда смотреть и копать?

Добрый день, получилось разобраться?

 

Пользователь 5354408

Заглянувший

Сообщений: 2
Авторитет:

0

Рейтинг пользователя:

0

Регистрация: 17.06.2021

#4

0

22.06.2021 11:20:49

Цитата
Святослав Рымарев написал:

Цитата
Максим Чувашов написал:
Доброго времени суток.

Воспользовался примером  

https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=99&LESSON_ID=8579  
Скачал его к себе загрузил на сервер, установил и изменил ключи как пложено.

При переходе на страницу приложение index.php в результатах пишет  

Код
  [error] => no_install_app
[error_information] => error install app, pls install local application   

 
Нажал «переустановить» приложение результат такой же. Не могу понять где проблема, что не работает. Подскажите куда смотреть и копать?

Нужно проверить, корректно ли берутся данные из файла. Проверить пути к файлу settings.json

Можете более подробно описать какие действия можно предпринять?  

 

Пользователь 5409408

Заглянувший

Сообщений: 1
Авторитет:

0

Рейтинг пользователя:

0

Регистрация: 10.07.2021

Добрый день, проблема актуальна, никак не соображу, в чем проблема…

 

Пользователь 4754588

Заглянувший

Сообщений: 1
Авторитет:

0

Рейтинг пользователя:

0

Регистрация: 31.05.2021

Здравствуйте.
Тоже не могу понять, есть идеи?

 

Пользователь 5458976

Посетитель

Сообщений: 40
Баллов: 7
Авторитет:

0

Рейтинг пользователя:

0

Регистрация: 30.07.2021

Внимание! Данный пример работает на основе SDK CRest. Перед использованием примера необходимо открыть через браузер файл checkserver.php и проверить корректность настроек вашего сервера.
Это что пишет? Все ок?

 

Пользователь 4095174

Посетитель

Сообщений: 55
Баллов: 9
Авторитет:

0

Рейтинг пользователя:

0

Регистрация: 26.06.2020

#8

0

15.09.2021 13:44:08

Уточнение.
1) При установки приложения выдается первый-временный токен срок жизни у которого 30 секунд, если мне память не изменяет.
2) После переустановки приложения Вам надо перейти не на index.php, а на install.php и уложиться в те же упомянутые выше 30 секунд. Для того что бы обновить первый-временный токен.
минимальный install.php вроде такой может быть.

Код
<?php
require_once (__DIR__.'/crest.php');

$result = CRest::installApp();
if($result['rest_only'] === false):?>
   <head>
      <sc ript src="//api.bitrix24.com/api/v1/"></sc ript>
      <?php if($result['install'] == true):?>
         <sc ript>
            BX24.init(function(){
               BX24.installFinish();
            });
         </sc ript>
      <?php endif;?>
   </head>
   <body>
      <?php if($result['install'] == true):?>
         installation has been finished
      <?php else:?>
         installation error
      <?php endif;?>
   </body>
<?php endif;
 

Пользователь 92319

Заглянувший

Сообщений: 18
Баллов: 1
Авторитет:

1

Рейтинг пользователя:

0

Регистрация: 15.02.2012

#9

0

19.10.2021 09:02:45

Не перепутайте местами install и index path.

Содержание

  1. Error install app pls install local application bitrix24
  2. Разработка приложения для Битрикс24 от А до Я. Часть 2 — реализуем установку приложения
  3. Начинаем разработку
  4. Доставка полезной информации от экспертов по Битрикс24!

Error install app pls install local application bitrix24

Trying to download a file using crm.documentgenerator.document.download does not work as this is not a JSON.
Kindly add support for that

Created at 4 weeks ago

I have a custom field with multiple selection.

doesn’t work. How can I add contact via API to a field with multiple selection?

Created at 4 months ago

Т.к. используется http_build_query
Нельзя в фильтре например передать параметр false он будет преобразован в 0

Created at 4 months ago

параметр event не всегда передаётся битриксом. из за этого выскакивают лишние notice, которые на пример в AJAX ответе могут всё испортить.

было
if ($_REQUEST[ ‘event’ ] == ‘ONAPPINSTALL’ && !empty($_REQUEST[ ‘auth’ ])) <
стало
if (isset($_REQUEST[ ‘event’ ]) && $_REQUEST[ ‘event’ ] == ‘ONAPPINSTALL’ && !empty($_REQUEST[ ‘auth’ ])) <

Created at 9 months ago

Почему бы не добавить garbage collector, который будет удалять лишние лог файлы. Видел клиентов, у которых логи за год накопились на 300 МБ и по 100000 с лишним файлов.

Created at 10 months ago

Составил запрос, при помощи вашей библиотеки, при каждой отправке запроса, приходят разные данные, что я делаю не так?
мой запрос:

`function getCalls() <
$startDate = $_POST[‘startDate’];
$endDate = $_POST[‘endDate’];
$select = json_decode($_POST[‘select’]);
$departmentId = json_decode($_POST[‘users’]);
$leadID = 0;
$finish = false;
$finalResult = [];

Created at 1 year ago

If there is a mysql error message in the curl output, the $result is empty after the json_decode, because such error message is not a valid json. There is no useful information in the response — no error, no error_information, just a pointless empty $result. That’s not acceptable.

Created at 2 years ago

Hi, I’m using CRest. I have created my application in Bitrix but I’m getting the message:

Array ( [error] => no_install_app [error_information] => error install app, pls install local application )

Источник

Разработка приложения для Битрикс24 от А до Я. Часть 2 — реализуем установку приложения

На прошлой неделе мы начали рассказывать о том, как разрабатываются приложения для Битрикс24. В первом выпуске мы определили, с чего начинать разработку, а также описали основные задачи, выполняемые приложением, и данные, которые будут использоваться для этого. Если вы не читали первую часть, рекомендуем начать именно с нее, чтобы у вас была полная картина разработки.

Ну а сегодня мы продолжим познавать мастерство разработки. Начнем с логического старта — с установки приложения, а точнее с реализации функции. Далее повествование идет от лица Вадима — нашего руководителя отдела web-разработки.

Начинаем разработку

Схема 1. Установка приложения

Сохраняем в лог запрос с портала

Пробуем получить ранее сохраненные о портале данные

Есть ли запись о портале?

фиксируем факт установки в логах

формируем поля для новой записи

фиксируем в логах значения полей

добавляем новую запись

фиксируем в логах результат добавления

формируем запрос для подписки на событие OnAppUninstall

отсылаем запрос на портал

фиксируем в логах запрос и результат выполнения

фиксируем факт обновления в логах

формируем поля для обновления записи

фиксируем в логах значения полей

обновляем старую запись

фиксируем в логах результат обновления

формируем запрос на текущий список наших подписок на события

отсылаем запрос на портал

фиксируем в логах запрос и результат выполнения

инициализируем массив под батч-запрос

собираем в массив запросы на удаление старой подписки

если массив не пустой, отправляем запрос на портал

фиксируем в логах запрос и результат выполнения

формируем запрос для новой подписки на событие OnAppUninstall

отсылаем запрос на портал

фиксируем в логах запрос и результат выполнения

Выводим страницу пользователю

подключаем js-библиотеку портала

Я расписал всю логику без какой-либо оптимизации. Некоторые авторы так и рекомендуют делать: вначале писать все подряд, добиваться рабочего кода, а уж только затем приступать к оптимизации. Кажется, Фаулер говорил, что нельзя одновременно писать код и по ходу дела оптимизировать, нужно разделять на два этапа. Теперь же, глядя на то, что у нас получилось, можно заметить, что у нас один, по крайней мере, код выполняется в обеих ветках условия, а именно подписка на событие. Что ж, можно его вынести за рамки условия и удалить из обеих веток. Однако, в действительности алгоритм будет куда сложнее, поскольку в этом автор проживает в сказочном мире, где все всегда заканчивается хорошо. К сожалению, он в этом не одинок. Для начала создадим переменную errors и проинициализируем массивом.

Вопрос об обработке ошибок настолько важен, насколько часто упускается из вида. Можем ли мы сказать порталу, что установка завершена, если нам не удастся подписаться на события или сохранить запись о портале? Вероятно, ничего уж такого критичного в плане основной задачи приложения не произойдет, мы лишь не сможем потом определить факт обновления и не сможем почистить таблицы от данных портала, на котором приложение было удалено. И все-таки факт того, что написано кривовато, привяжется навсегда к приложению в наших головах, повысит уровень адреналина, снизит стрессоустойчивость. Короче, самим же хуже будет. А потому давайте решать, что завершать установку при наличии подобных ошибок нельзя ни в коем случае.

Схема 2. Вторая версия кода (конкретные действия спрятаны в вызовы функций):

Сохраняем в лог запрос с портала

Пробуем получить ранее сохраненные о портале данные

Есть ли запись о портале?

фиксируем факт установки приложения в логах

фиксируем факт обновления в логах

Выводим страницу пользователю

есть ли сообщение об ошибке?

подключаем js-библиотеку портала

Думаю, что все понятно, кроме вызовов createUserMessage() и rollBackIntstall().

По первому. Дело в том, что ошибки могут быть как разного уровня критичности, так и требовать разных действий от пользователя. В данном случае фатальные ошибки — это неудача сохранения данных о портале, отписки от событий, подписки на события; некритичные — ошибка записи в логи. При фатальной ошибке мы должны сформировать пользователю сообщение, из которого он сумел бы понять свою ситуацию и предпринять правильные действия. Если нам не удалось записать что-то в базу, то проблема на нашей стороне, скорее временная и исчезнет, например, как только станет доступным сервер базы данных, в этом случае нужно посоветовать подождать и повторить установку. Если не удается отписаться/подписаться, то, возможно, у пользователя, устанавливающего приложение, недостаточно прав, и тогда ему нужно сообщить, чтобы установку выполнил администратор портала.

Теперь по второму методу. С ним попроще: если мы зафиксировали у себя данные о портале (добавили или обновили), а ошибка произошла на этапе событий, то очевидно нам следует вернуть назад все, как было. Отсюда возникает задача запоминать выполненные действия и исходные данные. Либо, если все действия были связаны с базой данных, использовать транзакции.

Если теперь посмотреть на код приложения, то понятно становится высказывание, что программирование — это работа с ошибками. И посмотрев на код еще раз, вижу, что в двух местах вызывается запись в лог факта установки/обновления и совсем не там, где надо бы. Эти два действия фактически различаются лишь значением одного поля EVENT_TYPE, и вызываются из мест, в которых факт установки/обновления еще не факт. Так что исправим эту ошибку. Там, где было логирование, будем лишь запоминать тип события, а само логирование перенесем в конец установки, когда точно известно, все ли прошло гладко.

Схема 3. Третья версия:

Сохраняем в лог запрос с портала

Пробуем получить ранее сохраненные о портале данные

Есть ли запись о портале?

фиксируем факт установки/обновления в логах

Выводим страницу пользователю

есть ли сообщение об ошибке?

подключаем js-библиотеку портала

Логика установки приложения теперь не вызывает вопросов. Но вызывает вопрос логирование. В коде указано, что мы логируем сам запрос с портала. Вот он:

здесь (не удивляйтесь только):

А я их, вроде как, рекомендовал шифровать при сохранении в базу. Если так рассудить, то нам и логировать-то его не нужно, поскольку все, что в нем содержится нам итак будет известно, разве что язык. В логи всегда попадет либо факт установки, либо ошибки при установке. Но в действительности запрос может приходить и не с портала вовсе, а из хакерского приложения. В таком случае зачем его вообще логировать? И по этом размышлении становится понятно, что непонятно, как приложение вообще защищено от левых запросов. В результате наш список действий дополняется еще одним пунктом:

17. Проверка валидности запроса извне.

В данном случае (при установке приложения) мы так и так делаем запрос на портал для получения дополнительной информации, вызывая app.info с переданным нам access_token (AUTH_ID). Какие возникают варианты ответа:

  • пустой
  • с ошибкой
  • с данными о приложении (и портале)

Причиной первого может быть формирование неверного УРЛ к rest-у портала, например, когда кто-то стукнулся на страницу установки напрямую и никакого домена в запросе нет или он неверный. Второй вариант. Все запросы в портал приложение должно, по-хорошему, не только выполнять через защищенное соединение (по SSL), но и с проверкой сертификата сервера, не допуская самоподписанные. При пустом теле ответа можно проверить код ошибки (http status code ответа), и, если до сервера достучаться не удалось именно из-за неверного сертификата, то об этом необходимо сообщить пользователю. Битрикс24 существует как облачный, так и коробочный, и здесь мы имеем дело, стало быть, с коробкой, размещенной на собственном сервере владельца.

С ошибкой. Причиной этого варианта, скорее всего, будет также коробка. Например, она возвратит нам 404 Not Found страницу, т.е. целый лопух html, или скорее страницу с формой авторизации. Это будет означать, что в коробке не установлен модуль rest. Об этом также следует сообщить пользователю. Другая причина — ошибка в работе rest-а при неверно накатанном обновлении портала, ну. тут, вероятно, следует показать саму ошибку, поскольку вариации непредсказуемы.

Последний вариант — успешный, так что возвратимся к логированию. Запросы можно слать миллиардами и забить всю нашу таблицу логов, поэтому логировать стоит, по всей видимости, только валидные запросы, в которых rest все-таки вернул какой-то ответ или была ошибка ssl-сертификата. В логи необходимо из запроса, пожалуй, писать данные по тем ключам, которые не содержат конфиденциальной информации. Т.е. значения токенов писать точно не стоит.

Теперь перейдем к сохранению данных в базу. По идее, когда запрос определен как валидный, и app.info вернул информацию о портале, переданные LANG, member_id должны содержать корректную информацию. Однако, в этом вопросе лучше придерживаться параноидального метода, который гласит: не верь присланным данным. Поэтому все, что сохраняется в базу должно быть предварительно обезврежено. Так язык всегда состоит из двух букв и не содержит ничего кроме букв, member_id состоит лишь из букв и цифр и т.д. Перед сохранением нужно удалить все неподходящие символы регуляркой, обезопасив приложение от атак. Еще лучше, когда эту работу берет на себя фреймворк, который вы используете. Так, например, БУС (Битрикс Управление Сайтом) всегда проверяет получаемые из запроса данные. И, если ваше приложение строится на подобном фреймворке, получать данные следует не прямым способом, предусмотренным самим языком программирования, а через вызов соответствующего метода фреймворка (напр., в БУС объект request имеет методы get() — для получения данных, переданных как GET, так и POST методом, и getPost() — для получения исключительно «постовых» данных).

На этом с установкой, пожалуй, все. Но на примере подписки хотелось бы чуть коснуться rest-запросов портала. REST Битрикс24 довольно неплохой. Да, порою требуемые для передачи параметры удивляют. Например, где-то регистр ключей имеет значение, а где-то не имеет. А есть еще и запросы, в которых все данные имеют соответствующие ключи, но, тем не менее, переданы должны быть в нужной очередности. Однако, в батч-запросах можно, формируя последовательность команд, из одной обращаться к данным, полученным из предыдущей. Это круто. Рассмотрим удаление подписки на события, оставшейся от прежней версии приложения. Для этого нам нужно получить наши старые обработчики, делаем запрос (команда, параметры):

получаем в результате что-то вроде:

Портал возвращает все прежние подписки нашего приложения. При переходе с версии на версию у нас меняется УРЛ, теперь он должен вместо v1 должен содержать v2.

Удаляем старую подписку, поскольку метода event.update не существует. Можно это сделать отдельными запросами, но Битрикс24 накладывает на их частоту ограничения. При этом частота обращений учитывается по порталу и IP-адресу, с которого они делаются. Так что, если на портале установлено десять наших приложений, размещенных на одном сервере, то ограничение по запросам будет для них общим. Согласно документации: «Разрешается два запроса в секунду. Если лимит превышается, то ограничение начинает срабатывать после 50 запросов». Так что лучше нам будет удаление подписки обернуть в один батч-запрос. Я не буду расписывать здесь все тонкости, а предположу, что есть, как у меня лично, некий метод, который уже умеет формировать подобные запросы. Замечу только, что есть ограничение по кол-во команд, переданных в одном таком запросе, а именно — не более 50. Итак отписка/подписка:

if array res.result

foreach res.result as item

if not empty batch

if not empty res.result_errors

Как я уже сказал, я не вникаю в архитектуру приложения, и имена вызываемых функций здесь служат лишь для указания, какое действие выполняется в данный момент и какие данные оно использует. В этом коде мы точно уверены, что наши подписки никогда не превысят ограничения батч-запросов в 50 команд, но там, где это неочевидно, следует вставлять проверку. При этом нужно учитывать, что размер команд вместе с их параметрами (представьте обновление 5000 компаний или счетов за один раз) может составлять в байтах довольно большое число, так что не стоит собирать все команды в один массив, потом резать его по 50 штук, и затем отправлять эти нарезки на выполнение. Fatal error, cannot allocate memory могут быть запросто получены. Алгоритм обработки в данном случае должен быть иным. Пример в псевдокоде:

while row = getSomething()

if count batch equal 50

Иными словами, внутри цикла мы все время проверяем не достигли ли мы максимума в 50 запросов. Если да, то тут же их выполняем и очищаем наш массив batch — т.е. отсчет начат заново. По окончании цикла вновь проверяем batch, ведь последний проход мог закончится, так и не достигнув кол-ва 50. Если после цикла массив не пустой, то это необработанный остаток, выполняем его.

Ну, теперь и с установкой, и с запросами точно все.

Доставка полезной информации от экспертов по Битрикс24!

Подпишитесь, и раз в неделю у вас на почте будет подборка полезных советов и обзоров про Битрикс24

Источник

CG-root

Trying to download a file using crm.documentgenerator.document.download does not work as this is not a JSON.
Kindly add support for that

forionne-admin

Hi,

I have a custom field with multiple selection.

image

'UF_CRM_1618612593' => "246"

or

'UF_CRM_1618612593' => 246

or

'UF_CRM_1618612593' => [
          ['VALUE' => 246]
        ]

doesn’t work. How can I add contact via API to a field with multiple selection?

MrDeff

Т.к. используется http_build_query
Нельзя в фильтре например передать параметр false он будет преобразован в 0

muravikov

параметр event не всегда передаётся битриксом. из за этого выскакивают лишние notice, которые на пример в AJAX ответе могут всё испортить.

было
if ($_REQUEST[ ‘event’ ] == ‘ONAPPINSTALL’ && !empty($_REQUEST[ ‘auth’ ])) {
стало
if (isset($_REQUEST[ ‘event’ ]) && $_REQUEST[ ‘event’ ] == ‘ONAPPINSTALL’ && !empty($_REQUEST[ ‘auth’ ])) {

bromimo

Почему бы не добавить garbage collector, который будет удалять лишние лог файлы. Видел клиентов, у которых логи за год накопились на 300 МБ и по 100000 с лишним файлов.

a-kuimov

Составил запрос, при помощи вашей библиотеки, при каждой отправке запроса, приходят разные данные, что я делаю не так?
мой запрос:

`function getCalls() {
$startDate = $_POST[‘startDate’];
$endDate = $_POST[‘endDate’];
$select = json_decode($_POST[‘select’]);
$departmentId = json_decode($_POST[‘users’]);
$leadID = 0;
$finish = false;
$finalResult = [];

while (!$finish)
{
    /**
     * Выполняем пока не заберем все данные, в этом случае не стоит забывать и про задержку между хитами.
     * Либо каждый раз выбираем только 50, начиная с того элемента, на котором остановилась прошлая итерация
     */

    $users = CRest::call(
        'voximplant.statistic.get',
        [
            'order' => ['ID' => 'ASC'],
            'select' => $select,
            'filter' => [
                '>ID' => $leadID,
                'PORTAL_USER_ID' => $departmentId,
                'CALL_TYPE' => 1,
                ">CALL_START_DATE" => $startDate.'T00:00:00',
                "<CALL_START_DATE" => $endDate.'T23:59:59',
            ],
            'start' => -1
        ]
    );

    if (count($users['result']) > 0)
    {
        foreach ($users['result'] as $lead)
        {
            $leadID = $lead['ID'];
            $subResult = [];
            foreach ($select as $value) {
                $subResult[$value] = $lead[$value];
            }
            $finalResult[] = $subResult;
        }
        // Do something
    }
    else
    {
        $finish = true;
    }
}

echo json_encode($finalResult);

}`

dmitry-volkoff

If there is a mysql error message in the curl output, the $result is empty after the json_decode, because such error message is not a valid json. There is no useful information in the response — no error, no error_information, just a pointless empty $result. That’s not acceptable.

csandovalgt

Hi, I’m using CRest. I have created my application in Bitrix but I’m getting the message:

Array ( [error] => no_install_app [error_information] => error install app, pls install local application )

Looking the crest.php file I see that it is trying to get information from the file settings.json:
file_exists(DIR . ‘/settings.json’)
but I don’t see this file in the project.

Do you know where I can get this file?

Интеграция Б24 с Тильдой

Передача заказа из Тильды в сделку Битрикс24 с товарами. Стандартная интеграция передает товары в виде текста в комментарий, поэтому будем использовать вебхук.

Содержание

Синхронизация товаров

Чтобы товары предавались из Тильды в Битрикс, их нужно создать и синхронизировать.

Создание товаров в Тильде

На странице сайта добавим блог из раздела Магазин, например, ST305N.

ST305N

Прокручиваем настройки контента в самый низ и находим фразу: “Если у вас много товаров, для управления ими используйте каталог товаров”. Нажимаем на каталог товаров.

Каталог товаров

Вы попадете на страницу создания каталога. Нажмите кнопку Начать работать с каталогом.

Начать работу с каталогом

В каталоге есть предустановленные товары для примера.

Предустановленные товары

Удаляем эти товары и создаем свои, нажав на кнопку Добавить товар

Вводим название товара и нажимаем Добавить.

Добавить новый товар

Откроется карточка товара, в которой нужно заполнить все параметры и нажать Сохранить и закрыть. Аналогично добавляем другие товары.

Если у вас уже были добавлены товары в виде карточки, а не в каталоге, то их можно перенести в каталог. В настройках контента блока ST305N, или аналогичного, в самом низу нажмите Скопировать товары в каталог из данного блока.

Скопировать товары в каталог

Для смены раздела нажимаем Разделы каталога.

Разделы каталога

Раздел по умолчанию удаляем с помощью значка корзины. Новый раздел добавляем с помощью кнопки Добавить раздел.

Добавление раздела

Теперь экспортируем товары для загрузки в Битрикс24.

Экспорт товаров Тильда

Если вы действуете в обратном порядке и загружаете товары из битрикса, то в каталоге есть импорт товаров из CSV.

Импорт товаров Тильда

Обратите внимание, что на странице сайта появилась вкладка Товары.

Товары на странице магазина

Вернемся на страницу магазина и к карточкам товаров в блоке ST305N. Прокручиваем в самый низ настроек и в параметре Выводить товары из каталога выбираем раздел или все товары. Нажимаем Сохранить и закрыть.

Контент ST305N

Вместо предустановленных  товаров в блоке ST305N появились товары каталога.  Можно публиковать страницу.

Товары каталога на странице

Импорт товаров в Битрикс24

Переходим в Битрикс24, CRM —> Товары и Склады —> Каталог товаров.

Каталог товаров в Б24

Далее нажмите на шестеренку и выберите Импорт товаров.

Импорт товаров Б24

Появятся параметры импорта. Выберите файл CSV с товарами, который мы выгрузили в Тильде, и нажмите Далее.

Выбор файла

На втором шаге необходимо сопоставить поля каталогов в Битриксе и Тильде.

  • Tilda UID — Внешний код;
  • Title — Название;
  • Description — Описание;
  • Photo — Детальная картинка;
  • Price — Цена. 

Нажмите Далее.

Настройка соответствия полей

Настройка соответствия полей

На последнем этапе вы увидите сколько товаров импортировано. Нажмите Готово.

Результат импорта

Перед вами откроется каталог товаров.

Импортированные товары

Если вы действуете в обратном порядке, и товары в Битриксе у вас уже есть, то их можно выгрузить в Excel, сохранить в CSV и загрузить в Тильду. К сожалению, картинки при таком варианте автоматом не выгрузятся.

Экспорт товаров Б24

С товарами разобрались, сопоставлять будем по внешнему коду. Теперь переходим непосредственно к интеграции.

Интеграция Тильды с Битрикс24

Сначала создадим вебхук в битриксе.

Настройка вебхука в Б24

В левом меню Битрикс24 Разработчикам —> Другое —> Входящий вебхук.

Разработчикам

Другое

Входящий вебхук

Редактируем название, метод можно не трогать, в настройке прав выбираем CRM(crm), сохраняем.

Вебхук

Вебхук для вызова rest api нам понадобится далее. Так что либо скопируйте сразу, либо оставьте вкладку с вебхуком открытой.

Созданные вебхуки всегда можно посмотреть в интеграциях. Разработчикам —> Интеграции.

Интеграции

Настройка вебхука на стороннем сервере

Т.к. облачный Битрикс не позволяет разместить код, нужно отдельное место. Можно использовать сайт, если он у вас есть, главное чтобы там отрабатывал код на php.У меня сайты расположены на Бегете, поэтому покажу на нем. Заходим Хостинг —> Файловый менеджер.

Файловый менеджер

Заходим в папку сайта, в котором хотим разместить вебхук, далее public_html и создаем три файла.

Файлы

  1. Tilda.php
				
					 [
            'TITLE' => $_POST['Name'].', '.$_POST['Email'].', '.$_POST['Phone'],
            "TYPE_ID" => "GOODS", 
            "STAGE_ID" => "NEW", 					
            "OPENED" => "Y", 
            "ASSIGNED_BY_ID" => 1, 
            "PROBABILITY" => 90,
            "CURRENCY_ID" => "RUB", 
            "OPPORTUNITY" => (float)$_POST['payment']['amount'],
        ]
    ]
);

$ID = (int)$resultDeal['result'];
//CRest::log($_POST, '', '/pak.log');
//CRest::log($resultDeal, 'ID='.$ID, '/pak.log');

if ($ID > 0)
{
    $rows = array();
    $sort = 1;
  
    foreach($_POST['payment']['products'] as $key => $item)
    {
        $item['sku'] = trim($item['sku']);

        $pid = 0;
        $name = $item['name'];
        $measure = '9';
        
        if (!empty($item['sku']))
        {
            $resultProduct = CRest::call(
                'crm.product.list',
                [
                    'order' => array( "ID" => "ASC" ),
                    'filter' => array( "PROPERTY_107" =>  $item['sku']),
                    'select' => [ "ID", "NAME", "MEASURE" ]
                ]
            );
            
            
            if ($resultProduct['total'] == 1)
            {
                $pid = $resultProduct['result'][0]['ID'];
                $measure = $resultProduct['result'][0]['MEASURE'];
            }
            else
            {
                $name = $item['sku'].': '.$item['name'];
            }
            
            unset($resultProduct);
        }

        $rows[] = array(
                'PRODUCT_ID' => $pid,
                'PRODUCT_NAME' => $name,
                'PRICE' => (float)$item['price'],
                'QUANTITY' => (float)$item['quantity'],
                'SORT' => $sort++,
                'TAX_RATE' => 15,
                'TAX_INCLUDED' => 'Y',
                'MEASURE_CODE' => $measure
            );
    }
    //CRest::log($rows, 'ROWS', '/pak.log');
    
    $result = CRest::call(
        'crm.deal.productrows.set',
        [
            'id' => $ID,
            'rows' => $rows
        ]
    );
    
    //CRest::log($result, 'RESULT', '/pak.log');
}
?>

				
			
  1. crest.php
				
					'.$str.' '.$val.PHP_EOL.'

‘;
}
public static function log($val, $str = », $log_file = ‘/upload/tmp/clan_dev.log’)
{
if (!empty($log_file))
{
if (substr($log_file, 0, 1) == ‘/’)
{
$f = fopen($_SERVER[«DOCUMENT_ROOT»] . $log_file, ‘a’);
if (!$f)
die(‘Alarm! Log-file not accepted!’ . $_SERVER[«DOCUMENT_ROOT»] . $log_file);
$rc = fwrite($f, date(‘j.m.Y H:i:s ‘) . (!empty($str) ? $str . ‘ = ‘ : ‘ ‘) . print_r($val, 1) . PHP_EOL);
if (!$rc)
die(‘Alarm! Log-file not writable! ‘ . $_SERVER[«DOCUMENT_ROOT»] . $log_file);
fclose($f);
}
}
}public static function installApp()
{
$result = [
‘rest_only’ => true,
‘install’ => false
];
if($_REQUEST[ ‘event’ ] == ‘ONAPPINSTALL’ && !empty($_REQUEST[ ‘auth’ ]))
{
$result[‘install’] = static::setAppSettings($_REQUEST[ ‘auth’ ], true);
}
elseif($_REQUEST[‘PLACEMENT’] == ‘DEFAULT’)
{
$result[‘rest_only’] = false;
$result[‘install’] = static::setAppSettings(
[
‘access_token’ => htmlspecialchars($_REQUEST[‘AUTH_ID’]),
‘expires_in’ => htmlspecialchars($_REQUEST[‘AUTH_EXPIRES’]),
‘application_token’ => htmlspecialchars($_REQUEST[‘APP_SID’]),
‘refresh_token’ => htmlspecialchars($_REQUEST[‘REFRESH_ID’]),
‘domain’ => htmlspecialchars($_REQUEST[‘DOMAIN’]),
‘client_endpoint’ => ‘https://’ . htmlspecialchars($_REQUEST[‘DOMAIN’]) . ‘/rest/’,
],
true
);
}static::setLog(
[
‘request’ => $_REQUEST,
‘result’ => $result
],
‘installApp’
);
return $result;
}/**
* @var $arParams array
* $arParams = [
* ‘method’ => ‘some rest method’,
* ‘params’ => []//array params of method
* ];
* @return mixed array|string|boolean curl-return or error
*
*/
protected static function callCurl($arParams)
{
if(!function_exists(‘curl_init’))
{
return [
‘error’ => ‘error_php_lib_curl’,
‘error_information’ => ‘need install curl lib’
];
}
$arSettings = static::getAppSettings();
if($arSettings !== false)
{
if(isset($arParams[ ‘this_auth’ ]) && $arParams[ ‘this_auth’ ] == ‘Y’)
{
$url = ‘https://oauth.bitrix.info/oauth/token/’;
}
else
{
$url = $arSettings[ «client_endpoint» ] . $arParams[ ‘method’ ] . ‘.’ . static::TYPE_TRANSPORT;
if(empty($arSettings[ ‘is_web_hook’ ]) || $arSettings[ ‘is_web_hook’ ] != ‘Y’)
{
$arParams[ ‘params’ ][ ‘auth’ ] = $arSettings[ ‘access_token’ ];
}
}$sPostFields = http_build_query($arParams[ ‘params’ ]);try
{
$obCurl = curl_init();
curl_setopt($obCurl, CURLOPT_URL, $url);
curl_setopt($obCurl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($obCurl, CURLOPT_POSTREDIR, 10);
curl_setopt($obCurl, CURLOPT_USERAGENT, ‘Bitrix24 CRest PHP ‘ . static::VERSION);
if($sPostFields)
{
curl_setopt($obCurl, CURLOPT_POST, true);
curl_setopt($obCurl, CURLOPT_POSTFIELDS, $sPostFields);
}
curl_setopt(
$obCurl, CURLOPT_FOLLOWLOCATION, (isset($arParams[ ‘followlocation’ ]))
? $arParams[ ‘followlocation’ ] : 1
);
if(defined(«C_REST_IGNORE_SSL») && C_REST_IGNORE_SSL === true)
{
curl_setopt($obCurl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($obCurl, CURLOPT_SSL_VERIFYHOST, false);
}
$out = curl_exec($obCurl);
$info = curl_getinfo($obCurl);
if(curl_errno($obCurl))
{
$info[ ‘curl_error’ ] = curl_error($obCurl);
}
if(static::TYPE_TRANSPORT == ‘xml’ && (!isset($arParams[ ‘this_auth’ ]) || $arParams[ ‘this_auth’ ] != ‘Y’))//auth only json support
{
$result = $out;
}
else
{
$result = static::expandData($out);
}
curl_close($obCurl);if(!empty($result[ ‘error’ ]))
{
if($result[ ‘error’ ] == ‘expired_token’ && empty($arParams[ ‘this_auth’ ]))
{
$result = static::GetNewAuth($arParams);
}
else
{
$arErrorInform = [
‘expired_token’ => ‘expired token, cant get new auth? Check access oauth server.’,
‘invalid_token’ => ‘invalid token, need reinstall application’,
‘invalid_grant’ => ‘invalid grant, check out define C_REST_CLIENT_SECRET or C_REST_CLIENT_ID’,
‘invalid_client’ => ‘invalid client, check out define C_REST_CLIENT_SECRET or C_REST_CLIENT_ID’,
‘QUERY_LIMIT_EXCEEDED’ => ‘Too many requests, maximum 2 query by second’,
‘ERROR_METHOD_NOT_FOUND’ => ‘Method not found! You can see the permissions of the application: CRest::call(‘scope’)’,
‘NO_AUTH_FOUND’ => ‘Some setup error b24, check in table «b_module_to_module» event «OnRestCheckAuth»‘,
‘INTERNAL_SERVER_ERROR’ => ‘Server down, try later’
];
if(!empty($arErrorInform[ $result[ ‘error’ ] ]))
{
$result[ ‘error_information’ ] = $arErrorInform[ $result[ ‘error’ ] ];
}
}
}
if(!empty($info[ ‘curl_error’ ]))
{
$result[ ‘error’ ] = ‘curl_error’;
$result[ ‘error_information’ ] = $info[ ‘curl_error’ ];
}static::setLog(
[
‘url’ => $url,
‘info’ => $info,
‘params’ => $arParams,
‘result’ => $result
],
‘callCurl’
);return $result;
}
catch(Exception $e)
{
static::setLog(
[
‘message’ => $e->getMessage(),
‘code’ => $e->getCode(),
‘trace’ => $e->getTrace(),
‘params’ => $arParams
],
‘exceptionCurl’
);return [
‘error’ => ‘exception’,
‘error_exception_code’ => $e->getCode(),
‘error_information’ => $e->getMessage(),
];
}
}
else
{
static::setLog(
[
‘params’ => $arParams
],
’emptySetting’
);
}return [
‘error’ => ‘no_install_app’,
‘error_information’ => ‘error install app, pls install local application ‘
];
}/**
* Generate a request for callCurl()
*
* @var $method string
* @var $params array method params
* @return mixed array|string|boolean curl-return or error
*/public static function call($method, $params = [])
{
$arPost = [
‘method’ => $method,
‘params’ => $params
];
if(defined(‘C_REST_CURRENT_ENCODING’))
{
$arPost[ ‘params’ ] = static::changeEncoding($arPost[ ‘params’ ]);
}$result = static::callCurl($arPost);
return $result;
}/**
* @example $arData:
* $arData = [
* ‘find_contact’ => [
* ‘method’ => ‘crm.duplicate.findbycomm’,
* ‘params’ => [ «entity_type» => «CONTACT», «type» => «EMAIL», «values» => array(«info@bitrix24.com») ]
* ],
* ‘get_contact’ => [
* ‘method’ => ‘crm.contact.get’,
* ‘params’ => [ «id» => ‘$result[find_contact][CONTACT][0]’ ]
* ],
* ‘get_company’ => [
* ‘method’ => ‘crm.company.get’,
* ‘params’ => [ «id» => ‘$result[get_contact][COMPANY_ID]’, «select» => [«*»],]
* ]
* ];
*
* @var $arData array
* @var $halt integer 0 or 1 stop batch on error
* @return array
*
*/public static function callBatch($arData, $halt = 0)
{
$arResult = [];
if(is_array($arData))
{
if(defined(‘C_REST_CURRENT_ENCODING’))
{
$arData = static::changeEncoding($arData);
}
$arDataRest = [];
$i = 0;
foreach($arData as $key => $data)
{
if(!empty($data[ ‘method’ ]))
{
$i++;
if(static::BATCH_COUNT >= $i)
{
$arDataRest[ ‘cmd’ ][ $key ] = $data[ ‘method’ ];
if(!empty($data[ ‘params’ ]))
{
$arDataRest[ ‘cmd’ ][ $key ] .= ‘?’ . http_build_query($data[ ‘params’ ]);
}
}
}
}
if(!empty($arDataRest))
{
$arDataRest[ ‘halt’ ] = $halt;
$arPost = [
‘method’ => ‘batch’,
‘params’ => $arDataRest
];
$arResult = static::callCurl($arPost);
}
}
return $arResult;
}/**
* Getting a new authorization and sending a request for the 2nd time
*
* @var $arParams array request when authorization error returned
* @return array query result from $arParams
*
*/private static function GetNewAuth($arParams)
{
$result = [];
$arSettings = static::getAppSettings();
if($arSettings !== false)
{
$arParamsAuth = [
‘this_auth’ => ‘Y’,
‘params’ =>
[
‘client_id’ => $arSettings[ ‘C_REST_CLIENT_ID’ ],
‘grant_type’ => ‘refresh_token’,
‘client_secret’ => $arSettings[ ‘C_REST_CLIENT_SECRET’ ],
‘refresh_token’ => $arSettings[ «refresh_token» ],
]
];
$newData = static::callCurl($arParamsAuth);
if(isset($newData[ ‘C_REST_CLIENT_ID’ ]))
{
unset($newData[ ‘C_REST_CLIENT_ID’ ]);
}
if(isset($newData[ ‘C_REST_CLIENT_SECRET’ ]))
{
unset($newData[ ‘C_REST_CLIENT_SECRET’ ]);
}
if(isset($newData[ ‘error’ ]))
{
unset($newData[ ‘error’ ]);
}
if(static::setAppSettings($newData))
{
$arParams[ ‘this_auth’ ] = ‘N’;
$result = static::callCurl($arParams);
}
}
return $result;
}/**
* @var $arSettings array settings application
* @var $isInstall boolean true if install app by installApp()
* @return boolean
*/private static function setAppSettings($arSettings, $isInstall = false)
{
$return = false;
if(is_array($arSettings))
{
$oldData = static::getAppSettings();
if($isInstall != true && !empty($oldData) && is_array($oldData))
{
$arSettings = array_merge($oldData, $arSettings);
}
$return = static::setSettingData($arSettings);
}
return $return;
}/**
* @return mixed setting application for query
*/private static function getAppSettings()
{
if(defined(«C_REST_WEB_HOOK_URL») && !empty(C_REST_WEB_HOOK_URL))
{
$arData = [
‘client_endpoint’ => C_REST_WEB_HOOK_URL,
‘is_web_hook’ => ‘Y’
];
$isCurrData = true;
}
else
{
$arData = static::getSettingData();
$isCurrData = false;
if(
!empty($arData[ ‘access_token’ ]) &&
!empty($arData[ ‘domain’ ]) &&
!empty($arData[ ‘refresh_token’ ]) &&
!empty($arData[ ‘application_token’ ]) &&
!empty($arData[ ‘client_endpoint’ ])
)
{
$isCurrData = true;
}
}return ($isCurrData) ? $arData : false;
}/**
* Can overridden this method to change the data storage location.
*
* @return array setting for getAppSettings()
*/protected static function getSettingData()
{
$return = [];
if(file_exists(__DIR__ . ‘/settings.json’))
{
$return = static::expandData(file_get_contents(__DIR__ . ‘/settings.json’));
if(defined(«C_REST_CLIENT_ID») && !empty(C_REST_CLIENT_ID))
{
$return[‘C_REST_CLIENT_ID’] = C_REST_CLIENT_ID;
}
if(defined(«C_REST_CLIENT_SECRET») && !empty(C_REST_CLIENT_SECRET))
{
$return[‘C_REST_CLIENT_SECRET’] = C_REST_CLIENT_SECRET;
}
}
return $return;
}/**
* @var $data mixed
* @var $encoding boolean true — encoding to utf8, false — decoding
*
* @return string json_encode with encoding
*/
protected static function changeEncoding($data, $encoding = true)
{
if(is_array($data))
{
$result = [];
foreach ($data as $k => $item)
{
$k = static::changeEncoding($k, $encoding);
$result[$k] = static::changeEncoding($item, $encoding);
}
}
else
{
if($encoding)
{
$result = iconv(C_REST_CURRENT_ENCODING, «UTF-8//TRANSLIT», $data);
}
else
{
$result = iconv( «UTF-8″,C_REST_CURRENT_ENCODING, $data);
}
}return $result;
}/**
* @var $data mixed
* @var $debag boolean
*
* @return string json_encode with encoding
*/
protected static function wrapData($data, $debag = false)
{
if(defined(‘C_REST_CURRENT_ENCODING’))
{
$data = static::changeEncoding($data, true);
}
$return = json_encode($data, JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT);if($debag)
{
$e = json_last_error();
if ($e != JSON_ERROR_NONE)
{
if ($e == JSON_ERROR_UTF8)
{
return ‘Failed encoding! Recommended ‘UTF — 8′ or set define C_REST_CURRENT_ENCODING = current site encoding for function iconv()’;
}
}
}return $return;
}/**
* @var $data mixed
* @var $debag boolean
*
* @return string json_decode with encoding
*/
protected static function expandData($data)
{
$return = json_decode($data, true);
if(defined(‘C_REST_CURRENT_ENCODING’))
{
$return = static::changeEncoding($return, false);
}
return $return;
}/**
* Can overridden this method to change the data storage location.
*
* @var $arSettings array settings application
* @return boolean is successes save data for setSettingData()
*/protected static function setSettingData($arSettings)
{
return (boolean)file_put_contents(__DIR__ . ‘/settings.json’, static::wrapData($arSettings));
}/**
* Can overridden this method to change the log data storage location.
*
* @var $arData array of logs data
* @var $type string to more identification log data
* @return boolean is successes save log data
*/public static function setLog($arData, $type = »)
{
$return = false;
if(!defined(«C_REST_BLOCK_LOG») || C_REST_BLOCK_LOG !== true)
{
if(defined(«C_REST_LOGS_DIR»))
{
$path = C_REST_LOGS_DIR;
}
else
{
$path = __DIR__ . ‘/logs/’;
}
$path .= date(«Y-m-d/H») . ‘/’;if (!file_exists($path))
{
@mkdir($path, 0775, true);
}$path .= time() . ‘_’ . $type . ‘_’ . rand(1, 9999999) . ‘log’;
if(!defined(«C_REST_LOG_TYPE_DUMP») || C_REST_LOG_TYPE_DUMP !== true)
{
$jsonLog = static::wrapData($arData);
if ($jsonLog === false)
{
$return = file_put_contents($path . ‘_backup.txt’, var_export($arData, true));
}
else
{
$return = file_put_contents($path . ‘.json’, $jsonLog);
}
}
else
{
$return = file_put_contents($path . ‘.txt’, var_export($arData, true));
}
}
return $return;
}/**
* check minimal settings server to work CRest
* @var $print boolean
* @return array of errors
*/
public static function checkServer($print = true)
{
$return = [];//check curl lib install
if(!function_exists(‘curl_init’))
{
$return[‘curl_error’] = ‘Need install curl lib.’;
}//creat setting file
file_put_contents(__DIR__ . ‘/settings_check.json’, static::wrapData([‘test’=>’data’]));
if(!file_exists(__DIR__ . ‘/settings_check.json’))
{
$return[‘setting_creat_error’] = ‘Check permission! Recommended: folders: 775, files: 664’;
}
unlink(__DIR__ . ‘/settings_check.json’);
//creat logs folder and files
$path = __DIR__ . ‘/logs/’.date(«Y-m-d/H») . ‘/’;
if(!mkdir($path, 0775, true) && !file_exists($path))
{
$return[‘logs_folder_creat_error’] = ‘Check permission! Recommended: folders: 775, files: 664’;
}
else
{
file_put_contents($path . ‘test.txt’, var_export([‘test’=>’data’], true));
if(!file_exists($path . ‘test.txt’))
{
$return[‘logs_file_creat_error’] = ‘check permission! recommended: folders: 775, files: 664’;
}
unlink($path . ‘test.txt’);
}if($print === true)
{
if(empty($return))
{
$return[‘success’] = ‘Success!’;
}
echo ‘

';
				print_r($return);
				echo '

‘;}return $return;
}
}

  1. settings.php (здесь нам понадобится адрес вебхука, который мы создали в битриксе)
				
					
				
			

На этом все, переходим к Тильде.

Настройка вебхука на Тильде

В Тильде переходим в Настройки сайта —> Формы —> Другое —> Webhook.

Настройки сайта

Формы

Другое-вебхук

Вводим адрес, по которому лежит файлик Tilda.php, который мы создали ранее, и нажимаем Добавить.

Добавить вебхук

После сохранения система предложит добавить приемщик данных ко всем формам на сайте. Нажмите Добавить, либо сами отметьте в формах только что созданный вебхук.

Добавить ко всем формам

После нажатия кнопки система перекинет вас в подключенные сервисы. Заходим в настройки созданного вебхука.

Настройки вебхука

Отметьте опцию Передавать данные по товарам в заказе – массивом, а также укажите название в списке (так будет называться вебхук в формах Тильды). Нажмите Сохранить.

Настройки вебхука

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

Корзина

Установите галочку на вебхуке и нажмите Сохранить и закрыть.

Подключенные сервисы

Теперь необходимо опубликовать изменения.

Опубликовать

Все готово, можно тестировать.

Результат

Заказ

Спасибо

В битриксе появляется сделка с контактными данными и товарами.

Сделка

Товары

Читайте также

Текст по кругу в Тильде

Онлайн школы

Вера


26.06.2022


2 комментария

Генерация ссылки

Битрикс 24

Вера


02.06.2022


Комментариев нет

Интеграция Б24 со службами доставки

Битрикс 24

Вера


13.03.2022


Комментариев нет

Отправка письма после покупки в Тильде

Онлайн школы

Вера


27.02.2022


Комментариев нет

Pic

Matt Pears
2 Hours

How likely are you to recommend our company to your friends and family?

3 minutes
You

Pic

Hey there, we’re just writing to let you know that you’ve been subscribed to a repository on GitHub.

Pic

Matt Pears
40 seconds

Ok, Understood!

Just now
You

Pic

You’ll receive notifications for all issues, pull requests!

Pic

Matt Pears
40 seconds

You can unwatch this repository immediately by clicking here:
https://github.com

Just now
You

Pic

Discover what students who viewed Learn Figma — UI/UX Design. Essential Training also viewed

Pic

Matt Pears
40 seconds

Most purchased Business courses during this sale!

Just now
You

Pic

Company BBQ to celebrate the last quater achievements and goals. Food and drinks provided

Битрикс для разработчиков

Битрикс для разработчиков

Andrei-Kolomenkov



Andrei
Kolomenkov

в mozila всё ок,работает,а в Chrome пишет No install app, pls install local application, при это токены свежие, в чем может быть дело в случае с chrome ?

russian

software

it

bitrix

06:05 13.10.2022


2

ответов

Андрей-



Андрей


«есть решение, но оно не работает. в чем проблема?» — лично я ничего не понял из описания. я бы начал искать что за плагин нужен и как это обойти

06:12 13.10.2022

Andrew-Zahalski



Andrew
Zahalski

Посмотри консоль, может установка финализируется на js а он троит

06:59 13.10.2022

Для расширения функциональности Bitrix24 удобно использовать приложения. В данной статье описано создание с нуля локального serverless приложения.

Для установки нашего приложения нам понадобится собственно портал bitrix24, в котором мы обладаем правами администратора или правом установки и редактирования приложений.

Если такого портала нет — создать его можно здесь.

Панель управления приложениями доступна по адресу https://<your-portal-name>.bitrix24.com/marketplace/local/.

Выбираем пункт Для личного использования. После нажатия кнопки Добавить мы попадаем в диалог создания приложения.

Здесь нам нужно будет остановится, так как добавлять пока нечего. Оставим вкладку браузера открытой и приступим к созданию нашего приложения.

Официальная javascript-библиотека

Создадим папку с произвольным названием и в ней единственный пока файл index.html со следующим содержанием (исходный код):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Bitrix24 app tutorial</title>
    <!-- подключаем библиотеку BX24 -->
    <script src="https://api.bitrix24.com/api/v1/"></script>
  </head>
  <body>
    <script>
      /**
      * передаем методу  `init` в качестве параметра callback-функцию 
      * - наше приложение 
      */
      BX24.init(app);

      function app() {
        const initDate = BX24.getAuth();
        console.log("ititDate: ", initDate);
      }
    </script>
  </body>
</html>

Помещаем файл index.html в zip-архив и указываем этот архив в качестве значения поля Загрузите архив с вашим приложением (zip)* в диалоге создания приложения.
Затем нажимаем кнопку «Сохранить»

Посмотрим, что у нас получилось.

Кликаем по Перейти к приложению и видим… пустое место на месте нашего приложения.

Все необходимое для нас на данном этапе находится сейчас в консоли разработчика.

Мы видим, что наше приложение успешно получило данные необходимые для авторизации.

Официальная javascript-библиотека c promise

Использование callback-функций имеет свои преимущества, но не всем нравится или не всегда подходит к ситуации.
Поэтому попробуем получить тот же результат в promise-стиле. Для этого изменим наш index.html (исходный код)

   <body>
     <script>
       /**
-      * передаем методу  `init` в качестве параметра callback-функцию 
-      * - наше приложение 
-      */
-      BX24.init(app);
+       * оборачиваем метод  `init` в promise
+       */
+      var bx24Promise = new Promise(function(resolve, reject) {
+        try {
+          BX24.init(resolve);
+        } catch (err) {
+          resolve(err);
+        }
+      });
+
+      bx24Promise
+        .then(function() {
+          app();
+        })
+        })
+        .catch(function(err) {
+          console.error(err);
+        });

После этого переходим https://<your-portal-name>.bitrix24.com/marketplace/local/list/.
Переходим к редактированию нашего приложения. Измененный файл index.html помещаем в zip-архив и обновляем его в нашем приложении.
Смотрим результат — все работает.
Если откроем файл index.html в браузере локально, то увидим, что обработка ошибок тоже работает.

Неофициальная библиотека BX24

Если вы планируете писать приложение на typescript (можно использовать и с javascript) и/или обладаете умеренным авантюризмом, то можно попробовать использовать сторонние библиотеки для авторизации.
Например эту.
В этом случае наш index.html нужно будет изменить следующим образом (исходный код):

     <meta http-equiv="X-UA-Compatible" content="ie=edge" />
     <title>Bitrix24 app tutorial</title>
     <!-- подключаем библиотеку BX24 -->
-    <script src="https://api.bitrix24.com/api/v1/"></script>
+    <script src="https://unpkg.com/bx24@latest/lib/index.js"></script>
   </head>
   <body>
     <script>
-      /**
-       * оборачиваем метод  `init` в promise
-       */
-      var bx24Promise = new Promise(function(resolve, reject) {
-        try {
-          BX24.init(resolve);
-        } catch (err) {
-          resolve(err);
-        }
-      });
+        var bx24 = new BX24();

-      bx24Promise
-        .then(function() {
-          app();
+        bx24.getAuth()
+        .then(function(auth) {
+            console.log(auth);
         })
-        .catch(function(err) {
-          console.error(err);
-        });
-
-      function app() {
-        const initDate = BX24.getAuth();
-        console.log("ititDate: ", initDate);
-      }
     </script>
   </body>
 </html>

Опять архивируем, опять обновляем наше приложение, опять смотрим, опять все работает.

Инструменты разработки

Полагаю, что к этому времени вы уже достаточно устали от процедуры архивирования и обновления приложения.
Попробуем сделать процесс разработки чуть более удобным и быстрым.
Для этого нам понадобится nodejs .
Проверить его наличие на компьютере можно выполнив:

node -v 

В папке нашего проекта инициализируем npm:

npm init -y

Установим необходимые пакеты:

npm i axios react react-dom bx24
npm i -D parcel-bundler express

Можно аналогично создать проект с помощью create-react-ap или angular-cli.

Состояние проекта после всех изменений можно посмотреть здесь.

Создадим в корне нашего проекта файл server.js

const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const www = process.env.WWW || './dist';
app.use(express.static(www));
console.log(`serving ${www}`);
app.get('*', (req, res) => {
  res.sendFile(`index.html`, { root: www });
});
app.post('*', (req, res) => {
  res.sendFile(`index.html`, { root: www });
});
app.listen(port, () => console.log(`listening on http://localhost:${port}`));

Этот файл нам нужен для запуска сервера. Встроенные в create-react-app и angular-cli серверы не подойдут, так как сервер стороннего приложения должен отвечать на POST запросы. Для всех, кроме 127.0.0.1 есть еще требования к наличию https.

Создадим папки src и public
В папку public перенесем index.html и изменим его содержимое на:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Bitrix24 app tutorial</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="../src/index.js">
    </script>
  </body>
</html>

В папке src создадим файлы

// src/index.js
import App from './app/app';
import React from "react";
import ReactDOM from "react-dom";

import "./css/styles.css";

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

// app/app.js
import React, { Component } from "react";
import getCurrentUser from "./services/get-current-user.service";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: true
    };
    getCurrentUser().then(currentUser => {
      this.setState({
        user: currentUser,
        loading: false
      });
    });
  }
  render() {
    if (!this.state.loading) {
      return (
        <div className="App">
          <h1>
            Hello {this.state.user.LAST_NAME} {this.state.user.NAME}
          </h1>
          <h2>Start editing to see some magic happen!</h2>
        </div>
      );
    } else {
      return (
        <div>Загрузка...</div>
      )
    }
  }
}

export default App;

// app/services/get-current-user.service.js
import { BX24 } from "bx24";
import axios from "axios";

function getCurrentUser() {
  const bx24 = new BX24();
  const urlParams = new URLSearchParams(window.location.search);
  const baseUrl = `
    ${urlParams.get("PROTOCOL") === 0 ? "https" : "http"}://${urlParams.get("DOMAIN")}
`;

  const currentUserPromise = new Promise((resolve, reject) => {
    bx24.getAuth().then(auth => {
      axios({
        method: "get",
        url: baseUrl + "/rest/user.current?auth=" + auth.ACCESS_TOKEN
      }).then(response => {
        resolve(response.data.result);
      });
    });
  });
  return currentUserPromise;
}

export default getCurrentUser;

Если package.json еще не создан, выполним:

npm init -y

Добавим скрипты в package.json:

"scripts": {
    //
    "start": "node server.js",
    "watch": "parcel watch ./public/index.html"
}

Далее так как и команда start и команда watch не заканчиваются, их нужно запускать параллельно. Для этого в двух командных строках запускаем

npm run watch

и

npm run start

Завершим настройку среды разработки редактированием нашего приложения в Bitrix24.
Перейдем в диалог редактирования нашего приложения и укажем в поле
Укажите ссылку* значение http://127.0.0.1:3000/

Перейдите к просмотру вашего приложения:
Вы должны увидеть приветствие с именем текущего пользователя:

Если использовать официальную библиотеку, то отличаться будут только два файла:

// src/app/serviced/get-current-user.service.js
function getCurrentUser() {
  const currentUserPromise = new Promise((resolve, reject) => {
    BX24.init(function() {
      BX24.callMethod("user.current", {}, function(res) {
        resolve(res.answer.result);
      });
    });
  });
  return currentUserPromise;
}

export default getCurrentUser;

и

<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Bitrix24 app tutorial</title>
    <script src="https://api.bitrix24.com/api/v1/"></script>
  </head>
  <body>
    <div id="root"></div>
    <script src="../src/index.js">
    </script>
  </body>
</html>

Итоговый код проекта для использования официальной библиотеки здесь.

Ознакомиться со всеми возможными методами и возможностями API можно здесь.

Исходный код можно увидеть здесь.

И последнее замечание. Описанные выше способы и методы не являются набором лучших практик. Это скорее предложение к конструктивному обсуждению.

UPD: желающих высказаться о 1С-Битрикс или Битрикс24 прошу сделать небольшое интеллектуальное усилие и осознать, что статья не о Битрикс24 и совсем не о 1С-Битрикс.
Это если в Питере прохожий объясняет другому, как пройти к Петропавловской крепости и тут третий вмешивается с репликой:
«Да тиран был ваш Петр I. Тиран и деспот. И усы у него дурацкие».

Если есть конструктивные замечания к коду в СТАТЬЕ или к подходам или к используемым паттернам — добро пожаловать.

Добрый день.

Изучаю приложения Bitrix. Есть у них т.н. «приложения первого типа» — содержат обычные файлы html и js, общаются с API Битрикса с помощью специальной JS библиотеки. Проблем с добавлением самого приложения нет, проблем с обращением к данным Битрикса тоже нет — я могу спокойно получить список контактов или клиентов и т.д.

Вопрос в следующем — как встроить пункт вызова этого приложения в меню, к примеру, контакта?

Пошерстил руководство, нашел метод REST API — placement.bind. Нашел уроки, но все они касаются внешних приложений — с использованием access_token авторизации через OAth… Также смущает обязательность использования параметра HANDLER — в мануале это URL обработчика (по сути внешнего приложения), а мой обработчик тут же вот, во фрейме самого Битрикс.

В описании самого битрикс сказано, что все токены разрешений передаются приложению первого типа в момент вызова. Подозреваю, что требуется использовать файл install.html с вызовом методов Битрикса для встраивания приложения, но поиск в сети не даёт примеров хотя бы в общем виде.

Запрос в поддержку Битрикс отправлял, ответа от них пока что не поступало.

bitrix-tools / crest
Goto Github
PK

View Code? Open in Web Editor
NEW

92.0
20.0
60.0
22 KB

Tiny PHP SDK to call Bitrix24 REST methods via webhooks and OAuth 2.0

License: MIT License

PHP 100.00%

crest’s Introduction

English | Русский

@bitrix/crest — small PHPSDK used for Bitrix24 REST API for webhooks, local or public applications.

Contents

  1. Description
  2. Calling using inbound webhook
  3. Calling from local application
  4. Calling from public application

Description

  • Provided examples use cURL module to run REST requests. You need to learn how to enable cURL module on your server.
  • Your web server must have valid SSL certificate.
  • Examples use base SDK as CRest class for running requests and extending authentication tokens. Download files from repository and implement necessary authorization updates in settings.php file and place it on your server, inserting an example you need from documentation.
  • When SDK errors occur, you can open file checkserver.php in browser; it performs minimum server settings check to enable CRest class operation.
  • When project uses CRest class and encoding is not UTF-8, there are 2 additional actions:

    Open files from archive and update the encoding to the required one.

    Declare the constant C_REST_CURRENT_ENCODING in the settings.php file. For example, when project has encoding windows-1251, the constant must look as follows:

define('C_REST_CURRENT_ENCODING','windows-1251');

Calling REST using inbound webhook

Indicate webhook URL in define C_REST_WEB_HOOK_URL inside the settings.php file:

define('C_REST_WEB_HOOK_URL','https://xxx.bitrix24.ru/rest/1/douasdqdsxSWgc3mgc1/');

Insert example text into the index.php file:

require_once('src/crest.php');

// put an example below
echo '<PRE>';
print_r(CRest::call(
   'crm.lead.add',
   [
      'fields' =>[
          'TITLE' => 'Lead name',//Title*[string]
          'NAME' => 'Name',//Name[string]
          'LAST_NAME' => 'Last name',//Last name[string]
      ]
   ])
);

echo '</PRE>';

Indicate example URL in the browser address bar https://example.com/index.php to see the example results.

Calling REST from local application

Insert example text into the index.php file:

require_once('src/crest.php');

// put an example below
echo '<PRE>';
print_r(CRest::call(
   'crm.lead.add',
   [
      'fields' =>[
          'TITLE' => 'Lead name',//Title*[string]
          'NAME' => 'Name',//Name[string]
          'LAST_NAME' => 'Last name',//Last name[string]
      ]
   ])
);

echo '</PRE>';

Indicate your app URL https://example.com/index.php as well as installation script URL https://example.com/install.php inside the local app details form.
Indicate parameters client_id and client_secret for OAuth 2.0 authorization in define C_REST_CLIENT_ID and C_REST_CLIENT_SECRET inside the settings.php file.
Take these values from the local app details form.

In the list of local applications, click on your local app and select “Reinstall”. It’s required for install.php to operate correctly after you have indicated correct values C_REST_CLIENT_ID and C_REST_CLIENT_SECRET.
After installation is complete, you will see example results. When the example demonstrates widget embedding into other Bitrix24 tools, switch to these tools.

Calling REST from public application

Insert example text into the index.php file:

require_once('src/crest.php');

// put an example below
echo '<PRE>';
print_r(CRest::call(
   'crm.lead.add',
   [
      'fields' =>[
          'TITLE' => 'Lead name',//Title*[string]
          'NAME' => 'Name',//Name[string]
          'LAST_NAME' => 'Last name',//Last name[string]
      ]
   ])
);

echo '</PRE>';

Add a public application inside the partner’s account to get client_id and client_secret as well as when saving an application. Indicate parameters client_id and client_secret OAuth 2.0 authorization in define C_REST_CLIENT_ID and C_REST_CLIENT_SECRET in the settings.php file.
Select and indicate your application URL https://example.com/index.php and installation script URL https://example.com/install.php inside the app details form. After saving the version, open the version details and install your app to any available Bitrix24 by clicking on «Install». After installation is complete, you will see example results (when the example demonstrates widget embedding into other Bitrix24 tools, switch to these tools). For actual public app operation, you must inherit CRest class by redefining methods getSettingData/setSettingData that get/save authentication tokens in text file. These methods are not designed for application working on several Bitrix24 simultaneously.



@bitrix/crest — небольшой PHPSDK для использования REST API Битрикс24 в локальных,
тиражных приложениях или через вебхуки

Содержание

  1. Описание
  2. Вызов при помощи входящего вебхука
  3. Вызов из локального приложения
  4. Вызов из тиражного приложения

Описание

  • Примеры используют модуль cURL для выполнения REST-запросов. Узнайте, как включить модуль cURL на своем сервере.
  • На вашем веб-сервере должен быть установлен валидный SSL-сертификат.
  • Примеры используют базовый SDK в виде класса CRest для выполнения запросов и продления токенов авторизации. Скачайте файлы из репозитория внесите необходимые правки, связанные с авторизацией в файл settings.php и разместите на своем сервере, вставив нужный вам пример из документации.
  • При возникновении проблем с работой SDK вы можете открыть через браузер файл checkserver.php, который произведёт минимальную проверку настроек сервера для работы класса CRest.
  • Если в проекте используется класс CRest и кодировка отличается от utf8, то необходимо сделать 2 дополнительных действия:

    Открыть файлы из архива и изменить их кодировку на необходимую.

    В файле settings.php объявить константу C_REST_CURRENT_ENCODING. Например, если проект в кодировке windows-1251 константа выглядеть должна так:

define('C_REST_CURRENT_ENCODING','windows-1251');

Вызов REST с использованием входящего вебхука

Укажите URL вебхука в define C_REST_WEB_HOOK_URL в файле settings.php:

define('C_REST_WEB_HOOK_URL','https://xxx.bitrix24.ru/rest/1/douasdqdsxSWgc3mgc1/');

Вставьте текст примера в файл index.php:

require_once('src/crest.php');

// put an example below
echo '<PRE>';
print_r(CRest::call(
   'crm.lead.add',
   [
      'fields' =>[
          'TITLE' => 'Название лида',//Заголовок*[string]
          'NAME' => 'Имя',//Имя[string]
          'LAST_NAME' => 'Фамилия',//Фамилия[string]
      ]
   ])
);

echo '</PRE>';

Укажите URL к примеру в адресной строке браузера https://example.com/index.php, чтобы увидеть результат работы примера.

Вызов REST из локального приложения

Вставьте текст примера в файл index.php:

require_once('src/crest.php');

// put an example below
echo '<PRE>';
print_r(CRest::call(
   'crm.lead.add',
   [
      'fields' =>[
          'TITLE' => 'Название лида',//Заголовок*[string]
          'NAME' => 'Имя',//Имя[string]
          'LAST_NAME' => 'Фамилия',//Фамилия[string]
      ]
   ])
);

echo '</PRE>';

В карточке локального приложения укажите URL своего приложения https://example.com/index.php и URL скрипта установки https://example.com/install.php.
Укажите значения параметров client_id и client_secret для авторизации OAuth 2.0 в define C_REST_CLIENT_ID и C_REST_CLIENT_SECRET в файле settings.php, взяв эти значения из карточки локального приложения.

В списке локальных приложений нажмите правой кнопкой мыши на своё локальное приложение и выберите пункт «Переустановить». Это нужно чтобы корректно сработал install.php после того, как вы вставили корректные значения C_REST_CLIENT_ID и C_REST_CLIENT_SECRET.
После установки вы увидите результат работы примера. Если пример демонстрирует встраивание виджетов в другие инструменты Битрикс24, необходимо перейти в эти инструменты.

Вызов REST из тиражного приложения

Вставьте текст примера в файл index.php

require_once('src/crest.php');

// put an example below
echo '<PRE>';
print_r(CRest::call(
   'crm.lead.add',
   [
      'fields' =>[
          'TITLE' => 'Название лида',//Заголовок*[string]
          'NAME' => 'Имя',//Имя[string]
          'LAST_NAME' => 'Фамилия',//Фамилия[string]
      ]
   ])
);

echo '</PRE>';

Добавьте тиражное приложение в партнерском кабинете для получения client_id и client_secret и при сохранении приложения.
Укажите значения параметров client_id и client_secret для авторизации OAuth 2.0 в define C_REST_CLIENT_ID и C_REST_CLIENT_SECRET в файле settings.php.

В карточке приложения добавьте версию и укажите URL своего приложения https://example.com/index.php и URL скрипта установки https://example.com/install.php в карточке версии.
После сохранения версии откройте карточку версии и, нажав на ссылку «Установить на своем Битрикс24», установите свое приложение на любой доступный вам Битрикс24.
После установки вы увидите результат работы примера (в случае, если пример демонстрирует встраивание виджетов в другие инструменты Битрикс24, необходимо перейти в эти инструменты).
Для реального тиражного приложения необходимо пронаследовать класс CRest, переопределив методы getSettingData/setSettingData, которые занимается получением/сохранением токенов авторизации в текстовый файл. Эти методы не предназначены для эксплуатации приложения на нескольких Битрикс24 одновременно.

crest’s People

crest’s Issues

empty result in the case of server error

If there is a mysql error message in the curl output, the $result is empty after the json_decode, because such error message is not a valid json. There is no useful information in the response — no error, no error_information, just a pointless empty $result. That’s not acceptable.

Custom Field With Multiple Selection Doesn’t Work

Hi,

I have a custom field with multiple selection.

image

'UF_CRM_1618612593' => "246"

or

'UF_CRM_1618612593' => 246

or

'UF_CRM_1618612593' => [
          ['VALUE' => 246]
        ]

doesn’t work. How can I add contact via API to a field with multiple selection?

Garbage collector?

Почему бы не добавить garbage collector, который будет удалять лишние лог файлы. Видел клиентов, у которых логи за год накопились на 300 МБ и по 100000 с лишним файлов.

http_build_query boolean value

Т.к. используется http_build_query
Нельзя в фильтре например передать параметр false он будет преобразован в 0

разное количество данных при одном и том же запросе

Составил запрос, при помощи вашей библиотеки, при каждой отправке запроса, приходят разные данные, что я делаю не так?
мой запрос:

`function getCalls() {
$startDate = $_POST[‘startDate’];
$endDate = $_POST[‘endDate’];
$select = json_decode($_POST[‘select’]);
$departmentId = json_decode($_POST[‘users’]);
$leadID = 0;
$finish = false;
$finalResult = [];

while (!$finish)
{
    /**
     * Выполняем пока не заберем все данные, в этом случае не стоит забывать и про задержку между хитами.
     * Либо каждый раз выбираем только 50, начиная с того элемента, на котором остановилась прошлая итерация
     */

    $users = CRest::call(
        'voximplant.statistic.get',
        [
            'order' => ['ID' => 'ASC'],
            'select' => $select,
            'filter' => [
                '>ID' => $leadID,
                'PORTAL_USER_ID' => $departmentId,
                'CALL_TYPE' => 1,
                ">CALL_START_DATE" => $startDate.'T00:00:00',
                "<CALL_START_DATE" => $endDate.'T23:59:59',
            ],
            'start' => -1
        ]
    );

    if (count($users['result']) > 0)
    {
        foreach ($users['result'] as $lead)
        {
            $leadID = $lead['ID'];
            $subResult = [];
            foreach ($select as $value) {
                $subResult[$value] = $lead[$value];
            }
            $finalResult[] = $subResult;
        }
        // Do something
    }
    else
    {
        $finish = true;
    }
}

echo json_encode($finalResult);

}`

Crest getSettingData()

Hi, I’m using CRest. I have created my application in Bitrix but I’m getting the message:

Array ( [error] => no_install_app [error_information] => error install app, pls install local application )

Looking the crest.php file I see that it is trying to get information from the file settings.json:
file_exists(DIR . ‘/settings.json’)
but I don’t see this file in the project.

Do you know where I can get this file?

Support for crm.documentgenerator.document.download is missing

Trying to download a file using crm.documentgenerator.document.download does not work as this is not a JSON.
Kindly add support for that

поменяйте пожалуйста 36ю строчку, добавив проверку на существование значения

параметр event не всегда передаётся битриксом. из за этого выскакивают лишние notice, которые на пример в AJAX ответе могут всё испортить.

было
if ($_REQUEST[ ‘event’ ] == ‘ONAPPINSTALL’ && !empty($_REQUEST[ ‘auth’ ])) {
стало
if (isset($_REQUEST[ ‘event’ ]) && $_REQUEST[ ‘event’ ] == ‘ONAPPINSTALL’ && !empty($_REQUEST[ ‘auth’ ])) {

Recommend Projects

  • React photo

    React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo

    Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo

    Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo

    TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo

    Django

    The Web framework for perfectionists with deadlines.

  • Laravel photo

    Laravel

    A PHP framework for web artisans

  • D3 photo

    D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Visualization

    Some thing interesting about visualization, use data art

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo

    Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo

    Microsoft

    Open source projects and samples from Microsoft.

  • Google photo

    Google

    Google ❤️ Open Source for everyone.

  • Alibaba photo

    Alibaba

    Alibaba Open Source for everyone

  • D3 photo

    D3

    Data-Driven Documents codes.

  • Tencent photo

    Tencent

    China tencent open source team.

Понравилась статья? Поделить с друзьями:
  • Error inserting template into database
  • Error inserting data into table microsoft sql server import wizard
  • Error insert or update on table violates foreign key constraint
  • Error insert into mysql
  • Error insert has more expressions than target columns