Passed
Pull Request — development (#3540)
by Emanuele
07:11
created

ManageLanguages::action_editlang()   B

Complexity

Conditions 7
Paths 24

Size

Total Lines 81
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 47
nc 24
nop 0
dl 0
loc 81
ccs 0
cts 39
cp 0
crap 56
rs 8.223
c 1
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file handles the administration of languages tasks.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
namespace ElkArte\AdminController;
18
19
use ElkArte\AbstractController;
20
use ElkArte\Action;
21
use ElkArte\Cache\Cache;
22
use ElkArte\Exceptions\Exception;
23
use ElkArte\SettingsForm\SettingsForm;
24
use ElkArte\Languages\Txt;
25
use ElkArte\Util;
26
use ElkArte\Languages\Editor as LangEditor;
27
use ElkArte\Languages\Loader as LangLoader;
28
29
/**
30
 * Manage languages controller class.
31
 *
32
 * @package Languages
33
 */
34
class ManageLanguages extends AbstractController
35
{
36
	/**
37
	 * This is the main function for the languages area.
38
	 *
39
	 * What it does:
40
	 *
41
	 * - It dispatches the requests.
42
	 * - Loads the ManageLanguages template. (sub-actions will use it)
43
	 *
44
	 * @event integrate_sa_manage_languages Used to add more sub actions
45
	 * @uses ManageSettings language file
46
	 * @see  \ElkArte\AbstractController::action_index()
47
	 */
48
	public function action_index()
49
	{
50
		global $context, $txt;
51
52
		theme()->getTemplates()->load('ManageLanguages');
53
		Txt::load('ManageSettings');
54
55
		$subActions = array(
56
			'edit' => array($this, 'action_edit', 'permission' => 'admin_forum'),
57
			'settings' => array($this, 'action_languageSettings_display', 'permission' => 'admin_forum'),
58
			'downloadlang' => array($this, 'action_downloadlang', 'permission' => 'admin_forum'),
59
			'editlang' => array($this, 'action_editlang', 'permission' => 'admin_forum'),
60
		);
61
62
		// Get ready for action
63
		$action = new Action('manage_languages');
64
65
		// Load up all the tabs...
66
		$context[$context['admin_menu_name']]['tab_data'] = array(
67
			'title' => $txt['language_configuration'],
68
			'description' => $txt['language_description'],
69
		);
70
71
		// By default we're managing languages, call integrate_sa_manage_languages
72
		$subAction = $action->initialize($subActions, 'edit');
73
74
		// Some final bits
75
		$context['sub_action'] = $subAction;
76
		$context['page_title'] = $txt['edit_languages'];
77
		$context['sub_template'] = 'show_settings';
78
79
		// Call the right function for this sub-action.
80
		$action->dispatch($subAction);
81
	}
82
83
	/**
84
	 * Interface for adding a new language.
85
	 *
86
	 * @uses ManageLanguages template, add_language sub-template.
87
	 */
88
	public function action_add()
89
	{
90
		global $context, $txt;
91
92
		// Are we searching for new languages on the site?
93
		if (!empty($this->_req->post->lang_add_sub))
94
		{
95
			// Need fetch_web_data.
96
			require_once(SUBSDIR . '/Package.subs.php');
97
			require_once(SUBSDIR . '/Language.subs.php');
98
99
			$context['elk_search_term'] = $this->_req->getPost('lang_add', 'trim|htmlspecialchars[ENT_COMPAT]');
100
101
			$listOptions = array(
102
				'id' => 'languages',
103
				'get_items' => array(
104
					'function' => 'list_getLanguagesList',
105
				),
106
				'columns' => array(
107
					'name' => array(
108
						'header' => array(
109
							'value' => $txt['name'],
110
						),
111
						'data' => array(
112
							'db' => 'name',
113
						),
114
					),
115
					'description' => array(
116
						'header' => array(
117
							'value' => $txt['add_language_elk_desc'],
118
						),
119
						'data' => array(
120
							'db' => 'description',
121
						),
122
					),
123
					'version' => array(
124
						'header' => array(
125
							'value' => $txt['add_language_elk_version'],
126
						),
127
						'data' => array(
128
							'db' => 'version',
129
						),
130
					),
131
					'utf8' => array(
132
						'header' => array(
133
							'value' => $txt['add_language_elk_utf8'],
134
						),
135
						'data' => array(
136
							'db' => 'utf8',
137
						),
138
					),
139
					'install_link' => array(
140
						'header' => array(
141
							'value' => $txt['add_language_elk_install'],
142
							'class' => 'centertext',
143
						),
144
						'data' => array(
145
							'db' => 'install_link',
146
							'class' => 'centertext',
147
						),
148
					),
149
				),
150
			);
151
152
			createList($listOptions);
153
		}
154
155
		$context['sub_template'] = 'add_language';
156
	}
157
158
	/**
159
	 * This lists all the current languages and allows editing of them.
160
	 */
161
	public function action_edit()
162
	{
163
		global $txt, $context, $language;
164
165
		require_once(SUBSDIR . '/Language.subs.php');
166
167
		// Setting a new default?
168
		if (!empty($this->_req->post->set_default) && !empty($this->_req->post->def_language))
169
		{
170
			checkSession();
171
			validateToken('admin-lang');
172
173
			$lang_exists = false;
174
			$available_langs = getLanguages();
175
			foreach ($available_langs as $lang)
176
			{
177
				if ($this->_req->post->def_language === $lang['filename'])
178
				{
179
					$lang_exists = true;
180
					break;
181
				}
182
			}
183
184
			if ($this->_req->post->def_language !== $language && $lang_exists)
185
			{
186
				$language = $this->_req->post->def_language;
187
				$this->updateLanguage($language);
188
				redirectexit('action=admin;area=languages;sa=edit');
189
			}
190
		}
191
192
		// Create another one time token here.
193
		createToken('admin-lang');
194
		createToken('admin-ssc');
195
196
		$listOptions = array(
197
			'id' => 'language_list',
198
			'items_per_page' => 20,
199
			'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'languages']),
200
			'title' => $txt['edit_languages'],
201
			'data_check' => array(
202
				'class' => function ($rowData) {
203
					if ($rowData['default'])
204
					{
205
						return 'highlight2';
206
					}
207
					else
208
					{
209
						return '';
210
					}
211
				},
212
			),
213
			'get_items' => array(
214
				'function' => 'list_getLanguages',
215
			),
216
			'get_count' => array(
217
				'function' => 'list_getNumLanguages',
218
			),
219
			'columns' => array(
220
				'default' => array(
221
					'header' => array(
222
						'value' => $txt['languages_default'],
223
						'class' => 'centertext',
224
					),
225
					'data' => array(
226
						'function' => function ($rowData) {
227
							return '<input type="radio" name="def_language" value="' . $rowData['id'] . '" ' . ($rowData['default'] ? 'checked="checked"' : '') . ' class="input_radio" />';
228
						},
229
						'style' => 'width: 8%;',
230
						'class' => 'centertext',
231
					),
232
				),
233
				'name' => array(
234
					'header' => array(
235
						'value' => $txt['languages_lang_name'],
236
					),
237
					'data' => array(
238
						'function' => function ($rowData) {
239
							return sprintf('<a href="%1$s">%2$s<i class="icon icon-small i-modify"></i></a>', getUrl('admin', ['action' => 'admin', 'area' => 'languages', 'sa' => 'editlang', 'lid' => $rowData['id']]), $rowData['name']);
240
						},
241
					),
242
				),
243
				'count' => array(
244
					'header' => array(
245
						'value' => $txt['languages_users'],
246
					),
247
					'data' => array(
248
						'db_htmlsafe' => 'count',
249
					),
250
				),
251
				'locale' => array(
252
					'header' => array(
253
						'value' => $txt['languages_locale'],
254
					),
255
					'data' => array(
256
						'db_htmlsafe' => 'locale',
257
					),
258
				),
259
			),
260
			'form' => array(
261
				'href' => getUrl('admin', ['action' => 'admin', 'area' => 'languages']),
262
				'token' => 'admin-lang',
263
			),
264
			'additional_rows' => array(
265
				array(
266
					'class' => 'submitbutton',
267
					'position' => 'bottom_of_list',
268
					'value' => '
269
						<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" />
270
						<input type="submit" name="set_default" value="' . $txt['save'] . '"' . (is_writable(BOARDDIR . '/Settings.php') ? '' : ' disabled="disabled"') . ' />
271
						<input type="hidden" name="' . $context['admin-ssc_token_var'] . '" value="' . $context['admin-ssc_token'] . '" />',
272
				),
273
			),
274
			// For highlighting the default.
275
			'javascript' => '
276
				initHighlightSelection(\'language_list\');
277
			',
278
		);
279
280
		// Display a warning if we cannot edit the default setting.
281
		if (!is_writable(BOARDDIR . '/Settings.php'))
282
		{
283
			$listOptions['additional_rows'][] = array(
284
				'position' => 'after_title',
285
				'value' => $txt['language_settings_writable'],
286
				'class' => 'smalltext alert',
287
			);
288
		}
289
290
		createList($listOptions);
291
292
		$context['sub_template'] = 'show_list';
293
		$context['default_list'] = 'language_list';
294
	}
295
296
	/**
297
	 * Update the language in use
298
	 *
299
	 * @param string $language
300
	 */
301
	private function updateLanguage($language)
302
	{
303
		$configVars = array(
304
			array('language', '', 'file')
305
		);
306
		$configValues = array(
307
			'language' => $language
308
		);
309
		$settingsForm = new SettingsForm(SettingsForm::FILE_ADAPTER);
310
		$settingsForm->setConfigVars($configVars);
311
		$settingsForm->setConfigValues((array) $configValues);
312
		$settingsForm->save();
313
	}
314
315
	/**
316
	 * Download a language file from the website.
317
	 *
318
	 * What it does:
319
	 *
320
	 * - Requires a valid download ID ("did") in the URL.
321
	 * - Also handles installing language files.
322
	 * - Attempts to chmod things as needed.
323
	 * - Uses a standard list to display information about all the files and where they'll be put.
324
	 *
325
	 * @uses ManageLanguages template, download_language sub-template.
326
	 * @uses Admin template, show_list sub-template.
327
	 */
328
	public function action_downloadlang()
329
	{
330
		// @todo for the moment there is no facility to download packages, so better kill it here
331
		throw new Exception('no_access', false);
332
333
		Txt::load('ManageSettings');
0 ignored issues
show
Unused Code introduced by
ElkArte\Languages\Txt::load('ManageSettings') is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
334
		require_once(SUBSDIR . '/Package.subs.php');
335
336
		// Clearly we need to know what to request.
337
		if (!isset($this->_req->query->did))
338
		{
339
			throw new Exception('no_access', false);
340
		}
341
342
		// Some lovely context.
343
		$context['download_id'] = $this->_req->query->did;
344
		$context['sub_template'] = 'download_language';
345
		$context['menu_data_' . $context['admin_menu_id']]['current_subsection'] = 'add';
346
347
		// Can we actually do the installation - and do they want to?
348
		if (!empty($this->_req->post->do_install) && !empty($this->_req->post->copy_file))
349
		{
350
			checkSession('get');
351
			validateToken('admin-dlang');
352
353
			$chmod_files = array();
354
			$install_files = array();
355
356
			// Check writable status.
357
			foreach ($this->_req->post->copy_file as $file)
358
			{
359
				// Check it's not very bad.
360
				if (strpos($file, '..') !== false || (strpos($file, 'themes') !== 0 && !preg_match('~agreement\.[A-Za-z-_0-9]+\.txt$~', $file)))
361
				{
362
					throw new Exception($txt['languages_download_illegal_paths']);
363
				}
364
365
				$chmod_files[] = BOARDDIR . '/' . $file;
366
				$install_files[] = $file;
367
			}
368
369
			// Call this in case we have work to do.
370
			$file_status = create_chmod_control($chmod_files);
371
			$files_left = $file_status['files']['notwritable'];
372
373
			// Something not writable?
374
			if (!empty($files_left))
375
			{
376
				$context['error_message'] = $txt['languages_download_not_chmod'];
377
			}
378
			// Otherwise, go go go!
379
			elseif (!empty($install_files))
380
			{
381
				// @todo retrieve the language pack per naming pattern from our sites
382
				read_tgz_file('http://download.elkarte.net/fetch_language.php?version=' . urlencode(strtr(FORUM_VERSION, array('ElkArte ' => ''))) . ';fetch=' . urlencode($this->_req->query->did), BOARDDIR, false, true, $install_files);
383
384
				// Make sure the files aren't stuck in the cache.
385
				package_flush_cache();
386
				$context['install_complete'] = sprintf($txt['languages_download_complete_desc'], getUrl('admin', ['action' => 'admin', 'area' => 'languages']));
387
388
				return;
389
			}
390
		}
391
392
		// @todo Open up the old china.
393
		$archive_content = read_tgz_file('http://download.elkarte.net/fetch_language.php?version=' . urlencode(strtr(FORUM_VERSION, array('ElkArte ' => ''))) . ';fetch=' . urlencode($this->_req->query->did), null);
394
395
		if (empty($archive_content))
396
		{
397
			throw new Exception($txt['add_language_error_no_response']);
398
		}
399
400
		// Now for each of the files, let's do some *stuff*
401
		$context['files'] = array(
402
			'lang' => array(),
403
			'other' => array(),
404
		);
405
		$context['make_writable'] = array();
406
		foreach ($archive_content as $file)
407
		{
408
			$dirname = dirname($file['filename']);
409
			$filename = basename($file['filename']);
410
			$extension = substr($filename, strrpos($filename, '.') + 1);
411
412
			// Don't do anything with files we don't understand.
413
			if (!in_array($extension, array('php', 'jpg', 'gif', 'jpeg', 'png', 'txt')))
414
			{
415
				continue;
416
			}
417
418
			// Basic data.
419
			$context_data = array(
420
				'name' => $filename,
421
				'destination' => BOARDDIR . '/' . $file['filename'],
422
				'generaldest' => $file['filename'],
423
				'size' => $file['size'],
424
				// Does chmod status allow the copy?
425
				'writable' => false,
426
				// Should we suggest they copy this file?
427
				'default_copy' => true,
428
				// Does the file already exist, if so is it same or different?
429
				'exists' => false,
430
			);
431
432
			// Does the file exist, is it different and can we overwrite?
433
			if (file_exists(BOARDDIR . '/' . $file['filename']))
434
			{
435
				if (is_writable(BOARDDIR . '/' . $file['filename']))
436
				{
437
					$context_data['writable'] = true;
438
				}
439
440
				// Finally, do we actually think the content has changed?
441
				if ($file['size'] == filesize(BOARDDIR . '/' . $file['filename']) && $file['md5'] === md5_file(BOARDDIR . '/' . $file['filename']))
442
				{
443
					$context_data['exists'] = 'same';
444
					$context_data['default_copy'] = false;
445
				}
446
				// Attempt to discover newline character differences.
447
				elseif ($file['md5'] === md5(preg_replace("~[\r]?\n~", "\r\n", file_get_contents(BOARDDIR . '/' . $file['filename']))))
448
				{
449
					$context_data['exists'] = 'same';
450
					$context_data['default_copy'] = false;
451
				}
452
				else
453
				{
454
					$context_data['exists'] = 'different';
455
				}
456
			}
457
			// No overwrite?
458
			elseif (is_writable(BOARDDIR . '/' . $dirname))
459
			{
460
				// Can we at least stick it in the directory...
461
				$context_data['writable'] = true;
462
			}
463
464
			// 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...
465
			if ($extension === 'php' && preg_match('~\w+\.\w+(?:-utf8)?\.php~', $filename))
466
			{
467
				$context_data += array(
468
					'version' => '??',
469
					'cur_version' => false,
470
					'version_compare' => 'newer',
471
				);
472
473
				list ($name,) = explode('.', $filename);
474
475
				// Let's get the new version, I like versions, they tell me that I'm up to date.
476
				if (preg_match('~\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '~i', $file['preview'], $match) == 1)
477
				{
478
					$context_data['version'] = $match[1];
479
				}
480
481
				// Now does the old file exist - if so what is it's version?
482
				if (file_exists(BOARDDIR . '/' . $file['filename']))
483
				{
484
					// OK - what is the current version?
485
					$fp = fopen(BOARDDIR . '/' . $file['filename'], 'rb');
486
					$header = fread($fp, 768);
487
					fclose($fp);
488
489
					// Find the version.
490
					if (preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '(?:[\s]{2}|\*/)~i', $header, $match) == 1)
491
					{
492
						$context_data['cur_version'] = $match[1];
493
494
						// How does this compare?
495
						if ($context_data['cur_version'] === $context_data['version'])
496
						{
497
							$context_data['version_compare'] = 'same';
498
						}
499
						elseif ($context_data['cur_version'] > $context_data['version'])
500
						{
501
							$context_data['version_compare'] = 'older';
502
						}
503
504
						// Don't recommend copying if the version is the same.
505
						if ($context_data['version_compare'] != 'newer')
506
						{
507
							$context_data['default_copy'] = false;
508
						}
509
					}
510
				}
511
512
				// Add the context data to the main set.
513
				$context['files']['lang'][] = $context_data;
514
			}
515
			else
516
			{
517
				// If we think it's a theme thing, work out what the theme is.
518
				if (strpos($dirname, 'themes') === 0 && preg_match('~themes[\\/]([^\\/]+)[\\/]~', $dirname, $match))
519
				{
520
					$theme_name = $match[1];
521
				}
522
				else
523
				{
524
					$theme_name = 'misc';
525
				}
526
527
				// Assume it's an image, could be an acceptance note etc but rare.
528
				$context['files']['images'][$theme_name][] = $context_data;
529
			}
530
531
			// Collect together all non-writable areas.
532
			if (!$context_data['writable'])
533
			{
534
				$context['make_writable'][] = $context_data['destination'];
535
			}
536
		}
537
538
		// So, I'm a perfectionist - let's get the theme names.
539
		$indexes = array();
540
		foreach ($context['files']['images'] as $k => $dummy)
541
		{
542
			$indexes[] = $k;
543
		}
544
545
		$context['theme_names'] = array();
546
		if (!empty($indexes))
547
		{
548
			require_once(SUBSDIR . '/Themes.subs.php');
549
			$value_data = array(
550
				'query' => array(),
551
				'params' => array(),
552
			);
553
554
			foreach ($indexes as $k => $index)
555
			{
556
				$value_data['query'][] = 'value LIKE {string:value_' . $k . '}';
557
				$value_data['params']['value_' . $k] = '%' . $index;
558
			}
559
560
			$themes = validateThemeName($indexes, $value_data);
561
562
			// Now we have the id_theme we can get the pretty description.
563
			if (!empty($themes))
564
			{
565
				$context['theme_names'] = getBasicThemeInfos($themes);
566
			}
567
		}
568
569
		// Before we go to far can we make anything writable, eh, eh?
570
		if (!empty($context['make_writable']))
571
		{
572
			// What is left to be made writable?
573
			$file_status = create_chmod_control($context['make_writable']);
574
			$context['still_not_writable'] = $file_status['files']['notwritable'];
575
576
			// Mark those which are now writable as such.
577
			foreach ($context['files'] as $type => $data)
578
			{
579
				if ($type == 'lang')
580
				{
581
					foreach ($data as $k => $file)
582
					{
583
						if (!$file['writable'] && !in_array($file['destination'], $context['still_not_writable']))
584
						{
585
							$context['files'][$type][$k]['writable'] = true;
586
						}
587
					}
588
				}
589
				else
590
				{
591
					foreach ($data as $theme => $files)
592
					{
593
						foreach ($files as $k => $file)
594
						{
595
							if (!$file['writable'] && !in_array($file['destination'], $context['still_not_writable']))
596
							{
597
								$context['files'][$type][$theme][$k]['writable'] = true;
598
							}
599
						}
600
					}
601
				}
602
			}
603
604
			// Are we going to need more language stuff?
605
			if (!empty($context['still_not_writable']))
606
			{
607
				Txt::load('Packages');
608
			}
609
		}
610
611
		// This is the list for the main files.
612
		$listOptions = array(
613
			'id' => 'lang_main_files_list',
614
			'title' => $txt['languages_download_main_files'],
615
			'get_items' => array(
616
				'function' => function () {
617
					global $context;
618
619
					return $context['files']['lang'];
620
				},
621
			),
622
			'columns' => array(
623
				'name' => array(
624
					'header' => array(
625
						'value' => $txt['languages_download_filename'],
626
					),
627
					'data' => array(
628
						'function' => function ($rowData) {
629
							global $txt;
630
631
							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'] : '');
632
						},
633
					),
634
				),
635
				'writable' => array(
636
					'header' => array(
637
						'value' => $txt['languages_download_writable'],
638
					),
639
					'data' => array(
640
						'function' => function ($rowData) {
641
							global $txt;
642
643
							return '<span class="' . ($rowData['writable'] ? 'success' : 'error') . ';">' . ($rowData['writable'] ? $txt['yes'] : $txt['no']) . '</span>';
644
						},
645
					),
646
				),
647
				'version' => array(
648
					'header' => array(
649
						'value' => $txt['languages_download_version'],
650
					),
651
					'data' => array(
652
						'function' => function ($rowData) {
653
							return '<span class="' . ($rowData['version_compare'] == 'older' ? 'error' : ($rowData['version_compare'] == 'same' ? 'softalert' : 'success')) . ';">' . $rowData['version'] . '</span>';
654
						},
655
					),
656
				),
657
				'exists' => array(
658
					'header' => array(
659
						'value' => $txt['languages_download_exists'],
660
					),
661
					'data' => array(
662
						'function' => function ($rowData) {
663
							global $txt;
664
665
							return $rowData['exists'] ? ($rowData['exists'] == 'same' ? $txt['languages_download_exists_same'] : $txt['languages_download_exists_different']) : $txt['no'];
666
						},
667
					),
668
				),
669
				'copy' => array(
670
					'header' => array(
671
						'value' => $txt['languages_download_copy'],
672
						'class' => 'centertext',
673
					),
674
					'data' => array(
675
						'function' => function ($rowData) {
676
							return '<input type="checkbox" name="copy_file[]" value="' . $rowData['generaldest'] . '" ' . ($rowData['default_copy'] ? 'checked="checked"' : '') . ' class="input_check" />';
677
						},
678
						'style' => 'width: 4%;',
679
						'class' => 'centertext',
680
					),
681
				),
682
			),
683
		);
684
685
		// Kill the cache, as it is now invalid..
686
		$cache = Cache::instance();
687
		$cache->put('known_languages', null, $cache->maxLevel(1) ? 86400 : 3600);
688
689
		createList($listOptions);
690
691
		createToken('admin-dlang');
692
	}
693
694
	/**
695
	 * Edit a particular set of language entries.
696
	 */
697
	public function action_editlang()
698
	{
699
		global $settings, $context, $txt;
700
701
		$base_lang_dir = SOURCEDIR . '/ElkArte/Languages';
702
		require_once(SUBSDIR . '/Language.subs.php');
703
		Txt::load('ManageSettings');
704
705
		// Select the languages tab.
706
		$context['menu_data_' . $context['admin_menu_id']]['current_subsection'] = 'edit';
707
		$context['page_title'] = $txt['edit_languages'];
708
		$context['sub_template'] = 'modify_language_entries';
709
710
		$context['lang_id'] = $this->_req->query->lid;
711
		$file_id = !empty($this->_req->post->tfid) ? $this->_req->post->tfid : '';
712
713
		// Clean the ID - just in case.
714
		preg_match('~([A-Za-z0-9_-]+)~', $context['lang_id'], $matches);
715
		$context['lang_id'] = $matches[1];
716
		$matches = '';
717
		preg_match('~([A-Za-z0-9_-]+)~', $file_id, $matches);
0 ignored issues
show
Bug introduced by
$matches of type string is incompatible with the type string[] expected by parameter $matches of preg_match(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

717
		preg_match('~([A-Za-z0-9_-]+)~', $file_id, /** @scrutinizer ignore-type */ $matches);
Loading history...
718
		$file_id = ucfirst($matches[1] ?? '');
719
720
		// This will be where we look
721
		$lang_dirs = glob($base_lang_dir . '/*', GLOB_ONLYDIR);
722
		$images_dirs = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $images_dirs is dead and can be removed.
Loading history...
723
724
		$current_file = $file_id ? $base_lang_dir . '/' . $file_id . '/' . ucfirst($context['lang_id']) . '.php' : '';
0 ignored issues
show
Unused Code introduced by
The assignment to $current_file is dead and can be removed.
Loading history...
725
726
		// Now for every theme get all the files and stick them in context!
727
		$context['possible_files'] =  array_map(function($file) use ($file_id, $txt) {
728
			return [
729
				'id' => basename($file, '.php'),
730
				'name' => $txt['lang_file_desc_' . basename($file)] ?? basename($file),
731
				'path' => $file,
732
				'selected' => $file_id == basename($file),
733
			];
734
		}, $lang_dirs);
735
736
		if ($context['lang_id'] != 'english')
737
		{
738
			$possiblePackage = findPossiblePackages($context['lang_id']);
739
			if ($possiblePackage !== false)
0 ignored issues
show
introduced by
The condition $possiblePackage !== false is always true.
Loading history...
740
			{
741
				$context['langpack_uninstall_link'] = getUrl('admin', ['action' => 'admin', 'area' => 'packages', 'sa' => 'uninstall', 'package' => $possiblePackage[1], 'pid' => $possiblePackage[0]]);
742
			}
743
		}
744
745
		// Quickly load index language entries.
746
		$mtxt = [];
747
		$new_lang = new LangLoader($context['lang_id'], $mtxt, database());
748
		$new_lang->load('Index', true);
749
750
		// Setup the primary settings context.
751
		$context['primary_settings'] = array(
752
			'name' => Util::ucwords(strtr($context['lang_id'], array('_' => ' ', '-utf8' => ''))),
753
			'locale' => $mtxt['lang_locale'],
754
			'dictionary' => $mtxt['lang_dictionary'],
755
			'spelling' => $mtxt['lang_spelling'],
756
			'rtl' => $mtxt['lang_rtl'],
757
		);
758
759
		// Quickly load index language entries.
760
		$edit_lang = new LangEditor($context['lang_id'], database());
761
		$edit_lang->load($file_id, true);
0 ignored issues
show
Unused Code introduced by
The call to ElkArte\Languages\Editor::load() has too many arguments starting with true. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

761
		$edit_lang->/** @scrutinizer ignore-call */ 
762
              load($file_id, true);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
762
763
		$context['file_entries'] = $edit_lang->getForEditing();
764
765
		// Are we saving?
766
		$save_strings = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $save_strings is dead and can be removed.
Loading history...
767
		if (isset($this->_req->post->save_entries) && !empty($this->_req->post->entry))
768
		{
769
			checkSession();
770
			validateToken('admin-mlang');
771
772
			$edit_lang->save($file_id, $this->_req->post->entry);
773
774
			redirectexit('action=admin;area=languages;sa=editlang;lid=' . $context['lang_id']);
775
		}
776
777
		createToken('admin-mlang');
778
	}
779
780
	/**
781
	 * Edit language related settings.
782
	 *
783
	 * - Accessed by ?action=admin;area=languages;sa=settings
784
	 * - This method handles the display, allows to edit, and saves the result
785
	 * for the _languageSettings form.
786
	 *
787
	 * @event integrate_save_language_settings
788
	 */
789
	public function action_languageSettings_display()
790
	{
791
		global $context, $txt;
792
793
		// Initialize the form
794
		$settingsForm = new SettingsForm(SettingsForm::FILE_ADAPTER);
795
796
		// Initialize it with our settings
797
		$settingsForm->setConfigVars($this->_settings());
798
799
		// Warn the user if the backup of Settings.php failed.
800
		$settings_not_writable = !is_writable(BOARDDIR . '/Settings.php');
801
		$settings_backup_fail = !@is_writable(BOARDDIR . '/Settings_bak.php') || !@copy(BOARDDIR . '/Settings.php', BOARDDIR . '/Settings_bak.php');
802
803
		// Saving settings?
804
		if (isset($this->_req->query->save))
805
		{
806
			checkSession();
807
808
			call_integration_hook('integrate_save_language_settings');
809
810
			$settingsForm->setConfigValues((array) $this->_req->post);
811
			$settingsForm->save();
812
			redirectexit('action=admin;area=languages;sa=settings');
813
		}
814
815
		// Setup the template stuff.
816
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'languages', 'sa' => 'settings', 'save']);
817
		$context['settings_title'] = $txt['language_settings'];
818
		$context['save_disabled'] = $settings_not_writable;
819
820
		if ($settings_not_writable)
821
		{
822
			$context['error_type'] = 'notice';
823
			$context['settings_message'] = $txt['settings_not_writable'];
824
		}
825
		elseif ($settings_backup_fail)
826
		{
827
			$context['error_type'] = 'notice';
828
			$context['settings_message'] = $txt['admin_backup_fail'];
829
		}
830
831
		// Fill the config array in contextual data for the template.
832
		$settingsForm->prepare();
833
	}
834
835
	/**
836
	 * Load up all of the language settings
837
	 *
838
	 * @event integrate_modify_language_settings Use to add new config options
839
	 */
840
	private function _settings()
841
	{
842
		global $txt;
843
844
		// Warn the user if the backup of Settings.php failed.
845
		$settings_not_writable = !is_writable(BOARDDIR . '/Settings.php');
846
847
		$config_vars = array(
848
			'language' => array('language', $txt['default_language'], 'file', 'select', array(), null, 'disabled' => $settings_not_writable),
849
			array('userLanguage', $txt['userLanguage'], 'db', 'check', null, 'userLanguage'),
850
		);
851
852
		call_integration_hook('integrate_modify_language_settings', array(&$config_vars));
853
854
		// Get our languages. No cache.
855
		$languages = getLanguages(false);
856
		foreach ($languages as $lang)
857
		{
858
			$config_vars['language'][4][] = array($lang['filename'], strtr($lang['name'], array('-utf8' => ' (UTF-8)')));
859
		}
860
861
		return $config_vars;
862
	}
863
864
	/**
865
	 * Return the form settings for use in admin search
866
	 */
867
	public function settings_search()
868
	{
869
		return $this->_settings();
870
	}
871
}
872