1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file handles the administration of languages tasks. |
5
|
|
|
* |
6
|
|
|
* @name ElkArte Forum |
7
|
|
|
* @copyright ElkArte Forum contributors |
8
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause |
9
|
|
|
* |
10
|
|
|
* This file contains code covered by: |
11
|
|
|
* copyright: 2011 Simple Machines (http://www.simplemachines.org) |
12
|
|
|
* license: BSD, See included LICENSE.TXT for terms and conditions. |
13
|
|
|
* |
14
|
|
|
* @version 1.1.4 |
15
|
|
|
* |
16
|
|
|
*/ |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Manage languages controller class. |
20
|
|
|
* |
21
|
|
|
* @package Languages |
22
|
|
|
*/ |
23
|
|
|
class ManageLanguages_Controller extends Action_Controller |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* This is the main function for the languages area. |
27
|
|
|
* |
28
|
|
|
* What it does: |
29
|
|
|
* |
30
|
|
|
* - It dispatches the requests. |
31
|
|
|
* - Loads the ManageLanguages template. (sub-actions will use it) |
32
|
|
|
* |
33
|
|
|
* @event integrate_sa_manage_languages Used to add more sub actions |
34
|
|
|
* @uses ManageSettings language file |
35
|
|
|
* @see Action_Controller::action_index() |
36
|
|
|
*/ |
37
|
|
|
public function action_index() |
38
|
|
|
{ |
39
|
|
|
global $context, $txt; |
40
|
|
|
|
41
|
|
|
loadTemplate('ManageLanguages'); |
42
|
|
|
loadLanguage('ManageSettings'); |
43
|
|
|
|
44
|
|
|
$subActions = array( |
45
|
|
|
'edit' => array($this, 'action_edit', 'permission' => 'admin_forum'), |
46
|
|
|
'settings' => array($this, 'action_languageSettings_display', 'permission' => 'admin_forum'), |
47
|
|
|
'downloadlang' => array($this, 'action_downloadlang', 'permission' => 'admin_forum'), |
48
|
|
|
'editlang' => array($this, 'action_editlang', 'permission' => 'admin_forum'), |
49
|
|
|
); |
50
|
|
|
|
51
|
|
|
// Get ready for action |
52
|
|
|
$action = new Action('manage_languages'); |
53
|
|
|
|
54
|
|
|
// Load up all the tabs... |
55
|
|
|
$context[$context['admin_menu_name']]['tab_data'] = array( |
56
|
|
|
'title' => $txt['language_configuration'], |
57
|
|
|
'description' => $txt['language_description'], |
58
|
|
|
); |
59
|
|
|
|
60
|
|
|
// By default we're managing languages, call integrate_sa_manage_languages |
61
|
|
|
$subAction = $action->initialize($subActions, 'edit'); |
62
|
|
|
|
63
|
|
|
// Some final bits |
64
|
|
|
$context['sub_action'] = $subAction; |
65
|
|
|
$context['page_title'] = $txt['edit_languages']; |
66
|
|
|
$context['sub_template'] = 'show_settings'; |
67
|
|
|
|
68
|
|
|
// Call the right function for this sub-action. |
69
|
|
|
$action->dispatch($subAction); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Interface for adding a new language. |
74
|
|
|
* |
75
|
|
|
* @uses ManageLanguages template, add_language sub-template. |
76
|
|
|
*/ |
77
|
|
|
public function action_add() |
78
|
|
|
{ |
79
|
|
|
global $context, $txt; |
80
|
|
|
|
81
|
|
|
// Are we searching for new languages on the site? |
82
|
|
|
if (!empty($this->_req->post->lang_add_sub)) |
83
|
|
|
{ |
84
|
|
|
// Need fetch_web_data. |
85
|
|
|
require_once(SUBSDIR . '/Package.subs.php'); |
86
|
|
|
require_once(SUBSDIR . '/Language.subs.php'); |
87
|
|
|
|
88
|
|
|
$context['elk_search_term'] = $this->_req->getPost('lang_add', 'trim|htmlspecialchars[ENT_COMPAT]'); |
89
|
|
|
|
90
|
|
|
$listOptions = array( |
91
|
|
|
'id' => 'languages', |
92
|
|
|
'get_items' => array( |
93
|
|
|
'function' => 'list_getLanguagesList', |
94
|
|
|
), |
95
|
|
|
'columns' => array( |
96
|
|
|
'name' => array( |
97
|
|
|
'header' => array( |
98
|
|
|
'value' => $txt['name'], |
99
|
|
|
), |
100
|
|
|
'data' => array( |
101
|
|
|
'db' => 'name', |
102
|
|
|
), |
103
|
|
|
), |
104
|
|
|
'description' => array( |
105
|
|
|
'header' => array( |
106
|
|
|
'value' => $txt['add_language_elk_desc'], |
107
|
|
|
), |
108
|
|
|
'data' => array( |
109
|
|
|
'db' => 'description', |
110
|
|
|
), |
111
|
|
|
), |
112
|
|
|
'version' => array( |
113
|
|
|
'header' => array( |
114
|
|
|
'value' => $txt['add_language_elk_version'], |
115
|
|
|
), |
116
|
|
|
'data' => array( |
117
|
|
|
'db' => 'version', |
118
|
|
|
), |
119
|
|
|
), |
120
|
|
|
'utf8' => array( |
121
|
|
|
'header' => array( |
122
|
|
|
'value' => $txt['add_language_elk_utf8'], |
123
|
|
|
), |
124
|
|
|
'data' => array( |
125
|
|
|
'db' => 'utf8', |
126
|
|
|
), |
127
|
|
|
), |
128
|
|
|
'install_link' => array( |
129
|
|
|
'header' => array( |
130
|
|
|
'value' => $txt['add_language_elk_install'], |
131
|
|
|
'class' => 'centertext', |
132
|
|
|
), |
133
|
|
|
'data' => array( |
134
|
|
|
'db' => 'install_link', |
135
|
|
|
'class' => 'centertext', |
136
|
|
|
), |
137
|
|
|
), |
138
|
|
|
), |
139
|
|
|
); |
140
|
|
|
|
141
|
|
|
createList($listOptions); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
$context['sub_template'] = 'add_language'; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* This lists all the current languages and allows editing of them. |
149
|
|
|
*/ |
150
|
|
|
public function action_edit() |
151
|
|
|
{ |
152
|
|
|
global $txt, $context, $scripturl, $language; |
153
|
|
|
|
154
|
|
|
require_once(SUBSDIR . '/Language.subs.php'); |
155
|
|
|
|
156
|
|
|
// Setting a new default? |
157
|
|
|
if (!empty($this->_req->post->set_default) && !empty($this->_req->post->def_language)) |
158
|
|
|
{ |
159
|
|
|
checkSession(); |
160
|
|
|
validateToken('admin-lang'); |
161
|
|
|
|
162
|
|
|
$lang_exists = false; |
163
|
|
|
$available_langs = getLanguages(); |
164
|
|
|
foreach ($available_langs as $lang) |
165
|
|
|
{ |
166
|
|
|
if ($this->_req->post->def_language == $lang['filename']) |
167
|
|
|
{ |
168
|
|
|
$lang_exists = true; |
169
|
|
|
break; |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
if ($this->_req->post->def_language != $language && $lang_exists) |
174
|
|
|
{ |
175
|
|
|
$language = $this->_req->post->def_language; |
176
|
|
|
$this->updateLanguage($language); |
177
|
|
|
redirectexit('action=admin;area=languages;sa=edit'); |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
// Create another one time token here. |
182
|
|
|
createToken('admin-lang'); |
183
|
|
|
createToken('admin-ssc'); |
184
|
|
|
|
185
|
|
|
$listOptions = array( |
186
|
|
|
'id' => 'language_list', |
187
|
|
|
'items_per_page' => 20, |
188
|
|
|
'base_href' => $scripturl . '?action=admin;area=languages', |
189
|
|
|
'title' => $txt['edit_languages'], |
190
|
|
|
'data_check' => array( |
191
|
|
|
'class' => function ($rowData) { |
192
|
|
|
if ($rowData['default']) |
193
|
|
|
return 'highlight2'; |
194
|
|
|
else |
195
|
|
|
return ''; |
196
|
|
|
}, |
197
|
|
|
), |
198
|
|
|
'get_items' => array( |
199
|
|
|
'function' => 'list_getLanguages', |
200
|
|
|
), |
201
|
|
|
'get_count' => array( |
202
|
|
|
'function' => 'list_getNumLanguages', |
203
|
|
|
), |
204
|
|
|
'columns' => array( |
205
|
|
|
'default' => array( |
206
|
|
|
'header' => array( |
207
|
|
|
'value' => $txt['languages_default'], |
208
|
|
|
'class' => 'centertext', |
209
|
|
|
), |
210
|
|
|
'data' => array( |
211
|
|
|
'function' => function ($rowData) { |
212
|
|
|
return '<input type="radio" name="def_language" value="' . $rowData['id'] . '" ' . ($rowData['default'] ? 'checked="checked"' : '') . ' class="input_radio" />'; |
213
|
|
|
}, |
214
|
|
|
'style' => 'width: 8%;', |
215
|
|
|
'class' => 'centertext', |
216
|
|
|
), |
217
|
|
|
), |
218
|
|
|
'name' => array( |
219
|
|
|
'header' => array( |
220
|
|
|
'value' => $txt['languages_lang_name'], |
221
|
|
|
), |
222
|
|
|
'data' => array( |
223
|
|
|
'function' => function ($rowData) { |
224
|
|
|
global $scripturl; |
225
|
|
|
|
226
|
|
|
return sprintf('<a href="%1$s?action=admin;area=languages;sa=editlang;lid=%2$s">%3$s<i class="icon icon-small i-modify"></i></a>', $scripturl, $rowData['id'], $rowData['name']); |
227
|
|
|
}, |
228
|
|
|
), |
229
|
|
|
), |
230
|
|
|
'count' => array( |
231
|
|
|
'header' => array( |
232
|
|
|
'value' => $txt['languages_users'], |
233
|
|
|
), |
234
|
|
|
'data' => array( |
235
|
|
|
'db_htmlsafe' => 'count', |
236
|
|
|
), |
237
|
|
|
), |
238
|
|
|
'locale' => array( |
239
|
|
|
'header' => array( |
240
|
|
|
'value' => $txt['languages_locale'], |
241
|
|
|
), |
242
|
|
|
'data' => array( |
243
|
|
|
'db_htmlsafe' => 'locale', |
244
|
|
|
), |
245
|
|
|
), |
246
|
|
|
), |
247
|
|
|
'form' => array( |
248
|
|
|
'href' => $scripturl . '?action=admin;area=languages', |
249
|
|
|
'token' => 'admin-lang', |
250
|
|
|
), |
251
|
|
|
'additional_rows' => array( |
252
|
|
|
array( |
253
|
|
|
'class' => 'submitbutton', |
254
|
|
|
'position' => 'bottom_of_list', |
255
|
|
|
'value' => ' |
256
|
|
|
<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" /> |
257
|
|
|
<input type="submit" name="set_default" value="' . $txt['save'] . '"' . (is_writable(BOARDDIR . '/Settings.php') ? '' : ' disabled="disabled"') . ' /> |
258
|
|
|
<input type="hidden" name="' . $context['admin-ssc_token_var'] . '" value="' . $context['admin-ssc_token'] . '" />', |
259
|
|
|
), |
260
|
|
|
), |
261
|
|
|
// For highlighting the default. |
262
|
|
|
'javascript' => ' |
263
|
|
|
initHighlightSelection(\'language_list\'); |
264
|
|
|
', |
265
|
|
|
); |
266
|
|
|
|
267
|
|
|
// Display a warning if we cannot edit the default setting. |
268
|
|
|
if (!is_writable(BOARDDIR . '/Settings.php')) |
269
|
|
|
$listOptions['additional_rows'][] = array( |
270
|
|
|
'position' => 'after_title', |
271
|
|
|
'value' => $txt['language_settings_writable'], |
272
|
|
|
'class' => 'smalltext alert', |
273
|
|
|
); |
274
|
|
|
|
275
|
|
|
createList($listOptions); |
276
|
|
|
|
277
|
|
|
$context['sub_template'] = 'show_list'; |
278
|
|
|
$context['default_list'] = 'language_list'; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Download a language file from the website. |
283
|
|
|
* |
284
|
|
|
* What it does: |
285
|
|
|
* |
286
|
|
|
* - Requires a valid download ID ("did") in the URL. |
287
|
|
|
* - Also handles installing language files. |
288
|
|
|
* - Attempts to chmod things as needed. |
289
|
|
|
* - Uses a standard list to display information about all the files and where they'll be put. |
290
|
|
|
* |
291
|
|
|
* @uses ManageLanguages template, download_language sub-template. |
292
|
|
|
* @uses Admin template, show_list sub-template. |
293
|
|
|
*/ |
294
|
|
|
public function action_downloadlang() |
295
|
|
|
{ |
296
|
|
|
// @todo for the moment there is no facility to download packages, so better kill it here |
297
|
|
|
throw new Elk_Exception('no_access', false); |
298
|
|
|
|
299
|
|
|
loadLanguage('ManageSettings'); |
|
|
|
|
300
|
|
|
require_once(SUBSDIR . '/Package.subs.php'); |
301
|
|
|
|
302
|
|
|
// Clearly we need to know what to request. |
303
|
|
|
if (!isset($this->_req->query->did)) |
304
|
|
|
throw new Elk_Exception('no_access', false); |
305
|
|
|
|
306
|
|
|
// Some lovely context. |
307
|
|
|
$context['download_id'] = $this->_req->query->did; |
308
|
|
|
$context['sub_template'] = 'download_language'; |
309
|
|
|
$context['menu_data_' . $context['admin_menu_id']]['current_subsection'] = 'add'; |
310
|
|
|
|
311
|
|
|
// Can we actually do the installation - and do they want to? |
312
|
|
|
if (!empty($this->_req->post->do_install) && !empty($this->_req->post->copy_file)) |
313
|
|
|
{ |
314
|
|
|
checkSession('get'); |
315
|
|
|
validateToken('admin-dlang'); |
316
|
|
|
|
317
|
|
|
$chmod_files = array(); |
318
|
|
|
$install_files = array(); |
319
|
|
|
|
320
|
|
|
// Check writable status. |
321
|
|
|
foreach ($this->_req->post->copy_file as $file) |
322
|
|
|
{ |
323
|
|
|
// Check it's not very bad. |
324
|
|
|
if (strpos($file, '..') !== false || (strpos($file, 'themes') !== 0 && !preg_match('~agreement\.[A-Za-z-_0-9]+\.txt$~', $file))) |
325
|
|
|
throw new Elk_Exception($txt['languages_download_illegal_paths']); |
326
|
|
|
|
327
|
|
|
$chmod_files[] = BOARDDIR . '/' . $file; |
328
|
|
|
$install_files[] = $file; |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
// Call this in case we have work to do. |
332
|
|
|
$file_status = create_chmod_control($chmod_files); |
333
|
|
|
$files_left = $file_status['files']['notwritable']; |
334
|
|
|
|
335
|
|
|
// Something not writable? |
336
|
|
|
if (!empty($files_left)) |
337
|
|
|
$context['error_message'] = $txt['languages_download_not_chmod']; |
338
|
|
|
// Otherwise, go go go! |
339
|
|
|
elseif (!empty($install_files)) |
340
|
|
|
{ |
341
|
|
|
// @todo retrieve the language pack per naming pattern from our sites |
342
|
|
|
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); |
343
|
|
|
|
344
|
|
|
// Make sure the files aren't stuck in the cache. |
345
|
|
|
package_flush_cache(); |
346
|
|
|
$context['install_complete'] = sprintf($txt['languages_download_complete_desc'], $scripturl . '?action=admin;area=languages'); |
347
|
|
|
|
348
|
|
|
return; |
349
|
|
|
} |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
// @todo Open up the old china. |
353
|
|
|
$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); |
354
|
|
|
|
355
|
|
|
if (empty($archive_content)) |
356
|
|
|
throw new Elk_Exception($txt['add_language_error_no_response']); |
357
|
|
|
|
358
|
|
|
// Now for each of the files, let's do some *stuff* |
359
|
|
|
$context['files'] = array( |
360
|
|
|
'lang' => array(), |
361
|
|
|
'other' => array(), |
362
|
|
|
); |
363
|
|
|
$context['make_writable'] = array(); |
364
|
|
|
foreach ($archive_content as $file) |
365
|
|
|
{ |
366
|
|
|
$dirname = dirname($file['filename']); |
367
|
|
|
$filename = basename($file['filename']); |
368
|
|
|
$extension = substr($filename, strrpos($filename, '.') + 1); |
369
|
|
|
|
370
|
|
|
// Don't do anything with files we don't understand. |
371
|
|
|
if (!in_array($extension, array('php', 'jpg', 'gif', 'jpeg', 'png', 'txt'))) |
372
|
|
|
continue; |
373
|
|
|
|
374
|
|
|
// Basic data. |
375
|
|
|
$context_data = array( |
376
|
|
|
'name' => $filename, |
377
|
|
|
'destination' => BOARDDIR . '/' . $file['filename'], |
378
|
|
|
'generaldest' => $file['filename'], |
379
|
|
|
'size' => $file['size'], |
380
|
|
|
// Does chmod status allow the copy? |
381
|
|
|
'writable' => false, |
382
|
|
|
// Should we suggest they copy this file? |
383
|
|
|
'default_copy' => true, |
384
|
|
|
// Does the file already exist, if so is it same or different? |
385
|
|
|
'exists' => false, |
386
|
|
|
); |
387
|
|
|
|
388
|
|
|
// Does the file exist, is it different and can we overwrite? |
389
|
|
|
if (file_exists(BOARDDIR . '/' . $file['filename'])) |
390
|
|
|
{ |
391
|
|
View Code Duplication |
if (is_writable(BOARDDIR . '/' . $file['filename'])) |
392
|
|
|
$context_data['writable'] = true; |
393
|
|
|
|
394
|
|
|
// Finally, do we actually think the content has changed? |
395
|
|
|
if ($file['size'] == filesize(BOARDDIR . '/' . $file['filename']) && $file['md5'] === md5_file(BOARDDIR . '/' . $file['filename'])) |
396
|
|
|
{ |
397
|
|
|
$context_data['exists'] = 'same'; |
398
|
|
|
$context_data['default_copy'] = false; |
399
|
|
|
} |
400
|
|
|
// Attempt to discover newline character differences. |
401
|
|
|
elseif ($file['md5'] === md5(preg_replace("~[\r]?\n~", "\r\n", file_get_contents(BOARDDIR . '/' . $file['filename'])))) |
402
|
|
|
{ |
403
|
|
|
$context_data['exists'] = 'same'; |
404
|
|
|
$context_data['default_copy'] = false; |
405
|
|
|
} |
406
|
|
|
else |
407
|
|
|
$context_data['exists'] = 'different'; |
408
|
|
|
} |
409
|
|
|
// No overwrite? |
410
|
|
View Code Duplication |
else |
411
|
|
|
{ |
412
|
|
|
// Can we at least stick it in the directory... |
413
|
|
|
if (is_writable(BOARDDIR . '/' . $dirname)) |
414
|
|
|
$context_data['writable'] = true; |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
// 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... |
418
|
|
|
if ($extension == 'php' && preg_match('~\w+\.\w+(?:-utf8)?\.php~', $filename)) |
419
|
|
|
{ |
420
|
|
|
$context_data += array( |
421
|
|
|
'version' => '??', |
422
|
|
|
'cur_version' => false, |
423
|
|
|
'version_compare' => 'newer', |
424
|
|
|
); |
425
|
|
|
|
426
|
|
|
list ($name,) = explode('.', $filename); |
427
|
|
|
|
428
|
|
|
// Let's get the new version, I like versions, they tell me that I'm up to date. |
429
|
|
|
if (preg_match('~\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '~i', $file['preview'], $match) == 1) |
430
|
|
|
$context_data['version'] = $match[1]; |
431
|
|
|
|
432
|
|
|
// Now does the old file exist - if so what is it's version? |
433
|
|
|
if (file_exists(BOARDDIR . '/' . $file['filename'])) |
434
|
|
|
{ |
435
|
|
|
// OK - what is the current version? |
436
|
|
|
$fp = fopen(BOARDDIR . '/' . $file['filename'], 'rb'); |
437
|
|
|
$header = fread($fp, 768); |
438
|
|
|
fclose($fp); |
439
|
|
|
|
440
|
|
|
// Find the version. |
441
|
|
|
if (preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '(?:[\s]{2}|\*/)~i', $header, $match) == 1) |
442
|
|
|
{ |
443
|
|
|
$context_data['cur_version'] = $match[1]; |
444
|
|
|
|
445
|
|
|
// How does this compare? |
446
|
|
|
if ($context_data['cur_version'] == $context_data['version']) |
447
|
|
|
$context_data['version_compare'] = 'same'; |
448
|
|
|
elseif ($context_data['cur_version'] > $context_data['version']) |
449
|
|
|
$context_data['version_compare'] = 'older'; |
450
|
|
|
|
451
|
|
|
// Don't recommend copying if the version is the same. |
452
|
|
|
if ($context_data['version_compare'] != 'newer') |
453
|
|
|
$context_data['default_copy'] = false; |
454
|
|
|
} |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
// Add the context data to the main set. |
458
|
|
|
$context['files']['lang'][] = $context_data; |
459
|
|
|
} |
460
|
|
|
else |
461
|
|
|
{ |
462
|
|
|
// If we think it's a theme thing, work out what the theme is. |
463
|
|
View Code Duplication |
if (strpos($dirname, 'themes') === 0 && preg_match('~themes[\\/]([^\\/]+)[\\/]~', $dirname, $match)) |
464
|
|
|
$theme_name = $match[1]; |
465
|
|
|
else |
466
|
|
|
$theme_name = 'misc'; |
467
|
|
|
|
468
|
|
|
// Assume it's an image, could be an acceptance note etc but rare. |
469
|
|
|
$context['files']['images'][$theme_name][] = $context_data; |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
// Collect together all non-writable areas. |
473
|
|
|
if (!$context_data['writable']) |
474
|
|
|
$context['make_writable'][] = $context_data['destination']; |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
// So, I'm a perfectionist - let's get the theme names. |
478
|
|
|
$indexes = array(); |
479
|
|
|
foreach ($context['files']['images'] as $k => $dummy) |
480
|
|
|
$indexes[] = $k; |
481
|
|
|
|
482
|
|
|
$context['theme_names'] = array(); |
483
|
|
|
if (!empty($indexes)) |
484
|
|
|
{ |
485
|
|
|
require_once(SUBSDIR . '/Themes.subs.php'); |
486
|
|
|
$value_data = array( |
487
|
|
|
'query' => array(), |
488
|
|
|
'params' => array(), |
489
|
|
|
); |
490
|
|
|
|
491
|
|
|
foreach ($indexes as $k => $index) |
492
|
|
|
{ |
493
|
|
|
$value_data['query'][] = 'value LIKE {string:value_' . $k . '}'; |
494
|
|
|
$value_data['params']['value_' . $k] = '%' . $index; |
495
|
|
|
} |
496
|
|
|
|
497
|
|
|
$themes = validateThemeName($indexes, $value_data); |
498
|
|
|
|
499
|
|
|
// Now we have the id_theme we can get the pretty description. |
500
|
|
|
if (!empty($themes)) |
501
|
|
|
$context['theme_names'] = getBasicThemeInfos($themes); |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
// Before we go to far can we make anything writable, eh, eh? |
505
|
|
|
if (!empty($context['make_writable'])) |
506
|
|
|
{ |
507
|
|
|
// What is left to be made writable? |
508
|
|
|
$file_status = create_chmod_control($context['make_writable']); |
509
|
|
|
$context['still_not_writable'] = $file_status['files']['notwritable']; |
510
|
|
|
|
511
|
|
|
// Mark those which are now writable as such. |
512
|
|
|
foreach ($context['files'] as $type => $data) |
513
|
|
|
{ |
514
|
|
|
if ($type == 'lang') |
515
|
|
|
{ |
516
|
|
View Code Duplication |
foreach ($data as $k => $file) |
517
|
|
|
if (!$file['writable'] && !in_array($file['destination'], $context['still_not_writable'])) |
518
|
|
|
$context['files'][$type][$k]['writable'] = true; |
519
|
|
|
} |
520
|
|
|
else |
521
|
|
|
{ |
522
|
|
|
foreach ($data as $theme => $files) |
523
|
|
View Code Duplication |
foreach ($files as $k => $file) |
524
|
|
|
if (!$file['writable'] && !in_array($file['destination'], $context['still_not_writable'])) |
525
|
|
|
$context['files'][$type][$theme][$k]['writable'] = true; |
526
|
|
|
} |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
// Are we going to need more language stuff? |
530
|
|
|
if (!empty($context['still_not_writable'])) |
531
|
|
|
loadLanguage('Packages'); |
532
|
|
|
} |
533
|
|
|
|
534
|
|
|
// This is the list for the main files. |
535
|
|
|
$listOptions = array( |
536
|
|
|
'id' => 'lang_main_files_list', |
537
|
|
|
'title' => $txt['languages_download_main_files'], |
538
|
|
|
'get_items' => array( |
539
|
|
|
'function' => function () { |
540
|
|
|
global $context; |
541
|
|
|
return $context['files']['lang']; |
542
|
|
|
}, |
543
|
|
|
), |
544
|
|
|
'columns' => array( |
545
|
|
|
'name' => array( |
546
|
|
|
'header' => array( |
547
|
|
|
'value' => $txt['languages_download_filename'], |
548
|
|
|
), |
549
|
|
|
'data' => array( |
550
|
|
|
'function' => function ($rowData) { |
551
|
|
|
global $txt; |
552
|
|
|
|
553
|
|
|
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'] : ''); |
554
|
|
|
}, |
555
|
|
|
), |
556
|
|
|
), |
557
|
|
|
'writable' => array( |
558
|
|
|
'header' => array( |
559
|
|
|
'value' => $txt['languages_download_writable'], |
560
|
|
|
), |
561
|
|
|
'data' => array( |
562
|
|
|
'function' => function ($rowData) { |
563
|
|
|
global $txt; |
564
|
|
|
|
565
|
|
|
return '<span class="' . ($rowData['writable'] ? 'success' : 'error') . ';">' . ($rowData['writable'] ? $txt['yes'] : $txt['no']) . '</span>'; |
566
|
|
|
}, |
567
|
|
|
), |
568
|
|
|
), |
569
|
|
|
'version' => array( |
570
|
|
|
'header' => array( |
571
|
|
|
'value' => $txt['languages_download_version'], |
572
|
|
|
), |
573
|
|
|
'data' => array( |
574
|
|
|
'function' => function ($rowData) { |
575
|
|
|
return '<span class="' . ($rowData['version_compare'] == 'older' ? 'error' : ($rowData['version_compare'] == 'same' ? 'softalert' : 'success')) . ';">' . $rowData['version'] . '</span>'; |
576
|
|
|
}, |
577
|
|
|
), |
578
|
|
|
), |
579
|
|
|
'exists' => array( |
580
|
|
|
'header' => array( |
581
|
|
|
'value' => $txt['languages_download_exists'], |
582
|
|
|
), |
583
|
|
|
'data' => array( |
584
|
|
|
'function' => function ($rowData) { |
585
|
|
|
global $txt; |
586
|
|
|
|
587
|
|
|
return $rowData['exists'] ? ($rowData['exists'] == 'same' ? $txt['languages_download_exists_same'] : $txt['languages_download_exists_different']) : $txt['no']; |
588
|
|
|
}, |
589
|
|
|
), |
590
|
|
|
), |
591
|
|
|
'copy' => array( |
592
|
|
|
'header' => array( |
593
|
|
|
'value' => $txt['languages_download_copy'], |
594
|
|
|
'class' => 'centertext', |
595
|
|
|
), |
596
|
|
|
'data' => array( |
597
|
|
|
'function' => function ($rowData) { |
598
|
|
|
return '<input type="checkbox" name="copy_file[]" value="' . $rowData['generaldest'] . '" ' . ($rowData['default_copy'] ? 'checked="checked"' : '') . ' class="input_check" />'; |
599
|
|
|
}, |
600
|
|
|
'style' => 'width: 4%;', |
601
|
|
|
'class' => 'centertext', |
602
|
|
|
), |
603
|
|
|
), |
604
|
|
|
), |
605
|
|
|
); |
606
|
|
|
|
607
|
|
|
// Kill the cache, as it is now invalid.. |
608
|
|
|
$cache = Cache::instance(); |
609
|
|
|
$cache->put('known_languages', null, $cache->maxLevel(1) ? 86400 : 3600); |
610
|
|
|
|
611
|
|
|
createList($listOptions); |
612
|
|
|
|
613
|
|
|
createToken('admin-dlang'); |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
/** |
617
|
|
|
* Edit a particular set of language entries. |
618
|
|
|
*/ |
619
|
|
|
public function action_editlang() |
620
|
|
|
{ |
621
|
|
|
global $settings, $context, $txt, $modSettings, $language, $scripturl; |
622
|
|
|
|
623
|
|
|
require_once(SUBSDIR . '/Language.subs.php'); |
624
|
|
|
loadLanguage('ManageSettings'); |
625
|
|
|
|
626
|
|
|
// Select the languages tab. |
627
|
|
|
$context['menu_data_' . $context['admin_menu_id']]['current_subsection'] = 'edit'; |
628
|
|
|
$context['page_title'] = $txt['edit_languages']; |
629
|
|
|
$context['sub_template'] = 'modify_language_entries'; |
630
|
|
|
|
631
|
|
|
$context['lang_id'] = $this->_req->query->lid; |
632
|
|
|
list ($theme_id, $file_id) = empty($this->_req->post->tfid) || strpos($this->_req->post->tfid, '+') === false ? array(1, '') : explode('+', $this->_req->post->tfid); |
633
|
|
|
|
634
|
|
|
// Clean the ID - just in case. |
635
|
|
|
preg_match('~([A-Za-z0-9_-]+)~', $context['lang_id'], $matches); |
636
|
|
|
$context['lang_id'] = $matches[1]; |
637
|
|
|
|
638
|
|
|
// Get all the theme data. |
639
|
|
|
require_once(SUBSDIR . '/Themes.subs.php'); |
640
|
|
|
$themes = getCustomThemes(); |
641
|
|
|
|
642
|
|
|
// This will be where we look |
643
|
|
|
$lang_dirs = array(); |
644
|
|
|
$images_dirs = array(); |
645
|
|
|
|
646
|
|
|
// Check we have themes with a path and a name - just in case - and add the path. |
647
|
|
|
foreach ($themes as $id => $data) |
648
|
|
|
{ |
649
|
|
|
if (count($data) != 2) |
650
|
|
|
unset($themes[$id]); |
651
|
|
View Code Duplication |
elseif (is_dir($data['theme_dir'] . '/languages/' . $context['lang_id'])) |
652
|
|
|
$lang_dirs[$id] = $data['theme_dir'] . '/languages/' . $context['lang_id']; |
653
|
|
|
|
654
|
|
|
// How about image directories? |
655
|
|
View Code Duplication |
if (is_dir($data['theme_dir'] . '/images/' . $context['lang_id'])) |
656
|
|
|
$images_dirs[$id] = $data['theme_dir'] . '/images/' . $context['lang_id']; |
657
|
|
|
} |
658
|
|
|
|
659
|
|
|
$current_file = $file_id ? $lang_dirs[$theme_id] . '/' . $file_id . '.' . $context['lang_id'] . '.php' : ''; |
660
|
|
|
|
661
|
|
|
// Now for every theme get all the files and stick them in context! |
662
|
|
|
$context['possible_files'] = array(); |
663
|
|
|
foreach ($lang_dirs as $theme => $theme_dir) |
664
|
|
|
{ |
665
|
|
|
// Open it up. |
666
|
|
|
$dir = dir($theme_dir); |
667
|
|
|
while ($entry = $dir->read()) |
668
|
|
|
{ |
669
|
|
|
// We're only after the files for this language. |
670
|
|
|
if (preg_match('~^([A-Za-z]+)\.' . $context['lang_id'] . '\.php$~', $entry, $matches) == 0) |
671
|
|
|
continue; |
672
|
|
|
|
673
|
|
|
if (!isset($context['possible_files'][$theme])) |
674
|
|
|
$context['possible_files'][$theme] = array( |
675
|
|
|
'id' => $theme, |
676
|
|
|
'name' => $themes[$theme]['name'], |
677
|
|
|
'files' => array(), |
678
|
|
|
); |
679
|
|
|
|
680
|
|
|
$context['possible_files'][$theme]['files'][] = array( |
681
|
|
|
'id' => $matches[1], |
682
|
|
|
'name' => isset($txt['lang_file_desc_' . $matches[1]]) ? $txt['lang_file_desc_' . $matches[1]] : $matches[1], |
683
|
|
|
'selected' => $theme_id == $theme && $file_id == $matches[1], |
684
|
|
|
); |
685
|
|
|
} |
686
|
|
|
$dir->close(); |
687
|
|
|
usort($context['possible_files'][$theme]['files'], function ($val1, $val2) { |
688
|
|
|
return strcmp($val1['name'], $val2['name']); |
689
|
|
|
}); |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
if ($context['lang_id'] != 'english') |
693
|
|
|
{ |
694
|
|
|
$possiblePackage = findPossiblePackages($context['lang_id']); |
695
|
|
|
if ($possiblePackage !== false) |
696
|
|
|
{ |
697
|
|
|
$context['langpack_uninstall_link'] = $scripturl . '?action=admin;area=packages;sa=uninstall;package=' . $possiblePackage[1] . ';pid=' . $possiblePackage[0]; |
698
|
|
|
} |
699
|
|
|
} |
700
|
|
|
|
701
|
|
|
// We no longer wish to speak this language. |
702
|
|
|
// @todo - languages have been moved to packages |
703
|
|
|
// this may or may not be used in the future, for now it's not used at all |
704
|
|
|
// @deprecated since 1.0 |
705
|
|
|
if (!empty($this->_req->post->delete_main) && $context['lang_id'] != 'english') |
706
|
|
|
{ |
707
|
|
|
checkSession(); |
708
|
|
|
validateToken('admin-mlang'); |
709
|
|
|
|
710
|
|
|
// @todo FTP Controls? |
711
|
|
|
require_once(SUBSDIR . '/Package.subs.php'); |
712
|
|
|
|
713
|
|
|
// First, Make a backup? |
714
|
|
|
if (!empty($modSettings['package_make_backups']) && (!isset($this->_req->session->last_backup_for) || $this->_req->session->last_backup_for != $context['lang_id'] . '$$$')) |
715
|
|
|
{ |
716
|
|
|
$_SESSION['last_backup_for'] = $context['lang_id'] . '$$$'; |
717
|
|
|
package_create_backup('backup_lang_' . $context['lang_id']); |
718
|
|
|
} |
719
|
|
|
|
720
|
|
|
// Second, loop through the array to remove the files. |
721
|
|
|
foreach ($lang_dirs as $curPath) |
722
|
|
|
{ |
723
|
|
|
foreach ($context['possible_files'][1]['files'] as $lang) |
724
|
|
|
if (file_exists($curPath . '/' . $lang['id'] . '.' . $context['lang_id'] . '.php')) |
725
|
|
|
unlink($curPath . '/' . $lang['id'] . '.' . $context['lang_id'] . '.php'); |
726
|
|
|
|
727
|
|
|
// Check for the email template. |
728
|
|
|
if (file_exists($curPath . '/EmailTemplates.' . $context['lang_id'] . '.php')) |
729
|
|
|
unlink($curPath . '/EmailTemplates.' . $context['lang_id'] . '.php'); |
730
|
|
|
} |
731
|
|
|
|
732
|
|
|
// Third, the agreement file. |
733
|
|
|
if (file_exists(BOARDDIR . '/agreement.' . $context['lang_id'] . '.txt')) |
734
|
|
|
unlink(BOARDDIR . '/agreement.' . $context['lang_id'] . '.txt'); |
735
|
|
|
|
736
|
|
|
// Fourth, a related images folder? |
737
|
|
|
if (!empty($images_dirs)) |
738
|
|
|
{ |
739
|
|
|
foreach ($images_dirs as $curPath) |
740
|
|
|
{ |
741
|
|
|
if (is_dir($curPath)) |
742
|
|
|
deltree($curPath); |
743
|
|
|
} |
744
|
|
|
} |
745
|
|
|
|
746
|
|
|
// Members can no longer use this language. |
747
|
|
|
removeLanguageFromMember($context['lang_id']); |
748
|
|
|
|
749
|
|
|
// Fifth, update getLanguages() cache. |
750
|
|
|
$cache = Cache::instance(); |
751
|
|
|
$cache->put('known_languages', null, $cache->maxLevel(1) ? 86400 : 3600); |
752
|
|
|
|
753
|
|
|
// Sixth, if we deleted the default language, set us back to english. |
754
|
|
|
if ($context['lang_id'] == $language) |
755
|
|
|
{ |
756
|
|
|
$language = 'english'; |
757
|
|
|
$this->updateLanguage($language); |
758
|
|
|
} |
759
|
|
|
|
760
|
|
|
// Seventh, get out of here. |
761
|
|
|
redirectexit('action=admin;area=languages;sa=edit;' . $context['session_var'] . '=' . $context['session_id']); |
762
|
|
|
} |
763
|
|
|
|
764
|
|
|
// Saving primary settings? |
765
|
|
|
$madeSave = false; |
766
|
|
|
if (!empty($this->_req->post->save_main) && !$current_file) |
767
|
|
|
{ |
768
|
|
|
checkSession(); |
769
|
|
|
validateToken('admin-mlang'); |
770
|
|
|
|
771
|
|
|
// Read in the current file. |
772
|
|
|
$current_data = implode('', file($settings['default_theme_dir'] . '/languages/' . $context['lang_id'] . '/index.' . $context['lang_id'] . '.php')); |
773
|
|
|
|
774
|
|
|
// These are the replacements. old => new |
775
|
|
|
$replace_array = array( |
776
|
|
|
'~\$txt\[\'lang_locale\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_locale\'] = \'' . addslashes($this->_req->post->locale) . '\';', |
777
|
|
|
'~\$txt\[\'lang_dictionary\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_dictionary\'] = \'' . addslashes($this->_req->post->dictionary) . '\';', |
778
|
|
|
'~\$txt\[\'lang_spelling\'\]\s=\s(\'|")[^\r\n]+~' => '$txt[\'lang_spelling\'] = \'' . addslashes($this->_req->post->spelling) . '\';', |
779
|
|
|
'~\$txt\[\'lang_rtl\'\]\s=\s[A-Za-z0-9]+;~' => '$txt[\'lang_rtl\'] = ' . (!empty($this->_req->post->rtl) ? 'true' : 'false') . ';', |
780
|
|
|
); |
781
|
|
|
$current_data = preg_replace(array_keys($replace_array), array_values($replace_array), $current_data); |
782
|
|
|
$fp = fopen($settings['default_theme_dir'] . '/languages/' . $context['lang_id'] . '/index.' . $context['lang_id'] . '.php', 'w+'); |
783
|
|
|
fwrite($fp, $current_data); |
784
|
|
|
fclose($fp); |
785
|
|
|
|
786
|
|
|
if ($this->_checkOpcache()) |
787
|
|
|
opcache_invalidate($settings['default_theme_dir'] . '/languages/' . $context['lang_id'] . '/index.' . $context['lang_id'] . '.php'); |
788
|
|
|
|
789
|
|
|
$madeSave = true; |
790
|
|
|
} |
791
|
|
|
|
792
|
|
|
// Quickly load index language entries. |
793
|
|
|
$old_txt = $txt; |
794
|
|
|
require($settings['default_theme_dir'] . '/languages/' . $context['lang_id'] . '/index.' . $context['lang_id'] . '.php'); |
795
|
|
|
$context['lang_file_not_writable_message'] = is_writable($settings['default_theme_dir'] . '/languages/' . $context['lang_id'] . '/index.' . $context['lang_id'] . '.php') ? '' : sprintf($txt['lang_file_not_writable'], $settings['default_theme_dir'] . '/languages/' . $context['lang_id'] . '/index.' . $context['lang_id'] . '.php'); |
796
|
|
|
|
797
|
|
|
// Setup the primary settings context. |
798
|
|
|
$context['primary_settings'] = array( |
799
|
|
|
'name' => Util::ucwords(strtr($context['lang_id'], array('_' => ' ', '-utf8' => ''))), |
800
|
|
|
'locale' => $txt['lang_locale'], |
801
|
|
|
'dictionary' => $txt['lang_dictionary'], |
802
|
|
|
'spelling' => $txt['lang_spelling'], |
803
|
|
|
'rtl' => $txt['lang_rtl'], |
804
|
|
|
); |
805
|
|
|
|
806
|
|
|
// Restore normal service. |
807
|
|
|
$txt = $old_txt; |
808
|
|
|
|
809
|
|
|
// Are we saving? |
810
|
|
|
$save_strings = array(); |
811
|
|
|
if (isset($this->_req->post->save_entries) && !empty($this->_req->post->entry)) |
812
|
|
|
{ |
813
|
|
|
checkSession(); |
814
|
|
|
validateToken('admin-mlang'); |
815
|
|
|
|
816
|
|
|
// Clean each entry! |
817
|
|
|
foreach ($this->_req->post->entry as $k => $v) |
818
|
|
|
{ |
819
|
|
|
// Only try to save if it's changed! |
820
|
|
|
if ($this->_req->post->entry[$k] != $this->_req->post->comp[$k]) |
821
|
|
|
$save_strings[$k] = cleanLangString($v, false); |
822
|
|
|
} |
823
|
|
|
} |
824
|
|
|
|
825
|
|
|
// If we are editing a file work away at that. |
826
|
|
|
if ($current_file) |
827
|
|
|
{ |
828
|
|
|
$context['entries_not_writable_message'] = is_writable($current_file) ? '' : sprintf($txt['lang_entries_not_writable'], $current_file); |
829
|
|
|
|
830
|
|
|
$entries = array(); |
831
|
|
|
|
832
|
|
|
// We can't just require it I'm afraid - otherwise we pass in all kinds of variables! |
833
|
|
|
$multiline_cache = ''; |
834
|
|
|
foreach (file($current_file) as $line) |
835
|
|
|
{ |
836
|
|
|
// Got a new entry? |
837
|
|
|
if ($line[0] == '$' && !empty($multiline_cache)) |
838
|
|
|
{ |
839
|
|
|
preg_match('~\$(helptxt|txt|editortxt)\[\'(.+)\'\]\s?=\s?(.+);~ms', strtr($multiline_cache, array("\r" => '')), $matches); |
840
|
|
View Code Duplication |
if (!empty($matches[3])) |
841
|
|
|
{ |
842
|
|
|
$entries[$matches[2]] = array( |
843
|
|
|
'type' => $matches[1], |
844
|
|
|
'full' => $matches[0], |
845
|
|
|
'entry' => $matches[3], |
846
|
|
|
); |
847
|
|
|
$multiline_cache = ''; |
848
|
|
|
} |
849
|
|
|
} |
850
|
|
|
$multiline_cache .= $line; |
851
|
|
|
} |
852
|
|
|
|
853
|
|
|
// Last entry to add? |
854
|
|
|
if ($multiline_cache) |
855
|
|
|
{ |
856
|
|
|
preg_match('~\$(helptxt|txt|editortxt)\[\'(.+)\'\]\s?=\s?(.+);~ms', strtr($multiline_cache, array("\r" => '')), $matches); |
857
|
|
View Code Duplication |
if (!empty($matches[3])) |
858
|
|
|
$entries[$matches[2]] = array( |
859
|
|
|
'type' => $matches[1], |
860
|
|
|
'full' => $matches[0], |
861
|
|
|
'entry' => $matches[3], |
862
|
|
|
); |
863
|
|
|
} |
864
|
|
|
|
865
|
|
|
// These are the entries we can definitely save. |
866
|
|
|
$final_saves = array(); |
867
|
|
|
|
868
|
|
|
$context['file_entries'] = array(); |
869
|
|
|
foreach ($entries as $entryKey => $entryValue) |
870
|
|
|
{ |
871
|
|
|
// Nowadays some entries have fancy keys, so better use something "portable" for the form |
872
|
|
|
$md5EntryKey = md5($entryKey); |
873
|
|
|
|
874
|
|
|
// Ignore some things we set separately. |
875
|
|
|
$ignore_files = array('lang_character_set', 'lang_locale', 'lang_dictionary', 'lang_spelling', 'lang_rtl'); |
876
|
|
|
if (in_array($entryKey, $ignore_files)) |
877
|
|
|
continue; |
878
|
|
|
|
879
|
|
|
// These are arrays that need breaking out. |
880
|
|
|
$arrays = array('days', 'days_short', 'months', 'months_titles', 'months_short', 'happy_birthday_author', 'karlbenson1_author', 'nite0859_author', 'zwaldowski_author', 'geezmo_author', 'karlbenson2_author'); |
881
|
|
|
if (in_array($entryKey, $arrays)) |
882
|
|
|
{ |
883
|
|
|
// Get off the first bits. |
884
|
|
|
$entryValue['entry'] = substr($entryValue['entry'], strpos($entryValue['entry'], '(') + 1, strrpos($entryValue['entry'], ')') - strpos($entryValue['entry'], '(')); |
885
|
|
|
$entryValue['entry'] = explode(',', strtr($entryValue['entry'], array(' ' => ''))); |
886
|
|
|
|
887
|
|
|
// Now create an entry for each item. |
888
|
|
|
$cur_index = 0; |
889
|
|
|
$save_cache = array( |
890
|
|
|
'enabled' => false, |
891
|
|
|
'entries' => array(), |
892
|
|
|
); |
893
|
|
|
foreach ($entryValue['entry'] as $id => $subValue) |
894
|
|
|
{ |
895
|
|
|
// Is this a new index? |
896
|
|
|
if (preg_match('~^(\d+)~', $subValue, $matches)) |
897
|
|
|
{ |
898
|
|
|
$cur_index = $matches[1]; |
899
|
|
|
$subValue = substr($subValue, strpos($subValue, '\'')); |
900
|
|
|
} |
901
|
|
|
|
902
|
|
|
// Clean up some bits. |
903
|
|
|
$subValue = strtr($subValue, array('"' => '', '\'' => '', ')' => '')); |
904
|
|
|
|
905
|
|
|
// Can we save? |
906
|
|
|
if (isset($save_strings[$md5EntryKey . '-+- ' . $cur_index])) |
907
|
|
|
{ |
908
|
|
|
$save_cache['entries'][$cur_index] = strtr($save_strings[$md5EntryKey . '-+- ' . $cur_index], array('\'' => '')); |
909
|
|
|
$save_cache['enabled'] = true; |
910
|
|
|
} |
911
|
|
|
else |
912
|
|
|
$save_cache['entries'][$cur_index] = $subValue; |
913
|
|
|
|
914
|
|
|
$context['file_entries'][] = array( |
915
|
|
|
'key' => $entryKey . '-+- ' . $cur_index, |
916
|
|
|
'display_key' => $entryKey . '-+- ' . $cur_index, |
917
|
|
|
'value' => $subValue, |
918
|
|
|
'rows' => 1, |
919
|
|
|
); |
920
|
|
|
$cur_index++; |
921
|
|
|
} |
922
|
|
|
|
923
|
|
|
// Do we need to save? |
924
|
|
|
if ($save_cache['enabled']) |
925
|
|
|
{ |
926
|
|
|
// Format the string, checking the indexes first. |
927
|
|
|
$items = array(); |
928
|
|
|
$cur_index = 0; |
929
|
|
|
foreach ($save_cache['entries'] as $k2 => $v2) |
930
|
|
|
{ |
931
|
|
|
// Manually show the custom index. |
932
|
|
|
if ($k2 != $cur_index) |
933
|
|
|
{ |
934
|
|
|
$items[] = $k2 . ' => \'' . $v2 . '\''; |
935
|
|
|
$cur_index = $k2; |
936
|
|
|
} |
937
|
|
|
else |
938
|
|
|
$items[] = '\'' . $v2 . '\''; |
939
|
|
|
|
940
|
|
|
$cur_index++; |
941
|
|
|
} |
942
|
|
|
|
943
|
|
|
// Now create the string! |
944
|
|
|
$final_saves[$entryKey] = array( |
945
|
|
|
'find' => $entryValue['full'], |
946
|
|
|
'replace' => '$' . $entryValue['type'] . '[\'' . $entryKey . '\'] = array(' . implode(', ', $items) . ');', |
947
|
|
|
); |
948
|
|
|
} |
949
|
|
|
} |
950
|
|
|
else |
951
|
|
|
{ |
952
|
|
|
// Saving? |
953
|
|
|
if (isset($save_strings[$md5EntryKey]) && $save_strings[$md5EntryKey] != $entryValue['entry']) |
954
|
|
|
{ |
955
|
|
|
// @todo Fix this properly. |
956
|
|
|
if ($save_strings[$md5EntryKey] == '') |
957
|
|
|
$save_strings[$md5EntryKey] = '\'\''; |
958
|
|
|
|
959
|
|
|
// Set the new value. |
960
|
|
|
$entryValue['entry'] = $save_strings[$md5EntryKey]; |
961
|
|
|
|
962
|
|
|
// And we know what to save now! |
963
|
|
|
$final_saves[$entryKey] = array( |
964
|
|
|
'find' => $entryValue['full'], |
965
|
|
|
'replace' => '$' . $entryValue['type'] . '[\'' . $entryKey . '\'] = ' . $save_strings[$md5EntryKey] . ';', |
966
|
|
|
); |
967
|
|
|
} |
968
|
|
|
|
969
|
|
|
$editing_string = cleanLangString($entryValue['entry'], true); |
970
|
|
|
$context['file_entries'][] = array( |
971
|
|
|
'key' => $md5EntryKey, |
972
|
|
|
'display_key' => $entryKey, |
973
|
|
|
'value' => $editing_string, |
974
|
|
|
'rows' => (int) (strlen($editing_string) / 38) + substr_count($editing_string, "\n") + 1, |
975
|
|
|
); |
976
|
|
|
} |
977
|
|
|
} |
978
|
|
|
|
979
|
|
|
// Any saves to make? |
980
|
|
|
if (!empty($final_saves)) |
981
|
|
|
{ |
982
|
|
|
checkSession(); |
983
|
|
|
|
984
|
|
|
$file_contents = implode('', file($current_file)); |
985
|
|
|
foreach ($final_saves as $save) |
986
|
|
|
$file_contents = strtr($file_contents, array($save['find'] => $save['replace'])); |
987
|
|
|
|
988
|
|
|
// Save the actual changes. |
989
|
|
|
$fp = fopen($current_file, 'w+'); |
990
|
|
|
fwrite($fp, strtr($file_contents, array("\r" => ''))); |
991
|
|
|
fclose($fp); |
992
|
|
|
|
993
|
|
|
if ($this->_checkOpcache()) |
994
|
|
|
opcache_invalidate($current_file); |
995
|
|
|
|
996
|
|
|
$madeSave = true; |
997
|
|
|
} |
998
|
|
|
|
999
|
|
|
// Another restore. |
1000
|
|
|
$txt = $old_txt; |
1001
|
|
|
} |
1002
|
|
|
|
1003
|
|
|
// If we saved, redirect. |
1004
|
|
|
if ($madeSave) |
1005
|
|
|
redirectexit('action=admin;area=languages;sa=editlang;lid=' . $context['lang_id']); |
1006
|
|
|
|
1007
|
|
|
createToken('admin-mlang'); |
1008
|
|
|
} |
1009
|
|
|
|
1010
|
|
|
/** |
1011
|
|
|
* Edit language related settings. |
1012
|
|
|
* |
1013
|
|
|
* - Accessed by ?action=admin;area=languages;sa=settings |
1014
|
|
|
* - This method handles the display, allows to edit, and saves the result |
1015
|
|
|
* for the _languageSettings form. |
1016
|
|
|
* |
1017
|
|
|
* @event integrate_save_language_settings |
1018
|
|
|
*/ |
1019
|
|
|
public function action_languageSettings_display() |
1020
|
|
|
{ |
1021
|
|
|
global $scripturl, $context, $txt; |
1022
|
|
|
|
1023
|
|
|
// Initialize the form |
1024
|
|
|
$settingsForm = new Settings_Form(Settings_Form::FILE_ADAPTER); |
1025
|
|
|
|
1026
|
|
|
// Initialize it with our settings |
1027
|
|
|
$settingsForm->setConfigVars($this->_settings()); |
1028
|
|
|
|
1029
|
|
|
// Warn the user if the backup of Settings.php failed. |
1030
|
|
|
$settings_not_writable = !is_writable(BOARDDIR . '/Settings.php'); |
1031
|
|
|
$settings_backup_fail = !@is_writable(BOARDDIR . '/Settings_bak.php') || !@copy(BOARDDIR . '/Settings.php', BOARDDIR . '/Settings_bak.php'); |
1032
|
|
|
|
1033
|
|
|
// Saving settings? |
1034
|
|
|
if (isset($this->_req->query->save)) |
1035
|
|
|
{ |
1036
|
|
|
checkSession(); |
1037
|
|
|
|
1038
|
|
|
call_integration_hook('integrate_save_language_settings'); |
1039
|
|
|
|
1040
|
|
|
$settingsForm->setConfigValues((array) $this->_req->post); |
1041
|
|
|
$settingsForm->save(); |
1042
|
|
|
redirectexit('action=admin;area=languages;sa=settings'); |
1043
|
|
|
} |
1044
|
|
|
|
1045
|
|
|
// Setup the template stuff. |
1046
|
|
|
$context['post_url'] = $scripturl . '?action=admin;area=languages;sa=settings;save'; |
1047
|
|
|
$context['settings_title'] = $txt['language_settings']; |
1048
|
|
|
$context['save_disabled'] = $settings_not_writable; |
1049
|
|
|
|
1050
|
|
View Code Duplication |
if ($settings_not_writable) |
1051
|
|
|
{ |
1052
|
|
|
$context['error_type'] = 'notice'; |
1053
|
|
|
$context['settings_message'] = $txt['settings_not_writable']; |
1054
|
|
|
} |
1055
|
|
|
elseif ($settings_backup_fail) |
1056
|
|
|
{ |
1057
|
|
|
$context['error_type'] = 'notice'; |
1058
|
|
|
$context['settings_message'] = $txt['admin_backup_fail']; |
1059
|
|
|
} |
1060
|
|
|
|
1061
|
|
|
// Fill the config array in contextual data for the template. |
1062
|
|
|
$settingsForm->prepare(); |
1063
|
|
|
} |
1064
|
|
|
|
1065
|
|
|
/** |
1066
|
|
|
* Checks if the Zend Opcahce is installed, active and cmd functions available. |
1067
|
|
|
* |
1068
|
|
|
* @return bool |
1069
|
|
|
*/ |
1070
|
|
|
private function _checkOpcache() |
1071
|
|
|
{ |
1072
|
|
|
return (extension_loaded('Zend OPcache') && ini_get('opcache.enable') && stripos(BOARDDIR, ini_get('opcache.restrict_api')) !== 0); |
1073
|
|
|
} |
1074
|
|
|
|
1075
|
|
|
/** |
1076
|
|
|
* Load up all of the language settings |
1077
|
|
|
* |
1078
|
|
|
* @event integrate_modify_language_settings Use to add new config options |
1079
|
|
|
*/ |
1080
|
1 |
|
private function _settings() |
1081
|
|
|
{ |
1082
|
1 |
|
global $txt; |
1083
|
|
|
|
1084
|
|
|
// Warn the user if the backup of Settings.php failed. |
1085
|
1 |
|
$settings_not_writable = !is_writable(BOARDDIR . '/Settings.php'); |
1086
|
|
|
|
1087
|
|
|
$config_vars = array( |
1088
|
1 |
|
'language' => array('language', $txt['default_language'], 'file', 'select', array(), null, 'disabled' => $settings_not_writable), |
1089
|
1 |
|
array('userLanguage', $txt['userLanguage'], 'db', 'check', null, 'userLanguage'), |
1090
|
1 |
|
); |
1091
|
|
|
|
1092
|
1 |
|
call_integration_hook('integrate_modify_language_settings', array(&$config_vars)); |
1093
|
|
|
|
1094
|
|
|
// Get our languages. No cache. |
1095
|
1 |
|
$languages = getLanguages(false); |
1096
|
1 |
|
foreach ($languages as $lang) |
1097
|
1 |
|
$config_vars['language'][4][] = array($lang['filename'], strtr($lang['name'], array('-utf8' => ' (UTF-8)'))); |
1098
|
|
|
|
1099
|
1 |
|
return $config_vars; |
1100
|
|
|
} |
1101
|
|
|
|
1102
|
|
|
/** |
1103
|
|
|
* Return the form settings for use in admin search |
1104
|
|
|
*/ |
1105
|
1 |
|
public function settings_search() |
1106
|
|
|
{ |
1107
|
1 |
|
return $this->_settings(); |
1108
|
|
|
} |
1109
|
|
|
|
1110
|
|
|
/** |
1111
|
|
|
* Update the language in use |
1112
|
|
|
* |
1113
|
|
|
* @param string $language |
1114
|
|
|
*/ |
1115
|
|
|
private function updateLanguage($language) |
1116
|
|
|
{ |
1117
|
|
|
$configVars = array( |
1118
|
|
|
array('language', '', 'file') |
1119
|
|
|
); |
1120
|
|
|
$configValues = array( |
1121
|
|
|
'language' => $language |
1122
|
|
|
); |
1123
|
|
|
$settingsForm = new Settings_Form(Settings_Form::FILE_ADAPTER); |
1124
|
|
|
$settingsForm->setConfigVars($configVars); |
1125
|
|
|
$settingsForm->setConfigValues((array) $configValues); |
1126
|
|
|
$settingsForm->save(); |
1127
|
|
|
} |
1128
|
|
|
} |
1129
|
|
|
|
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
orexit
statements that have been added for debug purposes.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.