Как написать модуль для битрикс

Просмотров: 54891
Дата последнего изменения: 20.02.2023

Сложность урока:

2 уровень — несложные понятия и действия, но не расслабляйтесь.

3

4

5

  Создание модуля

Цитатник веб-разработчиков.

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

Партнерские модули отличаются от стандартных модулей следующим:

  • ID модуля — полный код партнерского модуля, который задается в формате: код_партнера.код_модуля.

    Часть код_партнера постоянна для партнера (задается в карточке партнера). Часть код_модуля вводится партнером при добавлении нового модуля. Эти коды должны быть алфавитно-цифровыми, но первым символом не может быть цифра, и код неким образом должен соответствовать сути модуля. Например, для модуля форума желательно задать код forum. Тогда полный код будет mycompany.forum. Использование точки для разделения кода партнера и кода модуля необходимо, иначе ваш модуль не будет виден в списке установленных решений Marketplace, а попадет в список системных модулей, что является некорректной ситуацией.

    Важно! Код модуля обязательно должен быть задан в нижнем регистре, иначе не будет работать метод IncludeModule.

    Код не должен начинаться с цифры — это может помешать установке модуля. Также запрещается использование подчеркивания «_«.

  • В файле /install/index.php кроме той информации, которая задается в любом стандартном модуле, необходимо еще указать:
  • $this->PARTNER_NAME = "Имя партнера - автора модуля"; 
    $this->PARTNER_URI = "http://www.mysite.ru";

    У клиента эта информация будет доступна в списке модулей.

Внимание! Модуль необходимо создавать в кодировке windows-1251, при установке его на сайт с кодировкой UTF-8 происходит автоматическая перекодировка.

Помните, что в Bitrix Framework принято, что версия не может быть равной 0, то есть 0.0.1 — минимальный номер версии.

Помните, что только языковые файлы из папки /ru/ конвертируются в кодировку сайта.

Примечание: Допускается наличие

обфусцированных


Обфускация (или запутывание кода) — приведение исходного кода или исполняемого кода программы к виду, сохраняющему её функциональность, но затрудняющему анализ, понимание алгоритмов работы и модификацию при декомпиляции.

Подробнее…




частей кода, при этом в описании модуля должна присутствовать информация о наличии такого кода.

  Инфоблоки или таблицы БД?

Цитатник веб-разработчиков.

Максим Месилов: Инфоблоки отлично подходят для прототипирования и макетирования функционала. На уровне своего приложения (модуля) делаете прослойку, которая отвечает за хранение данных и начинаете использовать инфоблоки.

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

При создании собственных модулей у разработчиков часто возникает вопрос: при написании собственного модуля что целесообразнее: использование инфоблоков или собственные таблицы? Ответ на этот вопрос зависит от решаемой задачи. Наличие в Bitrix Framework инфоблоков не означает обязательности их использования для реализации своих модулей.

Инфоблоки — это универсальность. По этой причине:

  • Инфоблоки часто избыточны по своим возможностям;
  • При использовании инфоблоков разработчик может работать с модулем как с обычным компонентом, не нужно дорабатывать API (и описывать его).

Собственные таблицы — это прежде всего производительность. Используя свои таблицы, разработчик:

  • может сделать модуль куда более быстродействующим, чем если бы делал модуль со стандартным API;
  • при разработке собственного API, принимает на себя всю ответственность по безопасной работе своего модуля;
  • должен подумать о том, что с его кодом будут работать другие разработчики: комментарии и документация.

  Документация по теме

  • Внедряемся в любой пункт меню в админке (блог).
  • Marketplace (группа в социальной сети компании 1С-Битрикс)
  • Пользовательские поля в собственном модуле (блог)
  • Написание модуля на D7+ORM (блог)

Наш модуль после установки добавляет на страницы сайта кнопку плавной прокрутки страницы вверх. Какие файлы и папки могут и должны быть, можно изучить в соответствующем разделе документации Битрикс. Модуль разместим в директории local/modules и создадим такую файловую структуру:

[scrollup]
    [install]
        [assets]
            [scripts]
                script.js
            [styles]
                style.css
        index.php
        step.php
        unstep.php
        version.php
    [lang]
        [ru]
            [install]
                index.php
            options.php
    [lib]
        Main.php
    include.php
    options.php

Как видите, внутри local/modules/scrollup у нас:

  • install — набор скриптов для установки и удаления модуля;
  • lang — набор языковых файлов модуля;
  • lib — набор файлов, в которых реализуется логика решения;
  • include.php — файл, подключаемый при вызове модуля в коде;
  • options.php — страница настроек, подключаемая в административной части.

Рассмотрим подробнее папку install:

  • assets — содержит JavaScript и CSS кнопки, которые будут подключаться в пользовательской части;
  • index.php — файл, в котором содержится описание модуля и реализуется его установка и удаление;
  • step.php и unstep.php — соответственно шаги установки и удаления, их может быть несколько;
  • version.php — файл содержит версию и время обновления нашего модуля.
<?php
/*
 * Файл local/modules/scrollup/install/version.php
 */

$arModuleVersion = array(
    'VERSION'      => '1.0.0',
    'VERSION_DATE' => '2018-10-20 11:00:00'
);

Теперь нам нужно написать класс, в котором будет реализована установка и удаление модуля. Имя класса должно совпадать с директорией модуля и являться наследником от CModule. Метод doInstall() вызывается при установке модуля из панели управления, метод doUninstall() — при деинсталляции модуля. Метод doInstall() подключает файл step.php, а метод doUninstall() — файл unstep.php

<?php
/*
 * Файл local/modules/scrollup/install/index.php
 */

use BitrixMainLocalizationLoc;
use BitrixMainModuleManager;
use BitrixMainConfigOption;
use BitrixMainEventManager;
use BitrixMainApplication;
use BitrixMainIODirectory;

Loc::loadMessages(__FILE__);

class scrollup extends CModule {

    public function __construct() {
        if (is_file(__DIR__.'/version.php')) {
            include_once(__DIR__.'/version.php');
            $this->MODULE_ID           = get_class($this);
            $this->MODULE_VERSION      = $arModuleVersion['VERSION'];
            $this->MODULE_VERSION_DATE = $arModuleVersion['VERSION_DATE'];
            $this->MODULE_NAME         = Loc::getMessage('SCROLLUP_NAME');
            $this->MODULE_DESCRIPTION  = Loc::getMessage('SCROLLUP_DESCRIPTION');
        } else {
            CAdminMessage::showMessage(
                Loc::getMessage('SCROLLUP_FILE_NOT_FOUND').' version.php'
            );
        }
    }
    
    public function doInstall() {

        global $APPLICATION;

        // мы используем функционал нового ядра D7 — поддерживает ли его система?
        if (CheckVersion(ModuleManager::getVersion('main'), '14.00.00')) {
            // копируем файлы, необходимые для работы модуля
            $this->installFiles();
            // создаем таблицы БД, необходимые для работы модуля
            $this->installDB();
            // регистрируем модуль в системе
            ModuleManager::registerModule($this->MODULE_ID);
            // регистрируем обработчики событий
            $this->installEvents();
        } else {
            CAdminMessage::showMessage(
                Loc::getMessage('SCROLLUP_INSTALL_ERROR')
            );
            return;
        }

        $APPLICATION->includeAdminFile(
            Loc::getMessage('SCROLLUP_INSTALL_TITLE').' «'.Loc::getMessage('SCROLLUP_NAME').'»',
            __DIR__.'/step.php'
        );
    }
    
    public function installFiles() {
        // копируем js-файлы, необходимые для работы модуля
        CopyDirFiles(
            __DIR__.'/assets/scripts',
            Application::getDocumentRoot().'/bitrix/js/'.$this->MODULE_ID.'/',
            true,
            true
        );
        // копируем css-файлы, необходимые для работы модуля
        CopyDirFiles(
            __DIR__.'/assets/styles',
            Application::getDocumentRoot().'/bitrix/css/'.$this->MODULE_ID.'/',
            true,
            true
        );
    }
    
    public function installDB() {
        return;
    }

    public function installEvents() {
        // перед выводом буферизированного контента добавим свой HTML код,
        // в котором сохраним настройки для нашей кнопки прокрутки наверх
        EventManager::getInstance()->registerEventHandler(
            'main',
            'OnBeforeEndBufferContent',
            $this->MODULE_ID,
            'ScrollUp\Main',
            'appendJavaScriptAndCSS'
        );
    }

