ManageLanguages::action_languageSettings_display()   A
last analyzed

Complexity

Conditions 5
Paths 12

Size

Total Lines 45
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 22
c 0
b 0
f 0
nc 12
nop 0
dl 0
loc 45
ccs 0
cts 0
cp 0
crap 30
rs 9.2568
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\Exceptions\Exception;
22
use ElkArte\Helper\FileFunctions;
23
use ElkArte\Helper\Util;
24
use ElkArte\Languages\Editor as LangEditor;
25
use ElkArte\Languages\Loader as LangLoader;
26
use ElkArte\Languages\Txt;
27
use ElkArte\SettingsForm\SettingsForm;
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 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
		// By default we're managing languages, call integrate_sa_manage_languages
66
		$subAction = $action->initialize($subActions, 'edit');
67
68
		// Some final bits
69
		$context['sub_action'] = $subAction;
70
		$context['page_title'] = $txt['edit_languages'];
71
		$context['sub_template'] = 'show_settings';
72
73
		// Load up all the tabs...
74
		$context[$context['admin_menu_name']]['object']->prepareTabData([
75
			'title' => 'language_configuration',
76
			'description' => 'language_description',
77
		]);
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
		$fileFunc = FileFunctions::instance();
167
168
		// Setting a new default?
169
		if (!empty($this->_req->post->set_default) && !empty($this->_req->post->def_language))
170
		{
171
			checkSession();
172
			validateToken('admin-lang');
173
174
			$lang_exists = false;
175
			$available_langs = getLanguages();
176
			foreach ($available_langs as $lang)
177
			{
178
				if ($this->_req->post->def_language === $lang['filename'])
179
				{
180
					$lang_exists = true;
181
					break;
182
				}
183
			}
184
185
			if ($this->_req->post->def_language !== $language && $lang_exists)
186
			{
187
				$language = $this->_req->post->def_language;
188
				$this->updateLanguage($language);
189
				redirectexit('action=admin;area=languages;sa=edit');
190
			}
191
		}
192
193
		// Create another one time token here.
194
		createToken('admin-lang');
195
		createToken('admin-ssc');
196
197
		$listOptions = array(
198
			'id' => 'language_list',
199
			'items_per_page' => 20,
200
			'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'languages']),
201
			'title' => $txt['edit_languages'],
202
			'get_items' => array(
203
				'function' => 'list_getLanguages',
204
			),
205
			'get_count' => array(
206
				'function' => 'list_getNumLanguages',
207
			),
208
			'columns' => array(
209
				'default' => array(
210
					'header' => array(
211
						'value' => $txt['languages_default'],
212
						'class' => 'centertext',
213
					),
214
					'data' => array(
215
						'function' => static fn($rowData) => '<input type="radio" name="def_language" value="' . $rowData['id'] . '" ' . ($rowData['default'] ? 'checked="checked"' : '') . ' class="input_radio" />',
216
						'style' => 'width: 8%;',
217
						'class' => 'centertext',
218
					),
219
				),
220
				'name' => array(
221
					'header' => array(
222
						'value' => $txt['languages_lang_name'],
223
					),
224
					'data' => array(
225
						'function' => static fn($rowData) => 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']),
226
					),
227
				),
228
				'count' => array(
229
					'header' => array(
230
						'value' => $txt['languages_users'],
231
					),
232
					'data' => array(
233
						'db_htmlsafe' => 'count',
234
					),
235
				),
236
				'locale' => array(
237
					'header' => array(
238
						'value' => $txt['languages_locale'],
239
					),
240
					'data' => array(
241
						'db_htmlsafe' => 'locale',
242
					),
243
				),
244
			),
245
			'form' => array(
246
				'href' => getUrl('admin', ['action' => 'admin', 'area' => 'languages']),
247
				'token' => 'admin-lang',
248
			),
249
			'additional_rows' => array(
250
				array(
251
					'class' => 'submitbutton',
252
					'position' => 'bottom_of_list',
253
					'value' => '
254
						<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" />
255
						<input type="submit" name="set_default" value="' . $txt['save'] . '"' . ($fileFunc->isWritable(BOARDDIR . '/Settings.php') ? '' : ' disabled="disabled"') . ' />
256
						<input type="hidden" name="' . $context['admin-ssc_token_var'] . '" value="' . $context['admin-ssc_token'] . '" />',
257
				),
258
			),
259
		);
