Jelajahi Sumber

Ticket #5558 - enable-app.sql manual run.

AntonLV 2 bulan lalu
induk
melakukan
56e72641c3

+ 19 - 0
install/sql/system.sql

@@ -5690,6 +5690,25 @@ INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon
 ('sys_studio_api_keys', 'single', 'delete', '_Delete', 'remove', 1, 1, 1),
 ('sys_studio_api_keys', 'independent', 'add', '_adm_form_btn_field_add', '', 0, 0, 1);
 
+-- GRID: API Configs
+
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `override_class_name`, `override_class_file`) VALUES
+('sys_studio_api_configs', 'Sql', 'SELECT * FROM `sys_modules` WHERE 1 ', 'sys_modules', 'id', 'name', '', '', 100, NULL, 'start', '', 'name,title,vendor', '', 'like', '', '', 'BxTemplStudioApiConfigs', '');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `hidden_on`, `order`) VALUES
+('sys_studio_api_configs', 'title', '_adm_api_ttl_module', '20%', 0, 0, '', '', 1),
+('sys_studio_api_configs', 'actions', '', '80%', 0, 0, '', '', 2);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('sys_studio_api_configs', 'single', 'apply', '_adm_api_btn_apply', 'recycle', 1, 1, 1),
+('sys_studio_api_configs', 'single', 'remove', '_adm_api_btn_remove', 'eraser', 1, 1, 2),
+('sys_studio_api_configs', 'single', 'export', '_adm_api_btn_export', 'file-export', 1, 0, 3),
+-- ('sys_studio_api_configs', 'single', 'import', '_adm_api_btn_import', 'file-import', 1, 1, 4),
+('sys_studio_api_configs', 'independent', 'apply_all', '_adm_api_btn_apply_all', '', 0, 1, 1),
+('sys_studio_api_configs', 'independent', 'remove_all', '_adm_api_btn_remove_all', '', 0, 1, 2),
+('sys_studio_api_configs', 'independent', 'export_all', '_adm_api_btn_export_all', '', 0, 0, 3),
+('sys_studio_api_configs', 'independent', 'import_all', '_adm_api_btn_import_all', '', 0, 0, 4);
+
 -- GRID: Agents Models
 INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `responsive`, `show_total_count`, `override_class_name`, `override_class_file`) VALUES
 ('sys_studio_agents_models', 'Sql', 'SELECT * FROM `sys_agents_models` WHERE 1 ', 'sys_agents_models', 'id', 'changed', 'active', '', 20, NULL, 'start', '', 'type,model,title,capabilities', '', 'like', 'type,model,title,capabilities', '', 128, 1, 1, 'BxTemplStudioAgentsModels', '');

+ 22 - 0
modules/boonex/english/data/langs/system/en.xml

@@ -469,6 +469,28 @@
 	<string name="_adm_admtools_Permissions"><![CDATA[Permissions]]></string>
 	<string name="_adm_admtools_Writable"><![CDATA[Writable]]></string>
 	<string name="_adm_admtools_title"><![CDATA[Admin Tools]]></string>