    public function doUninstall() {

        global $APPLICATION;

        $this->uninstallFiles();
        $this->uninstallDB();
        $this->uninstallEvents();

        ModuleManager::unRegisterModule($this->MODULE_ID);

        $APPLICATION->includeAdminFile(
            Loc::getMessage('SCROLLUP_UNINSTALL_TITLE').' «'.Loc::getMessage('SCROLLUP_NAME').'»',
            __DIR__.'/unstep.php'
        );

    }

    public function uninstallFiles() {
        // удаляем js-файлы
        Directory::deleteDirectory(
            Application::getDocumentRoot().'/bitrix/js/'.$this->MODULE_ID
        );
        // удаляем css-файлы
        Directory::deleteDirectory(
            Application::getDocumentRoot().'/bitrix/css/'.$this->MODULE_ID
        );
        // удаляем настройки нашего модуля
        Option::delete($this->MODULE_ID);
    }
    
    public function uninstallDB() {
        return;
    }
    
    public function uninstallEvents() {
        // удаляем наш обработчик события
        EventManager::getInstance()->unRegisterEventHandler(
            'main',
            'OnBeforeEndBufferContent',
            $this->MODULE_ID,
            'ScrollUp\Main',
            'appendJavaScriptAndCSS'
        );
    }

}
<?php
/*
 * Файл local/modules/scrollup/lang/ru/install/index.php
 */

$MESS['SCROLLUP_NAME']              = 'Кнопка «Наверх»';
$MESS['SCROLLUP_DESCRIPTION']       = 'Добавляет на сайт кнопку плавной прокрутки наверх.';

$MESS['SCROLLUP_FILE_NOT_FOUND']    = 'Не найден файл';
$MESS['SCROLLUP_INSTALL_TITLE']     = 'Установка модуля';
$MESS['SCROLLUP_INSTALL_ERROR']     = 'Версия главного модуля ниже 14, обновите систему.';
$MESS['SCROLLUP_INSTALL_SUCCESS']   = 'Модуль успешно установлен';
$MESS['SCROLLUP_INSTALL_FAILED']    = 'Ошибка при установке модуля';

$MESS['SCROLLUP_UNINSTALL_TITLE']   = 'Удаление модуля';
$MESS['SCROLLUP_UNINSTALL_SUCCESS'] = 'Модуль успешно удален';
$MESS['SCROLLUP_UNINSTALL_FAILED']  = 'Ошибка при удалении модуля';

$MESS['SCROLLUP_RETURN_MODULES']    = 'Вернуться в список модулей';
<?php
/*
 * Файл local/modules/scrollup/install/step.php
 */

use BitrixMainLocalizationLoc;

Loc::loadMessages(__FILE__);

if (!check_bitrix_sessid()) {
    return;
}

if ($errorException = $APPLICATION->getException()) {
    // ошибка при установке модуля
    CAdminMessage::showMessage(
        Loc::getMessage('SCROLLUP_INSTALL_FAILED').': '.$errorException->GetString()
    );
} else {
    // модуль успешно установлен
    CAdminMessage::showNote(
        Loc::getMessage('SCROLLUP_INSTALL_SUCCESS')
    );
}
?>

<form action="<?= $APPLICATION->getCurPage(); ?>"> <!-- Кнопка возврата к списку модулей -->
    <input type="hidden" name="lang" value="<?= LANGUAGE_ID; ?>" />
    <input type="submit" value="<?= Loc::getMessage('SCROLLUP_RETURN_MODULES'); ?>">
</form>
<?php
/*
 * Файл local/modules/scrollup/install/unstep.php
 */

use BitrixMainLocalizationLoc;

Loc::loadMessages(__FILE__);

if (!check_bitrix_sessid()){
    return;
}

if ($errorException = $APPLICATION->getException()) {
    // ошибка при удалении модуля
    CAdminMessage::showMessage(
        Loc::getMessage('SCROLLUP_UNINSTALL_FAILED').': '.$errorException->GetString()
    );
} else {
    // модуль успешно удален
    CAdminMessage:showNote(
        Loc::getMessage('SCROLLUP_UNINSTALL_SUCCESS')
    );
}
?>

<form action="<?= $APPLICATION->getCurPage(); ?>"> <!-- Кнопка возврата к списку модулей -->
    <input type="hidden" name="lang" value="<?= LANGUAGE_ID; ?>" />
    <input type="submit" value="<?= Loc::getMessage('SCROLLUP_RETURN_MODULES'); ?>">
</form>

Теперь в панели управления на странице «Настройки • Настройки продукта • Модули» мы видим наш модуль:

И можем установить его:

Создадим страницу настроек для модуля. Через панель управления контент менеджер сможет:

  • Включать/выключать модуль;
  • Подключать библиотеку jQuery, если она еще не подключена;
  • Изменять ширину/высоту/радиус и цвет кнопки;
  • Менять положение кнопки и скорость анимации.
<?php
/*
 * Файл local/modules/scrollup/options.php
 */

use BitrixMainLocalizationLoc;
use BitrixMainHttpApplication;
use BitrixMainLoader;
use BitrixMainConfigOption;

Loc::loadMessages(__FILE__);

// получаем идентификатор модуля
$request = HttpApplication::getInstance()->getContext()->getRequest();
$module_id = htmlspecialchars($request['mid'] != '' ? $request['mid'] : $request['id']);
// подключаем наш модуль
Loader::includeModule($module_id);

/*
 * Параметры модуля со значениями по умолчанию
 */
$aTabs = array(
    array(
        /*
         * Первая вкладка «Основные настройки»
         */
        'DIV'     => 'edit1',
        'TAB'     => Loc::getMessage('SCROLLUP_OPTIONS_TAB_GENERAL'),
        'TITLE'   => Loc::getMessage('SCROLLUP_OPTIONS_TAB_GENERAL'),
        'OPTIONS' => array(
            array(
                'switch_on',                                   // имя элемента формы
                Loc::getMessage('SCROLLUP_OPTIONS_SWITCH_ON'), // поясняющий текст — «Включить прокрутку»
                'Y',                                           // значение по умолчанию «да»
                array('checkbox')                              // тип элемента формы — checkbox
            ),
            array(
                'jquery_on',                                   // имя элемента формы
                Loc::getMessage('SCROLLUP_OPTIONS_JQUERY_ON'), // поясняющий текст — «Подключить jQuery»
                'N',                                           // значение по умолчанию «нет»
                array('checkbox')                              // тип элемента формы — checkbox
            ),
        )
    ),
    array(
        /*
         * Вторая вкладка «Дополнительные настройки»
         */
        'DIV'     => 'edit2',
        'TAB'     => Loc::getMessage('SCROLLUP_OPTIONS_TAB_ADDITIONAL'),
        'TITLE'   => Loc::getMessage('SCROLLUP_OPTIONS_TAB_ADDITIONAL'),
        'OPTIONS' => array(
            /*
             * секция «Внешний вид»
             */
            Loc::getMessage('SCROLLUP_OPTIONS_SECTION_VIEW'),
            array(
                'width',                                    // имя элемента формы
                Loc::getMessage('SCROLLUP_OPTIONS_WIDTH'),  // поясняющий текст — «Ширина (пикселей)»
                '50',                                       // значение по умолчанию 50px
                array('text', 5)                            // тип элемента формы — input type="text", ширина 5 симв.
            ),
            array(
                'height',                                   // имя элемента формы
                Loc::getMessage('SCROLLUP_OPTIONS_HEIGHT'), // поясняющий текст — «Высота (пикселей)»
                '50',                                       // значение по умолчанию 50px
                array('text', 5)                            // тип элемента формы — input type="text", ширина 5 симв.
            ),
            array(
                'radius',                                   // имя элемента формы
                Loc::getMessage('SCROLLUP_OPTIONS_RADIUS'), // поясняющий текст — «Радиус (пикселей)»
                '50',                                       // значение по умолчанию 50px
                array('text', 5)                            // тип элемента формы — input type="text", ширина 5 симв.
            ),
            array(
                'color',                                    // имя элемента формы
                Loc::getMessage('SCROLLUP_OPTIONS_COLOR'),  // поясняющий текст — «Цвет фона»
                '#bf3030',                                  // значение по умолчанию #bf3030
                array('text', 5)                            // тип элемента формы — input type="text", ширина 5 симв.
            ),
            /*
             * секция «Положение на странице»
             */
            Loc::getMessage('SCROLLUP_OPTIONS_SECTION_LAYOUT'),
            array(
                'side',                                       // имя элемента формы
                Loc::getMessage('SCROLLUP_OPTIONS_POSITION'), // поясняющий текст — «Положение кнопки»
                'left',                                       // значение по умолчанию «left»
                array(
                    'selectbox',                              // тип элемента формы — <select>
                    array(
                        'left'  => Loc::getMessage('SCROLLUP_OPTIONS_SIDE_LEFT'),
                        'right' => Loc::getMessage('SCROLLUP_OPTIONS_SIDE_RIGHT')
                    )
                )
            ),
            array(
                'indent_bottom',                                   // имя элемента формы
                Loc::getMessage('SCROLLUP_OPTIONS_INDENT_BOTTOM'), // поясняющий текст — «Отступ снизу (пикселей)»
                '10',                                              // значение по умолчанию 10px
                array('text', 5)                                   // тип элемента формы — input type="text"
            ),
            array(
                'indent_side',                                     // имя элемента формы
                Loc::getMessage('SCROLLUP_OPTIONS_INDENT_SIDE'),   // поясняющий текст — «Отступ сбоку (пикселей)»
                '10',                                              // значение по умолчанию 10px
                array('text', 5)                                   // тип элемента формы — input type="text"
            ),
            /*
             * секция «Поведение»
             */
            Loc::getMessage('SCROLLUP_OPTIONS_SECTION_ACTION'),
            array(
                'speed',                                   // имя элемента формы
                Loc::getMessage('SCROLLUP_OPTIONS_SPEED'), // поясняющий текст — «Скорость прокрутки»
                'normal',                                  // значение по умолчанию «normal»
                array(
                    'selectbox',                           // тип элемента формы — <select>
                    array(
                        'slow'   => Loc::getMessage('SCROLLUP_OPTIONS_SPEED_SLOW'),
                        'normal' => Loc::getMessage('SCROLLUP_OPTIONS_SPEED_NORMAL'),
                        'fast'   => Loc::getMessage('SCROLLUP_OPTIONS_SPEED_FAST')
                    )
                )
            )
        )
    )
);