260
261
		// Display a warning if we cannot edit the default setting.
262
		if (!$fileFunc->isWritable(BOARDDIR . '/Settings.php'))
263
		{
264
			$listOptions['additional_rows'][] = array(
265
				'position' => 'after_title',
266
				'value' => $txt['language_settings_writable'],
267
				'class' => 'smalltext alert',
268
			);
269
		}
270
271
		createList($listOptions);
272
273
		$context['sub_template'] = 'show_list';
274
		$context['default_list'] = 'language_list';
275
	}
276
277
	/**
278
	 * Update the language in use
279
	 *
280
	 * @param string $language
281
	 */
282
	private function updateLanguage($language)
283
	{
284
		$configVars = array(
285
			array('language', '', 'file')
286
		);
287
		$configValues = array(
288
			'language' => $language
289
		);
290
		$settingsForm = new SettingsForm(SettingsForm::FILE_ADAPTER);
291
		$settingsForm->setConfigVars($configVars);
292
		$settingsForm->setConfigValues($configValues);
293
		$settingsForm->save();
294
	}
295
296
	/**
297
	 * Download a language file from the website.
298
	 *
299
	 * What it does:
300
	 *
301
	 * - Requires a valid download ID ("did") in the URL.
302
	 * - Also handles installing language files.
303
	 * - Attempts to chmod things as needed.
304
	 * - Uses a standard list to display information about all the files and where they'll be put.
305
	 *
306
	 * @uses ManageLanguages template, download_language sub-template.
307
	 * @uses Admin template, show_list sub-template.
308
	 */
309
	public function action_downloadlang()
310
	{
311
		// @todo for the moment there is no facility to download packages, so better kill it here
312
		throw new Exception('no_access', false);
313
	}
314
315
	/**
316
	 * Edit a particular set of language entries.
317
	 */
318
	public function action_editlang()
319
	{
320
		global $context, $txt;
321
322
		$base_lang_dir = LANGUAGEDIR;
323
		require_once(SUBSDIR . '/Language.subs.php');
324
		Txt::load('ManageSettings');
325
326
		// Select the languages tab.
327
		$context['menu_data_' . $context['admin_menu_id']]['current_subsection'] = 'edit';
328
		$context['page_title'] = $txt['edit_languages'];
329
		$context['sub_template'] = 'modify_language_entries';
330
331
		$context['lang_id'] = $this->_req->query->lid;
332
		$file_id = empty($this->_req->post->tfid) ? '' : $this->_req->post->tfid;
333
334
		// Clean the ID - just in case.
335
		preg_match('~([A-Za-z0-9_-]+)~', $context['lang_id'], $matches);
336
		$context['lang_id'] = $matches[1];
337
		$matches = '';
338
		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

338
		preg_match('~([A-Za-z0-9_-]+)~', $file_id, /** @scrutinizer ignore-type */ $matches);
Loading history...
339
		$file_id = ucfirst($matches[1] ?? '');
340
341
		// This will be where we look
342
		$lang_dirs = glob($base_lang_dir . '/*', GLOB_ONLYDIR);
343
344
		// Now for every theme get all the files and stick them in context!
345
		$context['possible_files'] = array_map(static fn($file) => [
346
			'id' => basename($file, '.php'),
347
			'name' => $txt['lang_file_desc_' . basename($file)] ?? basename($file),
348
			'path' => $file,
349
			'selected' => $file_id === basename($file),
350
		], $lang_dirs);
351
352
		if ($context['lang_id'] !== 'english')
353
		{
354
			$possiblePackage = findPossiblePackages($context['lang_id']);
355
			if ($possiblePackage !== false)
0 ignored issues
show
introduced by
The condition $possiblePackage !== false is always true.
Loading history...
356
			{
357
				$context['langpack_uninstall_link'] = getUrl('admin', ['action' => 'admin', 'area' => 'packages', 'sa' => 'uninstall', 'package' => $possiblePackage[1], 'pid' => $possiblePackage[0]]);
358
			}
359
		}
360
361
		// Quickly load index language entries.
362
		$mtxt = [];
363
		$new_lang = new LangLoader($context['lang_id'], $mtxt, database());
364
		$new_lang->load('Index', true);
365
366
		// Setup the primary settings context.
367
		$context['primary_settings'] = array(
368
			'name' => Util::ucwords(strtr($context['lang_id'], array('_' => ' ', '-utf8' => ''))),
369
			'locale' => $mtxt['lang_locale'],
370
			'dictionary' => $mtxt['lang_dictionary'],
371
			'spelling' => $mtxt['lang_spelling'],
372
			'rtl' => $mtxt['lang_rtl'],
373
		);
374
375
		// Quickly load index language entries.
376
		$edit_lang = new LangEditor($context['lang_id'], database());
377
		$edit_lang->load($file_id);
378
379
		$context['file_entries'] = $edit_lang->getForEditing();
380
381
		// Are we saving?
382
		if (isset($this->_req->post->save_entries) && !empty($this->_req->post->entry))
383
		{
384
			checkSession();
385
			validateToken('admin-mlang');
386
387
			$edit_lang->save($file_id, $this->_req->post->entry);
388
389
			redirectexit('action=admin;area=languages;sa=editlang;lid=' . $context['lang_id']);
390
		}
391
392
		createToken('admin-mlang');
393
	}
