Написал модуль, который добавляет «Госуслуги» в «социальные сервисы»:


require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
use Bitrix\Main\Context;
use Bitrix\Main\Application;
$request = Context::getCurrent()->getRequest();
$connection = Application::getInstance()->getConnection();
$result = array_values($connection->query("SHOW TABLES")->fetchAll());
if ($request->getPost('table')) {
$output = '';
foreach ($request->getPost('table') as $table) {
$showTableQuery = "SHOW CRE ATE TABLE " . $table;
$showTableResult = $connection->query($showTableQuery)->fetchAll();
foreach($showTableResult as $showTableRow) {
$output .= "\n\n" . $showTableRow["Cre ate Table"] . ";\n\n";
}
$selectQuery = "SEL ECT * FR OM " . $table;
$rows = $connection->query($selectQuery)->fetchAll();
foreach ($rows as $row) {
$tableColumnArray = array_keys($row);
$tableValueArray = array_values($row);
$output .= "\nINSERT INTO $table (";
$output .= "" . implode(", ", $tableColumnArray) . ") VALUES (";
$output .= "'" . implode("','", $tableValueArray) . "');\n";
}
//dump($output);
}
$fileName = $_SERVER['DOCUMENT_ROOT']. '/database_backup_on_' . date('y-m-d') . '.sql';
$fileHandle = fopen($fileName, 'w+');
fwrite($fileHandle, $output);
fclose($fileHandle);
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . basename($fileName));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($fileName));
ob_clean();
flush();
readfile($fileName);
unlink($fileName);
}
?>
<!DO CTYPE html>
<ht ml>
<head>
<title>Создать бекап базы</title>
<sc ript src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></sc ript>
<li nk href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
</head>
<body>
<br />
<div class="container">
<div class="row">
<h2 align="center">Создать бекап базы</h2>
<br />
<fo rm method="post" id="export_form">
<h3>Выберите таблицы для бекапа</h3>
<?php
foreach($result as $table)
{
$table = array_values($table);
?>
<div class="checkbox">
<label><input type="checkbox" class="checkbox_table" name="table[]" value="<?=$table[0];?>" /> <?=$table[0];?></label>
</div>
<?php
}
?>
<div class="form-group">
<input type="submit" name="submit" id="submit" class="btn btn-info" value="Export" />
</div>
</form>
</div>
</div>
</body>
</html>
<sc ript>
$(document).ready(function() {
$('#submit').click(function() {
var count = 0;
$('.checkbox_table').each(function() {
if($(this).is(':checked')) {
count = count + 1;
}
});
if(count > 0) {
$('#export_form').submit();
} else {
alert("Выберите как минимум одну таблицу для экспорта");
return false;
}
});
});
</sc ript>
|
use Bitrix\Main\ORM\Query\QueryHelper;
$entity = Iblock::wakeUp($iblockId)->getEntityDataClass();
$referencePropCode = 'PROCEDURE_ID'; // код св-ва тип "Привязка к элементам" которое используется для связи элементов инфоблоков
$query = $entity::query()
->setSelect([
'ID',
'NAME',
'PREVIEW_TEXT',
'PREVIEW_PICTURE' => '',
$referencePropCode .'_ID_VALUE' => $referencePropCode,
$referencePropCode . '.ELEMENT.NAME',
$referencePropCode . '.ELEMENT.COLOR.VALUE'
])
->setOrder(["ID" => "desc"])
->setFilter([])
->setLimit(10)
->setOffset(0);
$collection = QueryHelper::decompose($query);
foreach ($collection as $element) {
$element->getId();
} |
try {
if (!\Bitrix\Main\Loader::includeModule('disk')) throw new \Exception('Не подключен модуль disk!');
$driver = \Bitrix\Disk\Driver::getInstance();
$storage = $driver->getStorageByCommonId('shared_files_' . SITE_ID);
if (!$storage) throw new \Exception('Не определено хранилище!');
$folder = $storage->addFolder([
'CODE' => 'budget_base_31_2024',
'NAME' => date('Y')
], []);
if ($folder) {
echo "Директория \"{$folder->getName()}\" добавлена в хранилище \"{$storage->getName()}\"";
}
} catch (\Throwable $e) {
echo $e->getMessage();
}
|
use Bitrix\Disk\Folder;
use Bitrix\Main\Engine\CurrentUser;
try {
if (!\Bitrix\Main\Loader::includeModule('disk')) throw new \Exception('Не подключен модуль disk!');
$folder = Folder::load([
'CODE' => 'budget_base_31_2024',
]);
if (!$folder) throw new \Exception('Не определена директория на диске');
$userId = CurrentUser::get()->getId();
$result = $folder->deleteTree($userId);
if ($result) {
echo "Директория \"{$folder->getName()}\" удалена из хранилища";
}
} catch (\Throwable $e) {
echo $e->getMessage();
}
|
\Bitrix\Main\Loader::includeModule('sale');
$paymentTable = \Bitrix\Sale\PaymentCollection::getList([
'select' => ['ID', 'DATE_PAID', 'SUM'],
'filter' => [
'=PAID' => 'Y',
'PAY_SYSTEM_ID' => 12,
'>=DATE_PAID' => new \Bitrix\Main\Type\DateTime('15.07.2023 00:00:00'),
'<=DATE_PAID' => new \Bitrix\Main\Type\DateTime('30.10.2023 14:00:00'),
]
]);
$xml = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL . PHP_EOL;
$xml .= '<main>' . PHP_EOL;
while ($item = $paymentTable->fetch())
{
$timestamp = $item['DATE_PAID']->format('d.m.Y H:i:s');
$id = $item['ID'];
$sum = number_format((float)$item['SUM'], 2, '.', '');
$check = <<<XM L
<check>
<timestamp>$timestamp</timestamp>
<external_id>sell_correction_$id</external_id>
<is_bso>false</is_bso>
<correction>
<operation>sell_correction</operation>
<company>
<sno>usn_income</sno>
<inn>9718129333</inn>
<payment_address>https://example.ru/</payment_address>
</company>
<correction_info>
<type>self</type>
<base_date>2023-10-31</base_date>
<base_number>1</base_number>
<base_name>Акт</base_name>
</correction_info>
<payments>
<payment>
<type>1</type>
<sum>$sum</sum>
</payment>
</payments>
<vats>
<vat>
<type>none</type>
<sum>0</sum>
</vat>
</vats>
<cashier>Иванов Иван Иванович</cashier>
</correction>
</check>
XML;
$xml .= $check . PHP_EOL;
}
$xml .= '</main>';
echo $xml; |
Многие обратили внимание на неожиданно большое количество условно-бесплатных приложений в маркетплейсе Б24 и преогромное количество вакансий с содержанием "интеграция с 1С и внешними системами"
Есть много готовых удобных инструментов для интеграции, требующие дополнительных ресурсов. Удобные, в два-три клика. Сегодня расскажу про "30 строк кода" или новую тему для обучения в "школах программирования за месяц" "Вложи 20тр, получи 420тр" . Спасибо Равшану Намазову и его партнеру Юлию Кирюше , а то бы мне и в голову не пришла идея опубликовать статью и писать код ( )
Обмен данными через Web Сервисы ? Теперь это сделать можно легко и быстро. (1С web-service, которую может настроить выпускник ВУЗа) Со стороны Bitrix24 - не надо писать апплеты с валидацией, не надо заморачиваться с токенизацией и форматом данных и подробной документацией, тестированием. Есть FastAPI и fastBitrix24. Весь код представлен на
Для организации обмена данными между 1С и Bitrix24 с использованием PHP, FastAPI и 1С, можно следовать следующей схеме:
Схема обмена данными
Шаги реализации
В вашем бизнес-процессе Bitrix24, используйте curl для отправки данных на FastAPI приложение. Пример кода на PHP:
<?php $data = array( "name" => "Example Name", "email" => "example@example.com" ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://your-fastapi-server/api/endpoint"); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json' )); $response = curl_exec($ch); curl_close($ch); echo $response; ?> |
2. FastAPI приложение
Создайте приложение на FastAPI, которое будет принимать запросы от Bitrix24 и отправлять их в 1С. Пример кода на Python с использованием FastAPI и requests:
from fastapi import FastAPI, Request
import requests
import json
app = FastAPI()
@app.post("/api/endpoint")
async def handle_request(request: Request):
data = await request.json()
xdto_data = {
"name": data["name"],
"email": data["email"]
}
response = requests.post(
"http://your-1c-server/api/endpoint",
json=xdto_data,
headers={"Content-Type": "application/json"}
)
return {"status": "success", "1c_response": response.json()} |
3. Обработка запроса в 1С
Создайте обработчик в 1С, который будет принимать запросы и обрабатывать данные в формате XDTO. Пример кода на 1С
Процедура ОбработатьЗапрос(Запрос)
HTTPЗапрос = Новый HTTPЗапрос(Запрос);
ТелоЗапроса = HTTPЗапрос.ПолучитьТелоКакСтроку();
Данные = СтрНайти(ТелоЗапроса);
// Пример обработки данных
Имя = Данные["name"];
ЭлектроннаяПочта = Данные["email"];
// Обработка данных (сохранение в базе и т.д.)
HTTPОтвет = Новый HTTPОтвет(200);
HTTPОтвет.УстановитьТело(Новый HTTPСообщение(Новый Строка(Формат("Данные успешно получены: %1", Имя))));
Запрос.ОтправитьОтвет(HTTPОтвет);
КонецПроцедуры |
Пример схемы взаимодействия
Пример XDTO данных
Формат XDTO данных может быть любым, но для примера это может быть JSON:
{
"name": "Example Name",
"email": "example@example.com"
} |
Таким образом, схема обмена данными между 1С и Bitrix24 с использованием PHP, FastAPI и 1С будет состоять из четко определенных шагов по передаче данных между системами, обеспечивая гибкость и масштабируемость интеграции.
Весь код представлен на
# Содержимое command-every-second.sh
#!/bin/bash
command=$@
# Пробелы и косые в команде заменяются на нижние подчеркивания.
no_spaces=`echo $command | sed -e 's/\s/_/g' -e 's|/|_|g'`
# Имя файла для блокировки на время выполнения основной команды.
lockfile=/tmp/$no_spaces.lock
# Раз в секунду пытаемся поставить блокировку на команду и выполнить.
for i in {1..60}
do
/usr/bin/flock -n $lockfile $command
/bin/sleep 1
done
rm $lockfile
# Пример команды для крона.
crontab -e
* * * * * nice -n 1 ionice -c2 -n4 /var/www/www-root/data/command-every-second.sh /opt/php81/bin/php -f /var/www/www-root/data/www/example.ru/bitrix/php_interface/cron_events.php >/dev/null 2>&1
|
yum install brotli #Сборка динамических модулей для NGINX https://serverdiary.com/linux/how-to-install-and-configure-nginx-brotli/ nginx -V wget http://nginx.org/download/nginx-1.16.1.tar.gz tar zxvf nginx-1.16.1.tar.gz git clone https://github.com/google/ngx_brotli.git cd ngx_brotli/ git submodule update --init cd ../nginx-1.16.1 yum groupinstall 'Development Tools' -y yum install gcc-c++ flex bison yajl yajl-devel curl-devel curl GeoIP-devel doxygen zlib-devel yum install lmdb lmdb-devel libxml2 libxml2-devel ssdeep ssdeep-devel lua lua-devel yum install pcre-devel yum install openssl-devel ./configure --add-dynamic-module=../ngx_brotli ...сюда копируем опции сборки из вывода nginx -V... make modules cp objs/ngx_http_brotli_static_module.so /etc/nginx/modules cp objs/ngx_http_brotli_filter_module.so /etc/nginx/modules chmod 644 /etc/nginx/modules/ngx_http_brotli_static_module.so chmod 644 /etc/nginx/modules/ngx_http_brotli_filter_module.so |
vi /etc/nginx/nginx.conf
......
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
......
server {
.......
.......
brotli on;
brotli_static on; # for static compression
brotli_comp_level 6; # this setting can vary from 1-11
brotli_types text/xml image/svg+xml application/x-font-ttf image/vnd.microsoft.icon application/x-font-opentype application/json font/eot application/vnd.ms-fontobject application/javascript font/otf application/xml application/xhtml+xml text/javascript application/x-javascript text/plain application/x-font-truetype application/xml+rss image/x-icon font/opentype text/css image/x-win-bitmap;
gzip on;
.......
.......
}
.......
nginx -t
nginx -s reload
|
yum install libwebp-tools
vi ~/webp-convert.sh
# Содержимое ~/webp-convert.sh
#!/bin/bash
# Скрипт для первоначальной конвертации картинок в конкретной директории.
# converting JPEG images
find $1 -type f -and \( -iname "*.jpg" -o -iname "*.jpeg" \) \
-exec bash -c '
webp_path=$(sed 's/\.[^.]*$/.webp/' <<< "$0");
if [ ! -f "$webp_path" ]; then
cwebp -quiet -q 90 "$0" -o "$webp_path";
jpg_size=$(wc -c "$0" | cut -d " " -f 1);
webp_size=$(wc -c "$webp_path" | cut -d " " -f 1);
if [ $jpg_size -lt $webp_size ]; then
if [ -f "$webp_path" ]; then
$(rm -f "$webp_path");
fi;
fi;
fi;' {} \;
# converting PNG images
find $1 -type f -and -iname "*.png" \
-exec bash -c '
webp_path=$(sed 's/\.[^.]*$/.webp/' <<< "$0");
if [ ! -f "$webp_path" ]; then
cwebp -quiet -lossless "$0" -o "$webp_path";
png_size=$(wc -c "$0" | cut -d " " -f 1);
webp_size=$(wc -c "$webp_path" | cut -d " " -f 1);
if [ $png_size -lt $webp_size ]; then
if [ -f "$webp_path" ]; then
$(rm -f "$webp_path");
fi;
fi;
fi;' {} \;
chmod a+x ~/webp-convert.sh
/var/www/www-root/data/webp-convert.sh /var/www/www-root/data/www/example.ru/upload
|
yum install epel-release
yum install inotify-tools
vi /etc/webp_convert.conf
# Содержимое /etc/webp_convert.conf
MONITOR=/var/www/www-root/data/www/example.ru/
# Пустой файл convert_webp.log создавался заранее.
LOG_FILE=/var/www/www-root/data/www/example.ru/logs/convert_webp.log
WEB_SERVER_USER=www-root:www-root
# Изначально сервис конвертировал картинки в webp.
# Позже было добавлено сжатие brotli, а название сервиса не поменялось.
vi /etc/rc.d/init.d/webp_convert.sh
# Содержимое /etc/rc.d/init.d/webp_convert.sh
#!/bin/bash
# webp_convert: Start/Stop convertation images to webp
#
# chkconfig: - 80 20
# description: use inotifywait for track and convert images to webp.
#
# processname: webp_convert
. /etc/rc.d/init.d/functions
. /etc/webp_convert.conf
LOCK=/var/lock/subsys/webp_convert
RETVAL=0
watching() {
/usr/bin/inotifywait \
-q -m -r --format '%e %w%f' \
-s $LOG_FILE \
-e close_write -e moved_from -e moved_to -e delete $MONITOR \
| grep -i -E '\.(jpe?g|png|js|css)$' --line-buffered \
| while read operation path; do
ts=$(date +"%C%y%m%d%H%M%S")
echo "$ts :: $operation :: $path" >> $LOG_FILE;
webp_path="$(sed 's/\.[^.]*$/.webp/' <<< "$path")";
brotli_path="${path}.br";
if [ $operation = "MOVED_FROM" ] || [ $operation = "DELETE" ]; then # if the file is moved or deleted
if [ $(grep -i -E '\.(css|js)$' <<< "$path") ]; then
if [ -f "$brotli_path" ]; then
$(rm -f "$brotli_path");
fi;
else
if [ -f "$webp_path" ]; then
$(rm -f "$webp_path");
fi;
fi;
elif [ $operation = "CLOSE_WRITE,CLOSE" ] || [ $operation = "MOVED_TO" ]; then # if new file is created
if [ $(grep -i '\.png$' <<< "$path") ]; then
$(cwebp -quiet -lossless "$path" -o "$webp_path");
$(chown "$WEB_SERVER_USER" "$webp_path");
png_size=$(wc -c "$path" | cut -d " " -f 1);
webp_size=$(wc -c "$webp_path" | cut -d " " -f 1);
if [ $png_size -lt $webp_size ]; then
if [ -f "$webp_path" ]; then
$(rm -f "$webp_path");
fi;
fi;
elif [ $(grep -i -E '\.(css|js)$' <<< "$path") ]; then
$(brotli -q 11 -k -f "$path");
$(chown "$WEB_SERVER_USER" "$brotli_path");
else
$(cwebp -quiet -q 90 "$path" -o "$webp_path");
$(chown "$WEB_SERVER_USER" "$webp_path");
jpg_size=$(wc -c "$path" | cut -d " " -f 1);
webp_size=$(wc -c "$webp_path" | cut -d " " -f 1);
if [ $jpg_size -lt $webp_size ]; then
if [ -f "$webp_path" ]; then
$(rm -f "$webp_path");
fi;
fi;
fi;
fi;
done;
}
start() {
start_date=$(date +"%C%y%m%d%H%M%S");
echo "$start_date :: Setting up watches." >> $LOG_FILE;
echo "Wathing dir: $MONITOR";
echo "Log to file: $LOG_FILE";
echo "Apache user: $WEB_SERVER_USER";
echo "Starting inotifywait for webp_convert...";
watching &
status inotifywait
RETVAL=$?
[ $RETVAL -eq 0 ] && touch $LOCK
return $RETVAL
}
stop() {
echo -n $"Stopping inotifywait and webp_convert: "
killproc inotifywait
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f $LOCK
return $RETVAL
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status inotifywait
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|status|restart}"
exit 1
esac
exit $?
chmod a+x /etc/rc.d/init.d/webp_convert.sh
# По умолчанию количество watches 8192, что не хватит для работы
# Устанавливаем максимальное количество watches 524288, но помним, что один watch забирает 1Кб ОЗУ.
# Для 524288 потребуется 512 мегабайт оперативной памяти.
# https://askubuntu.com/questions/154255/how-can-i-tell-if-i-am-out-of-inotify-watches
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
/etc/rc.d/init.d/webp_convert start
chkconfig --add webp_convert
chkconfig webp_convert on
|
# https://alexey.detr.us/en/posts/2018/2018-08-20-webp-nginx-with-fallback/
http {
# ...
map $http_accept $webp_ext {
default "";
"~image\/webp" ".webp";
}
map $uri $file_ext {
default "";
"~(\.\w+)$" $1;
}
# ...
}
server {
# ...
location ~* "^(?<path>.+)\.(png|jpeg|jpg|gif)$" {
try_files $path$webp_ext $path$file_ext =404;
}
# ...
}
# В файл /etc/nginx/mime.types добавить строку
image/webp webp;
nginx -t
nginx -s reload
|
yum install redis autoconf gcc make systemctl enable redis systemctl start redis |
wget https://github.com/igbinary/igbinary/archive/refs/tags/3.2.14.zip -O igbinary.zip unzip -o ./igbinary.zip cd igbinary-3.2.14/ /opt/php81/bin/phpize ./configure --with-php-config=/opt/php81/bin/php-config make && make install #Обязательное расширение echo 'extension=igbinary.so' > /opt/php81/etc/php.d/igbinary.ini wget https://github.com/phpredis/phpredis/archive/refs/tags/5.3.7.zip -O phpredis.zip unzip -o ./phpredis.zip cd phpredis-5.3.7/ /opt/php81/bin/phpize ./configure --with-php-config=/opt/php81/bin/php-config --enable-redis-igbinary make && make install echo 'extension=redis.so' > /opt/php81/etc/php.d/redis.ini #Рестарт сервера php, в данном случае apache systemctl restart httpd |
Available serializers php, igbinary |
# путь может отличаться на сервере # не запускать из под root, лучше под своим пользователем. # !! сайт битрикса добавил в путь пробел. #ln -s ../. ./www/local local |
При использовании умного фильтра, не выдает результаты ,а окончание сайта filter/clear/apply/ ,при чем в каталоге он работает, а в другом разделе нет, есть ли идеи? if ($this->StartResultCache($arParams['CACHE_TIME'], [ ...$additionalCacheId ]))
{
// загрузка данных
$this->IncludeComponentTemplate(); // кеширование $arResult и html-шаблона
}
// установка мета-данных страницы |
if ($this->StartResultCache($arParams['CACHE_TIME'], [ ...$additionalCacheId ]))
{
// загрузка данных $arResult для кеширования
$this->EndResultCache(); // кеширование только $arResult
}
// обработка запроса пользователя
$this->IncludeComponentTemplate();
|
| IncludeComponentTemplate | EndResultCache | |
|---|---|---|
| Примеры компонентов | bitrix:catalog.section bitrix:catalog.element bitrix:news.list bitrix.news.detail | bitrix:catalog.smart.filter bitrix:menu bitrix:form.result.new (похожий подход) |
| Назначение | HTML-шаблон одинаков для большой группы пользователей | Пользователь может сделать выбор, ввести данные |
| Данные в кеше | HTML-шаблона, $arResult, $templateData | $arResult |
| Выполнение result_modifier.php и template.php | Один раз | Каждый хит |
| Параметры запроса $_REQUEST, $_GET, $_POST, $_SESSION, $USER в result_modifier.php и template.php | Передавать в параметрах вызова компонента $arParams с приведением значения по допустимому списку вариантов. Пример - вариант отображения списка или сортировка. | Без ограничений. Для вывода использовать функцию htmlspecialcharsbx() (уязвимость XSS). Рекомендуется использовать данные $arResult. |
| Глобальные переменные (например, $APPLICATION) в result_modifier.php и template.php | Сохранить данные в $arResult или $templateData. Выполнить в component_epilog.php ^1. Например, установка заголовка страницы ^2. | Без ограничений |
| Кеширование дополнительной выборки в result_modifier.php и template.php | Не требуется | Требуется |
<?php
// ... header
CIBlock::disableTagCache(2);
CIBlock::disableTagCache(3);
?><?php
$APPLICATION->IncludeComponent('bitrix:catalog', ...);
?><?php
CIBlock::enableTagCache(2);
CIBlock::enableTagCache(3);
// ... footer
|
//$_SERVER['DOCUMENT_ROOT'] = ....; use for cli
register_shutdown_function(function() {
print_r(error_get_last());
}); |
$reflection = new ReflectionClass(\Psr\Log\LoggerInterface::class); echo $reflection->getFileName(); |
{
"require": { ... },
"replace": {
"psr/log": "*",
"psr/http-message": "*"
}
} |