+        <string name="_adm_api_page_block_title_config_modules"><![CDATA[Modules]]></string>
+        <string name="_adm_api_page_block_title_config_system"><![CDATA[System]]></string>
+        <string name="_adm_api_page_block_title_"><![CDATA[]]></string>
+        <string name="_adm_api_btn_apply"><![CDATA[Apply]]></string>
+        <string name="_adm_api_btn_remove"><![CDATA[Remove]]></string>
+        <string name="_adm_api_btn_import"><![CDATA[Import]]></string>
+        <string name="_adm_api_btn_export"><![CDATA[Export]]></string>
+        <string name="_adm_api_btn_apply_all"><![CDATA[Apply All Apps Settings]]></string>
+        <string name="_adm_api_btn_remove_all"><![CDATA[Remove All Apps Settings]]></string>
+        <string name="_adm_api_btn_import_all"><![CDATA[Import App(s) Settings]]></string>
+        <string name="_adm_api_btn_export_all"><![CDATA[Export All Apps Settings]]></string>
+        <string name="_adm_api_err_action_file_not_found"><![CDATA[File not found.]]></string>
+        <string name="_adm_api_err_action_handler_not_found"><![CDATA[Cannot process action. It does not have an associated handler.]]></string>
+        <string name="_adm_api_err_cannot_perform_action"><![CDATA[Cannot perform action. Please report.]]></string>
+        <string name="_adm_api_msg_applied"><![CDATA[Settings were applied.]]></string>
+        <string name="_adm_api_msg_applied_for"><![CDATA[All apps settings were applied.]]></string>
+        <string name="_adm_api_msg_export_empty"><![CDATA[There are no settings to export.]]></string>
+        <string name="_adm_api_msg_imported"><![CDATA[Settings were imported.]]></string>
+        <string name="_adm_api_msg_removed"><![CDATA[Settings were removed.]]></string>
+        <string name="_adm_api_msg_removed_for"><![CDATA[All apps settings were removed.]]></string>
+        <string name="_adm_api_popup_import"><![CDATA[Import Settings]]></string>
+        <string name="_adm_api_ttl_module"><![CDATA[Module]]></string>
 	<string name="_adm_block_cpt_categories"><![CDATA[Categories]]></string>
 	<string name="_adm_block_cpt_category"><![CDATA[Category: {0}]]></string>
 	<string name="_adm_block_cpt_checkout"><![CDATA[Checkout]]></string>

+ 21 - 0
modules/boonex/russian/data/langs/system/ru.xml

@@ -467,6 +467,27 @@
 	<string name="_adm_admtools_Permissions"><![CDATA[Разрешения]]></string>
 	<string name="_adm_admtools_Writable"><![CDATA[Записываемый]]></string>
 	<string name="_adm_admtools_title"><![CDATA[Инструменты администратора]]></string>
+        <string name="_adm_api_page_block_title_config_modules"><![CDATA[Модули]]></string>
+        <string name="_adm_api_page_block_title_config_system"><![CDATA[Система]]></string>
+        <string name="_adm_api_btn_apply"><![CDATA[Применить]]></string>
+        <string name="_adm_api_btn_remove"><![CDATA[Удалить]]></string>
+        <string name="_adm_api_btn_import"><![CDATA[Импортировать]]></string>
+        <string name="_adm_api_btn_export"><![CDATA[Экспортировать]]></string>
+        <string name="_adm_api_btn_apply_all"><![CDATA[Применить настройки всех приложений]]></string>
+        <string name="_adm_api_btn_remove_all"><![CDATA[Удалить настройки всех приложений]]></string>
+        <string name="_adm_api_btn_import_all"><![CDATA[Импортировать настройки приложения(й)]]></string>
+        <string name="_adm_api_btn_export_all"><![CDATA[Экспортировать настройки всех приложений]]></string>
+        <string name="_adm_api_err_action_file_not_found"><![CDATA[Файл не найден.]]></string>
+        <string name="_adm_api_err_action_handler_not_found"><![CDATA[Невозможно выполнить действие. Отсутствует связанный обработчик.]]></string>
+        <string name="_adm_api_err_cannot_perform_action"><![CDATA[Невозможно выполнить действие. Пожалуйста, оповестите нас об этой проблеме.]]></string>
+        <string name="_adm_api_msg_applied"><![CDATA[Настройки были применены.]]></string>
+        <string name="_adm_api_msg_applied_for"><![CDATA[Настройки всех приложений были применены.]]></string>
+        <string name="_adm_api_msg_export_empty"><![CDATA[Настройки для экспорта отсутствуют.]]></string>
+        <string name="_adm_api_msg_imported"><![CDATA[Настройки были импортированы.]]></string>
+        <string name="_adm_api_msg_removed"><![CDATA[Настройки были удалены.]]></string>
+        <string name="_adm_api_msg_removed_for"><![CDATA[Настройки всех приложений были удалены.]]></string>
+        <string name="_adm_api_popup_import"><![CDATA[Импортировать настройки]]></string>
+        <string name="_adm_api_ttl_module"><![CDATA[Приложение]]></string>
 	<string name="_adm_block_cpt_categories"><![CDATA[Категории]]></string>
 	<string name="_adm_block_cpt_category"><![CDATA[Категория: {0}]]></string>
 	<string name="_adm_block_cpt_checkout"><![CDATA[Оплата]]></string>