/*
 * Создаем форму для редактирвания параметров модуля
 */
$tabControl = new CAdminTabControl(
    'tabControl',
    $aTabs
);

$tabControl->begin();
?>
<form action="<?= $APPLICATION->getCurPage(); ?>?mid=<?=$module_id; ?>&lang=<?= LANGUAGE_ID; ?>" method="post">
    <?= bitrix_sessid_post(); ?>
    <?php
    foreach ($aTabs as $aTab) { // цикл по вкладкам
        if ($aTab['OPTIONS']) {
            $tabControl->beginNextTab();
            __AdmSettingsDrawList($module_id, $aTab['OPTIONS']);
        }
    }
    $tabControl->buttons();
    ?>
    <input type="submit" name="apply" 
           value="<?= Loc::GetMessage('SCROLLUP_OPTIONS_INPUT_APPLY'); ?>" class="adm-btn-save" />
    <input type="submit" name="default"
           value="<?= Loc::GetMessage('SCROLLUP_OPTIONS_INPUT_DEFAULT'); ?>" />
</form>

<?php
$tabControl->end();

/*
 * Обрабатываем данные после отправки формы
 */
if ($request->isPost() && check_bitrix_sessid()) {

    foreach ($aTabs as $aTab) { // цикл по вкладкам
        foreach ($aTab['OPTIONS'] as $arOption) {
            if (!is_array($arOption)) { // если это название секции
                continue;
            }
            if ($arOption['note']) { // если это примечание
                continue;
            }
            if ($request['apply']) { // сохраняем введенные настройки
                $optionValue = $request->getPost($arOption[0]);
                if ($arOption[0] == 'switch_on') {
                    if ($optionValue == '') {
                        $optionValue = 'N';
                    }
                }
                if ($arOption[0] == 'jquery_on') {
                    if ($optionValue == '') {
                        $optionValue = 'N';
                    }
                }
                Option::set($module_id, $arOption[0], is_array($optionValue) ? implode(',', $optionValue) : $optionValue);
            } elseif ($request['default']) { // устанавливаем по умолчанию
                Option::set($module_id, $arOption[0], $arOption[2]);
            }
        }
    }

    LocalRedirect($APPLICATION->getCurPage().'?mid='.$module_id.'&lang='.LANGUAGE_ID);

}
?>
<?php
/*
 * Файл local/modules/scrollup/lang/ru/options.php
 */

$MESS['SCROLLUP_OPTIONS_TAB_GENERAL']    = 'Основные настройки'; // Первая вкладка «Основные настройки»
$MESS['SCROLLUP_OPTIONS_SWITCH_ON']      = 'Включить прокрутку';
$MESS['SCROLLUP_OPTIONS_JQUERY_ON']      = 'Подключить jQuery';

$MESS['SCROLLUP_OPTIONS_TAB_ADDITIONAL'] = 'Дополнительные настройки'; // Вторая вкладка «Дополнительные настройки»
$MESS['SCROLLUP_OPTIONS_SECTION_VIEW']   = 'Внешний вид'; // секция «Внешний вид»
$MESS['SCROLLUP_OPTIONS_WIDTH']          = 'Ширина (пикселей)';
$MESS['SCROLLUP_OPTIONS_HEIGHT']         = 'Высота (пикселей)';
$MESS['SCROLLUP_OPTIONS_RADIUS']         = 'Радиус (пикселей)';
$MESS['SCROLLUP_OPTIONS_COLOR']          = 'Цвет фона';
$MESS['SCROLLUP_OPTIONS_SECTION_LAYOUT'] = 'Положение на странице'; // секция «Положение на странице»
$MESS['SCROLLUP_OPTIONS_POSITION']       = 'Положение кнопки';
$MESS['SCROLLUP_OPTIONS_SIDE_LEFT']      = 'Слева';
$MESS['SCROLLUP_OPTIONS_SIDE_RIGHT']     = 'Справа';
$MESS['SCROLLUP_OPTIONS_INDENT_BOTTOM']  = 'Отступ снизу (пикселей)';
$MESS['SCROLLUP_OPTIONS_INDENT_SIDE']    = 'Отступ сбоку (пикселей)';
$MESS['SCROLLUP_OPTIONS_SECTION_ACTION'] = 'Поведение'; // секция «Поведение»
$MESS['SCROLLUP_OPTIONS_SPEED']          = 'Скорость прокрутки';
$MESS['SCROLLUP_OPTIONS_SPEED_SLOW']     = 'Низкая';
$MESS['SCROLLUP_OPTIONS_SPEED_NORMAL']   = 'Средняя';
$MESS['SCROLLUP_OPTIONS_SPEED_FAST']     = 'Высокая';

$MESS['SCROLLUP_OPTIONS_INPUT_APPLY']    = 'Сохранить настройки';
$MESS['SCROLLUP_OPTIONS_INPUT_DEFAULT']  = 'Установить по умолчанию';

Страница настроек модуля теперь выглядит так:

Когда мы описывали код установки модуля, то привязались к событию OnBeforeEndBufferContent и указали метод appendJavaScriptAndCSS() класса Main как обработчик. Теперь настало время его написать:

<?php
/*
 * Файл local/modules/scrollup/lib/Main.php
 */

namespace ScrollUp;

use BitrixMainConfigOption;
use BitrixMainPageAsset;

class Main {

    public function appendJavaScriptAndCSS() {

        // прокрутка будет работать только в публичной части
        if (!defined('ADMIN_SECTION') && ADMIN_SECTION !== true) {
            $module_id = pathinfo(dirname(__DIR__))['basename'];
            $options = json_encode(
                array(
                    'switch_on'     => Option::get($module_id, 'switch_on', 'Y'),
                    'width'         => Option::get($module_id, 'width', '50'),
                    'height'        => Option::get($module_id, 'height', '50'),
                    'radius'        => Option::get($module_id, 'radius', '50'),
                    'color'         => Option::get($module_id, 'color', '#bf3030'),
                    'side'          => Option::get($module_id, 'side', 'left'),
                    'indent_bottom' => Option::get($module_id, 'indent_bottom', '10'),
                    'indent_side'   => Option::get($module_id, 'indent_side', '10'),
                    'speed'         => Option::get($module_id, 'speed', 'normal')
                )
            );
            Asset::getInstance()->addCss('/bitrix/css/'.$module_id.'/style.css');
            // подключить библиотеку jQuery?
            if (Option::get($module_id, 'jquery_on', 'N') == 'Y') {
                CJSCore::init(array('jquery2'));
            }
            Asset::getInstance()->addString(
                "<script id='".$module_id."-params' data-params='".$options."'></script>",
                true
            );
            Asset::getInstance()->addJs('/bitrix/js/'.$module_id.'/script.js');
            
        }
    }

}
<?php
/*
 * Файл local/modules/scrollup/include.php
 */

