Просмотр исходного кода

Ticket #4515 - Export/Import tools: Settings.

AntonLV 3 лет назад
Родитель
Сommit
3e1b130fe1

+ 1 - 0
modules/boonex/developer/classes/BxDevStudioPage.php

@@ -44,6 +44,7 @@ class BxDevStudioPage extends BxTemplStudioModule
     protected function getSettings()
     {
         $oOptions = new BxTemplStudioOptions('system', 'hidden');
+        $oOptions->enableManage(true);
 
         if(($mixedResult = $oOptions->checkAction()) !== false) {
             echoJson($mixedResult);

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

@@ -613,6 +613,7 @@
 	<string name="_adm_btn_installed_submit"><![CDATA[Installed]]></string>
 	<string name="_adm_btn_login_submit"><![CDATA[Enter]]></string>
 	<string name="_adm_btn_queued_submit"><![CDATA[Queued]]></string>
+        <string name="_adm_btn_settings_export"><![CDATA[Export]]></string>
 	<string name="_adm_btn_settings_import"><![CDATA[Import]]></string>
 	<string name="_adm_btn_settings_save"><![CDATA[Save]]></string>
 	<string name="_adm_btn_settings_mix_create"><![CDATA[Add New Mix]]></string>
@@ -1762,6 +1763,7 @@
 	<string name="_adm_stg_err_save_error_message"><![CDATA[Error in '{0}': {1}]]></string>
 	<string name="_adm_stg_err_cannot_perform"><![CDATA[Cannot perform the action.]]></string>
 	<string name="_adm_stg_err_mix_already_exists"><![CDATA[Mix with the same name already exists.]]></string>
+        <string name="_adm_stg_txt_import_popup"><![CDATA[Import Settings]]></string>
 	<string name="_adm_stg_txt_create_mix_popup"><![CDATA[Add New Mix]]></string>
 	<string name="_adm_stg_txt_import_mix_popup"><![CDATA[Import Mix]]></string>
         <string name="_adm_stg_txt_mix_dark"><![CDATA[Dark mode]]></string>

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

@@ -613,6 +613,7 @@
 	<string name="_adm_btn_installed_submit"><![CDATA[Установленные]]></string>
 	<string name="_adm_btn_login_submit"><![CDATA[Вход в Студию]]></string>
 	<string name="_adm_btn_queued_submit"><![CDATA[В очереди]]></string>
+        <string name="_adm_btn_settings_export"><![CDATA[Экспортировать]]></string>
 	<string name="_adm_btn_settings_import"><![CDATA[Импортировать]]></string>
 	<string name="_adm_btn_settings_save"><![CDATA[Сохранить]]></string>
 	<string name="_adm_btn_settings_mix_create"><![CDATA[Создать набор]]></string>
@@ -1740,6 +1741,7 @@
     <string name="_adm_stg_err_save_error_message"><![CDATA[Ошибка в '{0}': {1}]]></string>
     <string name="_adm_stg_err_cannot_perform"><![CDATA[Не могу выполнить действие.]]></string>
     <string name="_adm_stg_err_mix_already_exists"><![CDATA[Набор с таким именем уже существует.]]></string>
+    <string name="_adm_stg_txt_import_popup"><![CDATA[Импортировать настройки]]></string>
     <string name="_adm_stg_txt_create_mix_popup"><![CDATA[Добавить новый набор]]></string>
     <string name="_adm_stg_txt_import_mix_popup"><![CDATA[Импортировать набор]]></string>
     <string name="_adm_stg_txt_mix_dark"><![CDATA[Темный]]></string>

+ 81 - 1
studio/classes/BxDolStudioOptions.php

@@ -34,7 +34,8 @@ class BxDolStudioOptions extends BxDol
     protected $aCategories;
     protected $aCustomCategories;
 
-    protected $bReadOnly;	//--- Enable/Disable ReadOnly mode for system settings. Note. It doesn't affect Editable mixes.
+    protected $bManage;     //--- Enable/Disable Manage panel for system settings.
+    protected $bReadOnly;   //--- Enable/Disable ReadOnly mode for system settings. Note. It doesn't affect Editable mixes.
 
     protected $bMixes;	//--- Enable/Disable mixes feature.
     protected $sMix;
@@ -69,6 +70,7 @@ class BxDolStudioOptions extends BxDol
             }
         }
 
+        $this->bManage = false;
         $this->bReadOnly = false;
 
         $this->bMixes = false;