+ 41 - 0
studio/classes/BxDolStudioAPI.php

@@ -26,6 +26,47 @@ class BxDolStudioAPI extends BxTemplStudioWidget
         if(is_string($sPage) && !empty($sPage))
             $this->sPage = $sPage;
     }
+    
+    public function checkAction()
+    {
+        $sAction = '';
+    	if(($sAction = bx_get('api_action')) !== false)
+            $sAction = bx_process_input($sAction);
+        else
+            return false;
+
+        $aResult = ['code' => 1, 'message' => _t('_adm_api_err_action_handler_not_found')];
+        switch($sAction) {
+            default:
+                $sMethod = 'action' . $this->getClassName($sAction);
+                if(method_exists($this, $sMethod))
+                    $aResult = $this->$sMethod();
+        }
+
+        return $aResult;
+    }
+
+    protected function actionDownload()
+    {
+        $sFile = '';
+        if(($sFile = bx_get('api_value')) === false) 
+            return ['code' => 2];
+
+        $sPath = BX_DIRECTORY_PATH_TMP . $sFile;
+
+        $sContent = '';
+        if(!file_exists($sPath) || !($sContent = file_get_contents($sPath)))
+            return ['code' => 3, 'msg' => _t('_adm_api_err_action_file_not_found')];
+
+        @unlink($sPath);
+
+        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+        header("Content-type: text/plain");
+        header("Content-Length: " . strlen($sContent));
+        header("Content-Disposition: attachment; filename=\"" . $sFile . "\"");
+        echo $sContent;
+        exit;
+    }
 }
 
 /** @} */

+ 99 - 0
studio/classes/BxDolStudioApiConfigs.php