394
395
	/**
396
	 * Edit language related settings.
397
	 *
398
	 * - Accessed by ?action=admin;area=languages;sa=settings
399
	 * - This method handles the display, allows editing, and saves the result
400
	 * for the _languageSettings form.
401
	 *
402
	 * @event integrate_save_language_settings
403
	 */
404
	public function action_languageSettings_display()
405
	{
406
		global $context, $txt;
407
408
		// Initialize the form
409
		$settingsForm = new SettingsForm(SettingsForm::FILE_ADAPTER);
410
		$fileFunc = FileFunctions::instance();
411
412
		// Initialize it with our settings
413
		$settingsForm->setConfigVars($this->_settings());
414
415
		// Warn the user if the backup of Settings.php failed.
416
		$settings_not_writable = !$fileFunc->isWritable(BOARDDIR . '/Settings.php');
417
		$settings_backup_fail = !$fileFunc->isWritable(BOARDDIR . '/Settings_bak.php') || !@copy(BOARDDIR . '/Settings.php', BOARDDIR . '/Settings_bak.php');
418
419
		// Saving settings?
420
		if (isset($this->_req->query->save))
421
		{
422
			checkSession();
423
424
			call_integration_hook('integrate_save_language_settings');
425
426
			$settingsForm->setConfigValues((array) $this->_req->post);
427
			$settingsForm->save();
428
			redirectexit('action=admin;area=languages;sa=settings');
429
		}
430
431
		// Setup the template stuff.
432
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'languages', 'sa' => 'settings', 'save']);
433
		$context['settings_title'] = $txt['language_settings'];
434
		$context['save_disabled'] = $settings_not_writable;
435
436
		if ($settings_not_writable)
437
		{
438
			$context['error_type'] = 'notice';
439
			$context['settings_message'] = $txt['settings_not_writable'];
440
		}
441
		elseif ($settings_backup_fail)
442
		{
443
			$context['error_type'] = 'notice';
444
			$context['settings_message'] = $txt['admin_backup_fail'];
445
		}
446
447
		// Fill the config array in contextual data for the template.
448
		$settingsForm->prepare();
449
	}
450
451
	/**
452
	 * Load up all language settings
453
	 *
454
	 * @event integrate_modify_language_settings Use to add new config options
455
	 */
456
	private function _settings()
457
	{
458
		global $txt;
459
460
		// Warn the user if the backup of Settings.php failed.
461
		$settings_not_writable = !is_writable(BOARDDIR . '/Settings.php');
462
463
		$config_vars = array(
464
			'language' => array('language', $txt['default_language'], 'file', 'select', array(), null, 'disabled' => $settings_not_writable),
465
			array('userLanguage', $txt['userLanguage'], 'db', 'check', null, 'userLanguage'),
466
		);
467
468
		call_integration_hook('integrate_modify_language_settings', array(&$config_vars));
469
470
		// Get our languages. No cache.
471
		$languages = getLanguages(false);
472
		foreach ($languages as $lang)
473
		{
474
			$config_vars['language'][4][] = array($lang['name'], strtr($lang['name'], array('-utf8' => ' (UTF-8)')));
475
		}
476
477
		return $config_vars;
478
	}
479
480
	/**
481
	 * Return the form settings for use in admin search
482
	 */
483
	public function settings_search()
484
	{
485
		return $this->_settings();
486
	}
487
}
488