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

/phpBB/includes/acp/acp_language.php

https://github.com/VSEphpbb/phpbb
PHP | 466 lines | 355 code | 84 blank | 27 comment | 42 complexity | 59d0c33348d1b9f1c238483c375f9cbc MD5 | raw file
  1. <?php
  2. /**
  3. *
  4. * This file is part of the phpBB Forum Software package.
  5. *
  6. * @copyright (c) phpBB Limited <https://www.phpbb.com>
  7. * @license GNU General Public License, version 2 (GPL-2.0)
  8. *
  9. * For full copyright and license information, please see
  10. * the docs/CREDITS.txt file.
  11. *
  12. */
  13. /**
  14. * @ignore
  15. */
  16. if (!defined('IN_PHPBB'))
  17. {
  18. exit;
  19. }
  20. class acp_language
  21. {
  22. var $u_action;
  23. var $main_files;
  24. var $language_header = '';
  25. var $lang_header = '';
  26. var $language_file = '';
  27. var $language_directory = '';
  28. function main($id, $mode)
  29. {
  30. global $config, $db, $user, $template;
  31. global $phpbb_root_path, $phpEx, $request;
  32. if (!function_exists('validate_language_iso_name'))
  33. {
  34. include($phpbb_root_path . 'includes/functions_user.' . $phpEx);
  35. }
  36. // Check and set some common vars
  37. $action = (isset($_POST['update_details'])) ? 'update_details' : '';
  38. $action = (isset($_POST['remove_store'])) ? 'details' : $action;
  39. $submit = (empty($action) && !isset($_POST['update']) && !isset($_POST['test_connection'])) ? false : true;
  40. $action = (empty($action)) ? request_var('action', '') : $action;
  41. $form_name = 'acp_lang';
  42. add_form_key('acp_lang');
  43. $lang_id = request_var('id', 0);
  44. $selected_lang_file = request_var('language_file', '|common.' . $phpEx);
  45. list($this->language_directory, $this->language_file) = explode('|', $selected_lang_file);
  46. $this->language_directory = basename($this->language_directory);
  47. $this->language_file = basename($this->language_file);
  48. $user->add_lang('acp/language');
  49. $this->tpl_name = 'acp_language';
  50. $this->page_title = 'ACP_LANGUAGE_PACKS';
  51. switch ($action)
  52. {
  53. case 'update_details':
  54. if (!$submit || !check_form_key($form_name))
  55. {
  56. trigger_error($user->lang['FORM_INVALID']. adm_back_link($this->u_action), E_USER_WARNING);
  57. }
  58. if (!$lang_id)
  59. {
  60. trigger_error($user->lang['NO_LANG_ID'] . adm_back_link($this->u_action), E_USER_WARNING);
  61. }
  62. $sql = 'SELECT *
  63. FROM ' . LANG_TABLE . "
  64. WHERE lang_id = $lang_id";
  65. $result = $db->sql_query($sql);
  66. $row = $db->sql_fetchrow($result);
  67. $db->sql_freeresult($result);
  68. $sql_ary = array(
  69. 'lang_english_name' => request_var('lang_english_name', $row['lang_english_name']),
  70. 'lang_local_name' => utf8_normalize_nfc(request_var('lang_local_name', $row['lang_local_name'], true)),
  71. 'lang_author' => utf8_normalize_nfc(request_var('lang_author', $row['lang_author'], true)),
  72. );
  73. $db->sql_query('UPDATE ' . LANG_TABLE . '
  74. SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
  75. WHERE lang_id = ' . $lang_id);
  76. add_log('admin', 'LOG_LANGUAGE_PACK_UPDATED', $sql_ary['lang_english_name']);
  77. trigger_error($user->lang['LANGUAGE_DETAILS_UPDATED'] . adm_back_link($this->u_action));
  78. break;
  79. case 'details':
  80. if (!$lang_id)
  81. {
  82. trigger_error($user->lang['NO_LANG_ID'] . adm_back_link($this->u_action), E_USER_WARNING);
  83. }
  84. $this->page_title = 'LANGUAGE_PACK_DETAILS';
  85. $sql = 'SELECT *
  86. FROM ' . LANG_TABLE . '
  87. WHERE lang_id = ' . $lang_id;
  88. $result = $db->sql_query($sql);
  89. $lang_entries = $db->sql_fetchrow($result);
  90. $db->sql_freeresult($result);
  91. if (!$lang_entries)
  92. {
  93. trigger_error($user->lang['LANGUAGE_PACK_NOT_EXIST'] . adm_back_link($this->u_action), E_USER_WARNING);
  94. }
  95. $lang_iso = $lang_entries['lang_iso'];
  96. $template->assign_vars(array(
  97. 'S_DETAILS' => true,
  98. 'U_ACTION' => $this->u_action . "&amp;action=details&amp;id=$lang_id",
  99. 'U_BACK' => $this->u_action,
  100. 'LANG_LOCAL_NAME' => $lang_entries['lang_local_name'],
  101. 'LANG_ENGLISH_NAME' => $lang_entries['lang_english_name'],
  102. 'LANG_ISO' => $lang_iso,
  103. 'LANG_AUTHOR' => $lang_entries['lang_author'],
  104. 'L_MISSING_FILES' => $user->lang('THOSE_MISSING_LANG_FILES', $lang_entries['lang_local_name']),
  105. 'L_MISSING_VARS_EXPLAIN' => $user->lang('THOSE_MISSING_LANG_VARIABLES', $lang_entries['lang_local_name']),
  106. ));
  107. // If current lang is different from the default lang, then highlight missing files and variables
  108. if ($lang_iso != $config['default_lang'])
  109. {
  110. try
  111. {
  112. $iterator = new \RecursiveIteratorIterator(
  113. new \phpbb\recursive_dot_prefix_filter_iterator(
  114. new \RecursiveDirectoryIterator(
  115. $phpbb_root_path . 'language/' . $config['default_lang'] . '/',
  116. \FilesystemIterator::SKIP_DOTS
  117. )
  118. ),
  119. \RecursiveIteratorIterator::LEAVES_ONLY
  120. );
  121. }
  122. catch (\Exception $e)
  123. {
  124. return array();
  125. }
  126. foreach ($iterator as $file_info)
  127. {
  128. /** @var \RecursiveDirectoryIterator $file_info */
  129. $relative_path = $iterator->getInnerIterator()->getSubPathname();
  130. $relative_path = str_replace(DIRECTORY_SEPARATOR, '/', $relative_path);
  131. if (file_exists($phpbb_root_path . 'language/' . $lang_iso . '/' . $relative_path))
  132. {
  133. if (substr($relative_path, 0 - strlen($phpEx)) === $phpEx)
  134. {
  135. $missing_vars = $this->compare_language_files($config['default_lang'], $lang_iso, $relative_path);
  136. if (!empty($missing_vars))
  137. {
  138. $template->assign_block_vars('missing_varfile', array(
  139. 'FILE_NAME' => $relative_path,
  140. ));
  141. foreach ($missing_vars as $var)
  142. {
  143. $template->assign_block_vars('missing_varfile.variable', array(
  144. 'VAR_NAME' => $var,
  145. ));
  146. }
  147. }
  148. }
  149. }
  150. else
  151. {
  152. $template->assign_block_vars('missing_files', array(
  153. 'FILE_NAME' => $relative_path,
  154. ));
  155. }
  156. }
  157. }
  158. return;
  159. break;
  160. case 'delete':
  161. if (!$lang_id)
  162. {
  163. trigger_error($user->lang['NO_LANG_ID'] . adm_back_link($this->u_action), E_USER_WARNING);
  164. }
  165. $sql = 'SELECT *
  166. FROM ' . LANG_TABLE . '
  167. WHERE lang_id = ' . $lang_id;
  168. $result = $db->sql_query($sql);
  169. $row = $db->sql_fetchrow($result);
  170. $db->sql_freeresult($result);
  171. if ($row['lang_iso'] == $config['default_lang'])
  172. {
  173. trigger_error($user->lang['NO_REMOVE_DEFAULT_LANG'] . adm_back_link($this->u_action), E_USER_WARNING);
  174. }
  175. if (confirm_box(true))
  176. {
  177. $db->sql_query('DELETE FROM ' . LANG_TABLE . ' WHERE lang_id = ' . $lang_id);
  178. $sql = 'UPDATE ' . USERS_TABLE . "
  179. SET user_lang = '" . $db->sql_escape($config['default_lang']) . "'
  180. WHERE user_lang = '" . $db->sql_escape($row['lang_iso']) . "'";
  181. $db->sql_query($sql);
  182. // We also need to remove the translated entries for custom profile fields - we want clean tables, don't we?
  183. $sql = 'DELETE FROM ' . PROFILE_LANG_TABLE . ' WHERE lang_id = ' . $lang_id;
  184. $db->sql_query($sql);
  185. $sql = 'DELETE FROM ' . PROFILE_FIELDS_LANG_TABLE . ' WHERE lang_id = ' . $lang_id;
  186. $db->sql_query($sql);
  187. add_log('admin', 'LOG_LANGUAGE_PACK_DELETED', $row['lang_english_name']);
  188. trigger_error(sprintf($user->lang['LANGUAGE_PACK_DELETED'], $row['lang_english_name']) . adm_back_link($this->u_action));
  189. }
  190. else
  191. {
  192. $s_hidden_fields = array(
  193. 'i' => $id,
  194. 'mode' => $mode,
  195. 'action' => $action,
  196. 'id' => $lang_id,
  197. );
  198. confirm_box(false, $user->lang('DELETE_LANGUAGE_CONFIRM', $row['lang_english_name']), build_hidden_fields($s_hidden_fields));
  199. }
  200. break;
  201. case 'install':
  202. if (!check_link_hash($request->variable('hash', ''), 'acp_language'))
  203. {
  204. trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
  205. }
  206. $lang_iso = request_var('iso', '');
  207. $lang_iso = basename($lang_iso);
  208. if (!$lang_iso || !file_exists("{$phpbb_root_path}language/$lang_iso/iso.txt"))
  209. {
  210. trigger_error($user->lang['LANGUAGE_PACK_NOT_EXIST'] . adm_back_link($this->u_action), E_USER_WARNING);
  211. }
  212. $file = file("{$phpbb_root_path}language/$lang_iso/iso.txt");
  213. $lang_pack = array(
  214. 'iso' => $lang_iso,
  215. 'name' => trim(htmlspecialchars($file[0])),
  216. 'local_name'=> trim(htmlspecialchars($file[1], ENT_COMPAT, 'UTF-8')),
  217. 'author' => trim(htmlspecialchars($file[2], ENT_COMPAT, 'UTF-8'))
  218. );
  219. unset($file);
  220. $sql = 'SELECT lang_iso
  221. FROM ' . LANG_TABLE . "
  222. WHERE lang_iso = '" . $db->sql_escape($lang_iso) . "'";
  223. $result = $db->sql_query($sql);
  224. $row = $db->sql_fetchrow($result);
  225. $db->sql_freeresult($result);
  226. if ($row)
  227. {
  228. trigger_error($user->lang['LANGUAGE_PACK_ALREADY_INSTALLED'] . adm_back_link($this->u_action), E_USER_WARNING);
  229. }
  230. if (!$lang_pack['name'] || !$lang_pack['local_name'])
  231. {
  232. trigger_error($user->lang['INVALID_LANGUAGE_PACK'] . adm_back_link($this->u_action), E_USER_WARNING);
  233. }
  234. // Add language pack
  235. $sql_ary = array(
  236. 'lang_iso' => $lang_pack['iso'],
  237. 'lang_dir' => $lang_pack['iso'],
  238. 'lang_english_name' => $lang_pack['name'],
  239. 'lang_local_name' => $lang_pack['local_name'],
  240. 'lang_author' => $lang_pack['author']
  241. );
  242. $db->sql_query('INSERT INTO ' . LANG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
  243. $lang_id = $db->sql_nextid();
  244. // Now let's copy the default language entries for custom profile fields for this new language - makes admin's life easier.
  245. $sql = 'SELECT lang_id
  246. FROM ' . LANG_TABLE . "
  247. WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
  248. $result = $db->sql_query($sql);
  249. $default_lang_id = (int) $db->sql_fetchfield('lang_id');
  250. $db->sql_freeresult($result);
  251. // We want to notify the admin that custom profile fields need to be updated for the new language.
  252. $notify_cpf_update = false;
  253. // From the mysql documentation:
  254. // Prior to MySQL 4.0.14, the target table of the INSERT statement cannot appear in the FROM clause of the SELECT part of the query. This limitation is lifted in 4.0.14.
  255. // Due to this we stay on the safe side if we do the insertion "the manual way"
  256. $sql = 'SELECT field_id, lang_name, lang_explain, lang_default_value
  257. FROM ' . PROFILE_LANG_TABLE . '
  258. WHERE lang_id = ' . $default_lang_id;
  259. $result = $db->sql_query($sql);
  260. while ($row = $db->sql_fetchrow($result))
  261. {
  262. $row['lang_id'] = $lang_id;
  263. $db->sql_query('INSERT INTO ' . PROFILE_LANG_TABLE . ' ' . $db->sql_build_array('INSERT', $row));
  264. $notify_cpf_update = true;
  265. }
  266. $db->sql_freeresult($result);
  267. $sql = 'SELECT field_id, option_id, field_type, lang_value
  268. FROM ' . PROFILE_FIELDS_LANG_TABLE . '
  269. WHERE lang_id = ' . $default_lang_id;
  270. $result = $db->sql_query($sql);
  271. while ($row = $db->sql_fetchrow($result))
  272. {
  273. $row['lang_id'] = $lang_id;
  274. $db->sql_query('INSERT INTO ' . PROFILE_FIELDS_LANG_TABLE . ' ' . $db->sql_build_array('INSERT', $row));
  275. $notify_cpf_update = true;
  276. }
  277. $db->sql_freeresult($result);
  278. add_log('admin', 'LOG_LANGUAGE_PACK_INSTALLED', $lang_pack['name']);
  279. $message = sprintf($user->lang['LANGUAGE_PACK_INSTALLED'], $lang_pack['name']);
  280. $message .= ($notify_cpf_update) ? '<br /><br />' . $user->lang['LANGUAGE_PACK_CPF_UPDATE'] : '';
  281. trigger_error($message . adm_back_link($this->u_action));
  282. break;
  283. }
  284. $sql = 'SELECT user_lang, COUNT(user_lang) AS lang_count
  285. FROM ' . USERS_TABLE . '
  286. GROUP BY user_lang';
  287. $result = $db->sql_query($sql);
  288. $lang_count = array();
  289. while ($row = $db->sql_fetchrow($result))
  290. {
  291. $lang_count[$row['user_lang']] = $row['lang_count'];
  292. }
  293. $db->sql_freeresult($result);
  294. $sql = 'SELECT *
  295. FROM ' . LANG_TABLE . '
  296. ORDER BY lang_english_name';
  297. $result = $db->sql_query($sql);
  298. $installed = array();
  299. while ($row = $db->sql_fetchrow($result))
  300. {
  301. $installed[] = $row['lang_iso'];
  302. $tagstyle = ($row['lang_iso'] == $config['default_lang']) ? '*' : '';
  303. $template->assign_block_vars('lang', array(
  304. 'U_DETAILS' => $this->u_action . "&amp;action=details&amp;id={$row['lang_id']}",
  305. 'U_DOWNLOAD' => $this->u_action . "&amp;action=download&amp;id={$row['lang_id']}",
  306. 'U_DELETE' => $this->u_action . "&amp;action=delete&amp;id={$row['lang_id']}",
  307. 'ENGLISH_NAME' => $row['lang_english_name'],
  308. 'TAG' => $tagstyle,
  309. 'LOCAL_NAME' => $row['lang_local_name'],
  310. 'ISO' => $row['lang_iso'],
  311. 'USED_BY' => (isset($lang_count[$row['lang_iso']])) ? $lang_count[$row['lang_iso']] : 0,
  312. ));
  313. }
  314. $db->sql_freeresult($result);
  315. $new_ary = $iso = array();
  316. $dp = @opendir("{$phpbb_root_path}language");
  317. if ($dp)
  318. {
  319. while (($file = readdir($dp)) !== false)
  320. {
  321. if ($file[0] == '.' || !is_dir($phpbb_root_path . 'language/' . $file))
  322. {
  323. continue;
  324. }
  325. if (file_exists("{$phpbb_root_path}language/$file/iso.txt"))
  326. {
  327. if (!in_array($file, $installed))
  328. {
  329. if ($iso = file("{$phpbb_root_path}language/$file/iso.txt"))
  330. {
  331. if (sizeof($iso) == 3)
  332. {
  333. $new_ary[$file] = array(
  334. 'iso' => $file,
  335. 'name' => trim($iso[0]),
  336. 'local_name'=> trim($iso[1]),
  337. 'author' => trim($iso[2])
  338. );
  339. }
  340. }
  341. }
  342. }
  343. }
  344. closedir($dp);
  345. }
  346. unset($installed);
  347. if (sizeof($new_ary))
  348. {
  349. foreach ($new_ary as $iso => $lang_ary)
  350. {
  351. $template->assign_block_vars('notinst', array(
  352. 'ISO' => htmlspecialchars($lang_ary['iso']),
  353. 'LOCAL_NAME' => htmlspecialchars($lang_ary['local_name'], ENT_COMPAT, 'UTF-8'),
  354. 'NAME' => htmlspecialchars($lang_ary['name'], ENT_COMPAT, 'UTF-8'),
  355. 'U_INSTALL' => $this->u_action . '&amp;action=install&amp;iso=' . urlencode($lang_ary['iso']) . '&amp;hash=' . generate_link_hash('acp_language'))
  356. );
  357. }
  358. }
  359. unset($new_ary);
  360. }
  361. /**
  362. * Compare two language files
  363. */
  364. function compare_language_files($source_lang, $dest_lang, $file)
  365. {
  366. global $phpbb_root_path;
  367. $source_file = $phpbb_root_path . 'language/' . $source_lang . '/' . $file;
  368. $dest_file = $phpbb_root_path . 'language/' . $dest_lang . '/' . $file;
  369. if (!file_exists($dest_file))
  370. {
  371. return array();
  372. }
  373. $lang = array();
  374. include($source_file);
  375. $lang_entry_src = $lang;
  376. $lang = array();
  377. include($dest_file);
  378. $lang_entry_dst = $lang;
  379. unset($lang);
  380. return array_diff(array_keys($lang_entry_src), array_keys($lang_entry_dst));
  381. }
  382. }