@@ -0,0 +1,99 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaStudio UNA Studio
+ * @{
+ */
+
+class BxDolStudioApiConfigs extends BxTemplStudioGrid
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        $this->_aOptions['source'] .= " AND `name`<>'system' AND `enabled`<>0 ";
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+
+    protected function _getQueriesPagesConfigApi($sModule = '')
+    {
+        $sResult = '';
+        $sWhere = $sModule ? $this->oDb->prepareAsString(" AND `module`=?", $sModule) : "";
+
+        $aPages = $this->oDb->getAll("SELECT `object`, `config_api` FROM `sys_objects_page` WHERE `config_api` <> ''" . $sWhere . " ORDER BY `id` ASC");
+        if($aPages && is_array($aPages)) {
+            $sResult = "\n\n-- PAGES:\n";
+            foreach($aPages as $aPage)
+                $sResult .= $this->oDb->prepareAsString("UPDATE `sys_objects_page` SET `config_api`=? WHERE `object`=?;\n", $aPage['config_api'], $aPage['object']);
+        }
+
+        $aBlocks = $this->oDb->getAll("SELECT `object`, `module`, `title_system`, `title`, `config_api` FROM `sys_pages_blocks` WHERE `config_api` <> ''" . $sWhere . " ORDER BY `id` ASC");
+        if($aBlocks && is_array($aBlocks)) {
+            $sResult .= "\n\n-- PAGE BLOCKS:\n";
+            foreach($aBlocks as $aBlock)
+                $sResult .= $this->oDb->prepareAsString("UPDATE `sys_pages_blocks` SET `config_api`=? WHERE `object`=? AND `module`=? AND `title_system`=? AND `title`=?;\n", $aBlock['config_api'], $aBlock['object'], $aBlock['module'], $aBlock['title_system'], $aBlock['title']);
+        }
+
+        return $sResult;
+    }
+
+    protected function _getQueriesPagesActiveApi($sModule = '')
+    {
+        $sResult = '';
+        $sWhere = $sModule ? $this->oDb->prepareAsString(" AND `module`=?", $sModule) : "";
+
+        $aBlocks = $this->oDb->getAll("SELECT `object`, `module`, `title_system`, `title`, `active_api` FROM `sys_pages_blocks` WHERE `active_api` <> '0'" . $sWhere . " ORDER BY `id` ASC");
+        if($aBlocks && is_array($aBlocks)) {
+            $sResult = "\n\n-- PAGE BLOCKS:\n";
+            foreach($aBlocks as $aBlock)
+                $sResult .= $this->oDb->prepareAsString("UPDATE `sys_pages_blocks` SET `active_api`=? WHERE `object`=? AND `module`=? AND `title_system`=? AND `title`=?;\n", $aBlock['active_api'], $aBlock['object'], $aBlock['module'], $aBlock['title_system'], $aBlock['title']);
+        }
+
+        return $sResult;
+    }
+
+    protected function _getQueriesMenusConfigApi($sModule = '')
+    {
+        $sResult = '';
+        $sWhere = $sModule ? $this->oDb->prepareAsString(" AND `module`=?", $sModule) : "";
+
+        $aMenus = $this->oDb->getAll("SELECT `object`, `config_api` FROM `sys_objects_menu` WHERE `config_api` <> ''" . $sWhere . " ORDER BY `id` ASC");
+        if($aMenus && is_array($aMenus)) {
+            $sResult = "\n\n-- MENUS:\n";
+            foreach($aMenus as $aMenu)
+                $sResult .= $this->oDb->prepareAsString("UPDATE `sys_objects_menu` SET `config_api`=? WHERE `object`=?;\n", $aMenu['config_api'], $aMenu['object']);
+        }
+
+        $aItems = $this->oDb->getAll("SELECT `set_name`, `module`, `name`, `config_api` FROM `sys_menu_items` WHERE `config_api` <> ''" . $sWhere . " ORDER BY `id` ASC");
+        if($aItems && is_array($aItems)) {
+            $sResult .= "\n\n-- MENU ITEMS:\n";
+            foreach($aItems as $aItem)
+                $sResult .= $this->oDb->prepareAsString("UPDATE `sys_menu_items` SET `config_api`=? WHERE `set_name`=? AND `module`=? AND `name`=?;\n", $aItem['config_api'], $aItem['set_name'], $aItem['module'], $aItem['name']);
+        }
+
+        return $sResult;
+    }
+
+    protected function _getQueriesMenusActiveApi($sModule = '')
+    {
+        $sResult = '';
+        $sWhere = $sModule ? $this->oDb->prepareAsString(" AND `module`=?", $sModule) : "";
+
+        $aItems = $this->oDb->getAll("SELECT `set_name`, `module`, `name`, `active_api` FROM `sys_menu_items` WHERE `active_api` <> '0'" . $sWhere . " ORDER BY `id` ASC");
+        if($aItems && is_array($aItems)) {
+            $sResult = "\n\n-- MENU ITEMS:\n";
+            foreach($aItems as $aItem)
+                $sResult .= $this->oDb->prepareAsString("UPDATE `sys_menu_items` SET `active_api`=? WHERE `set_name`=? AND `module`=? AND `name`=?;\n", $aItem['active_api'], $aItem['set_name'], $aItem['module'], $aItem['name']);
+        }       
+
+        return $sResult;
+    }
+}
+
+/** @} */

+ 9 - 0
studio/classes/BxDolStudioInstaller.php

@@ -500,6 +500,15 @@ class BxDolStudioInstaller extends BxDolInstallerUtils
         bx_alert('system', 'disable', 0, false, array ('config' => $this->_aConfig, 'result' => &$aResult));
         return $aResult;
     }
+    
+    public function callAction($sOperation, $sAction)
+    {
+        $sMethod = 'action' . bx_gen_method_name($sAction);
+        if(!method_exists($this, $sMethod))
+            return ['code' => BX_DOL_STUDIO_INSTALLER_FAILED, 'msg' => _t('_sys_txt_not_found')];
+
+        return $this->$sMethod($sOperation);
+    }
 
     //--- Get/Set Methods ---//
     public function getVendor()

+ 19 - 0
studio/template/api_settings_form.html

@@ -0,0 +1,19 @@
+<div class="adm-api-settings-form">
+    <div class="adm-api-settings-form-cnt">
+        __form__
+        <script language="javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function (formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function (data) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        glGrids.__object__.processJson(data, '__action__');
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 14 - 3
studio/template/scripts/BxBaseStudioAPI.php

