|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* This file contains the database work for languages. |
|
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
|
|
|
* @version 2.0 dev |
|
11
|
|
|
* |
|
12
|
|
|
*/ |
|
13
|
|
|
|
|
14
|
|
|
/** |
|
15
|
|
|
* Removes the given language from all members.. |
|
16
|
|
|
* |
|
17
|
|
|
* @package Languages |
|
18
|
|
|
* @param int $lang_id |
|
19
|
|
|
*/ |
|
20
|
|
|
function removeLanguageFromMember($lang_id) |
|
21
|
|
|
{ |
|
22
|
|
|
$db = database(); |
|
23
|
|
|
|
|
24
|
|
|
$db->query('', ' |
|
25
|
|
|
UPDATE {db_prefix}members |
|
26
|
|
|
SET lngfile = {string:empty_string} |
|
27
|
|
|
WHERE lngfile = {string:current_language}', |
|
28
|
|
|
array( |
|
29
|
|
|
'empty_string' => '', |
|
30
|
|
|
'current_language' => $lang_id, |
|
31
|
|
|
) |
|
32
|
|
|
); |
|
33
|
|
|
} |
|
34
|
|
|
|
|
35
|
|
|
/** |
|
36
|
|
|
* How many languages? |
|
37
|
|
|
* |
|
38
|
|
|
* - Callback for the list in action_edit(). |
|
39
|
|
|
* |
|
40
|
|
|
* @package Languages |
|
41
|
|
|
*/ |
|
42
|
|
|
function list_getNumLanguages() |
|
43
|
|
|
{ |
|
44
|
|
|
return count(getLanguages()); |
|
45
|
|
|
} |
|
46
|
|
|
|
|
47
|
|
|
/** |
|
48
|
|
|
* Fetch the actual language information. |
|
49
|
|
|
* |
|
50
|
|
|
* What it does: |
|
51
|
|
|
* |
|
52
|
|
|
* - Callback for $listOptions['get_items']['function'] in action_edit. |
|
53
|
|
|
* - Determines which languages are available by looking for the "index.{language}.php" file. |
|
54
|
|
|
* - Also figures out how many users are using a particular language. |
|
55
|
|
|
* |
|
56
|
|
|
* @package Languages |
|
57
|
|
|
*/ |
|
58
|
|
|
function list_getLanguages() |
|
59
|
|
|
{ |
|
60
|
|
|
global $settings, $language, $txt; |
|
61
|
|
|
|
|
62
|
|
|
$db = database(); |
|
63
|
|
|
|
|
64
|
|
|
$languages = array(); |
|
65
|
|
|
// Keep our old entries. |
|
66
|
|
|
$old_txt = $txt; |
|
67
|
|
|
$backup_actual_theme_dir = $settings['actual_theme_dir']; |
|
68
|
|
|
$backup_base_theme_dir = !empty($settings['base_theme_dir']) ? $settings['base_theme_dir'] : ''; |
|
69
|
|
|
|
|
70
|
|
|
// Override these for now. |
|
71
|
|
|
$settings['actual_theme_dir'] = $settings['base_theme_dir'] = $settings['default_theme_dir']; |
|
72
|
|
|
$all_languages = getLanguages(); |
|
73
|
|
|
|
|
74
|
|
|
// Put them back. |
|
75
|
|
|
$settings['actual_theme_dir'] = $backup_actual_theme_dir; |
|
76
|
|
|
if (!empty($backup_base_theme_dir)) |
|
77
|
|
|
$settings['base_theme_dir'] = $backup_base_theme_dir; |
|
78
|
|
|
else |
|
79
|
|
|
unset($settings['base_theme_dir']); |
|
80
|
|
|
|
|
81
|
|
|
// Get the language files and data... |
|
82
|
|
|
foreach ($all_languages as $lang) |
|
83
|
|
|
{ |
|
84
|
|
|
// Load the file to get the character set. |
|
85
|
|
|
require($lang['location']); |
|
86
|
|
|
|
|
87
|
|
|
$languages[$lang['filename']] = array( |
|
88
|
|
|
'id' => $lang['filename'], |
|
89
|
|
|
'count' => 0, |
|
90
|
|
|
'char_set' => 'UTF-8', |
|
91
|
|
|
'default' => $language == $lang['filename'] || ($language == '' && $lang['filename'] == 'english'), |
|
92
|
|
|
'locale' => $txt['lang_locale'], |
|
93
|
|
|
'name' => \ElkArte\Util::ucwords(strtr($lang['filename'], array('_' => ' ', '-utf8' => ''))), |
|
94
|
|
|
); |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
// Work out how many people are using each language. |
|
98
|
|
|
$request = $db->query('', ' |
|
99
|
|
|
SELECT lngfile, COUNT(*) AS num_users |
|
100
|
|
|
FROM {db_prefix}members |
|
101
|
|
|
GROUP BY lngfile', |
|
102
|
|
|
array( |
|
103
|
|
|
) |
|
104
|
|
|
); |
|
105
|
|
|
while ($row = $db->fetch_assoc($request)) |
|
106
|
|
|
{ |
|
107
|
|
|
// Default? |
|
108
|
|
|
if (empty($row['lngfile']) || !isset($languages[$row['lngfile']])) |
|
109
|
|
|
$row['lngfile'] = $language; |
|
110
|
|
|
|
|
111
|
|
|
if (!isset($languages[$row['lngfile']]) && isset($languages['english'])) |
|
112
|
|
|
$languages['english']['count'] += $row['num_users']; |
|
113
|
|
|
elseif (isset($languages[$row['lngfile']])) |
|
114
|
|
|
$languages[$row['lngfile']]['count'] += $row['num_users']; |
|
115
|
|
|
} |
|
116
|
|
|
$db->free_result($request); |
|
117
|
|
|
|
|
118
|
|
|
// Restore the current users language. |
|
119
|
|
|
$txt = $old_txt; |
|
120
|
|
|
|
|
121
|
|
|
// Return how many we have. |
|
122
|
|
|
return $languages; |
|
123
|
|
|
} |
|
124
|
|
|
|
|
125
|
|
|
/** |
|
126
|
|
|
* This function cleans language entries to/from display. |
|
127
|
|
|
* |
|
128
|
|
|
* @package Languages |
|
129
|
|
|
* |
|
130
|
|
|
* @param string $string |
|
131
|
|
|
* @param boolean $to_display |
|
132
|
|
|
* |
|
133
|
|
|
* @return null|string|string[] |
|
134
|
|
|
*/ |
|
135
|
|
|
function cleanLangString($string, $to_display = true) |
|
136
|
|
|
{ |
|
137
|
|
|
// If going to display we make sure it doesn't have any HTML in it - etc. |
|
138
|
|
|
$new_string = ''; |
|
139
|
|
|
if ($to_display) |
|
140
|
|
|
{ |
|
141
|
|
|
// Are we in a string (0 = no, 1 = single quote, 2 = parsed) |
|
142
|
|
|
$in_string = 0; |
|
143
|
|
|
$is_escape = false; |
|
144
|
|
|
$str_len = strlen($string); |
|
145
|
|
|
for ($i = 0; $i < $str_len; $i++) |
|
146
|
|
|
{ |
|
147
|
|
|
// Handle escapes first. |
|
148
|
|
|
if ($string[$i] == '\\') |
|
149
|
|
|
{ |
|
150
|
|
|
// Toggle the escape. |
|
151
|
|
|
$is_escape = !$is_escape; |
|
152
|
|
|
|
|
153
|
|
|
// If we're now escaped don't add this string. |
|
154
|
|
|
if ($is_escape) |
|
155
|
|
|
continue; |
|
156
|
|
|
} |
|
157
|
|
|
// Special case - parsed string with line break etc? |
|
158
|
|
|
elseif (($string[$i] == 'n' || $string[$i] == 't') && $in_string == 2 && $is_escape) |
|
159
|
|
|
{ |
|
160
|
|
|
// Put the escape back... |
|
161
|
|
|
$new_string .= $string[$i] == 'n' ? "\n" : "\t"; |
|
162
|
|
|
$is_escape = false; |
|
163
|
|
|
continue; |
|
164
|
|
|
} |
|
165
|
|
|
// Have we got a single quote? |
|
166
|
|
View Code Duplication |
elseif ($string[$i] == '\'') |
|
167
|
|
|
{ |
|
168
|
|
|
// Already in a parsed string, or escaped in a linear string, means we print it - otherwise something special. |
|
169
|
|
|
if ($in_string != 2 && ($in_string != 1 || !$is_escape)) |
|
170
|
|
|
{ |
|
171
|
|
|
// Is it the end of a single quote string? |
|
172
|
|
|
if ($in_string == 1) |
|
173
|
|
|
$in_string = 0; |
|
174
|
|
|
// Otherwise it's the start! |
|
175
|
|
|
else |
|
176
|
|
|
$in_string = 1; |
|
177
|
|
|
|
|
178
|
|
|
// Don't actually include this character! |
|
179
|
|
|
continue; |
|
180
|
|
|
} |
|
181
|
|
|
} |
|
182
|
|
|
// Otherwise a double quote? |
|
183
|
|
View Code Duplication |
elseif ($string[$i] == '"') |
|
184
|
|
|
{ |
|
185
|
|
|
// Already in a single quote string, or escaped in a parsed string, means we print it - otherwise something special. |
|
186
|
|
|
if ($in_string != 1 && ($in_string != 2 || !$is_escape)) |
|
187
|
|
|
{ |
|
188
|
|
|
// Is it the end of a double quote string? |
|
189
|
|
|
if ($in_string == 2) |
|
190
|
|
|
$in_string = 0; |
|
191
|
|
|
// Otherwise it's the start! |
|
192
|
|
|
else |
|
193
|
|
|
$in_string = 2; |
|
194
|
|
|
|
|
195
|
|
|
// Don't actually include this character! |
|
196
|
|
|
continue; |
|
197
|
|
|
} |
|
198
|
|
|
} |
|
199
|
|
|
// A join/space outside of a string is simply removed. |
|
200
|
|
|
elseif ($in_string == 0 && (empty($string[$i]) || $string[$i] == '.')) |
|
201
|
|
|
continue; |
|
202
|
|
|
// Start of a variable? |
|
203
|
|
|
elseif ($in_string == 0 && $string[$i] == '$') |
|
204
|
|
|
{ |
|
205
|
|
|
// Find the whole of it! |
|
206
|
|
|
preg_match('~([\$A-Za-z0-9\'\[\]_-]+)~', substr($string, $i), $matches); |
|
207
|
|
|
if (!empty($matches[1])) |
|
208
|
|
|
{ |
|
209
|
|
|
// Come up with some pseudo thing to indicate this is a var. |
|
210
|
|
|
// @todo Do better than this, please! |
|
211
|
|
|
$new_string .= '{%' . $matches[1] . '%}'; |
|
212
|
|
|
|
|
213
|
|
|
// We're not going to re-parse this. |
|
214
|
|
|
$i += strlen($matches[1]) - 1; |
|
215
|
|
|
} |
|
216
|
|
|
|
|
217
|
|
|
continue; |
|
218
|
|
|
} |
|
219
|
|
|
// Right, if we're outside of a string we have DANGER, DANGER! |
|
220
|
|
|
elseif ($in_string == 0) |
|
221
|
|
|
{ |
|
222
|
|
|
continue; |
|
223
|
|
|
} |
|
224
|
|
|
|
|
225
|
|
|
// Actually add the character to the string! |
|
226
|
|
|
$new_string .= $string[$i]; |
|
227
|
|
|
|
|
228
|
|
|
// If anything was escaped it ain't any longer! |
|
229
|
|
|
$is_escape = false; |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
// Un-html then re-html the whole thing! |
|
233
|
|
|
$new_string = \ElkArte\Util::htmlspecialchars(un_htmlspecialchars($new_string)); |
|
234
|
|
|
} |
|
235
|
|
|
else |
|
236
|
|
|
{ |
|
237
|
|
|
// Keep track of what we're doing... |
|
238
|
|
|
$in_string = 0; |
|
239
|
|
|
|
|
240
|
|
|
// This is for deciding whether to HTML a quote. |
|
241
|
|
|
$in_html = false; |
|
242
|
|
|
$str_len = strlen($string); |
|
243
|
|
|
for ($i = 0; $i < $str_len; $i++) |
|
244
|
|
|
{ |
|
245
|
|
|
// We don't do parsed strings apart from for breaks. |
|
246
|
|
|
if ($in_string == 2) |
|
247
|
|
|
{ |
|
248
|
|
|
$in_string = 0; |
|
249
|
|
|
$new_string .= '"'; |
|
250
|
|
|
} |
|
251
|
|
|
|
|
252
|
|
|
// Not in a string yet? |
|
253
|
|
|
if ($in_string != 1) |
|
254
|
|
|
{ |
|
255
|
|
|
$in_string = 1; |
|
256
|
|
|
$new_string .= ($new_string ? ' . ' : '') . '\''; |
|
257
|
|
|
} |
|
258
|
|
|
|
|
259
|
|
|
// Is this a variable? |
|
260
|
|
|
if ($string[$i] == '{' && $string[$i + 1] == '%' && $string[$i + 2] == '$') |
|
261
|
|
|
{ |
|
262
|
|
|
// Grab the variable. |
|
263
|
|
|
preg_match('~\{%([\$A-Za-z0-9\'\[\]_-]+)%\}~', substr($string, $i), $matches); |
|
264
|
|
|
if (!empty($matches[1])) |
|
265
|
|
|
{ |
|
266
|
|
|
if ($in_string == 1) |
|
267
|
|
|
$new_string .= '\' . '; |
|
268
|
|
|
elseif ($new_string) |
|
269
|
|
|
$new_string .= ' . '; |
|
270
|
|
|
|
|
271
|
|
|
$new_string .= $matches[1]; |
|
272
|
|
|
$i += strlen($matches[1]) + 3; |
|
273
|
|
|
$in_string = 0; |
|
274
|
|
|
} |
|
275
|
|
|
|
|
276
|
|
|
continue; |
|
277
|
|
|
} |
|
278
|
|
|
// Is this a lt sign? |
|
279
|
|
View Code Duplication |
elseif ($string[$i] == '<') |
|
280
|
|
|
{ |
|
281
|
|
|
// Probably HTML? |
|
282
|
|
|
if ($string[$i + 1] != ' ') |
|
283
|
|
|
$in_html = true; |
|
284
|
|
|
// Assume we need an entity... |
|
285
|
|
|
else |
|
286
|
|
|
{ |
|
287
|
|
|
$new_string .= '<'; |
|
288
|
|
|
continue; |
|
289
|
|
|
} |
|
290
|
|
|
} |
|
291
|
|
|
// What about gt? |
|
292
|
|
View Code Duplication |
elseif ($string[$i] == '>') |
|
293
|
|
|
{ |
|
294
|
|
|
// Will it be HTML? |
|
295
|
|
|
if ($in_html) |
|
296
|
|
|
$in_html = false; |
|
297
|
|
|
// Otherwise we need an entity... |
|
298
|
|
|
else |
|
299
|
|
|
{ |
|
300
|
|
|
$new_string .= '>'; |
|
301
|
|
|
continue; |
|
302
|
|
|
} |
|
303
|
|
|
} |
|
304
|
|
|
// Is it a slash? If so escape it... |
|
305
|
|
|
if ($string[$i] == '\\') |
|
306
|
|
|
$new_string .= '\\'; |
|
307
|
|
|
// The infamous double quote? |
|
308
|
|
|
elseif ($string[$i] == '"') |
|
309
|
|
|
{ |
|
310
|
|
|
// If we're in HTML we leave it as a quote - otherwise we entity it. |
|
311
|
|
|
if (!$in_html) |
|
312
|
|
|
{ |
|
313
|
|
|
$new_string .= '"'; |
|
314
|
|
|
continue; |
|
315
|
|
|
} |
|
316
|
|
|
} |
|
317
|
|
|
// A single quote? |
|
318
|
|
|
elseif ($string[$i] == '\'') |
|
319
|
|
|
{ |
|
320
|
|
|
// Must be in a string so escape it. |
|
321
|
|
|
$new_string .= '\\'; |
|
322
|
|
|
} |
|
323
|
|
|
|
|
324
|
|
|
// Finally add the character to the string! |
|
325
|
|
|
$new_string .= $string[$i]; |
|
326
|
|
|
} |
|
327
|
|
|
|
|
328
|
|
|
// If we ended as a string then close it off. |
|
329
|
|
|
if ($in_string == 1) |
|
330
|
|
|
$new_string .= '\''; |
|
331
|
|
|
elseif ($in_string == 2) |
|
332
|
|
|
$new_string .= '"'; |
|
333
|
|
|
} |
|
334
|
|
|
|
|
335
|
|
|
return $new_string; |
|
336
|
|
|
} |
|
337
|
|
|
|
|
338
|
|
|
/** |
|
339
|
|
|
* Gets a list of available languages from the mother ship |
|
340
|
|
|
* |
|
341
|
|
|
* - Will return a subset if searching, otherwise all available |
|
342
|
|
|
* |
|
343
|
|
|
* @package Languages |
|
344
|
|
|
* @return array |
|
|
|
|
|
|
345
|
|
|
*/ |
|
346
|
|
|
function list_getLanguagesList() |
|
347
|
|
|
{ |
|
348
|
|
|
global $context, $txt, $scripturl; |
|
349
|
|
|
|
|
350
|
|
|
// We're going to use this URL. |
|
351
|
|
|
// @todo no we are not, this needs to be changed - again |
|
352
|
|
|
$url = 'http://download.elkarte.net/fetch_language.php?version=' . urlencode(strtr(FORUM_VERSION, array('ElkArte ' => ''))); |
|
353
|
|
|
|
|
354
|
|
|
// Load the class file and stick it into an array. |
|
355
|
|
|
$language_list = new \ElkArte\XmlArray(fetch_web_data($url), true); |
|
356
|
|
|
|
|
357
|
|
|
// Check that the site responded and that the language exists. |
|
358
|
|
|
if (!$language_list->exists('languages')) |
|
359
|
|
|
$context['langfile_error'] = 'no_response'; |
|
360
|
|
|
elseif (!$language_list->exists('languages/language')) |
|
361
|
|
|
$context['langfile_error'] = 'no_files'; |
|
362
|
|
|
else |
|
363
|
|
|
{ |
|
364
|
|
|
$language_list = $language_list->path('languages[0]'); |
|
365
|
|
|
$lang_files = $language_list->set('language'); |
|
366
|
|
|
$languages = array(); |
|
367
|
|
|
foreach ($lang_files as $file) |
|
368
|
|
|
{ |
|
369
|
|
|
// Were we searching? |
|
370
|
|
|
if (!empty($context['elk_search_term']) && strpos($file->fetch('name'), \ElkArte\Util::strtolower($context['elk_search_term'])) === false) |
|
371
|
|
|
continue; |
|
372
|
|
|
|
|
373
|
|
|
$languages[] = array( |
|
374
|
|
|
'id' => $file->fetch('id'), |
|
375
|
|
|
'name' => \ElkArte\Util::ucwords($file->fetch('name')), |
|
376
|
|
|
'version' => $file->fetch('version'), |
|
377
|
|
|
'utf8' => $txt['yes'], |
|
378
|
|
|
'description' => $file->fetch('description'), |
|
379
|
|
|
'install_link' => '<a href="' . $scripturl . '?action=admin;area=languages;sa=downloadlang;did=' . $file->fetch('id') . ';' . $context['session_var'] . '=' . $context['session_id'] . '">' . $txt['add_language_elk_install'] . '</a>', |
|
380
|
|
|
); |
|
381
|
|
|
} |
|
382
|
|
|
if (empty($languages)) |
|
383
|
|
|
$context['langfile_error'] = 'no_files'; |
|
384
|
|
|
else |
|
385
|
|
|
return $languages; |
|
386
|
|
|
} |
|
387
|
|
|
} |
|
388
|
|
|
|
|
389
|
|
|
/** |
|
390
|
|
|
* Finds installed language files of type lang |
|
391
|
|
|
* |
|
392
|
|
|
* @param string $lang |
|
393
|
|
|
* |
|
394
|
|
|
* @return array|bool |
|
|
|
|
|
|
395
|
|
|
*/ |
|
396
|
|
|
function findPossiblePackages($lang) |
|
397
|
|
|
{ |
|
398
|
|
|
$db = database(); |
|
399
|
|
|
|
|
400
|
|
|
$request = $db->query('', ' |
|
401
|
|
|
SELECT |
|
402
|
|
|
id_install, filename |
|
403
|
|
|
FROM {db_prefix}log_packages |
|
404
|
|
|
WHERE package_id LIKE {string:contains_lang} |
|
405
|
|
|
AND install_state = {int:installed}', |
|
406
|
|
|
array( |
|
407
|
|
|
'contains_lang' => 'elk_' . $lang . '_contribs:elk_' . $lang . '', |
|
408
|
|
|
'installed' => 1, |
|
409
|
|
|
) |
|
410
|
|
|
); |
|
411
|
|
|
$file_name = ''; |
|
412
|
|
|
if ($db->num_rows($request) > 0) |
|
413
|
|
|
{ |
|
414
|
|
|
list ($pid, $file_name) = $db->fetch_row($request); |
|
415
|
|
|
} |
|
416
|
|
|
$db->free_result($request); |
|
417
|
|
|
|
|
418
|
|
|
if (!empty($pid)) |
|
419
|
|
|
return array($pid, $file_name); |
|
420
|
|
|
else |
|
421
|
|
|
return false; |
|
422
|
|
|
} |
|
423
|
|
|
|
This check compares the return type specified in the
@returnannotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.If the return type contains the type array, this check recommends the use of a more specific type like
String[]orarray<String>.