Mercurial > hg > vamp-website
comparison forum/Sources/ManageServer.php @ 76:e3e11437ecea website
Add forum code
author | Chris Cannam |
---|---|
date | Sun, 07 Jul 2013 11:25:48 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
75:72f59aa7e503 | 76:e3e11437ecea |
---|---|
1 <?php | |
2 | |
3 /** | |
4 * Simple Machines Forum (SMF) | |
5 * | |
6 * @package SMF | |
7 * @author Simple Machines http://www.simplemachines.org | |
8 * @copyright 2011 Simple Machines | |
9 * @license http://www.simplemachines.org/about/smf/license.php BSD | |
10 * | |
11 * @version 2.0.4 | |
12 */ | |
13 | |
14 if (!defined('SMF')) | |
15 die('Hacking attempt...'); | |
16 | |
17 /* This file contains all the functionality required to be able to edit the | |
18 core server settings. This includes anything from which an error may result | |
19 in the forum destroying itself in a firey fury. | |
20 | |
21 void ModifySettings() | |
22 - Sets up all the available sub-actions. | |
23 - Requires the admin_forum permission. | |
24 - Uses the edit_settings adminIndex. | |
25 - Sets up all the tabs and selects the appropriate one based on the sub-action. | |
26 - Redirects to the appropriate function based on the sub-action. | |
27 | |
28 void ModifyGeneralSettings() | |
29 - shows an interface for the settings in Settings.php to be changed. | |
30 - uses the rawdata sub template (not theme-able.) | |
31 - requires the admin_forum permission. | |
32 - uses the edit_settings administration area. | |
33 - contains the actual array of settings to show from Settings.php. | |
34 - accessed from ?action=admin;area=serversettings;sa=general. | |
35 | |
36 void ModifyDatabaseSettings() | |
37 - shows an interface for the settings in Settings.php to be changed. | |
38 - uses the rawdata sub template (not theme-able.) | |
39 - requires the admin_forum permission. | |
40 - uses the edit_settings administration area. | |
41 - contains the actual array of settings to show from Settings.php. | |
42 - accessed from ?action=admin;area=serversettings;sa=database. | |
43 | |
44 void ModifyCookieSettings() | |
45 // !!! | |
46 | |
47 void ModifyCacheSettings() | |
48 // !!! | |
49 | |
50 void ModifyLoadBalancingSettings() | |
51 // !!! | |
52 | |
53 void AddLanguage() | |
54 // !!! | |
55 | |
56 void DownloadLanguage() | |
57 - Uses the ManageSettings template and the download_language sub-template. | |
58 - Requires a valid download ID ("did") in the URL. | |
59 - Also handles installing language files. | |
60 - Attempts to chmod things as needed. | |
61 - Uses a standard list to display information about all the files and where they'll be put. | |
62 | |
63 void ManageLanguages() | |
64 // !!! | |
65 | |
66 void ModifyLanguages() | |
67 // !!! | |
68 | |
69 int list_getNumLanguages() | |
70 // !!! | |
71 | |
72 array list_getLanguages() | |
73 - Callback for $listOptions['get_items']['function'] in ManageLanguageSettings. | |
74 - Determines which languages are available by looking for the "index.{language}.php" file. | |
75 - Also figures out how many users are using a particular language. | |
76 | |
77 void ModifyLanguageSettings() | |
78 // !!! | |
79 | |
80 void ModifyLanguage() | |
81 // !!! | |
82 | |
83 void prepareServerSettingsContext(array config_vars) | |
84 // !!! | |
85 | |
86 void prepareDBSettingContext(array config_vars) | |
87 // !!! | |
88 | |
89 void saveSettings(array config_vars) | |
90 - saves those settings set from ?action=admin;area=serversettings to the | |
91 Settings.php file and the database. | |
92 - requires the admin_forum permission. | |
93 - contains arrays of the types of data to save into Settings.php. | |
94 | |
95 void saveDBSettings(array config_vars) | |
96 // !!! | |
97 | |
98 */ | |
99 | |
100 /* Adding options to one of the setting screens isn't hard. Call prepareDBSettingsContext; | |
101 The basic format for a checkbox is: | |
102 array('check', 'nameInModSettingsAndSQL'), | |
103 | |
104 And for a text box: | |
105 array('text', 'nameInModSettingsAndSQL') | |
106 (NOTE: You have to add an entry for this at the bottom!) | |
107 | |
108 In these cases, it will look for $txt['nameInModSettingsAndSQL'] as the description, | |
109 and $helptxt['nameInModSettingsAndSQL'] as the help popup description. | |
110 | |
111 Here's a quick explanation of how to add a new item: | |
112 | |
113 * A text input box. For textual values. | |
114 ie. array('text', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'), | |
115 | |
116 * A text input box. For numerical values. | |
117 ie. array('int', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'), | |
118 | |
119 * A text input box. For floating point values. | |
120 ie. array('float', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'), | |
121 | |
122 * A large text input box. Used for textual values spanning multiple lines. | |
123 ie. array('large_text', 'nameInModSettingsAndSQL', 'OptionalNumberOfRows'), | |
124 | |
125 * A check box. Either one or zero. (boolean) | |
126 ie. array('check', 'nameInModSettingsAndSQL'), | |
127 | |
128 * A selection box. Used for the selection of something from a list. | |
129 ie. array('select', 'nameInModSettingsAndSQL', array('valueForSQL' => $txt['displayedValue'])), | |
130 Note that just saying array('first', 'second') will put 0 in the SQL for 'first'. | |
131 | |
132 * A password input box. Used for passwords, no less! | |
133 ie. array('password', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'), | |
134 | |
135 * A permission - for picking groups who have a permission. | |
136 ie. array('permissions', 'manage_groups'), | |
137 | |
138 * A BBC selection box. | |
139 ie. array('bbc', 'sig_bbc'), | |
140 | |
141 For each option: | |
142 type (see above), variable name, size/possible values. | |
143 OR make type '' for an empty string for a horizontal rule. | |
144 SET preinput - to put some HTML prior to the input box. | |
145 SET postinput - to put some HTML following the input box. | |
146 SET invalid - to mark the data as invalid. | |
147 PLUS You can override label and help parameters by forcing their keys in the array, for example: | |
148 array('text', 'invalidlabel', 3, 'label' => 'Actual Label') */ | |
149 | |
150 // This is the main pass through function, it creates tabs and the like. | |
151 function ModifySettings() | |
152 { | |
153 global $context, $txt, $scripturl, $boarddir; | |
154 | |
155 // This is just to keep the database password more secure. | |
156 isAllowedTo('admin_forum'); | |
157 | |
158 // Load up all the tabs... | |
159 $context[$context['admin_menu_name']]['tab_data'] = array( | |
160 'title' => $txt['admin_server_settings'], | |
161 'help' => 'serversettings', | |
162 'description' => $txt['admin_basic_settings'], | |
163 ); | |
164 | |
165 checkSession('request'); | |
166 | |
167 // The settings are in here, I swear! | |
168 loadLanguage('ManageSettings'); | |
169 | |
170 $context['page_title'] = $txt['admin_server_settings']; | |
171 $context['sub_template'] = 'show_settings'; | |
172 | |
173 $subActions = array( | |
174 'general' => 'ModifyGeneralSettings', | |
175 'database' => 'ModifyDatabaseSettings', | |
176 'cookie' => 'ModifyCookieSettings', | |
177 'cache' => 'ModifyCacheSettings', | |
178 'loads' => 'ModifyLoadBalancingSettings', | |
179 ); | |
180 | |
181 // By default we're editing the core settings | |
182 $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'general'; | |
183 $context['sub_action'] = $_REQUEST['sa']; | |
184 | |
185 // Warn the user if there's any relevant information regarding Settings.php. | |
186 if ($_REQUEST['sa'] != 'cache') | |
187 { | |
188 // Warn the user if the backup of Settings.php failed. | |
189 $settings_not_writable = !is_writable($boarddir . '/Settings.php'); | |
190 $settings_backup_fail = !@is_writable($boarddir . '/Settings_bak.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php'); | |
191 | |
192 if ($settings_not_writable) | |
193 $context['settings_message'] = '<div class="centertext"><strong>' . $txt['settings_not_writable'] . '</strong></div><br />'; | |
194 elseif ($settings_backup_fail) | |
195 $context['settings_message'] = '<div class="centertext"><strong>' . $txt['admin_backup_fail'] . '</strong></div><br />'; | |
196 | |
197 $context['settings_not_writable'] = $settings_not_writable; | |
198 } | |
199 | |
200 // Call the right function for this sub-action. | |
201 $subActions[$_REQUEST['sa']](); | |
202 } | |
203 | |
204 // General forum settings - forum name, maintenance mode, etc. | |
205 function ModifyGeneralSettings($return_config = false) | |
206 { | |
207 global $scripturl, $context, $txt; | |
208 | |
209 /* If you're writing a mod, it's a bad idea to add things here.... | |
210 For each option: | |
211 variable name, description, type (constant), size/possible values, helptext. | |
212 OR an empty string for a horizontal rule. | |
213 OR a string for a titled section. */ | |
214 $config_vars = array( | |
215 array('mbname', $txt['admin_title'], 'file', 'text', 30), | |
216 '', | |
217 array('maintenance', $txt['admin_maintain'], 'file', 'check'), | |
218 array('mtitle', $txt['maintenance_subject'], 'file', 'text', 36), | |
219 array('mmessage', $txt['maintenance_message'], 'file', 'text', 36), | |
220 '', | |
221 array('webmaster_email', $txt['admin_webmaster_email'], 'file', 'text', 30), | |
222 '', | |
223 array('enableCompressedOutput', $txt['enableCompressedOutput'], 'db', 'check', null, 'enableCompressedOutput'), | |
224 array('disableTemplateEval', $txt['disableTemplateEval'], 'db', 'check', null, 'disableTemplateEval'), | |
225 array('disableHostnameLookup', $txt['disableHostnameLookup'], 'db', 'check', null, 'disableHostnameLookup'), | |
226 ); | |
227 | |
228 if ($return_config) | |
229 return $config_vars; | |
230 | |
231 // Setup the template stuff. | |
232 $context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=general;save'; | |
233 $context['settings_title'] = $txt['general_settings']; | |
234 | |
235 // Saving settings? | |
236 if (isset($_REQUEST['save'])) | |
237 { | |
238 saveSettings($config_vars); | |
239 redirectexit('action=admin;area=serversettings;sa=general;' . $context['session_var'] . '=' . $context['session_id']); | |
240 } | |
241 | |
242 // Fill the config array. | |
243 prepareServerSettingsContext($config_vars); | |
244 } | |
245 | |
246 // Basic database and paths settings - database name, host, etc. | |
247 function ModifyDatabaseSettings($return_config = false) | |
248 { | |
249 global $scripturl, $context, $settings, $txt, $boarddir; | |
250 | |
251 /* If you're writing a mod, it's a bad idea to add things here.... | |
252 For each option: | |
253 variable name, description, type (constant), size/possible values, helptext. | |
254 OR an empty string for a horizontal rule. | |
255 OR a string for a titled section. */ | |
256 $config_vars = array( | |
257 array('db_server', $txt['database_server'], 'file', 'text'), | |
258 array('db_user', $txt['database_user'], 'file', 'text'), | |
259 array('db_passwd', $txt['database_password'], 'file', 'password'), | |
260 array('db_name', $txt['database_name'], 'file', 'text'), | |
261 array('db_prefix', $txt['database_prefix'], 'file', 'text'), | |
262 array('db_persist', $txt['db_persist'], 'file', 'check', null, 'db_persist'), | |
263 array('db_error_send', $txt['db_error_send'], 'file', 'check'), | |
264 array('ssi_db_user', $txt['ssi_db_user'], 'file', 'text', null, 'ssi_db_user'), | |
265 array('ssi_db_passwd', $txt['ssi_db_passwd'], 'file', 'password'), | |
266 '', | |
267 array('autoFixDatabase', $txt['autoFixDatabase'], 'db', 'check', false, 'autoFixDatabase'), | |
268 array('autoOptMaxOnline', $txt['autoOptMaxOnline'], 'db', 'int'), | |
269 '', | |
270 array('boardurl', $txt['admin_url'], 'file', 'text', 36), | |
271 array('boarddir', $txt['boarddir'], 'file', 'text', 36), | |
272 array('sourcedir', $txt['sourcesdir'], 'file', 'text', 36), | |
273 array('cachedir', $txt['cachedir'], 'file', 'text', 36), | |
274 ); | |
275 | |
276 if ($return_config) | |
277 return $config_vars; | |
278 | |
279 // Setup the template stuff. | |
280 $context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=database;save'; | |
281 $context['settings_title'] = $txt['database_paths_settings']; | |
282 $context['save_disabled'] = $context['settings_not_writable']; | |
283 | |
284 // Saving settings? | |
285 if (isset($_REQUEST['save'])) | |
286 { | |
287 saveSettings($config_vars); | |
288 redirectexit('action=admin;area=serversettings;sa=database;' . $context['session_var'] . '=' . $context['session_id']); | |
289 } | |
290 | |
291 // Fill the config array. | |
292 prepareServerSettingsContext($config_vars); | |
293 } | |
294 | |
295 // This function basically edits anything which is configuration and stored in the database, except for caching. | |
296 function ModifyCookieSettings($return_config = false) | |
297 { | |
298 global $context, $scripturl, $txt, $sourcedir, $modSettings, $cookiename, $user_settings; | |
299 | |
300 // Define the variables we want to edit. | |
301 $config_vars = array( | |
302 // Cookies... | |
303 array('cookiename', $txt['cookie_name'], 'file', 'text', 20), | |
304 array('cookieTime', $txt['cookieTime'], 'db', 'int'), | |
305 array('localCookies', $txt['localCookies'], 'db', 'check', false, 'localCookies'), | |
306 array('globalCookies', $txt['globalCookies'], 'db', 'check', false, 'globalCookies'), | |
307 array('secureCookies', $txt['secureCookies'], 'db', 'check', false, 'secureCookies', 'disabled' => !isset($_SERVER['HTTPS']) || !(strtolower($_SERVER['HTTPS']) == 'on' || strtolower($_SERVER['HTTPS']) == '1')), | |
308 '', | |
309 // Sessions | |
310 array('databaseSession_enable', $txt['databaseSession_enable'], 'db', 'check', false, 'databaseSession_enable'), | |
311 array('databaseSession_loose', $txt['databaseSession_loose'], 'db', 'check', false, 'databaseSession_loose'), | |
312 array('databaseSession_lifetime', $txt['databaseSession_lifetime'], 'db', 'int', false, 'databaseSession_lifetime'), | |
313 ); | |
314 | |
315 if ($return_config) | |
316 return $config_vars; | |
317 | |
318 $context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cookie;save'; | |
319 $context['settings_title'] = $txt['cookies_sessions_settings']; | |
320 | |
321 // Saving settings? | |
322 if (isset($_REQUEST['save'])) | |
323 { | |
324 saveSettings($config_vars); | |
325 | |
326 // If the cookie name was changed, reset the cookie. | |
327 if ($cookiename != $_POST['cookiename']) | |
328 { | |
329 $original_session_id = $context['session_id']; | |
330 include_once($sourcedir . '/Subs-Auth.php'); | |
331 | |
332 // Remove the old cookie. | |
333 setLoginCookie(-3600, 0); | |
334 | |
335 // Set the new one. | |
336 $cookiename = $_POST['cookiename']; | |
337 setLoginCookie(60 * $modSettings['cookieTime'], $user_settings['id_member'], sha1($user_settings['passwd'] . $user_settings['password_salt'])); | |
338 | |
339 redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $original_session_id, $context['server']['needs_login_fix']); | |
340 } | |
341 | |
342 redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $context['session_id']); | |
343 } | |
344 | |
345 // Fill the config array. | |
346 prepareServerSettingsContext($config_vars); | |
347 } | |
348 | |
349 // Simply modifying cache functions | |
350 function ModifyCacheSettings($return_config = false) | |
351 { | |
352 global $context, $scripturl, $txt, $helptxt, $modSettings; | |
353 | |
354 // Define the variables we want to edit. | |
355 $config_vars = array( | |
356 // Only a couple of settings, but they are important | |
357 array('select', 'cache_enable', array($txt['cache_off'], $txt['cache_level1'], $txt['cache_level2'], $txt['cache_level3'])), | |
358 array('text', 'cache_memcached'), | |
359 ); | |
360 | |
361 if ($return_config) | |
362 return $config_vars; | |
363 | |
364 // Saving again? | |
365 if (isset($_GET['save'])) | |
366 { | |
367 saveDBSettings($config_vars); | |
368 | |
369 // We have to manually force the clearing of the cache otherwise the changed settings might not get noticed. | |
370 $modSettings['cache_enable'] = 1; | |
371 cache_put_data('modSettings', null, 90); | |
372 | |
373 redirectexit('action=admin;area=serversettings;sa=cache;' . $context['session_var'] . '=' . $context['session_id']); | |
374 } | |
375 | |
376 $context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cache;save'; | |
377 $context['settings_title'] = $txt['caching_settings']; | |
378 $context['settings_message'] = $txt['caching_information']; | |
379 | |
380 // Detect an optimizer? | |
381 if (function_exists('eaccelerator_put')) | |
382 $detected = 'eAccelerator'; | |
383 elseif (function_exists('mmcache_put')) | |
384 $detected = 'MMCache'; | |
385 elseif (function_exists('apc_store')) | |
386 $detected = 'APC'; | |
387 elseif (function_exists('output_cache_put')) | |
388 $detected = 'Zend'; | |
389 elseif (function_exists('memcache_set')) | |
390 $detected = 'Memcached'; | |
391 elseif (function_exists('xcache_set')) | |
392 $detected = 'XCache'; | |
393 else | |
394 $detected = 'no_caching'; | |
395 | |
396 $context['settings_message'] = sprintf($context['settings_message'], $txt['detected_' . $detected]); | |
397 | |
398 // Prepare the template. | |
399 prepareDBSettingContext($config_vars); | |
400 } | |
401 | |
402 function ModifyLoadBalancingSettings($return_config = false) | |
403 { | |
404 global $txt, $scripturl, $context, $settings, $modSettings; | |
405 | |
406 // Setup a warning message, but disabled by default. | |
407 $disabled = true; | |
408 $context['settings_message'] = $txt['loadavg_disabled_conf']; | |
409 | |
410 if (strpos(strtolower(PHP_OS), 'win') === 0) | |
411 $context['settings_message'] = $txt['loadavg_disabled_windows']; | |
412 else | |
413 { | |
414 $modSettings['load_average'] = @file_get_contents('/proc/loadavg'); | |
415 if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) !== 0) | |
416 $modSettings['load_average'] = (float) $matches[1]; | |
417 elseif (($modSettings['load_average'] = @`uptime`) !== null && preg_match('~load averages?: (\d+\.\d+), (\d+\.\d+), (\d+\.\d+)~i', $modSettings['load_average'], $matches) !== 0) | |
418 $modSettings['load_average'] = (float) $matches[1]; | |
419 else | |
420 unset($modSettings['load_average']); | |
421 | |
422 if (!empty($modSettings['load_average'])) | |
423 { | |
424 $context['settings_message'] = sprintf($txt['loadavg_warning'], $modSettings['load_average']); | |
425 $disabled = false; | |
426 } | |
427 } | |
428 | |
429 // Start with a simple checkbox. | |
430 $config_vars = array( | |
431 array('check', 'loadavg_enable'), | |
432 ); | |
433 | |
434 // Set the default values for each option. | |
435 $default_values = array( | |
436 'loadavg_auto_opt' => '1.0', | |
437 'loadavg_search' => '2.5', | |
438 'loadavg_allunread' => '2.0', | |
439 'loadavg_unreadreplies' => '3.5', | |
440 'loadavg_show_posts' => '2.0', | |
441 'loadavg_forum' => '40.0', | |
442 ); | |
443 | |
444 // Loop through the settings. | |
445 foreach ($default_values as $name => $value) | |
446 { | |
447 // Use the default value if the setting isn't set yet. | |
448 $value = !isset($modSettings[$name]) ? $value : $modSettings[$name]; | |
449 $config_vars[] = array('text', $name, 'value' => $value, 'disabled' => $disabled); | |
450 } | |
451 | |
452 if ($return_config) | |
453 return $config_vars; | |
454 | |
455 $context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=loads;save'; | |
456 $context['settings_title'] = $txt['load_balancing_settings']; | |
457 | |
458 // Saving? | |
459 if (isset($_GET['save'])) | |
460 { | |
461 // Stupidity is not allowed. | |
462 foreach ($_POST as $key => $value) | |
463 { | |
464 if (strpos($key, 'loadavg') === 0 || $key === 'loadavg_enable') | |
465 continue; | |
466 elseif ($key == 'loadavg_auto_opt' && $value <= 1) | |
467 $_POST['loadavg_auto_opt'] = '1.0'; | |
468 elseif ($key == 'loadavg_forum' && $value < 10) | |
469 $_POST['loadavg_forum'] = '10.0'; | |
470 elseif ($value < 2) | |
471 $_POST[$key] = '2.0'; | |
472 } | |
473 | |
474 saveDBSettings($config_vars); | |
475 redirectexit('action=admin;area=serversettings;sa=loads;' . $context['session_var'] . '=' . $context['session_id']); | |
476 } | |
477 | |
478 prepareDBSettingContext($config_vars); | |
479 } | |
480 | |
481 // This is the main function for the language area. | |
482 function ManageLanguages() | |
483 { | |
484 global $context, $txt, $scripturl, $modSettings; | |
485 | |
486 loadLanguage('ManageSettings'); | |
487 | |
488 $context['page_title'] = $txt['edit_languages']; | |
489 $context['sub_template'] = 'show_settings'; | |
490 | |
491 $subActions = array( | |
492 'edit' => 'ModifyLanguages', | |
493 'add' => 'AddLanguage', | |
494 'settings' => 'ModifyLanguageSettings', | |
495 'downloadlang' => 'DownloadLanguage', | |
496 'editlang' => 'ModifyLanguage', | |
497 ); | |
498 | |
499 // By default we're managing languages. | |
500 $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'edit'; | |
501 $context['sub_action'] = $_REQUEST['sa']; | |
502 | |
503 // Load up all the tabs... | |
504 $context[$context['admin_menu_name']]['tab_data'] = array( | |
505 'title' => $txt['language_configuration'], | |
506 'description' => $txt['language_description'], | |
507 ); | |
508 | |
509 // Call the right function for this sub-acton. | |
510 $subActions[$_REQUEST['sa']](); | |
511 } | |
512 | |
513 // Interface for adding a new language | |
514 function AddLanguage() | |
515 { | |
516 global $context, $sourcedir, $forum_version, $boarddir, $txt, $smcFunc, $scripturl; | |
517 | |
518 // Are we searching for new languages courtesy of Simple Machines? | |
519 if (!empty($_POST['smf_add_sub'])) | |
520 { | |
521 // Need fetch_web_data. | |
522 require_once($sourcedir . '/Subs-Package.php'); | |
523 | |
524 $context['smf_search_term'] = htmlspecialchars(trim($_POST['smf_add'])); | |
525 | |
526 // We're going to use this URL. | |
527 $url = 'http://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => ''))); | |
528 | |
529 // Load the class file and stick it into an array. | |
530 loadClassFile('Class-Package.php'); | |
531 $language_list = new xmlArray(fetch_web_data($url), true); | |
532 | |
533 // Check it exists. | |
534 if (!$language_list->exists('languages')) | |
535 $context['smf_error'] = 'no_response'; | |
536 else | |
537 { | |
538 $language_list = $language_list->path('languages[0]'); | |
539 $lang_files = $language_list->set('language'); | |
540 $context['smf_languages'] = array(); | |
541 foreach ($lang_files as $file) | |
542 { | |
543 // Were we searching? | |
544 if (!empty($context['smf_search_term']) && strpos($file->fetch('name'), $smcFunc['strtolower']($context['smf_search_term'])) === false) | |
545 continue; | |
546 | |
547 $context['smf_languages'][] = array( | |
548 'id' => $file->fetch('id'), | |
549 'name' => $smcFunc['ucwords']($file->fetch('name')), | |
550 'version' => $file->fetch('version'), | |
551 'utf8' => $file->fetch('utf8'), | |
552 'description' => $file->fetch('description'), | |
553 'link' => $scripturl . '?action=admin;area=languages;sa=downloadlang;did=' . $file->fetch('id') . ';' . $context['session_var'] . '=' . $context['session_id'], | |
554 ); | |
555 } | |
556 if (empty($context['smf_languages'])) | |
557 $context['smf_error'] = 'no_files'; | |
558 } | |
559 } | |
560 | |
561 $context['sub_template'] = 'add_language'; | |
562 } | |
563 | |
564 // Download a language file from the Simple Machines website. | |
565 function DownloadLanguage() | |
566 { | |
567 global $context, $sourcedir, $forum_version, $boarddir, $txt, $smcFunc, $scripturl, $modSettings; | |
568 | |
569 loadLanguage('ManageSettings'); | |
570 require_once($sourcedir . '/Subs-Package.php'); | |
571 | |
572 // Clearly we need to know what to request. | |
573 if (!isset($_GET['did'])) | |
574 fatal_lang_error('no_access', false); | |
575 | |
576 // Some lovely context. | |
577 $context['download_id'] = $_GET['did']; | |
578 $context['sub_template'] = 'download_language'; | |
579 $context['menu_data_' . $context['admin_menu_id']]['current_subsection'] = 'add'; | |
580 | |
581 // Can we actually do the installation - and do they want to? | |
582 if (!empty($_POST['do_install']) && !empty($_POST['copy_file'])) | |
583 { | |
584 checkSession('get'); | |
585 | |
586 $chmod_files = array(); | |
587 $install_files = array(); | |
588 // Check writable status. | |
589 foreach ($_POST['copy_file'] as $file) | |
590 { | |
591 // Check it's not very bad. | |
592 if (strpos($file, '..') !== false || (substr($file, 0, 6) != 'Themes' && !preg_match('~agreement\.[A-Za-z-_0-9]+\.txt$~', $file))) | |
593 fatal_error($txt['languages_download_illegal_paths']); | |
594 | |
595 $chmod_files[] = $boarddir . '/' . $file; | |
596 $install_files[] = $file; | |
597 } | |
598 | |
599 // Call this in case we have work to do. | |
600 $file_status = create_chmod_control($chmod_files); | |
601 $files_left = $file_status['files']['notwritable']; | |
602 | |
603 // Something not writable? | |
604 if (!empty($files_left)) | |
605 $context['error_message'] = $txt['languages_download_not_chmod']; | |
606 // Otherwise, go go go! | |
607 elseif (!empty($install_files)) | |
608 { | |
609 $archive_content = read_tgz_file('http://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => ''))) . ';fetch=' . urlencode($_GET['did']), $boarddir, false, true, $install_files); | |
610 // Make sure the files aren't stuck in the cache. | |
611 package_flush_cache(); | |
612 $context['install_complete'] = sprintf($txt['languages_download_complete_desc'], $scripturl . '?action=admin;area=languages'); | |
613 | |
614 return; | |
615 } | |
616 } | |
617 | |
618 // Open up the old china. | |
619 if (!isset($archive_content)) | |
620 $archive_content = read_tgz_file('http://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => ''))) . ';fetch=' . urlencode($_GET['did']), null); | |
621 | |
622 if (empty($archive_content)) | |
623 fatal_error($txt['add_language_error_no_response']); | |
624 | |
625 // Now for each of the files, let's do some *stuff* | |
626 $context['files'] = array( | |
627 'lang' => array(), | |
628 'other' => array(), | |
629 ); | |
630 $context['make_writable'] = array(); | |
631 foreach ($archive_content as $file) | |
632 { | |
633 $dirname = dirname($file['filename']); | |
634 $filename = basename($file['filename']); | |
635 $extension = substr($filename, strrpos($filename, '.') + 1); | |
636 | |
637 // Don't do anything with files we don't understand. | |
638 if (!in_array($extension, array('php', 'jpg', 'gif', 'jpeg', 'png', 'txt'))) | |
639 continue; | |
640 | |
641 // Basic data. | |
642 $context_data = array( | |
643 'name' => $filename, | |
644 'destination' => $boarddir . '/' . $file['filename'], | |
645 'generaldest' => $file['filename'], | |
646 'size' => $file['size'], | |
647 // Does chmod status allow the copy? | |
648 'writable' => false, | |
649 // Should we suggest they copy this file? | |
650 'default_copy' => true, | |
651 // Does the file already exist, if so is it same or different? | |
652 'exists' => false, | |
653 ); | |
654 | |
655 // Does the file exist, is it different and can we overwrite? | |
656 if (file_exists($boarddir . '/' . $file['filename'])) | |
657 { | |
658 if (is_writable($boarddir . '/' . $file['filename'])) | |
659 $context_data['writable'] = true; | |
660 | |
661 // Finally, do we actually think the content has changed? | |
662 if ($file['size'] == filesize($boarddir . '/' . $file['filename']) && $file['md5'] == md5_file($boarddir . '/' . $file['filename'])) | |
663 { | |
664 $context_data['exists'] = 'same'; | |
665 $context_data['default_copy'] = false; | |
666 } | |
667 // Attempt to discover newline character differences. | |
668 elseif ($file['md5'] == md5(preg_replace("~[\r]?\n~", "\r\n", file_get_contents($boarddir . '/' . $file['filename'])))) | |
669 { | |
670 $context_data['exists'] = 'same'; | |
671 $context_data['default_copy'] = false; | |
672 } | |
673 else | |
674 $context_data['exists'] = 'different'; | |
675 } | |
676 // No overwrite? | |
677 else | |
678 { | |
679 // Can we at least stick it in the directory... | |
680 if (is_writable($boarddir . '/' . $dirname)) | |
681 $context_data['writable'] = true; | |
682 } | |
683 | |
684 // I love PHP files, that's why I'm a developer and not an artistic type spending my time drinking absinth and living a life of sin... | |
685 if ($extension == 'php' && preg_match('~\w+\.\w+(?:-utf8)?\.php~', $filename)) | |
686 { | |
687 $context_data += array( | |
688 'version' => '??', | |
689 'cur_version' => false, | |
690 'version_compare' => 'newer', | |
691 ); | |
692 | |
693 list ($name, $language) = explode('.', $filename); | |
694 | |
695 // Let's get the new version, I like versions, they tell me that I'm up to date. | |
696 if (preg_match('~\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '~i', $file['preview'], $match) == 1) | |
697 $context_data['version'] = $match[1]; | |
698 | |
699 // Now does the old file exist - if so what is it's version? | |
700 if (file_exists($boarddir . '/' . $file['filename'])) | |
701 { | |
702 // OK - what is the current version? | |
703 $fp = fopen($boarddir . '/' . $file['filename'], 'rb'); | |
704 $header = fread($fp, 768); | |
705 fclose($fp); | |
706 | |
707 // Find the version. | |
708 if (preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '(?:[\s]{2}|\*/)~i', $header, $match) == 1) | |
709 { | |
710 $context_data['cur_version'] = $match[1]; | |
711 | |
712 // How does this compare? | |
713 if ($context_data['cur_version'] == $context_data['version']) | |
714 $context_data['version_compare'] = 'same'; | |
715 elseif ($context_data['cur_version'] > $context_data['version']) | |
716 $context_data['version_compare'] = 'older'; | |
717 | |
718 // Don't recommend copying if the version is the same. | |
719 if ($context_data['version_compare'] != 'newer') | |
720 $context_data['default_copy'] = false; | |
721 } | |
722 } | |
723 | |
724 // Add the context data to the main set. | |
725 $context['files']['lang'][] = $context_data; | |
726 } | |
727 else | |
728 { | |
729 // If we think it's a theme thing, work out what the theme is. | |
730 if (substr($dirname, 0, 6) == 'Themes' && preg_match('~Themes[\\/]([^\\/]+)[\\/]~', $dirname, $match)) | |
731 $theme_name = $match[1]; | |
732 else | |
733 $theme_name = 'misc'; | |
734 | |
735 // Assume it's an image, could be an acceptance note etc but rare. | |
736 $context['files']['images'][$theme_name][] = $context_data; | |
737 } | |
738 | |
739 // Collect together all non-writable areas. | |
740 if (!$context_data['writable']) | |
741 $context['make_writable'][] = $context_data['destination']; | |
742 } | |
743 | |
744 // So, I'm a perfectionist - let's get the theme names. | |
745 $theme_indexes = array(); | |
746 foreach ($context['files']['images'] as $k => $dummy) | |
747 $indexes[] = $k; | |
748 | |
749 $context['theme_names'] = array(); | |
750 if (!empty($indexes)) | |
751 { | |
752 $value_data = array( | |
753 'query' => array(), | |
754 'params' => array(), | |
755 ); | |
756 | |
757 foreach ($indexes as $k => $index) | |
758 { | |
759 $value_data['query'][] = 'value LIKE {string:value_' . $k . '}'; | |
760 $value_data['params']['value_' . $k] = '%' . $index; | |
761 } | |
762 | |
763 $request = $smcFunc['db_query']('', ' | |
764 SELECT id_theme, value | |
765 FROM {db_prefix}themes | |
766 WHERE id_member = {int:no_member} | |
767 AND variable = {string:theme_dir} | |
768 AND (' . implode(' OR ', $value_data['query']) . ')', | |
769 array_merge($value_data['params'], array( | |
770 'no_member' => 0, | |
771 'theme_dir' => 'theme_dir', | |
772 'index_compare_explode' => 'value LIKE \'%' . implode('\' OR value LIKE \'%', $indexes) . '\'', | |
773 )) | |
774 ); | |
775 $themes = array(); | |
776 while ($row = $smcFunc['db_fetch_assoc']($request)) | |
777 { | |
778 // Find the right one. | |
779 foreach ($indexes as $index) | |
780 if (strpos($row['value'], $index) !== false) | |
781 $themes[$row['id_theme']] = $index; | |
782 } | |
783 $smcFunc['db_free_result']($request); | |
784 | |
785 if (!empty($themes)) | |
786 { | |
787 // Now we have the id_theme we can get the pretty description. | |
788 $request = $smcFunc['db_query']('', ' | |
789 SELECT id_theme, value | |
790 FROM {db_prefix}themes | |
791 WHERE id_member = {int:no_member} | |
792 AND variable = {string:name} | |
793 AND id_theme IN ({array_int:theme_list})', | |
794 array( | |
795 'theme_list' => array_keys($themes), | |
796 'no_member' => 0, | |
797 'name' => 'name', | |
798 ) | |
799 ); | |
800 while ($row = $smcFunc['db_fetch_assoc']($request)) | |
801 { | |
802 // Now we have it... | |
803 $context['theme_names'][$themes[$row['id_theme']]] = $row['value']; | |
804 } | |
805 $smcFunc['db_free_result']($request); | |
806 } | |
807 } | |
808 | |
809 // Before we go to far can we make anything writable, eh, eh? | |
810 if (!empty($context['make_writable'])) | |
811 { | |
812 // What is left to be made writable? | |
813 $file_status = create_chmod_control($context['make_writable']); | |
814 $context['still_not_writable'] = $file_status['files']['notwritable']; | |
815 | |
816 // Mark those which are now writable as such. | |
817 foreach ($context['files'] as $type => $data) | |
818 { | |
819 if ($type == 'lang') | |
820 { | |
821 foreach ($data as $k => $file) | |
822 if (!$file['writable'] && !in_array($file['destination'], $context['still_not_writable'])) | |
823 $context['files'][$type][$k]['writable'] = true; | |
824 } | |
825 else | |
826 { | |
827 foreach ($data as $theme => $files) | |
828 foreach ($files as $k => $file) | |
829 if (!$file['writable'] && !in_array($file['destination'], $context['still_not_writable'])) | |
830 $context['files'][$type][$theme][$k]['writable'] = true; | |
831 } | |
832 } | |
833 | |
834 // Are we going to need more language stuff? | |
835 if (!empty($context['still_not_writable'])) | |
836 loadLanguage('Packages'); | |
837 } | |
838 | |
839 // This is the list for the main files. | |
840 $listOptions = array( | |
841 'id' => 'lang_main_files_list', | |
842 'title' => $txt['languages_download_main_files'], | |
843 'get_items' => array( | |
844 'function' => create_function('', ' | |
845 global $context; | |
846 return $context[\'files\'][\'lang\']; | |
847 '), | |
848 ), | |
849 'columns' => array( | |
850 'name' => array( | |
851 'header' => array( | |
852 'value' => $txt['languages_download_filename'], | |
853 ), | |
854 'data' => array( | |
855 'function' => create_function('$rowData', ' | |
856 global $context, $txt; | |
857 | |
858 return \'<strong>\' . $rowData[\'name\'] . \'</strong><br /><span class="smalltext">\' . $txt[\'languages_download_dest\'] . \': \' . $rowData[\'destination\'] . \'</span>\' . ($rowData[\'version_compare\'] == \'older\' ? \'<br />\' . $txt[\'languages_download_older\'] : \'\'); | |
859 '), | |
860 ), | |
861 ), | |
862 'writable' => array( | |
863 'header' => array( | |
864 'value' => $txt['languages_download_writable'], | |
865 ), | |
866 'data' => array( | |
867 'function' => create_function('$rowData', ' | |
868 global $txt; | |
869 | |
870 return \'<span style="color: \' . ($rowData[\'writable\'] ? \'green\' : \'red\') . \';">\' . ($rowData[\'writable\'] ? $txt[\'yes\'] : $txt[\'no\']) . \'</span>\'; | |
871 '), | |
872 'style' => 'text-align: center', | |
873 ), | |
874 ), | |
875 'version' => array( | |
876 'header' => array( | |
877 'value' => $txt['languages_download_version'], | |
878 ), | |
879 'data' => array( | |
880 'function' => create_function('$rowData', ' | |
881 global $txt; | |
882 | |
883 return \'<span style="color: \' . ($rowData[\'version_compare\'] == \'older\' ? \'red\' : ($rowData[\'version_compare\'] == \'same\' ? \'orange\' : \'green\')) . \';">\' . $rowData[\'version\'] . \'</span>\'; | |
884 '), | |
885 ), | |
886 ), | |
887 'exists' => array( | |
888 'header' => array( | |
889 'value' => $txt['languages_download_exists'], | |
890 ), | |
891 'data' => array( | |
892 'function' => create_function('$rowData', ' | |
893 global $txt; | |
894 | |
895 return $rowData[\'exists\'] ? ($rowData[\'exists\'] == \'same\' ? $txt[\'languages_download_exists_same\'] : $txt[\'languages_download_exists_different\']) : $txt[\'no\']; | |
896 '), | |
897 ), | |
898 ), | |
899 'copy' => array( | |
900 'header' => array( | |
901 'value' => $txt['languages_download_copy'], | |
902 ), | |
903 'data' => array( | |
904 'function' => create_function('$rowData', ' | |
905 return \'<input type="checkbox" name="copy_file[]" value="\' . $rowData[\'generaldest\'] . \'" \' . ($rowData[\'default_copy\'] ? \'checked="checked"\' : \'\') . \' class="input_check" />\'; | |
906 '), | |
907 'style' => 'text-align: center; width: 4%;', | |
908 ), | |
909 ), | |
910 ), | |
911 ); | |
912 | |
913 // Kill the cache, as it is now invalid.. | |
914 if (!empty($modSettings['cache_enable'])) | |
915 { | |
916 cache_put_data('known_languages', null, !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600); | |
917 cache_put_data('known_languages_all', null, !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600); | |
918 } | |
919 | |
920 require_once($sourcedir . '/Subs-List.php'); | |
921 createList($listOptions); | |
922 | |
923 $context['default_list'] = 'lang_main_files_list'; | |
924 } | |
925 | |
926 // This lists all the current languages and allows editing of them. | |
927 function ModifyLanguages() | |
928 { | |
929 global $txt, $context, $scripturl; | |
930 global $user_info, $smcFunc, $sourcedir, $language, $boarddir, $forum_version; | |
931 | |
932 // Setting a new default? | |
933 if (!empty($_POST['set_default']) && !empty($_POST['def_language'])) | |
934 { | |
935 checkSession(); | |
936 | |
937 getLanguages(true, false); | |
938 $lang_exists = false; | |
939 foreach ($context['languages'] as $lang) | |
940 { | |
941 if ($_POST['def_language'] == $lang['filename']) | |
942 { | |
943 $lang_exists = true; | |
944 break; | |
945 } | |
946 } | |
947 | |
948 if ($_POST['def_language'] != $language && $lang_exists) | |
949 { | |
950 require_once($sourcedir . '/Subs-Admin.php'); | |
951 updateSettingsFile(array('language' => '\'' . $_POST['def_language'] . '\'')); | |
952 $language = $_POST['def_language']; | |
953 } | |
954 } | |
955 | |
956 $listOptions = array( | |
957 'id' => 'language_list', | |
958 'items_per_page' => 20, | |
959 'base_href' => $scripturl . '?action=admin;area=languages', | |
960 'title' => $txt['edit_languages'], | |
961 'get_items' => array( | |
962 'function' => 'list_getLanguages', | |
963 ), | |
964 'get_count' => array( | |
965 'function' => 'list_getNumLanguages', | |
966 ), | |
967 'columns' => array( | |
968 'default' => array( | |
969 'header' => array( | |
970 'value' => $txt['languages_default'], | |
971 ), | |
972 'data' => array( | |
973 'function' => create_function('$rowData', ' | |
974 return \'<input type="radio" name="def_language" value="\' . $rowData[\'id\'] . \'" \' . ($rowData[\'default\'] ? \'checked="checked"\' : \'\') . \' onclick="highlightSelected(\\\'list_language_list_\' . $rowData[\'id\'] . \'\\\');" class="input_radio" />\'; | |
975 '), | |
976 'style' => 'text-align: center; width: 8%;', | |
977 ), | |
978 ), | |
979 'name' => array( | |
980 'header' => array( | |
981 'value' => $txt['languages_lang_name'], | |
982 ), | |
983 'data' => array( | |
984 'function' => create_function('$rowData', ' | |
985 global $scripturl, $context; | |
986 | |
987 return sprintf(\'<a href="%1$s?action=admin;area=languages;sa=editlang;lid=%2$s">%3$s</a>\', $scripturl, $rowData[\'id\'], $rowData[\'name\']); | |
988 '), | |
989 ), | |
990 ), | |
991 'character_set' => array( | |
992 'header' => array( | |
993 'value' => $txt['languages_character_set'], | |
994 ), | |
995 'data' => array( | |
996 'db_htmlsafe' => 'char_set', | |
997 ), | |
998 ), | |
999 'count' => array( | |
1000 'header' => array( | |
1001 'value' => $txt['languages_users'], | |
1002 ), | |
1003 'data' => array( | |
1004 'db_htmlsafe' => 'count', | |
1005 'style' => 'text-align: center', | |
1006 ), | |
1007 ), | |
1008 'locale' => array( | |
1009 'header' => array( | |
1010 'value' => $txt['languages_locale'], | |
1011 ), | |
1012 'data' => array( | |
1013 'db_htmlsafe' => 'locale', | |
1014 ), | |
1015 ), | |
1016 ), | |
1017 'form' => array( | |
1018 'href' => $scripturl . '?action=admin;area=languages', | |
1019 ), | |
1020 'additional_rows' => array( | |
1021 array( | |
1022 'position' => 'below_table_data', | |
1023 'value' => '<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" /><input type="submit" name="set_default" value="' . $txt['save'] . '"' . (is_writable($boarddir . '/Settings.php') ? '' : ' disabled="disabled"') . ' class="button_submit" />', | |
1024 'style' => 'text-align: right;', | |
1025 ), | |
1026 ), | |
1027 // For highlighting the default. | |
1028 'javascript' => ' | |
1029 var prevClass = ""; | |
1030 var prevDiv = ""; | |
1031 function highlightSelected(box) | |
1032 { | |
1033 if (prevClass != "") | |
1034 prevDiv.className = prevClass; | |
1035 | |
1036 prevDiv = document.getElementById(box); | |
1037 prevClass = prevDiv.className; | |
1038 | |
1039 prevDiv.className = "highlight2"; | |
1040 } | |
1041 highlightSelected("list_language_list_' . ($language == '' ? 'english' : $language). '"); | |
1042 ', | |
1043 ); | |
1044 | |
1045 // Display a warning if we cannot edit the default setting. | |
1046 if (!is_writable($boarddir . '/Settings.php')) | |
1047 $listOptions['additional_rows'][] = array( | |
1048 'position' => 'after_title', | |
1049 'value' => $txt['language_settings_writable'], | |
1050 'class' => 'smalltext alert', | |
1051 ); | |
1052 | |
1053 require_once($sourcedir . '/Subs-List.php'); | |
1054 createList($listOptions); | |
1055 | |
1056 $context['sub_template'] = 'show_list'; | |
1057 $context['default_list'] = 'language_list'; | |
1058 } | |
1059 | |
1060 // How many languages? | |
1061 function list_getNumLanguages() | |
1062 { | |
1063 global $settings; | |
1064 | |
1065 // Return how many we have. | |
1066 return count(getLanguages(true, false)); | |
1067 } | |
1068 | |
1069 // Fetch the actual language information. | |
1070 function list_getLanguages() | |
1071 { | |
1072 global $settings, $smcFunc, $language, $context, $txt; | |
1073 | |
1074 $languages = array(); | |
1075 // Keep our old entries. | |
1076 $old_txt = $txt; | |
1077 $backup_actual_theme_dir = $settings['actual_theme_dir']; | |
1078 $backup_base_theme_dir = !empty($settings['base_theme_dir']) ? $settings['base_theme_dir'] : ''; | |
1079 | |
1080 // Override these for now. | |
1081 $settings['actual_theme_dir'] = $settings['base_theme_dir'] = $settings['default_theme_dir']; | |
1082 getLanguages(true, false); | |
1083 | |
1084 // Put them back. | |
1085 $settings['actual_theme_dir'] = $backup_actual_theme_dir; | |
1086 if (!empty($backup_base_theme_dir)) | |
1087 $settings['base_theme_dir'] = $backup_base_theme_dir; | |
1088 else | |
1089 unset($settings['base_theme_dir']); | |
1090 | |
1091 // Get the language files and data... | |
1092 foreach ($context['languages'] as $lang) | |
1093 { | |
1094 // Load the file to get the character set. | |
1095 require($settings['default_theme_dir'] . '/languages/index.' . $lang['filename'] . '.php'); | |
1096 | |
1097 $languages[$lang['filename']] = array( | |
1098 'id' => $lang['filename'], | |
1099 'count' => 0, | |
1100 'char_set' => $txt['lang_character_set'], | |
1101 'default' => $language == $lang['filename'] || ($language == '' && $lang['filename'] == 'english'), | |
1102 'locale' => $txt['lang_locale'], | |
1103 'name' => $smcFunc['ucwords'](strtr($lang['filename'], array('_' => ' ', '-utf8' => ''))), | |
1104 ); | |
1105 } | |
1106 | |
1107 // Work out how many people are using each language. | |
1108 $request = $smcFunc['db_query']('', ' | |
1109 SELECT lngfile, COUNT(*) AS num_users | |
1110 FROM {db_prefix}members | |
1111 GROUP BY lngfile', | |
1112 array( | |
1113 ) | |
1114 ); | |
1115 while ($row = $smcFunc['db_fetch_assoc']($request)) | |
1116 { | |
1117 // Default? | |
1118 if (empty($row['lngfile']) || !isset($languages[$row['lngfile']])) | |
1119 $row['lngfile'] = $language; | |
1120 | |
1121 if (!isset($languages[$row['lngfile']]) && isset($languages['english'])) | |
1122 $languages['english']['count'] += $row['num_users']; | |
1123 elseif (isset($languages[$row['lngfile']])) | |
1124 $languages[$row['lngfile']]['count'] += $row['num_users']; | |
1125 } | |
1126 $smcFunc['db_free_result']($request); | |
1127 | |
1128 // Restore the current users language. | |
1129 $txt = $old_txt; | |
1130 | |
1131 // Return how many we have. | |
1132 return $languages; | |
1133 } | |
1134 | |
1135 // Edit language related settings. | |
1136 function ModifyLanguageSettings($return_config = false) | |
1137 { | |
1138 global $scripturl, $context, $txt, $boarddir, $settings, $smcFunc; | |
1139 | |
1140 // Warn the user if the backup of Settings.php failed. | |
1141 $settings_not_writable = !is_writable($boarddir . '/Settings.php'); | |
1142 $settings_backup_fail = !@is_writable($boarddir . '/Settings_bak.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php'); | |
1143 | |
1144 /* If you're writing a mod, it's a bad idea to add things here.... | |
1145 For each option: | |
1146 variable name, description, type (constant), size/possible values, helptext. | |
1147 OR an empty string for a horizontal rule. | |
1148 OR a string for a titled section. */ | |
1149 $config_vars = array( | |
1150 'language' => array('language', $txt['default_language'], 'file', 'select', array(), null, 'disabled' => $settings_not_writable), | |
1151 array('userLanguage', $txt['userLanguage'], 'db', 'check', null, 'userLanguage'), | |
1152 ); | |
1153 | |
1154 if ($return_config) | |
1155 return $config_vars; | |
1156 | |
1157 // Get our languages. No cache and use utf8. | |
1158 getLanguages(false, false); | |
1159 foreach ($context['languages'] as $lang) | |
1160 $config_vars['language'][4][$lang['filename']] = array($lang['filename'], strtr($lang['name'], array('-utf8' => ' (UTF-8)'))); | |
1161 | |
1162 // Saving settings? | |
1163 if (isset($_REQUEST['save'])) | |
1164 { | |
1165 checkSession(); | |
1166 saveSettings($config_vars); | |
1167 redirectexit('action=admin;area=languages;sa=settings'); | |
1168 } | |
1169 | |
1170 // Setup the template stuff. | |
1171 $context['post_url'] = $scripturl . '?action=admin;area=languages;sa=settings;save'; | |
1172 $context['settings_title'] = $txt['language_settings']; | |
1173 $context['save_disabled'] = $settings_not_writable; | |
1174 | |
1175 if ($settings_not_writable) | |
1176 $context['settings_message'] = '<div class="centertext"><strong>' . $txt['settings_not_writable'] . '</strong></div><br />'; | |
1177 elseif ($settings_backup_fail) | |
1178 $context['settings_message'] = '<div class="centertext"><strong>' . $txt['admin_backup_fail'] . '</strong></div><br />'; | |
1179 | |
1180 // Fill the config array. | |
1181 prepareServerSettingsContext($config_vars); | |
1182 } | |
1183 | |
1184 // Edit a particular set of language entries. | |
1185 function ModifyLanguage() | |
1186 { | |
1187 global $settings, $context, $smcFunc, $txt, $modSettings, $boarddir, $sourcedir, $language; | |
1188 | |
1189 loadLanguage('ManageSettings'); | |
1190 | |
1191 // Select the languages tab. | |
1192 $context['menu_data_' . $context['admin_menu_id']]['current_subsection'] = 'edit'; | |
1193 $context['page_title'] = $txt['edit_languages']; | |
1194 $context['sub_template'] = 'modify_language_entries'; | |
1195 | |
1196 $context['lang_id'] = $_GET['lid']; | |
1197 list($theme_id, $file_id) = empty($_REQUEST['tfid']) || strpos($_REQUEST['tfid'], '+') === false ? array(1, '') : explode('+', $_REQUEST['tfid']); | |
1198 | |
1199 // Clean the ID - just in case. | |
1200 preg_match('~([A-Za-z0-9_-]+)~', $context['lang_id'], $matches); | |
1201 $context['lang_id'] = $matches[1]; | |
1202 | |
1203 // Get all the theme data. | |
1204 $request = $smcFunc['db_query']('', ' | |
1205 SELECT id_theme, variable, value | |
1206 FROM {db_prefix}themes | |
1207 WHERE id_theme != {int:default_theme} | |
1208 AND id_member = {int:no_member} | |
1209 AND variable IN ({string:name}, {string:theme_dir})', | |
1210 array( | |
1211 'default_theme' => 1, | |
1212 'no_member' => 0, | |
1213 'name' => 'name', | |
1214 'theme_dir' => 'theme_dir', | |
1215 ) | |
1216 ); | |
1217 $themes = array( | |
1218 1 => array( | |
1219 'name' => $txt['dvc_default'], | |
1220 'theme_dir' => $settings['default_theme_dir'], | |
1221 ), | |
1222 ); | |
1223 while ($row = $smcFunc['db_fetch_assoc']($request)) | |
1224 $themes[$row['id_theme']][$row['variable']] = $row['value']; | |
1225 $smcFunc['db_free_result']($request); | |
1226 | |
1227 // This will be where we look | |
1228 $lang_dirs = array(); | |
1229 // Check we have themes with a path and a name - just in case - and add the path. | |
1230 foreach ($themes as $id => $data) | |
1231 { | |
1232 if (count($data) != 2) | |
1233 unset($themes[$id]); | |
1234 elseif (is_dir($data['theme_dir'] . '/languages')) | |
1235 $lang_dirs[$id] = $data['theme_dir'] . '/languages'; | |
1236 | |
1237 // How about image directories? | |
1238 if (is_dir($data['theme_dir'] . '/images/' . $context['lang_id'])) | |
1239 $images_dirs[$id] = $data['theme_dir'] . '/images/' . $context['lang_id']; | |
1240 } | |
1241 | |
1242 $current_file = $file_id ? $lang_dirs[$theme_id] . '/' . $file_id . '.' . $context['lang_id'] . '.php' : ''; | |
1243 | |
1244 // Now for every theme get all the files and stick them in context! | |
1245 $context['possible_files'] = array(); | |
1246 foreach ($lang_dirs as $theme => $theme_dir) | |
1247 { | |
1248 // Open it up. | |
1249 $dir = dir($theme_dir); | |
1250 while ($entry = $dir->read()) | |
1251 { | |
1252 // We're only after the files for this language. | |
1253 if (preg_match('~^([A-Za-z]+)\.' . $context['lang_id'] . '\.php$~', $entry, $matches) == 0) | |
1254 continue; | |
1255 | |
1256 //!!! Temp! | |
1257 if ($matches[1] == 'EmailTemplates') | |
1258 continue; | |
1259 | |
1260 if (!isset($context['possible_files'][$theme])) | |
1261 $context['possible_files'][$theme] = array( | |
1262 'id' => $theme, | |
1263 'name' => $themes[$theme]['name'], | |
1264 'files' => array(), | |
1265 ); | |
1266 | |
1267 $context['possible_files'][$theme]['files'][] = array( | |
1268 'id' => $matches[1], | |
1269 'name' => isset($txt['lang_file_desc_' . $matches[1]]) ? $txt['lang_file_desc_' . $matches[1]] : $matches[1], | |
1270 'selected' => $theme_id == $theme && $file_id == $matches[1], | |
1271 ); | |
1272 } | |
1273 $dir->close(); | |
1274 } | |
1275 | |
1276 // We no longer wish to speak this language. | |
1277 if (!empty($_POST['delete_main']) && $context['lang_id'] != 'english') | |
1278 { | |
1279 checkSession(); | |
1280 | |
1281 // !!! Todo: FTP Controls? | |
1282 require_once($sourcedir . '/Subs-Package.php'); | |
1283 | |
1284 // First, Make a backup? | |
1285 if (!empty($modSettings['package_make_backups']) && (!isset($_SESSION['last_backup_for']) || $_SESSION['last_backup_for'] != $context['lang_id'] . '$$$')) | |
1286 { | |
1287 $_SESSION['last_backup_for'] = $context['lang_id'] . '$$$'; | |
1288 package_create_backup('backup_lang_' . $context['lang_id']); | |
1289 } | |
1290 | |
1291 // Second, loop through the array to remove the files. | |
1292 foreach ($lang_dirs as $curPath) | |
1293 { | |
1294 foreach ($context['possible_files'][1]['files'] as $lang) | |
1295 if (file_exists($curPath . '/' . $lang['id'] . '.' . $context['lang_id'] . '.php')) | |
1296 unlink($curPath . '/' . $lang['id'] . '.' . $context['lang_id'] . '.php'); | |
1297 | |
1298 // Check for the email template. | |
1299 if (file_exists($curPath . '/EmailTemplates.' . $context['lang_id'] . '.php')) | |
1300 unlink($curPath . '/EmailTemplates.' . $context['lang_id'] . '.php'); | |
1301 } | |
1302 | |
1303 // Third, the agreement file. | |
1304 if (file_exists($boarddir . '/agreement.' . $context['lang_id'] . '.txt')) | |
1305 unlink($boarddir . '/agreement.' . $context['lang_id'] . '.txt'); | |
1306 | |
1307 // Fourth, a related images folder? | |
1308 foreach ($images_dirs as $curPath) | |
1309 if (is_dir($curPath)) | |
1310 deltree($curPath); | |
1311 | |
1312 // Members can no longer use this language. | |
1313 $smcFunc['db_query']('', ' | |
1314 UPDATE {db_prefix}members | |
1315 SET lngfile = {string:empty_string} | |
1316 WHERE lngfile = {string:current_language}', | |
1317 array( | |
1318 'empty_string' => '', | |
1319 'current_language' => $context['lang_id'], | |
1320 ) | |
1321 ); | |
1322 | |
1323 // Fifth, update getLanguages() cache. | |
1324 if (!empty($modSettings['cache_enable'])) | |
1325 { | |
1326 cache_put_data('known_languages', null, !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600); | |
1327 cache_put_data('known_languages_all', null, !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600); | |
1328 } | |
1329 | |
1330 // Sixth, if we deleted the default language, set us back to english? | |
1331 if ($context['lang_id'] == $language) | |
1332 { | |
1333 require_once($sourcedir . '/Subs-Admin.php'); | |
1334 $language = 'english'; | |
1335 updateSettingsFile(array('language' => '\'' . $language . '\'')); | |
1336 } | |
1337 | |
1338 // Seventh, get out of here. | |
1339 redirectexit('action=admin;area=languages;sa=edit;' . $context['session_var'] . '=' . $context['session_id']); | |
1340 } | |
1341 | |
1342 // Saving primary settings? | |
1343 $madeSave = false; | |
1344 if (!empty($_POST['save_main']) && !$current_file) | |
1345 { | |
1346 checkSession(); | |
1347 | |
1348 // Read in the current file. | |
1349 $current_data = implode('', file($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php')); | |
1350 // These are the replacements. old => new | |
1351 $replace_array = array( | |
1352 '~\$txt\[\'lang_character_set\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_character_set\'] = \'' . addslashes($_POST['character_set']) . '\';', | |
1353 '~\$txt\[\'lang_locale\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_locale\'] = \'' . addslashes($_POST['locale']) . '\';', | |
1354 '~\$txt\[\'lang_dictionary\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_dictionary\'] = \'' . addslashes($_POST['dictionary']) . '\';', | |
1355 '~\$txt\[\'lang_spelling\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_spelling\'] = \'' . addslashes($_POST['spelling']) . '\';', | |
1356 '~\$txt\[\'lang_rtl\'\]\s=\s[A-Za-z0-9]+;~' => '$txt[\'lang_rtl\'] = ' . (!empty($_POST['rtl']) ? 'true' : 'false') . ';', | |
1357 ); | |
1358 $current_data = preg_replace(array_keys($replace_array), array_values($replace_array), $current_data); | |
1359 $fp = fopen($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php', 'w+'); | |
1360 fwrite($fp, $current_data); | |
1361 fclose($fp); | |
1362 | |
1363 $madeSave = true; | |
1364 } | |
1365 | |
1366 // Quickly load index language entries. | |
1367 $old_txt = $txt; | |
1368 require($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php'); | |
1369 $context['lang_file_not_writable_message'] = is_writable($settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php') ? '' : sprintf($txt['lang_file_not_writable'], $settings['default_theme_dir'] . '/languages/index.' . $context['lang_id'] . '.php'); | |
1370 // Setup the primary settings context. | |
1371 $context['primary_settings'] = array( | |
1372 'name' => $smcFunc['ucwords'](strtr($context['lang_id'], array('_' => ' ', '-utf8' => ''))), | |
1373 'character_set' => $txt['lang_character_set'], | |
1374 'locale' => $txt['lang_locale'], | |
1375 'dictionary' => $txt['lang_dictionary'], | |
1376 'spelling' => $txt['lang_spelling'], | |
1377 'rtl' => $txt['lang_rtl'], | |
1378 ); | |
1379 | |
1380 // Restore normal service. | |
1381 $txt = $old_txt; | |
1382 | |
1383 // Are we saving? | |
1384 $save_strings = array(); | |
1385 if (isset($_POST['save_entries']) && !empty($_POST['entry'])) | |
1386 { | |
1387 checkSession(); | |
1388 | |
1389 // Clean each entry! | |
1390 foreach ($_POST['entry'] as $k => $v) | |
1391 { | |
1392 // Only try to save if it's changed! | |
1393 if ($_POST['entry'][$k] != $_POST['comp'][$k]) | |
1394 $save_strings[$k] = cleanLangString($v, false); | |
1395 } | |
1396 } | |
1397 | |
1398 // If we are editing a file work away at that. | |
1399 if ($current_file) | |
1400 { | |
1401 $context['entries_not_writable_message'] = is_writable($current_file) ? '' : sprintf($txt['lang_entries_not_writable'], $current_file); | |
1402 | |
1403 $entries = array(); | |
1404 // We can't just require it I'm afraid - otherwise we pass in all kinds of variables! | |
1405 $multiline_cache = ''; | |
1406 foreach (file($current_file) as $line) | |
1407 { | |
1408 // Got a new entry? | |
1409 if ($line[0] == '$' && !empty($multiline_cache)) | |
1410 { | |
1411 preg_match('~\$(helptxt|txt)\[\'(.+)\'\]\s=\s(.+);~', strtr($multiline_cache, array("\n" => '', "\t" => '')), $matches); | |
1412 if (!empty($matches[3])) | |
1413 { | |
1414 $entries[$matches[2]] = array( | |
1415 'type' => $matches[1], | |
1416 'full' => $matches[0], | |
1417 'entry' => $matches[3], | |
1418 ); | |
1419 $multiline_cache = ''; | |
1420 } | |
1421 } | |
1422 $multiline_cache .= $line . "\n"; | |
1423 } | |
1424 // Last entry to add? | |
1425 if ($multiline_cache) | |
1426 { | |
1427 preg_match('~\$(helptxt|txt)\[\'(.+)\'\]\s=\s(.+);~', strtr($multiline_cache, array("\n" => '', "\t" => '')), $matches); | |
1428 if (!empty($matches[3])) | |
1429 $entries[$matches[2]] = array( | |
1430 'type' => $matches[1], | |
1431 'full' => $matches[0], | |
1432 'entry' => $matches[3], | |
1433 ); | |
1434 } | |
1435 | |
1436 // These are the entries we can definitely save. | |
1437 $final_saves = array(); | |
1438 | |
1439 $context['file_entries'] = array(); | |
1440 foreach ($entries as $entryKey => $entryValue) | |
1441 { | |
1442 // Ignore some things we set separately. | |
1443 $ignore_files = array('lang_character_set', 'lang_locale', 'lang_dictionary', 'lang_spelling', 'lang_rtl'); | |
1444 if (in_array($entryKey, $ignore_files)) | |
1445 continue; | |
1446 | |
1447 // These are arrays that need breaking out. | |
1448 $arrays = array('days', 'days_short', 'months', 'months_titles', 'months_short'); | |
1449 if (in_array($entryKey, $arrays)) | |
1450 { | |
1451 // Get off the first bits. | |
1452 $entryValue['entry'] = substr($entryValue['entry'], strpos($entryValue['entry'], '(') + 1, strrpos($entryValue['entry'], ')') - strpos($entryValue['entry'], '(')); | |
1453 $entryValue['entry'] = explode(',', strtr($entryValue['entry'], array(' ' => ''))); | |
1454 | |
1455 // Now create an entry for each item. | |
1456 $cur_index = 0; | |
1457 $save_cache = array( | |
1458 'enabled' => false, | |
1459 'entries' => array(), | |
1460 ); | |
1461 foreach ($entryValue['entry'] as $id => $subValue) | |
1462 { | |
1463 // Is this a new index? | |
1464 if (preg_match('~^(\d+)~', $subValue, $matches)) | |
1465 { | |
1466 $cur_index = $matches[1]; | |
1467 $subValue = substr($subValue, strpos($subValue, '\'')); | |
1468 } | |
1469 | |
1470 // Clean up some bits. | |
1471 $subValue = strtr($subValue, array('"' => '', '\'' => '', ')' => '')); | |
1472 | |
1473 // Can we save? | |
1474 if (isset($save_strings[$entryKey . '-+- ' . $cur_index])) | |
1475 { | |
1476 $save_cache['entries'][$cur_index] = strtr($save_strings[$entryKey . '-+- ' . $cur_index], array('\'' => '')); | |
1477 $save_cache['enabled'] = true; | |
1478 } | |
1479 else | |
1480 $save_cache['entries'][$cur_index] = $subValue; | |
1481 | |
1482 $context['file_entries'][] = array( | |
1483 'key' => $entryKey . '-+- ' . $cur_index, | |
1484 'value' => $subValue, | |
1485 'rows' => 1, | |
1486 ); | |
1487 $cur_index++; | |
1488 } | |
1489 | |
1490 // Do we need to save? | |
1491 if ($save_cache['enabled']) | |
1492 { | |
1493 // Format the string, checking the indexes first. | |
1494 $items = array(); | |
1495 $cur_index = 0; | |
1496 foreach ($save_cache['entries'] as $k2 => $v2) | |
1497 { | |
1498 // Manually show the custom index. | |
1499 if ($k2 != $cur_index) | |
1500 { | |
1501 $items[] = $k2 . ' => \'' . $v2 . '\''; | |
1502 $cur_index = $k2; | |
1503 } | |
1504 else | |
1505 $items[] = '\'' . $v2 . '\''; | |
1506 | |
1507 $cur_index++; | |
1508 } | |
1509 // Now create the string! | |
1510 $final_saves[$entryKey] = array( | |
1511 'find' => $entryValue['full'], | |
1512 'replace' => '$' . $entryValue['type'] . '[\'' . $entryKey . '\'] = array(' . implode(', ', $items) . ');', | |
1513 ); | |
1514 } | |
1515 } | |
1516 else | |
1517 { | |
1518 // Saving? | |
1519 if (isset($save_strings[$entryKey]) && $save_strings[$entryKey] != $entryValue['entry']) | |
1520 { | |
1521 // !!! Fix this properly. | |
1522 if ($save_strings[$entryKey] == '') | |
1523 $save_strings[$entryKey] = '\'\''; | |
1524 | |
1525 // Set the new value. | |
1526 $entryValue['entry'] = $save_strings[$entryKey]; | |
1527 // And we know what to save now! | |
1528 $final_saves[$entryKey] = array( | |
1529 'find' => $entryValue['full'], | |
1530 'replace' => '$' . $entryValue['type'] . '[\'' . $entryKey . '\'] = ' . $save_strings[$entryKey] . ';', | |
1531 ); | |
1532 } | |
1533 | |
1534 $editing_string = cleanLangString($entryValue['entry'], true); | |
1535 $context['file_entries'][] = array( | |
1536 'key' => $entryKey, | |
1537 'value' => $editing_string, | |
1538 'rows' => (int) (strlen($editing_string) / 38) + substr_count($editing_string, "\n") + 1, | |
1539 ); | |
1540 } | |
1541 } | |
1542 | |
1543 // Any saves to make? | |
1544 if (!empty($final_saves)) | |
1545 { | |
1546 checkSession(); | |
1547 | |
1548 $file_contents = implode('', file($current_file)); | |
1549 foreach ($final_saves as $save) | |
1550 $file_contents = strtr($file_contents, array($save['find'] => $save['replace'])); | |
1551 | |
1552 // Save the actual changes. | |
1553 $fp = fopen($current_file, 'w+'); | |
1554 fwrite($fp, $file_contents); | |
1555 fclose($fp); | |
1556 | |
1557 $madeSave = true; | |
1558 } | |
1559 | |
1560 // Another restore. | |
1561 $txt = $old_txt; | |
1562 } | |
1563 | |
1564 // If we saved, redirect. | |
1565 if ($madeSave) | |
1566 redirectexit('action=admin;area=languages;sa=editlang;lid=' . $context['lang_id']); | |
1567 } | |
1568 | |
1569 // This function could be two functions - either way it cleans language entries to/from display. | |
1570 function cleanLangString($string, $to_display = true) | |
1571 { | |
1572 global $smcFunc; | |
1573 | |
1574 // If going to display we make sure it doesn't have any HTML in it - etc. | |
1575 $new_string = ''; | |
1576 if ($to_display) | |
1577 { | |
1578 // Are we in a string (0 = no, 1 = single quote, 2 = parsed) | |
1579 $in_string = 0; | |
1580 $is_escape = false; | |
1581 for ($i = 0; $i < strlen($string); $i++) | |
1582 { | |
1583 // Handle ecapes first. | |
1584 if ($string{$i} == '\\') | |
1585 { | |
1586 // Toggle the escape. | |
1587 $is_escape = !$is_escape; | |
1588 // If we're now escaped don't add this string. | |
1589 if ($is_escape) | |
1590 continue; | |
1591 } | |
1592 // Special case - parsed string with line break etc? | |
1593 elseif (($string{$i} == 'n' || $string{$i} == 't') && $in_string == 2 && $is_escape) | |
1594 { | |
1595 // Put the escape back... | |
1596 $new_string .= $string{$i} == 'n' ? "\n" : "\t"; | |
1597 $is_escape = false; | |
1598 continue; | |
1599 } | |
1600 // Have we got a single quote? | |
1601 elseif ($string{$i} == '\'') | |
1602 { | |
1603 // Already in a parsed string, or escaped in a linear string, means we print it - otherwise something special. | |
1604 if ($in_string != 2 && ($in_string != 1 || !$is_escape)) | |
1605 { | |
1606 // Is it the end of a single quote string? | |
1607 if ($in_string == 1) | |
1608 $in_string = 0; | |
1609 // Otherwise it's the start! | |
1610 else | |
1611 $in_string = 1; | |
1612 | |
1613 // Don't actually include this character! | |
1614 continue; | |
1615 } | |
1616 } | |
1617 // Otherwise a double quote? | |
1618 elseif ($string{$i} == '"') | |
1619 { | |
1620 // Already in a single quote string, or escaped in a parsed string, means we print it - otherwise something special. | |
1621 if ($in_string != 1 && ($in_string != 2 || !$is_escape)) | |
1622 { | |
1623 // Is it the end of a double quote string? | |
1624 if ($in_string == 2) | |
1625 $in_string = 0; | |
1626 // Otherwise it's the start! | |
1627 else | |
1628 $in_string = 2; | |
1629 | |
1630 // Don't actually include this character! | |
1631 continue; | |
1632 } | |
1633 } | |
1634 // A join/space outside of a string is simply removed. | |
1635 elseif ($in_string == 0 && (empty($string{$i}) || $string{$i} == '.')) | |
1636 continue; | |
1637 // Start of a variable? | |
1638 elseif ($in_string == 0 && $string{$i} == '$') | |
1639 { | |
1640 // Find the whole of it! | |
1641 preg_match('~([\$A-Za-z0-9\'\[\]_-]+)~', substr($string, $i), $matches); | |
1642 if (!empty($matches[1])) | |
1643 { | |
1644 // Come up with some pseudo thing to indicate this is a var. | |
1645 //!!! Do better than this, please! | |
1646 $new_string .= '{%' . $matches[1] . '%}'; | |
1647 | |
1648 // We're not going to reparse this. | |
1649 $i += strlen($matches[1]) - 1; | |
1650 } | |
1651 | |
1652 continue; | |
1653 } | |
1654 // Right, if we're outside of a string we have DANGER, DANGER! | |
1655 elseif ($in_string == 0) | |
1656 { | |
1657 continue; | |
1658 } | |
1659 | |
1660 // Actually add the character to the string! | |
1661 $new_string .= $string{$i}; | |
1662 // If anything was escaped it ain't any longer! | |
1663 $is_escape = false; | |
1664 } | |
1665 | |
1666 // Unhtml then rehtml the whole thing! | |
1667 $new_string = htmlspecialchars(un_htmlspecialchars($new_string)); | |
1668 } | |
1669 else | |
1670 { | |
1671 // Keep track of what we're doing... | |
1672 $in_string = 0; | |
1673 // This is for deciding whether to HTML a quote. | |
1674 $in_html = false; | |
1675 for ($i = 0; $i < strlen($string); $i++) | |
1676 { | |
1677 // Handle line breaks! | |
1678 if ($string{$i} == "\n" || $string{$i} == "\t") | |
1679 { | |
1680 // Are we in a string? Is it the right type? | |
1681 if ($in_string == 1) | |
1682 { | |
1683 // Change type! | |
1684 $new_string .= '\' . "\\' . ($string{$i} == "\n" ? 'n' : 't'); | |
1685 $in_string = 2; | |
1686 } | |
1687 elseif ($in_string == 2) | |
1688 $new_string .= '\\' . ($string{$i} == "\n" ? 'n' : 't'); | |
1689 // Otherwise start one off - joining if required. | |
1690 else | |
1691 $new_string .= ($new_string ? ' . ' : '') . '"\\' . ($string{$i} == "\n" ? 'n' : 't'); | |
1692 | |
1693 continue; | |
1694 } | |
1695 // We don't do parsed strings apart from for breaks. | |
1696 elseif ($in_string == 2) | |
1697 { | |
1698 $in_string = 0; | |
1699 $new_string .= '"'; | |
1700 } | |
1701 | |
1702 // Not in a string yet? | |
1703 if ($in_string != 1) | |
1704 { | |
1705 $in_string = 1; | |
1706 $new_string .= ($new_string ? ' . ' : '') . '\''; | |
1707 } | |
1708 | |
1709 // Is this a variable? | |
1710 if ($string{$i} == '{' && $string{$i + 1} == '%' && $string{$i + 2} == '$') | |
1711 { | |
1712 // Grab the variable. | |
1713 preg_match('~\{%([\$A-Za-z0-9\'\[\]_-]+)%\}~', substr($string, $i), $matches); | |
1714 if (!empty($matches[1])) | |
1715 { | |
1716 if ($in_string == 1) | |
1717 $new_string .= '\' . '; | |
1718 elseif ($new_string) | |
1719 $new_string .= ' . '; | |
1720 | |
1721 $new_string .= $matches[1]; | |
1722 $i += strlen($matches[1]) + 3; | |
1723 $in_string = 0; | |
1724 } | |
1725 | |
1726 continue; | |
1727 } | |
1728 // Is this a lt sign? | |
1729 elseif ($string{$i} == '<') | |
1730 { | |
1731 // Probably HTML? | |
1732 if ($string{$i + 1} != ' ') | |
1733 $in_html = true; | |
1734 // Assume we need an entity... | |
1735 else | |
1736 { | |
1737 $new_string .= '<'; | |
1738 continue; | |
1739 } | |
1740 } | |
1741 // What about gt? | |
1742 elseif ($string{$i} == '>') | |
1743 { | |
1744 // Will it be HTML? | |
1745 if ($in_html) | |
1746 $in_html = false; | |
1747 // Otherwise we need an entity... | |
1748 else | |
1749 { | |
1750 $new_string .= '>'; | |
1751 continue; | |
1752 } | |
1753 } | |
1754 // Is it a slash? If so escape it... | |
1755 if ($string{$i} == '\\') | |
1756 $new_string .= '\\'; | |
1757 // The infamous double quote? | |
1758 elseif ($string{$i} == '"') | |
1759 { | |
1760 // If we're in HTML we leave it as a quote - otherwise we entity it. | |
1761 if (!$in_html) | |
1762 { | |
1763 $new_string .= '"'; | |
1764 continue; | |
1765 } | |
1766 } | |
1767 // A single quote? | |
1768 elseif ($string{$i} == '\'') | |
1769 { | |
1770 // Must be in a string so escape it. | |
1771 $new_string .= '\\'; | |
1772 } | |
1773 | |
1774 // Finally add the character to the string! | |
1775 $new_string .= $string{$i}; | |
1776 } | |
1777 | |
1778 // If we ended as a string then close it off. | |
1779 if ($in_string == 1) | |
1780 $new_string .= '\''; | |
1781 elseif ($in_string == 2) | |
1782 $new_string .= '"'; | |
1783 } | |
1784 | |
1785 return $new_string; | |
1786 } | |
1787 | |
1788 // Helper function, it sets up the context for the manage server settings. | |
1789 function prepareServerSettingsContext(&$config_vars) | |
1790 { | |
1791 global $context, $modSettings; | |
1792 | |
1793 $context['config_vars'] = array(); | |
1794 foreach ($config_vars as $identifier => $config_var) | |
1795 { | |
1796 if (!is_array($config_var) || !isset($config_var[1])) | |
1797 $context['config_vars'][] = $config_var; | |
1798 else | |
1799 { | |
1800 $varname = $config_var[0]; | |
1801 global $$varname; | |
1802 | |
1803 $context['config_vars'][] = array( | |
1804 'label' => $config_var[1], | |
1805 'help' => isset($config_var[5]) ? $config_var[5] : '', | |
1806 'type' => $config_var[3], | |
1807 'size' => empty($config_var[4]) ? 0 : $config_var[4], | |
1808 'data' => isset($config_var[4]) && is_array($config_var[4]) ? $config_var[4] : array(), | |
1809 'name' => $config_var[0], | |
1810 'value' => $config_var[2] == 'file' ? htmlspecialchars($$varname) : (isset($modSettings[$config_var[0]]) ? htmlspecialchars($modSettings[$config_var[0]]) : (in_array($config_var[3], array('int', 'float')) ? 0 : '')), | |
1811 'disabled' => !empty($context['settings_not_writable']) || !empty($config_var['disabled']), | |
1812 'invalid' => false, | |
1813 'javascript' => '', | |
1814 'preinput' => '', | |
1815 'postinput' => '', | |
1816 ); | |
1817 } | |
1818 } | |
1819 } | |
1820 | |
1821 // Helper function, it sets up the context for database settings. | |
1822 function prepareDBSettingContext(&$config_vars) | |
1823 { | |
1824 global $txt, $helptxt, $context, $modSettings, $sourcedir; | |
1825 | |
1826 loadLanguage('Help'); | |
1827 | |
1828 $context['config_vars'] = array(); | |
1829 $inlinePermissions = array(); | |
1830 $bbcChoice = array(); | |
1831 foreach ($config_vars as $config_var) | |
1832 { | |
1833 // HR? | |
1834 if (!is_array($config_var)) | |
1835 $context['config_vars'][] = $config_var; | |
1836 else | |
1837 { | |
1838 // If it has no name it doesn't have any purpose! | |
1839 if (empty($config_var[1])) | |
1840 continue; | |
1841 | |
1842 // Special case for inline permissions | |
1843 if ($config_var[0] == 'permissions' && allowedTo('manage_permissions')) | |
1844 $inlinePermissions[] = $config_var[1]; | |
1845 elseif ($config_var[0] == 'permissions') | |
1846 continue; | |
1847 | |
1848 // Are we showing the BBC selection box? | |
1849 if ($config_var[0] == 'bbc') | |
1850 $bbcChoice[] = $config_var[1]; | |
1851 | |
1852 $context['config_vars'][$config_var[1]] = array( | |
1853 'label' => isset($config_var['text_label']) ? $config_var['text_label'] : (isset($txt[$config_var[1]]) ? $txt[$config_var[1]] : (isset($config_var[3]) && !is_array($config_var[3]) ? $config_var[3] : '')), | |
1854 'help' => isset($helptxt[$config_var[1]]) ? $config_var[1] : '', | |
1855 'type' => $config_var[0], | |
1856 'size' => !empty($config_var[2]) && !is_array($config_var[2]) ? $config_var[2] : (in_array($config_var[0], array('int', 'float')) ? 6 : 0), | |
1857 'data' => array(), | |
1858 'name' => $config_var[1], | |
1859 'value' => isset($modSettings[$config_var[1]]) ? ($config_var[0] == 'select' ? $modSettings[$config_var[1]] : htmlspecialchars($modSettings[$config_var[1]])) : (in_array($config_var[0], array('int', 'float')) ? 0 : ''), | |
1860 'disabled' => false, | |
1861 'invalid' => !empty($config_var['invalid']), | |
1862 'javascript' => '', | |
1863 'var_message' => !empty($config_var['message']) && isset($txt[$config_var['message']]) ? $txt[$config_var['message']] : '', | |
1864 'preinput' => isset($config_var['preinput']) ? $config_var['preinput'] : '', | |
1865 'postinput' => isset($config_var['postinput']) ? $config_var['postinput'] : '', | |
1866 ); | |
1867 | |
1868 // If this is a select box handle any data. | |
1869 if (!empty($config_var[2]) && is_array($config_var[2])) | |
1870 { | |
1871 // If we allow multiple selections, we need to adjust a few things. | |
1872 if ($config_var[0] == 'select' && !empty($config_var['multiple'])) | |
1873 { | |
1874 $context['config_vars'][$config_var[1]]['name'] .= '[]'; | |
1875 $context['config_vars'][$config_var[1]]['value'] = unserialize($context['config_vars'][$config_var[1]]['value']); | |
1876 } | |
1877 | |
1878 // If it's associative | |
1879 if (isset($config_var[2][0]) && is_array($config_var[2][0])) | |
1880 $context['config_vars'][$config_var[1]]['data'] = $config_var[2]; | |
1881 else | |
1882 { | |
1883 foreach ($config_var[2] as $key => $item) | |
1884 $context['config_vars'][$config_var[1]]['data'][] = array($key, $item); | |
1885 } | |
1886 } | |
1887 | |
1888 // Finally allow overrides - and some final cleanups. | |
1889 foreach ($config_var as $k => $v) | |
1890 { | |
1891 if (!is_numeric($k)) | |
1892 { | |
1893 if (substr($k, 0, 2) == 'on') | |
1894 $context['config_vars'][$config_var[1]]['javascript'] .= ' ' . $k . '="' . $v . '"'; | |
1895 else | |
1896 $context['config_vars'][$config_var[1]][$k] = $v; | |
1897 } | |
1898 | |
1899 // See if there are any other labels that might fit? | |
1900 if (isset($txt['setting_' . $config_var[1]])) | |
1901 $context['config_vars'][$config_var[1]]['label'] = $txt['setting_' . $config_var[1]]; | |
1902 elseif (isset($txt['groups_' . $config_var[1]])) | |
1903 $context['config_vars'][$config_var[1]]['label'] = $txt['groups_' . $config_var[1]]; | |
1904 } | |
1905 | |
1906 // Set the subtext in case it's part of the label. | |
1907 // !!! Temporary. Preventing divs inside label tags. | |
1908 $divPos = strpos($context['config_vars'][$config_var[1]]['label'], '<div'); | |
1909 if ($divPos !== false) | |
1910 { | |
1911 $context['config_vars'][$config_var[1]]['subtext'] = preg_replace('~</?div[^>]*>~', '', substr($context['config_vars'][$config_var[1]]['label'], $divPos)); | |
1912 $context['config_vars'][$config_var[1]]['label'] = substr($context['config_vars'][$config_var[1]]['label'], 0, $divPos); | |
1913 } | |
1914 } | |
1915 } | |
1916 | |
1917 // If we have inline permissions we need to prep them. | |
1918 if (!empty($inlinePermissions) && allowedTo('manage_permissions')) | |
1919 { | |
1920 require_once($sourcedir . '/ManagePermissions.php'); | |
1921 init_inline_permissions($inlinePermissions, isset($context['permissions_excluded']) ? $context['permissions_excluded'] : array()); | |
1922 } | |
1923 | |
1924 // What about any BBC selection boxes? | |
1925 if (!empty($bbcChoice)) | |
1926 { | |
1927 // What are the options, eh? | |
1928 $temp = parse_bbc(false); | |
1929 $bbcTags = array(); | |
1930 foreach ($temp as $tag) | |
1931 $bbcTags[] = $tag['tag']; | |
1932 | |
1933 $bbcTags = array_unique($bbcTags); | |
1934 $totalTags = count($bbcTags); | |
1935 | |
1936 // The number of columns we want to show the BBC tags in. | |
1937 $numColumns = isset($context['num_bbc_columns']) ? $context['num_bbc_columns'] : 3; | |
1938 | |
1939 // Start working out the context stuff. | |
1940 $context['bbc_columns'] = array(); | |
1941 $tagsPerColumn = ceil($totalTags / $numColumns); | |
1942 | |
1943 $col = 0; $i = 0; | |
1944 foreach ($bbcTags as $tag) | |
1945 { | |
1946 if ($i % $tagsPerColumn == 0 && $i != 0) | |
1947 $col++; | |
1948 | |
1949 $context['bbc_columns'][$col][] = array( | |
1950 'tag' => $tag, | |
1951 // !!! 'tag_' . ? | |
1952 'show_help' => isset($helptxt[$tag]), | |
1953 ); | |
1954 | |
1955 $i++; | |
1956 } | |
1957 | |
1958 // Now put whatever BBC options we may have into context too! | |
1959 $context['bbc_sections'] = array(); | |
1960 foreach ($bbcChoice as $bbc) | |
1961 { | |
1962 $context['bbc_sections'][$bbc] = array( | |
1963 'title' => isset($txt['bbc_title_' . $bbc]) ? $txt['bbc_title_' . $bbc] : $txt['bbcTagsToUse_select'], | |
1964 'disabled' => empty($modSettings['bbc_disabled_' . $bbc]) ? array() : $modSettings['bbc_disabled_' . $bbc], | |
1965 'all_selected' => empty($modSettings['bbc_disabled_' . $bbc]), | |
1966 ); | |
1967 } | |
1968 } | |
1969 } | |
1970 | |
1971 // Helper function. Saves settings by putting them in Settings.php or saving them in the settings table. | |
1972 function saveSettings(&$config_vars) | |
1973 { | |
1974 global $boarddir, $sc, $cookiename, $modSettings, $user_settings; | |
1975 global $sourcedir, $context, $cachedir; | |
1976 | |
1977 // Fix the darn stupid cookiename! (more may not be allowed, but these for sure!) | |
1978 if (isset($_POST['cookiename'])) | |
1979 $_POST['cookiename'] = preg_replace('~[,;\s\.$]+~' . ($context['utf8'] ? 'u' : ''), '', $_POST['cookiename']); | |
1980 | |
1981 // Fix the forum's URL if necessary. | |
1982 if (isset($_POST['boardurl'])) | |
1983 { | |
1984 if (substr($_POST['boardurl'], -10) == '/index.php') | |
1985 $_POST['boardurl'] = substr($_POST['boardurl'], 0, -10); | |
1986 elseif (substr($_POST['boardurl'], -1) == '/') | |
1987 $_POST['boardurl'] = substr($_POST['boardurl'], 0, -1); | |
1988 if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://') | |
1989 $_POST['boardurl'] = 'http://' . $_POST['boardurl']; | |
1990 } | |
1991 | |
1992 // Any passwords? | |
1993 $config_passwords = array( | |
1994 'db_passwd', | |
1995 'ssi_db_passwd', | |
1996 ); | |
1997 | |
1998 // All the strings to write. | |
1999 $config_strs = array( | |
2000 'mtitle', 'mmessage', | |
2001 'language', 'mbname', 'boardurl', | |
2002 'cookiename', | |
2003 'webmaster_email', | |
2004 'db_name', 'db_user', 'db_server', 'db_prefix', 'ssi_db_user', | |
2005 'boarddir', 'sourcedir', 'cachedir', | |
2006 ); | |
2007 // All the numeric variables. | |
2008 $config_ints = array( | |
2009 ); | |
2010 // All the checkboxes. | |
2011 $config_bools = array( | |
2012 'db_persist', 'db_error_send', | |
2013 'maintenance', | |
2014 ); | |
2015 | |
2016 // Now sort everything into a big array, and figure out arrays and etc. | |
2017 $new_settings = array(); | |
2018 foreach ($config_passwords as $config_var) | |
2019 { | |
2020 if (isset($_POST[$config_var][1]) && $_POST[$config_var][0] == $_POST[$config_var][1]) | |
2021 $new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var][0], '\'\\') . '\''; | |
2022 } | |
2023 foreach ($config_strs as $config_var) | |
2024 { | |
2025 if (isset($_POST[$config_var])) | |
2026 $new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var], '\'\\') . '\''; | |
2027 } | |
2028 foreach ($config_ints as $config_var) | |
2029 { | |
2030 if (isset($_POST[$config_var])) | |
2031 $new_settings[$config_var] = (int) $_POST[$config_var]; | |
2032 } | |
2033 foreach ($config_bools as $key) | |
2034 { | |
2035 if (!empty($_POST[$key])) | |
2036 $new_settings[$key] = '1'; | |
2037 else | |
2038 $new_settings[$key] = '0'; | |
2039 } | |
2040 | |
2041 // Save the relevant settings in the Settings.php file. | |
2042 require_once($sourcedir . '/Subs-Admin.php'); | |
2043 updateSettingsFile($new_settings); | |
2044 | |
2045 // Now loopt through the remaining (database-based) settings. | |
2046 $new_settings = array(); | |
2047 foreach ($config_vars as $config_var) | |
2048 { | |
2049 // We just saved the file-based settings, so skip their definitions. | |
2050 if (!is_array($config_var) || $config_var[2] == 'file') | |
2051 continue; | |
2052 | |
2053 // Rewrite the definition a bit. | |
2054 $new_settings[] = array($config_var[3], $config_var[0]); | |
2055 } | |
2056 | |
2057 // Save the new database-based settings, if any. | |
2058 if (!empty($new_settings)) | |
2059 saveDBSettings($new_settings); | |
2060 } | |
2061 | |
2062 // Helper function for saving database settings. | |
2063 function saveDBSettings(&$config_vars) | |
2064 { | |
2065 global $sourcedir, $context; | |
2066 | |
2067 $inlinePermissions = array(); | |
2068 foreach ($config_vars as $var) | |
2069 { | |
2070 if (!isset($var[1]) || (!isset($_POST[$var[1]]) && $var[0] != 'check' && $var[0] != 'permissions' && ($var[0] != 'bbc' || !isset($_POST[$var[1] . '_enabledTags'])))) | |
2071 continue; | |
2072 | |
2073 // Checkboxes! | |
2074 elseif ($var[0] == 'check') | |
2075 $setArray[$var[1]] = !empty($_POST[$var[1]]) ? '1' : '0'; | |
2076 // Select boxes! | |
2077 elseif ($var[0] == 'select' && in_array($_POST[$var[1]], array_keys($var[2]))) | |
2078 $setArray[$var[1]] = $_POST[$var[1]]; | |
2079 elseif ($var[0] == 'select' && !empty($var['multiple']) && array_intersect($_POST[$var[1]], array_keys($var[2])) != array()) | |
2080 { | |
2081 // For security purposes we validate this line by line. | |
2082 $options = array(); | |
2083 foreach ($_POST[$var[1]] as $invar) | |
2084 if (in_array($invar, array_keys($var[2]))) | |
2085 $options[] = $invar; | |
2086 | |
2087 $setArray[$var[1]] = serialize($options); | |
2088 } | |
2089 // Integers! | |
2090 elseif ($var[0] == 'int') | |
2091 $setArray[$var[1]] = (int) $_POST[$var[1]]; | |
2092 // Floating point! | |
2093 elseif ($var[0] == 'float') | |
2094 $setArray[$var[1]] = (float) $_POST[$var[1]]; | |
2095 // Text! | |
2096 elseif ($var[0] == 'text' || $var[0] == 'large_text') | |
2097 $setArray[$var[1]] = $_POST[$var[1]]; | |
2098 // Passwords! | |
2099 elseif ($var[0] == 'password') | |
2100 { | |
2101 if (isset($_POST[$var[1]][1]) && $_POST[$var[1]][0] == $_POST[$var[1]][1]) | |
2102 $setArray[$var[1]] = $_POST[$var[1]][0]; | |
2103 } | |
2104 // BBC. | |
2105 elseif ($var[0] == 'bbc') | |
2106 { | |
2107 | |
2108 $bbcTags = array(); | |
2109 foreach (parse_bbc(false) as $tag) | |
2110 $bbcTags[] = $tag['tag']; | |
2111 | |
2112 if (!isset($_POST[$var[1] . '_enabledTags'])) | |
2113 $_POST[$var[1] . '_enabledTags'] = array(); | |
2114 elseif (!is_array($_POST[$var[1] . '_enabledTags'])) | |
2115 $_POST[$var[1] . '_enabledTags'] = array($_POST[$var[1] . '_enabledTags']); | |
2116 | |
2117 $setArray[$var[1]] = implode(',', array_diff($bbcTags, $_POST[$var[1] . '_enabledTags'])); | |
2118 } | |
2119 // Permissions? | |
2120 elseif ($var[0] == 'permissions') | |
2121 $inlinePermissions[] = $var[1]; | |
2122 } | |
2123 | |
2124 if (!empty($setArray)) | |
2125 updateSettings($setArray); | |
2126 | |
2127 // If we have inline permissions we need to save them. | |
2128 if (!empty($inlinePermissions) && allowedTo('manage_permissions')) | |
2129 { | |
2130 require_once($sourcedir . '/ManagePermissions.php'); | |
2131 save_inline_permissions($inlinePermissions); | |
2132 } | |
2133 } | |
2134 | |
2135 ?> |