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 .= '&lt;';
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 .= '&gt;';
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 .= '&quot;';
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 ?>