@@ -29,10 +29,11 @@ class BxBaseStudioAPI extends BxDolStudioAPI
             BX_DOL_STUDIO_API_TYPE_KEYS => array('icon' => 'key'),
         );
 
-        $this->aGridObjects = array(
+        $this->aGridObjects = [
             'keys' => 'sys_studio_api_keys',
             'origins' => 'sys_studio_api_origins',
-        );
+            'configs' => 'sys_studio_api_configs',
+        ];
     }
 
     public function getPageMenu($aMenu = array(), $aMarkers = array())
@@ -71,7 +72,17 @@ class BxBaseStudioAPI extends BxDolStudioAPI
             'api_config'
         ]);
 
-        return $oOptions->getCode() . $this->getPageJsCode();
+        $this->aPageCss = array_merge($this->aPageCss, $oOptions->getCss());
+        $this->aPageJs = array_merge($this->aPageJs, $oOptions->getJs());
+
+        return [[
+            'caption' => '_adm_api_page_block_title_config_system',
+            'content' => $oOptions->getCode() . $this->getPageJsCode()
+        ], [
+            'caption' => '_adm_api_page_block_title_config_modules',
+            'content' => $this->getGrid($this->aGridObjects['configs'])
+            
+        ]];
     }
 
     protected function getKeys()

+ 260 - 0
studio/template/scripts/BxBaseStudioApiConfigs.php