BitrixMainLoader::registerAutoloadClasses(
    'scrollup',
    array(
        'ScrollUp\Main' => 'lib/Main.php',
    )
);

Осталось только написать js-код для прокрутки страницы наверх и задать css-стили для кнопки:

/*
 * Файл local/modules/scrollup/install/assets/scripts/script.js
 */

$(function() {

    var params = $('#scrollup-params').data().params;

    if (params.switch_on == 'Y') {

        var style  = 'width: ' + params.width + 'px;';
            style += 'height: ' + params.height + 'px;';
            style += 'border-radius: ' + params.radius + 'px;';
            style += 'background-color: ' + params.color + ';';
            style += 'bottom: ' + params.indent_bottom + 'px;';
            style += params.side + ': ' + params.indent_side + 'px;';

        var speed = 600;
        if (params.speed == 'slow') {
            speed = 300;
        } else if (params.speed == 'fast'){
            speed = 1000;
        };

        $('body').append('<div id="scrollup-button" style="' + style +'"></div>');

        var button = $('#scrollup-button');
        $(window).on('load', function() {
            if ($(this).scrollTop() > 300) {
                button.fadeIn(600);
            }
        });

        $(window).on('scroll', function() {
            if ($(this).scrollTop() > 300) {
                button.fadeIn(600);
            } else {
                button.fadeOut(600);
            };
        });

        button.on('click', function() {
            $('html, body').animate({
                scrollTop: 0
            }, speed);
        });

    };

});
/*
 * Файл local/modules/scrollup/install/assets/styles/style.css
 */

#scrollup-button {
    position: fixed;
    cursor: pointer;
    display: none;
}
#scrollup-button:before {
    content: "";
    width: 0;
    height: 0;
    border-left: 15px solid transparent;
    border-right: 15px solid transparent;
    border-bottom: 25px solid #fff;
    margin: 9px auto 0;
    display: block;
}

Дополнительно

  • Скачать модуль scrollup

Поиск:
CMS • OnBeforeEndBufferContent • Web-разработка • doInstall • doUninstall • step.php • unstep.php • version.php • Битрикс • Модуль • Прокрутка • Событие • Установка • registerAutoloadClasses • registerModule • unRegisterModule • registerEventHandler • unRegisterEventHandler • CopyDirFiles • deleteDirectory

Каталог оборудования

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Производители

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Функциональные группы

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

В интернете есть много информации по тому, насколько хороша архитектура D7, но мало практической информации как, например, организовать код, какие новые возможности мы можем использовать на практике и для чего. В данной статье я опишу о том как создать модуль D7, какие основные моменты нужно учесть.

Всю теоретическую информацию о D7, вы можете найти в документации для разработчиков на сайте 1С:Битрикс. А на практике я опишу как использовать: namespace, ORM, автозагрузку классов.

Для начала создадим основную структуру модуля, состоящую из следующих папок, файлов, структура модуля D7 аналогична обычному модулю:

далее по порядку разберём файлы модуля.

Данный файл создаёт пункты меню в административном интерфейсе 1С:Битрикс.

defined('B_PROLOG_INCLUDED') and (B_PROLOG_INCLUDED === true) or die();
use BitrixMainLocalizationLoc;
Loc::loadMessages(__FILE__);
$aMenu = array(
    array(
        'parent_menu' => 'global_menu_content',
        'sort' => 400,
        'text' => "Тестовый модуль",
        'title' => "",
        'url' => 'perfmon_table.php?lang=ru&table_name=brainkit_test',
        'items_id' => 'menu_references'
    )
);
return $aMenu;
    

Файл /install/db/install.sql

В данном файле напишем запрос на создание таблицы ‘brainkit_test’ помимо основных полей ID и TITLE, добавим поля SORT и CREATED, для сортировки и сохранения даты изменения записи в таблице соответственно:

CREATE TABLE `brainkit_test` (
        `ID` INT(11) NOT NULL AUTO_INCREMENT,
        `TITLE` VARCHAR(65536) NOT NULL,
        `SORT` INT(11) DEFAULT 500,
        `CREATED` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY(ID)
    );

Файл /install/db/uninstall.sql

А здесь sql запрос на удаление созданной базы:

DROP TABLE IF EXISTS `brainkit_test`;

Файл /install/index.php

Данный файл является классом, который наследуется от класса Cmodule, выполняется при установке или удалении модуля через административный интерфейс.

В конструкторе класса указывается справочная информация о модуле: версия, дата обновления, имя и описание.

В методах класса должны обязатально присутствовать методы: DoInstall и DoUninstall, которые отрабатывают при установке и удалении модуля, а внутри них должны вызываться функции RegisterModule и UnRegisterModule для регистрации и удаления регистрации модуля в базе соответственно.

Внутри методов DoInstall/DoUninstall реализуем методы для: создания/удаления таблицы в базе, установки/удаления событий и дополнительных файлов модуля. Так как для нашего модуля не нужны события и дополнительные файлы, то эти методы просто оставим для будущего расширения возможностей модуля.

Исходный код файла:

<?

IncludeModuleLangFile(__FILE__);

use BitrixMainModuleManager;

Class Brainkit_D7 extends CModule
{

    var $MODULE_ID = "brainkit.d7";
    var $MODULE_VERSION;
    var $MODULE_VERSION_DATE;
    var $MODULE_NAME;
    var $MODULE_DESCRIPTION;
    var $errors;

    function __construct()
    {
        //$arModuleVersion = array();
        $this->MODULE_VERSION = "1.0.0";
        $this->MODULE_VERSION_DATE = "20.03.2016";https://dev.sokolov.ru/jewelry-shops/RU/Sankt-Peterburg/?country=RU&city=Sankt-Peterburg
        $this->MODULE_NAME = "Пример модуля D7";
        $this->MODULE_DESCRIPTION = "Тестовый модуль для разработчиков, можно использовать как основу для разработки новых модулей для 1С:Битрикс";
    }

    function DoInstall()
    {
        $this->InstallDB();
        $this->InstallEvents();
        $this->InstallFiles();
        RegisterModule("brainkit.d7");
        return true;
    }

    function DoUninstall()
    {
        $this->UnInstallDB();
        $this->UnInstallEvents();
        $this->UnInstallFiles();
        UnRegisterModule("brainkit.d7");
        return true;
    }

    function InstallDB()
    {
        global $DB;
        $this->errors = false;
        $this->errors = $DB->RunSQLBatch($_SERVER['DOCUMENT_ROOT'] . "/bitrix/modules/brainkit.d7/install/db/install.sql");
        if (!$this->errors) {

            return true;
        } else
            return $this->errors;
    }

    function UnInstallDB()
    {
        global $DB;
        $this->errors = false;
        $this->errors = $DB->RunSQLBatch($_SERVER['DOCUMENT_ROOT'] . "/local/modules/Brainkit.D7/install/db/uninstall.sql");
        if (!$this->errors) {
            return true;
        } else
            return $this->errors;
    }

    function InstallEvents()
    {
        return true;
    }

    function UnInstallEvents()
    {
        return true;
    }

    function InstallFiles()
    {
        return true;
    }

    function UnInstallFiles()
    {
        return true;
    }
}

Файл /lang/ru/lib/data.php

Данный файл служит для реализации переводов для файла /lib/data.php, в нём простой массив $MESS, в котором хранитяся пары ключ=>значение перевода. А часть ru в пути к файлу является указанимем на язык перевода.

После того как мы создали файл, в любой части кода модуля, для получения перевода, мы можем вызвать функцию Loc::getMessage(‘KEY’), где KEY — ключ перевода:

<?
$MESS["DATA_ENTITY_ID_FIELD"] = "ID";
$MESS["DATA_ENTITY_TITLE_FIELD"] = "Заголовок";

