PageRenderTime 41ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/translate/lib/controller/asset/collect.php

https://gitlab.com/alexprowars/bitrix
PHP | 493 lines | 374 code | 67 blank | 52 comment | 31 complexity | 505c965a83a3b1231f005ef80d305dcd MD5 | raw file
  1. <?php
  2. namespace Bitrix\Translate\Controller\Asset;
  3. use Bitrix\Main;
  4. use Bitrix\Main\Error;
  5. use Bitrix\Main\Localization\Loc;
  6. use Bitrix\Translate;
  7. use Bitrix\Translate\Index;
  8. /**
  9. * Harvester of the lang folder disposition.
  10. */
  11. class Collect
  12. extends Translate\Controller\Action
  13. implements Translate\Controller\ITimeLimit, Translate\Controller\IProcessParameters
  14. {
  15. use Translate\Controller\Stepper;
  16. use Translate\Controller\ProcessParams;
  17. /** @var string */
  18. private $seekPathLangId;
  19. /** @var string */
  20. private $languageId;
  21. /** @var bool */
  22. private $convertEncoding;
  23. /** @var string */
  24. private $encoding;
  25. /** @var string */
  26. private $assemblyDate;
  27. /** @var bool */
  28. private $packFile;
  29. /** @var string */
  30. private $tmpFolderPath;
  31. /** @var int */
  32. private $totalFileCount;
  33. /** @var string */
  34. public static $documentRoot;
  35. /** @var boolean */
  36. public static $useTranslationRepository;
  37. /** @var string[] */
  38. public static $enabledLanguages;
  39. /** @var string[] */
  40. public static $allowedEncodings;
  41. /** @var string[] */
  42. public static $translationRepositoryLanguages;
  43. /**
  44. * \Bitrix\Main\Engine\Action constructor.
  45. *
  46. * @param string $name Action name.
  47. * @param Main\Engine\Controller $controller Parent controller object.
  48. * @param array $config Additional configuration.
  49. */
  50. public function __construct($name, Main\Engine\Controller $controller, $config = array())
  51. {
  52. $this->keepField([
  53. 'seekPathLangId', 'convertEncoding', 'encoding', 'assemblyDate',
  54. 'languageId', 'packFile', 'tmpFolderPath', 'totalFileCount'
  55. ]);
  56. parent::__construct($name, $controller, $config);
  57. self::$useTranslationRepository = Main\Localization\Translation::useTranslationRepository();
  58. self::$enabledLanguages = Translate\Config::getEnabledLanguages();
  59. self::$allowedEncodings = Translate\Config::getAllowedEncodings();
  60. self::$documentRoot = rtrim(Translate\IO\Path::tidy(Main\Application::getDocumentRoot()), '/');
  61. if (self::$useTranslationRepository)
  62. {
  63. self::$translationRepositoryLanguages = Translate\Config::getTranslationRepositoryLanguages();
  64. }
  65. }
  66. /**
  67. * Runs controller action.
  68. *
  69. * @param string $path Stating path.
  70. * @param boolean $runBefore Flag to run onBeforeRun event handler.
  71. * @return array
  72. */
  73. public function run($path = '', $runBefore = false)
  74. {
  75. if ($runBefore)
  76. {
  77. $this->onBeforeRun();
  78. }
  79. if (empty($path))
  80. {
  81. $path = Grabber::START_PATH;
  82. }
  83. $path = '/'. trim($path, '/.\\');
  84. if ($this->isNewProcess)
  85. {
  86. $this->clearProgressParameters();
  87. $this->totalItems = 0;
  88. $this->processedItems = 0;
  89. $this->totalFileCount = 0;
  90. // language
  91. $languageId = $this->controller->getRequest()->get('languageId');
  92. if (empty($languageId))
  93. {
  94. $this->addError(new Main\Error(Loc::getMessage('TR_ERROR_SELECT_LANGUAGE')));
  95. }
  96. if (!in_array($languageId, self::$enabledLanguages))
  97. {
  98. $this->addError(new Main\Error(Loc::getMessage('TR_ERROR_LANGUAGE_ID')));
  99. }
  100. else
  101. {
  102. $this->languageId = $languageId;
  103. }
  104. // convert encoding
  105. $this->convertEncoding = ($this->controller->getRequest()->get('convertEncoding') === 'Y');
  106. // encoding
  107. $encoding = $this->controller->getRequest()->get('encoding');
  108. if ($encoding !== null && in_array($encoding, self::$allowedEncodings))
  109. {
  110. $this->encoding = $encoding;
  111. }
  112. elseif ($this->convertEncoding)
  113. {
  114. $this->addError(new Main\Error(Loc::getMessage('TR_ERROR_ENCODING')));
  115. }
  116. if ($this->convertEncoding)
  117. {
  118. if (self::$useTranslationRepository)
  119. {
  120. $encodingIn = Main\Localization\Translation::getSourceEncoding($this->languageId);
  121. $encodingOut = $this->encoding;
  122. if ($encodingIn === 'utf-8' && $encodingOut !== 'utf-8')
  123. {
  124. $this->addError(new Error(Loc::getMessage('TR_ERROR_LANGUAGE_CHARSET_NON_UTF')));
  125. }
  126. }
  127. elseif (Translate\Config::isUtfMode())
  128. {
  129. $encodingIn = 'utf-8';
  130. $encodingOut = $this->encoding;
  131. if (Translate\Config::getCultureEncoding($this->languageId) !== 'utf-8')
  132. {
  133. $this->addError(new Error(Loc::getMessage('TR_ERROR_LANGUAGE_CHARSET_NON_UTF')));
  134. }
  135. }
  136. else
  137. {
  138. $encodingIn = Translate\Config::getCultureEncoding($this->languageId);
  139. if (!$encodingIn)
  140. {
  141. $encodingIn = Main\Localization\Translation::getCurrentEncoding();
  142. }
  143. $this->encoding = $encodingOut = 'utf-8';
  144. }
  145. $this->convertEncoding = (mb_strtolower($encodingIn) !== mb_strtolower($encodingOut));
  146. }
  147. // assembly date
  148. $assemblyDate = $this->controller->getRequest()->get('assemblyDate');
  149. if ($assemblyDate !== null && preg_replace("/[\D]+/", "", $assemblyDate) && mb_strlen($assemblyDate) == 8)
  150. {
  151. $this->assemblyDate = $assemblyDate;
  152. }
  153. else
  154. {
  155. $this->addError(new Main\Error(Loc::getMessage('TR_ERROR_LANGUAGE_DATE')));
  156. }
  157. // pack
  158. $this->packFile = ($this->controller->getRequest()->get('packFile') === 'Y');
  159. if (!$this->hasErrors())
  160. {
  161. $exportFolder = Translate\Config::getExportFolder();
  162. if (!empty($exportFolder))
  163. {
  164. $tempDir = new Translate\IO\Directory($exportFolder.'/'.$this->languageId);
  165. if ($tempDir->isExists())
  166. {
  167. $tempDir->wipe();
  168. }
  169. else
  170. {
  171. $tempDir->create();
  172. }
  173. }
  174. else
  175. {
  176. $tempDir = Translate\IO\Directory::generateTemporalDirectory('translate');
  177. $tempDir = $tempDir->createSubdirectory($this->languageId);
  178. }
  179. $this->tmpFolderPath = $tempDir->getPhysicalPath(). '/';
  180. if (!$tempDir->isExists())
  181. {
  182. $this->addError(
  183. new Error(Loc::getMessage('TR_ERROR_CREATE_TEMP_FOLDER', array('#PATH#' => $this->tmpFolderPath)))
  184. );
  185. }
  186. }
  187. if (!$this->hasErrors())
  188. {
  189. $fileDateMarkFullPath = $this->tmpFolderPath.
  190. str_replace('#LANG_ID#', $this->languageId, Translate\SUPD_LANG_DATE_MARK);
  191. Translate\IO\Path::checkCreatePath(dirname($fileDateMarkFullPath));
  192. $fileDateMark = new Main\IO\File($fileDateMarkFullPath);
  193. if ($fileDateMark->putContents($assemblyDate) === false)
  194. {
  195. $this->addError(
  196. new Error(Loc::getMessage('TR_ERROR_OPEN_FILE', array('#FILE#' => $fileDateMarkFullPath)))
  197. );
  198. }
  199. }
  200. $this->totalItems = (int)Index\Internals\PathLangTable::getCount(array('=%PATH' => $path.'%'));
  201. $this->processedItems = 0;
  202. $this->saveProgressParameters();
  203. return array(
  204. 'STATUS' => ($this->totalItems > 0 ? Translate\Controller\STATUS_PROGRESS : Translate\Controller\STATUS_COMPLETED),
  205. 'PROCESSED_ITEMS' => 0,
  206. 'TOTAL_ITEMS' => $this->totalItems,
  207. );
  208. }
  209. $progressParams = $this->getProgressParameters();
  210. $this->languageId = $progressParams['languageId'];
  211. $this->convertEncoding = $progressParams['convertEncoding'];
  212. $this->encoding = $progressParams['encoding'];
  213. $this->assemblyDate = $progressParams['assemblyDate'];
  214. $this->packFile = $progressParams['packFile'];
  215. $this->tmpFolderPath = $progressParams['tmpFolderPath'];
  216. if (isset($progressParams['totalItems']) && (int)$progressParams['totalItems'] > 0)
  217. {
  218. $this->totalItems = (int)$progressParams['totalItems'];
  219. $this->processedItems = (int)$progressParams['processedItems'];
  220. $this->totalFileCount = (int)$progressParams['totalFileCount'];
  221. }
  222. if (isset($progressParams['seekPathLangId']))
  223. {
  224. $this->seekPathLangId = $progressParams['seekPathLangId'];
  225. }
  226. return $this->performStep('runCollecting', array('path' => $path));
  227. }
  228. /**
  229. * Copying lang files.
  230. *
  231. * @param array $params Parameters.
  232. * @return array
  233. */
  234. private function runCollecting(array $params = [])
  235. {
  236. if ($this->convertEncoding)
  237. {
  238. $sourceEncoding = Main\Localization\Translation::getSourceEncoding($this->languageId);
  239. }
  240. if (isset($params['path']))
  241. {
  242. $path = $params['path'];
  243. }
  244. else
  245. {
  246. $path = Grabber::START_PATH;
  247. }
  248. $pathFilter = array(
  249. '=%PATH' => $path.'%'
  250. );
  251. if (!empty($this->seekPathLangId))
  252. {
  253. $pathFilter['>ID'] = $this->seekPathLangId;
  254. }
  255. $cachePathLangRes = Index\Internals\PathLangTable::getList(array(
  256. 'filter' => $pathFilter,
  257. 'order' => array('ID' => 'ASC'),
  258. 'select' => ['ID', 'PATH'],
  259. ));
  260. $processedItemCount = 0;
  261. while ($pathLang = $cachePathLangRes->fetch())
  262. {
  263. foreach ($this->lookThroughLangFolder($pathLang['PATH']. '/'.$this->languageId) as $filePaths)
  264. {
  265. foreach ($filePaths as $langFilePath => $fullPath)
  266. {
  267. $targetFolder = new Main\IO\Directory($this->tmpFolderPath. dirname($langFilePath));
  268. if (!$targetFolder->isExists())
  269. {
  270. $targetFolder->create();
  271. }
  272. $source = new Main\IO\File($fullPath);
  273. $target = new Main\IO\File($targetFolder->getPhysicalPath(). '/'. basename($langFilePath));
  274. try
  275. {
  276. $content = $source->getContents();
  277. $content = str_replace(array("\r\n", "\r"), array("\n", "\n"), $content);
  278. if ($this->convertEncoding)
  279. {
  280. $content = Main\Text\Encoding::convertEncoding($content, $sourceEncoding, $this->encoding);
  281. }
  282. $target->putContents($content);
  283. $this->totalFileCount ++;
  284. }
  285. catch (Main\IO\IoException $exception)
  286. {
  287. $this->addError(new Main\Error($exception->getMessage()));
  288. }
  289. // check user abortion
  290. if (connection_status() !== CONNECTION_NORMAL)
  291. {
  292. throw new Main\SystemException('Process has been broken course user aborted connection.');
  293. }
  294. }
  295. }
  296. $processedItemCount ++;
  297. if ($this->instanceTimer()->hasTimeLimitReached())
  298. {
  299. $this->seekPathLangId = (int)$pathLang['ID'];
  300. break;
  301. }
  302. }
  303. $this->processedItems += $processedItemCount;
  304. $result = array(
  305. 'PROCESSED_ITEMS' => $this->processedItems,
  306. 'TOTAL_ITEMS' => $this->totalItems,
  307. );
  308. if ($this->instanceTimer()->hasTimeLimitReached() !== true)
  309. {
  310. $this->declareAccomplishment();
  311. // we have to continue process in next action
  312. $this->processToken = null;
  313. $this->seekPathLangId = null;
  314. $this->saveProgressParameters();
  315. $messagePlaceholders = array(
  316. '#TOTAL_FILES#' => $this->totalFileCount,
  317. '#LANG#' => mb_strtoupper($this->languageId),
  318. '#PATH#' => '~tmp~',
  319. );
  320. $result['SUMMARY'] = Loc::getMessage('TR_LANGUAGE_COLLECTED_FOLDER', $messagePlaceholders);
  321. }
  322. return $result;
  323. }
  324. /**
  325. * Runs through lang folder and collects full path to lang files.
  326. *
  327. * @param string $langFolderRelPath Relative project path of the language folder.
  328. *
  329. * @return \Generator|array
  330. */
  331. private function lookThroughLangFolder($langFolderRelPath)
  332. {
  333. $files = [];
  334. $folders = [];
  335. $langFolderFullPath = Translate\IO\Path::tidy(self::$documentRoot.'/'.$langFolderRelPath);
  336. $storeFolderRelPath = str_replace(Grabber::START_PATH, '', $langFolderRelPath);
  337. if (self::$useTranslationRepository && in_array($this->languageId, self::$translationRepositoryLanguages))
  338. {
  339. $langFolderFullPath = Main\Localization\Translation::convertLangPath($langFolderFullPath, $this->languageId);
  340. }
  341. $childrenList = Translate\IO\FileSystemHelper::getFileList($langFolderFullPath);
  342. if (!empty($childrenList))
  343. {
  344. foreach ($childrenList as $fullPath)
  345. {
  346. $name = basename($fullPath);
  347. if (in_array($name, Translate\IGNORE_FS_NAMES))
  348. {
  349. continue;
  350. }
  351. if ((mb_substr($name, -4) === '.php') && is_file($fullPath))
  352. {
  353. $files[$storeFolderRelPath.'/'.$name] = $fullPath;
  354. }
  355. }
  356. }
  357. // dir only
  358. $childrenList = Translate\IO\FileSystemHelper::getFolderList($langFolderFullPath);
  359. if (!empty($childrenList))
  360. {
  361. $ignoreDev = implode('|', Translate\IGNORE_MODULE_NAMES);
  362. foreach ($childrenList as $fullPath)
  363. {
  364. $name = basename($fullPath);
  365. if (in_array($name, Translate\IGNORE_FS_NAMES))
  366. {
  367. continue;
  368. }
  369. $relPath = $langFolderRelPath.'/'.$name;
  370. if (!is_dir($fullPath))
  371. {
  372. continue;
  373. }
  374. if (in_array($relPath, Translate\IGNORE_BX_NAMES))
  375. {
  376. continue;
  377. }
  378. // /bitrix/modules/[smth]/dev/
  379. if (preg_match("#^bitrix/modules/[^/]+/({$ignoreDev})#", trim($relPath, '/')))
  380. {
  381. continue;
  382. }
  383. if (in_array($name, Translate\IGNORE_LANG_NAMES))
  384. {
  385. continue;
  386. }
  387. $folders[$langFolderRelPath.'/'.$name] = $langFolderRelPath.'/'.$name;
  388. }
  389. }
  390. if (count($files) > 0)
  391. {
  392. yield $files;
  393. }
  394. if (count($folders) > 0)
  395. {
  396. foreach ($folders as $subFolderPath)
  397. {
  398. foreach ($this->lookThroughLangFolder($subFolderPath) as $subFiles)// go deeper
  399. {
  400. yield $subFiles;
  401. }
  402. }
  403. }
  404. }
  405. /**
  406. * Returns progress option name
  407. *
  408. * @return string
  409. */
  410. public function getProgressParameterOptionName()
  411. {
  412. $controller = $this->getController();
  413. return $controller::SETTING_ID;
  414. }
  415. }