* @copyright 2007-2014 PrestaShop SA * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * International Registered Trademark & Property of PrestaShop SA */ class AdminTranslationsControllerCore extends AdminController { /** Name of theme by default */ const DEFAULT_THEME_NAME = _PS_DEFAULT_THEME_NAME_; const TEXTAREA_SIZED = 70; /** @var string : Link which list all pack of language */ protected $link_lang_pack = 'http://www.prestashop.com/download/lang_packs/get_each_language_pack.php'; /** @var int : number of sentence which can be translated */ protected $total_expression = 0; /** @var int : number of sentence which aren't translated */ protected $missing_translations = 0; /** @var array : List of ISO code for all languages */ protected $all_iso_lang = array(); /** @var array */ protected $modules_translations = array(); /** @var array : List of folder which must be ignored */ protected static $ignore_folder = array('.', '..', '.svn', '.git', '.htaccess', 'index.php'); /** @var array : List of theme by translation type : FRONT, BACK, ERRORS... */ protected $translations_informations = array(); /** @var array : List of all languages */ protected $languages; /** @var array : List of all themes */ protected $themes; /** @var string : Directory of selected theme */ protected $theme_selected; /** @var string : Name of translations type */ protected $type_selected; /** @var Language object : Language for the selected language */ protected $lang_selected; /** @var boolean : Is true if number of var exceed the suhosin request or post limit */ protected $post_limit_exceed = false; public function __construct() { $this->bootstrap = true; $this->multishop_context = Shop::CONTEXT_ALL; $this->table = 'translations'; parent::__construct(); } /* * Set the type which is selected */ public function setTypeSelected($type_selected) { $this->type_selected = $type_selected; } /** * AdminController::initContent() override * @see AdminController::initContent() */ public function initContent() { $this->initTabModuleList(); $this->initPageHeaderToolbar(); if (!is_null($this->type_selected)) { $method_name = 'initForm'.$this->type_selected; if (method_exists($this, $method_name)) $this->content = $this->initForm($method_name); else { $this->errors[] = sprintf(Tools::displayError('"%s" does not exist.'), $this->type_selected); $this->content = $this->initMain(); } } else $this->content = $this->initMain(); $this->context->smarty->assign(array( 'content' => $this->content, 'show_page_header_toolbar' => $this->show_page_header_toolbar, 'page_header_toolbar_title' => $this->page_header_toolbar_title, 'page_header_toolbar_btn' => $this->page_header_toolbar_btn)); } /** * This function create vars by default and call the good method for generate form * * @param $method_name * @return call the method $this->method_name() */ public function initForm($method_name) { // Create a title for each translation page $title = sprintf( $this->l('%1$s (Language: %2$s, Theme: %3$s)'), $this->translations_informations[$this->type_selected]['name'], $this->lang_selected->name, $this->theme_selected ? $this->theme_selected : $this->l('none') ); // Set vars for all forms $this->tpl_view_vars = array( 'lang' => $this->lang_selected->iso_code, 'title' => $title, 'type' => $this->type_selected, 'theme' => $this->theme_selected, 'post_limit_exceeded' => $this->post_limit_exceed, 'url_submit' => self::$currentIndex.'&submitTranslations'.ucfirst($this->type_selected).'=1&token='.$this->token, 'toggle_button' => $this->displayToggleButton(), 'textarea_sized' => AdminTranslationsControllerCore::TEXTAREA_SIZED ); // Call method initForm for a type return $this->{$method_name}(); } /** * AdminController::initToolbar() override * @see AdminController::initToolbar() */ public function initToolbar() { $this->toolbar_btn['save-and-stay'] = array( 'short' => 'SaveAndStay', 'href' => '#', 'desc' => $this->l('Save and stay'), ); $this->toolbar_btn['save'] = array( 'href' => '#', 'desc' => $this->l('Update translations') ); $this->toolbar_btn['cancel'] = array( 'href' => self::$currentIndex.'&token='.$this->token, 'desc' => $this->l('Cancel') ); } /** * Generate the Main page */ public function initMain() { // Block add/update a language $packs_to_install = array(); $packs_to_update = array(); $token = Tools::getAdminToken('AdminLanguages'.(int)Tab::getIdFromClassName('AdminLanguages').(int)$this->context->employee->id); $file_name = $this->link_lang_pack.'?version='._PS_VERSION_; $array_stream_context = @stream_context_create(array('http' => array('method' => 'GET', 'timeout' => 8))); if ($lang_packs = Tools::file_get_contents($file_name, false, $array_stream_context)) // Notice : for php < 5.2 compatibility, Tools::jsonDecode. The second parameter to true will set us if ($lang_packs != '' && $lang_packs = Tools::jsonDecode($lang_packs, true)) foreach ($lang_packs as $key => $lang_pack) { if (!Language::isInstalled($lang_pack['iso_code'])) $packs_to_install[$key] = $lang_pack; else $packs_to_update[$key] = $lang_pack; } $this->tpl_view_vars = array( 'theme_default' => self::DEFAULT_THEME_NAME, 'theme_lang_dir' =>_THEME_LANG_DIR_, 'token' => $this->token, 'languages' => $this->languages, 'translations_type' => $this->translations_informations, 'packs_to_install' => $packs_to_install, 'packs_to_update' => $packs_to_update, 'url_submit' => self::$currentIndex.'&token='.$this->token, 'themes' => $this->themes, 'id_theme_current' => $this->context->shop->id_theme, 'url_create_language' => 'index.php?controller=AdminLanguages&addlang&token='.$token, ); $this->toolbar_scroll = false; $this->base_tpl_view = 'main.tpl'; $this->content .= $this->renderKpis(); $this->content .= parent::renderView(); return $this->content; } /** * This method merge each arrays of modules translation in the array of modules translations */ protected function getModuleTranslations() { global $_MODULE; $name_var = $this->translations_informations[$this->type_selected]['var']; if (!isset($_MODULE) && !isset($GLOBALS[$name_var])) $GLOBALS[$name_var] = array(); else if (isset($_MODULE)) if (is_array($GLOBALS[$name_var]) && is_array($_MODULE)) $GLOBALS[$name_var] = array_merge($GLOBALS[$name_var], $_MODULE); else $GLOBALS[$name_var] = $_MODULE; } /** * This method is only used by AdminTranslations::submitCopyLang(). * * It try to create folder in new theme. * * When a translation file is copied for a module, its translation key is wrong. * We have to change the translation key and rewrite the file. * * @param string $dest file name * @return bool */ protected function checkDirAndCreate($dest) { $bool = true; // To get only folder path $path = dirname($dest); // If folder wasn't already added // Do not use Tools::file_exists_cache because it changes over time! if (!file_exists($path)) if (!mkdir($path, 0777, true)) { $bool &= false; $this->errors[] = sprintf($this->l('Cannot create the folder "%s". Please check your directory writing permissions.'), $path); } return $bool; } /** * Read the Post var and write the translation file. * This method overwrites the old translation file. * * @param bool $override_file : set true if this file is a override */ protected function writeTranslationFile($override_file = false) { $type = Tools::toCamelCase($this->type_selected, true); $translation_informations = $this->translations_informations[$this->type_selected]; if ($override_file) $file_path = $translation_informations['override']['dir'].$translation_informations['override']['file']; else $file_path = $translation_informations['dir'].$translation_informations['file']; if (!file_exists($file_path)) { if (!file_exists(dirname($file_path)) && !mkdir(dirname($file_path), 0777, true)) throw new PrestaShopException(sprintf(Tools::displayError('Directory "%s" cannot be created'), dirname($file_path))); elseif (!touch($file_path)) throw new PrestaShopException(sprintf(Tools::displayError('File "%s" cannot be created'), $file_path)); } $thm_name = str_replace('.', '', Tools::getValue('theme')); $kpi_key = substr(strtoupper($thm_name.'_'.Tools::getValue('lang')), 0, 16); if ($fd = fopen($file_path, 'w')) { // Get value of button save and stay $save_and_stay = Tools::isSubmit('submitTranslations'.$type.'AndStay'); // Get language $lang = strtolower(Tools::getValue('lang')); // Unset all POST which are not translations unset( $_POST['submitTranslations'.$type], $_POST['submitTranslations'.$type.'AndStay'], $_POST['lang'], $_POST['token'], $_POST['theme'], $_POST['type'] ); // Get all POST which aren't empty $to_insert = array(); foreach ($_POST as $key => $value) if (!empty($value)) $to_insert[$key] = $value; ConfigurationKPI::updateValue('FRONTOFFICE_TRANSLATIONS_EXPIRE', time()); ConfigurationKPI::updateValue('TRANSLATE_TOTAL_'.$kpi_key, count($_POST)); ConfigurationKPI::updateValue('TRANSLATE_DONE_'.$kpi_key, count($to_insert)); // translations array is ordered by key (easy merge) ksort($to_insert); $tab = $translation_informations['var']; fwrite($fd, " $value) fwrite($fd, '$'.$tab.'[\''.pSQL($key, true).'\'] = \''.pSQL($value, true).'\';'."\n"); fwrite($fd, "\n?>"); fclose($fd); // Redirect if ($save_and_stay) $this->redirect(true); else $this->redirect(); } else throw new PrestaShopException(sprintf(Tools::displayError('Cannot write this file: "%s"'), $file_path)); } public function submitCopyLang() { if (!($from_lang = Tools::getValue('fromLang')) || !($to_lang = Tools::getValue('toLang'))) $this->errors[] = $this->l('You must select two languages in order to copy data from one to another.'); else if (!($from_theme = Tools::getValue('fromTheme')) || !($to_theme = Tools::getValue('toTheme'))) $this->errors[] = $this->l('You must select two themes in order to copy data from one to another.'); else if (!Language::copyLanguageData(Language::getIdByIso($from_lang), Language::getIdByIso($to_lang))) $this->errors[] = $this->l('An error occurred while copying data.'); else if ($from_lang == $to_lang && $from_theme == $to_theme) $this->errors[] = $this->l('There is nothing to copy (same language and theme).'); else { $theme_exists = array('from_theme' => false, 'to_theme' => false); foreach ($this->themes as $theme) { if ($theme->directory == $from_theme) $theme_exists['from_theme'] = true; if ($theme->directory == $to_theme) $theme_exists['to_theme'] = true; } if ($theme_exists['from_theme'] == false || $theme_exists['to_theme'] == false) $this->errors[] = $this->l('Theme(s) not found'); } if (count($this->errors)) return; $bool = true; $items = Language::getFilesList($from_lang, $from_theme, $to_lang, $to_theme, false, false, true); foreach ($items as $source => $dest) { if (!$this->checkDirAndCreate($dest)) $this->errors[] = sprintf($this->l('Impossible to create the directory "%s".'), $dest); elseif (!copy($source, $dest)) $this->errors[] = sprintf($this->l('Impossible to copy "%s" to "%s".'), $source, $dest); elseif (strpos($dest, 'modules') && basename($source) === $from_lang.'.php' && $bool !== false) if (!$this->changeModulesKeyTranslation($dest, $from_theme, $to_theme)) $this->errors[] = sprintf($this->l('Impossible to translate "$dest".'), $dest); } if (!count($this->errors)) $this->redirect(false, 14); $this->errors[] = $this->l('A part of the data has been copied but some of the language files could not be found.'); } /** * Change the key translation to according it to theme name. * * @param string $path * @param string $theme_from * @param string $theme_to * @return boolean */ public function changeModulesKeyTranslation($path, $theme_from, $theme_to) { $content = file_get_contents($path); $arr_replace = array(); $bool_flag = true; if (preg_match_all('#\$_MODULE\[\'([^\']+)\'\]#Ui', $content, $matches)) { foreach ($matches[1] as $key => $value) $arr_replace[$value] = str_replace($theme_from, $theme_to, $value); $content = str_replace(array_keys($arr_replace), array_values($arr_replace), $content); $bool_flag = (file_put_contents($path, $content) === false) ? false : true; } return $bool_flag; } public function exportTabs() { // Get name tabs by iso code $tabs = Tab::getTabs($this->lang_selected->id); // Get name of the default tabs $tabs_default_lang = Tab::getTabs(1); $tabs_default = array(); foreach ($tabs_default_lang as $tab) $tabs_default[$tab['class_name']] = pSQL($tab['name']); // Create content $content = "lang_selected->iso_code.DIRECTORY_SEPARATOR; $path = $dir.'tabs.php'; // Check if tabs.php exists for the selected Iso Code if (!Tools::file_exists_cache($dir)) if (!mkdir($dir, 0777, true)) throw new PrestaShopException('The file '.$dir.' cannot be created.'); if (!file_put_contents($path, $content)) throw new PrestaShopException('File "'.$path.'" doesn\'t exists and cannot be created in '.$dir); if (!is_writable($path)) $this->displayWarning(sprintf(Tools::displayError('This file must be writable: %s'), $path)); } public function submitExportLang() { if ($this->lang_selected->iso_code && $this->theme_selected) { $this->exportTabs(); $items = array_flip(Language::getFilesList($this->lang_selected->iso_code, $this->theme_selected, false, false, false, false, true)); $file_name = _PS_TRANSLATIONS_DIR_.'/export/'.$this->lang_selected->iso_code.'.gzip'; require_once(_PS_TOOL_DIR_.'tar/Archive_Tar.php'); $gz = new Archive_Tar($file_name, true); if ($gz->createModify($items, null, _PS_ROOT_DIR_)) { ob_start(); header('Pragma: public'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Cache-Control: public'); header('Content-Description: File Transfer'); header('Content-type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.$this->lang_selected->iso_code.'.gzip'.'"'); header('Content-Transfer-Encoding: binary'); ob_end_flush(); readfile($file_name); @unlink($file_name); exit; } $this->errors[] = Tools::displayError('An error occurred while creating archive.'); } $this->errors[] = Tools::displayError('Please select a language and a theme.'); } public static function checkAndAddMailsFiles($iso_code, $files_list) { if (Language::getIdByIso('en')) $default_language = 'en'; else $default_language = Language::getIsoById((int)Configuration::get('PS_LANG_DEFAULT')); if (!$default_language || !Validate::isLanguageIsoCode($default_language)) return false; // 1 - Scan mails files $mails = array(); if (Tools::file_exists_cache(_PS_MAIL_DIR_.$default_language.'/')) $mails = scandir(_PS_MAIL_DIR_.$default_language.'/'); $mails_new_lang = array(); // Get all email files foreach ($files_list as $file) { if (preg_match('#^mails\/([a-z0-9]+)\/#Ui', $file['filename'], $matches)) { $slash_pos = strrpos($file['filename'], '/'); $mails_new_lang[] = substr($file['filename'], -(strlen($file['filename']) - $slash_pos - 1)); } } // Get the difference $arr_mails_needed = array_diff($mails, $mails_new_lang); // Add mails files foreach ($arr_mails_needed as $mail_to_add) if (!in_array($mail_to_add, self::$ignore_folder)) @copy(_PS_MAIL_DIR_.$default_language.'/'.$mail_to_add, _PS_MAIL_DIR_.$iso_code.'/'.$mail_to_add); // 2 - Scan modules files $modules = scandir(_PS_MODULE_DIR_); $module_mail_en = array(); $module_mail_iso_code = array(); foreach ($modules as $module) { if (!in_array($module, self::$ignore_folder) && Tools::file_exists_cache(_PS_MODULE_DIR_.$module.'/mails/'.$default_language.'/')) { $arr_files = scandir(_PS_MODULE_DIR_.$module.'/mails/'.$default_language.'/'); foreach ($arr_files as $file) { if (!in_array($file, self::$ignore_folder)) { if (Tools::file_exists_cache(_PS_MODULE_DIR_.$module.'/mails/'.$default_language.'/'.$file)) $module_mail_en[] = _PS_MODULE_DIR_.$module.'/mails/ISO_CODE/'.$file; if (Tools::file_exists_cache(_PS_MODULE_DIR_.$module.'/mails/'.$iso_code.'/'.$file)) $module_mail_iso_code[] = _PS_MODULE_DIR_.$module.'/mails/ISO_CODE/'.$file; } } } } // Get the difference in this modules $arr_modules_mails_needed = array_diff($module_mail_en, $module_mail_iso_code); // Add mails files for this modules foreach ($arr_modules_mails_needed as $file) { $file_en = str_replace('ISO_CODE', $default_language, $file); $file_iso_code = str_replace('ISO_CODE', $iso_code, $file); $dir_iso_code = substr($file_iso_code, 0, -(strlen($file_iso_code) - strrpos($file_iso_code, '/') - 1)); if (!file_exists($dir_iso_code)) { mkdir($dir_iso_code); file_put_contents($dir_iso_code.'/index.php', Tools::getDefaultIndexContent()); } if (Tools::file_exists_cache($file_en)) copy($file_en, $file_iso_code); } } /** * Move theme translations in selected themes * * @param array $files * @param array $themes_selected */ public function checkAndAddThemesFiles($files, $themes_selected) { foreach ($files as $file) { // Check if file is a file theme if (preg_match('#^themes\/([a-z0-9]+)\/lang\/#Ui', $file['filename'], $matches)) { $slash_pos = strrpos($file['filename'], '/'); $name_file = substr($file['filename'], -(strlen($file['filename']) - $slash_pos - 1)); $name_default_theme = $matches[1]; $deleted_old_theme = false; // Get the old file theme if (file_exists(_PS_THEME_DIR_.'lang/'.$name_file)) $theme_file_old = _PS_THEME_DIR_.'lang/'.$name_file; else { $deleted_old_theme = true; $theme_file_old = str_replace(self::DEFAULT_THEME_NAME, $name_default_theme, _PS_THEME_DIR_.'lang/'.$name_file); } // Move the old file theme in the new folder foreach ($themes_selected as $theme_name) if (file_exists($theme_file_old)) copy($theme_file_old, str_replace($name_default_theme, $theme_name, $theme_file_old)); if ($deleted_old_theme) @unlink($theme_file_old); } } } /** * Add new translations tabs by code ISO * * @param array $iso_code * @param array $files */ public static function addNewTabs($iso_code, $files) { $errors = array(); foreach ($files as $file) { // Check if file is a file theme if (preg_match('#^translations\/'.$iso_code.'\/tabs.php#Ui', $file['filename'], $matches) && Validate::isLanguageIsoCode($iso_code)) { // Include array width new translations tabs $_TABS = array(); clearstatcache(); if (file_exists(_PS_ROOT_DIR_.DIRECTORY_SEPARATOR.$file['filename'])) include_once(_PS_ROOT_DIR_.DIRECTORY_SEPARATOR.$file['filename']); if (count($_TABS)) { foreach ($_TABS as $class_name => $translations) { // Get instance of this tab by class name $tab = Tab::getInstanceFromClassName($class_name); //Check if class name exists if (isset($tab->class_name) && !empty($tab->class_name)) { $id_lang = Language::getIdByIso($iso_code); $tab->name[(int)$id_lang] = $translations; // Do not crash at intall if (!isset($tab->name[Configuration::get('PS_LANG_DEFAULT')])) $tab->name[(int)Configuration::get('PS_LANG_DEFAULT')] = $translations; if (!Validate::isGenericName($tab->name[(int)$id_lang])) $errors[] = sprintf(Tools::displayError('Tab "%s" is not valid'), $tab->name[(int)$id_lang]); else $tab->update(); } } } } } return $errors; } public static function checkTranslationFile($content) { $lines = array_map('trim', explode("\n", $content)); $global = false; foreach ($lines as $line) { // PHP tags if (in_array($line, array('', ''))) continue; // Global variable declaration if (!$global && preg_match('/^global\s+\$([a-z0-9-_]+)\s*;$/i', $line, $matches)) { $global = $matches[1]; continue; } // Global variable initialization if ($global != false && preg_match('/^\$'.preg_quote($global, '/').'\s*=\s*array\(\s*\)\s*;$/i', $line)) continue; // Global variable initialization without declaration if (!$global && preg_match('/^\$([a-z0-9-_]+)\s*=\s*array\(\s*\)\s*;$/i', $line, $matches)) { $global = $matches[1]; continue; } // Assignation if (preg_match('/^\$'.preg_quote($global, '/').'\[\''._PS_TRANS_PATTERN_.'\'\]\s*=\s*\''._PS_TRANS_PATTERN_.'\'\s*;$/i', $line)) continue; // Sometimes the global variable is returned... if (preg_match('/^return\s+\$'.preg_quote($global, '/').'\s*;$/i', $line, $matches)) continue; return false; } return true; } public function submitImportLang() { if (!isset($_FILES['file']['tmp_name']) || !$_FILES['file']['tmp_name']) $this->errors[] = Tools::displayError('No file has been selected.'); else { require_once(_PS_TOOL_DIR_.'tar/Archive_Tar.php'); $gz = new Archive_Tar($_FILES['file']['tmp_name'], true); $filename = $_FILES['file']['name']; $iso_code = str_replace(array('.tar.gz', '.gzip'), '', $filename); if (Validate::isLangIsoCode($iso_code)) { $themes_selected = Tools::getValue('theme', array(self::DEFAULT_THEME_NAME)); $files_list = AdminTranslationsController::filterTranslationFiles($gz->listContent()); $files_paths = AdminTranslationsController::filesListToPaths($files_list); $uniqid = uniqid(); $sandbox = _PS_CACHE_DIR_.'sandbox'.DIRECTORY_SEPARATOR.$uniqid.DIRECTORY_SEPARATOR; if ($gz->extractList($files_paths, $sandbox)) { foreach ($files_list as $file2check) { //don't validate index.php, will be overwrite when extract in translation directory if (pathinfo($file2check['filename'], PATHINFO_BASENAME) == 'index.php') continue; if (preg_match('@^[0-9a-z-_/\\\\]+\.php$@i', $file2check['filename'])) { if (!AdminTranslationsController::checkTranslationFile(file_get_contents($sandbox.$file2check['filename']))) $this->errors[] = sprintf(Tools::displayError('Validation failed for: %s'), $file2check['filename']); } elseif (!preg_match('@^[0-9a-z-_/\\\\]+\.(html|tpl|txt)$@i', $file2check['filename'])) $this->errors[] = sprintf(Tools::displayError('Unidentified file found: %s'), $file2check['filename']); } Tools::deleteDirectory($sandbox, true); } $i = 0; $tmp_array = array(); foreach ($files_paths as $files_path) { $path = dirname($files_path); if (is_dir(_PS_TRANSLATIONS_DIR_.'../'.$path) && !is_writable(_PS_TRANSLATIONS_DIR_.'../'.$path) && !in_array($path, $tmp_array)) { $this->errors[] = (!$i++? Tools::displayError('The archive cannot be extracted.').' ' : '').Tools::displayError('The server does not have permissions for writing.').' '.sprintf(Tools::displayError('Please check rights for %s'), $path); $tmp_array[] = $path; } } if (count($this->errors)) return false; if ($error = $gz->extractList($files_paths, _PS_TRANSLATIONS_DIR_.'../')) { if (is_object($error) && !empty($error->message)) $this->errors[] = Tools::displayError('The archive cannot be extracted.'). ' '.$error->message; else { foreach ($files_list as $file2check) if (pathinfo($file2check['filename'], PATHINFO_BASENAME) == 'index.php' && file_put_contents(_PS_TRANSLATIONS_DIR_.'../'.$file2check['filename'], Tools::getDefaultIndexContent())) continue; // Clear smarty modules cache Tools::clearCache(); if (Validate::isLanguageFileName($filename)) { if (!Language::checkAndAddLanguage($iso_code)) $conf = 20; else { // Reset cache Language::loadLanguages(); AdminTranslationsController::checkAndAddMailsFiles($iso_code, $files_list); $this->checkAndAddThemesFiles($files_list, $themes_selected); $tab_errors = AdminTranslationsController::addNewTabs($iso_code, $files_list); if (count($tab_errors)) { $this->errors += $tab_errors; return false; } } } $this->redirect(false, (isset($conf) ? $conf : '15')); } } $this->errors[] = Tools::displayError('The archive cannot be extracted.'); } else $this->errors[] = sprintf(Tools::displayError('ISO CODE invalid "%1$s" for the following file: "%2$s"'), $iso_code, $filename); } } /** * Filter the translation files contained in a .gzip pack * and return only the ones that we want. * * Right now the function only needs to check that * the modules for which we want to add translations * are present on the shop (installed or not). * * $list is the output of Archive_Tar::listContent() */ public static function filterTranslationFiles($list) { $kept = array(); foreach ($list as $file) { if ('index.php' == basename($file['filename'])) continue; if (preg_match('#^modules/([^/]+)/#', $file['filename'], $m)) { if (is_dir(_PS_MODULE_DIR_.$m[1])) $kept[] = $file; } else $kept[] = $file; } return $kept; } /** * Turn the list returned by * AdminTranslationsController::filterTranslationFiles() * into a list of paths that can be passed to * Archive_Tar::extractList() */ public static function filesListToPaths($list) { $paths = array(); foreach ($list as $item) $paths[] = $item['filename']; return $paths; } public function submitAddLang() { $arr_import_lang = explode('|', Tools::getValue('params_import_language')); /* 0 = Language ISO code, 1 = PS version */ if (Validate::isLangIsoCode($arr_import_lang[0])) { $array_stream_context = @stream_context_create(array('http' => array('method' => 'GET', 'timeout' => 10))); $content = Tools::file_get_contents('http://www.prestashop.com/download/lang_packs/gzip/'.$arr_import_lang[1].'/'.Tools::strtolower($arr_import_lang[0]).'.gzip', false, $array_stream_context); if ($content) { $file = _PS_TRANSLATIONS_DIR_.$arr_import_lang[0].'.gzip'; if ((bool)@file_put_contents($file, $content)) { require_once(_PS_TOOL_DIR_.'/tar/Archive_Tar.php'); $gz = new Archive_Tar($file, true); $files_list = AdminTranslationsController::filterTranslationFiles($gz->listContent()); if ($error = $gz->extractList(AdminTranslationsController::filesListToPaths($files_list), _PS_TRANSLATIONS_DIR_.'../')) { if (is_object($error) && !empty($error->message)) $this->errors[] = Tools::displayError('The archive cannot be extracted.'). ' '.$error->message; else { if (!Language::checkAndAddLanguage($arr_import_lang[0])) $conf = 20; else { // Reset cache Language::loadLanguages(); // Clear smarty modules cache Tools::clearCache(); AdminTranslationsController::checkAndAddMailsFiles($arr_import_lang[0], $files_list); if ($tab_errors = AdminTranslationsController::addNewTabs($arr_import_lang[0], $files_list)) $this->errors += $tab_errors; } if (!unlink($file)) $this->errors[] = sprintf(Tools::displayError('Cannot delete the archive %s.'), $file); $this->redirect(false, (isset($conf) ? $conf : '15')); } } elseif (!unlink($file)) $this->errors[] = sprintf(Tools::displayError('Cannot delete the archive %s.'), $file); } else $this->errors[] = Tools::displayError('The server does not have permissions for writing.').' '.sprintf(Tools::displayError('Please check rights for %s'), dirname($file)); } else $this->errors[] = Tools::displayError('Language not found.'); } else $this->errors[] = Tools::displayError('Invalid parameter.'); } /** * This method check each file (tpl or php file), get its sentences to translate, * compare with posted values and write in iso code translation file. * * @param string $file_name * @param array $files * @param string $theme_name * @param string $module_name * @param string|boolean $dir * @return void */ protected function findAndWriteTranslationsIntoFile($file_name, $files, $theme_name, $module_name, $dir = false) { // These static vars allow to use file to write just one time. static $cache_file = array(); static $str_write = ''; static $array_check_duplicate = array(); // Set file_name in static var, this allow to open and wright the file just one time if (!isset($cache_file[$theme_name.'-'.$file_name])) { $str_write = ''; $cache_file[$theme_name.'-'.$file_name] = true; if (!Tools::file_exists_cache(dirname($file_name))) mkdir(dirname($file_name), 0777, true); if (!Tools::file_exists_cache($file_name)) file_put_contents($file_name, ''); if (!is_writable($file_name)) throw new PrestaShopException(sprintf( Tools::displayError('Cannot write to the theme\'s language file (%s). Please check write permissions.'), $file_name )); // this string is initialized one time for a file $str_write .= "userParseFile($content, $this->type_selected, $type_file, $module_name); // Write each translation on its module file $template_name = substr(basename($file), 0, -4); foreach ($matches as $key) { if ($theme_name) { $post_key = md5(strtolower($module_name).'_'.strtolower($theme_name).'_'.strtolower($template_name).'_'.md5($key)); $pattern = '\'<{'.strtolower($module_name).'}'.strtolower($theme_name).'>'.strtolower($template_name).'_'.md5($key).'\''; } else { $post_key = md5(strtolower($module_name).'_'.strtolower($template_name).'_'.md5($key)); $pattern = '\'<{'.strtolower($module_name).'}prestashop>'.strtolower($template_name).'_'.md5($key).'\''; } if (array_key_exists($post_key, $_POST) && !empty($_POST[$post_key]) && !in_array($pattern, $array_check_duplicate)) { $array_check_duplicate[] = $pattern; $str_write .= '$_MODULE['.$pattern.'] = \''.pSQL(str_replace(array("\r\n", "\r", "\n"), ' ', $_POST[$post_key])).'\';'."\n"; $this->total_expression++; } } } } if (isset($cache_file[$theme_name.'-'.$file_name]) && $str_write != " $file) { if ($file{0} === '.' || in_array(substr($file, 0, strrpos($file, '.')), $this->all_iso_lang)) unset($files[$key]); else if ($type_clear === 'file' && !in_array(substr($file, strrpos($file, '.')), $arr_good_ext)) unset($files[$key]); else if ($type_clear === 'directory' && (!is_dir($path.$file) || in_array($file, $arr_exclude))) unset($files[$key]); } return $files; } /** * This method get translation for each files of a module, * compare with global $_MODULES array and fill AdminTranslations::modules_translations array * With key as English sentences and values as their iso code translations. * * @param array $files * @param string $theme_name * @param string $module_name * @param string|boolean $dir * @param string $iso_code * @return void */ protected function findAndFillTranslations($files, $theme_name, $module_name, $dir = false) { $name_var = $this->translations_informations[$this->type_selected]['var']; // added for compatibility $GLOBALS[$name_var] = array_change_key_case($GLOBALS[$name_var]); // Thank to this var similar keys are not duplicate // in AndminTranslation::modules_translations array // see below $array_check_duplicate = array(); foreach ($files as $file) { if ((preg_match('/^(.*).tpl$/', $file) || preg_match('/^(.*).php$/', $file)) && Tools::file_exists_cache($file_path = $dir.$file)) { // Get content for this file $content = file_get_contents($file_path); // Module files can now be ignored by adding this string in a file if (strpos($content, 'IGNORE_THIS_FILE_FOR_TRANSLATION') !== false) continue; // Get file type $type_file = substr($file, -4) == '.tpl' ? 'tpl' : 'php'; // Parse this content $matches = $this->userParseFile($content, $this->type_selected, $type_file, $module_name); // Write each translation on its module file $template_name = substr(basename($file), 0, -4); foreach ($matches as $key) { $md5_key = md5($key); $module_key = '<{'.Tools::strtolower($module_name).'}'.strtolower($theme_name).'>'.Tools::strtolower($template_name).'_'.$md5_key; $default_key = '<{'.Tools::strtolower($module_name).'}prestashop>'.Tools::strtolower($template_name).'_'.$md5_key; // to avoid duplicate entry if (!in_array($module_key, $array_check_duplicate)) { $array_check_duplicate[] = $module_key; if (!isset($this->modules_translations[$theme_name][$module_name][$template_name][$key]['trad'])) $this->total_expression++; if ($theme_name && array_key_exists($module_key, $GLOBALS[$name_var])) $this->modules_translations[$theme_name][$module_name][$template_name][$key]['trad'] = html_entity_decode($GLOBALS[$name_var][$module_key], ENT_COMPAT, 'UTF-8'); elseif (array_key_exists($default_key, $GLOBALS[$name_var])) $this->modules_translations[$theme_name][$module_name][$template_name][$key]['trad'] = html_entity_decode($GLOBALS[$name_var][$default_key], ENT_COMPAT, 'UTF-8'); else { $this->modules_translations[$theme_name][$module_name][$template_name][$key]['trad'] = ''; $this->missing_translations++; } $this->modules_translations[$theme_name][$module_name][$template_name][$key]['use_sprintf'] = $this->checkIfKeyUseSprintf($key); } } } } } /** * Get list of files which must be parsed by directory and by type of translations * * @return array : list of files by directory */ public function getFileToParseByTypeTranslation() { $directories = array(); switch ($this->type_selected) { case 'front': $directories['tpl'] = array(_PS_ALL_THEMES_DIR_ => scandir(_PS_ALL_THEMES_DIR_)); self::$ignore_folder[] = 'modules'; $directories['tpl'] = array_merge($directories['tpl'], $this->listFiles(_PS_THEME_SELECTED_DIR_)); if (isset($directories['tpl'][_PS_THEME_SELECTED_DIR_.'pdf/'])) unset($directories['tpl'][_PS_THEME_SELECTED_DIR_.'pdf/']); if (Tools::file_exists_cache(_PS_THEME_OVERRIDE_DIR_)) $directories['tpl'] = array_merge($directories['tpl'], $this->listFiles(_PS_THEME_OVERRIDE_DIR_)); break; case 'back': $directories = array( 'php' => array( _PS_ADMIN_CONTROLLER_DIR_ => scandir(_PS_ADMIN_CONTROLLER_DIR_), _PS_OVERRIDE_DIR_.'controllers/admin/' => scandir(_PS_OVERRIDE_DIR_.'controllers/admin/'), _PS_CLASS_DIR_.'helper/' => scandir(_PS_CLASS_DIR_.'helper/'), _PS_CLASS_DIR_.'controller/' => array('AdminController.php'), _PS_CLASS_DIR_ => array('PaymentModule.php') ), 'tpl' => $this->listFiles(_PS_ADMIN_DIR_.DIRECTORY_SEPARATOR.'themes/'), 'specific' => array( _PS_ADMIN_DIR_.DIRECTORY_SEPARATOR => array( 'header.inc.php', 'footer.inc.php', 'index.php', 'functions.php' ) ) ); // For translate the template which are overridden if (file_exists(_PS_OVERRIDE_DIR_.'controllers'.DIRECTORY_SEPARATOR.'admin'.DIRECTORY_SEPARATOR.'templates')) $directories['tpl'] = array_merge($directories['tpl'], $this->listFiles(_PS_OVERRIDE_DIR_.'controllers'.DIRECTORY_SEPARATOR.'admin'.DIRECTORY_SEPARATOR.'templates')); break; case 'errors': $directories['php'] = array( _PS_ROOT_DIR_ => scandir(_PS_ROOT_DIR_), _PS_ADMIN_DIR_.DIRECTORY_SEPARATOR => scandir(_PS_ADMIN_DIR_.DIRECTORY_SEPARATOR), _PS_FRONT_CONTROLLER_DIR_ => scandir(_PS_FRONT_CONTROLLER_DIR_), _PS_ADMIN_CONTROLLER_DIR_ => scandir(_PS_ADMIN_CONTROLLER_DIR_), _PS_OVERRIDE_DIR_.'controllers/front/' => scandir(_PS_OVERRIDE_DIR_.'controllers/front/'), _PS_OVERRIDE_DIR_.'controllers/admin/' => scandir(_PS_OVERRIDE_DIR_.'controllers/admin/') ); // Get all files for folders classes/ and override/classes/ recursively $directories['php'] = array_merge($directories['php'], $this->listFiles(_PS_CLASS_DIR_, array(), 'php')); $directories['php'] = array_merge($directories['php'], $this->listFiles(_PS_OVERRIDE_DIR_.'classes/', array(), 'php')); break; case 'fields': $directories['php'] = $this->listFiles(_PS_CLASS_DIR_, array(), 'php'); break; case 'pdf': $tpl_theme = Tools::file_exists_cache(_PS_THEME_SELECTED_DIR_.'pdf/') ? scandir(_PS_THEME_SELECTED_DIR_.'pdf/') : array(); $directories = array( 'php' => array( _PS_CLASS_DIR_.'pdf/' => scandir(_PS_CLASS_DIR_.'pdf/'), _PS_OVERRIDE_DIR_.'classes/pdf/' => scandir(_PS_OVERRIDE_DIR_.'classes/pdf/') ), 'tpl' => array( _PS_PDF_DIR_ => scandir(_PS_PDF_DIR_), _PS_THEME_SELECTED_DIR_.'pdf/' => $tpl_theme ) ); break; case 'mails': $directories['php'] = array( _PS_FRONT_CONTROLLER_DIR_ => scandir(_PS_FRONT_CONTROLLER_DIR_), _PS_ADMIN_CONTROLLER_DIR_ => scandir(_PS_ADMIN_CONTROLLER_DIR_), _PS_OVERRIDE_DIR_.'controllers/front/' => scandir(_PS_OVERRIDE_DIR_.'controllers/front/'), _PS_OVERRIDE_DIR_.'controllers/admin/' => scandir(_PS_OVERRIDE_DIR_.'controllers/admin/'), _PS_ADMIN_DIR_.DIRECTORY_SEPARATOR => scandir(_PS_ADMIN_DIR_.DIRECTORY_SEPARATOR), _PS_ADMIN_DIR_.DIRECTORY_SEPARATOR.'tabs/' => scandir(_PS_ADMIN_DIR_.DIRECTORY_SEPARATOR.'/tabs') ); // Get all files for folders classes/ and override/classes/ recursively $directories['php'] = array_merge($directories['php'], $this->listFiles(_PS_CLASS_DIR_, array(), 'php')); $directories['php'] = array_merge($directories['php'], $this->listFiles(_PS_OVERRIDE_DIR_.'classes/', array(), 'php')); $directories['php'] = array_merge($directories['php'], $this->getModulesHasMails()); break; } return $directories; } /** * This method parse a file by type of translation and type file * * @param $content * @param $type_translation : front, back, errors, modules... * @param string|bool $type_file : (tpl|php) * @param string $module_name : name of the module * @return return $matches */ protected function userParseFile($content, $type_translation, $type_file = false, $module_name = '') { switch ($type_translation) { case 'front': // Parsing file in Front office $regex = '/\{l\s*s=([\'\"])'._PS_TRANS_PATTERN_.'\1(\s*sprintf=.*)?(\s*js=1)?\s*\}/U'; break; case 'back': // Parsing file in Back office if ($type_file == 'php') $regex = '/this->l\((\')'._PS_TRANS_PATTERN_.'\'[\)|\,]/U'; else if ($type_file == 'specific') $regex = '/Translate::getAdminTranslation\((\')'._PS_TRANS_PATTERN_.'\'(?:,.*)*\)/U'; else $regex = '/\{l\s*s\s*=([\'\"])'._PS_TRANS_PATTERN_.'\1(\s*sprintf=.*)?(\s*js=1)?(\s*slashes=1)?.*\}/U'; break; case 'errors': // Parsing file for all errors syntax $regex = '/Tools::displayError\((\')'._PS_TRANS_PATTERN_.'\'(,\s*(.+))?\)/U'; break; case 'modules': // Parsing modules file if ($type_file == 'php') $regex = '/->l\((\')'._PS_TRANS_PATTERN_.'\'(, ?\'(.+)\')?(, ?(.+))?\)/U'; else // In tpl file look for something that should contain mod='module_name' according to the documentation $regex = '/\{l\s*s=([\'\"])'._PS_TRANS_PATTERN_.'\1.*\s+mod=\''.$module_name.'\'.*\}/U'; break; case 'pdf': // Parsing PDF file if ($type_file == 'php') $regex = '/HTMLTemplate.*::l\((\')'._PS_TRANS_PATTERN_.'\'[\)|\,]/U'; else $regex = '/\{l\s*s=([\'\"])'._PS_TRANS_PATTERN_.'\1(\s*sprintf=.*)?(\s*js=1)?(\s*pdf=\'true\')?\s*\}/U'; break; } if (!is_array($regex)) $regex = array($regex); $strings = array(); foreach ($regex as $regex_row) { $matches = array(); $n = preg_match_all($regex_row, $content, $matches); for ($i = 0; $i < $n; $i += 1) { $quote = $matches[1][$i]; $string = $matches[2][$i]; if ($quote === '"') { // Escape single quotes because the core will do it when looking for the translation of this string $string = str_replace('\'', '\\\'', $string); // Unescape double quotes $string = preg_replace('/\\\\+"/', '"', $string); } $strings[] = $string; } } return array_unique($strings); } /** * Get all translations informations for all type of translations * * array( * 'type' => array( * 'name' => string : title for the translation type, * 'var' => string : name of var for the translation file, * 'dir' => string : dir of translation file * 'file' => string : file name of translation file * ) * ) */ public function getTranslationsInformations() { $this->translations_informations = array( 'front' => array( 'name' => $this->l('Front-office translations'), 'var' => '_LANG', 'dir' => defined('_PS_THEME_SELECTED_DIR_') ? _PS_THEME_SELECTED_DIR_.'lang/' : '', 'file' => $this->lang_selected->iso_code.'.php' ), 'back' => array( 'name' => $this->l('Back-office translations'), 'var' => '_LANGADM', 'dir' => _PS_TRANSLATIONS_DIR_.$this->lang_selected->iso_code.'/', 'file' => 'admin.php' ), 'errors' => array( 'name' => $this->l('Error message translations'), 'var' => '_ERRORS', 'dir' => _PS_TRANSLATIONS_DIR_.$this->lang_selected->iso_code.'/', 'file' => 'errors.php' ), 'fields' => array( 'name' => $this->l('Field name translations'), 'var' => '_FIELDS', 'dir' => _PS_TRANSLATIONS_DIR_.$this->lang_selected->iso_code.'/', 'file' => 'fields.php' ), 'modules' => array( 'name' => $this->l('Installed modules translations'), 'var' => '_MODULES', 'dir' => _PS_MODULE_DIR_, 'file' => '' ), 'pdf' => array( 'name' => $this->l('PDF translations'), 'var' => '_LANGPDF', 'dir' => _PS_TRANSLATIONS_DIR_.$this->lang_selected->iso_code.'/', 'file' => 'pdf.php' ), 'mails' => array( 'name' => $this->l('Email templates translations'), 'var' => '_LANGMAIL', 'dir' => _PS_MAIL_DIR_.$this->lang_selected->iso_code.'/', 'file' => 'lang.php' ) ); if (defined('_PS_THEME_SELECTED_DIR_')) { $this->translations_informations['modules']['override'] = array('dir' => _PS_THEME_SELECTED_DIR_.'modules/', 'file' => ''); $this->translations_informations['pdf']['override'] = array('dir' => _PS_THEME_SELECTED_DIR_.'pdf/lang/', 'file' => $this->lang_selected->iso_code.'.php'); $this->translations_informations['mails']['override'] = array('dir' => _PS_THEME_SELECTED_DIR_.'mails/'.$this->lang_selected->iso_code.'/', 'file' => 'lang.php'); } } /** * Get all informations on : languages, theme and the translation type. */ public function getInformations() { // Get all Languages $this->languages = Language::getLanguages(false); // Get all iso_code of languages foreach ($this->languages as $language) $this->all_iso_lang[] = $language['iso_code']; // Get all themes $this->themes = Theme::getThemes(); // Get folder name of theme if (($theme = Tools::getValue('theme')) && !is_array($theme)) { $theme_exists = $this->theme_exists($theme); if (!$theme_exists) throw new PrestaShopException(sprintf(Tools::displayError('Invalid theme "%s"'), Tools::safeOutput($theme))); $this->theme_selected = Tools::safeOutput($theme); } // Set the path of selected theme if ($this->theme_selected) define('_PS_THEME_SELECTED_DIR_', _PS_ROOT_DIR_.'/themes/'.$this->theme_selected.'/'); else define('_PS_THEME_SELECTED_DIR_', ''); // Get type of translation if (($type = Tools::getValue('type')) && !is_array($type)) $this->type_selected = strtolower(Tools::safeOutput($type)); // Get selected language if (Tools::getValue('lang') || Tools::getValue('iso_code')) { $iso_code = Tools::getValue('lang') ? Tools::getValue('lang') : Tools::getValue('iso_code'); if (!Validate::isLangIsoCode($iso_code) || !in_array($iso_code, $this->all_iso_lang)) throw new PrestaShopException(sprintf(Tools::displayError('Invalid iso code "%s"'), Tools::safeOutput($iso_code))); $this->lang_selected = new Language((int)Language::getIdByIso($iso_code)); } else $this->lang_selected = new Language((int)Language::getIdByIso('en')); // Get all information for translations $this->getTranslationsInformations(); } public function renderKpis() { $time = time(); $kpis = array(); /* The data generation is located in AdminStatsControllerCore */ $helper = new HelperKpi(); $helper->id = 'box-languages'; $helper->icon = 'icon-microphone'; $helper->color = 'color1'; $helper->href = $this->context->link->getAdminLink('AdminLanguages'); $helper->title = $this->l('Enabled Languages', null, null, false); if (ConfigurationKPI::get('ENABLED_LANGUAGES') !== false) $helper->value = ConfigurationKPI::get('ENABLED_LANGUAGES'); if (ConfigurationKPI::get('ENABLED_LANGUAGES_EXPIRE') < $time) $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=enabled_languages'; $kpis[] = $helper->generate(); $helper = new HelperKpi(); $helper->id = 'box-country'; $helper->icon = 'icon-home'; $helper->color = 'color2'; $helper->title = $this->l('Main Country', null, null, false); $helper->subtitle = $this->l('30 Days', null, null, false); if (ConfigurationKPI::get('MAIN_COUNTRY', $this->context->language->id) !== false) $helper->value = ConfigurationKPI::get('MAIN_COUNTRY', $this->context->language->id); if (ConfigurationKPI::get('MAIN_COUNTRY_EXPIRE', $this->context->language->id) < $time) $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=main_country'; $kpis[] = $helper->generate(); $helper = new HelperKpi(); $helper->id = 'box-translations'; $helper->icon = 'icon-list'; $helper->color = 'color3'; $helper->title = $this->l('Front Office Translations', null, null, false); if (ConfigurationKPI::get('FRONTOFFICE_TRANSLATIONS') !== false) $helper->value = ConfigurationKPI::get('FRONTOFFICE_TRANSLATIONS'); if (ConfigurationKPI::get('FRONTOFFICE_TRANSLATIONS_EXPIRE') < $time) $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=frontoffice_translations'; $kpis[] = $helper->generate(); $helper = new HelperKpiRow(); $helper->kpis = $kpis; return $helper->generate(); } /** * AdminController::postProcess() override * @see AdminController::postProcess() */ public function postProcess() { $this->getInformations(); /* PrestaShop demo mode */ if (_PS_MODE_DEMO_) { $this->errors[] = Tools::displayError('This functionality has been disabled.'); return; } /* PrestaShop demo mode */ try { if (Tools::isSubmit('submitCopyLang')) { if ($this->tabAccess['add'] === '1') $this->submitCopyLang(); else $this->errors[] = Tools::displayError('You do not have permission to add this.'); } elseif (Tools::isSubmit('submitExport')) { if ($this->tabAccess['add'] === '1') $this->submitExportLang(); else $this->errors[] = Tools::displayError('You do not have permission to add this.'); } elseif (Tools::isSubmit('submitImport')) { if ($this->tabAccess['add'] === '1') $this->submitImportLang(); else $this->errors[] = Tools::displayError('You do not have permission to add this.'); } elseif (Tools::isSubmit('submitAddLanguage')) { if ($this->tabAccess['add'] === '1') $this->submitAddLang(); else $this->errors[] = Tools::displayError('You do not have permission to add this.'); } elseif (Tools::isSubmit('submitTranslationsPdf')) { if ($this->tabAccess['edit'] === '1') // Only the PrestaShop team should write the translations into the _PS_TRANSLATIONS_DIR_ if (!$this->theme_selected) $this->writeTranslationFile(); else $this->writeTranslationFile(true); else $this->errors[] = Tools::displayError('You do not have permission to edit this.'); } elseif (Tools::isSubmit('submitTranslationsBack') || Tools::isSubmit('submitTranslationsErrors') || Tools::isSubmit('submitTranslationsFields') || Tools::isSubmit('submitTranslationsFront')) { if ($this->tabAccess['edit'] === '1') $this->writeTranslationFile(); else $this->errors[] = Tools::displayError('You do not have permission to edit this.'); } elseif (Tools::isSubmit('submitTranslationsMails') || Tools::isSubmit('submitTranslationsMailsAndStay')) { if ($this->tabAccess['edit'] === '1') $this->submitTranslationsMails(); else $this->errors[] = Tools::displayError('You do not have permission to edit this.'); } elseif (Tools::isSubmit('submitTranslationsModules')) { if ($this->tabAccess['edit'] === '1') { // Get list of modules if ($modules = $this->getListModules()) { // Get files of all modules $arr_files = $this->getAllModuleFiles($modules, null, $this->lang_selected->iso_code, true); // Find and write all translation modules files foreach ($arr_files as $value) $this->findAndWriteTranslationsIntoFile($value['file_name'], $value['files'], $value['theme'], $value['module'], $value['dir']); // Clear modules cache Tools::clearCache(); // Redirect if (Tools::getIsset('submitTranslationsModulesAndStay')) $this->redirect(true); else $this->redirect(); } } else $this->errors[] = Tools::displayError('You do not have permission to edit this.'); } } catch (PrestaShopException $e) { $this->errors[] = $e->getMessage(); } } /** * This method redirect in the translation main page or in the translation page * * @param bool $save_and_stay : true if the user has clicked on the button "save and stay" * @param bool $conf : id of confirmation message */ protected function redirect($save_and_stay = false, $conf = false) { $conf = !$conf ? 4 : $conf; $url_base = self::$currentIndex.'&token='.$this->token.'&conf='.$conf; if ($save_and_stay) Tools::redirectAdmin($url_base.'&lang='.$this->lang_selected->iso_code.'&type='.$this->type_selected.'&theme='.$this->theme_selected); else Tools::redirectAdmin($url_base); } protected function getMailPattern() { // Let the indentation like it. return '
'.$subject_mail.'
'; $str_return .= '
'.$this->l('There was a problem getting the mail files.').'
'.sprintf($this->l('English language files must exist in %s folder'), ''.preg_replace('@/[a-z]{2}(/?)$@', '/en$1', $mails['directory']).'').'
'.(isset($title['en']) ? $title['en'] : '').'