Файл /lib/test.php

В данном файле пристуствует namespace, который называется в соответвии будет храниться основная логика работы модуля, файл подключается в файле /include.php, для примера реализуем в данном файле получение одной записи из таблицы модуля с помощью ORM класса DataTable:

<?php

namespace BrainkitD7;

use BrainkitDataDataTable;

class Test{

    public static function get($cond) {
        $result = DataTable::getList(
                        array(
                            'select' => array('*')
        ));
        $row = $result->fetch();
        print "<pre>"; print_r($row); print "</pre>";
        return $row;
    }

}
    

Файл /lib/data.php

Данный файл служит для организации доступа к таблице с помощью ORM, в нём создаётся класс DataTable, который наследуется от класса EntityDataManager, который в свою очередь реализует парадигму ORM. Для того что бы подключить наш класс к ORM, мы должны указать имя таблицы и задать структуру, для этого есть 2 метода getTableName и getMap соответственно. Исходный код:

<?php
namespace BrainkitD7;

use BitrixMainEntity;
use BitrixMainLocalizationLoc;
 

/**
 * Class DataTable
 * 
 * Fields:
 * <ul>
 * <li> ID int mandatory
 * <li> LINK string mandatory
 * <li> PATH string mandatory
 * <li> SORT int optional default 500
 * <li> CREATED datetime mandatory default 'CURRENT_TIMESTAMP'
 * </ul>
 *
 * @package BrainkitData
 **/
 
class DataTable extends EntityDataManager
{
   /**
    * Returns DB table name for entity.
    *
    * @return string
    */
   public static function getTableName()
   {
      return 'brainkit_test';
   }

   /**
    * Returns entity map definition.
    *
    * @return array
    */
   public static function getMap()
   {
      return array(
         'ID' => array(
            'data_type' => 'integer',
            'primary' => true,
            'autocomplete' => true,
            'title' => Loc::getMessage('DATA_ENTITY_ID_FIELD'),
         ),
         'TITLE' => array(
            'data_type' => 'text',
            'required' => true,
            'title' => Loc::getMessage('DATA_ENTITY_TITLE_FIELD'),
         ),
         'SORT' => array(
            'data_type' => 'integer',
            'title' => Loc::getMessage('DATA_ENTITY_SORT_FIELD'),
         ),
         'CREATED' => array(
            'data_type' => 'datetime',
            'title' => Loc::getMessage('DATA_ENTITY_CREATED_FIELD'),
         ),
      );
   }
}

файл /include.php

Файл необходим для подключения классов или дополнительных файлов модуля. Если соблюдать соответствие namespace и имени модуля, то поддерживается автозагрузка класса. Автозагрузка работает, по инфомации от 1С:Битрикс медленнее, чем поключение файла в include.php. Для примера загрузим класс test.php без автозагрузки:

<?php
BitrixMainLoader::registerAutoloadClasses(
   "brainkit.d7",
   array(
      "Brainkit\D7\Test" => "lib/test.php",
   )
);
    

Запуск модуля

Для того что бы протестировать и запустить наш модуль мы должны:

  • установить модуль в административном интерфейсе
  • добавить 1 запись в тестовую таблицу brainkit_test
  • создать тестовую страницу, например, /test/index.php в корне сайта со следующим содержимым:
<?
require($_SERVER['DOCUMENT_ROOT'].'/bitrix/header.php');
$APPLICATION->SetTitle("test");
?><?
if (CModule::IncludeModule("brainkit.d7")){
    BrainkitD7Test::get();
}
?><?
require($_SERVER['DOCUMENT_ROOT'].'/bitrix/footer.php');
?>

если необходимо протестировать автозагрузку классов, то в файле include.php нужно закомментировать строку, которая подключает наш класс и запустить тестовую страницу снова

Вывод

Структура файлов модуля D7 не отличается от обычного модуля, отличается подход к разработке, код делается более структурированный, используется namespace, для отдельных таблиц мы не пишем отдельные запросы, а реализуем ORM, и при правильной организации кода, мы можем обойтись без файла include.php.

  • Устновка и использование CMS
  • Оформление модуля
  • Настройки модуля

    • Инициализация
    • Проверка и сохранение настроек
    • Рендер настроек
    • Мультисайтовость

      • Рендер
      • Прием настроек при сохранении
  • БД

    • Список статусов заказов и отгрузок
  • Крон
  • События
  • Подгрузка статики
  • Ajax
  • Popup
  • Обновления
  • Итог

Была задача на разработку модуля фискализации продаж (вот репозиторий) для движка 1С-Битрикс. Спустя некоторое время он был сделан и залит в маркетплейс. На офсайте есть полезный раздел как разрабатывать свои модули. Там многое описано, поэтому остановимся только на интересных моментах (не описанных либо слабо задокументированных).

Устновка и использование CMS

На странцие загрузки есть несколько вариантов:

  • при помощи скрипта BitrixSetup.php, достаточно закинуть его на сервер и запустить через web
  • BitrixVM — виртуальная машина с Bitrix окружением, сам не устанавливал, но имел дело с обслуживанием у одного клиента, прикольно
  • классический дистрибутив размеров чуть больше 500 мб …

Есть пробная версия на 30 дней, после чего в верхней части сайта будет сообщение:

Срок работы пробной версии продукта истек. Через две недели этот сайт полностью прекратит свою работу. Вы можете купить полнофункциональную версию продукта на сайте www.1c-bitrix.ru.

А еще через несколько дней сайт перестанет работать и будет только сообщение:

Срок работы пробной версии продукта истек. Вы можете купить полнофункциональную версию продукта на сайте www.1c-bitrix.ru.

Это контроллируется обфусцированным файлом bitrix/modules/main/include.php.

Установка модулей и обновлений на пробной версии недоступны, однако, если запросить пробный ключ то можно эти возможности разблокировать. Схема работает, пользовался неоднократно для разработки. Но это не отменяет пробных 30 дней.

Оформление модуля

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

Файлово модуль можно разместить в local/modules/module_id если директория модуля будет называться просто module_id (название модуля), то его можно найти в админке НастройкиМодули, а настройки в НастройкиНастройки модулей. Если именование директории модуля будет по правилам тогда модуль можно найти в админке MarketplaceУстановленные решения.

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

Настройки модуля

Здесь вскользь упоминается про настройки. Оказалось не сложно … до тех пор пока не потребовалась поддержка мультисайтовости. Но это можно подсмотреть в модулях «из коробки», например хороший пример модуль «Бизнес процессы» файл настроек которого располагается по пути bitrix/modules/bizproc/options.php.

Обширным источником знаний об организации настроек в своем модуле для 1С-Битрикс являются исходники модулей из коробки.

Настройки для одного сайта на движке 1С-Битрикс:
Настройки для одного сайта на движке 1С-Битрикс

Организация настроек модуля храниться в файле options.php в директории модуля.

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

Это выглядит как-то не консистентно, с одной стороны инфраструктура движка предоставляет богатый функционал для работы с движком и модулем интернет-магазина (есть даже почти PSR4) с возможностью отделить логику от представления, с другой стороны настройки модулей без таких возможностей.

Однако, по ходу реализации настроек модуля можно попытаться отделить логику от представления, но не будем пока отходить от принятой концепции :)

Файл будет содержать следующие разделы кода:

  • инициализация
  • проверка и сохранение настроек
  • рендер настроек

Инициализация

Потребуется 3 объекта ядра движка запрос, настроки, загрузчик:

use BitrixMainHttpApplication;
use BitrixMainConfigOption;
use BitrixMainLoader;

Затем необходимо получить текущий запрос и загрузить модуль если в логике настроек (сохранение/проверка) используется код модуля, как было в моем случае:

$request = HttpApplication::getInstance()->getContext()->getRequest();

// идентификатор модуля это название его директории
// файл настроек находится по пути idModule/options.php
$idModule = dirname(__DIR__);
Loader::includeModule($idModule);

Теперь массив настроек:

$tabs = [
    [
        "DIV"       => "css класс",
        "TAB"       => "Название вкладки настроек модуля",
        "OPTIONS"   => [
            [
                "id_point", // идентификатор пункта настроек
                "Label",    // надпись возле пункта настроек
                "default",  // значение по умолчанию
                
                // опиание GUI элемента, для каждого вида свое описание
                [
                    "text", 
                    11
                ]
            ],
            ...
        ]
    ]
];