@@ -0,0 +1,260 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaView UNA Studio Representation classes
+ * @ingroup     UnaStudio
+ * @{
+ */
+
+class BxBaseStudioApiConfigs extends BxDolStudioApiConfigs
+{
+    protected $_sConfigFile;
+    protected $_sInstallerFile;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+        
+        $this->_sConfigFile = 'install/config.php';
+        $this->_sInstallerFile = 'install/installer.php';
+
+        $this->oDb = BxDolModuleQuery::getInstance();
+    }
+
+    public function performActionApply()
+    {
+        $iId = $this->_getIds();
+        
+        $mixedResult = $this->_performActionApply($iId);
+        if($mixedResult === true)
+            $mixedResult = ['code' => 0, 'msg' => _t('_adm_api_msg_applied')];
+        else
+            $mixedResult = ['code' => 1, 'msg' => is_string($mixedResult) ? $mixedResult : _t('_adm_err_operation_failed')];
+
+        return echoJson($mixedResult);
+    }
+
+    public function performActionRemove()
+    {
+        $iId = $this->_getIds();
+
+        $aResult = [];
+        if($this->_performActionRemove($iId))
+            $aResult = ['code' => 0, 'msg' => _t('_adm_api_msg_removed')];
+        else
+            $aResult = ['code' => 1, 'msg' => _t('_adm_err_operation_failed')];
+
+        return echoJson($aResult);
+    }
+
+    public function performActionExport()
+    {
+        $iId = $this->_getIds();
+
+        list($bResult, $sResult) = $this->_performActionExport($iId);
+        if(!$bResult)
+            return echoJson(['code' => 1, 'msg' => _t($sResult)]);
+
+        return echoJson([
+            'code' => 0,
+            'redirect' => BX_DOL_URL_STUDIO . bx_append_url_params('api.php', [
+                'page' => 'api_config',
+                'api_action' => 'download',
+                'api_value' => $sResult
+            ])
+        ]);
+    }
+
+    public function performActionApplyAll()
+    {
+        $iAffected = 0;
+        
+        $aModules = $this->oDb->getModulesBy(['type' => 'modules']);
+        foreach($aModules as $aModule) {
+            if((int)$aModule['enabled'] == 0)
+                continue;
+
+            $iAffected += ($this->_performActionApply($aModule['id']) === true ? 1 : 0);
+        }
+
+        return echoJson(['code' => 0, 'msg' => _t('_adm_api_msg_applied_for', $iAffected)]);
+    }
+
+    public function performActionRemoveAll()
+    {
+        $iAffected = 0;
+        
+        $aModules = $this->oDb->getModulesBy(['type' => 'modules']);
+        foreach($aModules as $aModule) {
+            if((int)$aModule['enabled'] == 0)
+                continue;
+
+            $iAffected += ($this->_performActionRemove($aModule['id']) === true ? 1 : 0);
+        }
+
+        return echoJson(['code' => 0, 'msg' => _t('_adm_api_msg_removed_for', $iAffected)]);
+    }
+
+    public function performActionExportAll()
+    {
+        list($bResult, $sResult) = $this->_performActionExport();
+        if(!$bResult)
+            return echoJson(['code' => 1, 'msg' => _t($sResult)]);
+
+        return echoJson([
+            'code' => 0,
+            'redirect' => BX_DOL_URL_STUDIO . bx_append_url_params('api.php', [
+                'page' => 'api_config',
+                'api_action' => 'download',
+                'api_value' => $sResult
+            ])
+        ]);
+    }
+
+    public function performActionImportAll()
+    {
+        $sAction = 'import_all';
+
+        $sForm = 'adm_api_settings_' . $sAction;
+    	$aForm = [
+            'form_attrs' => [
+                'id' => $sForm,
+                'name' => $sForm,
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=sys_studio_api_configs&a=' . $sAction,
+                'method' => 'post',
+                'enctype' => 'multipart/form-data'
+            ],
+            'params' => [
+                'db' => [
+                    'submit_name' => 'save'
+                ],
+            ],
+            'inputs' => [
+            	'file' => [
+                    'type' => 'file',
+                    'name' => 'file',
+                    'caption' => '',
+                    'value' => '',
+                ],
+                'controls' => [
+                    'type' => 'input_set', [
+                        'type' => 'submit',
+                        'name' => 'save',
+                        'value' => _t('_adm_api_btn_import'),
+                    ], [
+                        'type' => 'button',
+                        'name' => 'cancel',
+                        'value' => _t('_Cancel'),
+                        'attrs' => [
+                            'class' => 'bx-def-margin-sec-left-auto',
+                            'onclick' => '$(".bx-popup-applied:visible").dolPopupHide()'
+                        ]
+                    ]
+                ]
+            ]
+        ];
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $sError = _t('_adm_api_err_cannot_perform_action');
+
+            $sFile = '';
+            if(!($sFile = $_FILES['file']['tmp_name'] ?? false))
+                return echoJson(['code' => 1, 'msg' => $sError]);
+
+            if($this->oDb->executeSQL($sFile) !== true)
+                return echoJson(['code' => 2, 'msg' => $sError]);
+
+            BxDolCacheUtilities::getInstance()->clear('db');
+
+            return echoJson(['code' => 0, 'msg' => _t('_adm_api_msg_imported')]);
+        }
+
+        $sFormId = $oForm->getId();
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($sFormId . '_popup', _t('_adm_api_popup_import'), $this->_oTemplate->parseHtmlByName('api_settings_form.html', [
+            'form_id' => $sFormId,
+            'form' => $oForm->getCode(true),
+            'object' => $this->_sObject,
+            'action' => $sAction
+        ]));
+
+        return echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false]]]);
+    }
+
+    protected function _performActionApply($iId)
+    {
+        $aModule = $this->oDb->getModuleById($iId);
+        if(!$aModule || !is_array($aModule))
+            return false;
+
+        $aConfig = BxDolStudioInstallerUtils::getModuleConfig(BX_DIRECTORY_PATH_MODULES . $aModule['path'] . $this->_sConfigFile);
+        if(!$aConfig || !is_array($aConfig))
+            return false;
+
+        $sPathInstaller = BX_DIRECTORY_PATH_MODULES . $aModule['path'] . $this->_sInstallerFile;
+        if(!file_exists($sPathInstaller))
+            return false;
+
+        require_once($sPathInstaller);
+
+        $sClassName = $aModule['class_prefix'] . 'Installer';
+        $oInstaller = new $sClassName($aConfig);
+        $mixedResult = $oInstaller->callAction('enable', 'execute_sql_addons');
+
+        return $mixedResult == BX_DOL_STUDIO_INSTALLER_SUCCESS ? true : ($mixedResult['msg'] ?? false);
+    }
+    
+    protected function _performActionRemove($iId)
+    {
+        $aModule = $this->oDb->getModuleById($iId);
+        if(!$aModule || !is_array($aModule))
+            return false;
+
+        $this->oDb->query("UPDATE `sys_pages_blocks` SET `active_api`=0 WHERE `module`=:module", [
+            'module' => $aModule['name']
+        ]);
+
+        $this->oDb->query("UPDATE `sys_menu_items` SET `active_api`=0 WHERE `module`=:module", [
+            'module' => $aModule['name']
+        ]);
+
+        return true;
+    }
+    
+    protected function _performActionExport($iId = 0)
+    {
+        $sModule = '';
+        if($iId && ($aModule = BxDolModuleDb::getInstance()->getModuleById($iId)) && is_array($aModule))
+            $sModule = $aModule['name'];
+
+        $sContent = '';
+        if(($_sContent = $this->_getQueriesPagesConfigApi($sModule)))
+            $sContent .= "-- \n-- Pages 'Config API':\n-- " . $_sContent;
+        if(($_sContent = $this->_getQueriesPagesActiveApi($sModule)))
+            $sContent .= "-- \n-- Pages 'Active API':\n-- " . $_sContent;
+        if(($_sContent = $this->_getQueriesMenusConfigApi($sModule)))
+            $sContent .= "-- \n-- Manus 'Config API':\n-- " . $_sContent;
+        if(($_sContent = $this->_getQueriesMenusActiveApi($sModule)))
+            $sContent .= "-- \n-- Manus 'Active API':\n-- " . $_sContent;   
+
+        if(!$sContent)
+            return [false, '_adm_api_msg_export_empty'];
+
+        $iNow = time();
+        $sFile = "api_settings_" . ($sModule ?: 'all') . "_". date('d_m_Y', $iNow) . ".sql";
+
+        $mixedResult = false;
+        if(($oHandle = fopen(BX_DIRECTORY_PATH_TMP . $sFile, 'w'))) {
+            $mixedResult = fwrite($oHandle, $sContent);
+            fclose($oHandle);
+        }
+
+        return $mixedResult ? [true, $sFile] : [false, '_adm_err_operation_failed'];
+    }
+}
+
+/** @} */

