PageRenderTime 100ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/adminstrator/tabs/AdminTranslations.php

http://marocmall.googlecode.com/
PHP | 1538 lines | 1268 code | 89 blank | 181 comment | 167 complexity | 41b3447a27b798789968d525957042c0 MD5 | raw file
Possible License(s): LGPL-2.1

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

  1. <?php
  2. /*
  3. * 2007-2011 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-2011 PrestaShop SA
  23. * @version Release: $Revision: 9147 $
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. * International Registered Trademark & Property of PrestaShop SA
  26. */
  27. include_once(PS_ADMIN_DIR.'/../classes/AdminTab.php');
  28. include_once(PS_ADMIN_DIR.'/../tools/tar/Archive_Tar.php');
  29. include_once(PS_ADMIN_DIR.'/../tools/pear/PEAR.php');
  30. define ('TEXTAREA_SIZED', 70);
  31. class AdminTranslations extends AdminTab
  32. {
  33. protected $link_lang_pack = 'http://www.prestashop.com/download/lang_packs/get_each_language_pack.php';
  34. protected $total_expression = 0;
  35. protected $all_iso_lang = array();
  36. protected $modules_translations = array();
  37. const DEFAULT_THEME_NAME = 'default';
  38. protected static $tpl_regexp = '';
  39. protected static $php_regexp = '';
  40. /**
  41. * Is true if number of var exceed the suhosin request or post limit
  42. *
  43. * @var boolean
  44. */
  45. protected $suhosin_limit_exceed = false;
  46. public function __construct()
  47. {
  48. parent::__construct();
  49. self::$tpl_regexp = '/\{l s=\''._PS_TRANS_PATTERN_.'\'( mod=\'.+\')?( js=1)?\}/U';
  50. // added ? after spaces because some peoples forget them. see PSCFI-2501
  51. self::$php_regexp = '/->l\(\''._PS_TRANS_PATTERN_.'\'(, ?\'(.+)\')?(, ?(.+))?\)/U';
  52. }
  53. /**
  54. * This method merge each arrays of modules translation in
  55. * the array of modules translations
  56. *
  57. * @param boolean $is_default if true a prefix is set before each keys in global $_MODULES array
  58. */
  59. protected function getModuleTranslations($is_default = false)
  60. {
  61. global $_MODULES, $_MODULE;
  62. if (!isset($_MODULE) AND !isset($_MODULES))
  63. $_MODULES = array();
  64. elseif (isset($_MODULE))
  65. {
  66. if (is_array($_MODULE) AND $is_default === true)
  67. {
  68. $_NEW_MODULE = array();
  69. foreach($_MODULE as $key=>$value)
  70. {
  71. $_NEW_MODULE[self::DEFAULT_THEME_NAME.$key] = $value;
  72. }
  73. $_MODULE = $_NEW_MODULE;
  74. }
  75. $_MODULES = (is_array($_MODULES) AND is_array($_MODULE)) ? array_merge($_MODULES, $_MODULE) : $_MODULE;
  76. }
  77. }
  78. /**
  79. * This method is only used by AdminTranslations::submitCopyLang().
  80. *
  81. * It try to create folder in new theme.
  82. *
  83. * When a translation file is copied for a module, its translation key is wrong.
  84. * We have to change the translation key and rewrite the file.
  85. *
  86. * @param string $dest file name
  87. * @return bool
  88. */
  89. protected function checkDirAndCreate($dest)
  90. {
  91. $bool = true;
  92. // To get only folder path
  93. $path = dirname($dest);
  94. // If folder wasn't already added
  95. if (!file_exists($path))
  96. {
  97. if (!mkdir($path, 0777, true))
  98. {
  99. $bool &= false;
  100. $this->_errors[] = $this->l('Cannot create the folder').' "'.$path.'". '.$this->l('Check directory writing permissions.');
  101. }
  102. }
  103. return $bool;
  104. }
  105. protected function writeTranslationFile($type, $path, $mark = false, $fullmark = false)
  106. {
  107. global $currentIndex;
  108. if ($fd = fopen($path, 'w'))
  109. {
  110. unset($_POST['submitTranslations'.$type], $_POST['lang']);
  111. unset($_POST['token']);
  112. $toInsert = array();
  113. foreach($_POST AS $key => $value)
  114. if (!empty($value))
  115. $toInsert[$key] = $value;
  116. $tab = ($fullmark ? Tools::strtoupper($fullmark) : 'LANG').($mark ? Tools::strtoupper($mark) : '');
  117. fwrite($fd, "<?php\n\nglobal \$_".$tab.";\n\$_".$tab." = array();\n");
  118. foreach($toInsert AS $key => $value)
  119. fwrite($fd, '$_'.$tab.'[\''.pSQL($key, true).'\'] = \''.pSQL($value, true).'\';'."\n");
  120. fwrite($fd, "\n?>");
  121. fclose($fd);
  122. Tools::redirectAdmin($currentIndex.'&conf=4&token='.$this->token);
  123. }
  124. else
  125. die('Cannot write language file');
  126. }
  127. public function submitCopyLang()
  128. {
  129. global $currentIndex;
  130. if (!($fromLang = strval(Tools::getValue('fromLang'))) OR !($toLang = strval(Tools::getValue('toLang'))))
  131. $this->_errors[] = $this->l('you must select 2 languages in order to copy data from one to another');
  132. elseif (!($fromTheme = strval(Tools::getValue('fromTheme'))) OR !($toTheme = strval(Tools::getValue('toTheme'))))
  133. $this->_errors[] = $this->l('you must select 2 themes in order to copy data from one to another');
  134. elseif (!Language::copyLanguageData(Language::getIdByIso($fromLang), Language::getIdByIso($toLang)))
  135. $this->_errors[] = $this->l('an error occurred while copying data');
  136. elseif ($fromLang == $toLang AND $fromTheme == $toTheme)
  137. $this->_errors[] = $this->l('nothing to copy! (same language and theme)');
  138. if (sizeof($this->_errors))
  139. return ;
  140. $bool = true;
  141. $items = Language::getFilesList($fromLang, $fromTheme, $toLang, $toTheme, false, false, true);
  142. foreach ($items AS $source => $dest)
  143. {
  144. $bool &= $this->checkDirAndCreate($dest);
  145. $bool &= @copy($source, $dest);
  146. if (strpos($dest, 'modules') AND basename($source) === $fromLang.'.php' AND $bool !== false)
  147. {
  148. $bool &= $this->changeModulesKeyTranslation($dest, $fromTheme, $toTheme);
  149. }
  150. }
  151. if ($bool)
  152. Tools::redirectAdmin($currentIndex.'&conf=14&token='.$this->token);
  153. $this->_errors[] = $this->l('a part of the data has been copied but some language files could not be found or copied');
  154. }
  155. /**
  156. * Change the key translation to according it to theme name.
  157. *
  158. * @param string $path
  159. * @param string $theme_from
  160. * @param string $theme_to
  161. * @return boolean
  162. */
  163. public function changeModulesKeyTranslation ($path, $theme_from, $theme_to)
  164. {
  165. $content = file_get_contents($path);
  166. $arr_replace = array();
  167. $bool_flag = true;
  168. if (preg_match_all('#\$_MODULE\[\'([^\']+)\'\]#Ui', $content, $matches))
  169. {
  170. foreach ($matches[1] as $key=>$value)
  171. {
  172. $arr_replace[$value] = str_replace($theme_from, $theme_to, $value);
  173. }
  174. $content = str_replace(array_keys($arr_replace), array_values($arr_replace), $content);
  175. $bool_flag = (file_put_contents($path, $content) === false) ? false : true;
  176. }
  177. return $bool_flag;
  178. }
  179. public function submitExportLang()
  180. {
  181. global $currentIndex;
  182. $lang = strtolower(Tools::getValue('iso_code'));
  183. $theme = strval(Tools::getValue('theme'));
  184. if ($lang AND $theme)
  185. {
  186. $items = array_flip(Language::getFilesList($lang, $theme, false, false, false, false, true));
  187. $gz = new Archive_Tar(_PS_TRANSLATIONS_DIR_.'/export/'.$lang.'.gzip', true);
  188. if ($gz->createModify($items, NULL, _PS_ROOT_DIR_));
  189. Tools::redirect('translations/export/'.$lang.'.gzip');
  190. $this->_errors[] = Tools::displayError('An error occurred while creating archive.');
  191. }
  192. $this->_errors[] = Tools::displayError('Please choose a language and theme.');
  193. }
  194. public function checkAndAddMailsFiles ($iso_code, $files_list)
  195. {
  196. $mails = scandir(_PS_MAIL_DIR_.'en/');
  197. $mails_new_lang = array();
  198. foreach ($files_list as $file)
  199. {
  200. if (preg_match('#^mails\/([a-z0-9]+)\/#Ui', $file['filename'], $matches))
  201. {
  202. $slash_pos = strrpos($file['filename'], '/');
  203. $mails_new_lang[] = substr($file['filename'], -(strlen($file['filename'])-$slash_pos-1));
  204. }
  205. }
  206. $arr_mails_needed = array_diff($mails, $mails_new_lang);
  207. foreach ($arr_mails_needed as $mail_to_add)
  208. {
  209. if ($mail_to_add !== '.' && $mail_to_add !== '..' && $mail_to_add !== '.svn')
  210. {
  211. @copy(_PS_MAIL_DIR_.'en/'.$mail_to_add, _PS_MAIL_DIR_.$iso_code.'/'.$mail_to_add);
  212. }
  213. }
  214. }
  215. public function submitImportLang()
  216. {
  217. global $currentIndex;
  218. if (!isset($_FILES['file']['tmp_name']) OR !$_FILES['file']['tmp_name'])
  219. $this->_errors[] = Tools::displayError('No file selected');
  220. else
  221. {
  222. $gz = new Archive_Tar($_FILES['file']['tmp_name'], true);
  223. $iso_code = str_replace('.gzip', '', $_FILES['file']['name']);
  224. $files_list = $gz->listContent();
  225. if ($gz->extract(_PS_TRANSLATIONS_DIR_.'../', false))
  226. {
  227. $this->checkAndAddMailsFiles($iso_code, $files_list);
  228. if (Validate::isLanguageFileName($_FILES['file']['name']))
  229. {
  230. if (!Language::checkAndAddLanguage($iso_code))
  231. $conf = 20;
  232. }
  233. Tools::redirectAdmin($currentIndex.'&conf='.(isset($conf) ? $conf : '15').'&token='.$this->token);
  234. }
  235. $this->_errors[] = Tools::displayError('Archive cannot be extracted.');
  236. }
  237. }
  238. public function submitAddLang()
  239. {
  240. global $currentIndex;
  241. $arr_import_lang = explode('|', Tools::getValue('params_import_language')); /* 0 = Language ISO code, 1 = PS version */
  242. if (Validate::isLangIsoCode($arr_import_lang[0]))
  243. {
  244. if ($content = Tools::file_get_contents('http://www.prestashop.com/download/lang_packs/gzip/'.$arr_import_lang[1].'/'.$arr_import_lang[0].'.gzip', false, @stream_context_create(array('http' => array('method' => 'GET', 'timeout' => 5)))))
  245. {
  246. $file = _PS_TRANSLATIONS_DIR_.$arr_import_lang[0].'.gzip';
  247. if (file_put_contents($file, $content))
  248. {
  249. $gz = new Archive_Tar($file, true);
  250. $files_list = $gz->listContent();
  251. if ($gz->extract(_PS_TRANSLATIONS_DIR_.'../', false))
  252. {
  253. $this->checkAndAddMailsFiles($arr_import_lang[0], $files_list);
  254. if (!Language::checkAndAddLanguage($arr_import_lang[0]))
  255. $conf = 20;
  256. if (!unlink($file))
  257. $this->_errors[] = Tools::displayError('Cannot delete archive');
  258. Tools::redirectAdmin($currentIndex.'&conf='.(isset($conf) ? $conf : '15').'&token='.$this->token);
  259. }
  260. $this->_errors[] = Tools::displayError('Archive cannot be extracted.');
  261. if (!unlink($file))
  262. $this->_errors[] = Tools::displayError('Cannot delete archive');
  263. }
  264. else
  265. $this->_errors[] = Tools::displayError('Server does not have permissions for writing.');
  266. }
  267. else
  268. $this->_errors[] = Tools::displayError('Language not found');
  269. }
  270. else
  271. $this->_errors[] = Tools::displayError('Invalid parameter');
  272. }
  273. /**
  274. * This method check each file (tpl or php file), get its sentences to translate,
  275. * compare with posted values and write in iso code translation file.
  276. *
  277. * @param string $file_name
  278. * @param array $files
  279. * @param string $theme_name
  280. * @param string $module_name
  281. * @param string|boolean $dir
  282. * @return void
  283. */
  284. protected function findAndWriteTranslationsIntoFile($file_name, $files, $theme_name, $module_name, $dir = false)
  285. {
  286. // These static vars allow to use file to write just one time.
  287. static $_cache_file = array();
  288. static $str_write = '';
  289. static $array_check_duplicate = array();
  290. // Default translations and Prestashop overriding themes are distinguish
  291. $is_default = $theme_name === self::DEFAULT_THEME_NAME ? true : false;
  292. // Set file_name in static var, this allow to open and wright the file just one time
  293. if (!isset($_cache_file[($is_default ? self::DEFAULT_THEME_NAME : $theme_name).'-'.$file_name]) )
  294. {
  295. $str_write = '';
  296. $_cache_file[($is_default ? self::DEFAULT_THEME_NAME : $theme_name).'-'.$file_name] = true;
  297. if (!file_exists($file_name))
  298. file_put_contents($file_name, '');
  299. if (!is_writable($file_name))
  300. die ($this->l('Cannot write the theme\'s language file ').'('.$file_name.')'.$this->l('. Please check write permissions.'));
  301. // this string is initialized one time for a file
  302. $str_write .= "<?php\n\nglobal \$_MODULE;\n\$_MODULE = array();\n";
  303. $array_check_duplicate = array();
  304. }
  305. if (!$dir)
  306. $dir = ($theme_name == self::DEFAULT_THEME_NAME ? _PS_MODULE_DIR_.$module_name.'/' : _PS_ALL_THEMES_DIR_.$theme_name.'/modules/'.$module_name.'/');
  307. foreach ($files AS $template_file)
  308. {
  309. if ((preg_match('/^(.*).tpl$/', $template_file) OR ($is_default AND preg_match('/^(.*).php$/', $template_file))) AND file_exists($tpl = $dir.$template_file))
  310. {
  311. // Get translations key
  312. $content = file_get_contents($tpl);
  313. preg_match_all(substr($template_file, -4) == '.tpl' ? self::$tpl_regexp : self::$php_regexp, $content, $matches);
  314. // Write each translation on its module file
  315. $template_name = substr(basename($template_file), 0, -4);
  316. foreach ($matches[1] AS $key)
  317. {
  318. $post_key = md5(strtolower($module_name).'_'.($is_default ? self::DEFAULT_THEME_NAME : strtolower($theme_name)).'_'.strtolower($template_name).'_'.md5($key));
  319. $pattern = '\'<{'.strtolower($module_name).'}'.($is_default ? 'prestashop' : strtolower($theme_name)).'>'.strtolower($template_name).'_'.md5($key).'\'';
  320. if (array_key_exists($post_key, $_POST) AND !empty($_POST[$post_key]) AND !in_array($pattern, $array_check_duplicate))
  321. {
  322. $array_check_duplicate[] = $pattern;
  323. $str_write .= '$_MODULE['.$pattern.'] = \''.pSQL($_POST[$post_key]).'\';'."\n";
  324. $this->total_expression++;
  325. }
  326. }
  327. }
  328. }
  329. if (isset($_cache_file[($is_default ? self::DEFAULT_THEME_NAME : $theme_name).'-'.$file_name]) AND $str_write != "<?php\n\nglobal \$_MODULE;\n\$_MODULE = array();\n")
  330. file_put_contents($file_name, $str_write);
  331. }
  332. public function clearModuleFiles ($files, $type_clear = 'file', $path = '')
  333. {
  334. $arr_exclude = array('img', 'js', 'mails');
  335. $arr_good_ext = array('.tpl', '.php');
  336. foreach ($files as $key=>$file)
  337. {
  338. if ($file{0} === '.' OR in_array(substr($file, 0, strrpos($file,'.')), $this->all_iso_lang))
  339. unset($files[$key]);
  340. elseif ($type_clear === 'file' AND !in_array(substr($file, strrpos($file,'.')),$arr_good_ext))
  341. unset($files[$key]);
  342. elseif ($type_clear === 'directory' AND (!is_dir($path.$file) OR in_array($file, $arr_exclude)))
  343. unset($files[$key]);
  344. }
  345. return $files;
  346. }
  347. /**
  348. * This method get translation for each files of a module,
  349. * compare with global $_MODULES array and fill AdminTranslations::modules_translations array
  350. * With key as English sentences and values as their iso code translations.
  351. *
  352. * @param array $files
  353. * @param string $theme_name
  354. * @param string $module_name
  355. * @param string|boolean $dir
  356. * @param string $iso_code
  357. * @return void
  358. */
  359. protected function findAndFillTranslations($files, $theme_name, $module_name, $dir = false, $iso_code = '')
  360. {
  361. global $_MODULES;
  362. // added for compatibility
  363. $_MODULES = array_change_key_case($_MODULES);
  364. // Default translations and Prestashop overriding themes are distinguish
  365. $is_default = $theme_name === self::DEFAULT_THEME_NAME ? true : false;
  366. if (!$dir)
  367. $dir = ($theme_name === self::DEFAULT_THEME_NAME ? _PS_MODULE_DIR_.$module_name.'/' : _PS_ALL_THEMES_DIR_.$theme_name.'/modules/'.$module_name.'/');
  368. // Thank to this var similar keys are not duplicate
  369. // in AndminTranslation::modules_translations array
  370. // see below
  371. $array_check_duplicate = array();
  372. foreach ($files AS $template_file)
  373. {
  374. if ((preg_match('/^(.*).tpl$/', $template_file) OR ($is_default AND preg_match('/^(.*).php$/', $template_file))) AND file_exists($tpl = $dir.$template_file))
  375. {
  376. $content = file_get_contents($tpl);
  377. // module files can now be ignored by adding this string in a file
  378. if (strpos($content, 'IGNORE_THIS_FILE_FOR_TRANSLATION') !== false)
  379. continue;
  380. // Get translations key
  381. preg_match_all(substr($template_file, -4) == '.tpl' ? self::$tpl_regexp : self::$php_regexp, $content, $matches);
  382. // Write each translation on its module file
  383. $template_name = substr(basename($template_file), 0, -4);
  384. foreach ($matches[1] AS $key)
  385. {
  386. $module_key = ($is_default ? self::DEFAULT_THEME_NAME : '').'<{'.Tools::strtolower($module_name).'}'.strtolower($is_default ? 'prestashop' : $theme_name).'>'.Tools::strtolower($template_name).'_'.md5($key);
  387. // to avoid duplicate entry
  388. if (!in_array($module_key, $array_check_duplicate))
  389. {
  390. $array_check_duplicate[] = $module_key;
  391. $this->modules_translations[strtolower($is_default ? self::DEFAULT_THEME_NAME : $theme_name)][$module_name][$template_name][$key]
  392. = key_exists($module_key, $_MODULES) ? html_entity_decode($_MODULES[$module_key], ENT_COMPAT, 'UTF-8') : '';
  393. $this->total_expression++;
  394. }
  395. }
  396. }
  397. }
  398. }
  399. public function postProcess()
  400. {
  401. global $currentIndex;
  402. if (Tools::isSubmit('submitCopyLang'))
  403. {
  404. if ($this->tabAccess['add'] === '1')
  405. $this->submitCopyLang();
  406. else
  407. $this->_errors[] = Tools::displayError('You do not have permission to add here.');
  408. }
  409. elseif (Tools::isSubmit('submitExport'))
  410. {
  411. if ($this->tabAccess['add'] === '1')
  412. $this->submitExportLang();
  413. else
  414. $this->_errors[] = Tools::displayError('You do not have permission to add here.');
  415. }
  416. elseif (Tools::isSubmit('submitImport'))
  417. {
  418. if ($this->tabAccess['add'] === '1')
  419. $this->submitImportLang();
  420. else
  421. $this->_errors[] = Tools::displayError('You do not have permission to add here.');
  422. }
  423. elseif (Tools::isSubmit('submitAddLanguage'))
  424. {
  425. if ($this->tabAccess['add'] === '1')
  426. $this->submitAddLang();
  427. else
  428. $this->_errors[] = Tools::displayError('You do not have permission to add here.');
  429. }
  430. elseif (Tools::isSubmit('submitTranslationsFront'))
  431. {
  432. if ($this->tabAccess['edit'] === '1')
  433. {
  434. if (!Validate::isLanguageIsoCode(Tools::strtolower(Tools::getValue('lang'))))
  435. die(Tools::displayError());
  436. $this->writeTranslationFile('Front', _PS_THEME_DIR_.'lang/'.Tools::strtolower(Tools::getValue('lang')).'.php');
  437. }
  438. else
  439. $this->_errors[] = Tools::displayError('You do not have permission to edit here.');
  440. }
  441. elseif (Tools::isSubmit('submitTranslationsPDF'))
  442. {
  443. if ($this->tabAccess['edit'] === '1')
  444. {
  445. if (!Validate::isLanguageIsoCode(Tools::strtolower(Tools::getValue('lang'))))
  446. die(Tools::displayError());
  447. $this->writeTranslationFile('PDF', _PS_TRANSLATIONS_DIR_.Tools::strtolower(Tools::getValue('lang')).'/pdf.php', 'PDF');
  448. }
  449. else
  450. $this->_errors[] = Tools::displayError('You do not have permission to edit here.');
  451. }
  452. elseif (Tools::isSubmit('submitTranslationsBack'))
  453. {
  454. if ($this->tabAccess['edit'] === '1')
  455. {
  456. if (!Validate::isLanguageIsoCode(Tools::strtolower(Tools::getValue('lang'))))
  457. die(Tools::displayError());
  458. $this->writeTranslationFile('Back', _PS_TRANSLATIONS_DIR_.Tools::strtolower(Tools::getValue('lang')).'/admin.php', 'ADM');
  459. }
  460. else
  461. $this->_errors[] = Tools::displayError('You do not have permission to edit here.');
  462. }
  463. elseif (Tools::isSubmit('submitTranslationsErrors'))
  464. {
  465. if ($this->tabAccess['edit'] === '1')
  466. {
  467. if (!Validate::isLanguageIsoCode(Tools::strtolower(Tools::getValue('lang'))))
  468. die(Tools::displayError());
  469. $this->writeTranslationFile('Errors', _PS_TRANSLATIONS_DIR_.Tools::strtolower(Tools::getValue('lang')).'/errors.php', false, 'ERRORS');
  470. }
  471. else
  472. $this->_errors[] = Tools::displayError('You do not have permission to edit here.');
  473. }
  474. elseif (Tools::isSubmit('submitTranslationsFields'))
  475. {
  476. if ($this->tabAccess['edit'] === '1')
  477. {
  478. if (!Validate::isLanguageIsoCode(Tools::strtolower(Tools::getValue('lang'))))
  479. die(Tools::displayError());
  480. $this->writeTranslationFile('Fields', _PS_TRANSLATIONS_DIR_.Tools::strtolower(Tools::getValue('lang')).'/fields.php', false, 'FIELDS');
  481. }
  482. else
  483. $this->_errors[] = Tools::displayError('You do not have permission to edit here.');
  484. }
  485. elseif (Tools::isSubmit('submitTranslationsMails') || Tools::isSubmit('submitTranslationsMailsAndStay'))
  486. {
  487. if ($this->tabAccess['edit'] === '1' && ($id_lang = Language::getIdByIso(Tools::getValue('lang'))) > 0)
  488. {
  489. $this->submitTranslationsMails($id_lang);
  490. }
  491. else
  492. $this->_errors[] = Tools::displayError('You do not have permission to edit here.');
  493. }
  494. elseif (Tools::isSubmit('submitTranslationsModules'))
  495. {
  496. if ($this->tabAccess['edit'] === '1')
  497. {
  498. $array_lang_src = Language::getLanguages(false);
  499. foreach ($array_lang_src as $language)
  500. $this->all_iso_lang[] = $language['iso_code'];
  501. $lang = Tools::strtolower($_POST['lang']);
  502. if (!Validate::isLanguageIsoCode($lang))
  503. die(Tools::displayError());
  504. if (!$modules = scandir(_PS_MODULE_DIR_))
  505. $this->displayWarning(Tools::displayError('There are no modules in your copy of PrestaShop. Use the Modules tab to activate them or go to our Website to download additional Modules.'));
  506. else
  507. {
  508. $arr_find_and_write = array();
  509. $arr_files = $this->getAllModuleFiles($modules, _PS_MODULE_DIR_, $lang, true);
  510. $arr_find_and_write = array_merge($arr_find_and_write, $arr_files);
  511. if (file_exists(_PS_THEME_DIR_.'/modules/'))
  512. {
  513. $modules = scandir(_PS_THEME_DIR_.'/modules/');
  514. $arr_files = $this->getAllModuleFiles($modules, _PS_THEME_DIR_.'modules/', $lang);
  515. $arr_find_and_write = array_merge($arr_find_and_write, $arr_files);
  516. }
  517. foreach ($arr_find_and_write as $key=>$value)
  518. $this->findAndWriteTranslationsIntoFile($value['file_name'], $value['files'], $value['theme'], $value['module'], $value['dir']);
  519. Tools::redirectAdmin($currentIndex.'&conf=4&token='.$this->token);
  520. }
  521. }
  522. else
  523. $this->_errors[] = Tools::displayError('You do not have permission to edit here.');
  524. }
  525. }
  526. protected function getMailPattern()
  527. {
  528. // Let the indentation like it.
  529. return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd">
  530. <html>
  531. <head>
  532. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  533. <title>#title</title>
  534. </head>
  535. <body>
  536. #content
  537. </body>
  538. </html>';
  539. }
  540. /**
  541. * This method is used to wright translation for mails.
  542. * This wrights subject translation files
  543. * (in root/mails/lang_choosen/lang.php or root/_PS_THEMES_DIR_/mails/lang_choosen/lang.php)
  544. * and mails files.
  545. *
  546. * @param int $id_lang
  547. */
  548. protected function submitTranslationsMails ($id_lang)
  549. {
  550. global $currentIndex;
  551. $obj_lang = new Language($id_lang);
  552. $params_redirect = (Tools::isSubmit('submitTranslationsMailsAndStay') ? '&lang='.Tools::getValue('lang').'&type='.Tools::getValue('type') : '');
  553. $arr_mail_content = array();
  554. $arr_mail_path = array();
  555. if (Tools::getValue('core_mail')) {
  556. $arr_mail_content['core_mail'] = Tools::getValue('core_mail');
  557. $arr_mail_path['core_mail'] = _PS_MAIL_DIR_.$obj_lang->iso_code.'/';
  558. }
  559. if (Tools::getValue('module_mail')) {
  560. $arr_mail_content['module_mail'] = Tools::getValue('module_mail');
  561. $arr_mail_path['module_mail'] = _PS_MODULE_DIR_.'{module}'.'/mails/'.$obj_lang->iso_code.'/';
  562. }
  563. if (Tools::getValue('theme_mail')) {
  564. $arr_mail_content['theme_mail'] = Tools::getValue('theme_mail');
  565. $arr_mail_path['theme_mail'] = _PS_THEME_DIR_.'mails/'.$obj_lang->iso_code.'/';
  566. }
  567. if (Tools::getValue('theme_module_mail')) {
  568. $arr_mail_content['theme_module_mail'] = Tools::getValue('theme_module_mail');
  569. $arr_mail_path['theme_module_mail'] = _PS_THEME_DIR_.'modules/{module}'.'/mails/'.$obj_lang->iso_code.'/';
  570. }
  571. // Save each mail content
  572. foreach ($arr_mail_content as $group_name=>$all_content)
  573. {
  574. foreach ($all_content as $type_content=>$mails)
  575. {
  576. foreach ($mails as $mail_name=>$content)
  577. {
  578. $module_name = false;
  579. $module_name_pipe_pos = stripos($mail_name, '|');
  580. if ($module_name_pipe_pos)
  581. {
  582. $module_name = substr($mail_name, 0, $module_name_pipe_pos);
  583. $mail_name = substr($mail_name, $module_name_pipe_pos+1);
  584. }
  585. if ($type_content == 'html')
  586. {
  587. $content = Tools::htmlentitiesUTF8($content);
  588. $content = htmlspecialchars_decode($content);
  589. // replace correct end of line
  590. $content = str_replace("\r\n", PHP_EOL, $content);
  591. $title = '';
  592. if (Tools::getValue('title_'.$group_name.'_'.$mail_name))
  593. {
  594. $title = Tools::getValue('title_'.$group_name.'_'.$mail_name);
  595. }
  596. $string_mail = $this->getMailPattern();
  597. $content = str_replace(array('#title', '#content'), array($title, $content), $string_mail);
  598. // Magic Quotes shall... not.. PASS!
  599. if (_PS_MAGIC_QUOTES_GPC_)
  600. $content = stripslashes($content);
  601. }
  602. if (Validate::isCleanHTML($content))
  603. {
  604. $path = $arr_mail_path[$group_name];
  605. if ($module_name)
  606. $path = str_replace('{module}', $module_name, $path);
  607. file_put_contents($path.$mail_name.'.'.$type_content, $content);
  608. chmod($path.$mail_name.'.'.$type_content, 0777);
  609. }
  610. else
  611. {
  612. $this->_errors[] = Tools::displayError('HTML e-mail templates cannot contain JavaScript code.');
  613. }
  614. }
  615. }
  616. }
  617. // Update subjects
  618. $array_subjects = array();
  619. if ($subjects = Tools::getValue('subject') AND is_array($subjects))
  620. {
  621. $array_subjects['core_and_modules'] = array('translations'=>array(), 'path'=>$arr_mail_path['core_mail'].'lang.php');
  622. if (isset($arr_mail_path['theme_mail']))
  623. $array_subjects['themes_and_modules'] = array('translations'=>array(), 'path'=>$arr_mail_path['theme_mail'].'lang.php');
  624. foreach ($subjects AS $group => $subject_translation)
  625. {
  626. if ($group == 'core_mail' || $group == 'module_mail') {
  627. $array_subjects['core_and_modules']['translations'] = array_merge($array_subjects['core_and_modules']['translations'], $subject_translation);
  628. }
  629. elseif ( isset($array_subjects['themes_and_modules']) && ($group == 'theme_mail' || $group == 'theme_module_mail')) {
  630. $array_subjects['themes_and_modules']['translations'] = array_merge($array_subjects['themes_and_modules']['translations'], $subject_translation);
  631. }
  632. }
  633. }
  634. if (!empty($array_subjects)) {
  635. foreach ($array_subjects as $type=>$infos) {
  636. $this->writeSubjectTranslationFile($infos['translations'], $infos['path']);
  637. }
  638. }
  639. if (count($this->_errors) == 0)
  640. Tools::redirectAdmin($currentIndex.'&conf=4&token='.$this->token.$params_redirect);
  641. }
  642. public function display()
  643. {
  644. global $currentIndex, $cookie;
  645. $translations = array(
  646. 'front' => $this->l('Front Office translations'),
  647. 'back' => $this->l('Back Office translations'),
  648. 'errors' => $this->l('Error message translations'),
  649. 'fields' => $this->l('Field name translations'),
  650. 'modules' => $this->l('Module translations'),
  651. 'pdf' => $this->l('PDF translations'),
  652. 'mails' => $this->l('E-mail template translations'),
  653. );
  654. if ($type = Tools::getValue('type'))
  655. $this->{'displayForm'.ucfirst($type)}(Tools::strtolower(Tools::getValue('lang')));
  656. else
  657. {
  658. $languages = Language::getLanguages(false);
  659. echo '<fieldset class="width3"><legend><img src="../img/admin/translation.gif" />'.$this->l('Modify translations').'</legend>'.
  660. $this->l('Here you can modify translations for all text input into PrestaShop.').'<br />'.
  661. $this->l('First, select a section (such as Back Office or Modules), then click the flag representing the language you want to edit.').'<br /><br />
  662. <form method="get" action="index.php" id="typeTranslationForm">
  663. <input type="hidden" name="tab" value="AdminTranslations" />
  664. <input type="hidden" name="lang" id="translation_lang" value="0" />
  665. <select name="type" style="float:left; margin-right:10px;">';
  666. foreach ($translations AS $key => $translation)
  667. echo '<option value="'.$key.'">'.$translation.'&nbsp;</option>';
  668. echo '</select>';
  669. foreach ($languages AS $language)
  670. echo '<a href="javascript:chooseTypeTranslation(\''.$language['iso_code'].'\')">
  671. <img src="'._THEME_LANG_DIR_.$language['id_lang'].'.jpg" alt="'.$language['iso_code'].'" title="'.$language['iso_code'].'" />
  672. </a>';
  673. echo '<input type="hidden" name="token" value="'.$this->token.'" /></form></fieldset>
  674. <br /><br /><h2>'.$this->l('Translation exchange').'</h2>';
  675. echo '<form action="'.$currentIndex.'&token='.$this->token.'" method="post" enctype="multipart/form-data">
  676. <fieldset class="width3">
  677. <legend>
  678. <img src="../img/admin/import.gif" />'.$this->l('Add / Update a language').'
  679. </legend>
  680. <div id="submitAddLangContent" style="float:left;"><p>'.$this->l('You can add or update a language directly from prestashop.com here').'</p>';
  681. $this->displayWarning($this->l('If you choose to update an existing language pack, all your previous customization in the theme named prestashop will be lost. This includes front office expressions and default e-mail templates.'));
  682. echo '<div style="font-weight:bold; float:left;">'.$this->l('Language you want to add or update:').' ';
  683. if ($lang_packs = Tools::file_get_contents($this->link_lang_pack .'?version='._PS_VERSION_, false, @stream_context_create(array('http' => array('method' => 'GET', 'timeout' => 5)))))
  684. {
  685. // Notice : for php < 5.2 compatibility, Tools::jsonDecode. The second parameter to true will set us
  686. if ($lang_packs != '' AND $lang_packs = Tools::jsonDecode($lang_packs,true))
  687. {
  688. echo '
  689. <select id="params_import_language" name="params_import_language">
  690. <optgroup label="'.$this->l('Add a language').'">';
  691. $alreadyInstalled = '<optgroup label="'.$this->l('Update a language').'">';
  692. foreach($lang_packs AS $lang_pack)
  693. {
  694. if (!Language::isInstalled($lang_pack['iso_code']))
  695. echo '<option value="'.$lang_pack['iso_code'].'|'.$lang_pack['version'].'">'.$lang_pack['name'].'</option>';
  696. else
  697. $alreadyInstalled.='<option value="'.$lang_pack['iso_code'].'|'.$lang_pack['version'].'">'.$lang_pack['name'].'</option>';
  698. }
  699. echo '
  700. </optgroup>'.$alreadyInstalled.'</optgroup>
  701. </select> &nbsp;<input type="submit" value="'.$this->l('Add or update the language').'" name="submitAddLanguage" class="button" />';
  702. }
  703. echo '</div>';
  704. }
  705. else
  706. echo '<br /><br /><p class="error">'.$this->l('Cannot connect to prestashop.com to get languages list.').'</p></div>';
  707. echo ' </div>
  708. </fieldset>
  709. </form><br /><br />';
  710. echo '<form action="'.$currentIndex.'&token='.$this->token.'" method="post" enctype="multipart/form-data">
  711. <fieldset class="width3">
  712. <legend>
  713. <img src="../img/admin/import.gif" />'.$this->l('Import a language pack manually').'
  714. </legend>
  715. <div id="submitImportContent">'.
  716. $this->l('If the name format is: isocode.gzip (e.g. fr.gzip) and the language corresponding to this package does not exist, it will automatically be created.').
  717. $this->l('Be careful, as it will replace all existing data for the destination language!').'<br /><br />'.
  718. $this->l('Language pack to import:').' <input type="file" name="file" /> &nbsp;<input type="submit" value="'.$this->l('Import').'" name="submitImport" class="button" /></p>
  719. </div>
  720. </fieldset>
  721. </form>
  722. <br /><br />
  723. <form action="'.$currentIndex.'&token='.$this->token.'" method="post" enctype="multipart/form-data">
  724. <fieldset class="width3"><legend><img src="../img/admin/export.gif" />'.$this->l('Export a language').'</legend>
  725. <p>'.$this->l('Export data from one language to a file (language pack).').'<br />'.
  726. $this->l('Choose the theme from which you want to export translations.').'<br />
  727. <select name="iso_code" style="margin-top:10px;">';
  728. foreach ($languages AS $language)
  729. echo '<option value="'.$language['iso_code'].'">'.$language['name'].'</option>';
  730. echo '
  731. </select>
  732. &nbsp;&nbsp;&nbsp;
  733. <select name="theme" style="margin-top:10px;">';
  734. $themes = self::getThemesList();
  735. foreach ($themes AS $theme)
  736. echo '<option value="'.$theme['name'].'">'.$theme['name'].'</option>';
  737. echo '
  738. </select>&nbsp;&nbsp;
  739. <input type="submit" class="button" name="submitExport" value="'.$this->l('Export').'" />
  740. </fieldset>
  741. </form>
  742. <br /><br />';
  743. $allLanguages = Language::getLanguages(false);
  744. echo '
  745. <form action="'.$currentIndex.'&token='.$this->token.'" method="post">
  746. <fieldset class="width3"><legend><img src="../img/admin/copy_files.gif" />'.$this->l('Copy').'</legend>
  747. <p>'.$this->l('Copies data from one language to another.').'<br />'.
  748. $this->l('Be careful, as it will replace all existing data for the destination language!').'<br />'.
  749. $this->l('If necessary').', <b><a href="index.php?tab=AdminLanguages&addlang&token='.Tools::getAdminToken('AdminLanguages'.(int)(Tab::getIdFromClassName('AdminLanguages')).(int)($cookie->id_employee)).'">'.$this->l('first create a new language').'</a></b>.</p>
  750. <div style="float:left;">
  751. <p>
  752. <div style="width:75px; font-weight:bold; float:left;">'.$this->l('From:').'</div>
  753. <select name="fromLang">';
  754. foreach ($languages AS $language)
  755. echo '<option value="'.$language['iso_code'].'">'.$language['name'].'</option>';
  756. echo '
  757. </select>
  758. &nbsp;&nbsp;&nbsp;
  759. <select name="fromTheme">';
  760. $themes = self::getThemesList();
  761. foreach ($themes AS $theme)
  762. echo '<option value="'.$theme['name'].'">'.$theme['name'].'</option>';
  763. echo '
  764. </select> <span style="font-style: bold; color: red;">*</span>
  765. </p>
  766. <p>
  767. <div style="width:75px; font-weight:bold; float:left;">'.$this->l('To:').'</div>
  768. <select name="toLang">';
  769. foreach ($allLanguages AS $language)
  770. echo '<option value="'.$language['iso_code'].'">'.$language['name'].'</option>';
  771. echo '
  772. </select>
  773. &nbsp;&nbsp;&nbsp;
  774. <select name="toTheme">';
  775. $themes = self::getThemesList();
  776. foreach ($themes AS $theme)
  777. echo '<option value="'.$theme['name'].'">'.$theme['name'].'</option>';
  778. echo '
  779. </select>
  780. </p>
  781. </div>
  782. <div style="float:left;">
  783. <input type="submit" value="'.$this->l(' Copy ').'" name="submitCopyLang" class="button" style="margin:25px 0px 0px 25px;" />
  784. </div>
  785. <p style="clear: left; padding: 16px 0px 0px 0px;"><span style="font-style: bold; color: red;">*</span> '.$this->l('Language files (as indicated at Tools >> Languages >> Edition) must be complete to allow copying of translations').'</p>
  786. </fieldset>
  787. </form>';
  788. }
  789. }
  790. public function fileExists($dir, $file, $var)
  791. {
  792. ${$var} = array();
  793. if (!file_exists($dir))
  794. if (!mkdir($dir, 0700))
  795. die('Please create the directory '.$dir);
  796. if (!file_exists($dir.'/'.$file))
  797. if (!file_put_contents($dir.'/'.$file, "<?php\n\nglobal \$".$var.";\n\$".$var." = array();\n\n?>"))
  798. die('Please create a "'.$file.'" file in '.$dir);
  799. if (!is_writable($dir.'/'.$file))
  800. $this->displayWarning(Tools::displayError('This file must be writable:').' '.$dir.'/'.$file);
  801. include($dir.'/'.$file);
  802. return ${$var};
  803. }
  804. public function displayToggleButton($closed = false)
  805. {
  806. $str_output = '
  807. <script type="text/javascript">';
  808. if (Tools::getValue('type') == 'mails')
  809. $str_output .= '$(document).ready(function(){
  810. openCloseAllDiv(\''.Tools::safeOutput($_GET['type']).'_div\', this.value == openAll); toggleElemValue(this.id, openAll, closeAll);
  811. });';
  812. $str_output .= '
  813. var openAll = \''.html_entity_decode($this->l('Expand all fieldsets'), ENT_NOQUOTES, 'UTF-8').'\';
  814. var closeAll = \''.html_entity_decode($this->l('Close all fieldsets'), ENT_NOQUOTES, 'UTF-8').'\';
  815. </script>
  816. <input type="button" class="button" id="buttonall" onclick="openCloseAllDiv(\''.Tools::safeOutput($_GET['type']).'_div\', this.value == openAll); toggleElemValue(this.id, openAll, closeAll);" />
  817. <script type="text/javascript">toggleElemValue(\'buttonall\', '.($closed ? 'openAll' : 'closeAll').', '.($closed ? 'closeAll' : 'openAll').');</script>';
  818. return $str_output;
  819. }
  820. protected function displaySubmitButtons($name)
  821. {
  822. return '
  823. <input type="submit" name="submitTranslations'.ucfirst($name).'" value="'.$this->l('Update translations').'" class="button" />
  824. <input type="submit" name="submitTranslations'.ucfirst($name).'AndStay" value="'.$this->l('Update and stay').'" class="button" />';
  825. }
  826. public function displayAutoTranslate()
  827. {
  828. $languageCode = Tools::htmlentitiesUTF8(Language::getLanguageCodeByIso(Tools::getValue('lang')));
  829. return '
  830. <input type="button" class="button" onclick="translateAll();" value="'.$this->l('Translate with Google (experimental)').'" />
  831. <script type="text/javascript" src="http://www.google.com/jsapi"></script>
  832. <script type="text/javascript">
  833. var gg_translate = {
  834. language_code : \''.$languageCode.'\',
  835. not_available : \''.addslashes(html_entity_decode($this->l('this language is not available on Google Translate API'), ENT_QUOTES, 'utf-8')).'\',
  836. tooltip_title : \''.addslashes(html_entity_decode($this->l('Google translate suggests :'), ENT_QUOTES, 'utf-8')).'\'
  837. };
  838. </script>
  839. <script type="text/javascript" src="../js/gg-translate.js"></script>
  840. <script type="text/javascript">
  841. var displayOnce = 0;
  842. google.load("language", "1");
  843. function translateAll() {
  844. if (!ggIsTranslatable(gg_translate[\'language_code\']))
  845. alert(\'"\'+gg_translate[\'language_code\']+\'" : \'+gg_translate[\'not_available\']);
  846. else
  847. {
  848. $.each($(\'input[type="text"]\'), function() {
  849. var tdinput = $(this);
  850. if (tdinput.attr("value") == "" && tdinput.parent("td").prev().html()) {
  851. google.language.translate(tdinput.parent("td").prev().html(), "en", gg_translate[\'language_code\'], function(result) {
  852. if (!result.error)
  853. tdinput.val(result.translation);
  854. else if (displayOnce == 0)
  855. {
  856. displayOnce = 1;
  857. alert(result.error.message);
  858. }
  859. });
  860. }
  861. });
  862. $.each($("textarea"), function() {
  863. var tdtextarea = $(this);
  864. if (tdtextarea.html() == "" && tdtextarea.parent("td").prev().html()) {
  865. google.language.translate(tdtextarea.parent("td").prev().html(), "en", gg_translate[\'language_code\'], function(result) {
  866. if (!result.error)
  867. tdtextarea.html(result.translation);
  868. else if (displayOnce == 0)
  869. {
  870. displayOnce = 1;
  871. alert(result.error.message);
  872. }
  873. });
  874. }
  875. });
  876. }
  877. }
  878. </script>';
  879. }
  880. public function displayLimitPostWarning($count)
  881. {
  882. $str_output = '';
  883. if ((ini_get('suhosin.post.max_vars') AND ini_get('suhosin.post.max_vars') < $count)
  884. OR (ini_get('suhosin.request.max_vars') AND ini_get('suhosin.request.max_vars') < $count))
  885. {
  886. if (ini_get('suhosin.post.max_vars') < $count OR ini_get('suhosin.request.max_vars') < $count)
  887. {
  888. $this->suhosin_limit_exceed = true;
  889. $str_output .= '<div class="warning">'.$this->l('Warning, your hosting provider is using the suhosin patch for PHP, which limits the maximum number of fields to post in a form:').'<br/>'
  890. .'<b>'.ini_get('suhosin.post.max_vars').'</b> '.$this->l('for suhosin.post.max_vars.').'<br/>'
  891. .'<b>'.ini_get('suhosin.request.max_vars').'</b> '.$this->l('for suhosin.request.max_vars.').'<br/>'
  892. .$this->l('Please ask your hosting provider to increase the suhosin post and request limit to')
  893. .' <u><b>'.((int)$count + 100).'</b></u> '.$this->l('at least.').' '.$this->l('or edit the translation file manually.').'</div>';
  894. }
  895. }
  896. return $str_output;
  897. }
  898. public function displayFormFront($lang)
  899. {
  900. global $currentIndex;
  901. $_LANG = $this->fileExists(_PS_THEME_DIR_.'lang', Tools::strtolower($lang).'.php', '_LANG');
  902. $str_output = '';
  903. /* List templates to parse */
  904. $templates = array_merge(scandir(_PS_THEME_DIR_), scandir(_PS_ALL_THEMES_DIR_));
  905. $count = 0;
  906. $files = array();
  907. foreach ($templates AS $template)
  908. if (preg_match('/^(.*).tpl$/', $template) AND (file_exists($tpl = _PS_THEME_DIR_.$template) OR file_exists($tpl = _PS_ALL_THEMES_DIR_.$template)))
  909. {
  910. $template2 = substr(basename($template), 0, -4);
  911. $newLang = array();
  912. $fd = fopen($tpl, 'r');
  913. $content = fread($fd, filesize($tpl));
  914. /* Search language tags (eg {l s='to translate'}) */
  915. $regex = '/\{l s=\''._PS_TRANS_PATTERN_.'\'( js=1)?\}/U';
  916. preg_match_all($regex, $content, $matches);
  917. /* Get string translation */
  918. foreach($matches[1] AS $key)
  919. {
  920. if (empty($key))
  921. {
  922. $this->_errors[] = $this->l('Empty string found, please edit:').' <br />'._PS_THEME_DIR_.''.$template;
  923. $newLang[$key] = '';
  924. }
  925. else
  926. {
  927. $key2 = $template2.'_'.md5($key);
  928. $newLang[$key] = (key_exists($key2, $_LANG)) ? html_entity_decode($_LANG[$key2], ENT_COMPAT, 'UTF-8') : '';
  929. }
  930. }
  931. $files[$template2] = $newLang;
  932. $count += sizeof($newLang);
  933. }
  934. $str_output .= '
  935. <h2>'.$this->l('Language').' : '.Tools::strtoupper($lang).' - '.$this->l('Front-Office translations').'</h2>
  936. '.$this->l('Total expressions').' : <b>'.$count.'</b>. '.$this->l('Click the fieldset title to expand or close the fieldset.').'.<br /><br />';
  937. $str_output .= $this->displayLimitPostWarning($count);
  938. if (!$this->suhosin_limit_exceed)
  939. {
  940. $str_output .= '
  941. <form method="post" action="'.$currentIndex.'&submitTranslationsFront=1&token='.$this->token.'" class="form">';
  942. $str_output .= $this->displayToggleButton(sizeof($_LANG) >= $count);
  943. $str_output .= $this->displayAutoTranslate();
  944. $str_output .= '<input type="hidden" name="lang" value="'.$lang.'" /><input type="submit" name="submitTranslationsFront" value="'.$this->l('Update translations').'" class="button" /><br /><br />';
  945. foreach ($files AS $k => $newLang)
  946. if (sizeof($newLang))
  947. {
  948. $countValues = array_count_values($newLang);
  949. $empty = isset($countValues['']) ? $countValues[''] : 0;
  950. $str_output .= '
  951. <fieldset><legend style="cursor : pointer" onclick="$(\'#'.$k.'-tpl\').slideToggle();">'.$k.' - <font color="blue">'.sizeof($newLang).'</font> '.$this->l('expressions').' (<font color="red">'.$empty.'</font>)</legend>
  952. <div name="front_div" id="'.$k.'-tpl" style="display: '.($empty ? 'block' : 'none').';">
  953. <table cellpadding="2">';
  954. foreach ($newLang AS $key => $value)
  955. {
  956. $str_output .= '<tr><td style="width: 40%">'.stripslashes($key).'</td><td>';
  957. if (strlen($key) != 0 && strlen($key) < TEXTAREA_SIZED)
  958. $str_output .= '= <input type="text" style="width: 450px" name="'.$k.'_'.md5($key).'" value="'.stripslashes(preg_replace('/"/', '\&quot;', stripslashes($value))).'" />';
  959. elseif (strlen($key))
  960. $str_output .= '= <textarea rows="'.(int)(strlen($key) / TEXTAREA_SIZED).'" style="width: 450px" name="'.$k.'_'.md5($key).'">'.stripslashes(preg_replace('/"/', '\&quot;', stripslashes($value))).'</textarea>';
  961. else
  962. $str_output .= '<span class="error-inline">'.implode(', ', $this->_errors).'</span>';
  963. $str_output .= '</td></tr>';
  964. }
  965. $str_output .= '
  966. </table>
  967. </div>
  968. </fieldset><br />';
  969. }
  970. $str_output .= '<br /><input type="submit" name="submitTranslationsFront" value="'.$this->l('Update translations').'" class="button" /></form>';
  971. }
  972. if (!empty($this->_errors))
  973. $this->displayErrors();
  974. echo $str_output;
  975. }
  976. public function displayFormBack($lang)
  977. {
  978. global $currentIndex;
  979. $_LANGADM = $this->fileExists(_PS_TRANSLATIONS_DIR_.$lang, 'admin.php', '_LANGADM');
  980. $str_output = '';
  981. /* List templates to parse */
  982. $count = 0;
  983. $tabs = scandir(PS_ADMIN_DIR.'/tabs');
  984. $tabs[] = '../../classes/AdminTab.php';
  985. $files = array();
  986. foreach ($tabs AS $tab)
  987. if (preg_match('/^(.*)\.php$/', $tab) AND file_exists($tpl = PS_ADMIN_DIR.'/tabs/'.$tab))
  988. {
  989. $tab = basename(substr($tab, 0, -4));
  990. $fd = fopen($tpl, 'r');
  991. $content = fread($fd, filesize($tpl));
  992. fclose($fd);
  993. $regex = '/this->l\(\''._PS_TRANS_PATTERN_.'\'[\)|\,]/U';
  994. preg_match_all($regex, $content, $matches);
  995. foreach ($matches[1] AS $key)
  996. $tabsArray[$tab][$key] = stripslashes(key_exists($tab.md5($key), $_LANGADM) ? html_entity_decode($_LANGADM[$tab.md5($key)], ENT_COMPAT, 'UTF-8') : '');
  997. $count += isset($tabsArray[$tab]) ? sizeof($tabsArray[$tab]) : 0;
  998. }
  999. foreach (array('header.inc', 'footer.inc', 'index', 'login', 'password', 'functions') AS $tab)
  1000. {
  1001. $tab = PS_ADMIN_DIR.'/'.$tab.'.php';
  1002. $fd = fopen($tab, 'r');
  1003. $content = fread($fd, filesize($tab));
  1004. fclose($fd);
  1005. $regex = '/translate\(\''._PS_TRANS_PATTERN_.'\'\)/U';
  1006. preg_match_all($regex, $content, $matches);
  1007. foreach ($matches[1] AS $key)
  1008. $tabsArray['index'][$key] = stripslashes(key_exists('index'.md5($key), $_LANGADM) ? html_entity_decode($_LANGADM['index'.md5($key)], ENT_COMPAT, 'UTF-8') : '');
  1009. $count += isset($tabsArray['index']) ? sizeof($tabsArray['index']) : 0;
  1010. }
  1011. $str_output .= '
  1012. <h2>'.$this->l('Language').' : '.Tools::strtoupper($lang).' - '.$this->l('Back-Office translations').'</h2>
  1013. '.$this->l('Expressions to translate').' : <b>'.$count.'</b>. '.$this->l('Click on the titles to open fieldsets').'.<br /><br />';
  1014. $str_output .= $this->displayLimitPostWarning($count);
  1015. if (!$this->suhosin_limit_exceed)
  1016. {
  1017. $str_output .= '
  1018. <form method="post" action="'.$currentIndex.'&submitTranslationsBack=1&token='.$this->token.'" class="form">';
  1019. $str_output .= $this->displayToggleButton();
  1020. $str_output .= $this->displayAutoTranslate();
  1021. $str_output .= '<input type="hidden" name="lang" value="'.$lang.'" /><input type="submit" name="submitTranslationsBack" value="'.$this->l('Update translations').'" class="button" /><br /><br />';
  1022. foreach ($tabsArray AS $k => $newLang)
  1023. if (sizeof($newLang))
  1024. {
  1025. $countValues = array_count_values($newLang);
  1026. $empty = isset($countValues['']) ? $countValues[''] : 0;
  1027. $str_output .= '
  1028. <fieldset><legend style="cursor : pointer" onclick="$(\'#'.$k.'-tpl\').slideToggle();">'.$k.' - <font color="blue">'.sizeof($newLang).'</font> '.$this->l('expressions').' (<font color="red">'.$empty.'</font>)</legend>
  1029. <div name="back_div" id="'.$k.'-tpl" style="display: '.($empty ? 'block' : 'none').';">
  1030. <table cellpadding="2">';
  1031. foreach ($newLang AS $key => $value)
  1032. {
  1033. $str_output .= '<tr><td style="width: 40%">'.stripslashes($key).'</td><td>= ';
  1034. if (strlen($key) < TEXTAREA_SIZED)
  1035. $str_output .= '<input type="text" style="width: 450px" name="'.$k.md5($key).'" value="'.stripslashes(preg_replace('/"/', '\&quot;', $value)).'" /></td></tr>';
  1036. else
  1037. $str_output .= '<textarea rows="'.(int)(strlen($key) / TEXTAREA_SIZED).'" style="width: 450px" name="'.$k.md5($key).'">'.stripslashes(preg_replace('/"/', '\&quot;', $value)).'</textarea></td></tr>';
  1038. }
  1039. $str_output .= '
  1040. </table>
  1041. </div>
  1042. </fieldset><br />';
  1043. }
  1044. $str_output .= '<br /><input type="submit" name="submitTranslationsBack" value="'.$this->l('Update translations').'" class="button" /></form>';
  1045. }
  1046. echo $str_output;
  1047. }
  1048. public function displayFormErrors($lang)
  1049. {
  1050. global $currentIndex;
  1051. $_ERRORS = $this->fileExists(_PS_TRANSLATIONS_DIR_.$lang, 'errors.php', '_ERRORS');
  1052. $str_output = '';
  1053. /* List files to parse */
  1054. $stringToTranslate = array();
  1055. $dirToParse = array(PS_ADMIN_DIR.'/../',
  1056. PS_ADMIN_DIR.'/../classes/',
  1057. PS_ADMIN_DIR.'/../controllers/',
  1058. PS_ADMIN_DIR.'/../override/classes/',
  1059. PS_ADMIN_DIR.'/../override/controllers/',
  1060. PS_ADMIN_DIR.'/',
  1061. PS_ADMIN_DIR.'/tabs/');
  1062. if (!file_exists(_PS_MODULE_DIR_))
  1063. die($this->displayWarning(Tools::displayError('Fatal error: Module directory is not here anymore ').'('._PS_MODULE_DIR_.')'));
  1064. if (!is_writable(_PS_MODULE_DIR_))
  1065. $this->displayWarning(Tools::displayError('The module directory must be writable'));
  1066. if (!$modules = scandir(_PS_MODULE_DIR_))
  1067. $this->displayWarning(Tools::displayError('There are no modules in your copy of PrestaShop. Use the Modules tab to activate them or go to our Website to download additional Modules.'));
  1068. else
  1069. {
  1070. $count = 0;
  1071. foreach ($modules AS $module)
  1072. if (is_dir(_PS_MODULE_DIR_.$module) && $module != '.' && $module != '..' && $module != '.svn' )
  1073. $dirToParse[] = _PS_MODULE_DIR_.$module.'/';
  1074. }
  1075. foreach ($dirToParse AS $dir)
  1076. foreach (scandir($dir) AS $file)
  1077. if (preg_match('/\.php$/', $file) AND file_exists($fn = $dir.$file) AND $file != 'index.php')
  1078. {
  1079. if (!filesize($fn))
  1080. continue;
  1081. preg_match_all('/Tools::displayError\(\''._PS_TRANS_PATTERN_.'\'(, (true|false))?\)/U', fread(fopen($fn, 'r'), filesize($fn)), $matches);
  1082. foreach($matches[1] AS $key)
  1083. $stringToTranslate[$key] = (key_exists(md5($key), $_ERRORS)) ? html_entity_decode($_ERRORS[md5($key)], ENT_COMPAT, 'UTF-8') : '';
  1084. }
  1085. $irow = 0;
  1086. $str_output .= $this->displayAutoTranslate();
  1087. $str_output .= '<h2>'.$this->l('Language').' : '.Tools::strtoupper($lang).' - '.$this->l('Error translations').'</h2>'
  1088. .$this->l('Errors to translate').' : <b>'.sizeof($stringToTranslate).'</b><br /><br />';
  1089. $str_output .= $this->displayLimitPostWarning(sizeof($stringToTranslate));
  1090. if (!$this->suhosin_limit_exceed)
  1091. {
  1092. $str_output .= '
  1093. <form method="post" action="'.$currentIndex.'&submitTranslationsErrors=1&lang='.$lang.'&token='.$this->token.'" class="form">
  1094. <input type="submit" name="submitTranslationsErrors" value="'.$this->l('Update translations').'" class="button" /><br /><br />
  1095. <table cellpadding="0" cellspacing="0" class="table">';
  1096. ksort($stringToTranslate);
  1097. foreach ($stringToTranslate AS $key => $value)
  1098. $str_output .= '<tr '.(empty($value) ? 'style="background-color:#FBB"' : (++$irow % 2 ? 'class="alt_row"' : '')).'><td>'.stripslashes($key).'</td><td style="width: 430px">= <input type="text" name="'.md5($key).'" value="'.preg_replace('/"/', '&quot;', stripslashes($value)).'" style="width: 380px"></td></tr>';
  1099. $str_output .= '</table><br /><input type="submit" name="submitTranslationsErrors" value="'.$this->l('Update translations').'" class="button" /></form>';
  1100. }
  1101. echo $str_

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