@@ -95,6 +97,18 @@ class BxDolStudioOptions extends BxDol
 
         $aResult = ['code' => 0, 'message' => ''];
         switch($sAction) {
+            case 'import':
+                $aResult = array_merge($aResult, $this->getPopupCodeImport());
+                break;
+
+            case 'export':
+                $aResult = array_merge($aResult, $this->export());
+                break;
+
+            case 'download':
+                $aResult = array_merge($aResult, $this->download());
+                break;
+
             case 'select-mix':
                 $aResult = array_merge($aResult, $this->selectMix($sValue));
                 break;
@@ -141,6 +155,11 @@ class BxDolStudioOptions extends BxDol
         return $this->sType;
     }
 
+    public function enableManage($bManage = true)
+    {
+    	$this->bManage = $bManage;
+    }
+
     public function enableReadOnly($bReadOnly = true)
     {
     	$this->bReadOnly = $bReadOnly;
@@ -151,6 +170,67 @@ class BxDolStudioOptions extends BxDol
     	$this->bMixes = $bMixes;
     }
 
+    public function export()
+    {
+        return [
+            'url' => bx_append_url_params($this->sBaseUrl, [
+                $this->sParamPrefix . '_action' => 'download',
+            ]),
+            'eval' => $this->getJsObject() . '.onExport(oData);'
+        ];
+    }
+
+    public function download()
+    {
+        $aTypes = [];
+        $this->oDb->getTypes(['type' => 'all'], $aTypes, false);
+        if(empty($aTypes) || !is_array($aTypes))
+            BxDolStudioTemplate::getInstance()->displayPageNotFound();
+
+        $aListTypes = [];
+        foreach($aTypes as $aType) {
+            $aCategories = [];
+            $this->oDb->getCategories(['type' => 'by_type_id_key_name', 'value' => $aType['id']], $aCategories, false);
+            if(empty($aCategories) || !is_array($aCategories))
+                continue;
+
+            $aListCategories = [];
+            foreach($aCategories as $aCategory) {
+                $aOptions = [];
+                $this->oDb->getOptions(['type' => 'by_category_id', 'value' => $aCategory['id'], 'for_export' => 1], $aOptions, false);
+                if(empty($aOptions) || !is_array($aOptions))
+                    continue;
+
+                $aListCategories[] = [
+                    'name' => $aCategory['name'],
+                    'options' => $aOptions
+                ];
+            }
+
+            $aListTypes[] = [
+                'name' => $aType['name'],
+                'categories' => $aListCategories
+            ];
+        }
+
+        $iNow = time();
+    	$sContent = json_encode([
+            'meta' => [
+                'version' => bx_get_ver(),
+                'date' => date('D, d M Y H:i:s', $iNow)
+            ],
+            'types' => $aListTypes
+    	]);
+
+    	header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+        header("Content-type: application/json");
+        header("Content-Length: " . strlen($sContent));
+        header("Content-Disposition: attachment; filename=\"settings_". date('d_m_Y', $iNow) . ".json\"");
+
+        echo $sContent;
+        exit;
+    }
+
     public function selectMix($sName)
     {
         $this->oDb->updateMixes(['active' => 0], [

+ 15 - 5
studio/classes/BxDolStudioOptionsQuery.php

@@ -371,6 +371,7 @@ class BxDolStudioOptionsQuery extends BxDolDb implements iBxDolSingleton
 
     public function getOptions($aParams, &$aItems, $bReturnCount = true)
     {
+        $aBindings = [];
         $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
         $sSelectClause = $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
 
@@ -380,7 +381,7 @@ class BxDolStudioOptionsQuery extends BxDolDb implements iBxDolSingleton
         switch($aParams['type']) {
             case 'by_id':
                 $aMethod['name'] = 'getRow';
-                $aMethod['params'][1] = [
+                $aBindings = [
                     'id' => $aParams['value']
                 ];
 
@@ -390,7 +391,7 @@ class BxDolStudioOptionsQuery extends BxDolDb implements iBxDolSingleton
 
             case 'by_name':
                 $aMethod['name'] = 'getRow';
-                $aMethod['params'][1] = [
+                $aBindings = [
                     'name' => $aParams['value']
                 ];
 
@@ -399,15 +400,23 @@ class BxDolStudioOptionsQuery extends BxDolDb implements iBxDolSingleton
                 break;
 
             case 'by_category_id':
-            	$aMethod['params'][1] = [
+            	$aBindings = [
                     'category_id' => $aParams['value']
                 ];
 
                 $sWhereClause .= "AND `to`.`category_id`=:category_id";
+
+                if(!empty($aParams['for_export'])) {
+                    $aMethod['name'] = 'getPairs';
+                    $aMethod['params'][1] = 'name';
+                    $aMethod['params'][2] = 'value';
+
+                    $sWhereClause .= " AND `to`.`type` NOT IN ('file', 'image')";
+                }
                 break;
 
             case 'by_category_name':
-            	$aMethod['params'][1] = [
+            	$aBindings = [
                     'name' => $aParams['value']
                 ];
 
@@ -416,7 +425,7 @@ class BxDolStudioOptionsQuery extends BxDolDb implements iBxDolSingleton
                 break;
 
             case 'by_category_name_full':
-            	$aMethod['params'][1] = [
+            	$aBindings = [
                     'name' => $aParams['value']
                 ];
 
@@ -440,6 +449,7 @@ class BxDolStudioOptionsQuery extends BxDolDb implements iBxDolSingleton
                 `to`.`order` AS `order`" . $sSelectClause . "
             FROM `sys_options` AS `to` " . $sJoinClause . "
             WHERE 1 " . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+        $aMethod['params'][] = $aBindings;
         $aItems = call_user_func_array([$this, $aMethod['name']], $aMethod['params']);
 
         if(!$bReturnCount)

+ 16 - 0
studio/js/options.js

@@ -45,6 +45,22 @@ BxDolStudioOptions.prototype.onSubmitted = function(sFormId, sTocken, oData) {
     this.bBusy = false;
 };
 
+BxDolStudioOptions.prototype.import = function(oButton) {
+    this.mixActionWithValue(oButton, 'import');
+};
+
+BxDolStudioOptions.prototype.onImport = function(oData) {
+    document.location.href = document.location.href;
+};
+
+BxDolStudioOptions.prototype.export = function(oButton, iId) {
+    this.mixActionWithValue(oButton, 'export', iId);
+};
+
+BxDolStudioOptions.prototype.onExport = function(oData) {
+    document.location.href = oData.url;
+};
+
 BxDolStudioOptions.prototype.mixCreate = function(oButton) {
     this.mixActionWithValue(oButton, 'create-mix');
 };

+ 19 - 15
studio/template/css/options.css

@@ -1,27 +1,31 @@
-div.bx-stg-mixes {
+div.bx-stg-cells {
     position: relative;
-    white-space: nowrap;
+    display: flex;
 }
-div.bx-stg-mixes div.bx-sm-cell {
+div.bx-stg-cells div.bx-sc-cell {
     position: relative;
+    flex: 1 1 auto;
 }
-div.bx-stg-mixes div.bx-sm-cell.bx-sm-select {
-    float: left;
-    width: 50%;
+div.bx-stg-cells div.bx-sc-cell .bx-btn {
+    display: inline-block;
+    float: none;
+    vertical-align: middle;
 }
-div.bx-stg-mixes div.bx-sm-cell.bx-sm-new {
-    float: right;
-    width: 50%;
 
+/*-- Manage --*/
+div.bx-stg-manage div.bx-sc-cell.bx-sc-right {
     text-align: right;
 }
 
-div.bx-stg-mixes div.bx-sm-cell select {
-    width: auto;
+/*-- Mixes --*/
+div.bx-stg-mixes div.bx-sc-cell.bx-sc-select {
+    width: 50%;
 }
+div.bx-stg-mixes div.bx-sc-cell.bx-sc-new {
+    width: 50%;
 
-div.bx-stg-mixes div.bx-sm-cell .bx-btn {
-    display: inline-block;
-    float: none;
-    vertical-align: middle;
+    text-align: right;
+}
+div.bx-stg-mixes div.bx-sc-cell select {
+    width: auto;
 }

+ 16 - 3
studio/template/options.html

@@ -7,9 +7,22 @@
         sMix: '__mix__',
     });
 </script>
+<bx_if:show_manage>
+    <div class="bx-stg-cells bx-stg-manage bx-def-margin-bottom">
+        <div class="bx-sc-cell bx-sc-left">&nbsp</div>
+        <div class="bx-sc-cell bx-sc-right">
+            <a class="bx-btn" href="javascript:void(0)" onclick="javascript: __js_object__.import(this)">
+                <bx_text:_adm_btn_settings_import />
+            </a>
+            <a class="bx-btn" href="javascript:void(0)" onclick="javascript: __js_object__.export(this)">
+                <bx_text:_adm_btn_settings_export />
+            </a>
+        </div>
+    </div>
+</bx_if:show_manage>
 <bx_if:show_mixes>
-    <div class="bx-stg-mixes bx-def-margin-bottom bx-clearfix">
-        <div class="bx-sm-cell bx-sm-select">
+    <div class="bx-stg-cells bx-stg-mixes bx-def-margin-bottom bx-clearfix">
+        <div class="bx-sc-cell bx-sc-select">
             <div class="bx-form-input-wrapper bx-form-input-wrapper-select">
                 <bx_if:show_select_mix>
                     <select class="bx-def-font-inputs bx-form-input-select" onchange="__js_object__.mixSelect(this)">
@@ -20,7 +33,7 @@
                 </bx_if:show_select_mix>
             </div>
         </div>
-        <div class="bx-sm-cell bx-sm-new">
+        <div class="bx-sc-cell bx-sc-new">
             <a class="bx-btn" href="javascript:void(0)" onclick="javascript: __js_object__.mixCreate(this)">
                 <bx_text:_adm_btn_settings_mix_create />
             </a>

+ 19 - 0
studio/template/options_manage_popup.html

@@ -0,0 +1,19 @@
+<div class="bx-stg-manage">
+    <div class="bx-stg-manage-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 (oData) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        __js_object__.processResult(oData);
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 98 - 0
studio/template/scripts/BxBaseStudioOptions.php

@@ -193,6 +193,12 @@ class BxBaseStudioOptions extends BxDolStudioOptions
             'type' => $this->sType,
             'category' => is_array($this->sCategory) ? json_encode($this->sCategory) : $this->sCategory,
             'mix' => $this->sMix,
+            'bx_if:show_manage' => [
+                'condition' => $this->bManage,
+                'content' => [
+                    'js_object' => $sJsObject,
+                ]
+            ],
             'bx_if:show_mixes' => [
                 'condition' => $this->bMixes,
                 'content' => [
@@ -226,6 +232,98 @@ class BxBaseStudioOptions extends BxDolStudioOptions
         ]);
     }
 
+    public function getPopupCodeImport()
+    {
+    	$oTemplate = BxDolStudioTemplate::getInstance();
+    	$sJsObject = $this->getJsObject();
+
+    	$sForm = 'adm-settings-import-form';
+    	$aForm = [
+            'form_attrs' => [
+                'id' => $sForm,
+                'name' => $sForm,
+                'action' => bx_append_url_params($this->sBaseUrl, [$this->sParamPrefix . '_action' => 'import']),
+                '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_btn_settings_import'),
+                    ], [
+                        'type' => 'button',
+                        'name' => 'cancel',
+                        'value' => _t('_adm_txt_confirm_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_stg_err_cannot_perform');
+
+            $aFile = $_FILES['file'];
+            if(empty($aFile['tmp_name']))
+                return ['code' => '1', 'message' => $sError];
+
+            $sFile = $aFile['tmp_name'];
+            $rHandle = @fopen($sFile, "r");
+            if(!$rHandle)
+                return ['code' => '2', 'message' => $sError];
+
+            $sContents = fread($rHandle, filesize($sFile));
+            fclose($rHandle);
+
+            $aContent = json_decode($sContents, true);
+            if(!is_array($aContent) || empty($aContent['meta']) || empty($aContent['types']))
+                return ['code' => '3', 'message' => $sError];
+
+            foreach($aContent['types'] as $aType) {
+                if(empty($aType['categories']) || !is_array($aType['categories']))
+                    continue;
+
+                foreach($aType['categories'] as $aCategory) {
+                    if(empty($aCategory['options']) || !is_array($aCategory['options']))
+                        continue;
+
+                    foreach($aCategory['options'] as $sOptName => $sOptValue)
+                        setParam($sOptName, $sOptValue);
+                }
+            }
+
+            $this->clearCache();
+            return ['eval' => $sJsObject . '.onImport(oData);'];
+        }
+
+        return [
+            'popup' => BxTemplStudioFunctions::getInstance()->popupBox('adm-stg-import-popup', _t('_adm_stg_txt_import_popup'), $oTemplate->parseHtmlByName('options_manage_popup.html', [
+                'js_object' => $sJsObject,
+                'form_id' => $sForm,
+                'form' => $oForm->getCode(true),
+            ]))
+        ];
+    }
+
     public function getPopupCodeCreateMix()
     {
     	$oTemplate = BxDolStudioTemplate::getInstance();