Страница настроек модуля состоит из вкладок, в массиве $tabs, каждая вкладка должна располагаться в отдельном массиве.

В ключе OPTIONS располагаются непосредственно настройки вкладки. Рассмотрим несколько видов GUI элементов:

  • text — тестовое поле
[
    "token", 
    "Токен компании:", 
    "", // по умолчанию пусто
    [
        "text", 
        20 // размер строки
    ]
]
  • checkbox, принимает значения Y (отмечено) или N (не отмечено)
[
    "only2", 
    "Создавать только второй чек:", 
    "N", // по умолчанию не отмечено
    ["checkbox"]
]
  • selectbox — выдападающий список
/* $paySystems - ассоциативный массив систем оплат где:
 - ключ - идентификатор системы оплаты
 - значение - название
*/
[
    "paysystem", 
    "Создавать чек для оплаты:", 
    array_keys($paySystems)[0],
    [
        "selectbox", 
        $paySystems
    ]
]

Проверка и сохранение настроек

Для начала нужно проверить что пришел POST запрос с актуальным идентификатором сессии:

if ($request->isPost() && check_bitrix_sessid()) {
    // принятие
    // проверка
    // сохранение
}

Теперь внутри мы можем получить отправленные настройки проверить и сохранить. Разделим эти процессы и сначала соберем настройки на основании массива настроек $tabs["OPTIONS"]:

$options = [];
foreach ($tab["OPTIONS"] as $option) {
  if (!is_array($option)) {
    continue;
  }

  if ($request["apply"]) {
    if($type == "checkbox") {
      $options[$option[0]] = $request->getPost($option[0]);
    }
    else {
      $options[$option[0]] = trim($request->getPost($option[0]));
    }
  }
  else if ($request["default"]) {
    $options[$option[0]] = trim($option[2]);
  }
}

Теперь имея массив отправленных юзером данных, можно проверить корректность настроек, если есть в этом необходимость. Мне надо было проверить авторизационные данные клиента внешнего сервиса.

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

И наконец сохранение:

foreach ($options as $key => $value) {
    Option::set($idModule, $key, $value);
}

// перенаправление юзера на страницу настроек модуля
// иначе при обновлении этой страницы с модулем форма будет отправлена повторно
LocalRedirect($APPLICATION->GetCurPage()."?mid=".$idModule."&lang=".LANG);

Рендер настроек

Рендерить будем при помощи класса CAdminTabControl для многостраничных форм и магической незадокументированной функциией __AdmSettingsDrawList, упоминание о ней можно найти здесь и здесь.

Также не забываем про безопасность, которая обеспечивается вставкой скрытого поля с идентификатором сессии bitrix_sessid_post, чтобы потом при приеме данных из формы можно было использовать проверку этой сессии на возможность записи настроек check_bitrix_sessid.

$tabControl = new CAdminTabControl("tabControl", $tabs);
$tabControl->Begin();

?><form action="<?php echo($APPLICATION->GetCurPage()); ?>?mid=<?php echo($idModule); ?>&lang=<?php echo(LANG); ?>" method="post">
<?php
$tabControl->BeginNextTab();
    
foreach ($tabs as $tab) {
    if ($tab["OPTIONS"]) {
        $tabControl->BeginNextTab();
        __AdmSettingsDrawList($idModule, $tab["OPTIONS"]);
    }
}
$tabControl->Buttons();
?><input type="submit" name="apply" value="Применить" class="adm-btn-save" />
<?php echo(bitrix_sessid_post()); ?>
</form>
<?php $tabControl->End();

Мультисайтовость

Настройки для нескольких сайтов на одном инстансе движка 1С-Битрикс (мультисайтовость):
Настройки для нескольких сайтов на одном инстансе движка 1С-Битрикс (мультисайтовость)

В самом начале был упомянут модуль «Бизнес процессы», файл настроек которого расположен по пути bitrix/modules/bizproc/options.php. Это наиболее простой и наглядный пример организации настроек для модуля с поддержкой мультисайтовости 1С-Битрикс.

Что же меняется в базовой реализации настроек модуля при поддержке мультисайтовости? Немного:

  • массив $tabs остается как и прежде
  • для валидации, сохранения и рендера потребуется информация об имеющихся сайтах на инстансе движка:
$siteIds = GetSites();

// получить массив сайтов [lid => name, ...]
function GetSites()
{
  $res = BitrixMainSiteTable::getList();
  $sites = [];
  while ($site = $res->fetch()) {
    $sites[$site["LID"]] = $site["NAME"];
  }

  return $sites;
}
  • в рендере нужно самостоятельно писать верстку формы настроек с учетом мультисайтовости
  • при обработке форм так же нужно учитывать особенности реализации формы

Информацию о сайтах на инстансе 1С-Битрикс можно посмотреть в БД в таблицах b_lang и b_landing_site.

Рендер

Основная обертка формы и вкладки настроек такие же. Однако, кроме вкладок настроек модуля, теперь нужны подвкладки внутри вкладок, создать их можно при помощи CAdminViewTabControl, документацию по которой не удалось найти. Создаем подвкладки:

foreach ($siteIds as $siteId => $siteName) {
  $subTabs[] = [
    "DIV" => "opt_site_$siteId", 
    "TAB" => "$siteName ($siteId)", 
    'TITLE' => '', 
    "OPTIONS" => $tabs["OPTIONS"]
  ];
}
$subTabControl = new CAdminViewTabControl("subTabControl", $subTabs);

__AdmSettingsDrawList здесь уже не подходит, так как она рендерит верстку вне подвкладок. Штатного средства 1С-Битрикс найти не удалось, поэтому была написана своя функция рендера настроек RenderSettings.

function RenderSettings($idModule, $settings, $siteId)
{
  $a = [];
  foreach ($settings as $value) {
    $name = $value[0];
    $name2 = $value[0]."_".$siteId;
    $desc = $value[1];
    $default = $value[2];
    $type = $value[3][0];
    $val = $value[3][1];

    //$realval = Option::get($idModule, $name, $default, $sSiteId);
    $realval = settings::get($name, $siteId);
    $realval = ($realval !== null ? $realval : $default);

    $inner = "";

    switch ($type) {
      case "text":
        $inner = '<input type="text" size="'.$val.'" maxlength="255" value="'.$realval.'" name="'.$name2.'">';
      break;
      case "selectbox":
        $options = [];
        foreach($val as $key2 => $val2)
          $options[] = '<option value="'.$key2.'"'.($realval == $key2 ? 'selected=""' : '').'>'.$val2.'</option>';
        $inner = '<select name="'.$name2.'">'.implode("n", $options).'</select>';
      break;
      case "checkbox":
        $inner = '<input type="checkbox" id="'.$name2.'" name="'.$name2.'" value="'.$realval.'"'.($realval == "Y" ? 'checked=""' : '').'  class="adm-designed-checkbox">
        <label class="adm-designed-checkbox-label" for="'.$name2.'" title=""></label>';
      break;
      default:
      break;
    }

    $a[] = '<div style="display: block; margin-bottom: 5px;"><label style="display: inline-block;width:50%;text-align: right;">'.$desc.'</label>'.$inner.'</div>';
  }

  return implode("n", $a);
}

Функция может обработать только text, select, checkbox, другое пока не понадобилось. На вход принимает 3 аргумента:

  • $idModule — идентификатор модуля
  • $settings$tabs["OPTIONS"]
  • $siteIdLID сайта

Функция вернет рендер GUI элементов, при этом к названию элементов добавляться постфикс _${LID}, чтобы их можно было как-то отличить между сайтами.

Прежде чем пройтись циклом по вкладкам массива $tabs необходимо стартовать процесс обработки вкладок и затем отрендерить настройки:

$subTabControl->Begin();
foreach ($siteIds as $siteId => $siteName) {
  $subTabControl->BeginNextTab();

  foreach ($tabs as $tab) {
    if ($tab["OPTIONS"]) {
      echo RenderSettings($idModule, $tab["OPTIONS"], $siteId);
    }
  }
}
$subTabControl->End();

Прием настроек при сохранении

Имея мультисайтовость имеем одинаковые имена настроек для каждого сайт, но разные значения. Названия GUI элементов у нас имеют постфикс с _${LID} сайта, значит проходимся по массиву сайтов, используя _${LID} сайта проходимся по массиву настроек и пробуем извлечь настройки:

