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

+ 21 - 0
modules/boonex/developer/classes/BxDevBuilderPage.php

@@ -30,6 +30,11 @@ class BxDevBuilderPage extends BxTemplStudioBuilderPage
         $this->oModule->_oTemplate->addStudioCss(array('builder_page.css'));
     }
 
+    function getPageCode($sPage = '', $bWrap = true)
+    {
+        return $this->oModule->_oTemplate->getJsCode('main') . parent::getPageCode($sPage, $bWrap);
+    }
+
     function getBlockPanelTop($aBlock)
     {
          return BxBaseStudioWidget::getBlockPanelTop(
@@ -451,6 +456,22 @@ class BxDevBuilderPage extends BxTemplStudioBuilderPage
         BxDolForm::setSubmittedValue('content', $sValue, $oForm->aFormAttrs['method']);
     }
 
+    protected function _getTmplVarsBlockPanelTop()
+    {
+        $aResult = parent::_getTmplVarsBlockPanelTop();
+
+        $aResult['bx_if:show_actions_global'] = [
+            'condition' => $this->sPage == '' || empty($this->aPageRebuild),
+            'content' => [
+                'js_object' => $this->oModule->_oConfig->getJsObject('main'),
+                'action_page_import' => 'import',
+                'action_page_export' => 'export'
+            ]
+        ];
+
+        return $aResult;
+    }
+
     protected function _getTmplVarsBlockPanelTopActions()
     {
         $sJsObject = $this->getPageJsObject();

+ 9 - 2
modules/boonex/developer/classes/BxDevConfig.php

@@ -79,8 +79,15 @@ class BxDevConfig extends BxBaseModGeneralConfig
             'form_display_pgt_keys_add' => $this->_sName . '_pgt_keys_add'
         );
 
-        $this->aJsClasses = array('polyglot' => 'BxDevPolyglot');
-        $this->aJsObjects = array('polyglot' => 'oBxDevPolyglot');
+        $this->aJsClasses = [
+            'main' => 'BxDevMain',
+            'polyglot' => 'BxDevPolyglot'
+        ];
+        $this->aJsObjects = [
+            'main' => 'oBxDevMain',
+            'polyglot' => 'oBxDevPolyglot'
+        ];
+
         $this->sAnimationEffect = 'fade';
         $this->iAnimationSpeed = 'slow';
     }

+ 71 - 0
modules/boonex/developer/classes/BxDevFormsForms.php

@@ -23,6 +23,11 @@ class BxDevFormsForms extends BxTemplStudioFormsForms
         $this->_aOptions['actions_single']['export']['attr']['title'] = _t('_bx_dev_frm_btn_forms_gl_export');
     }
 
+    public function getCode($isDisplayHeader = true)
+    {
+        return $this->oModule->_oTemplate->getJsCode('main') . parent::getCode($isDisplayHeader);
+    }
+
     public function performActionAdd()
     {
         $sAction = 'add';
@@ -186,5 +191,71 @@ class BxDevFormsForms extends BxTemplStudioFormsForms
 
         echoJson(array('popup' => $sContent));
     }
