PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/components/bitrix/rest.configuration.import/class.php

https://gitlab.com/alexprowars/bitrix
PHP | 602 lines | 546 code | 55 blank | 1 comment | 81 complexity | 5ad090a7136da8611898bb7dfa16c421 MD5 | raw file
  1. <?
  2. if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true)
  3. {
  4. die();
  5. }
  6. use Bitrix\Main\Application;
  7. use Bitrix\Main\ArgumentException;
  8. use Bitrix\Main\ErrorCollection;
  9. use Bitrix\Main\Localization\Loc;
  10. use Bitrix\Main\Text\Encoding;
  11. use Bitrix\Main\Web\Uri;
  12. use Bitrix\Main\Error;
  13. USE Bitrix\Main\IO\File;
  14. use Bitrix\Main\Web\Json;
  15. use Bitrix\Rest\AppTable;
  16. use Bitrix\Rest\Configuration\Manifest;
  17. use Bitrix\Rest\Configuration\Helper;
  18. use Bitrix\Rest\Configuration\Setting;
  19. use Bitrix\Rest\Configuration\Structure;
  20. use Bitrix\Rest\Configuration\Action\Import;
  21. use Bitrix\Rest\Marketplace\Client;
  22. use Bitrix\Rest\AppLogTable;
  23. use Bitrix\Disk\Driver;
  24. Loc::loadMessages(__FILE__);
  25. class CRestConfigurationImportComponent extends CBitrixComponent
  26. {
  27. /** @var ErrorCollection $errors */
  28. protected $errors;
  29. protected $contextPostfix = 'import';
  30. protected $type = 'configuration';
  31. protected function checkRequiredParams()
  32. {
  33. return true;
  34. }
  35. protected function getContextPostFix()
  36. {
  37. return $this->contextPostfix.$this->arParams['MANIFEST_CODE'].$this->arParams['APP'];
  38. }
  39. protected function getContext()
  40. {
  41. return Helper::getInstance()->getContextUser($this->getContextPostFix());
  42. }
  43. protected function prepareConfigurationUrl($url)
  44. {
  45. if ("UTF-8" !== mb_strtoupper(LANG_CHARSET))
  46. {
  47. $uri = new Uri($url);
  48. $path = $uri->getPath();
  49. $name = bx_basename($path);
  50. $prepareName = Encoding::convertEncoding($name, LANG_CHARSET, "UTF-8");
  51. $prepareName = rawurlencode($prepareName);
  52. $path = str_replace($name, $prepareName, $path);
  53. $uri->setPath($path);
  54. $url = $uri->getUri();
  55. }
  56. return $url;
  57. }
  58. private function getArchive($url, $app = [])
  59. {
  60. $result = [];
  61. $fileInfo = \CFile::MakeFileArray($url);
  62. if (!empty($fileInfo['tmp_name']))
  63. {
  64. $result['ERRORS_UPLOAD_FILE'] = \CFile::CheckFile(
  65. $fileInfo,
  66. 0,
  67. [
  68. 'application/gzip',
  69. 'application/x-gzip',
  70. 'application/zip',
  71. 'application/x-zip-compressed',
  72. 'application/x-tar'
  73. ]
  74. );
  75. if ($result['ERRORS_UPLOAD_FILE'] === '')
  76. {
  77. $context = $this->getContext();
  78. $setting = new Setting($context);
  79. $setting->deleteFull();
  80. $structure = new Structure($context);
  81. if ($structure->unpack($fileInfo))
  82. {
  83. $result['IMPORT_CONTEXT'] = $context;
  84. $result['APP'] = $app;
  85. $result['IMPORT_FOLDER_FILES'] = $structure->getFolder();
  86. $result['IMPORT_ACCESS'] = true;
  87. }
  88. }
  89. }
  90. return $result;
  91. }
  92. private function registerImport(array $app, array $config)
  93. {
  94. $result = [];
  95. if (!empty($config['MANIFEST']['CODE']))
  96. {
  97. $additional = [];
  98. if (!empty($this->arParams['ADDITIONAL']) && is_array($this->arParams['ADDITIONAL']))
  99. {
  100. $additional = $this->arParams['ADDITIONAL'];
  101. }
  102. $userId = 0;
  103. global $USER;
  104. if ($USER->isAuthorized())
  105. {
  106. $userId = $USER->getId();
  107. }
  108. $import = new Import();
  109. $register = $import->register(
  110. $config,
  111. $additional,
  112. $userId,
  113. $app['CODE'] ?? '',
  114. false
  115. );
  116. if ($register['processId'] > 0)
  117. {
  118. $result['IMPORT_PROCESS_ID'] = $register['processId'];
  119. $result['IMPORT_ACCESS'] = true;
  120. $result['APP'] = $app;
  121. $result['MANIFEST_CODE'] = $config['MANIFEST']['CODE'];
  122. }
  123. else
  124. {
  125. $this->errors->setError(new Error(Loc::getMessage('REST_CONFIGURATION_IMPORT_ERROR_PROCESS_REGISTRATION')));
  126. return false;
  127. }
  128. }
  129. else
  130. {
  131. $this->errors->setError(new Error(Loc::getMessage('REST_CONFIGURATION_IMPORT_MANIFEST_NOT_FOUND')));
  132. return false;
  133. }
  134. return $result;
  135. }
  136. private function isManifestAccess($code = '')
  137. {
  138. $access = Manifest::checkAccess(Manifest::ACCESS_TYPE_IMPORT, $code);
  139. if ($access['result'] !== true)
  140. {
  141. $this->errors->setError(
  142. new Error(
  143. $access['message'] !== ''
  144. ? htmlspecialcharsbx($access['message'])
  145. : Loc::getMessage('REST_CONFIGURATION_IMPORT_ACCESS_DENIED')
  146. )
  147. );
  148. return false;
  149. }
  150. return true;
  151. }
  152. protected function prepareResult()
  153. {
  154. $result = [
  155. 'IMPORT_ACCESS' => false,
  156. 'IMPORT_FOLDER_FILES' => '',
  157. 'IMPORT_MANIFEST_FILE' => [],
  158. 'MANIFEST' => [],
  159. 'MANIFEST_CODE' => '',
  160. ];
  161. $title = '';
  162. $result['MAX_FILE_SIZE']['MEGABYTE'] = Helper::getInstance()->getMaxFileSize();
  163. $result['MAX_FILE_SIZE']['BYTE'] = round($result['MAX_FILE_SIZE']['MEGABYTE']*1024*1024, 2);
  164. if(!empty($this->arParams['MANIFEST_CODE']))
  165. {
  166. $result['MANIFEST'] = Manifest::get($this->arParams['MANIFEST_CODE']);
  167. if(is_null($result['MANIFEST']))
  168. {
  169. $this->errors->setError(new Error(Loc::getMessage('REST_CONFIGURATION_IMPORT_MANIFEST_NOT_FOUND')));
  170. return false;
  171. }
  172. else
  173. {
  174. $result['MANIFEST_CODE'] = $result['MANIFEST']['CODE'];
  175. }
  176. }
  177. if ($this->arParams['MODE'] == 'ROLLBACK')
  178. {
  179. if (!$this->isManifestAccess($result['MANIFEST_CODE']))
  180. {
  181. return false;
  182. }
  183. $expertMode = ($this->request->getQuery('expert') && $this->request->getQuery('expert') == 'Y') ? true : false;
  184. $result['ROLLBACK_ITEMS'] = [];
  185. $appList = Helper::getInstance()->getBasicAppList();
  186. $manifestCode = array_search($this->arParams['ROLLBACK_APP'], $appList);
  187. if($manifestCode !== false)
  188. {
  189. $storage = Helper::getInstance()->getStorageBackup();
  190. if($storage)
  191. {
  192. $fakeSecurityContext = Driver::getInstance()->getFakeSecurityContext();
  193. foreach($storage->getChildren($fakeSecurityContext, []) as $child)
  194. {
  195. if($child instanceof \Bitrix\Disk\Folder)
  196. {
  197. $createTime = $child->getCreateTime();
  198. $result['ROLLBACK_ITEMS'][] = [
  199. 'ID' => 'DEFAULT_' . $child->getId(),
  200. 'CODE' => $child->getId(),
  201. 'NAME' => Loc::getMessage(
  202. 'REST_CONFIGURATION_ROLLBACK_DEFAULT_TITLE',
  203. [
  204. '#CREATE_TIME#' => $createTime->toString()
  205. ]
  206. ),
  207. 'IS_DEFAULT' => 'Y'
  208. ];
  209. }
  210. }
  211. }
  212. }
  213. if(empty($result['ROLLBACK_ITEMS']) && $manifestCode !== false)
  214. {
  215. $res = AppTable::getList(
  216. [
  217. 'filter' => array(
  218. "=CODE" => $appList[$manifestCode],
  219. "!=STATUS" => AppTable::STATUS_LOCAL
  220. ),
  221. ]
  222. );
  223. if($appInfo = $res->fetch())
  224. {
  225. $clean = true;
  226. $checkResult = AppTable::checkUninstallAvailability($appInfo['ID'], $clean);
  227. if($checkResult->isEmpty())
  228. {
  229. AppTable::uninstall($appInfo['ID'], $clean);
  230. $appFields = [
  231. 'ACTIVE' => 'N',
  232. 'INSTALLED' => 'N',
  233. ];
  234. AppTable::update($appInfo['ID'], $appFields);
  235. AppLogTable::log($appInfo['ID'], AppLogTable::ACTION_TYPE_UNINSTALL);
  236. unset($result);
  237. $result['DELETE_FINISH'] = true;
  238. }
  239. }
  240. }
  241. elseif(check_bitrix_sessid())
  242. {
  243. $id = $this->request->getPost("ROLLBACK_ID");
  244. if($id !== NULL)
  245. {
  246. $key = array_search($id, array_column($result['ROLLBACK_ITEMS'],'ID'));
  247. if($key !== false)
  248. {
  249. $result['ROLLBACK_SELECTED'] = $result['ROLLBACK_ITEMS'][$key];
  250. if($result['ROLLBACK_SELECTED']['IS_DEFAULT'] == 'N')
  251. {
  252. $this->arParams['APP'] = $result['ROLLBACK_SELECTED']['CODE'];
  253. }
  254. else
  255. {
  256. $result['IMPORT_ROLLBACK_DISK_FOLDER_ID'] = $result['ROLLBACK_SELECTED']['CODE'];
  257. $result['IMPORT_ROLLBACK_STORAGE_PARAMS'] = Helper::getInstance()->getStorageBackupParam();
  258. }
  259. }
  260. }
  261. }
  262. if(!$expertMode && !empty($result['ROLLBACK_ITEMS']))
  263. {
  264. $item = reset($result['ROLLBACK_ITEMS']);
  265. if($item['IS_DEFAULT'] == 'N')
  266. {
  267. $this->arParams['APP'] = $item['CODE'];
  268. }
  269. else
  270. {
  271. $result['IMPORT_ROLLBACK_DISK_FOLDER_ID'] = $item['CODE'];
  272. $result['IMPORT_ROLLBACK_STORAGE_PARAMS'] = Helper::getInstance()->getStorageBackupParam();
  273. }
  274. }
  275. if($manifestCode !== false)
  276. {
  277. $result['UNINSTALL_APP_ON_FINISH'] = $appList[$manifestCode];
  278. }
  279. }
  280. if (
  281. $this->arParams['MODE'] === 'ZIP'
  282. && (int)$this->arParams['ZIP_ID'] > 0
  283. )
  284. {
  285. $site = Client::getSite((int)$this->arParams['ZIP_ID']);
  286. if (!$this->isManifestAccess($site['CONFIG']['MANIFEST']['CODE'] ?? ''))
  287. {
  288. return false;
  289. }
  290. $app = AppTable::getByClientId($site['APP_CODE']);
  291. if ($app['ACTIVE'] === 'Y' && $app['INSTALLED'] === 'Y')
  292. {
  293. if (!empty($site['CONFIG']))
  294. {
  295. $registerResult = $this->registerImport($app, $site['CONFIG']);
  296. if ($registerResult === false)
  297. {
  298. return $result;
  299. }
  300. else
  301. {
  302. $result = array_merge($result, $registerResult);
  303. }
  304. }
  305. elseif (!empty($site['PATH']))
  306. {
  307. $result = array_merge($result, $this->getArchive($site['PATH'], $app));
  308. }
  309. }
  310. else
  311. {
  312. $result['INSTALL_APP'] = $site['APP_CODE'];
  313. $title = Loc::getMessage('REST_CONFIGURATION_IMPORT_PREPARATION_TITLE');
  314. }
  315. }
  316. elseif (!empty($this->arParams['APP']))
  317. {
  318. if (!$this->isManifestAccess($result['MANIFEST_CODE']))
  319. {
  320. return false;
  321. }
  322. $app = AppTable::getByClientId($this->arParams['APP']);
  323. if ($app['ACTIVE'] === 'Y')
  324. {
  325. $request = Application::getInstance()->getContext()->getRequest();
  326. $check_hash = $request->getQuery("check_hash");
  327. $install_hash = $request->getQuery("install_hash");
  328. $appInfo = Client::getApp(
  329. $app['CODE'],
  330. $app['VERSION'],
  331. ($check_hash)?:false,
  332. ($install_hash)?:false
  333. );
  334. if ($appInfo)
  335. {
  336. $appInfo = $appInfo["ITEMS"];
  337. if ($appInfo['TYPE'] === AppTable::TYPE_CONFIGURATION && !empty($appInfo['CONFIG_URL']))
  338. {
  339. $url = $this->prepareConfigurationUrl($appInfo['CONFIG_URL']);
  340. $result = array_merge($result, $this->getArchive($url, $app));
  341. }
  342. }
  343. }
  344. }
  345. else
  346. {
  347. $result['IMPORT_ACCESS'] = true;
  348. if(
  349. !empty($_FILES["CONFIGURATION"])
  350. && file_exists($_FILES["CONFIGURATION"]["tmp_name"])
  351. && check_bitrix_sessid()
  352. )
  353. {
  354. if (!$this->isManifestAccess($result['MANIFEST_CODE']))
  355. {
  356. return false;
  357. }
  358. $result['ERRORS_UPLOAD_FILE'] = CFile::CheckFile(
  359. $_FILES["CONFIGURATION"],
  360. $result['MAX_FILE_SIZE']['BYTE'],
  361. [
  362. 'application/gzip',
  363. 'application/x-gzip',
  364. 'application/zip',
  365. 'application/x-zip-compressed',
  366. 'application/x-tar'
  367. ],
  368. 'gz,tar,zip'
  369. );
  370. if($result['ERRORS_UPLOAD_FILE'] === '')
  371. {
  372. try
  373. {
  374. $context = $this->getContext();
  375. $setting = new Setting($context);
  376. $setting->deleteFull();
  377. $structure = new Structure($context);
  378. if($structure->unpack($_FILES["CONFIGURATION"]))
  379. {
  380. $result['IMPORT_CONTEXT'] = $context;
  381. $result['IMPORT_FOLDER_FILES'] = $structure->getFolder();
  382. }
  383. }
  384. catch (\Exception $e)
  385. {
  386. $result['ERRORS_UPLOAD_FILE'] = $e->getMessage();
  387. }
  388. }
  389. }
  390. }
  391. if($result['IMPORT_FOLDER_FILES'])
  392. {
  393. if (!$this->isManifestAccess($result['MANIFEST_CODE']))
  394. {
  395. return false;
  396. }
  397. $fileList = scandir($result['IMPORT_FOLDER_FILES']);
  398. $key = array_search('manifest.json', $fileList);
  399. if($key !== false)
  400. {
  401. $data = file_get_contents($result['IMPORT_FOLDER_FILES'].$fileList[$key]);
  402. try
  403. {
  404. $result['IMPORT_MANIFEST_FILE'] = Json::decode($data);
  405. if(!empty($result['IMPORT_MANIFEST_FILE']))
  406. {
  407. if(!empty($result['MANIFEST']))
  408. {
  409. if($result['IMPORT_MANIFEST_FILE']['CODE'] != $result['MANIFEST']['CODE'])
  410. {
  411. $this->errors->setError(new Error(Loc::getMessage('REST_CONFIGURATION_IMPORT_MANIFEST_NOT_CURRENT')));
  412. return false;
  413. }
  414. else
  415. {
  416. $result['MANIFEST_CODE'] = htmlspecialcharsbx($result['IMPORT_MANIFEST_FILE']['CODE']);
  417. }
  418. }
  419. elseif(!empty($result['APP']['ID']))
  420. {
  421. $result['MANIFEST_CODE'] = htmlspecialcharsbx($result['IMPORT_MANIFEST_FILE']['CODE']);
  422. }
  423. }
  424. }
  425. catch(ArgumentException $e)
  426. {
  427. }
  428. }
  429. else
  430. {
  431. $this->errors->setError(new Error(Loc::getMessage('REST_CONFIGURATION_IMPORT_MANIFEST_NOT_CURRENT')));
  432. return false;
  433. }
  434. }
  435. elseif($result['IMPORT_ROLLBACK_DISK_FOLDER_ID'] && $result['IMPORT_ROLLBACK_STORAGE_PARAMS'])
  436. {
  437. if (!$this->isManifestAccess($result['MANIFEST_CODE']))
  438. {
  439. return false;
  440. }
  441. try
  442. {
  443. $storage = Driver::getInstance()->addStorageIfNotExist(
  444. $result['IMPORT_ROLLBACK_STORAGE_PARAMS']
  445. );
  446. if($storage)
  447. {
  448. $folder = $storage->getChild(
  449. [
  450. '=ID' => $result['IMPORT_ROLLBACK_DISK_FOLDER_ID']
  451. ]
  452. );
  453. if($folder)
  454. {
  455. $file = $folder->getChild(
  456. [
  457. '=NAME' => 'manifest.json'
  458. ]
  459. );
  460. if($file && $file->getFileId() > 0)
  461. {
  462. $server = Application::getInstance()->getContext()->getServer();
  463. $documentRoot = $server->getDocumentRoot();
  464. $filePath = $documentRoot.\CFile::GetPath(
  465. $file->getFileId()
  466. );
  467. if(File::isFileExists($filePath))
  468. {
  469. $manifestContent = File::getFileContents($filePath);
  470. if($manifestContent != '')
  471. {
  472. $manifest = Json::decode($manifestContent);
  473. if($manifest['CODE'])
  474. {
  475. $result['MANIFEST_CODE'] = $manifest['CODE'];
  476. $result['IMPORT_MANIFEST_FILE'] = $manifest;
  477. }
  478. }
  479. }
  480. }
  481. }
  482. }
  483. }
  484. catch (\Exception $e)
  485. {
  486. }
  487. }
  488. if (isset($this->arParams['SET_TITLE']) && $this->arParams['SET_TITLE'] == 'Y')
  489. {
  490. global $APPLICATION;
  491. if ($this->arParams['MODE'] == 'ROLLBACK')
  492. {
  493. $APPLICATION->SetTitle(Loc::getMessage('REST_CONFIGURATION_IMPORT_ROLLBACK_TITLE'));
  494. }
  495. else
  496. {
  497. if (empty($result['MANIFEST']))
  498. {
  499. if (!empty($result['IMPORT_MANIFEST_FILE']['CODE']))
  500. {
  501. $result['MANIFEST'] = Manifest::get($result['IMPORT_MANIFEST_FILE']['CODE']);
  502. }
  503. elseif (!empty($result['MANIFEST_CODE']))
  504. {
  505. $result['MANIFEST'] = Manifest::get($result['MANIFEST_CODE']);
  506. }
  507. }
  508. if ($title === '')
  509. {
  510. if (!empty($result['MANIFEST']['IMPORT_TITLE_PAGE']))
  511. {
  512. $title = $result['MANIFEST']['IMPORT_TITLE_PAGE'];
  513. }
  514. else
  515. {
  516. $title = Loc::getMessage('REST_CONFIGURATION_IMPORT_TITLE');
  517. }
  518. }
  519. $APPLICATION->SetTitle($title);
  520. }
  521. }
  522. $this->arResult = $result;
  523. return true;
  524. }
  525. protected function printErrors()
  526. {
  527. foreach ($this->errors as $error)
  528. {
  529. ShowError($error);
  530. }
  531. }
  532. public function executeComponent()
  533. {
  534. $this->errors = new ErrorCollection();
  535. if (!$this->checkRequiredParams())
  536. {
  537. $this->printErrors();
  538. return;
  539. }
  540. if (!$this->prepareResult())
  541. {
  542. $this->printErrors();
  543. return;
  544. }
  545. $this->includeComponentTemplate();
  546. }
  547. }