$options = [];
foreach ($siteIds as $siteId => $name) {
  $options[$siteId] = [];
  foreach ($tabs as $tab) {
    foreach ($tab["OPTIONS"] as $option)
    {
      if (!is_array($option)) {
        continue;
      }

      $type = $option[3][0];

      if($request["apply"]) {
        if($type == "checkbox") {
          $options[$siteId][$option[0]] = $request->getPost($option[0]."_".$siteId) !== null ? "Y": "N";
        }
        else {
          $options[$siteId][$option[0]] = trim($request->getPost($option[0]."_".$siteId));
        }
      }
      else if($oRequest["default"]) {
        $options[$siteId][$option[0]] = trim($option[2]);
      }
    }
  }
}

Важный момент с checkbox, если он не отмечен на стороне клиента, то при отправке формы название элемента не будет в массиве данных, поэтому checkbox обрабатываем по особенному.

БД

Есть специальное пространство имен, а есть глобальная переменная $DB — объект для работы с базой данных. Документация исчерпывающая, я использовал в основном чистый SQL и ForSql для экранирования.

Так как наша компания предоставляет онлайн-кассы с возможностью вывода из оборота маркированных товаров (передача кодов маркировки в «честный знак»), то для каждой позиции товара необходимо иметь поле ввода кода маркировки, что поддерживается в движке из коробки. Однако, не без приколов …

Максимальная длина кода в таблице БД 1С-Битрикс составляет 100 символов, этого не хватает для кодов маркировки обуви, которые состоят из 127 символов, и не достаточно для кодов маркировки ЕГАИС 3.0 размером 150 символов.

Полазив по форумам, понял что эта проблема еще не решена, поэтому написал свое решение:

ALTER TABLE `b_sale_store_barcode`
CHANGE `MARKING_CODE` `MARKING_CODE` VARCHAR(150) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL;

Список статусов заказов и отгрузок

Сам список статусов (заказа/отгрузки) можно увидеть в админке 1С-Битрикс в разделе Магазин=>Настройки=>Статусы.

Страница статусов в админке 1с-Битрикс

Эта страница расположена по адресу /bitrix/admin/sale_status.php попробуем открыть этот файл и видим там:

require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/sale/admin/status.php");

Идем по указанному пути … и находим внутри файла код формирования данной страницы. Немного посмотрев обнаруживаем интересные строки напоминающие формирование запроса к БД:

$query = BitrixSaleInternalsStatusTable::query();
$query->setSelect([
    'ID', 'SORT', 'TYPE', 'NOTIFY', 'LID' => 'STATUS_LANG.LID',
    'COLOR' ,'NAME' => 'STATUS_LANG.NAME', 'DESCRIPTION' => 'STATUS_LANG.DESCRIPTION'
]);
$query->where(
    BitrixMainORMQueryQuery::filter()
        ->logic('OR')
        ->where('STATUS_LANG.LID', '=', LANGUAGE_ID)
        ->where('STATUS_LANG.LID', NULL)
);

Как оказалось BitrixSaleInternals это пространство имен с классами для работы с таблицами БД модуля интернет-магазин, а вышеприведенный код использует функционал ORM 1С-Битрикс.

Классы из BitrixSaleInternals подгружаются в файле /bitrix/modules/sale/autoload.php. наследуются от MainEntityDataManager.

Это сообщение на странице документации, (не рекомендуется, но целый модуль построен на этом):
Сообщение на странице

Сначала нужно получить объект запроса:

$query = BitrixSaleInternalsStatusTable::query();

Теперь нужно указать что будем выбирать из таблицы (таблица выборки уже указана в самом классе):

$query->setSelect([
    'ID', 'SORT', 'TYPE', 'NAME' => 'STATUS_LANG.NAME'
]);

В моей задаче требовалось выбрать все статусы относящиеся к доставке, поэтому добавляем условие TYPE='D':

$query->where(
    BitrixMainORMQueryQuery::filter()
        ->logic("AND")
        ->where('TYPE', '=', "D")
);

Хотелось бы поставить фильтр по языку STATUS_LANG.LID=LANGUAGE_ID, чтобы показывать для каждого языка поддерживаемого сайтом определенный статус (и те строки для которых язык не определен STATUS_LANG.LID = NULL):

$query->where(
    BitrixMainORMQueryQuery::filter()
        ->logic('OR')
        ->where('STATUS_LANG.LID', '=', LANGUAGE_ID)
        ->where('STATUS_LANG.LID', NULL)
);

Устанавливаем сортировку по полю SORT в прямом порядке:

$query->setOrder(["SORT" => "ASC"]);

На этом этапе можно посмотреть как будет выглядеть наш запрос к БД таким образом $query->getQuery();, получим что-то подобное:

SELECT 
    `sale_internals_status`.`ID` AS `ID`,
    `sale_internals_status`.`SORT` AS `SORT`,
    `sale_internals_status`.`TYPE` AS `TYPE`,
    `sale_internals_status_status_lang`.`NAME` AS `NAME`
FROM `b_sale_status` `sale_internals_status` 
LEFT JOIN `b_sale_status_lang` `sale_internals_status_status_lang` ON `sale_internals_status`.`ID` = `sale_internals_status_status_lang`.`STATUS_ID`
WHERE `sale_internals_status`.`TYPE` = 'D' AND (`sale_internals_status_status_lang`.`LID` = 'ru' OR `sale_internals_status_status_lang`.`LID` IS NULL)
ORDER BY `SORT` ASC

Казалось бы несколько строк кода php, а сгенерирован SQL с JOIN. Все потому что ORM и внутри уже настроены связи между объектами.

Теперь выполняем запрос и распределяем данные запроса по ассоциативному массиву:

$res = $query->exec()->fetchAll();
foreach ($aRes as $value) {
    $a[$value["ID"]] = $value["NAME"];
}

В итоге получим что-то подобное:

Array
(
  [DN] => Ожидает обработки
  [DA] => Комплектация заказа
  [DG] => Ожидаем приход товара
  [DT] => Ожидаем забора транспортной компанией
  [DS] => Передан в службу доставки
  [DF] => Отгружен
)

Крон

В 1С-Битрикс крон назвается агент. Документация исчерпывающая :)

События

В новой версии ядра рекомендуется использовать новый подход с использованием EventManager, а здесь список событий. Описание метода registerEventHandler:

$eventManager->registerEventHandler(
  "sale",                       // id модуля в котором есть это событие
  "OnSalePaymentEntitySaved",   // название события
  $this->MODULE_ID,             // ид модуля который регистрирует обработчик события
  "events",                     // название класса обработчика
  "OnSalePaymentEntitySaved"    // название статического метода обработчика
);

Функция обработчик выглядит примерно так:

public static function OnSalePaymentEntitySaved(BitrixMainEvent $event)
{
  // извлечение сущности сообщения
  $payment = $event->getParameter("ENTITY");
  ...
}

Удаление обработчика (например при деинсталяции модуля) производится методом unRegisterEventHandler, значение аргументов аналогично registerEventHandler.

Мне довелось использовать следующие события:

  • OnSalePaymentEntitySaved — после сохранения сущности при поступлении оплаты, извлечь объект оплаты:
$payment = $event->getParameter("ENTITY");
  • OnAdminContextMenuShow — при выводе кнопок в админском меню, где можно встраивать свои кнопки:
function OnAdminContextMenuShow(&$aItems)
{
  $items[] = [
        "TEXT" => "Текст на кнопке", 
        "LINK" => "javascript:js код",
        "TITLE" => "Всплывающая подсказка",
        //"MENU" => $array,  //массив вложенных кнопок, такого же формата
        "ICON" => "btn_delete"  //стиль кнопки 
    ];
}

В некоторых событиях (но не во всех) можно генерировать ошибки, которые будут видны в админке при обработке этого запроса (в каких запросах это можно делать, надо выяснять опытным путем):

return new BitrixMainEventResult(
    BitrixMainEventResult::ERROR,
    new BitrixSaleResultError("Текст ошибки"),
    'sale'  // видимо id модуля для которого генерируется ошибка :)
);

Подгрузка статики

Чтобы использовать CSS или JavaScript на страницах сайта необходимо зарегистрировать свои расширения.