+
+    public function performActionExportFull()
+    {
+        echoJson([
+            'url' => BX_DOL_URL_ROOT . bx_append_url_params($this->oModule->_oConfig->getBaseUri() . 'download', [
+                'type' => 'forms',
+            ]),
+            'eval' => $this->oModule->_oConfig->getJsObject('main') . '.onExport(oData);'
+        ]);
+    }
+    
+    public function performActionImportFull()
+    {
+        $sAction = 'import_full';
+
+        $aResult = $this->oModule->getPopupCodeImport([
+            'form_name' => 'bx-dev-forms-import-full',
+            'form_action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction
+        ]);
+        
+        if(!isset($aResult['code']) || (int)$aResult['code'] != 0)
+            return echoJson($aResult);
+
+        $aContent = $aResult['content'];
+        $bModeFull = isset($aContent['meta']['full']) && (bool)$aContent['meta']['full'] === true;
+
+        $aMfForm = $aMfDisplay = $aMfInput = $aMfDisplayInput = false;
+        foreach($aContent['meta']['masks'] as $sMask => $aMask)
+            ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
+
+        $iData = 0;
+        foreach($aContent['data'] as $aData) {
+            $iData += 1;
+
+            $sObject = $aData['form']['object'];
+            if($bModeFull && !$this->oDb->isForm($sObject))
+                $this->oDb->addForm($aData['form']);
+            else
+                $this->oDb->updateFormByObject($sObject, $aData['form']);
+            
+            foreach($aData['displays'] as $aDisplay)
+                if($bModeFull && !$this->oDb->isDisplay($sObject,  $aDisplay['display_name']))
+                    $this->oDb->addDisplay($aDisplay);
+                else
+                    $this->oDb->updateDisplayByObjectAndName($sObject, $aDisplay['display_name'], $aDisplay);
+
+            foreach($aData['inputs'] as $aInput)
+                if($bModeFull && !$this->oDb->isInput($sObject,  $aInput['name']))
+                    $this->oDb->addInput($aInput);
+                else
+                    $this->oDb->updateInputByObjectAndName($sObject, $aInput['name'], $aInput);
+                
+            foreach($aData['display_inputs'] as $aDisplayInput)
+                if($bModeFull && !$this->oDb->isDisplayInput($aDisplayInput['display_name'],  $aDisplayInput['input_name']))
+                    $this->oDb->addDisplayInput($aDisplayInput);
+                else
+                    $this->oDb->updateDisplayInputByDisplayAndInput($aDisplayInput['display_name'],  $aDisplayInput['input_name'], $aDisplayInput);
+        }
+
+        BxDolCacheUtilities::getInstance()->clear('db');
+
+        echoJson([
+            'msg' => _t('_bx_dev_msg_imported', $iData), 
+            'eval' => $this->oModule->_oConfig->getJsObject('main') . '.onImport(oData);'
+        ]);
+    }
 }
 /** @} */

+ 381 - 1
modules/boonex/developer/classes/BxDevModule.php

@@ -41,7 +41,8 @@ class BxDevModule extends BxDolModule
         return $this->aTools;
     }
 
