PageRenderTime 73ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 1ms

/controllers/admin/AdminTranslationsController.php

https://bitbucket.org/enurkov/prestashop
PHP | 2697 lines | 1932 code | 311 blank | 454 comment | 393 complexity | 92985ee301292543ad1fce51fa0b8ab4 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * 2007-2012 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2012 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. define ('TEXTAREA_SIZED', 70);
  27. class AdminTranslationsControllerCore extends AdminController
  28. {
  29. /** Name of theme by default */
  30. const DEFAULT_THEME_NAME = 'default';
  31. /** @var string : Link which list all pack of language */
  32. protected $link_lang_pack = 'http://www.prestashop.com/download/lang_packs/get_each_language_pack.php';
  33. /** @var int : number of sentence which can be translated */
  34. protected $total_expression = 0;
  35. /** @var int : number of sentence which aren't translated */
  36. protected $missing_translations = 0;
  37. /** @var array : List of ISO code for all languages */
  38. protected $all_iso_lang = array();
  39. /** @var array */
  40. protected $modules_translations = array();
  41. /** @var array : List of folder which must be ignored */
  42. protected static $ignore_folder = array('.', '..', '.svn', '.htaccess', 'index.php');
  43. /** @var array : List of theme by translation type : FRONT, BACK, ERRORS... */
  44. protected $translations_informations = array();
  45. /** @var array : List of theme by translation type : FRONT, BACK, ERRORS... */
  46. protected $translations_type_for_theme = array('front', 'modules', 'pdf', 'mails');
  47. /** @var array : List of all languages */
  48. protected $languages;
  49. /** @var array : List of all themes */
  50. protected $themes;
  51. /** @var string : Directory of selected theme */
  52. protected $theme_selected;
  53. /** @var string : Name of translations type */
  54. protected $type_selected;
  55. /** @var object : Language for the selected language */
  56. protected $lang_selected;
  57. /** @var boolean : Is true if number of var exceed the suhosin request or post limit */
  58. protected $post_limit_exceed = false;
  59. public function __construct()
  60. {
  61. $this->multishop_context = Shop::CONTEXT_ALL;
  62. parent::__construct();
  63. $this->table = 'translations';
  64. // Include all file for create or read an archive
  65. include_once(_PS_ADMIN_DIR_.'/../tools/tar/Archive_Tar.php');
  66. include_once(_PS_ADMIN_DIR_.'/../tools/pear/PEAR.php');
  67. }
  68. /*
  69. * Set the type which is selected
  70. */
  71. public function setTypeSelected($type_selected)
  72. {
  73. $this->type_selected = $type_selected;
  74. }
  75. /**
  76. * AdminController::initContent() override
  77. * @see AdminController::initContent()
  78. */
  79. public function initContent()
  80. {
  81. if (!is_null($this->type_selected))
  82. {
  83. $method_name = 'initForm'.$this->type_selected;
  84. if (method_exists($this, $method_name))
  85. $this->content = $this->initForm($method_name);
  86. else
  87. {
  88. $this->errors[] = sprintf(Tools::displayError('"%s" does not exist. Maybe you typed the URL manually.'), $this->type_selected);
  89. $this->content = $this->initMain();
  90. }
  91. }
  92. else
  93. $this->content = $this->initMain();
  94. $this->context->smarty->assign(array('content' => $this->content));
  95. }
  96. /**
  97. * This function create vars by default and call the good method for generate form
  98. *
  99. * @param $method_name
  100. * @return call the method $this->method_name()
  101. */
  102. public function initForm($method_name)
  103. {
  104. // Create a title for each translation page
  105. $title = sprintf(
  106. $this->l('%1$s (Language: %2$s, Theme: %3$s)'),
  107. $this->translations_informations[$this->type_selected]['name'],
  108. $this->lang_selected->name,
  109. $this->theme_selected
  110. );
  111. // Set vars for all forms
  112. $this->tpl_view_vars = array(
  113. 'lang' => $this->lang_selected->iso_code,
  114. 'title' => $title,
  115. 'type' => $this->type_selected,
  116. 'theme' => $this->theme_selected,
  117. 'post_limit_exceeded' => $this->post_limit_exceed,
  118. 'url_submit' => self::$currentIndex.'&submitTranslations'.ucfirst($this->type_selected).'=1&token='.$this->token,
  119. 'toggle_button' => $this->displayToggleButton(),
  120. 'textarea_sized' => TEXTAREA_SIZED,
  121. 'auto_translate' => ''
  122. );
  123. // Call method initForm for a type
  124. return $this->{$method_name}();
  125. }
  126. /**
  127. * AdminController::initToolbar() override
  128. * @see AdminController::initToolbar()
  129. */
  130. public function initToolbar()
  131. {
  132. $this->toolbar_btn['save-and-stay'] = array(
  133. 'short' => 'SaveAndStay',
  134. 'href' => '#',
  135. 'desc' => $this->l('Save and stay'),
  136. );
  137. $this->toolbar_btn['save'] = array(
  138. 'href' => '#',
  139. 'desc' => $this->l('Update translations')
  140. );
  141. $this->toolbar_btn['cancel'] = array(
  142. 'href' => self::$currentIndex.'&token='.$this->token,
  143. 'desc' => $this->l('Cancel')
  144. );
  145. }
  146. /**
  147. * Generate the Main page
  148. */
  149. public function initMain()
  150. {
  151. // Block add/update a language
  152. $packs_to_install = array();
  153. $packs_to_update = array();
  154. $token = Tools::getAdminToken('AdminLanguages'.(int)Tab::getIdFromClassName('AdminLanguages').(int)$this->context->employee->id);
  155. $file_name = $this->link_lang_pack.'?version='._PS_VERSION_;
  156. $array_stream_context = array('http' => array('method' => 'GET', 'timeout' => 5));
  157. if ($lang_packs = Tools::file_get_contents($file_name, false, @stream_context_create($array_stream_context)))
  158. // Notice : for php < 5.2 compatibility, Tools::jsonDecode. The second parameter to true will set us
  159. if ($lang_packs != '' && $lang_packs = Tools::jsonDecode($lang_packs, true))
  160. foreach ($lang_packs as $key => $lang_pack)
  161. {
  162. if (!Language::isInstalled($lang_pack['iso_code']))
  163. $packs_to_install[$key] = $lang_pack;
  164. else
  165. $packs_to_update[$key] = $lang_pack;
  166. }
  167. $this->tpl_view_vars = array(
  168. 'theme_default' => self::DEFAULT_THEME_NAME,
  169. 'theme_lang_dir' =>_THEME_LANG_DIR_,
  170. 'token' => $this->token,
  171. 'languages' => $this->languages,
  172. 'translations_type' => $this->translations_informations,
  173. 'translations_type_for_theme' => $this->translations_type_for_theme,
  174. 'packs_to_install' => $packs_to_install,
  175. 'packs_to_update' => $packs_to_update,
  176. 'url_submit' => self::$currentIndex.'&token='.$this->token,
  177. 'themes' => $this->themes,
  178. 'id_theme_current' => $this->context->shop->id_theme,
  179. 'url_create_language' => 'index.php?controller=AdminLanguages&addlang&token='.$token,
  180. );
  181. $this->toolbar_scroll = false;
  182. $this->base_tpl_view = 'main.tpl';
  183. return parent::renderView();
  184. }
  185. /**
  186. * This method merge each arrays of modules translation in the array of modules translations
  187. */
  188. protected function getModuleTranslations()
  189. {
  190. global $_MODULE;
  191. $name_var = $this->translations_informations[$this->type_selected]['var'];
  192. if (!isset($_MODULE) && !isset($GLOBALS[$name_var]))
  193. $GLOBALS[$name_var] = array();
  194. else if (isset($_MODULE))
  195. if (is_array($GLOBALS[$name_var]) && is_array($_MODULE))
  196. $GLOBALS[$name_var] = array_merge($GLOBALS[$name_var], $_MODULE);
  197. else
  198. $GLOBALS[$name_var] = $_MODULE;
  199. }
  200. /**
  201. * This method is only used by AdminTranslations::submitCopyLang().
  202. *
  203. * It try to create folder in new theme.
  204. *
  205. * When a translation file is copied for a module, its translation key is wrong.
  206. * We have to change the translation key and rewrite the file.
  207. *
  208. * @param string $dest file name
  209. * @return bool
  210. */
  211. protected function checkDirAndCreate($dest)
  212. {
  213. $bool = true;
  214. // To get only folder path
  215. $path = dirname($dest);
  216. // If folder wasn't already added
  217. // Do not use Tools::file_exists_cache because it changes over time!
  218. if (!file_exists($path))
  219. if (!mkdir($path, 0777, true))
  220. {
  221. $bool &= false;
  222. $this->errors[] = sprintf($this->l('Cannot create the folder "%s". Check directory writing permisions.'), $path);
  223. }
  224. return $bool;
  225. }
  226. /**
  227. * Read the Post var and write the translation file.
  228. * This method overwrites the old translation file.
  229. *
  230. * @param bool $override_file : set true if this file is a override
  231. */
  232. protected function writeTranslationFile($override_file = false)
  233. {
  234. $type = Tools::toCamelCase($this->type_selected, true);
  235. $translation_informations = $this->translations_informations[$this->type_selected];
  236. if ($override_file)
  237. $file_path = $translation_informations['override']['dir'].$translation_informations['override']['file'];
  238. else
  239. $file_path = $translation_informations['dir'].$translation_informations['file'];
  240. if (!file_exists($file_path))
  241. {
  242. if (!file_exists(dirname($file_path)) && !mkdir(dirname($file_path), 0777, true))
  243. throw new PrestaShopException(sprintf(Tools::displayError('Directory "%s" cannot be created'), dirname($file_path)));
  244. elseif (!touch($file_path))
  245. throw new PrestaShopException(sprintf(Tools::displayError('File "%s" cannot be created'), $file_path));
  246. }
  247. if ($fd = fopen($file_path, 'w'))
  248. {
  249. // Get value of button save and stay
  250. $save_and_stay = Tools::getValue('submitTranslations'.$type.'AndStay');
  251. // Get language
  252. $lang = strtolower(Tools::getValue('lang'));
  253. // Unset all POST which are not translations
  254. unset(
  255. $_POST['submitTranslations'.$type],
  256. $_POST['submitTranslations'.$type.'AndStay'],
  257. $_POST['lang'],
  258. $_POST['token'],
  259. $_POST['theme'],
  260. $_POST['type']
  261. );
  262. // Get all POST which aren't empty
  263. $to_insert = array();
  264. foreach ($_POST as $key => $value)
  265. if (!empty($value))
  266. $to_insert[$key] = $value;
  267. // translations array is ordered by key (easy merge)
  268. ksort($to_insert);
  269. $tab = $translation_informations['var'];
  270. fwrite($fd, "<?php\n\nglobal \$".$tab.";\n\$".$tab." = array();\n");
  271. foreach ($to_insert as $key => $value)
  272. fwrite($fd, '$'.$tab.'[\''.pSQL($key, true).'\'] = \''.pSQL($value, true).'\';'."\n");
  273. fwrite($fd, "\n?>");
  274. fclose($fd);
  275. // Redirect
  276. if ($save_and_stay)
  277. $this->redirect(true);
  278. else
  279. $this->redirect();
  280. }
  281. else
  282. throw new PrestaShopException(sprintf(Tools::displayError('Cannot write this file: "%s"'), $file_path));
  283. }
  284. public function submitCopyLang()
  285. {
  286. if (!($from_lang = Tools::getValue('fromLang')) || !($to_lang = Tools::getValue('toLang')))
  287. $this->errors[] = $this->l('You must select 2 languages in order to copy data from one to another');
  288. else if (!($from_theme = Tools::getValue('fromTheme')) || !($to_theme = Tools::getValue('toTheme')))
  289. $this->errors[] = $this->l('You must select 2 themes in order to copy data from one to another');
  290. else if (!Language::copyLanguageData(Language::getIdByIso($from_lang), Language::getIdByIso($to_lang)))
  291. $this->errors[] = $this->l('An error occurred while copying data');
  292. else if ($from_lang == $to_lang && $from_theme == $to_theme)
  293. $this->errors[] = $this->l('Nothing to copy! (same language and theme)');
  294. else
  295. {
  296. $theme_exists = array('from_theme' => false, 'to_theme' => false);
  297. foreach ($this->themes as $theme)
  298. {
  299. if ($theme->directory == $from_theme)
  300. $theme_exists['from_theme'] = true;
  301. if ($theme->directory == $to_theme)
  302. $theme_exists['to_theme'] = true;
  303. }
  304. if ($theme_exists['from_theme'] == false || $theme_exists['to_theme'] == false)
  305. $this->errors[] = $this->l('Theme(s) not found');
  306. }
  307. if (count($this->errors))
  308. return;
  309. $bool = true;
  310. $items = Language::getFilesList($from_lang, $from_theme, $to_lang, $to_theme, false, false, true);
  311. foreach ($items as $source => $dest)
  312. {
  313. $bool &= $this->checkDirAndCreate($dest);
  314. $bool &= @copy($source, $dest);
  315. if (strpos($dest, 'modules') && basename($source) === $from_lang.'.php' && $bool !== false)
  316. $bool &= $this->changeModulesKeyTranslation($dest, $from_theme, $to_theme);
  317. }
  318. if ($bool)
  319. $this->redirect(false, 14);
  320. $this->errors[] = $this->l('A part of the data has been copied but some language files could not be found or copied');
  321. }
  322. /**
  323. * Change the key translation to according it to theme name.
  324. *
  325. * @param string $path
  326. * @param string $theme_from
  327. * @param string $theme_to
  328. * @return boolean
  329. */
  330. public function changeModulesKeyTranslation($path, $theme_from, $theme_to)
  331. {
  332. $content = file_get_contents($path);
  333. $arr_replace = array();
  334. $bool_flag = true;
  335. if (preg_match_all('#\$_MODULE\[\'([^\']+)\'\]#Ui', $content, $matches))
  336. {
  337. foreach ($matches[1] as $key => $value)
  338. $arr_replace[$value] = str_replace($theme_from, $theme_to, $value);
  339. $content = str_replace(array_keys($arr_replace), array_values($arr_replace), $content);
  340. $bool_flag = (file_put_contents($path, $content) === false) ? false : true;
  341. }
  342. return $bool_flag;
  343. }
  344. public function exportTabs()
  345. {
  346. // Get name tabs by iso code
  347. $tabs = Tab::getTabs($this->lang_selected->id);
  348. // Get name of the default tabs
  349. $tabs_default_lang = Tab::getTabs(1);
  350. $tabs_default = array();
  351. foreach ($tabs_default_lang as $tab)
  352. $tabs_default[$tab['class_name']] = pSQL($tab['name']);
  353. // Create content
  354. $content = "<?php\n\n\$tabs = array();";
  355. if (!empty($tabs))
  356. foreach ($tabs as $tab)
  357. if ($tabs_default[$tab['class_name']] != pSQL($tab['name']))
  358. $content .= "\n\$tabs['".$tab['class_name']."'] = '".pSQL($tab['name'])."';";
  359. $content .= "\n\nreturn \$tabs;";
  360. $dir = _PS_TRANSLATIONS_DIR_.$this->lang_selected->iso_code.DIRECTORY_SEPARATOR;
  361. $path = $dir.'tabs.php';
  362. // Check if tabs.php exists for the selected Iso Code
  363. if (!Tools::file_exists_cache($dir))
  364. if (!mkdir($dir, 0777, true))
  365. throw new PrestaShopException('The file '.$dir.' cannot be created.');
  366. if (!file_put_contents($path, $content))
  367. throw new PrestaShopException('File "'.$path.'" doesn\'t exists and cannot be created in '.$dir);
  368. if (!is_writable($path))
  369. $this->displayWarning(sprintf(Tools::displayError('This file must be writable: %s'), $path));
  370. }
  371. public function submitExportLang()
  372. {
  373. if ($this->lang_selected->iso_code && $this->theme_selected)
  374. {
  375. $this->exportTabs();
  376. $items = array_flip(Language::getFilesList($this->lang_selected->iso_code, $this->theme_selected, false, false, false, false, true));
  377. $gz = new Archive_Tar(_PS_TRANSLATIONS_DIR_.'/export/'.$this->lang_selected->iso_code.'.gzip', true);
  378. $file_name = Tools::getCurrentUrlProtocolPrefix().Tools::getShopDomain().__PS_BASE_URI__.'translations/export/'.$this->lang_selected->iso_code.'.gzip';
  379. if ($gz->createModify($items, null, _PS_ROOT_DIR_));
  380. {
  381. ob_start();
  382. header('Pragma: public');
  383. header('Expires: 0');
  384. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  385. header('Cache-Control: public');
  386. header('Content-Description: File Transfer');
  387. header('Content-type: application/octet-stream');
  388. header('Content-Disposition: attachment; filename="'.$this->lang_selected->iso_code.'.gzip'.'"');
  389. header('Content-Transfer-Encoding: binary');
  390. ob_end_flush();
  391. @readfile($file_name);
  392. exit;
  393. }
  394. $this->errors[] = Tools::displayError('An error occurred while creating archive.');
  395. }
  396. $this->errors[] = Tools::displayError('Please choose a language and a theme.');
  397. }
  398. public static function checkAndAddMailsFiles($iso_code, $files_list)
  399. {
  400. // 1 - Scan mails files
  401. $mails = scandir(_PS_MAIL_DIR_.'en/');
  402. $mails_new_lang = array();
  403. // Get all email files
  404. foreach ($files_list as $file)
  405. {
  406. if (preg_match('#^mails\/([a-z0-9]+)\/#Ui', $file['filename'], $matches))
  407. {
  408. $slash_pos = strrpos($file['filename'], '/');
  409. $mails_new_lang[] = substr($file['filename'], -(strlen($file['filename']) - $slash_pos - 1));
  410. }
  411. }
  412. // Get the difference
  413. $arr_mails_needed = array_diff($mails, $mails_new_lang);
  414. // Add mails files
  415. foreach ($arr_mails_needed as $mail_to_add)
  416. if (!in_array($mail_to_add, self::$ignore_folder))
  417. @copy(_PS_MAIL_DIR_.'en/'.$mail_to_add, _PS_MAIL_DIR_.$iso_code.'/'.$mail_to_add);
  418. // 2 - Scan modules files
  419. $modules = scandir(_PS_MODULE_DIR_);
  420. $module_mail_en = array();
  421. $module_mail_iso_code = array();
  422. foreach ($modules as $module)
  423. {
  424. if (!in_array($module, self::$ignore_folder) && Tools::file_exists_cache(_PS_MODULE_DIR_.$module.'/mails/en/'))
  425. {
  426. $arr_files = scandir(_PS_MODULE_DIR_.$module.'/mails/en/');
  427. foreach ($arr_files as $file)
  428. {
  429. if (!in_array($file, self::$ignore_folder))
  430. {
  431. if (Tools::file_exists_cache(_PS_MODULE_DIR_.$module.'/mails/en/'.$file))
  432. $module_mail_en[] = _PS_MODULE_DIR_.$module.'/mails/ISO_CODE/'.$file;
  433. if (Tools::file_exists_cache(_PS_MODULE_DIR_.$module.'/mails/'.$iso_code.'/'.$file))
  434. $module_mail_iso_code[] = _PS_MODULE_DIR_.$module.'/mails/ISO_CODE/'.$file;
  435. }
  436. }
  437. }
  438. }
  439. // Get the difference in this modules
  440. $arr_modules_mails_needed = array_diff($module_mail_en, $module_mail_iso_code);
  441. // Add mails files for this modules
  442. foreach ($arr_modules_mails_needed as $file)
  443. {
  444. $file_en = str_replace('ISO_CODE', 'en', $file);
  445. $file_iso_code = str_replace('ISO_CODE', $iso_code, $file);
  446. $dir_iso_code = substr($file_iso_code, 0, -(strlen($file_iso_code) - strrpos($file_iso_code, '/') - 1));
  447. if (!file_exists($dir_iso_code))
  448. mkdir($dir_iso_code);
  449. if (Tools::file_exists_cache($file_en))
  450. copy($file_en, $file_iso_code);
  451. }
  452. }
  453. /**
  454. * Move theme translations in selected themes
  455. *
  456. * @param array $files
  457. * @param array $themes_selected
  458. */
  459. public function checkAndAddThemesFiles($files, $themes_selected)
  460. {
  461. foreach ($files as $file)
  462. {
  463. // Check if file is a file theme
  464. if (preg_match('#^themes\/([a-z0-9]+)\/lang\/#Ui', $file['filename'], $matches))
  465. {
  466. $slash_pos = strrpos($file['filename'], '/');
  467. $name_file = substr($file['filename'], -(strlen($file['filename']) - $slash_pos - 1));
  468. $name_default_theme = $matches[1];
  469. $deleted_old_theme = false;
  470. // Get the old file theme
  471. if (file_exists(_PS_THEME_DIR_.'lang/'.$name_file))
  472. $theme_file_old = _PS_THEME_DIR_.'lang/'.$name_file;
  473. else
  474. {
  475. $deleted_old_theme = true;
  476. $theme_file_old = str_replace(self::DEFAULT_THEME_NAME, $name_default_theme, _PS_THEME_DIR_.'lang/'.$name_file);
  477. }
  478. // Move the old file theme in the new folder
  479. foreach ($themes_selected as $theme_name)
  480. if (file_exists($theme_file_old))
  481. copy($theme_file_old, str_replace($name_default_theme, $theme_name, $theme_file_old));
  482. if ($deleted_old_theme)
  483. @unlink($theme_file_old);
  484. }
  485. }
  486. }
  487. /**
  488. * Add new translations tabs by code ISO
  489. *
  490. * @param array $iso_code
  491. * @param array $files
  492. */
  493. public static function addNewTabs($iso_code, $files)
  494. {
  495. $errors = array();
  496. foreach ($files as $file)
  497. {
  498. // Check if file is a file theme
  499. if (preg_match('#^translations\/'.$iso_code.'\/tabs.php#Ui', $file['filename'], $matches) && Validate::isLanguageIsoCode($iso_code))
  500. {
  501. // Include array width new translations tabs
  502. $tabs = include _PS_ROOT_DIR_.DIRECTORY_SEPARATOR.$file['filename'];
  503. foreach ($tabs as $class_name => $translations)
  504. {
  505. // Get instance of this tab by class name
  506. $tab = Tab::getInstanceFromClassName($class_name);
  507. //Check if class name exists
  508. if (isset($tab->class_name) && !empty($tab->class_name))
  509. {
  510. $id_lang = Language::getIdByIso($iso_code);
  511. $tab->name[(int)$id_lang] = $translations;
  512. if (!Validate::isGenericName($tab->name[(int)$id_lang]))
  513. $errors[] = sprintf(Tools::displayError('Tab "%s" is not valid'), $tab->name[(int)$id_lang]);
  514. else
  515. $tab->update();
  516. }
  517. }
  518. }
  519. }
  520. return $errors;
  521. }
  522. public static function checkTranslationFile($content)
  523. {
  524. $lines = array_map('trim', explode("\n", $content));
  525. $global = false;
  526. foreach ($lines as $line)
  527. {
  528. if (in_array($line, array('<?php', '?>', '')))
  529. continue;
  530. if (!$global && preg_match('/^global\s+\$([a-z0-9-_]+)\s*;$/i', $line, $matches))
  531. {
  532. $global = $matches[1];
  533. continue;
  534. }
  535. if ($global != false && preg_match('/^\$'.preg_quote($global, '/').'\s*=\s*array\(\s*\)\s*;$/i', $line))
  536. continue;
  537. if (!$global && preg_match('/^\$([a-z0-9-_]+)\s*=\s*array\(\s*\)\s*;$/i', $line, $matches))
  538. {
  539. $global = $matches[1];
  540. continue;
  541. }
  542. if (preg_match('/^\$'.preg_quote($global, '/').'\[\''._PS_TRANS_PATTERN_.'\'\]\s*=\s*\''._PS_TRANS_PATTERN_.'\'\s*;$/i', $line))
  543. continue;
  544. if (preg_match('/^return\s+\$'.preg_quote($global, '/').'\s*;$/i', $line, $matches))
  545. continue;
  546. return false;
  547. }
  548. return true;
  549. }
  550. public function submitImportLang()
  551. {
  552. if (!isset($_FILES['file']['tmp_name']) || !$_FILES['file']['tmp_name'])
  553. $this->errors[] = Tools::displayError('No file selected');
  554. else
  555. {
  556. $gz = new Archive_Tar($_FILES['file']['tmp_name'], true);
  557. $filename = $_FILES['file']['name'];
  558. $iso_code = str_replace(array('.tar.gz', '.gzip'), '', $filename);
  559. if (Validate::isLangIsoCode($iso_code))
  560. {
  561. $themes_selected = Tools::getValue('theme', array(self::DEFAULT_THEME_NAME));
  562. $files_list = $gz->listContent();
  563. $uniqid = uniqid();
  564. $sandbox = _PS_CACHE_DIR_.'sandbox'.DIRECTORY_SEPARATOR.$uniqid.DIRECTORY_SEPARATOR;
  565. if ($gz->extract($sandbox, false))
  566. {
  567. foreach ($files_list as $file2check)
  568. {
  569. //don't validate index.php, will be overwrite when extract in translation directory
  570. if (pathinfo($file2check['filename'], PATHINFO_BASENAME) == 'index.php')
  571. continue;
  572. if (preg_match('@^[0-9a-z-_/\\\\]+\.php$@i', $file2check['filename']))
  573. {
  574. if (!AdminTranslationsController::checkTranslationFile(file_get_contents($sandbox.$file2check['filename'])))
  575. $this->errors[] = sprintf(Tools::displayError('Validation failed for: %s'), $file2check['filename']);
  576. }
  577. elseif (!preg_match('@^[0-9a-z-_/\\\\]+\.(html|tpl|txt)$@i', $file2check['filename']))
  578. $this->errors[] = sprintf(Tools::displayError('Unidentified file found: %s'), $file2check['filename']);
  579. }
  580. Tools::deleteDirectory($sandbox, true);
  581. }
  582. if (count($this->errors))
  583. return false;
  584. if ($gz->extract(_PS_TRANSLATIONS_DIR_.'../', false))
  585. {
  586. foreach ($files_list as $file2check)
  587. if (pathinfo($file2check['filename'], PATHINFO_BASENAME) == 'index.php' && file_put_contents(_PS_TRANSLATIONS_DIR_.'../'.$file2check['filename'], Tools::getDefaultIndexContent()))
  588. continue;
  589. AdminTranslationsController::checkAndAddMailsFiles($iso_code, $files_list);
  590. $this->checkAndAddThemesFiles($files_list, $themes_selected);
  591. $tab_errors = AdminTranslationsController::addNewTabs($iso_code, $files_list);
  592. if (count($tab_errors))
  593. {
  594. $this->errors += $tab_errors;
  595. return false;
  596. }
  597. if (Validate::isLanguageFileName($filename))
  598. {
  599. if (!Language::checkAndAddLanguage($iso_code))
  600. $conf = 20;
  601. }
  602. $this->redirect(false, (isset($conf) ? $conf : '15'));
  603. }
  604. $this->errors[] = Tools::displayError('Archive cannot be extracted.');
  605. }
  606. else
  607. $this->errors[] = sprintf(Tools::displayError('ISO CODE invalid "%1$s" for the following file: "%2$s"'), $iso_code, $filename);
  608. }
  609. }
  610. public function submitAddLang()
  611. {
  612. $arr_import_lang = explode('|', Tools::getValue('params_import_language')); /* 0 = Language ISO code, 1 = PS version */
  613. if (Validate::isLangIsoCode($arr_import_lang[0]))
  614. {
  615. if ($content = Tools::file_get_contents(
  616. 'http://www.prestashop.com/download/lang_packs/gzip/'.$arr_import_lang[1].'/'.$arr_import_lang[0].'.gzip', false,
  617. @stream_context_create(array('http' => array('method' => 'GET', 'timeout' => 5)))))
  618. {
  619. $file = _PS_TRANSLATIONS_DIR_.$arr_import_lang[0].'.gzip';
  620. if (file_put_contents($file, $content))
  621. {
  622. $gz = new Archive_Tar($file, true);
  623. $files_list = $gz->listContent();
  624. if ($gz->extract(_PS_TRANSLATIONS_DIR_.'../', false))
  625. {
  626. AdminTranslationsController::checkAndAddMailsFiles($arr_import_lang[0], $files_list);
  627. $tab_errors = AdminTranslationsController::addNewTabs($arr_import_lang[0], $files_list);
  628. if (count($tab_errors))
  629. $this->errors += $tab_errors;
  630. else
  631. {
  632. if (!Language::checkAndAddLanguage($arr_import_lang[0]))
  633. $conf = 20;
  634. }
  635. if (!unlink($file))
  636. $this->errors[] = Tools::displayError('Cannot delete archive');
  637. $this->redirect(false, (isset($conf) ? $conf : '15'));
  638. }
  639. $this->errors[] = Tools::displayError('Archive cannot be extracted.');
  640. if (!unlink($file))
  641. $this->errors[] = Tools::displayError('Cannot delete archive');
  642. }
  643. else
  644. $this->errors[] = Tools::displayError('Server does not have permissions for writing.');
  645. }
  646. else
  647. $this->errors[] = Tools::displayError('Language not found');
  648. }
  649. else
  650. $this->errors[] = Tools::displayError('Invalid parameter');
  651. }
  652. /**
  653. * This method check each file (tpl or php file), get its sentences to translate,
  654. * compare with posted values and write in iso code translation file.
  655. *
  656. * @param string $file_name
  657. * @param array $files
  658. * @param string $theme_name
  659. * @param string $module_name
  660. * @param string|boolean $dir
  661. * @return void
  662. */
  663. protected function findAndWriteTranslationsIntoFile($file_name, $files, $theme_name, $module_name, $dir = false)
  664. {
  665. // These static vars allow to use file to write just one time.
  666. static $cache_file = array();
  667. static $str_write = '';
  668. static $array_check_duplicate = array();
  669. // Default translations and Prestashop overriding themes are distinguish
  670. $is_default = $theme_name === self::DEFAULT_THEME_NAME ? true : false;
  671. // Set file_name in static var, this allow to open and wright the file just one time
  672. if (!isset($cache_file[$theme_name.'-'.$file_name]))
  673. {
  674. $str_write = '';
  675. $cache_file[$theme_name.'-'.$file_name] = true;
  676. if (!Tools::file_exists_cache($file_name))
  677. file_put_contents($file_name, '');
  678. if (!is_writable($file_name))
  679. throw new PrestaShopException(sprintf(
  680. Tools::displayError('Cannot write to the theme\'s language file (%s). Please check write permissions.'),
  681. $file_name
  682. ));
  683. // this string is initialized one time for a file
  684. $str_write .= "<?php\n\nglobal \$_MODULE;\n\$_MODULE = array();\n";
  685. $array_check_duplicate = array();
  686. }
  687. foreach ($files as $file)
  688. {
  689. if (preg_match('/^(.*).(tpl|php)$/', $file) && Tools::file_exists_cache($file_path = $dir.$file) && !in_array($file, self::$ignore_folder))
  690. {
  691. // Get content for this file
  692. $content = file_get_contents($file_path);
  693. // Get file type
  694. $type_file = substr($file, -4) == '.tpl' ? 'tpl' : 'php';
  695. // Parse this content
  696. $matches = $this->userParseFile($content, $this->type_selected, $type_file);
  697. // Write each translation on its module file
  698. $template_name = substr(basename($file), 0, -4);
  699. foreach ($matches as $key)
  700. {
  701. if ($is_default)
  702. {
  703. $post_key = md5(strtolower($module_name).'_'.self::DEFAULT_THEME_NAME.'_'.strtolower($template_name).'_'.md5($key));
  704. $pattern = '\'<{'.strtolower($module_name).'}prestashop>'.strtolower($template_name).'_'.md5($key).'\'';
  705. }
  706. else
  707. {
  708. $post_key = md5(strtolower($module_name).'_'.strtolower($theme_name).'_'.strtolower($template_name).'_'.md5($key));
  709. $pattern = '\'<{'.strtolower($module_name).'}'.strtolower($theme_name).'>'.strtolower($template_name).'_'.md5($key).'\'';
  710. }
  711. if (array_key_exists($post_key, $_POST) && !empty($_POST[$post_key]) && !in_array($pattern, $array_check_duplicate))
  712. {
  713. $array_check_duplicate[] = $pattern;
  714. $str_write .= '$_MODULE['.$pattern.'] = \''.pSQL(str_replace(array("\r\n", "\r", "\n"), ' ', $_POST[$post_key])).'\';'."\n";
  715. $this->total_expression++;
  716. }
  717. }
  718. }
  719. }
  720. if (isset($cache_file[$theme_name.'-'.$file_name]) && $str_write != "<?php\n\nglobal \$_MODULE;\n\$_MODULE = array();\n")
  721. file_put_contents($file_name, $str_write);
  722. }
  723. /**
  724. * Clear the list of module file by type (file or directory)
  725. *
  726. * @param $files : list of files
  727. * @param string $type_clear (file|directory)
  728. * @param string $path
  729. * @return array : list of a good files
  730. */
  731. public function clearModuleFiles($files, $type_clear = 'file', $path = '')
  732. {
  733. // List of directory which not must be parsed
  734. $arr_exclude = array('img', 'js', 'mails');
  735. // List of good extention files
  736. $arr_good_ext = array('.tpl', '.php');
  737. foreach ($files as $key => $file)
  738. {
  739. if ($file{0} === '.' || in_array(substr($file, 0, strrpos($file, '.')), $this->all_iso_lang))
  740. unset($files[$key]);
  741. else if ($type_clear === 'file' && !in_array(substr($file, strrpos($file, '.')), $arr_good_ext))
  742. unset($files[$key]);
  743. else if ($type_clear === 'directory' && (!is_dir($path.$file) || in_array($file, $arr_exclude)))
  744. unset($files[$key]);
  745. }
  746. return $files;
  747. }
  748. /**
  749. * This method get translation for each files of a module,
  750. * compare with global $_MODULES array and fill AdminTranslations::modules_translations array
  751. * With key as English sentences and values as their iso code translations.
  752. *
  753. * @param array $files
  754. * @param string $theme_name
  755. * @param string $module_name
  756. * @param string|boolean $dir
  757. * @param string $iso_code
  758. * @return void
  759. */
  760. protected function findAndFillTranslations($files, $theme_name, $module_name, $dir = false)
  761. {
  762. $name_var = $this->translations_informations[$this->type_selected]['var'];
  763. // added for compatibility
  764. $GLOBALS[$name_var] = array_change_key_case($GLOBALS[$name_var]);
  765. // Default translations and Prestashop overriding themes are distinguish
  766. $is_default = $theme_name === self::DEFAULT_THEME_NAME ? true : false;
  767. // Thank to this var similar keys are not duplicate
  768. // in AndminTranslation::modules_translations array
  769. // see below
  770. $array_check_duplicate = array();
  771. foreach ($files as $file)
  772. {
  773. if ((preg_match('/^(.*).tpl$/', $file) || preg_match('/^(.*).php$/', $file)) && Tools::file_exists_cache($file_path = $dir.$file))
  774. {
  775. // Get content for this file
  776. $content = file_get_contents($file_path);
  777. // Module files can now be ignored by adding this string in a file
  778. if (strpos($content, 'IGNORE_THIS_FILE_FOR_TRANSLATION') !== false)
  779. continue;
  780. // Get file type
  781. $type_file = substr($file, -4) == '.tpl' ? 'tpl' : 'php';
  782. // Parse this content
  783. $matches = $this->userParseFile($content, $this->type_selected, $type_file);
  784. // Write each translation on its module file
  785. $template_name = substr(basename($file), 0, -4);
  786. foreach ($matches as $key)
  787. {
  788. $module_key = '<{'.Tools::strtolower($module_name).'}'.
  789. strtolower($is_default ? 'prestashop' : $theme_name).'>'.Tools::strtolower($template_name).'_'.md5($key);
  790. // to avoid duplicate entry
  791. if (!in_array($module_key, $array_check_duplicate))
  792. {
  793. $array_check_duplicate[] = $module_key;
  794. if (!isset($this->modules_translations[$theme_name][$module_name][$template_name][$key]['trad']))
  795. $this->total_expression++;
  796. if (array_key_exists($module_key, $GLOBALS[$name_var]))
  797. $this->modules_translations[$theme_name][$module_name][$template_name][$key]['trad'] = html_entity_decode($GLOBALS[$name_var][$module_key], ENT_COMPAT, 'UTF-8');
  798. else
  799. {
  800. $this->modules_translations[$theme_name][$module_name][$template_name][$key]['trad'] = '';
  801. $this->missing_translations++;
  802. }
  803. $this->modules_translations[$theme_name][$module_name][$template_name][$key]['use_sprintf'] = $this->checkIfKeyUseSprintf($key);
  804. }
  805. }
  806. }
  807. }
  808. }
  809. /**
  810. * Get list of files which must be parsed by directory and by type of translations
  811. *
  812. * @return array : list of files by directory
  813. */
  814. public function getFileToParseByTypeTranslation()
  815. {
  816. $directories = array();
  817. switch ($this->type_selected)
  818. {
  819. case 'front':
  820. $directories['tpl'] = array(_PS_ALL_THEMES_DIR_.'/' => scandir(_PS_ALL_THEMES_DIR_));
  821. self::$ignore_folder[] = 'modules';
  822. $directories['tpl'] = array_merge($directories['tpl'], $this->listFiles(_PS_THEME_SELECTED_DIR_));
  823. if (Tools::file_exists_cache(_PS_THEME_OVERRIDE_DIR_))
  824. $directories['tpl'] = array_merge($directories['tpl'], $this->listFiles(_PS_THEME_OVERRIDE_DIR_));
  825. break;
  826. case 'back':
  827. $directories = array(
  828. 'php' => array(
  829. _PS_ADMIN_CONTROLLER_DIR_.'/' => scandir(_PS_ADMIN_CONTROLLER_DIR_),
  830. _PS_OVERRIDE_DIR_.'controllers/admin/' => scandir(_PS_OVERRIDE_DIR_.'controllers/admin/'),
  831. _PS_CLASS_DIR_.'helper/' => scandir(_PS_CLASS_DIR_.'helper/'),
  832. _PS_CLASS_DIR_.'controller/' => array('AdminController.php'),
  833. _PS_CLASS_DIR_ => array('PaymentModule.php')
  834. ),
  835. 'tpl' => $this->listFiles(_PS_ADMIN_DIR_.'/themes/'),
  836. 'specific' => array(
  837. _PS_ADMIN_DIR_.'/' => array(
  838. 'header.inc.php',
  839. 'footer.inc.php',
  840. 'index.php',
  841. 'login.php',
  842. 'password.php',
  843. 'functions.php'
  844. )
  845. )
  846. );
  847. // For translate the template which are overridden
  848. if (file_exists(_PS_OVERRIDE_DIR_.'controllers'.DIRECTORY_SEPARATOR.'admin'.DIRECTORY_SEPARATOR.'templates'))
  849. $directories['tpl'] = array_merge($directories['tpl'], $this->listFiles(_PS_OVERRIDE_DIR_.'controllers'.DIRECTORY_SEPARATOR.'admin'.DIRECTORY_SEPARATOR.'templates'));
  850. break;
  851. case 'errors':
  852. $directories['php'] = array(
  853. _PS_ROOT_DIR_.'/' => scandir(_PS_ROOT_DIR_),
  854. _PS_ADMIN_DIR_.'/' => scandir(_PS_ADMIN_DIR_),
  855. _PS_FRONT_CONTROLLER_DIR_ => scandir(_PS_FRONT_CONTROLLER_DIR_),
  856. _PS_ADMIN_CONTROLLER_DIR_ => scandir(_PS_ADMIN_CONTROLLER_DIR_),
  857. _PS_OVERRIDE_DIR_.'controllers/front/' => scandir(_PS_OVERRIDE_DIR_.'controllers/front/'),
  858. _PS_OVERRIDE_DIR_.'controllers/admin/' => scandir(_PS_OVERRIDE_DIR_.'controllers/admin/')
  859. );
  860. // Get all files for folders classes/ and override/classes/ recursively
  861. $directories['php'] = array_merge($directories['php'], $this->listFiles(_PS_CLASS_DIR_, array(), 'php'));
  862. $directories['php'] = array_merge($directories['php'], $this->listFiles(_PS_OVERRIDE_DIR_.'classes/', array(), 'php'));
  863. break;
  864. case 'fields':
  865. $directories['php'] = $this->listFiles(_PS_CLASS_DIR_, array(), 'php');
  866. break;
  867. case 'pdf':
  868. $tpl_theme = Tools::file_exists_cache(_PS_THEME_SELECTED_DIR_.'pdf/') ? scandir(_PS_THEME_SELECTED_DIR_.'pdf/') : array();
  869. $directories = array(
  870. 'php' => array(
  871. _PS_CLASS_DIR_.'pdf/' => scandir(_PS_CLASS_DIR_.'pdf/'),
  872. _PS_OVERRIDE_DIR_.'classes/pdf/' => scandir(_PS_OVERRIDE_DIR_.'classes/pdf/')
  873. ),
  874. 'tpl' => array(
  875. _PS_PDF_DIR_ => scandir(_PS_PDF_DIR_),
  876. _PS_THEME_SELECTED_DIR_.'pdf/' => $tpl_theme
  877. )
  878. );
  879. break;
  880. case 'mails':
  881. $directories['php'] = array(
  882. _PS_FRONT_CONTROLLER_DIR_ => scandir(_PS_FRONT_CONTROLLER_DIR_),
  883. _PS_ADMIN_CONTROLLER_DIR_ => scandir(_PS_ADMIN_CONTROLLER_DIR_),
  884. _PS_OVERRIDE_DIR_.'controllers/front/' => scandir(_PS_OVERRIDE_DIR_.'controllers/front/'),
  885. _PS_OVERRIDE_DIR_.'controllers/admin/' => scandir(_PS_OVERRIDE_DIR_.'controllers/admin/'),
  886. _PS_ADMIN_DIR_.'/' => scandir(_PS_ADMIN_DIR_),
  887. _PS_ADMIN_DIR_.'/tabs/' => scandir(_PS_ADMIN_DIR_.'/tabs')
  888. );
  889. // Get all files for folders classes/ and override/classes/ recursively
  890. $directories['php'] = array_merge($directories['php'], $this->listFiles(_PS_CLASS_DIR_, array(), 'php'));
  891. $directories['php'] = array_merge($directories['php'], $this->listFiles(_PS_OVERRIDE_DIR_.'classes/', array(), 'php'));
  892. $directories['php'] = array_merge($directories['php'], $this->getModulesHasMails());
  893. break;
  894. }
  895. return $directories;
  896. }
  897. /**
  898. * This method parse a file by type of translation and type file
  899. *
  900. * @param $content
  901. * @param $type_translation : front, back, errors, modules...
  902. * @param string|bool $type_file : (tpl|php)
  903. * @return return $matches
  904. */
  905. protected function userParseFile($content, $type_translation, $type_file = false)
  906. {
  907. switch ($type_translation)
  908. {
  909. case 'front':
  910. // Parsing file in Front office
  911. $regex = '/\{l\s*s=\''._PS_TRANS_PATTERN_.'\'(\s*sprintf=.*)?(\s*js=1)?\s*\}/U';
  912. break;
  913. case 'back':
  914. // Parsing file in Back office
  915. if ($type_file == 'php')
  916. $regex = '/this->l\(\''._PS_TRANS_PATTERN_.'\'[\)|\,]/U';
  917. else if ($type_file == 'specific')
  918. $regex = '/translate\(\''._PS_TRANS_PATTERN_.'\'\)/U';
  919. else
  920. $regex = '/\{l\s*s\s*=\''._PS_TRANS_PATTERN_.'\'(\s*sprintf=.*)?(\s*js=1)?(\s*slashes=1)?\s*\}/U';
  921. break;
  922. case 'errors':
  923. // Parsing file for all errors syntax
  924. $regex = '/Tools::displayError\(\''._PS_TRANS_PATTERN_.'\'(,\s*(true|false))?\)/U';
  925. break;
  926. case 'modules':
  927. // Parsing modules file
  928. if ($type_file == 'php')
  929. $regex = '/->l\(\''._PS_TRANS_PATTERN_.'\'(, ?\'(.+)\')?(, ?(.+))?\)/U';
  930. else
  931. $regex = '/\{l\s*s=\''._PS_TRANS_PATTERN_.'\'(\s*sprintf=.*)?(\s*mod=\'.+\')?(\s*js=1)?\s*\}/U';
  932. break;
  933. case 'pdf':
  934. // Parsing PDF file
  935. if ($type_file == 'php')
  936. $regex = '/HTMLTemplate.*::l\(\''._PS_TRANS_PATTERN_.'\'[\)|\,]/U';
  937. else
  938. $regex = '/\{l\s*s=\''._PS_TRANS_PATTERN_.'\'(\s*sprintf=.*)?(\s*js=1)?(\s*pdf=\'true\')?\s*\}/U';
  939. break;
  940. }
  941. preg_match_all($regex, $content, $matches);
  942. return $matches[1];
  943. }
  944. /**
  945. * Get all translations informations for all type of translations
  946. *
  947. * array(
  948. * 'type' => array(
  949. * 'name' => string : title for the translation type,
  950. * 'var' => string : name of var for the translation file,
  951. * 'dir' => string : dir of translation file
  952. * 'file' => string : file name of translation file
  953. * )
  954. * )
  955. */
  956. public function getTranslationsInformations()
  957. {
  958. $this->translations_informations = array(
  959. 'front' => array(
  960. 'name' => $this->l('Front Office translations'),
  961. 'var' => '_LANG',
  962. 'dir' => _PS_THEME_SELECTED_DIR_.'lang/',
  963. 'file' => $this->lang_selected->iso_code.'.php'
  964. ),
  965. 'back' => array(
  966. 'name' => $this->l('Back Office translations'),
  967. 'var' => '_LANGADM',
  968. 'dir' => _PS_TRANSLATIONS_DIR_.$this->lang_selected->iso_code.'/',
  969. 'file' => 'admin.php'
  970. ),
  971. 'errors' => array(
  972. 'name' => $this->l('Error message translations'),
  973. 'var' => '_ERRORS',
  974. 'dir' => _PS_TRANSLATIONS_DIR_.$this->lang_selected->iso_code.'/',
  975. 'file' => 'errors.php'
  976. ),
  977. 'fields' => array(
  978. 'name' => $this->l('Field name translations'),
  979. 'var' => '_FIELDS',
  980. 'dir' => _PS_TRANSLATIONS_DIR_.$this->lang_selected->iso_code.'/',
  981. 'file' => 'fields.php'
  982. ),
  983. 'modules' => array(
  984. 'name' => $this->l('Installed module translations'),
  985. 'var' => '_MODULES',
  986. 'dir' => _PS_MODULE_DIR_,
  987. 'file' => '',
  988. 'override' => array(
  989. 'dir' => _PS_THEME_SELECTED_DIR_.'modules/',
  990. 'file' => ''
  991. )
  992. ),
  993. 'pdf' => array(
  994. 'name' => $this->l('PDF translations'),
  995. 'var' => '_LANGPDF',
  996. 'dir' => _PS_TRANSLATIONS_DIR_.$this->lang_selected->iso_code.'/',
  997. 'file' => 'pdf.php',
  998. 'override' => array(
  999. 'dir' => _PS_THEME_SELECTED_DIR_.'pdf/lang/',
  1000. 'file' => $this->lang_selected->iso_code.'.php'
  1001. )
  1002. ),
  1003. 'mails' => array(
  1004. 'name' => $this->l('E-mail template translations'),
  1005. 'var' => '_LANGMAIL',
  1006. 'dir' => _PS_MAIL_DIR_.$this->lang_selected->iso_code.'/',
  1007. 'file' => 'lang.php',
  1008. 'override' => array(
  1009. 'dir' => _PS_THEME_SELECTED_DIR_.'mails/'.$this->lang_selected->iso_code.'/',
  1010. 'file' => 'lang.php'
  1011. )
  1012. )
  1013. );
  1014. }
  1015. /**
  1016. * Get all informations on : languages, theme and the translation type.
  1017. */
  1018. public function getInformations()
  1019. {
  1020. // Get all Languages
  1021. $this->languages = Language::getLanguages(false);
  1022. // Get all iso_code of languages
  1023. foreach ($this->languages as $language)
  1024. $this->all_iso_lang[] = $language['iso_code'];
  1025. // Get all themes
  1026. $this->themes = Theme::getThemes();
  1027. // Get folder name of theme
  1028. if (($theme = Tools::getValue('theme')) && !is_array($theme))
  1029. {
  1030. $theme_exists = false;
  1031. foreach ($this->themes as $existing_theme)
  1032. if ($existing_theme->directory == $theme)
  1033. $theme_exists = true;
  1034. if ($theme_exists)
  1035. $this->theme_selected = Tools::safeOutput($theme);
  1036. else
  1037. throw new PrestaShopException(sprintf(Tools::displayError('Invalid theme "%s"'), $theme));
  1038. }
  1039. else
  1040. $this->theme_selected = self::DEFAULT_THEME_NAME;
  1041. // Set the path of selected theme
  1042. define('_PS_THEME_SELECTED_DIR_', _PS_ROOT_DIR_.'/themes/'.$this->theme_selected.'/');
  1043. // Get type of translation
  1044. if (($type = Tools::getValue('type')) && !is_array($type))
  1045. $this->type_selected = strtolower(Tools::safeOutput($type));
  1046. // Get selected language
  1047. if (Tools::getValue('lang') || Tools::getValue('iso_code'))
  1048. {
  1049. $iso_code = Tools::getValue('lang') ? Tools::getValue('lang') : Tools::getValue('iso_code');
  1050. if (!Validate::isLangIsoCode($iso_code) || !in_array($iso_code, $this->all_iso_lang))
  1051. throw new PrestaShopException(sprintf(Tools::displayError('Invalid iso code "%s"'), $iso_code));
  1052. $this->lang_selected = new Language((int)Language::getIdByIso($iso_code));
  1053. }
  1054. else
  1055. $this->lang_selected = new Language((int)Language::getIdByIso('en'));
  1056. // Get all information for translations
  1057. $this->getTranslationsInformations();
  1058. }
  1059. /**
  1060. * AdminController::postProcess() override
  1061. * @see AdminController::postProcess()
  1062. */
  1063. public function postProcess()
  1064. {
  1065. $this->getInformations();
  1066. /* PrestaShop demo mode */
  1067. if (_PS_MODE_DEMO_)
  1068. {
  1069. $this->errors[] = Tools::displayError('This functionality has been disabled.');
  1070. return;
  1071. }
  1072. /* PrestaShop demo mode */
  1073. try {
  1074. if (Tools::isSubmit('submitCopyLang'))
  1075. {
  1076. if ($this->tabAccess['add'] === '1')
  1077. $this->submitCopyLang();
  1078. else
  1079. $this->errors[] = Tools::displayError('You do not have permission to add here.');
  1080. }
  1081. else if (Tools::isSubmit('submitExport'))
  1082. {
  1083. if ($this->tabAccess['add'] === '1')
  1084. $this->submitExportLang();
  1085. else
  1086. $this->errors[] = Tools::displayError('You do not have permission to add here.');
  1087. }
  1088. else if (Tools::isSubmit('submitImport'))
  1089. {
  1090. if ($this->tabAccess['add'] === '1')
  1091. $this->submitImportLang();
  1092. else
  1093. $this->errors[] = Tools::displayError('You do not have permission to add here.');
  1094. }
  1095. else if (Tools::isSubmit('submitAddLanguage'))
  1096. {
  1097. if ($this->tabAccess['add'] === '1')
  1098. $this->submitAddLang();
  1099. else
  1100. $this->errors[] = Tools::displayError('You do not have permission to add here.');
  1101. }
  1102. else if (Tools::isSubmit('submitTranslationsFront'))
  1103. {
  1104. if ($this->tabAccess['edit'] === '1')
  1105. $this->writeTranslationFile();
  1106. else
  1107. $this->errors[] = Tools::displayError('You do not have permission to edit here.');
  1108. }
  1109. else if (Tools::isSubmit('submitTranslationsPdf'))
  1110. {
  1111. if ($this->tabAccess['edit'] === '1')
  1112. // Only the PrestaShop team should write the translations into the _PS_TRANSLATIONS_DIR_
  1113. if (($this->theme_selected == self::DEFAULT_THEME_NAME) && _PS_MODE_DEV_)
  1114. $this->writeTranslationFile();
  1115. else
  1116. $this->writeTranslationFile(true);
  1117. else
  1118. $this->errors[] = Tools::displayError('You do not have permission to edit here.');
  1119. }
  1120. else if (Tools::isSubmit('submitTranslationsBack'))
  1121. {
  1122. if ($this->tabAccess['edit'] === '1')
  1123. $this->writeTranslationFile();
  1124. else
  1125. $this->errors[] = Tools::displayError('You do not have permission to edit here.');
  1126. }
  1127. else if (Tools::isSubmit('submitTranslationsErrors'))
  1128. {
  1129. if ($this->tabAccess['edit'] === '1')
  1130. $this->writeTranslationFile();
  1131. else
  1132. $this->errors[] = Tools::displayError('You do not have permission to edit here.');
  1133. }
  1134. else if (Tools::isSubmit('submitTranslationsFields'))
  1135. {
  1136. if ($this->tabAccess['edit'] === '1')
  1137. $this->writeTranslationFile();
  1138. else
  1139. $this->errors[] = Tools::displayError('You do not have permission to edit here.');
  1140. }
  1141. else if (Tools::isSubmit('submitTranslationsMails') || Tools::isSubmit('submitTranslationsMailsAndStay'))
  1142. {
  1143. if ($this->tabAccess['edit'] === '1')
  1144. $this->submitTranslationsMails();
  1145. else
  1146. $this->errors[] = Tools::displayError('You do not have permission to edit here.');
  1147. }
  1148. else if (Tools::isSubmit('submitTranslationsModules'))
  1149. {
  1150. if ($this->tabAccess['edit'] === '1')
  1151. {
  1152. // Get a good path for module directory
  1153. if ($this->theme_selected == self::DEFAULT_THEME_NAME)
  1154. $i18n_dir = $this->translations_informations[$this->type_selected]['dir'];
  1155. else
  1156. $i18n_dir = $this->translations_informations[$this->type_selected]['override']['dir'];
  1157. // Get list of modules
  1158. if ($modules = $this->getListModules())
  1159. {
  1160. // Get files of all modules
  1161. $arr_files = $this->getAllModuleFiles($modules, $i18n_dir, $this->lang_selected->iso_code, true);
  1162. // Find and write all translation modules files
  1163. foreach ($arr_files as $value)
  1164. $this->findAndWriteTranslationsIntoFile($value['file_name'], $value['files'], $value['theme'], $value['module'], $value['dir']);
  1165. // Redirect
  1166. if (Tools::getValue('submitTranslationsModulesAndStay'))
  1167. $this->redirect(true);
  1168. else
  1169. $this->redirect();
  1170. }
  1171. }
  1172. else
  1173. $this->errors[] = Tools::displayError('You do not have permission to edit here.');
  1174. }
  1175. } catch (PrestaShopException $e) {
  1176. $this->errors[] = $e->getMessage();
  1177. }
  1178. }
  1179. /**
  1180. * This method redirect in the translation main page or in the translation page
  1181. *
  1182. * @param bool $save_and_stay : true if the user has clicked on the button "save and stay"
  1183. * @param bool $conf : id of confirmation message
  1184. */
  1185. protected function redirect($save_and_stay = false, $conf = false)
  1186. {
  1187. $conf = !$conf ? 4 : $conf;
  1188. $url_base = self::$currentIndex.'&token='.$this->token.'&conf='.$conf;
  1189. if ($save_and_stay)
  1190. Tools::redirectAdmin($url_base.'&lang='.$this->lang_selected->iso_code.'&type='.$this->type_selected.'&theme='.$this->theme_selected);
  1191. else
  1192. Tools::redirectAdmin($url_base);
  1193. }
  1194. protected function getMailPattern()
  1195. {
  1196. // Let the indentation like it.
  1197. return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd">
  1198. <html>
  1199. <head>
  1200. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  1201. <title>#title</title>
  1202. </head>
  1203. <body>
  1204. #content
  1205. </body>
  1206. </html>';
  1207. }
  1208. /**
  1209. * This method is used to wright translation for mails.
  1210. * This wrights subject translation files
  1211. * (in root/mails/lang_choosen/lang.php or root/_PS_THEMES_DIR_/mails/lang_choosen/lang.php)
  1212. * and mails files.
  1213. */
  1214. protected function submitTranslationsMails()
  1215. {
  1216. $arr_mail_content = array();
  1217. $arr_mail_path = array();
  1218. if (Tools::getValue('core_mail'))
  1219. {
  1220. $arr_mail_content['core_mail'] = Tools::getValue('core_mail');
  1221. // Get path of directory for find a good path of translation file
  1222. if ($this->theme_selected != self::DEFAULT_THEME_NAME)
  1223. $arr_mail_path['core_mail'] = $this->translations_informations[$this->type_selected]['override']['dir'];
  1224. else
  1225. $arr_mail_path['core_mail'] = $this->translations_informations[$this->type_selected]['dir'];
  1226. }
  1227. if (Tools::getValue('module_mail'))
  1228. {
  1229. $arr_mail_content['module_mail'] = Tools::getValue('module_mail');
  1230. // Get path of directory for find a good path of translation file
  1231. if ($this->theme_selected != self::DEFAULT_THEME_NAME)
  1232. $arr_mail_path['module_mail'] = $this->translations_informations['modules']['override']['dir'].'{module}/mails/'.$this->lang_selected->iso_code.'/';
  1233. else
  1234. $arr_mail_path['module_mail'] = $this->translations_informations['modules']['dir'].'{module}/mails/'.$this->lang_selected->iso_code.'/';
  1235. }
  1236. // Save each mail content
  1237. foreach ($arr_mail_content as $group_name => $all_content)
  1238. {
  1239. foreach ($all_content as $type_content => $mails)
  1240. {
  1241. foreach ($mails as $mail_name => $content)
  1242. {
  1243. $module_name = false;
  1244. $module_name_pipe_pos = stripos($mail_name, '|');
  1245. if ($module_name_pipe_pos)
  1246. {
  1247. $module_name = substr($mail_name, 0, $module_name_pipe_pos);
  1248. if (!Validate::isModuleName($module_name))
  1249. throw new PrestaShopException(sprinf(Tools::displayError('Invalid module name "%s"'), $module_name));
  1250. $mail_name = substr($mail_name, $module_name_pipe_pos + 1);
  1251. if (!Validate::isTplName($mail_name))
  1252. throw new PrestaShopException(sprintf(Tools::displayError('Invalid mail name "%s"'), $mail_name));
  1253. }
  1254. if ($type_content == 'html')
  1255. {
  1256. $content = Tools::htmlentitiesUTF8($content);
  1257. $content = htmlspecialchars_decode($content);
  1258. // replace correct end of line
  1259. $content = str_replace("\r\n", PHP_EOL, $content);
  1260. $title = '';
  1261. if (Tools::getValue('title_'.$group_name.'_'.$mail_name))
  1262. $title = Tools::getValue('title_'.$group_name.'_'.$mail_name);
  1263. $string_mail = $this->getMailPattern();
  1264. $content = str_replace(array('#title', '#content'), array($title, $content), $string_mail);
  1265. // Magic Quotes shall... not.. PASS!
  1266. if (_PS_MAGIC_QUOTES_GPC_)
  1267. $content = stripslashes($content);
  1268. }
  1269. if (Validate::isCleanHTML($content))
  1270. {
  1271. $path = $arr_mail_path[$group_name];
  1272. if ($module_name)
  1273. $path = str_replace('{module}', $module_name, $path);
  1274. file_put_contents($path.$mail_name.'.'.$type_content, $content);
  1275. }
  1276. else
  1277. throw new PrestaShopException(Tools::displayError('HTML e-mail templates cannot contain JavaScript code.'

Large files files are truncated, but you can click here to view the full file