1
0

BxDevModule.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup Developer Developer
  7. * @ingroup UnaModules
  8. *
  9. * @{
  10. */
  11. require_once('BxDevFunctions.php');
  12. define('BX_DEV_TOOLS_SETTINGS', 'settings');
  13. define('BX_DEV_TOOLS_FORMS', 'forms');
  14. define('BX_DEV_TOOLS_PAGES', 'pages');
  15. define('BX_DEV_TOOLS_NAVIGATION', 'navigation');
  16. define('BX_DEV_TOOLS_POLYGLOT', 'polyglot');
  17. define('BX_DEV_TOOLS_PERMISSIONS', 'permissions');
  18. class BxDevModule extends BxDolModule
  19. {
  20. public $aTools;
  21. function __construct($aModule)
  22. {
  23. parent::__construct($aModule);
  24. $this->aTools = array(
  25. array('name' => BX_DEV_TOOLS_SETTINGS, 'title' => '_adm_page_cpt_settings', 'icon' => 'bx-dev-mi-settings.svg'),
  26. array('name' => BX_DEV_TOOLS_POLYGLOT, 'title' => '_adm_page_cpt_polyglot', 'icon' => 'bx-dev-mi-polyglot.svg'),
  27. array('name' => BX_DEV_TOOLS_FORMS, 'title' => '_adm_page_cpt_builder_forms', 'icon' => 'bx-dev-mi-forms.svg'),
  28. array('name' => BX_DEV_TOOLS_NAVIGATION, 'title' => '_adm_page_cpt_builder_menus', 'icon' => 'bx-dev-mi-navigation.svg'),
  29. array('name' => BX_DEV_TOOLS_PAGES, 'title' => '_adm_page_cpt_builder_pages', 'icon' => 'bx-dev-mi-pages.svg'),
  30. //array('name' => BX_DEV_TOOLS_PERMISSIONS, 'title' => '_adm_page_cpt_builder_permissions', 'icon' => 'bx-dev-mi-permissions.png'),
  31. );
  32. }
  33. function getToolsList()
  34. {
  35. return $this->aTools;
  36. }
  37. public function actionResetHash($sType, $sModule = '')
  38. {
  39. $oDb = bx_instance('BxDolStudioInstallerQuery');
  40. $sResult = '';
  41. switch ($sType) {
  42. case 'system':
  43. $oHasher = bx_instance('BxDolInstallerHasher');
  44. $oDb->deleteModuleTrackFiles(BX_SYSTEM_MODULE_ID);
  45. $sResult = _t('_bx_dev_hash_' . ($oHasher->hashSystemFiles() ? 'msg' : 'err') . '_reset_hash_system');
  46. break;
  47. case 'modules':
  48. $oModuleQuery = BxDolModuleQuery::getInstance();
  49. if(!empty($sModule)) {
  50. $aNames = strpos($sModule, ',') !== false ? explode(',', $sModule) : array($sModule);
  51. $aModule = array();
  52. foreach($aNames as $aName)
  53. $aModules[] = $oModuleQuery->getModuleByName($aName);
  54. }
  55. else
  56. $aModules = $oModuleQuery->getModules();
  57. $aTmplVarsModules = array();
  58. if(!empty($aModules) && is_array($aModules))
  59. foreach($aModules as $aModule) {
  60. if($aModule['name'] == 'system')
  61. continue;
  62. $aConfig = BxDolInstallerUtils::getModuleConfig($aModule);
  63. $sPathInstaller = BX_DIRECTORY_PATH_MODULES . $aModule['path'] . 'install/installer.php';
  64. if(empty($aConfig) || !file_exists($sPathInstaller))
  65. continue;
  66. require_once($sPathInstaller);
  67. $sClassName = $aConfig['class_prefix'] . 'Installer';
  68. $oInstaller = new $sClassName($aConfig);
  69. $oDb->deleteModuleTrackFiles($aModule['id']);
  70. $aFiles = array();
  71. $oInstaller->hashFiles(BX_DIRECTORY_PATH_ROOT . 'modules/' . $aModule['path'], $aFiles);
  72. foreach($aFiles as $aFile)
  73. $oDb->insertModuleTrack($aModule['id'], $aFile);
  74. $aTmplVarsModules[] = array(
  75. 'module' => $aModule['title'],
  76. 'files' => count($aFiles)
  77. );
  78. }
  79. $sResult = $this->_oTemplate->parseHtmlByName('hash_modules.html', array(
  80. 'bx_repeat:modules' => $aTmplVarsModules
  81. ));
  82. break;
  83. }
  84. echo $sResult;
  85. exit;
  86. }
  87. public function actionExport()
  88. {
  89. $sType = bx_process_input(bx_get('type'));
  90. if(empty($sType))
  91. return echoJson([]);
  92. echoJson([
  93. 'url' => BX_DOL_URL_ROOT . bx_append_url_params($this->_oConfig->getBaseUri() . 'download', [
  94. 'type' => $sType,
  95. ]),
  96. 'eval' => $this->_oConfig->getJsObject('main') . '.onExport(oData);'
  97. ]);
  98. }
  99. public function actionDownload()
  100. {
  101. $sType = bx_process_input(bx_get('type'));
  102. if(empty($sType))
  103. return;
  104. $sMethod = '_eiExport' . bx_gen_method_name($sType);
  105. if(!method_exists($this, $sMethod))
  106. return;
  107. $mixedFull = ($mixedFull = bx_get('full')) !== false ? (int)$mixedFull != 0 : false;
  108. list($aMeta, $aData) = $this->$sMethod($mixedFull);
  109. $iNow = time();
  110. $sContent = json_encode([
  111. 'meta' => array_merge([
  112. 'version' => bx_get_ver(),
  113. 'type' => $sType,
  114. 'full' => $mixedFull,
  115. 'date' => date('D, d M Y H:i:s', $iNow),
  116. ], $aMeta),
  117. 'data' => $aData
  118. ]);
  119. header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
  120. header("Content-type: application/json");
  121. header("Content-Length: " . strlen($sContent));
  122. header("Content-Disposition: attachment; filename=\"" . $sType . ($mixedFull ? "_f" : "") . "_". date('d_m_Y', $iNow) . ".json\"");
  123. echo $sContent;
  124. exit;
  125. }
  126. public function actionImport()
  127. {
  128. $sType = bx_process_input(bx_get('type'));
  129. if(empty($sType))
  130. return echoJson([]);
  131. $aResult = $this->getPopupCodeImport($sType, [
  132. 'form_name' => 'bx-dev-' . $sType . '-import-full',
  133. 'form_action' => BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'import?type=' . $sType
  134. ]);
  135. if(!isset($aResult['code']) || (int)$aResult['code'] != 0)
  136. return echoJson($aResult);
  137. $sMethod = '_eiImport' . bx_gen_method_name($sType);
  138. if(!method_exists($this, $sMethod))
  139. return echoJson([]);
  140. echoJson($this->$sMethod($aResult['content'], $aResult['disable'] != 0));
  141. }
  142. public function getPopupCodeImport($sType, $aParams)
  143. {
  144. $sJsObject = $this->_oConfig->getJsObject('main');
  145. $sForm = $aParams['form_name'];
  146. $aForm = [
  147. 'form_attrs' => [
  148. 'id' => $sForm,
  149. 'name' => $sForm,
  150. 'action' => $aParams['form_action'],
  151. 'method' => 'post',
  152. 'enctype' => 'multipart/form-data'
  153. ],
  154. 'params' => [
  155. 'db' => [
  156. 'submit_name' => 'save'
  157. ],
  158. ],
  159. 'inputs' => [
  160. 'file' => [
  161. 'type' => 'file',
  162. 'name' => 'file',
  163. 'caption' => '',
  164. 'value' => '',
  165. ],
  166. 'disable' => [
  167. 'type' => 'switcher',
  168. 'name' => 'disable',
  169. 'caption' => _t('_bx_dev_txt_disable_before_import'),
  170. 'info' => _t('_bx_dev_txt_disable_before_import_note'),
  171. 'value' => '1',
  172. 'checked' => 0,
  173. 'db' => [
  174. 'pass' => 'Int',
  175. ],
  176. ],
  177. 'controls' => [
  178. 'type' => 'input_set', [
  179. 'type' => 'submit',
  180. 'name' => 'save',
  181. 'value' => _t('_bx_dev_btn_import_full'),
  182. ], [
  183. 'type' => 'button',
  184. 'name' => 'cancel',
  185. 'value' => _t('_bx_dev_btn_cancel'),
  186. 'attrs' => [
  187. 'class' => 'bx-def-margin-sec-left-auto',
  188. 'onclick' => '$(".bx-popup-applied:visible").dolPopupHide()'
  189. ]
  190. ]
  191. ]
  192. ]
  193. ];
  194. $oForm = new BxTemplStudioFormView($aForm);
  195. $oForm->initChecker();
  196. if($oForm->isSubmittedAndValid()) {
  197. $sError = _t('_bx_dev_err_cannot_perform');
  198. $sErrorWf = _t('_bx_dev_err_import_wrong_format');
  199. if(empty($_FILES['file']) || empty($_FILES['file']['tmp_name']))
  200. return ['code' => 1, 'msg' => $sError];
  201. $aFile = $_FILES['file'];
  202. $sFile = $aFile['tmp_name'];
  203. $rHandle = @fopen($sFile, "r");
  204. if(!$rHandle)
  205. return ['code' => 2, 'message' => $sError];
  206. $sContents = fread($rHandle, filesize($sFile));
  207. fclose($rHandle);
  208. $aContent = json_decode($sContents, true);
  209. if(!is_array($aContent) || empty($aContent['meta']) || empty($aContent['data']))
  210. return ['code' => 3, 'message' => $sErrorWf];
  211. if(empty($aContent['meta']['type']) || strcmp($aContent['meta']['type'], $sType) != 0)
  212. return ['code' => 4, 'message' => $sErrorWf];
  213. return ['code' => 0, 'content' => $aContent, 'disable' => $oForm->getCleanValue('disable')];
  214. }
  215. return [
  216. 'popup' => BxTemplStudioFunctions::getInstance()->popupBox('bx-dev-import-popup', _t('_bx_dev_txt_import_full_popup'), $this->_oTemplate->parseHtmlByName('import_popup.html', [
  217. 'js_object' => $sJsObject,
  218. 'form_id' => $sForm,
  219. 'form' => $oForm->getCode(true),
  220. ]))
  221. ];
  222. }
  223. public function isValidJson($s)
  224. {
  225. //--- Validation is disabled for now.
  226. //--- return json_decode($s, true) !== null;
  227. return true;
  228. }
  229. /**
  230. *
  231. * Methods for Export/Import feature.
  232. *
  233. */
  234. protected function _eiExportForms($bModeFull = false)
  235. {
  236. $oGrid = BxDolGrid::getObjectInstance($this->_oConfig->getObject('grid_forms'));
  237. if(!$oGrid)
  238. return [];
  239. $aForms = [];
  240. $oGrid->oDb->getForms(['type' => 'export'], $aForms, false);
  241. if(empty($aForms) || !is_array($aForms))
  242. return [];
  243. $aMeta = [
  244. 'masks' => [
  245. 'form' => ['object', 'active'],
  246. 'display' => ['display_name'],
  247. 'input' => ['object', 'name', 'privacy', 'checker_func', 'checker_params', 'checker_error', 'db_pass', 'db_params'],
  248. 'display_input' => ['display_name', 'input_name', 'visible_for_levels', 'active', 'order']
  249. ]
  250. ];
  251. if($bModeFull) {
  252. $aMeta['masks'] = [
  253. 'form' => ['object', 'module', 'title', 'action', 'form_attrs', 'submit_name', 'table', 'key', 'uri', 'uri_title', 'params', 'deletable', 'active', 'override_class_name', 'override_class_file'],
  254. 'display' => ['display_name', 'module', 'object', 'title', 'view_mode'],
  255. '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'],
  256. 'display_input' => ['display_name', 'input_name', 'visible_for_levels', 'active', 'order']
  257. ];
  258. }
  259. $aMfForm = $aMfDisplay = $aMfInput = $aMfDisplayInput = false;
  260. foreach($aMeta['masks'] as $sMask => $aMask)
  261. ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
  262. $aData = [];
  263. foreach($aForms as $aForm) {
  264. $sObject = $aForm['object'];
  265. $aDisplays = [];
  266. $oGrid->oDb->getDisplays(['type' => 'export_by_object', 'value' => $sObject], $aDisplays, false);
  267. $aListDisplays = [];
  268. $aDataDisplays = [];
  269. if(!empty($aDisplays) && is_array($aDisplays))
  270. foreach($aDisplays as $aDisplay) {
  271. $aListDisplays[] = $aDisplay['display_name'];
  272. $aDataDisplays[] = $aMfDisplay !== false ? array_intersect_key($aDisplay, $aMfDisplay) : $aDisplay;
  273. }
  274. $aInputs = [];
  275. $oGrid->oDb->getInputs(['type' => 'dump_inputs', 'value' => $sObject], $aInputs, false);
  276. $aDataInputs = [];
  277. if(!empty($aInputs) && is_array($aInputs))
  278. foreach($aInputs as $aInput)
  279. $aDataInputs[] = $aMfInput !== false ? array_intersect_key($aInput, $aMfInput) : $aInput;
  280. $aDisplayInputs = [];
  281. $oGrid->oDb->getInputs(['type' => 'dump_display_inputs', 'displays' => $aListDisplays], $aDisplayInputs, false);
  282. $aDataDisplayInputs = [];
  283. if(!empty($aDisplayInputs) && is_array($aDisplayInputs))
  284. foreach($aDisplayInputs as $aDisplayInput)
  285. $aDataDisplayInputs[] = $aMfDisplayInput !== false ? array_intersect_key($aDisplayInput, $aMfDisplayInput) : $aDisplayInput;
  286. $aData[] = [
  287. 'form' => $aMfForm !== false ? array_intersect_key($aForm, $aMfForm) : $aForm,
  288. 'displays' => $aDataDisplays,
  289. 'inputs' => $aDataInputs,
  290. 'display_inputs' => $aDataDisplayInputs
  291. ];
  292. }
  293. return [$aMeta, $aData];
  294. }
  295. protected function _eiExportMenus($bModeFull = false)
  296. {
  297. $oGrid = BxDolGrid::getObjectInstance($this->_oConfig->getObject('grid_nav_menus'));
  298. if(!$oGrid)
  299. return [];
  300. $aMenus = [];
  301. $oGrid->oDb->getMenus(['type' => 'export'], $aMenus, false);
  302. if(empty($aMenus) || !is_array($aMenus))
  303. return [];
  304. $aMeta = [
  305. 'masks' => [
  306. 'menu' => ['object', 'active'],
  307. 'set' => ['set_name'],
  308. 'item' => ['set_name', 'name', 'visible_for_levels', 'visibility_custom', 'hidden_on', 'hidden_on_cxt', 'hidden_on_pt', 'hidden_on_col', 'active', 'active_api', 'order']
  309. ]
  310. ];
  311. if($bModeFull) {
  312. $aMeta['masks'] = [
  313. 'menu' => ['object', 'title', 'set_name', 'module', 'template_id', 'deletable', 'active', 'override_class_name', 'override_class_file'],
  314. 'set' => ['set_name', 'module', 'title', 'deletable'],
  315. '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']
  316. ];
  317. }
  318. $aMfMenu = $aMfSet = $aMfItem = false;
  319. foreach($aMeta['masks'] as $sMask => $aMask)
  320. ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
  321. $aData = [];
  322. foreach($aMenus as $aMenu) {
  323. $aSet = [];
  324. $oGrid->oDb->getSets(['type' => 'export_by_name', 'value' => $aMenu['set_name']], $aSet, false);
  325. $aDataItems = [];
  326. if(!empty($aSet) && is_array($aSet)) {
  327. $aItems = [];
  328. $oGrid->oDb->getItems(['type' => 'export_by_set_name', 'value' => $aSet['set_name']], $aItems, false);
  329. if(!empty($aItems) && is_array($aItems))
  330. foreach($aItems as $aItem)
  331. $aDataItems[] = $aMfItem !== false ? array_intersect_key($aItem, $aMfItem) : $aItem;
  332. }
  333. $aData[] = [
  334. 'menu' => $aMfMenu !== false ? array_intersect_key($aMenu, $aMfMenu) : $aMenu,
  335. 'set' => !empty($aSet) && $aMfSet !== false ? array_intersect_key($aSet, $aMfSet) : $aSet,
  336. 'items' => $aDataItems,
  337. ];
  338. }
  339. return [$aMeta, $aData];
  340. }
  341. protected function _eiExportPages($bModeFull = false)
  342. {
  343. bx_import('BuilderPage', $this->_aModule);
  344. $oBuilderPage = new BxDevBuilderPage(['type' => '', 'page' => '', 'url' => '']);
  345. if(!$oBuilderPage)
  346. return [];
  347. $oBpDb = $oBuilderPage->getDb();
  348. $aPages = $oBpDb->getPages(['type' => 'export']);
  349. if(empty($aPages) || !is_array($aPages))
  350. return [];
  351. $aMeta = [
  352. 'masks' => [
  353. 'page' => ['object', 'visible_for_levels'],
  354. 'block' => ['object', 'cell_id', 'module', 'title_system', 'title', 'visible_for_levels', 'hidden_on', 'active', 'active_api', 'order']
  355. ]
  356. ];
  357. if($bModeFull) {
  358. $aMeta['masks'] = [
  359. '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'],
  360. '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']
  361. ];
  362. }
  363. $aMfPage = $aMfBlock = false;
  364. foreach($aMeta['masks'] as $sMask => $aMask)
  365. ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
  366. $aData = [];
  367. foreach($aPages as $aPage) {
  368. $aBlocks = [];
  369. $oBpDb->getBlocks(['type' => 'export_by_object', 'value' => $aPage['object']], $aBlocks, false);
  370. $aDataBlocks = [];
  371. if(!empty($aBlocks) && is_array($aBlocks))
  372. foreach($aBlocks as $aBlock)
  373. $aDataBlocks[] = $aMfBlock !== false ? array_intersect_key($aBlock, $aMfBlock) : $aBlock;
  374. $aData[] = [
  375. 'page' => $aMfPage !== false ? array_intersect_key($aPage, $aMfPage) : $aPage,
  376. 'blocks' => $aDataBlocks,
  377. ];
  378. }
  379. return [$aMeta, $aData];
  380. }
  381. protected function _eiImportPages($aContent, $bDisable = false)
  382. {
  383. bx_import('BuilderPage', $this->_aModule);
  384. $oBuilderPage = new BxDevBuilderPage(['type' => '', 'page' => '', 'url' => '']);
  385. if(!$oBuilderPage)
  386. return [];
  387. $oBpDb = $oBuilderPage->getDb();
  388. $bModeFull = isset($aContent['meta']['full']) && (bool)$aContent['meta']['full'] === true;
  389. $aMfMenu = $aMfSet = $aMfItem = false;
  390. foreach($aContent['meta']['masks'] as $sMask => $aMask)
  391. ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
  392. if($bDisable)
  393. $oBpDb->updateBlocks(['active' => 0, 'active_api' => 0]);
  394. $iData = 0;
  395. foreach($aContent['data'] as $aData) {
  396. $iData += 1;
  397. $sObject = $aData['page']['object'];
  398. if($bModeFull && !$oBpDb->isPageExists($sObject))
  399. $oBpDb->addPage($aData['page']);
  400. else
  401. $oBpDb->updatePageByObject($sObject, $aData['page']);
  402. foreach($aData['blocks'] as $aBlock) {
  403. $aFields = [
  404. 'object' => $aBlock['object'],
  405. 'module' => $aBlock['module'],
  406. 'title_system' => $aBlock['title_system'],
  407. 'title' => $aBlock['title']
  408. ];
  409. if($bModeFull && !$oBpDb->isBlockExists($aFields))
  410. $oBpDb->addBlock($aItem);
  411. else
  412. $oBpDb->updateBlockByFields($aFields, $aBlock);
  413. }
  414. }
  415. BxDolCacheUtilities::getInstance()->clear('db');
  416. return [
  417. 'msg' => _t('_bx_dev_msg_imported', $iData),
  418. 'eval' => $this->_oConfig->getJsObject('main') . '.onImport(oData);'
  419. ];
  420. }
  421. }
  422. /** @} */