-    public function actionResetHash($sType, $sModule = '') {
+    public function actionResetHash($sType, $sModule = '')
+    {
     	$oDb = bx_instance('BxDolStudioInstallerQuery');
 
     	$sResult = '';
@@ -104,5 +105,384 @@ class BxDevModule extends BxDolModule
     	echo $sResult;
     	exit;
     }
+    
+    public function actionExport()
+    {
+        $sType = bx_process_input(bx_get('type'));
+        if(empty($sType))
+            return echoJson([]);
+
+        echoJson([
+            'url' => BX_DOL_URL_ROOT . bx_append_url_params($this->_oConfig->getBaseUri() . 'download', [
+                'type' => $sType,
+            ]),
+            'eval' => $this->_oConfig->getJsObject('main') . '.onExport(oData);'
+        ]);
+    }
+
+    public function actionDownload()
+    {
+        $sType = bx_process_input(bx_get('type'));
+        if(empty($sType))
+            return;
+
+        $sMethod = '_eiExport' . bx_gen_method_name($sType);
+        if(!method_exists($this, $sMethod))
+            return;
+
+        $mixedFull = ($mixedFull = bx_get('full')) !== false ? (int)$mixedFull != 0 : false;
+
+        list($aMeta, $aData) = $this->$sMethod($mixedFull);
+
+        $iNow = time();
+    	$sContent = json_encode([
+            'meta' => array_merge([
+                'version' => bx_get_ver(),
+                'date' => date('D, d M Y H:i:s', $iNow),
+                'full' => $mixedFull
+            ], $aMeta),
+            'data' => $aData
+    	]);
+
+    	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=\"" . $sType . ($mixedFull ? "_f" : "") . "_". date('d_m_Y', $iNow) . ".json\"");
+
+        echo $sContent;
+        exit;
+    }
+    
+    public function actionImport()
+    {
+        $sType = bx_process_input(bx_get('type'));
+        if(empty($sType))
+            return echoJson([]);
+
+        $aResult = $this->getPopupCodeImport([
+            'form_name' => 'bx-dev-' . $sType . '-import-full',
+            'form_action' => BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'import?type=' . $sType
+        ]);
+
+        if(!isset($aResult['code']) || (int)$aResult['code'] != 0)
+            return echoJson($aResult);
+        
+        $sMethod = '_eiImport' . bx_gen_method_name($sType);
+        if(!method_exists($this, $sMethod))
+            return echoJson([]);
+
+        echoJson($this->$sMethod($aResult['content']));
+    }
+
+    public function getPopupCodeImport($aParams)
+    {
+    	$sJsObject = $this->_oConfig->getJsObject('main');
+
+    	$sForm = $aParams['form_name'];
+    	$aForm = [
+            'form_attrs' => [
+                'id' => $sForm,
+                'name' => $sForm,
+                'action' => $aParams['form_action'],
+                '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('_bx_dev_btn_import_full'),
+                    ], [
+                        'type' => 'button',
+                        'name' => 'cancel',
+                        'value' => _t('_bx_dev_btn_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('_bx_dev_err_cannot_perform');
+
+            if(empty($_FILES['file']) || empty($_FILES['file']['tmp_name']))
+                return ['code' => 1, 'msg' => $sError];
+
+            $aFile = $_FILES['file'];
+            $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['data']))
+                return ['code' => 3, 'message' => $sError];
+
+            return ['code' => 0, 'content' => $aContent];
+        }
+
+        return [
+            'popup' => BxTemplStudioFunctions::getInstance()->popupBox('bx-dev-import-popup', _t('_bx_dev_txt_import_full_popup'), $this->_oTemplate->parseHtmlByName('import_popup.html', [
+                'js_object' => $sJsObject,
+                'form_id' => $sForm,
+                'form' => $oForm->getCode(true),
+            ]))
+        ];
+    }
+
+    /**
+     * 
+     * Methods for Export/Import feature.
+     * 
+     */
+    protected function _eiExportForms($bModeFull = false)
+    {
+        $oGrid = BxDolGrid::getObjectInstance($this->_oConfig->getObject('grid_forms'));
+        if(!$oGrid)
+            return [];
+
+        $aForms = [];
+        $oGrid->oDb->getForms(['type' => 'export'], $aForms, false);
+        if(empty($aForms) || !is_array($aForms))
+            return [];
+
+        $aMeta = [
+            'masks' => [
+                'form' => ['object', 'active'],
+                'display' => ['display_name'],
+                'input' => ['object', 'name', 'privacy', 'checker_func', 'checker_params', 'checker_error', 'db_pass', 'db_params'],
+                'display_input' => ['display_name', 'input_name', 'visible_for_levels', 'active', 'order']
+            ]
+        ];
+
+        if($bModeFull) {
+            $aMeta['masks'] = [
+                'form' => ['object', 'module', 'title', 'action', 'form_attrs', 'submit_name', 'table', 'key', 'uri', 'uri_title', 'params', 'deletable', 'active', 'override_class_name', 'override_class_file'],
+                'display' => ['display_name', 'module', 'object', 'title', 'view_mode'],
+                'input' => ['object', 'module', 'name', 'value', 'values', 'checked', 'type', 'caption_system', 'caption', 'info', 'help', 'icon', 'required', 'unique', 'collapsed', 'html', 'privacy', 'rateable', 'attrs', 'attrs_tr', 'attrs_wrapper', 'checker_func', 'checker_params', 'checker_error', 'db_pass', 'db_params', 'editable', 'deletable'],
+                'display_input' => ['display_name', 'input_name', 'visible_for_levels', 'active', 'order']
+            ];
+        }
+
+        $aMfForm = $aMfDisplay = $aMfInput = $aMfDisplayInput = false;
+        foreach($aMeta['masks'] as $sMask => $aMask)
+            ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
+
+        $aData = [];
+        foreach($aForms as $aForm) {
+            $sObject = $aForm['object'];
+
+            $aDisplays = [];
+            $oGrid->oDb->getDisplays(['type' => 'export_by_object', 'value' => $sObject], $aDisplays, false);
+            
+            $aListDisplays = [];
+            $aDataDisplays = [];
+            if(!empty($aDisplays) && is_array($aDisplays))
+                foreach($aDisplays as $aDisplay) {
+                    $aListDisplays[] = $aDisplay['display_name'];
+                    $aDataDisplays[] = $aMfDisplay !== false ? array_intersect_key($aDisplay, $aMfDisplay) : $aDisplay;
+                }
+
+            $aInputs = [];
+            $oGrid->oDb->getInputs(['type' => 'dump_inputs', 'value' => $sObject], $aInputs, false);
+
+            $aDataInputs = [];
+            if(!empty($aInputs) && is_array($aInputs))
+                foreach($aInputs as $aInput)
+                    $aDataInputs[] = $aMfInput !== false ? array_intersect_key($aInput, $aMfInput) : $aInput;
+
+            $aDisplayInputs = [];
+            $oGrid->oDb->getInputs(['type' => 'dump_display_inputs', 'displays' => $aListDisplays], $aDisplayInputs, false);
+
+            $aDataDisplayInputs = [];
+            if(!empty($aDisplayInputs) && is_array($aDisplayInputs))
+                foreach($aDisplayInputs as $aDisplayInput)
+                    $aDataDisplayInputs[] = $aMfDisplayInput !== false ? array_intersect_key($aDisplayInput, $aMfDisplayInput) : $aDisplayInput;
+
+            $aData[] = [
+                'form' => $aMfForm !== false ? array_intersect_key($aForm, $aMfForm) : $aForm,
+                'displays' => $aDataDisplays,
+                'inputs' => $aDataInputs,
+                'display_inputs' => $aDataDisplayInputs
+            ];
+        }
+
+        return [$aMeta, $aData];
+    }
+
+    protected function _eiExportMenus($bModeFull = false)
+    {
+        $oGrid = BxDolGrid::getObjectInstance($this->_oConfig->getObject('grid_nav_menus'));
+        if(!$oGrid)
+            return [];
+
+        $aMenus = [];
+        $oGrid->oDb->getMenus(['type' => 'export'], $aMenus, false);
+        if(empty($aMenus) || !is_array($aMenus))
+            return [];
+
+        $aMeta = [
+            'masks' => [
+                'menu' => ['object', 'active'],
+                'set' => ['set_name'],
+                'item' => ['set_name', 'name', 'visible_for_levels', 'visibility_custom', 'hidden_on', 'hidden_on_cxt', 'hidden_on_pt', 'hidden_on_col', 'active', 'active_api', 'order']
+            ]
+        ];
+
+        if($bModeFull) {
+            $aMeta['masks'] = [
+                'menu' => ['object', 'title', 'set_name', 'module', 'template_id', 'deletable', 'active', 'override_class_name', 'override_class_file'],
+                'set' => ['set_name', 'module', 'title', 'deletable'],
+                'item' => ['parent_id', 'set_name', 'module', 'name', 'title_system', 'title', 'link', 'onclick', 'target', 'icon', 'addon', 'addon_cache', 'markers', 'submenu_object', 'submenu_popup', 'visible_for_levels', 'visibility_custom', 'hidden_on', 'hidden_on_cxt', 'hidden_on_pt', 'hidden_on_col', 'primary', 'collapsed', 'active', 'active_api', 'copyable', 'editable', 'order']
+            ];
+        }
+
+        $aMfMenu = $aMfSet = $aMfItem = false;
+        foreach($aMeta['masks'] as $sMask => $aMask)
+            ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
+
+        $aData = [];
+        foreach($aMenus as $aMenu) {
+            $aSet = [];
+            $oGrid->oDb->getSets(['type' => 'export_by_name', 'value' => $aMenu['set_name']], $aSet, false);
+
+            $aDataItems = [];
+            if(!empty($aSet) && is_array($aSet)) {
+                $aItems = [];
+                $oGrid->oDb->getItems(['type' => 'export_by_set_name', 'value' => $aSet['set_name']], $aItems, false);
+
+                if(!empty($aItems) && is_array($aItems))
+                    foreach($aItems as $aItem)
+                        $aDataItems[] = $aMfItem !== false ? array_intersect_key($aItem, $aMfItem) : $aItem;
+            }
+
+            $aData[] = [
+                'menu' => $aMfMenu !== false ? array_intersect_key($aMenu, $aMfMenu) : $aMenu,
+                'set' => !empty($aSet) && $aMfSet !== false ? array_intersect_key($aSet, $aMfSet) : $aSet,
+                'items' => $aDataItems,
+            ];
+        }
+
+        return [$aMeta, $aData];
+    }
+
+    protected function _eiExportPages($bModeFull = false)
+    {
+        bx_import('BuilderPage', $this->_aModule);
+        $oBuilderPage = new BxDevBuilderPage(['type' => '', 'page' => '', 'url' => '']);
+        if(!$oBuilderPage)
+            return [];
+
+        $oBpDb = $oBuilderPage->getDb();
+
+        $aPages = $oBpDb->getPages(['type' => 'export']);
+        if(empty($aPages) || !is_array($aPages))
+            return [];
+
+        $aMeta = [
+            'masks' => [
+                'page' => ['object', 'visible_for_levels'],
+                'block' => ['object', 'cell_id', 'module', 'title_system', 'title', 'visible_for_levels', 'hidden_on', 'active', 'active_api', 'order']
+            ]
+        ];
+
+        if($bModeFull) {
+            $aMeta['masks'] = [
+                'page' => ['author', 'added', 'object', 'uri', 'title_system', 'title', 'module', 'cover', 'cover_image', 'cover_title', 'type_id', 'layout_id', 'sticky_columns', 'submenu', 'visible_for_levels', 'visible_for_levels_editable', 'url', 'content_info', 'meta_description', 'meta_keywords', 'meta_robots', 'cache_lifetime', 'cache_editable', 'inj_head', 'inj_footer', 'deletable', 'override_class_name', 'override_class_file'],
+                'block' => ['object', 'cell_id', 'module', 'title_system', 'title', 'designbox_id', 'class', 'submenu', 'tabs', 'async', 'visible_for_levels', 'hidden_on', 'type', 'content', 'content_empty', 'text', 'text_updated', 'help', 'cache_lifetime', 'deletable', 'copyable', 'active', 'active_api', 'order']
+            ];
+        }
+
+        $aMfPage = $aMfBlock = false;
+        foreach($aMeta['masks'] as $sMask => $aMask)
+            ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
+
+        $aData = [];
+        foreach($aPages as $aPage) {
+            $aBlocks = [];
+            $oBpDb->getBlocks(['type' => 'export_by_object', 'value' => $aPage['object']], $aBlocks, false);
+
+            $aDataBlocks = [];
+            if(!empty($aBlocks) && is_array($aBlocks))
+                foreach($aBlocks as $aBlock)
+                    $aDataBlocks[] = $aMfBlock !== false ? array_intersect_key($aBlock, $aMfBlock) : $aBlock;
+
+            $aData[] = [
+                'page' => $aMfPage !== false ? array_intersect_key($aPage, $aMfPage) : $aPage,
+                'blocks' => $aDataBlocks,
+            ];
+        }
+
+        return [$aMeta, $aData];
+    }
+
+    protected function _eiImportPages($aContent)
+    {
+        bx_import('BuilderPage', $this->_aModule);
+        $oBuilderPage = new BxDevBuilderPage(['type' => '', 'page' => '', 'url' => '']);
+        if(!$oBuilderPage)
+            return [];
+
+        $oBpDb = $oBuilderPage->getDb();
+
+        $bModeFull = isset($aContent['meta']['full']) && (bool)$aContent['meta']['full'] === true;
+
+        $aMfMenu = $aMfSet = $aMfItem = false;
+        foreach($aContent['meta']['masks'] as $sMask => $aMask)
+            ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
+
+        $iData = 0;
+        foreach($aContent['data'] as $aData) {
+            $iData += 1;
+
+            $sObject = $aData['page']['object'];
+            if($bModeFull && !$oBpDb->isPageExists($sObject))
+                $oBpDb->addPage($aData['page']);
+            else
+                $oBpDb->updatePageByObject($sObject, $aData['page']);
+
+            foreach($aData['blocks'] as $aBlock) {
+                $aFields = [
+                    'object' => $aBlock['object'],
+                    'module' => $aBlock['module'], 
+                    'title_system' => $aBlock['title_system'], 
+                    'title' => $aBlock['title']
+                ];
+
+                if($bModeFull && !$oBpDb->isBlockExists($aFields))
+                    $oBpDb->addBlock($aItem);
+                else
+                    $oBpDb->updateBlockByFields($aFields, $aBlock);
+            }
+        }
+
+        BxDolCacheUtilities::getInstance()->clear('db');
+
+        return [
+            'msg' => _t('_bx_dev_msg_imported', $iData), 
+            'eval' => $this->_oConfig->getJsObject('main') . '.onImport(oData);'
+        ];
+    }
 }
 /** @} */

