PageRenderTime 26ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/phpBB/includes/acp/acp_language.php

http://github.com/phpbb/phpbb
PHP | 461 lines | 339 code | 86 blank | 36 comment | 33 complexity | aa9c32a678a2ead1507202ccbec48d49 MD5 | raw file
Possible License(s): GPL-3.0, AGPL-1.0
  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, $phpbb_log, $phpbb_container;
  31. global $phpbb_root_path, $phpEx, $request, $phpbb_dispatcher;
  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->variable('action', '') : $action;
  41. $form_name = 'acp_lang';
  42. add_form_key('acp_lang');
  43. $lang_id = $request->variable('id', 0);
  44. $selected_lang_file = $request->variable('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->variable('lang_english_name', $row['lang_english_name']),
  70. 'lang_local_name' => $request->variable('lang_local_name', $row['lang_local_name'], true),
  71. 'lang_author' => $request->variable('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. $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_LANGUAGE_PACK_UPDATED', false, array($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. $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_LANGUAGE_PACK_DELETED', false, array($row['lang_english_name']));
  188. $delete_message = sprintf($user->lang['LANGUAGE_PACK_DELETED'], $row['lang_english_name']);
  189. $lang_iso = $row['lang_iso'];
  190. /**
  191. * Run code after language deleted
  192. *
  193. * @event core.acp_language_after_delete
  194. * @var string lang_iso Language ISO code
  195. * @var string delete_message Delete message appear to user
  196. * @since 3.2.2-RC1
  197. */
  198. $vars = array('lang_iso', 'delete_message');
  199. extract($phpbb_dispatcher->trigger_event('core.acp_language_after_delete', compact($vars)));
  200. trigger_error($delete_message . adm_back_link($this->u_action));
  201. }
  202. else
  203. {
  204. $s_hidden_fields = array(
  205. 'i' => $id,
  206. 'mode' => $mode,
  207. 'action' => $action,
  208. 'id' => $lang_id,
  209. );
  210. confirm_box(false, $user->lang('DELETE_LANGUAGE_CONFIRM', $row['lang_english_name']), build_hidden_fields($s_hidden_fields));
  211. }
  212. break;
  213. case 'install':
  214. if (!check_link_hash($request->variable('hash', ''), 'acp_language'))
  215. {
  216. trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
  217. }
  218. $lang_iso = $request->variable('iso', '');
  219. $lang_iso = basename($lang_iso);
  220. if (!$lang_iso || !file_exists("{$phpbb_root_path}language/$lang_iso/iso.txt"))
  221. {
  222. trigger_error($user->lang['LANGUAGE_PACK_NOT_EXIST'] . adm_back_link($this->u_action), E_USER_WARNING);
  223. }
  224. $file = file("{$phpbb_root_path}language/$lang_iso/iso.txt");
  225. $lang_pack = array(
  226. 'iso' => $lang_iso,
  227. 'name' => trim(htmlspecialchars($file[0])),
  228. 'local_name'=> trim(htmlspecialchars($file[1], ENT_COMPAT, 'UTF-8')),
  229. 'author' => trim(htmlspecialchars($file[2], ENT_COMPAT, 'UTF-8'))
  230. );
  231. unset($file);
  232. $sql = 'SELECT lang_iso
  233. FROM ' . LANG_TABLE . "
  234. WHERE lang_iso = '" . $db->sql_escape($lang_iso) . "'";
  235. $result = $db->sql_query($sql);
  236. $row = $db->sql_fetchrow($result);
  237. $db->sql_freeresult($result);
  238. if ($row)
  239. {
  240. trigger_error($user->lang['LANGUAGE_PACK_ALREADY_INSTALLED'] . adm_back_link($this->u_action), E_USER_WARNING);
  241. }
  242. if (!$lang_pack['name'] || !$lang_pack['local_name'])
  243. {
  244. trigger_error($user->lang['INVALID_LANGUAGE_PACK'] . adm_back_link($this->u_action), E_USER_WARNING);
  245. }
  246. // Add language pack
  247. $sql_ary = array(
  248. 'lang_iso' => $lang_pack['iso'],
  249. 'lang_dir' => $lang_pack['iso'],
  250. 'lang_english_name' => $lang_pack['name'],
  251. 'lang_local_name' => $lang_pack['local_name'],
  252. 'lang_author' => $lang_pack['author']
  253. );
  254. $db->sql_query('INSERT INTO ' . LANG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
  255. $lang_id = $db->sql_nextid();
  256. // Now let's copy the default language entries for custom profile fields for this new language - makes admin's life easier.
  257. $sql = 'SELECT lang_id
  258. FROM ' . LANG_TABLE . "
  259. WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
  260. $result = $db->sql_query($sql);
  261. $default_lang_id = (int) $db->sql_fetchfield('lang_id');
  262. $db->sql_freeresult($result);
  263. // We want to notify the admin that custom profile fields need to be updated for the new language.
  264. $notify_cpf_update = false;
  265. // From the mysql documentation:
  266. // 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.
  267. // Due to this we stay on the safe side if we do the insertion "the manual way"
  268. $sql = 'SELECT field_id, lang_name, lang_explain, lang_default_value
  269. FROM ' . PROFILE_LANG_TABLE . '
  270. WHERE lang_id = ' . $default_lang_id;
  271. $result = $db->sql_query($sql);
  272. while ($row = $db->sql_fetchrow($result))
  273. {
  274. $row['lang_id'] = $lang_id;
  275. $db->sql_query('INSERT INTO ' . PROFILE_LANG_TABLE . ' ' . $db->sql_build_array('INSERT', $row));
  276. $notify_cpf_update = true;
  277. }
  278. $db->sql_freeresult($result);
  279. $sql = 'SELECT field_id, option_id, field_type, lang_value
  280. FROM ' . PROFILE_FIELDS_LANG_TABLE . '
  281. WHERE lang_id = ' . $default_lang_id;
  282. $result = $db->sql_query($sql);
  283. while ($row = $db->sql_fetchrow($result))
  284. {
  285. $row['lang_id'] = $lang_id;
  286. $db->sql_query('INSERT INTO ' . PROFILE_FIELDS_LANG_TABLE . ' ' . $db->sql_build_array('INSERT', $row));
  287. $notify_cpf_update = true;
  288. }
  289. $db->sql_freeresult($result);
  290. $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_LANGUAGE_PACK_INSTALLED', false, array($lang_pack['name']));
  291. $message = sprintf($user->lang['LANGUAGE_PACK_INSTALLED'], $lang_pack['name']);
  292. $message .= ($notify_cpf_update) ? '<br /><br />' . $user->lang['LANGUAGE_PACK_CPF_UPDATE'] : '';
  293. trigger_error($message . adm_back_link($this->u_action));
  294. break;
  295. }
  296. $sql = 'SELECT user_lang, COUNT(user_lang) AS lang_count
  297. FROM ' . USERS_TABLE . '
  298. GROUP BY user_lang';
  299. $result = $db->sql_query($sql);
  300. $lang_count = array();
  301. while ($row = $db->sql_fetchrow($result))
  302. {
  303. $lang_count[$row['user_lang']] = $row['lang_count'];
  304. }
  305. $db->sql_freeresult($result);
  306. $sql = 'SELECT *
  307. FROM ' . LANG_TABLE . '
  308. ORDER BY lang_english_name';
  309. $result = $db->sql_query($sql);
  310. $installed = array();
  311. while ($row = $db->sql_fetchrow($result))
  312. {
  313. $installed[] = $row['lang_iso'];
  314. $tagstyle = ($row['lang_iso'] == $config['default_lang']) ? '*' : '';
  315. $template->assign_block_vars('lang', array(
  316. 'U_DETAILS' => $this->u_action . "&amp;action=details&amp;id={$row['lang_id']}",
  317. 'U_DOWNLOAD' => $this->u_action . "&amp;action=download&amp;id={$row['lang_id']}",
  318. 'U_DELETE' => $this->u_action . "&amp;action=delete&amp;id={$row['lang_id']}",
  319. 'ENGLISH_NAME' => $row['lang_english_name'],
  320. 'TAG' => $tagstyle,
  321. 'LOCAL_NAME' => $row['lang_local_name'],
  322. 'ISO' => $row['lang_iso'],
  323. 'USED_BY' => (isset($lang_count[$row['lang_iso']])) ? $lang_count[$row['lang_iso']] : 0,
  324. ));
  325. }
  326. $db->sql_freeresult($result);
  327. $new_ary = $iso = array();
  328. /** @var \phpbb\language\language_file_helper $language_helper */
  329. $language_helper = $phpbb_container->get('language.helper.language_file');
  330. $iso = $language_helper->get_available_languages();
  331. foreach ($iso as $lang_array)
  332. {
  333. $lang_iso = $lang_array['iso'];
  334. if (!in_array($lang_iso, $installed))
  335. {
  336. $new_ary[$lang_iso] = $lang_array;
  337. }
  338. }
  339. unset($installed);
  340. if (count($new_ary))
  341. {
  342. foreach ($new_ary as $iso => $lang_ary)
  343. {
  344. $template->assign_block_vars('notinst', array(
  345. 'ISO' => htmlspecialchars($lang_ary['iso']),
  346. 'LOCAL_NAME' => htmlspecialchars($lang_ary['local_name'], ENT_COMPAT, 'UTF-8'),
  347. 'NAME' => htmlspecialchars($lang_ary['name'], ENT_COMPAT, 'UTF-8'),
  348. 'U_INSTALL' => $this->u_action . '&amp;action=install&amp;iso=' . urlencode($lang_ary['iso']) . '&amp;hash=' . generate_link_hash('acp_language'))
  349. );
  350. }
  351. }
  352. unset($new_ary);
  353. }
  354. /**
  355. * Compare two language files
  356. */
  357. function compare_language_files($source_lang, $dest_lang, $file)
  358. {
  359. global $phpbb_root_path;
  360. $source_file = $phpbb_root_path . 'language/' . $source_lang . '/' . $file;
  361. $dest_file = $phpbb_root_path . 'language/' . $dest_lang . '/' . $file;
  362. if (!file_exists($dest_file))
  363. {
  364. return array();
  365. }
  366. $lang = array();
  367. include($source_file);
  368. $lang_entry_src = $lang;
  369. $lang = array();
  370. include($dest_file);
  371. $lang_entry_dst = $lang;
  372. unset($lang);
  373. return array_diff(array_keys($lang_entry_src), array_keys($lang_entry_dst));
  374. }
  375. }