+ 11 - 0
studio/template/scripts/BxBaseStudioGrid.php

@@ -113,6 +113,17 @@ class BxBaseStudioGrid extends BxDolStudioGrid
             ]
         ]);
     }
+
+    protected function _getIds()
+    {
+        if(($aIds = bx_get('ids')) && is_array($aIds))
+            return reset($aIds);
+
+        if(($iId = bx_get('id')) !== false) 
+            return (int)$iId;
+
+        return false;
+    }
 }
 
 /** @} */

+ 3 - 3
studio/template/scripts/BxBaseStudioWidget.php

@@ -171,9 +171,9 @@ class BxBaseStudioWidget extends BxDolStudioWidget
                 if(isset($mixedContent['content']))
                     $sResult .= $this->getBlockCode($mixedContent);
                 else
-                    foreach($mixedContent as $sBlock)
-                        $sResult .= $this->getBlockCode([
-                            'content' => $sBlock
+                    foreach($mixedContent as $mixedBlock)
+                        $sResult .= $this->getBlockCode(is_array($mixedBlock) ? $mixedBlock : [
+                            'content' => $mixedBlock
                         ]);
             }
             else if(is_a($mixedContent, 'BxDolPage'))

+ 19 - 0
studio/template/scripts_templ/BxTemplStudioApiConfigs.php

@@ -0,0 +1,19 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaStudioView UNA Studio Representation classes
+ * @ingroup     UnaStudio
+ * @{
+ */
+
+class BxTemplStudioApiConfigs extends BxBaseStudioApiConfigs
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+    }
+}
+
+/** @} */

+ 1 - 0
template/scripts/BxBaseIconsetLucide.php

@@ -69,6 +69,7 @@ class BxBaseIconsetLucide extends BxBaseIconset
             'facebook-square' => 'log-in', //TODO: Brand icon. Update later.
             'file-alt' => 'file',
             'file-alt' => 'file-text',
+            'file-import' => 'file-input',
             'file-export' => 'file-output',
             'file-invoice' => 'receipt',
             'file-word' => 'file-text',