+ 67 - 0
modules/boonex/developer/classes/BxDevNavigationMenus.php

@@ -23,6 +23,11 @@ class BxDevNavigationMenus extends BxTemplStudioNavigationMenus
         $this->sUrlViewItems = BX_DOL_URL_STUDIO . 'module.php?name=' . $this->oModule->_oConfig->getName() . '&page=navigation&nav_page=items&nav_module=%s&nav_set=%s';
     }
 
+    public function getCode($isDisplayHeader = true)
+    {
+        return $this->oModule->_oTemplate->getJsCode('main') . parent::getCode($isDisplayHeader);
+    }
+
     public function performActionAdd()
     {
         $sAction = 'add';
@@ -150,6 +155,68 @@ class BxDevNavigationMenus extends BxTemplStudioNavigationMenus
         echoJson(array('popup' => $sContent));
     }
 
+    public function performActionExportFull()
+    {
+        echoJson([
+            'url' => BX_DOL_URL_ROOT . bx_append_url_params($this->oModule->_oConfig->getBaseUri() . 'download', [
+                'type' => 'menus',
+            ]),
+            'eval' => $this->oModule->_oConfig->getJsObject('main') . '.onExport(oData);'
+        ]);
+    }
+    
+    public function performActionImportFull()
+    {
+        $sAction = 'import_full';
+
+        $aResult = $this->oModule->getPopupCodeImport([
+            'form_name' => 'bx-dev-menus-import-full',
+            'form_action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction
+        ]);
+        
+        if(!isset($aResult['code']) || (int)$aResult['code'] != 0)
+            return echoJson($aResult);
+
+        $aContent = $aResult['content'];
+        $bModeFull = isset($aContent['meta']['full']) && (bool)$aContent['meta']['full'] === true;
+
+        $aMfMenu = $aMfSet = $aMfItem = false;
+        foreach($aContent['meta']['masks'] as $sMask => $aMask)
+            ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
+
+        $iData = 0;
+        foreach($aContent['data'] as $aData) {
+            $iData += 1;
+
+            $sObject = $aData['menu']['object'];
+            if($bModeFull && !$this->oDb->isMenuExists($sObject))
+                $this->oDb->addMenu($aData['menu']);
+            else
+                $this->oDb->updateMenuByObject($sObject, $aData['menu']);
+
+            $aSet = $aData['set'];
+            if(empty($aSet) || !is_array($aSet))
+                continue;
+
+            $sSetName = $aSet['set_name'];
+            if($bModeFull && !$this->oDb->isSetExists($sSetName))
+                $this->oDb->addSet($aSet);
+
+            foreach($aData['items'] as $aItem)
+                if($bModeFull && !$this->oDb->isItemExists($sObject,  $aItem['name']))
+                    $this->oDb->addItem($aItem);
+                else
+                    $this->oDb->updateItemBySetAndName($sSetName, $aItem['name'], $aItem);
+        }
+
+        BxDolCacheUtilities::getInstance()->clear('db');
+
+        echoJson([
+            'msg' => _t('_bx_dev_msg_imported', $iData), 
+            'eval' => $this->oModule->_oConfig->getJsObject('main') . '.onImport(oData);'
+        ]);
+    }
+
     private function fillInSelects(&$aInputs)
     {
         $aInputs['module']['values'] = array_merge(array('' => _t('_bx_dev_nav_txt_select_module')), BxDolStudioUtils::getModules());

+ 14 - 1
modules/boonex/developer/classes/BxDevTemplate.php

@@ -9,13 +9,26 @@
  * @{
  */
 
-class BxDevTemplate extends BxDolModuleTemplate
+class BxDevTemplate extends BxBaseModGeneralTemplate
 {
     function __construct(&$oConfig, &$oDb)
     {
         parent::__construct($oConfig, $oDb);
 
         $this->addStudioCss(['main.css']);
+        $this->addStudioJs(['main.js']);
+    }
+
+    public function getJsCode($sType, $aParams = [], $mixedWrap = true)
+    {
+        if(empty($aParams) || !is_array($aParams))
+            $aParams = [];
+
+        return parent::getJsCode($sType, array_merge($aParams, [
+            'sTxtExportType' => bx_js_string(_t('_bx_dev_txt_export_type')),
+            'sTxtBasic' => bx_js_string(_t('_bx_dev_btn_basic')),
+            'sTxtFull' => bx_js_string(_t('_bx_dev_btn_full'))
+        ]), $mixedWrap);
     }
 
     function displayPageSettings($sPage, $oContent, $sGetPageCodeMethod = 'getPageCode')

+ 11 - 0
modules/boonex/developer/install/langs/en.xml

@@ -526,4 +526,15 @@
     <string name="_bx_dev_hash_msg_reset_hash_modules"><![CDATA[Hash was successfully reset for the following modules:]]></string>
     <string name="_bx_dev_hash_txt_reset_hash_module"><![CDATA[Module]]></string>
     <string name="_bx_dev_hash_txt_reset_hash_files"><![CDATA[Files Hashed]]></string>
+
+    <!-- Export/Import -->
+    <string name="_bx_dev_btn_basic"><![CDATA[Basic]]></string>
+    <string name="_bx_dev_btn_full"><![CDATA[Full]]></string>
+    <string name="_bx_dev_btn_import_full"><![CDATA[Import]]></string>
+    <string name="_bx_dev_btn_export_full"><![CDATA[Export]]></string>
+    <string name="_bx_dev_btn_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_txt_import_full_popup"><![CDATA[Import]]></string>
+    <string name="_bx_dev_txt_export_type"><![CDATA[What type of export do you need?]]></string>
+    <string name="_bx_dev_err_cannot_perform"><![CDATA[Cannot perform the action.]]></string>
+    <string name="_bx_dev_msg_imported"><![CDATA[Import finished. {0} items were processed.]]></string>
 </resources>

+ 6 - 0
modules/boonex/developer/install/sql/install.sql

@@ -77,6 +77,8 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
 ('bx_developer_forms', 'independent', 'add', '_bx_dev_frm_btn_forms_gl_create', '', 0, 1),
+('bx_developer_forms', 'independent', 'import_full', '_bx_dev_btn_import_full', '', 0, 2),
+('bx_developer_forms', 'independent', 'export_full', '_bx_dev_btn_export_full', '', 0, 3),
 ('bx_developer_forms', 'single', 'export', '', 'download', 0, 1),
 ('bx_developer_forms', 'single', 'edit', '', 'pencil-alt', 0, 2),
 
@@ -401,12 +403,16 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
 ('bx_developer_nav_menus', 'independent', 'add', '_bx_dev_nav_btn_menus_gl_create', '', 0, 1),
+('bx_developer_nav_menus', 'independent', 'import_full', '_bx_dev_btn_import_full', '', 0, 2),
+('bx_developer_nav_menus', 'independent', 'export_full', '_bx_dev_btn_export_full', '', 0, 3),
 ('bx_developer_nav_menus', 'single', 'export', '', 'download', 0, 1),
 ('bx_developer_nav_menus', 'single', 'edit', '', 'pencil-alt', 0, 2),
 ('bx_developer_nav_menus', 'single', 'delete', '', 'remove', 1, 3),
+
 ('bx_developer_nav_sets', 'independent', 'add', '_bx_dev_nav_btn_sets_gl_create', '', 0, 1),
 ('bx_developer_nav_sets', 'single', 'edit', '', 'pencil-alt', 0, 1),
 ('bx_developer_nav_sets', 'single', 'delete', '', 'remove', 1, 2),
+
 ('bx_developer_nav_items', 'independent', 'import', '_bx_dev_nav_btn_items_gl_import', '', 0, 1),
 ('bx_developer_nav_items', 'independent', 'add', '_bx_dev_nav_btn_items_gl_create', '', 0, 2),
 ('bx_developer_nav_items', 'single', 'edit', '', 'pencil-alt', 0, 1),

+ 63 - 0
modules/boonex/developer/js/main.js

@@ -0,0 +1,63 @@
+function BxDevMain(oOptions) {
+    this._sActionUrl = oOptions.sActionUrl;
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxDevMain' : oOptions.sObjName;
+
+    this._sTxtExportType = oOptions.sTxtExportType == undefined ? '' : oOptions.sTxtExportType;
+    this._sTxtBasic = oOptions.sTxtBasic == undefined ? '' : oOptions.sTxtBasic;
+    this._sTxtFull = oOptions.sTxtFull == undefined ? '' : oOptions.sTxtFull;
+
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'slide' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+}
+
+BxDevMain.prototype.import = function(oButton, sType) {
+    this.performAction(oButton, 'import', {type: sType});
+};
+
+BxDevMain.prototype.onImport = function(oData) {
+    document.location.href = document.location.href;
+};
+
+BxDevMain.prototype.export = function(oButton, sType) {
+    this.performAction(oButton, 'export', {type: sType});
+};
+
+BxDevMain.prototype.onExport = function(oData) {
+    bx_confirm(this._sTxtExportType, function() {
+        document.location.href = bx_append_url_params(oData.url, {full: 0});
+    }, function() {
+        document.location.href = bx_append_url_params(oData.url, {full: 1});
+    }, {
+        yes: {
+            title: this._sTxtBasic
+        },
+        no: {
+            title: this._sTxtFull
+        }
+    });
+};
+
+BxDevMain.prototype.performAction = function(oSource, sAction, aParams, bConfirm) {
+    var $this = this;
+    var oDate = new Date();
+
+    bx_loading(oSource, true);
+
+    var oPerform = function() {
+        $.post(
+            $this._sActionUrl + sAction,
+            $.extend({}, aParams, {_t: oDate.getTime()}),
+            function (oData) {
+                bx_loading(oSource, false);
+
+                processJsonData(oData);
+            },
+            'json'
+        );
+    };
+
+    if(bConfirm != undefined && parseInt(bConfirm) == 1)
+	bx_confirm('', oPerform);
+    else
+        oPerform();
+};

+ 10 - 6
modules/boonex/developer/template/bp_block_panel_top.html

@@ -1,13 +1,17 @@
 <div class="bx-std-pt-cnt bx-def-padding-lefttopright bx-clearfix">
 	<div class="bx-std-pt-pages">__selector__</div>
 	<div class="bx-std-pt-buttons">
-	    <button class="bx-btn bx-def-margin-sec-left" onclick="javascript:__js_object__.performAction('__action_page_create__');"><bx_text:_adm_bp_btn_create_new /></button>
+	    <button class="bx-btn bx-def-margin-thd-left" onclick="javascript:__js_object__.performAction('__action_page_create__');"><bx_text:_adm_bp_btn_create_new /></button>
+            <bx_if:show_actions_global>
+                <button class="bx-btn bx-def-margin-thd-left" onclick="javascript:__js_object__.import(this, 'pages');"><bx_text:_bx_dev_btn_import_full /></button>
+                <button class="bx-btn bx-def-margin-thd-left" onclick="javascript:__js_object__.export(this, 'pages');"><bx_text:_bx_dev_btn_export_full /></button>
+            </bx_if:show_actions_global>
 	    <bx_if:show_actions>
-	        <button class="bx-btn bx-def-margin-sec-left" onclick="javascript:window.open('__url_view__','_blank');"><bx_text:_adm_bp_btn_view_page /></button>
-	        <button class="bx-btn bx-def-margin-sec-left" onclick="javascript:__js_object__.performAction('__action_page_export__')"><bx_text:_bx_dev_bp_btn_page_export /></button>
-	        <button class="bx-btn bx-def-margin-sec-left" onclick="javascript:__js_object__.performAction('__action_page_edit__')"><bx_text:_adm_bp_btn_settings /></button>
-	        <button class="bx-btn bx-def-margin-sec-left" onclick="javascript:__js_object__.performAction('__action_block_create__')"><bx_text:_adm_bp_btn_add_block /></button>
-	        <button class="bx-btn bx-def-margin-sec-left" onclick="javascript:__js_object__.deletePage();"><bx_text:_adm_bp_btn_delete /></button>
+	        <button class="bx-btn bx-def-margin-thd-left" onclick="javascript:window.open('__url_view__','_blank');"><bx_text:_adm_bp_btn_view_page /></button>
+	        <button class="bx-btn bx-def-margin-thd-left" onclick="javascript:__js_object__.performAction('__action_page_export__')"><bx_text:_bx_dev_bp_btn_page_export /></button>
+	        <button class="bx-btn bx-def-margin-thd-left" onclick="javascript:__js_object__.performAction('__action_page_edit__')"><bx_text:_adm_bp_btn_settings /></button>
+	        <button class="bx-btn bx-def-margin-thd-left" onclick="javascript:__js_object__.performAction('__action_block_create__')"><bx_text:_adm_bp_btn_add_block /></button>
+	        <button class="bx-btn bx-def-margin-thd-left" onclick="javascript:__js_object__.deletePage();"><bx_text:_adm_bp_btn_delete /></button>
 	    </bx_if:show_actions>
 	</div>
 </div>

+ 19 - 0
modules/boonex/developer/template/import_popup.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-import">
+    <div class="bx-dev-import-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();
+                        processJsonData(oData);
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 11 - 0
modules/boonex/russian/data/langs/bx_developer/ru.xml

@@ -526,4 +526,15 @@
     <string name="_bx_dev_hash_msg_reset_hash_modules"><![CDATA[Хэш успешно сброшен для следующих модулей:]]></string>
     <string name="_bx_dev_hash_txt_reset_hash_module"><![CDATA[Модуль]]></string>
     <string name="_bx_dev_hash_txt_reset_hash_files"><![CDATA[Файлов хэшировано]]></string>
+
+    <!-- Export/Import -->
+    <string name="_bx_dev_btn_basic"><![CDATA[Базовый]]></string>
+    <string name="_bx_dev_btn_full"><![CDATA[Полный]]></string>
+    <string name="_bx_dev_btn_import"><![CDATA[Импортировать]]></string>
+    <string name="_bx_dev_btn_export"><![CDATA[Экспортировать]]></string>
+    <string name="_bx_dev_btn_cancel"><![CDATA[Отмена]]></string>
+    <string name="_bx_dev_txt_import_popup"><![CDATA[Импортировать]]></string>
+    <string name="_bx_dev_txt_export_type"><![CDATA[Какой вид экспорта вам нужен?]]></string>
+    <string name="_bx_dev_err_cannot_perform"><![CDATA[Не могу выполнить действие.]]></string>
+    <string name="_bx_dev_msg_imported"><![CDATA[Импорт завершен. Обработано {0} элементов.]]></string>
 </resources>