Изначально я предполагал что статика будет загружаться из каталога модуля, все так и было на реальном хостинге где был nginx. Все работало. Но модераторы тестировали модуль в иной среде — на локальном сервере и только на apache (судя по всему), а в директории модулей есть конфиг .htaccess который запрещает туда обращаться:

Deny from All

Тогда я пошел смотреть как решают эту проблему другие модули …

Оказывается каждый модуль копирует свою статику в директорию /bitrix/js/module_id при этом не каждый удаляет эти данные при деинсталяции. А я то думал: почему на чистой установке движка >130.000 файлов …

Всю статику пришлось перенести в директорию модуля install и при установке модуля (STATIC_DIR_NAME — директории для копирования):

CopyDirFiles(
    $_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/" . $this->MODULE_ID."/install/static/js",
    $_SERVER["DOCUMENT_ROOT"] . "/bitrix/js/" . STATIC_DIR_NAME, true, true
);

При деинсталяции модуля удалять файлы так:

DeleteDirFilesEx("/bitrix/js/" . STATIC_DIR_NAME);

А регистрация расширений выглядит так:

CJSCore::RegisterExt("название_расширения", ['js' => "/bitrix/js/" . STATIC_DIR_NAME . "/script.js"]);
CUtil::InitJSCore(["название_расширения"]);

Ajax

Необходимо зарегистрировать свое расширение и инициализировать его, как в примере выше.

Другой вопрос: где это сделать? В моем случае понадобилось подключать в обработчике события OnAdminContextMenuShow чтобы вывести на админ-панель дополнительные кнопки и прикрутить к ним логику.

В директории модуля необходимо создать директорию lib, а в ней php скрипт/скрипты где разместить пространство имен VendorNameModuleName. Есть возможность разместить все в глобальном пространстве имен.

Наш пример:

  • VendorNameinnokassa
  • ModuleNamefiscal
  • Full ModuleNameinnokassa.fiscal
  • пространство имен — InnokassaFiscal

Необходимо реализовать класс, где методы с постфиксом Action будут доступны для вызова из JavaScript (без учета регистра и без постфикса Action):

// если не глобальное пространство имен, тогда надо указать
// namespace namespace;

// подключение пространства имен контроллера
use BitrixMainEngineController;

class ajax extends Controller
{
    public function testAction()
    {
        // объект HttpRequest
        $request = $this->getRequest();
        
        return ["response" => "ok"];
    }
}

При помощи HttpRequest, можно получить данные запроса.

Теперь этот метод можно вызвать на клиентской части через BX.ajax.runAction:

BX.ajax.runAction(
    // вызываемый метод формата: partner:module_name.namespace.class_name.method
    'partner:module_name.name.ajax.test', 
    {
        // данные для пост запроса
        data: {param: 10}
    }
)
.then(function(response) {
    // код после получения данных от сервера, обьект ответа в response
});

Придет объект вида:

{
  status: "success",
  data: {/* то что вернул метод класса (ajax::testAction) */}
  errors: []
}

Popup

Для popup есть документация и вот еще как создать popup почти одной строкой :)

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

Обновления

Это оказалось самым простым, в документации кратко написано как делать обновления. Только description.ru (описание изменений обновления на русском языке) необходимо писать в кодировке Windows-1251.

Итог

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

По ходу разработке, лояльности к подобного рода отечественным решениям стало больше, это та же CMS что и другие, только отечественная :)

Создание модуля «Битрикс» для начинающих

Сергей Лещенко, выдающийся разработчик и авторитетный эксперт сообщества программистов на «1С-Битрикс» считает, что часто используемый код следует выносить в модуль. Если возникнет необходимость исправить ошибки или внести усовершенствования, то куда проще отредактировать один отдельный модуль, чем вылавливать этот фрагмент кода по разным местам на множестве проектов. Прислушаемся же к мнению Сергея и рассмотрим.

Структура модуля «Битрикс»

Сначала немного анатомии. Разберем обязательную структуру файлов и папок партнерского модуля «Битрикс». Прежде всего, модуль должен размещаться по адресу
/bitrix/modules/ID ВАШЕГО МОДУЛЯ/
Где ID ВАШЕГО МОДУЛЯ – символьный код.

Модуль должен включать в себя папки

— admin
— classes
— lang
— install
И файлы
include.php
default_option.php
options.php
prolog.php

Создание модуля «Битрикс» любой сложности и назначения непременно начинается с организации подобной файловой структуры. Теперь детали. В папку admin следует помещать скрипты для управления внутренней, административной частью модуля. В папке classes должны быть каскадные таблицы и другие файлы, управляющие оформлением. Важное уточнение: общий дизайн скрипта оформляют классы из папки /classes/general/, вид работы с mysql настраивается классами из папки /classes/mysql/. Если вдруг придется использовать другой тип баз данных, например Oracle, то потребуется отдельная папка классов classes/oracle. В папке lang, согласно логике «Битрикс», должны быть языковые файлы. Папка install предназначена для файла описания модуля index.php, а также программ инсталляции и деинсталляции.

Как вы заметили, помимо папок в структуре обязательно присутствуют четыре файла. Options.php содержит общие настройки модуля. В отличие от файла default_option.php, содержащего настройки модуля по умолчанию. Идентификатор модуля ID и HTML-код пиктограммы модуля задаются в файле prolog.php. Через файл include.php настраиваются подключения всех файлов, используемых в работе модуля, в том числе и классов.

Инсталлятор модуля «Битрикс»

Итак, файловую структуру мы подготовили. Начнем наше тренировочное создание модуля «Битрикс» с разбора скрипта установки. Это файл index.php, расположенный по адресу /bitrix/modules/ID ВАШЕГО МОДУЛЯ/install/index.php

Файл должен включать описание класса с именем, совпадающим с ID модуля. Например, таким образом:

<?
Class mymodule extends CModule
{
var $MODULE_ID = «mymodule»;
var $MODULE_NAME;
function DoInstall()
{
global $DB, $APPLICATION, $step;
$APPLICATION->IncludeAdminFile(GetMessage(«FORM_INSTALL_TITLE»),
$_SERVER[«DOCUMENT_ROOT»].»/bitrix/modules/mymodule/install/step1.php»);
}
function DoUninstall()
{
global $DB, $APPLICATION, $step;
$APPLICATION->IncludeAdminFile(GetMessage(«FORM_INSTALL_TITLE»),
$_SERVER[«DOCUMENT_ROOT»].»/bitrix/modules/mymodule/install/unstep1.php»);
}
}?>

Когда пользователь выберет модуль из списка административной панели и кликнет кнопку «Установить», будет вызван именно метод DoInstall(). И наоборот, при нажатии кнопки «Удалить» сработает метод DoUninstall().

На первый взгляд, создание модуля «Битрикс» кажется не таким уж сложным делом! А вы как считаете?

Пока отзывов нет

Начнем с главного вопроса: что надо сделать, чтобы создать свой модуль для Битрикс? Как это ни странно будет звучать, но практически ничего. Посмотрев раздел «Управление модулями» в Битрикс SDK мы обнаружим, что формально для описания своего модуля нам понадобится всего три файла:

«/include.php»

«/options.php»

«/install/index.php»

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

Рис. 1. Дерево каталога модуля гостевой книги.

Коротко опишем, для чего понадобятся те, или иные каталоги.

«Classes/general» понадобится для описания класса CIX_Guestbook, который будет регулировать роли пользователей.

Раздел «/Help» будет содержать пользовательскую документацию и справку для разработчиков.

Каталог «install» понадобится нам для описания класса ix_guestbook, который как раз и будет заниматься регистрацией модуля в БУС. Он же зарегистрирует шаблоны для почтовых нотификаций, описанных в разделе «/install/events/».

В каталоге «/install/templates» мы опишем компоненты для использования нашей гостевой книги в визуальном HTML-редакторе.

Особое внимание обратим на каталог «/install/lang/». Обычно возникают определенные трудности с пониманием, куда и какие константы класть. Например, если мы хотим, чтобы русские константы были доступны внутри файла

«/bitrix/modules/ix_guestbook/install/templates/ix_guestbook/index.php»

надо было создать файл

«/bitrix/modules/ix_guestbook/install/templates/lang/ru/ix_guestbook/index.php».

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

Понравилась статья? Поделить с друзьями:
  • Как написать модератору сайта одноклассники
  • Как написать модераторам тик ток
  • Как написать модераторам телеграмм
  • Как написать модельному агентству
  • Как написать модель угроз