MDL-22015 AMOS - making room for new string manager
[moodle.git] / admin / lang.php
CommitLineData
f578af59 1<?php
f20c155a 2 /**
3 * Display the admin/language menu and process strings translation.
d9f4ba6f 4 *
5 * @param string $mode the mode of the script: null, "compare", "missing"
6 * @param string $currentfile the filename of the English file to edit (if mode==compare)
7 * @param bool $uselocal save translations into *_local pack?
f20c155a 8 */
31410e9a 9
1b6cbebf 10 $dbg = ''; // debug output;
11
045e9e24 12 require_once('../config.php');
6e4dc10f 13 require_once($CFG->libdir.'/adminlib.php');
326fc7ba 14
98240e51 15 admin_externalpage_setup('langedit');
31410e9a 16
957f6fc9 17 $context = get_context_instance(CONTEXT_SYSTEM);
2327394d 18
f20c155a 19 define('LANG_SUBMIT_REPEAT', 1); // repeat displaying submit button?
20 define('LANG_SUBMIT_REPEAT_EVERY', 20); // if so, after how many lines?
21 define('LANG_DISPLAY_MISSING_LINKS', 1); // display "go to first/next missing string" links?
22 define('LANG_DEFAULT_FILE', ''); // default file to translate. Empty allowed
1b6cbebf 23 define('LANG_DEFAULT_HELPFILE', ''); // default helpfile to translate. Empty allowed
f20c155a 24 define('LANG_LINK_MISSING_STRINGS', 1); // create links from "missing" page to "compare" page?
25 define('LANG_DEFAULT_USELOCAL', 0); // should *_utf8_local be used by default?
f024bea2 26 define('LANG_MISSING_TEXT_MAX_LEN', 60); // maximum length of the missing text to display
dd43d371 27 define('LANG_KEEP_ORPHANS', 1); // keep orphaned strings (i.e. strings w/o English reference)
d9f4ba6f 28 define('LANG_SEARCH_EXTRA', 1); // search lang files in extra locations
01724baa 29 define('LANG_ALWAYS_TEXTAREA', 1); // always use <textarea> even for short strings MDL-15738
f20c155a 30
1b6cbebf 31 $mode = optional_param('mode', '', PARAM_ALPHA);
32 if ($mode == 'helpfiles') {
33 // use different PARAM_ options according to mode
34 $currentfile = optional_param('currentfile', LANG_DEFAULT_HELPFILE, PARAM_PATH);
35 } else {
36 $currentfile = optional_param('currentfile', LANG_DEFAULT_FILE, PARAM_FILE);
37 }
38 $uselocal = optional_param('uselocal', -1, PARAM_INT);
f20c155a 39
40 if ($uselocal == -1) {
41 if (isset($SESSION->langtranslateintolocal)) {
42 $uselocal = $SESSION->langtranslateintolocal;
43 } else {
44 $uselocal = LANG_DEFAULT_USELOCAL;
45 }
46 } else {
47 $SESSION->langtranslateintolocal = $uselocal;
48 }
98240e51 49
2327394d 50 if (!has_capability('moodle/site:langeditmaster', $context, $USER->id, false)) {
51 // Force using _local
52 $uselocal = 1;
53 }
98240e51 54
2327394d 55 if (!has_capability('moodle/site:langeditmaster', $context, $USER->id, false) && (!$uselocal)) {
4fd532ea 56 print_error('cannoteditmasterlang', 'error');
98240e51 57 }
58
2327394d 59 if ((!has_capability('moodle/site:langeditlocal', $context, $USER->id, false)) && ($uselocal)) {
4fd532ea 60 print_error('cannotcustomizelocallang', 'error');
2327394d 61 }
98240e51 62
55e4b5f9 63 $strlanguage = get_string("language");
774ab660 64 $strcurrentlanguage = get_string("currentlanguage");
65 $strmissingstrings = get_string("missingstrings");
2bb407f5 66 $streditstrings = get_string("editstrings", 'admin');
67 $stredithelpdocs = get_string("edithelpdocs", 'admin');
774ab660 68 $strthislanguage = get_string("thislanguage");
99b112ee 69 $strgotofirst = get_string('gotofirst','admin');
fc935b30 70 $strfilestoredin = get_string('filestoredin', 'admin');
71 $strfilestoredinhelp = get_string('filestoredinhelp', 'admin');
72 $strswitchlang = get_string('switchlang', 'admin');
73 $strchoosefiletoedit = get_string('choosefiletoedit', 'admin');
45428e86 74 $streditennotallowed = get_string('langnoeditenglish', 'admin');
99b112ee 75 $strfilecreated = get_string('filecreated', 'admin');
344044b2 76 $strprev = get_string('previous');
77 $strnext = get_string('next');
98240e51 78 $strlocalstringcustomization = get_string('localstringcustomization', 'admin');
79 $strlangpackmaintaining = get_string('langpackmaintaining', 'admin');
80 $strnomissingstrings = get_string('nomissingstrings', 'admin');
d9f4ba6f 81 $streditingnoncorelangfile = get_string('editingnoncorelangfile', 'admin');
1b6cbebf 82 $strlanglocalpackage = get_string('langlocalpackage', 'admin');
83 $strlangmasterpackage = get_string('langmasterpackage', 'admin');
84 $strlangmasterenglish = get_string('langmasterenglish', 'admin');
2327394d 85
f20c155a 86 $currentlang = current_language();
e25cb710 87
774ab660 88 switch ($mode) {
89 case "missing":
f20c155a 90 // Missing array keys are not bugs here but missing strings
91 error_reporting(E_ALL ^ E_NOTICE);
cb011cfd 92 $title = $strmissingstrings;
774ab660 93 break;
94 case "compare":
2bb407f5 95 $title = $streditstrings;
774ab660 96 break;
1b6cbebf 97 case "helpfiles":
98 $title = $stredithelpdocs;
774ab660 99 default:
55e4b5f9 100 $title = $strlanguage;
774ab660 101 break;
102 }
632d6407 103
61ef8f9f 104 echo $OUTPUT->header();
db8c035f 105
106 // Prepare and render menu tabs
107 $firstrow = array();
108 $secondrow = array();
109 $inactive = NULL;
110 $activated = NULL;
111 $currenttab = $mode;
112 if ($uselocal) {
113 $inactive = array('uselocal');
114 $activated = array('uselocal');
115 } else {
116 $inactive = array('usemaster');
117 $activated = array('usemaster');
118 }
2327394d 119 if (has_capability('moodle/site:langeditlocal', $context, $USER->id, false)) {
98240e51 120 $firstrow[] = new tabobject('uselocal',
220a90c5 121 "$CFG->wwwroot/$CFG->admin/lang.php?mode=$mode&amp;currentfile=$currentfile&amp;uselocal=1",
2327394d 122 $strlocalstringcustomization );
123 }
124 if (has_capability('moodle/site:langeditmaster', $context, $USER->id, false)) {
125 $firstrow[] = new tabobject('usemaster',
220a90c5 126 "$CFG->wwwroot/$CFG->admin/lang.php?mode=$mode&amp;currentfile=$currentfile&amp;uselocal=0",
2327394d 127 $strlangpackmaintaining );
128 }
220a90c5 129 $secondrow[] = new tabobject('missing', "$CFG->wwwroot/$CFG->admin/lang.php?mode=missing", $strmissingstrings );
130 $secondrow[] = new tabobject('compare', "$CFG->wwwroot/$CFG->admin/lang.php?mode=compare", $streditstrings );
131 $secondrow[] = new tabobject('helpfiles', "$CFG->wwwroot/$CFG->admin/lang.php?mode=helpfiles", $stredithelpdocs );
db8c035f 132 $tabs = array($firstrow, $secondrow);
133 print_tabs($tabs, $currenttab, $inactive, $activated);
98240e51 134
774ab660 135
136 if (!$mode) {
1b6cbebf 137 // TODO this is a very nice place to put some translation statistics
20486a5a 138 echo $OUTPUT->box_start();
025583cd 139 $currlang = current_language();
0a5ce9dd 140 $langs = get_list_of_languages(false, true);
6fe3775a
PS
141 $select = new single_select(new moodle_url('/admin/lang.php'), 'lang', $langs, $currlang, null);
142 $select->label = $strcurrentlanguage.':';
143 $select->formid = 'chooselang';
144 echo $OUTPUT->render($select);
20486a5a 145 echo $OUTPUT->box_end();
73d6f52f 146 echo $OUTPUT->footer();
774ab660 147 exit;
148 }
31410e9a 149
774ab660 150 // Get a list of all the root files in the English directory
31410e9a 151
6ea1cd02 152 $langbase = $CFG->dataroot . '/lang';
bcfe401e 153 $enlangdir = "$CFG->dirroot/lang/en_utf8";
45428e86 154 if ($currentlang == 'en_utf8') {
155 $langdir = $enlangdir;
156 } else {
6ea1cd02 157 $langdir = "$langbase/$currentlang";
45428e86 158 }
6ea1cd02 159 $locallangdir = "$langbase/{$currentlang}_local";
1b6cbebf 160 $saveto = $uselocal ? $locallangdir : $langdir;
31410e9a 161
1b6cbebf 162 if (($mode == 'missing') || ($mode == 'compare')) {
163 // get the list of all English stringfiles
164 $stringfiles = lang_standard_locations();
165 if (LANG_SEARCH_EXTRA) {
166 $stringfiles += lang_extra_locations();
167 }
168 if (count($stringfiles) == 0) {
4fd532ea 169 print_error('cannotfindlang', 'error', '', 'English');
1b6cbebf 170 }
171 } elseif ($mode == 'helpfiles') {
172 $helpfiles = lang_help_standard_locations();
173 if (LANG_SEARCH_EXTRA) {
174 $helpfiles += lang_help_extra_locations();
175 }
176 if (count($helpfiles) == 0) {
4fd532ea 177 print_error("cannotfindhelp", 'error', '', 'English');
1b6cbebf 178 }
774ab660 179 }
31410e9a 180
1b6cbebf 181
182
183 if ($mode == 'missing') {
7988d9e7 184 if (!file_exists($langdir)) {
771dc7b2 185 print_error('invalidlangpack');
7988d9e7 186 }
187
f20c155a 188 // Following variables store the HTML output to be echo-ed
189 $m = '';
190 $o = '';
191
d9f4ba6f 192 $m_x = false;
193
194 // Total number of strings and missing strings
195 $totalcounter->strings = 0;
20486a5a 196 $totalcounter->missing = 0;
d9f4ba6f 197
774ab660 198 // For each file, check that a counterpart exists, then check all the strings
d9f4ba6f 199 foreach ($stringfiles as $stringfile) {
200 $location = $stringfile['location'];
201 $plugin = $stringfile['plugin'];
202 $prefix = $stringfile['prefix'];
203 $filename = $stringfile['filename'];
774ab660 204 unset($string);
20486a5a 205
d9f4ba6f 206 // Get some information about file locations:
207 // $enfilepath = the path to the English file distributed either in the core space or in plugin space
208 // $trfilepath = the path to the translated file distributed either in the lang pack or in plugin space
209 // $lcfilepath = the path to the _local customization
210 // $trfilename = the filename of the translated version of the file (including prefix for non-core files)
211 if ($location || $plugin) {
212 // non-core file in an extra location
213 $enfilepath = "$CFG->dirroot/$location/$plugin/lang/en_utf8/$filename";
214 $trfilepath = "$CFG->dirroot/$location/$plugin/lang/$currentlang/$filename";
215 $lcfilepath = "$locallangdir/$filename";
216 $trfilename = $filename;
217 if (!$m_x) {
218 $m .= '<hr />';
219 $m_x = true;
220 }
221 } else {
222 // core file in standard location
223 $enfilepath = "$CFG->dirroot/lang/en_utf8/$filename";
224 $trfilepath = "$langdir/$filename";
225 $lcfilepath = "$locallangdir/$filename";
226 $trfilename = $filename;
227 }
228 // $enstring = English strings distributed either in the core space or in plugin space
229 include($enfilepath);
1dfc37b8 230 $enstring = isset($string) ? $string : array();
774ab660 231 unset($string);
d9f4ba6f 232 ksort($enstring);
20486a5a 233
d9f4ba6f 234 //$lcstring = local customizations
235 $lcstring = array();
236 if (file_exists($lcfilepath)) {
237 include($lcfilepath);
238 $localfileismissing = 0;
1dfc37b8 239 if (isset($string) && is_array($string)) {
d9f4ba6f 240 $lcstring = $string;
241 }
242 unset($string);
243 ksort($lcstring);
244 } else {
245 $localfileismissing = 1;
246 }
145ec44b 247
d9f4ba6f 248 // $string = translated strings distibuted either in core lang pack or in plugin space
249 $string = array();
250 if (file_exists($trfilepath)) {
251 include($trfilepath);
f20c155a 252 $fileismissing = 0;
145ec44b 253 } else {
f20c155a 254 $fileismissing = 1;
8fbce1c8 255 $o .= $OUTPUT->notification(get_string("filemissing", "", $trfilepath), "notifyproblem");
145ec44b 256 }
f20c155a 257
258 $missingcounter = 0;
98240e51 259
d9f4ba6f 260 $first = true; // first missing string found in the file
261 // For all English strings in the current file check distributed translations and _local customizations
774ab660 262 foreach ($enstring as $key => $value) {
d9f4ba6f 263 $totalcounter->strings++;
264 $missingstring = false;
265 $missinglocalstring = false;
266 $translationsdiffer = false;
267 if (empty($string[$key]) and $string[$key] != "0") { // MDL-4735
268 // string is missing in distributed pack
269 $missingstring = true;
270 }
271 if (empty($lcstring[$key]) and $lcstring[$key] != "0") { // MDL-4735
272 // string is missing in _local customization
273 $missinglocalstring = true;
274 }
275 if (!$missingstring && !$missinglocalstring && ($lcstring[$key] != $string[$key])) {
276 $translationsdiffer = true;
277 }
278 if ($missingstring || $translationsdiffer) {
7f1717c6 279 $value = htmlspecialchars($value);
774ab660 280 $value = str_replace("$"."a", "\\$"."a", $value);
dcde9f02 281 $value = str_replace("%%","%",$value);
774ab660 282 if ($first) {
d9f4ba6f 283 $m .= "<a href=\"lang.php?mode=missing#$trfilename\">$trfilename";
f20c155a 284 $m .= $fileismissing ? '*' : '';
285 $m .= '</a> &nbsp; ';
d9f4ba6f 286 $o .= "<p><a name=\"$trfilename\"></a><b>".
287 get_string("stringsnotset","", $trfilepath)."</b></p><pre>";
774ab660 288 $first = false;
289 $somethingfound = true;
290 }
d9f4ba6f 291 if ($missingstring) {
292 $missingcounter++;
293 $totalcounter->missing++;
294 }
1559b68a 295 if ($translationsdiffer) {
296 $missingcounter++;
297 }
298 if (LANG_LINK_MISSING_STRINGS && ($missingstring || $translationsdiffer)) {
b32c296f 299 $missinglinkstart = "<a href=\"lang.php?mode=compare&amp;currentfile=$filename#$key\">";
f20c155a 300 $missinglinkend = '</a>';
301 } else {
302 $missinglinkstart = '';
303 $missinglinkend = '';
304 }
f024bea2 305 if (strlen($value) > LANG_MISSING_TEXT_MAX_LEN) {
8442e013 306 $value = lang_xhtml_save_substr($value, 0, LANG_MISSING_TEXT_MAX_LEN) . ' ...'; // MDL-8852
f024bea2 307 }
d9f4ba6f 308 if ($translationsdiffer) {
309 $o .= '// ';
310 }
311 $o .= "$"."string['".$missinglinkstart.$key.$missinglinkend."'] = \"$value\";";
312 if ($translationsdiffer) {
313 $o .= ' // differs from the translation in _local';
314 } elseif (!$missinglocalstring) {
315 $o .= ' // translated only in _local';
316 }
317 $o .= "\n";
774ab660 318 }
319 }
d2b12fef 320 if (!$first) {
f20c155a 321 $o .= '</pre><hr />';
d2b12fef 322 }
774ab660 323 }
98240e51 324
d9f4ba6f 325 if ($totalcounter->missing > 0) {
326 $totalcounter->missingpercent = sprintf('%02.1f', ($totalcounter->missing / $totalcounter->strings * 100));
2fff8846 327 echo $OUTPUT->heading(get_string('numberofstrings', 'admin', $totalcounter), 4);
d9f4ba6f 328 } else {
2fff8846 329 echo $OUTPUT->heading($strnomissingstrings, 4, 'notifysuccess');
d9f4ba6f 330 }
331
f20c155a 332 if ($m <> '') {
20486a5a 333 echo $OUTPUT->box($m, 'filenames');
f20c155a 334 }
d9f4ba6f 335
f20c155a 336 echo $o;
337
bcfe401e 338 if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/help", "CVS")) {
4fd532ea 339 print_error("cannotfindhelp", 'error', '', 'English');
774ab660 340 }
98240e51 341
774ab660 342 foreach ($files as $filekey => $file) { // check all the help files.
343 if (!file_exists("$langdir/help/$file")) {
8fbce1c8 344 echo $OUTPUT->notification(get_string("filemissing", "", "$langdir/help/$file"), 'notifyproblem');
774ab660 345 $somethingfound = true;
346 continue;
347 }
348 }
98240e51 349
bcfe401e 350 if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/docs", "CVS")) {
4fd532ea 351 print_error("cannotfinddoc", 'error', '', 'English');
774ab660 352 }
353 foreach ($files as $filekey => $file) { // check all the docs files.
354 if (!file_exists("$langdir/docs/$file")) {
8fbce1c8 355 echo $OUTPUT->notification(get_string("filemissing", "", "$langdir/docs/$file"), 'notifyproblem');
774ab660 356 $somethingfound = true;
357 continue;
358 }
359 }
98240e51 360
1c0200e0 361 if (!empty($somethingfound)) {
8fbce1c8 362 echo $OUTPUT->continue_button("lang.php");
774ab660 363 } else {
326fc7ba 364 notice(get_string("languagegood"), "lang.php" );
31410e9a 365 }
366
1b6cbebf 367 } else if ($mode == 'compare') {
98240e51 368
6ea1cd02 369 if (!file_exists($langbase) ){
370 if (!lang_make_directory($langbase) ){
4fd532ea 371 print_error('cannotcreatelangbase', 'error');
6ea1cd02 372 } else {
5a34b76b 373 echo '<div class="notifysuccess">Created directory '.
6ea1cd02 374 $langbase .'</div>'."<br />\n";
375 }
98240e51 376 }
f20c155a 377 if (!$uselocal && !file_exists($langdir)) {
378 if (!lang_make_directory($langdir)) {
4fd532ea 379 print_error('cannotcreatelangdir', 'error');
f20c155a 380 } else {
5a34b76b 381 echo '<div class="notifysuccess">Created directory '.
f20c155a 382 $langdir .'</div>'."<br />\n";
383 }
7988d9e7 384 }
f20c155a 385 if ($uselocal && !file_exists($locallangdir)) {
386 if (!lang_make_directory($locallangdir)) {
5a34b76b 387 echo '<div class="notifyproblem">ERROR: Could not create directory '.
f20c155a 388 $locallangdir .'</div>'."<br />\n";
389 $uselocal = 0;
390 } else {
5a34b76b 391 echo '<div class="notifysuccess">Created directory '.
f20c155a 392 $locallangdir .'</div>'."<br />\n";
393 }
394 }
98240e51 395
d9f4ba6f 396 if ($currentfile <> '') {
397 if (!$fileinfo = lang_get_file_info($currentfile, $stringfiles)) {
4fd532ea 398 print_error('cannotfindinfo', 'error', '', $currentfile);
d9f4ba6f 399 }
400 // check the filename is set up correctly, prevents bugs similar to MDL-10920
401 $location = $fileinfo['location'];
402 $plugin = $fileinfo['plugin'];
403 $prefix = $fileinfo['prefix'];
404 $filename = $fileinfo['filename'];
405 if ($location || $plugin) {
406 // file in an extra location
407 if ($currentfile != "{$prefix}{$plugin}.php") {
775f811a 408 print_error('filemismatch', 'error', '', (object)array('current'=>$currectfile, 'file'=>$prefix.$plugin.'.php'));
d9f4ba6f 409 }
410 if (!$uselocal) {
8fbce1c8 411 echo $OUTPUT->notification($streditingnoncorelangfile);
d9f4ba6f 412 $editable = false;
413 }
414 } else {
415 // file in standard location
416 if ($currentfile != $filename) {
775f811a 417 print_error('filemismatch', 'error', '', (object)array('current'=>$currectfile, 'file'=>$filename.'.php'));
d9f4ba6f 418 }
419 }
420
421 // Get some information about file locations:
422 // $enfilepath = the path to the English file distributed either in the core space or in plugin space
423 // $trfilepath = the path to the translated file distributed either in the lang pack or in plugin space
424 // $lcfilepath = the path to the _local customization
425 // $trfilename = the filename of the translated version of the file (including prefix for non-core files)
426 if ($location || $plugin) {
427 // non-core file in an extra location
428 $enfilepath = "$CFG->dirroot/$location/$plugin/lang/en_utf8/$filename";
429 $trfilepath = "$CFG->dirroot/$location/$plugin/lang/$currentlang/$filename";
430 $lcfilepath = "$locallangdir/$filename";
431 $trfilename = $filename;
432 } else {
433 // core file in standard location
434 $enfilepath = "$CFG->dirroot/lang/en_utf8/$filename";
435 $trfilepath = "$langdir/$filename";
436 $lcfilepath = "$locallangdir/$filename";
437 $trfilename = $filename;
438 }
439 }
440
094640f1 441 if (isset($_POST['currentfile'])){ // Save a file
4530d437 442 if (!confirm_sesskey()) {
5a2a5331 443 print_error('confirmsesskeybad', 'error');
4530d437 444 }
98240e51 445
5723bc6f 446 $newstrings = array();
98240e51 447
5723bc6f 448 foreach ($_POST as $postkey => $postval) {
449 $stringkey = lang_file_string_key($postkey);
450 $newstrings[$stringkey] = $postval;
451 }
98240e51 452
094640f1 453 unset($newstrings['currentfile']);
f20c155a 454
d456b18f 455 $packstring = array();
456 $saveinto = $langdir;
f20c155a 457 if ($uselocal) {
d456b18f 458 if(file_exists($trfilepath)) {
459 include($trfilepath);
460 if (isset($string)) {
461 $packstring = $string;
462 }
463 unset($string);
f20c155a 464 }
f20c155a 465 $saveinto = $locallangdir;
cb011cfd 466 }
20486a5a 467
f20c155a 468 if (lang_save_file($saveinto, $currentfile, $newstrings, $uselocal, $packstring)) {
8fbce1c8 469 echo $OUTPUT->notification(get_string("changessaved")." ($saveinto/$currentfile)", "notifysuccess");
f20c155a 470 } else {
775f811a 471 print_error('cannotsavefile', 'error', 'lang.php?mode=compare&amp;currentfile=$currentfile', $saveinto.'/'.$currentfile);
f20c155a 472 }
473 unset($packstring);
98240e51 474 }
cb011cfd 475
20486a5a 476 echo $OUTPUT->box_start('generalbox editstrings');
db8c035f 477 $menufiles = array();
d9f4ba6f 478 $menufiles_coregrp = 1;
479 foreach ($stringfiles as $stringfile) {
480 $item_key = $stringfile['filename'];
481 $item_label = $stringfile['filename'];
482 if ($stringfile['location'] != '' && $stringfile['plugin'] != '') {
483 $item_label .= ' ('.$stringfile['location'].'/'.$stringfile['plugin'].')';
484 if ($menufiles_coregrp == 1) {
485 $menufiles['extra'] = '------------';
486 $menufiles_coregrp = 0;
487 }
488 }
489 $menufiles[$item_key] = $item_label;
094640f1 490 }
1b6cbebf 491 $selectionlabel = '<code class="path">';
492 //$selectionlabel .= $strfilestoredin;
493 $selectionlabel .= $uselocal ? "{$currentlang}_local" : $currentlang;
494 $selectionlabel .= '/</code>';
6fe3775a
PS
495 $select = new single_select(new moodle_url('/admin/lang.php?mode=compare'), 'currentfile', $menufiles, $currentfile, array(''=>$strchoosefiletoedit));
496 $select->label = $selectionlabel;
497 $select->formid = 'choosefile';
498 echo $OUTPUT->render($select);
4bcc5118 499 echo $OUTPUT->help_icon('langswitchstorage', $strfilestoredinhelp);
20486a5a 500 echo $OUTPUT->box_end();
501
f20c155a 502 if ($currentfile <> '') {
45428e86 503 error_reporting(0);
d9f4ba6f 504 if (!isset($editable) || $editable) {
505 if (!file_exists("$saveto/$currentfile")) {
506 if (!@touch("$saveto/$currentfile")) {
2fff8846 507 echo $OUTPUT->heading(get_string("filemissing", "", "$saveto/$currentfile"), 4, 'error');
d9f4ba6f 508 } else {
2fff8846 509 echo $OUTPUT->heading($strfilecreated, 4, 'notifysuccess');
d9f4ba6f 510 }
511 }
512 if ($currentlang == "en_utf8" && !$uselocal) {
513 $editable = false;
2fff8846 514 echo $OUTPUT->heading($streditennotallowed, 4);
d9f4ba6f 515 } elseif ($f = fopen("$saveto/$currentfile","r+")) {
516 $editable = true;
517 fclose($f);
45428e86 518 } else {
d9f4ba6f 519 $editable = false;
8fbce1c8 520 echo $OUTPUT->notification(get_string("makeeditable", "", "$saveto/$currentfile"), 'notifyproblem');
f20c155a 521 }
cb011cfd 522 }
45428e86 523 error_reporting($CFG->debug);
98240e51 524
db8c035f 525 $o = ''; // stores the HTML output to be echo-ed
20486a5a 526
f20c155a 527 unset($string);
d9f4ba6f 528 include($enfilepath);
1dfc37b8 529 $enstring = isset($string) ? $string : array();
d9f4ba6f 530 //
1dfc37b8 531 // Following strings have moved into langconfig.php, but keep the here for backward compatibility
d9f4ba6f 532 //
f20c155a 533 if ($currentlang != 'en' and $currentfile == 'moodle.php') {
534 $enstring['thislanguage'] = "<< TRANSLATORS: Specify the name of your language here. If possible use Unicode Numeric Character References >>";
e3bc1066 535 $enstring['thischarset'] = "<< TRANSLATORS: Charset encoding - always use utf-8 >>";
f20c155a 536 $enstring['thisdirection'] = "<< TRANSLATORS: This string specifies the direction of your text, either left-to-right or right-to-left. Insert either 'ltr' or 'rtl' here. >>";
537 $enstring['parentlanguage'] = "<< TRANSLATORS: If your language has a Parent Language that Moodle should use when strings are missing from your language pack, then specify the code for it here. If you leave this blank then English will be used. Example: nl >>";
538 }
f20c155a 539 unset($string);
d9f4ba6f 540 ksort($enstring);
094640f1 541
d9f4ba6f 542 @include($lcfilepath);
f20c155a 543 $localstring = isset($string) ? $string : array();
544 unset($string);
d9f4ba6f 545 ksort($localstring);
98240e51 546
d9f4ba6f 547 @include($trfilepath);
548 $string = isset($string) ? $string : array();
549 ksort($string);
cb011cfd 550
094640f1 551 if ($editable) {
db8c035f 552 $o .= "<form id=\"$currentfile\" action=\"lang.php\" method=\"post\">";
553 $o .= '<div>';
f20c155a 554 }
db8c035f 555 $o .= "<table summary=\"\" width=\"100%\" class=\"translator\">";
f20c155a 556 $linescounter = 0;
557 $missingcounter = 0;
558 foreach ($enstring as $key => $envalue) {
559 $linescounter++ ;
560 if (LANG_SUBMIT_REPEAT && $editable && $linescounter % LANG_SUBMIT_REPEAT_EVERY == 0) {
db8c035f 561 $o .= '<tr><td>&nbsp;</td><td><br />';
e222eb89 562 $o .= '<input type="submit" tabindex="'.$missingcounter.'" name="update" value="'.get_string('savechanges').': '.$currentfile.'" />';
db8c035f 563 $o .= '<br />&nbsp;</td></tr>';
f20c155a 564 }
565 $envalue = nl2br(htmlspecialchars($envalue));
98240e51 566 $envalue = preg_replace('/(\$a\-\&gt;[a-zA-Z0-9]*|\$a)/', '<b>$0</b>', $envalue); // Make variables bold.
f20c155a 567 $envalue = str_replace("%%","%",$envalue);
568 $envalue = str_replace("\\","",$envalue); // Delete all slashes
569
db8c035f 570 $o .= "\n\n".'<tr class="';
57648935 571 if ($linescounter % 2 == 0) {
db8c035f 572 $o .= 'r0';
57648935 573 } else {
db8c035f 574 $o .= 'r1';
57648935 575 }
db8c035f 576 $o .= '">';
577 $o .= '<td dir="ltr" lang="en">';
b32c296f 578 $o .= '<span id="'.$key.'" class="stren">'.$envalue.'</span>';
db8c035f 579 $o .= '<br />'."\n";
580 $o .= '<span class="strkey">'.$key.'</span>';
581 $o .= '</td>'."\n";
f20c155a 582
45428e86 583 // Missing array keys are not bugs here but missing strings
584 error_reporting(E_ALL ^ E_NOTICE);
f20c155a 585 if ($uselocal) {
586 $value = lang_fix_value_from_file($localstring[$key]);
587 $value2 = lang_fix_value_from_file($string[$key]);
588 if ($value == '') {
589 $value = $value2;
590 }
774ab660 591 } else {
f20c155a 592 $value = lang_fix_value_from_file($string[$key]);
593 $value2 = lang_fix_value_from_file($localstring[$key]);
31410e9a 594 }
45428e86 595 error_reporting($CFG->debug);
1559b68a 596 $missingtarget = '';
597 $missingnext = '';
598 $missingprev = '';
599 $cellcolour = '';
600 $usetabindex = false;
f20c155a 601 if (!$value) {
1559b68a 602 // the string is not present in the pack being processed
f20c155a 603 if (!$value2) {
57648935 604 $cellcolour = 'class="bothmissing"';
a8d0a37a 605 $usetabindex = true;
f20c155a 606 } else {
57648935 607 $cellcolour = 'class="mastermissing"';
a8d0a37a 608 $usetabindex = true;
f20c155a 609 }
610 $missingcounter++;
611 if (LANG_DISPLAY_MISSING_LINKS) {
612 $missingtarget = '<a name="missing'.$missingcounter.'"></a>';
613 $missingnext = '<a href="#missing'.($missingcounter+1).'">'.
b5d0cafc 614 '<img src="' . $OUTPUT->pix_url('t/down') . '" class="iconsmall" alt="'.$strnext.'" /></a>';
f20c155a 615 $missingprev = '<a href="#missing'.($missingcounter-1).'">'.
b5d0cafc 616 '<img src="' . $OUTPUT->pix_url('t/up') . '" class="iconsmall" alt="'.$strprev.'" /></a>';
f20c155a 617 }
094640f1 618 } else {
1559b68a 619 // the string is translated in the pack being processed
b32c296f 620 if ($value <> $value2 && ($value2 <> '')) {
57648935 621 $cellcolour = 'class="localdifferent"';
a8d0a37a 622 $usetabindex = true;
1559b68a 623 $missingcounter++;
624 if (LANG_DISPLAY_MISSING_LINKS) {
625 $missingtarget = '<a name="missing'.$missingcounter.'"></a>';
626 $missingnext = '<a href="#missing'.($missingcounter+1).'">'.
b5d0cafc 627 '<img src="' . $OUTPUT->pix_url('t/down') . '" class="iconsmall" alt="'.$strnext.'" /></a>';
1559b68a 628 $missingprev = '<a href="#missing'.($missingcounter-1).'">'.
b5d0cafc 629 '<img src="' . $OUTPUT->pix_url('t/up') . '" class="iconsmall" alt="'.$strprev.'" /></a>';
1559b68a 630 }
094640f1 631 }
094640f1 632 }
094640f1 633
f20c155a 634 if ($editable) {
d9f4ba6f 635 $o .= '<td '.$cellcolour.' valign="top">';
636 if ($missingcounter > 1) {
637 $o .= $missingprev;
638 }
639 $o .= $missingtarget."\n";
f20c155a 640 if (isset($string[$key])) {
641 $valuelen = strlen($value);
642 } else {
643 $valuelen = strlen($envalue);
644 }
57648935 645 $cols=40;
a8d0a37a 646 if ($usetabindex) {
647 $tabindex = 'tabindex="'.$missingcounter.'"';
648 } else {
649 $tabindex = '';
650 }
f20c155a 651 if (strstr($value, "\r") or strstr($value, "\n") or $valuelen > $cols) {
652 $rows = ceil($valuelen / $cols);
a8d0a37a 653 $o .= '<textarea name="stringXXX'.lang_form_string_key($key).'" cols="'.$cols.'" rows="'.$rows.'" '.$tabindex.'>'.$value.'</textarea>'."\n";
f20c155a 654 } else {
655 if ($valuelen) {
656 $cols = $valuelen + 5;
657 }
01724baa 658 if (LANG_ALWAYS_TEXTAREA) {
659 $o .= '<textarea name="stringXXX'.lang_form_string_key($key).'" cols="'.$cols.'" rows="1" '.$tabindex.'>'.$value.'</textarea>'."\n";
660 } else {
661 $o .= '<input type="text" name="stringXXX'.lang_form_string_key($key).'" value="'.$value.'" size="'.$cols.'" '.$tabindex.' />';
662 }
f20c155a 663 }
664 if ($value2 <> '' && $value <> $value2) {
db8c035f 665 $o .= '<br /><span style="font-size:small">'.$value2.'</span>';
f20c155a 666 }
db8c035f 667 $o .= $missingnext . '</td>';
f20c155a 668
669 } else {
d9f4ba6f 670 $o .= '<td '.$cellcolour.' valign="top">'.$value.'<br />'.$value2.'</td>';
f20c155a 671 }
db8c035f 672 $o .= '</tr>'."\n";
31410e9a 673 }
f20c155a 674 if ($editable) {
db8c035f 675 $o .= '<tr><td>&nbsp;</td><td><br />';
d4a1fcaf 676 $o .= '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
db8c035f 677 $o .= '<input type="hidden" name="currentfile" value="'.$currentfile.'" />';
678 $o .= '<input type="hidden" name="mode" value="compare" />';
40c4ea50 679 $o .= '<input type="submit" name="update" tabindex="'.$missingcounter.'" value="'.get_string('savechanges').': '.$currentfile.'" />';
db8c035f 680 $o .= '</td></tr>';
f20c155a 681 }
db8c035f 682 $o .= '</table>';
fc3d4000 683 if ($editable) {
98240e51 684 $o .= '</div>';
db8c035f 685 $o .= '</form>';
686 }
687
688 if (LANG_DISPLAY_MISSING_LINKS) {
689 if ($missingcounter > 0) {
2fff8846 690 echo $OUTPUT->heading(get_string('numberofmissingstrings', 'admin', $missingcounter), 4);
db8c035f 691 if ($editable) {
2fff8846 692 echo $OUTPUT->heading('<a href="#missing1">'.$strgotofirst.'</a>', 4);
db8c035f 693 }
694 } else {
2fff8846 695 echo $OUTPUT->heading($strnomissingstrings, 4, 'notifysuccess');
db8c035f 696 }
fc3d4000 697 }
db8c035f 698 echo $o;
31410e9a 699
45428e86 700 } else {
701 // no $currentfile specified
db8c035f 702 // no useful information to display - maybe some help? instructions?
f20c155a 703 }
1b6cbebf 704
705 } elseif ($mode == 'helpfiles') {
706
707 $saveto = $saveto.'/help'; // the edited content will be saved to
708 $enlangdir = $enlangdir.'/help'; // English master help files localtion
709 $langdir = $langdir.'/help'; // current language master help files location
710 $locallangdir = $locallangdir.'/help'; // local modifications of help files location
711 $altdir = $uselocal ? $langdir : $locallangdir; // alternative to $saveto
712
713 $fileeditorrows = 10; // number of textareas' rows
714 $fileeditorcols = 100; // dtto cols
715 $filemissingmark = ' (***)'; // mark to add to non-existing or zero-length files
716 $fileoldmark = ' (old?)'; // mark to add to filenames in selection form if the English version is newer
717 $filetemplate = ''; // template for new files, e.g. CVS identification
718
719 if (isset($_POST['currentfile'])) { // Save a file
720 if (!confirm_sesskey()) {
5a2a5331 721 print_error('confirmsesskeybad', 'error');
1b6cbebf 722 }
723 if (lang_help_save_file($saveto, $currentfile, $_POST['filedata'])) {
8fbce1c8 724 echo $OUTPUT->notification(get_string("changessaved")." ($saveto/$currentfile)", "notifysuccess");
1b6cbebf 725 } else {
4fd532ea 726 print_error('cannotsavefile', 'error', 'lang.php?mode=compare&amp;currentfile=$currentfile', array($saveinto, $currentfile));
1b6cbebf 727 }
728 }
729
20486a5a 730 echo $OUTPUT->box_start('generalbox editstrings');
1b6cbebf 731 $menufiles = array();
732 $menufiles_coregrp = 1;
733 $origlocation = ''; // the location of the currentfile's English source will be stored here
734 $origplugin = ''; // dtto plugin
735 foreach ($helpfiles as $helppath => $helpfile) {
736 $item_key = $helpfile['filename'];
737 $item_label = $helpfile['filename'];
738 if ((!file_exists($saveto.'/'.$helpfile['filename'])) || (filesize($saveto.'/'.$helpfile['filename']) == 0)) {
739 $item_label .= $filemissingmark;
740 } else {
741 if (filemtime($saveto.'/'.$helpfile['filename']) < filemtime($helppath)) {
742 $item_label .= $fileoldmark;
743 }
744 if ($helpfile['location'] != '' && $helpfile['plugin'] != '') {
745 $item_label .= ' ('.$helpfile['location'].'/'.$helpfile['plugin'].')';
746 if ($menufiles_coregrp == 1) {
747 $menufiles['extra'] = '------------';
748 $menufiles_coregrp = 0;
749 }
750 }
751 }
752 $menufiles[$item_key] = $item_label;
753 if ($currentfile == $helpfile['filename']) {
754 $origlocation = $helpfile['location'];
755 $origplugin = $helpfile['plugin'];
756 }
757 }
758 $selectionlabel = '<code class="path">';
759 //$selectionlabel .= $strfilestoredin;
760 $selectionlabel .= $uselocal ? "{$currentlang}_local" : $currentlang;
761 $selectionlabel .= '/help/</code>';
6fe3775a
PS
762 $select = new single_select(new moodle_url('/admin/lang.php?mode=helpfiles'), 'currentfile', $menufiles, $currentfile, array(''=>$strchoosefiletoedit));
763 $select->label = $selectionlabel;
764 $select->formid = 'choosefile';
765 echo $OUTPUT->render($select);
4bcc5118 766 echo $OUTPUT->help_icon('langswitchstorage', $strfilestoredinhelp);
20486a5a 767 echo $OUTPUT->box_end();
1b6cbebf 768
769 if (!empty($currentfile)) {
770
771 if (!file_exists("$saveto/$currentfile")) {
772 $dbg .= "File does not exist: $saveto/$currentfile\n";
773 //check if directory exist
774 if (!file_exists(dirname("$saveto/$currentfile"))) {
775 if(!lang_make_directory(dirname("$saveto/$currentfile"))) {
776 echo ('Cannot create directory: '.dirname("$saveto/$currentfile"));
777 }
778 }
779 //
780 // file doesn't exist - let's check webserver's permission to create it
781 //
782 if (!@touch("$saveto/$currentfile")) {
783 //
784 // webserver is unable to create new file
785 //
8fbce1c8 786 echo $OUTPUT->notification(get_string('filemissing', '', "$saveto/$currentfile" ));
787 echo $OUTPUT->notification(get_string('makeeditable', '', "$saveto/$currentfile"));
1b6cbebf 788 $editable = false;
789 } else {
790 //
791 // webserver can create new file - we can delete it now and let
792 // it create again if its filesize() > 0
793 //
794 $editable = true;
795 unlink("$saveto/$currentfile");
796 }
797 } elseif (is_writable("$saveto/$currentfile")) {
798 $editable = true;
799 } else {
800 //
801 // file exists but it is not writeable by web server process :-(
802 //
803 $editable = false;
8fbce1c8 804 echo $OUTPUT->notification(get_string('makeeditable', '', "$saveto/$currentfile"));
1b6cbebf 805 }
806
807 // master en_utf8 in dataroot is not editable
808 if ((!$uselocal) && ($currentlang == 'en_utf8')) {
809 $editable = false;
810 }
811
812 echo '<div>';
813
814 if ($uselocal) {
4bcc5118
PS
815 $strsavetotitle = $strlanglocalpackage . $OUTPUT->help_icon('langpackages', $strlanglocalpackage);
816 $straltdirtitle = $strlangmasterpackage . $OUTPUT->help_icon('langpackages', $strlangmasterpackage);
1b6cbebf 817 } else {
4bcc5118
PS
818 $straltdirtitle = $strlanglocalpackage . $OUTPUT->help_icon('langpackages', $strlanglocalpackage);
819 $strsavetotitle = $strlangmasterpackage . $OUTPUT->help_icon('langpackages', $strlangmasterpackage);
1b6cbebf 820
821 }
822
823 if ($editable) {
824 // generate an editor for the current help file in $saveto
825 echo '<fieldset><legend>'.$strsavetotitle.'</legend>';
826 echo "<form id=\"helpfileeditor\" action=\"lang.php\" method=\"post\">";
d4a1fcaf 827 echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
1b6cbebf 828 echo '<input type="hidden" name="currentfile" value="'.$currentfile.'" />';
829 echo '<input type="hidden" name="mode" value="helpfiles" />';
ff9b4ea4 830 echo "<div class='mdl-align'>\n";
1b6cbebf 831 echo "<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"filedata\">";
832 if (file_exists("$saveto/$currentfile")) {
833 echo htmlspecialchars(file_get_contents("$saveto/$currentfile"));
834 } else {
835 echo ($filetemplate);
836 }
837 echo "</textarea>\n</div>\n";
85db96c5 838 echo '<div class="mdl-align"><input type="submit" value="'.get_string('savechanges').'" /></div>';
1b6cbebf 839 echo '</form>';
840 $preview_url = lang_help_preview_url($currentfile, !$uselocal);
841 if ($preview_url) {
75015e5f 842 echo $OUTPUT->action_link($preview_url, get_string('preview'), new popup_action('click', $preview_url));
1b6cbebf 843 }
844 echo '</fieldset>';
845 }
846
847 if (is_readable("$altdir/$currentfile")) {
848 // show the content of the same help file in alternative location
849 echo '<fieldset><legend>'.$straltdirtitle.'</legend>';
ff9b4ea4 850 echo "<div class='mdl-align'>\n";
1b6cbebf 851 echo "<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"\">";
852 if (file_exists("$altdir/$currentfile")) {
853 echo htmlspecialchars(file_get_contents("$altdir/$currentfile"));
854 } else {
855 echo ($filetemplate);
856 }
857 echo "</textarea>\n</div>\n";
858 $preview_url = lang_help_preview_url($currentfile, $uselocal);
859 if ($preview_url) {
75015e5f 860 echo $OUTPUT->action_link($preview_url, get_string('preview'), new popup_action('click', $preview_url));
1b6cbebf 861 }
862 echo '</fieldset>';
863 }
864
865 // show the content of the original English file either in core space or plugin space
866 if ($origlocation != '' && $origplugin != '') {
867 // non-core help file
868 $ensrc = "$CFG->dirroot/$origlocation/$origplugin/lang/en_utf8/help/$currentfile";
869 } else {
870 // core help file
871 $ensrc = "$enlangdir/$currentfile";
872 }
873 if (is_readable($ensrc)) {
874 echo '<fieldset><legend>'.$strlangmasterenglish;
4bcc5118 875 echo $OUTPUT->help_icon('langpackages', $strlangmasterenglish);
1b6cbebf 876 echo '</legend>';
ff9b4ea4 877 echo "<div class='mdl-align'>\n<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"\">";
1b6cbebf 878 echo htmlspecialchars(file_get_contents($ensrc));
879 echo "</textarea>\n</div>\n";
880 $preview_url = lang_help_preview_url($currentfile, true, 'en_utf8'); // do not display en_utf8_local
881 if ($preview_url) {
75015e5f 882 echo $OUTPUT->action_link($preview_url, get_string('preview'), new popup_action('click', $preview_url));
1b6cbebf 883 }
884 echo '</fieldset>';
885 }
886
887 echo '</div>'; // translator box
888 error_reporting($CFG->debug);
889 }
890
891 if (false && $CFG->debugdisplay && debugging('', DEBUG_DEVELOPER) ) {
892 echo '<hr />';
2fff8846 893 echo $OUTPUT->heading('Debugging info');
1b6cbebf 894 echo '<pre class="notifytiny">';
895 print_r($dbg);
896 print_r("\n\$currentfile = $currentfile");
897 print_r("\n\$enlangdir = $enlangdir");
898 print_r("\n\$langdir = $langdir");
899 print_r("\n\$locallangdir = $locallangdir");
900 print_r("\n\$saveto = $saveto");
901 print_r("\n\$altdir = $altdir");
902 print_r("\n\$origlocation = $origlocation");
903 print_r("\n\$origplugin = $origplugin");
904 print_r("\n\$ensrc = $ensrc");
905 print_r("\n\$helpfiles = ");
906 print_r($helpfiles);
907 echo '</pre>';
908 }
909
910 } // fi $mode == 'helpfiles'
911
31410e9a 912
73d6f52f 913 echo $OUTPUT->footer();
31410e9a 914
cb011cfd 915//////////////////////////////////////////////////////////////////////
916
f20c155a 917/**
918 * Save language translation file.
919 *
98240e51 920 * Thanks to Petri Asikainen for the original version of code
f20c155a 921 * used to save language files.
922 *
923 * @uses $CFG
924 * @uses $USER
925 * @param string $path Full pathname to the directory to use
926 * @param string $file File to overwrite
927 * @param array $strings Array of strings to write
928 * @param bool $local Should *_local version be saved?
929 * @param array $packstrings Array of default langpack strings (needed if $local)
930 * @return bool Created successfully?
98240e51 931 */
f20c155a 932function lang_save_file($path, $file, $strings, $local, $packstrings) {
452c1cb3 933 global $CFG, $USER;
dd43d371 934 if (LANG_KEEP_ORPHANS) {
935 // let us load the current content of the file
936 unset($string);
937 @include("$path/$file");
938 if (isset($string)) {
939 $orphans = $string;
940 unset($string);
941 } else {
942 $orphans = array();
943 }
944 }
945 // let us rewrite the file
f20c155a 946 if (!$f = @fopen("$path/$file","w")) {
cb011cfd 947 return false;
948 }
cb011cfd 949
950 fwrite($f, "<?PHP // \$Id\$ \n");
f20c155a 951 fwrite($f, " // $file - created with Moodle $CFG->release ($CFG->version)\n");
952 if ($local) {
953 fwrite($f, " // local modifications from $CFG->wwwroot\n");
954 }
955 fwrite($f, "\n\n");
cb011cfd 956 ksort($strings);
cb011cfd 957 foreach ($strings as $key => $value) {
f20c155a 958 @list($id, $stringname) = explode('XXX',$key);
dd43d371 959 $value = lang_fix_value_before_save($value);
9c9f7d77 960 if ($id == "string" and $value != ""){
a98fe272 961 if ((!$local) || (!isset($packstrings[$stringname])) || (lang_fix_value_from_file($packstrings[$stringname]) <> lang_fix_value_from_file($value))) {
fc3d4000 962 // Either we are saving the master language pack
a98fe272 963 // or the string is not saved in packstring - fixes PHP notices about missing key
fc3d4000 964 // or we are saving local language pack and the strings differ.
f20c155a 965 fwrite($f,"\$string['$stringname'] = '$value';\n");
fc3d4000 966 }
967 if (LANG_KEEP_ORPHANS && isset($orphans[$stringname])) {
968 unset($orphans[$stringname]);
f20c155a 969 }
cb011cfd 970 }
971 }
dd43d371 972 if (LANG_KEEP_ORPHANS) {
973 // let us add orphaned strings, i.e. already translated strings without the English referential source
974 foreach ($orphans as $key => $value) {
975 fwrite($f,"\$string['$key'] = '".lang_fix_value_before_save($value)."'; // ORPHANED\n");
976 }
977 }
de4214a5 978 fwrite($f,"\n?>\n");
cb011cfd 979 fclose($f);
cb011cfd 980 return true;
981}
982
f20c155a 983/**
dd43d371 984 * Fix value of the translated string after it is load from the file.
985 *
986 * These modifications are typically necessary to work with the same string coming from two sources.
987 * We need to compare the content of these sources and we want to have e.g. "This string\r\n"
98240e51 988 * to be the same as " This string\n".
f20c155a 989 *
990 * @param string $value Original string from the file
991 * @return string Fixed value
992 */
993function lang_fix_value_from_file($value='') {
994 $value = str_replace("\r","",$value); // Bad character caused by Windows
995 $value = preg_replace("/\n{3,}/", "\n\n", $value); // Collapse runs of blank lines
996 $value = trim($value); // Delete leading/trailing white space
997 $value = str_replace("\\","",$value); // Delete all slashes
998 $value = str_replace("%%","%",$value);
fc3d4000 999 $value = str_replace("&","&amp;",$value); // Fixes MDL-9248
f20c155a 1000 $value = str_replace("<","&lt;",$value);
1001 $value = str_replace(">","&gt;",$value);
1002 $value = str_replace('"',"&quot;",$value);
1003 return $value;
1004}
1005
dd43d371 1006/**
1007 * Fix value of the translated string before it is saved into the file
1008 *
1009 * @uses $CFG
1010 * @param string $value Raw string to be saved into the lang pack
1011 * @return string Fixed value
1012 */
1013function lang_fix_value_before_save($value='') {
1014 global $CFG;
1015 if ($CFG->lang != "zh_hk" and $CFG->lang != "zh_tw") { // Some MB languages include backslash bytes
1016 $value = str_replace("\\","",$value); // Delete all slashes
1017 }
ac20257a 1018 if (ini_get_bool('magic_quotes_sybase')) { // Unescape escaped sybase quotes
1019 $value = str_replace("''", "'", $value);
1020 }
93e1207e
DM
1021 // escape all embedded variables
1022 $value = str_replace('$', '\$', $value); // Add slashes for $
1023 // unescape placeholders: only $a and $a->something are allowed. All other $variables are left escaped
1024 $value = preg_replace('/\\\\\$a($|[^_a-zA-Z0-9\-]|\->[a-zA-Z0-9_]+)/', '$a\\1', $value);
dd43d371 1025 $value = str_replace("'", "\\'", $value); // Add slashes for '
1026 $value = str_replace('"', "\\\"", $value); // Add slashes for "
1027 $value = str_replace("%","%%",$value); // Escape % characters
1028 $value = str_replace("\r", "",$value); // Remove linefeed characters
1029 $value = trim($value); // Delete leading/trailing white space
1030 return $value;
1031}
1032
f20c155a 1033/**
1034 * Try and create a new language directory.
1035 *
1b6cbebf 1036 * Uses PHP>=5.0 syntax of mkdir and tries to create directories recursively.
1037 *
f20c155a 1038 * @uses $CFG
6ea1cd02 1039 * @param string $directory full path to the directory under $langbase
f20c155a 1040 * @return string|false Returns full path to directory if successful, false if not
1041 */
1042function lang_make_directory($dir, $shownotices=true) {
1043 global $CFG;
1044 umask(0000);
1045 if (! file_exists($dir)) {
1b6cbebf 1046 if (! @mkdir($dir, $CFG->directorypermissions, true)) { // recursive=true; PHP>=5.0 needed
f20c155a 1047 return false;
1048 }
1049 //@chmod($dir, $CFG->directorypermissions); // Just in case mkdir didn't do it
1050 }
1051 return $dir;
1052}
1053
dd43d371 1054/**
1055 * Return the string key name for use in HTML form.
1056 *
1057 * Required because '.' in form input names get replaced by '_' by PHP.
1058 *
1059 * @param string $keyfromfile The key name containing '.'
1060 * @return string The key name without '.'
1061 */
5723bc6f 1062function lang_form_string_key($keyfromfile) {
1063 return str_replace('.', '##46#', $keyfromfile); /// Derived from &#46, the ascii value for a period.
1064}
1065
dd43d371 1066/**
1067 * Return the string key name for use in file.
1068 *
1069 * Required because '.' in form input names get replaced by '_' by PHP.
1070 *
1071 * @param string $keyfromfile The key name without '.'
1072 * @return string The key name containing '.'
1073 */
5723bc6f 1074function lang_file_string_key($keyfromform) {
1075 return str_replace('##46#', '.', $keyfromform);
1076}
1077
8442e013 1078/**
1079 * Return the substring of the string and take care of XHTML compliance.
1080 *
1081 * There was a problem with pure substr() which could possibly produce XHTML parsing error:
1082 * substr('Marks &amp; Spencer', 0, 9) -> 'Marks &am' ... is not XHTML compliance
1083 * This function takes care of these cases. Fixes MDL-8852.
1084 *
1085 * Thanks to kovacsendre, the author of the function at http://php.net/substr
1086 *
1087 * @param string $str The original string
1088 * @param int $start Start position in the $value string
1089 * @param int $length Optional length of the returned substring
1090 * @return string The substring as returned by substr() with XHTML compliance
1091 * @todo Seems the function does not work with negative $start together with $length being set
1092 */
1093function lang_xhtml_save_substr($str, $start, $length = NULL) {
98240e51 1094 if ($length === 0) {
8442e013 1095 //stop wasting our time ;)
1096 return "";
1097 }
1098
1099 //check if we can simply use the built-in functions
1100 if (strpos($str, '&') === false) {
1101 // No entities. Use built-in functions
1102 if ($length === NULL) {
1103 return substr($str, $start);
1104 } else {
1105 return substr($str, $start, $length);
1106 }
1107 }
1108
1109 // create our array of characters and html entities
1110 $chars = preg_split('/(&[^;\s]+;)|/', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE);
1111 $html_length = count($chars);
1112
1113 // check if we can predict the return value and save some processing time, i.e.:
1114 // input string was empty OR
1115 // $start is longer than the input string OR
1116 // all characters would be omitted
1117 if (($html_length === 0) or ($start >= $html_length) or (isset($length) and ($length <= -$html_length))) {
1118 return '';
1119 }
1120
1121 //calculate start position
1122 if ($start >= 0) {
1123 $real_start = $chars[$start][1];
98240e51 1124 } else {
8442e013 1125 //start'th character from the end of string
1126 $start = max($start,-$html_length);
1127 $real_start = $chars[$html_length+$start][1];
1128 }
1129
1130 if (!isset($length)) {
1131 // no $length argument passed, return all remaining characters
1132 return substr($str, $real_start);
98240e51 1133 } elseif ($length > 0) {
8442e013 1134 // copy $length chars
98240e51 1135 if ($start+$length >= $html_length) {
8442e013 1136 // return all remaining characters
1137 return substr($str, $real_start);
98240e51 1138 } else {
8442e013 1139 //return $length characters
1140 return substr($str, $real_start, $chars[max($start,0)+$length][1] - $real_start);
1141 }
98240e51 1142 } else {
8442e013 1143 //negative $length. Omit $length characters from end
1144 return substr($str, $real_start, $chars[$html_length+$length][1] - $real_start);
1145 }
1146}
1147
16d86fda 1148/**
1149* Finds all English string files in the standard lang/en_utf8 location.
1150*
d9f4ba6f 1151* Core lang files should always be stored here and not in the module space (MDL-10920).
98240e51 1152* The English version of the file may be found in
16d86fda 1153* $CFG->dirroot/lang/en_utf8/filename
98240e51 1154* The localised version of the found file should be saved into
d9f4ba6f 1155* $CFG->dataroot/lang/currentlang[_local]/filename
16d86fda 1156* where "filename" is returned as a part of the file record.
1157*
1158* @return array Array of a file information. Compatible format with {@link lang_extra_locations()}
1159*/
1160function lang_standard_locations() {
1161 global $CFG;
1162 $files = array();
1163 // Standard location of master English string files.
1164 $places = array($CFG->dirroot.'/lang/en_utf8');
1165 foreach ($places as $place) {
1166 foreach (get_directory_list($place, '', false) as $file) {
1167 if ((substr($file, -4) == ".php") && ($file != "langconfig.php")) {
1168 $fullpath = $place.'/'.$file;
1169 $files[$fullpath] = array(
1170 'filename' => $file,
1171 'location' => '',
1172 'plugin' => '',
1173 'prefix' => '',
1174 );
1175 }
1176 }
1177 }
1178 return $files;
1179}
1180
1181/**
1182* Finds all English string files in non-standard location.
1183*
98240e51 1184* Searches for lang/en_utf8/*.php in various types of plugins (blocks, database presets, question types,
16d86fda 1185* 3rd party modules etc.) and returns an array of found files details.
1186*
98240e51 1187* The English version of the file may be found in
16d86fda 1188* $CFG->dirroot/location/plugin/lang/en_utf8/filename
98240e51 1189* The localised version of the found file should be saved into
d9f4ba6f 1190* $CFG->dataroot/lang/currentlang[_local]/prefix_plugin.php
16d86fda 1191* where "location", "plugin", "prefix" and "filename" are returned as a part of the file record.
1192*
1193* @return array Array of a file information. Compatible format with {@link lang_standard_locations()}
1194*/
1195function lang_extra_locations() {
1196 global $CFG;
1197 $files = array();
ef330d95 1198 $places = get_string_manager()->get_registered_plugin_types();
16d86fda 1199 foreach ($places as $prefix => $directories) {
4c0e52f0 1200 foreach ($directories as $directory) {
1201 foreach (get_list_of_plugins($directory) as $plugin) {
1202 $enlangdirlocation = $CFG->dirroot.'/'.$directory.'/'.$plugin.'/lang/en_utf8';
1203 foreach (get_directory_list($enlangdirlocation, '', false) as $file) {
1204 if ((substr($file, -4) == ".php") && ($file != "langconfig.php")) {
1205 $fullpath = $enlangdirlocation.'/'.$file;
1206 $files[$fullpath] = array(
1207 'filename' => $file,
1208 'location' => $directory,
1209 'plugin' => $plugin,
1210 'prefix' => $prefix,
1211 );
16d86fda 1212 }
1213 }
1214 }
1215 }
1216 }
1217 return $files;
1218}
1219
d9f4ba6f 1220/**
1221 * Lookup for a stringfile details.
1222 *
1223 * English files can be stored in several places (core space or module/plugin space). Their translations
1224 * go into the one directory - the current language pack. Therefore, the name of the stringfile may be
1225 * considered as a key of the list of all stringfiles.
1226 *
1227 * @param string $currentfile the filename
1228 * @param array $stringfiles the array of file info returned by {@link lang_extra_locations()}
1229 * @return array Array of a file information (filename, location, plugin, prefix) or null.
1230 */
1231function lang_get_file_info($currentfile, $stringfiles) {
1232 $found = false;
1233 foreach ($stringfiles as $path=>$stringfile) {
1234 if ($stringfile['filename'] == $currentfile) {
1235 $found = true;
1236 $ret = $stringfile;
1237 $ret['fullpath'] = $path;
1238 break;
1239 }
1240 }
1241 if ($found) {
1242 return $ret;
1243 } else {
1244 return null;
1245 }
1246}
5723bc6f 1247
1b6cbebf 1248/**
1249 * Returns all English help files in the standard lang/en_utf8/help location.
1250 *
1251 * Core help files should always be stored here and not in the module space (MDL-10920).
1252 * The English version of the file may be found in
1253 * $CFG->dirroot/lang/en_utf8/help/filename
1254 * The localised version of the found file should be saved into
1255 * $CFG->dataroot/lang/currentlang[_local]/help/filename
1256 * where "filename" is returned as a part of the file record.
1257 *
1258 * @return array Array of a file information. Compatible format with {@link lang_extra_locations()}
1259 */
1260function lang_help_standard_locations() {
1261 global $CFG;
1262 $files = array();
1263 // Standard location of master English help files.
1264 $places = array($CFG->dirroot.'/lang/en_utf8/help');
1265 foreach ($places as $place) {
1266 foreach (get_directory_list($place, 'CVS') as $file) {
1267 if ((substr($file, -5) == '.html') || (substr($file, -4) == '.txt' )) {
1268 $fullpath = $place.'/'.$file;
1269 $files[$fullpath] = array(
1270 'filename' => $file,
1271 'location' => '',
1272 'plugin' => '',
1273 'prefix' => '',
1274 );
1275 }
1276 }
1277 }
1278 return $files;
1279}
1280
1281/**
1282 * Returns all English help files in non-standard location.
1283 *
1284 * Searches for lang/en_utf8/help/* files in various types of plugins (blocks, database presets, question types,
1285 * 3rd party modules etc.) and returns an array of found files details.
1286 *
1287 * The English version of the file may be found in
1288 * $CFG->dirroot/location/plugin/lang/en_utf8/help/filename
1289 * The localised version of the found file should be saved into
1290 * $CFG->dataroot/lang/currentlang[_local]/help/prefix_plugin/filename (XXX is "prefix" here right?)
1291 * where "location", "plugin", "prefix" and "filename" are returned as a part of the file record.
1292 *
1293 * @return array Array of a file information. Compatible format with {@link lang_standard_locations()}
1294 */
1295function lang_help_extra_locations() {
1296 global $CFG;
1297 $files = array();
ef330d95 1298 $places = get_string_manager()->get_registered_plugin_types();
1b6cbebf 1299 foreach ($places as $prefix => $directories) {
4c0e52f0 1300 foreach ($directories as $directory) {
1301 foreach (get_list_of_plugins($directory) as $plugin) {
1302 $enlangdirlocation = $CFG->dirroot.'/'.$directory.'/'.$plugin.'/lang/en_utf8/help';
1303 foreach (get_directory_list($enlangdirlocation, 'CVS') as $file) {
1304 if ((substr($file, -5) == '.html') || (substr($file, -4) == '.txt' )) {
1305 $fullpath = $enlangdirlocation.'/'.$file;
1306 $files[$fullpath] = array(
1307 'filename' => $file,
1308 'location' => $directory,
1309 'plugin' => $plugin,
1310 'prefix' => $prefix,
1311 );
1b6cbebf 1312 }
1313 }
1314 }
1315 }
1316 }
1317 return $files;
1318}
1319
1320/**
1321 * Return a preview URL for help file, if available.
1322 *
1323 * @param string $currentfile The relative path to the help file, e.g. "assignment/types.html" - MDL-12291
1324 * @param bool $skiplocal Force displaying the helpfile from a master lang pack
1325 * @param string $forcelang Force language of the help, e.g. "en_utf8"
1326 * @return string $url
1327 */
1328function lang_help_preview_url($currentfile, $skiplocal=false, $forcelang = '') {
1329 $currentpathexp = explode('/', $currentfile);
1330 if (count($currentpathexp) > 1) {
1331 $url = '/help.php?module='.implode('/',array_slice($currentpathexp,0,count($currentpathexp)-1)).'&amp;file='.end($currentpathexp);
1332 } else {
1333 $url = '/help.php?module=moodle&amp;file='.$currentfile;
1334 }
1335 if ($skiplocal) {
1336 $url .= '&amp;skiplocal=1';
1337 }
1338 if ($forcelang) {
1339 $url .= '&amp;forcelang='.$forcelang;
1340 }
1341 return $url;
1342}
1343
1344
1345/**
1346 * Saves (overwrites) translated help file.
1347 *
1348 * @param string $helproot The path to the "help" folder
1349 * @param string $file The relative path to the html help file
1350 * @param string $content HTML data to be saved
1351 * @return bool False if save failed, true otherwise
1352 */
1353function lang_help_save_file($helproot, $file, $content) {
8fbce1c8 1354 global $CFG, $USER, $OUTPUT;
1b6cbebf 1355
1356 $content = str_replace("\r", "",$content); // Remove linefeed characters
1357 $content = preg_replace("/\n{3,}/", "\n\n", $content); // Collapse runs of blank lines
1358 $content = trim($content); // Delete leading/trailing whitespace
1359 if (is_readable("$helproot/$file") && filesize("$helproot/$file") > 0 && $content == '') {
8fbce1c8 1360 echo $OUTPUT->notification(get_string('langrmyourself', 'admin'));
1b6cbebf 1361 return true;
1362 }
1363
1364 error_reporting(0);
1365 if (!$f = fopen("$helproot/$file","w")) {
1366 error_reporting($CFG->debug);
1367 return false;
1368 }
1369 error_reporting($CFG->debug);
1370
294ce987 1371 fwrite($f, $content);
1b6cbebf 1372 fclose($f);
1373
1374 // Remove file if its empty
1375 if (filesize("$helproot/$file") == 0) {
1376 unlink("$helproot/$file");
1377 }
1378
1379 return true;
1380}
1381
1382
f578af59 1383