fixing grader report paging bar when groups are present
[moodle-pu.git] / admin / lang.php
blobebac3d372ebc9d20e0682cff8c965b96c411013f
1 <?PHP // $Id$
2 /**
3 * Display the admin/language menu and process strings translation.
4 */
6 require_once('../config.php');
7 require_once($CFG->libdir.'/adminlib.php');
9 admin_externalpage_setup('langedit');
11 $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
13 define('LANG_SUBMIT_REPEAT', 1); // repeat displaying submit button?
14 define('LANG_SUBMIT_REPEAT_EVERY', 20); // if so, after how many lines?
15 define('LANG_DISPLAY_MISSING_LINKS', 1); // display "go to first/next missing string" links?
16 define('LANG_DEFAULT_FILE', ''); // default file to translate. Empty allowed
17 define('LANG_LINK_MISSING_STRINGS', 1); // create links from "missing" page to "compare" page?
18 define('LANG_DEFAULT_USELOCAL', 0); // should *_utf8_local be used by default?
19 define('LANG_MISSING_TEXT_MAX_LEN', 60); // maximum length of the missing text to display
20 define('LANG_KEEP_ORPHANS', 1); // keep orphaned strings (i.e. strings w/o English reference)
22 $mode = optional_param('mode', '', PARAM_ALPHA);
23 $currentfile = optional_param('currentfile', LANG_DEFAULT_FILE, PARAM_FILE);
24 $uselocal = optional_param('uselocal', -1, PARAM_INT);
26 if ($uselocal == -1) {
27 if (isset($SESSION->langtranslateintolocal)) {
28 $uselocal = $SESSION->langtranslateintolocal;
29 } else {
30 $uselocal = LANG_DEFAULT_USELOCAL;
32 } else {
33 $SESSION->langtranslateintolocal = $uselocal;
36 if (!has_capability('moodle/site:langeditmaster', $context, $USER->id, false)) {
37 // Force using _local
38 $uselocal = 1;
41 if (!has_capability('moodle/site:langeditmaster', $context, $USER->id, false) && (!$uselocal)) {
42 print_error('cannoteditmasterlang');
45 if ((!has_capability('moodle/site:langeditlocal', $context, $USER->id, false)) && ($uselocal)) {
46 print_error('cannotcustomizelocallang');
49 $strlanguage = get_string("language");
50 $strcurrentlanguage = get_string("currentlanguage");
51 $strmissingstrings = get_string("missingstrings");
52 $streditstrings = get_string("editstrings", 'admin');
53 $stredithelpdocs = get_string("edithelpdocs", 'admin');
54 $strthislanguage = get_string("thislanguage");
55 $strgotofirst = get_string('gotofirst','admin');
56 $strfilestoredin = get_string('filestoredin', 'admin');
57 $strfilestoredinhelp = get_string('filestoredinhelp', 'admin');
58 $strswitchlang = get_string('switchlang', 'admin');
59 $strchoosefiletoedit = get_string('choosefiletoedit', 'admin');
60 $streditennotallowed = get_string('langnoeditenglish', 'admin');
61 $strfilecreated = get_string('filecreated', 'admin');
62 $strprev = get_string('previous');
63 $strnext = get_string('next');
64 $strlocalstringcustomization = get_string('localstringcustomization', 'admin');
65 $strlangpackmaintaining = get_string('langpackmaintaining', 'admin');
66 $strnomissingstrings = get_string('nomissingstrings', 'admin');
68 $currentlang = current_language();
70 switch ($mode) {
71 case "missing":
72 // Missing array keys are not bugs here but missing strings
73 error_reporting(E_ALL ^ E_NOTICE);
74 $title = $strmissingstrings;
75 break;
76 case "compare":
77 $title = $streditstrings;
78 break;
79 default:
80 $title = $strlanguage;
81 break;
83 $navigation = "<a href=\"lang.php\">$strlanguage</a> -> $title";
85 $crumbs[] = array('name' => $strlanguage, 'link' => "$CFG->wwwroot/admin/lang.php");
86 $navigation = build_navigation($crumbs);
88 admin_externalpage_print_header();
90 // Prepare and render menu tabs
91 $firstrow = array();
92 $secondrow = array();
93 $inactive = NULL;
94 $activated = NULL;
95 $currenttab = $mode;
96 if ($uselocal) {
97 $inactive = array('uselocal');
98 $activated = array('uselocal');
99 } else {
100 $inactive = array('usemaster');
101 $activated = array('usemaster');
103 if (has_capability('moodle/site:langeditlocal', $context, $USER->id, false)) {
104 $firstrow[] = new tabobject('uselocal',
105 $CFG->wwwroot."/admin/lang.php?mode=$mode&amp;currentfile=$currentfile&amp;uselocal=1",
106 $strlocalstringcustomization );
108 if (has_capability('moodle/site:langeditmaster', $context, $USER->id, false)) {
109 $firstrow[] = new tabobject('usemaster',
110 $CFG->wwwroot."/admin/lang.php?mode=$mode&amp;currentfile=$currentfile&amp;uselocal=0",
111 $strlangpackmaintaining );
113 $secondrow[] = new tabobject('missing', $CFG->wwwroot.'/admin/lang.php?mode=missing', $strmissingstrings );
114 $secondrow[] = new tabobject('compare', $CFG->wwwroot.'/admin/lang.php?mode=compare', $streditstrings );
115 // TODO
116 // langdoc.php functionality is planned to be merged into lang.php
117 $secondrow[] = new tabobject('langdoc', $CFG->wwwroot.'/admin/langdoc.php', $stredithelpdocs );
118 $tabs = array($firstrow, $secondrow);
119 print_tabs($tabs, $currenttab, $inactive, $activated);
122 if (!$mode) {
123 print_box_start();
124 $currlang = current_language();
125 $langs = get_list_of_languages(false, true);
126 popup_form ("$CFG->wwwroot/$CFG->admin/lang.php?lang=", $langs, "chooselang", $currlang, "", "", "", false, 'self', $strcurrentlanguage.':');
127 print_box_end();
128 admin_externalpage_print_footer();
129 exit;
132 // Get a list of all the root files in the English directory
134 $langbase = $CFG->dataroot . '/lang';
135 $enlangdir = "$CFG->dirroot/lang/en_utf8";
136 if ($currentlang == 'en_utf8') {
137 $langdir = $enlangdir;
138 } else {
139 $langdir = "$langbase/$currentlang";
141 $locallangdir = "$langbase/{$currentlang}_local";
143 if (! $stringfiles = get_directory_list($enlangdir, "", false)) {
144 error("Could not find English language pack!");
147 foreach ($stringfiles as $key => $file) {
148 if (substr($file, -4) != ".php") { //Avoid non php files to be showed
149 unset($stringfiles[$key]);
151 if ($file == "langconfig.php") { //Avoid langconfig.php to be showed
152 unset($stringfiles[$key]);
156 if ($mode == "missing") {
157 if (!file_exists($langdir)) {
158 error ('to edit this language pack, you need to put it in '.$CFG->dataroot.'/lang');
161 // Following variables store the HTML output to be echo-ed
162 $m = '';
163 $o = '';
165 // For each file, check that a counterpart exists, then check all the strings
166 foreach ($stringfiles as $file) {
167 unset($string);
168 include("$enlangdir/$file");
169 $enstring = $string;
171 ksort($enstring);
173 unset($string);
175 if (file_exists("$langdir/$file")) {
176 include("$langdir/$file");
177 $fileismissing = 0;
178 } else {
179 $fileismissing = 1;
180 // notify(get_string("filemissing", "", "$langdir/$file"));
181 $o .= '<div class="notifyproblem">'.get_string("filemissing", "", "$langdir/$file").'</div><br />';
182 $string = array();
185 $missingcounter = 0;
187 $first = true;
188 foreach ($enstring as $key => $value) {
189 if (empty($string[$key]) and $string[$key] != "0") { //bug fix 4735 mits
190 $value = htmlspecialchars($value);
191 $value = str_replace("$"."a", "\\$"."a", $value);
192 $value = str_replace("%%","%",$value);
193 if ($first) {
194 $m .= "<a href=\"lang.php?mode=missing#$file\">$file";
195 $m .= $fileismissing ? '*' : '';
196 $m .= '</a> &nbsp; ';
197 $o .= "<p><a name=\"$file\"></a><b>".get_string("stringsnotset","","$langdir/$file")."</b></p><pre>";
198 $first = false;
199 $somethingfound = true;
201 $missingcounter++;
202 if (LANG_LINK_MISSING_STRINGS) {
203 $missinglinkstart = "<a href=\"lang.php?mode=compare&amp;currentfile=$file#missing$missingcounter\">";
204 $missinglinkend = '</a>';
205 } else {
206 $missinglinkstart = '';
207 $missinglinkend = '';
209 if (strlen($value) > LANG_MISSING_TEXT_MAX_LEN) {
210 $value = lang_xhtml_save_substr($value, 0, LANG_MISSING_TEXT_MAX_LEN) . ' ...'; // MDL-8852
212 $o .= "$"."string['".$missinglinkstart.$key.$missinglinkend."'] = \"$value\";<br />";
215 if (!$first) {
216 $o .= '</pre><hr />';
220 if ($m <> '') {
221 print_box($m, 'filenames');
223 echo $o;
225 if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/help", "CVS")) {
226 error("Could not find English language help files!");
229 foreach ($files as $filekey => $file) { // check all the help files.
230 if (!file_exists("$langdir/help/$file")) {
231 echo "<p><font color=\"red\">".get_string("filemissing", "", "$langdir/help/$file")."</font></p>";
232 $somethingfound = true;
233 continue;
237 if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/docs", "CVS")) {
238 error("Could not find English language docs files!");
240 foreach ($files as $filekey => $file) { // check all the docs files.
241 if (!file_exists("$langdir/docs/$file")) {
242 echo "<p><font color=\"red\">".get_string("filemissing", "", "$langdir/docs/$file")."</font></p>";
243 $somethingfound = true;
244 continue;
248 if (!empty($somethingfound)) {
249 print_continue("lang.php");
250 } else {
251 notice(get_string("languagegood"), "lang.php" );
254 } else if ($mode == "compare") {
256 if (!file_exists($langbase) ){
257 if (!lang_make_directory($langbase) ){
258 error('ERROR: Could not create base lang directory ' . $langbase);
259 } else {
260 echo '<div class="notifysuccess">Created directory '.
261 $langbase .'</div>'."<br />\n";
264 if (!$uselocal && !file_exists($langdir)) {
265 if (!lang_make_directory($langdir)) {
266 error('ERROR: Could not create directory '.$langdir);
267 } else {
268 echo '<div class="notifysuccess">Created directory '.
269 $langdir .'</div>'."<br />\n";
272 if ($uselocal && !file_exists($locallangdir)) {
273 if (!lang_make_directory($locallangdir)) {
274 echo '<div class="notifyproblem">ERROR: Could not create directory '.
275 $locallangdir .'</div>'."<br />\n";
276 $uselocal = 0;
277 } else {
278 echo '<div class="notifysuccess">Created directory '.
279 $locallangdir .'</div>'."<br />\n";
283 if (isset($_POST['currentfile'])){ // Save a file
284 if (!confirm_sesskey()) {
285 error(get_string('confirmsesskeybad', 'error'));
288 $newstrings = array();
290 foreach ($_POST as $postkey => $postval) {
291 $stringkey = lang_file_string_key($postkey);
292 $newstrings[$stringkey] = $postval;
295 unset($newstrings['currentfile']);
297 if ($uselocal) {
298 include("$langdir/$currentfile");
299 if (isset($string)) {
300 $packstring = $string;
301 } else {
302 $packstring = array();
304 unset($string);
305 $saveinto = $locallangdir;
306 } else {
307 $packstring = array();
308 $saveinto = $langdir;
311 if (lang_save_file($saveinto, $currentfile, $newstrings, $uselocal, $packstring)) {
312 notify(get_string("changessaved")." ($saveinto/$currentfile)", "green");
313 } else {
314 error("Could not save the file '$saveinto/$currentfile'!", "lang.php?mode=compare&amp;currentfile=$currentfile");
316 unset($packstring);
319 print_box_start('generalbox editstrings');
320 $menufiles = array();
321 foreach ($stringfiles as $file) {
322 $menufiles[$file] = $file;
324 popup_form("$CFG->wwwroot/$CFG->admin/lang.php?mode=compare&amp;currentfile=", $menufiles, "choosefile",
325 $currentfile, $strchoosefiletoedit);
327 echo '<div class="filestorageinfobox">';
328 echo $strfilestoredin;
329 echo '<code class="path">';
330 echo $uselocal ? "{$currentlang}_local" : $currentlang;
331 echo '</code>';
332 helpbutton('langswitchstorage', $strfilestoredinhelp, 'moodle');
333 echo '</div>';
334 print_box_end();
336 if ($currentfile <> '') {
337 $saveto = $uselocal ? $locallangdir : $langdir;
338 error_reporting(0);
339 if (!file_exists("$saveto/$currentfile")) {
340 if (!@touch("$saveto/$currentfile")) {
341 print_heading(get_string("filemissing", "", "$saveto/$currentfile"), '', 4, 'error');
342 } else {
343 print_heading($strfilecreated, '', 4, 'notifysuccess');
346 if ($currentlang == "en_utf8" && !$uselocal) {
347 $editable = false;
348 print_heading($streditennotallowed, '', 4);
349 } elseif ($f = fopen("$saveto/$currentfile","r+")) {
350 $editable = true;
351 fclose($f);
352 } else {
353 $editable = false;
354 echo "<p><font size=\"1\">".get_string("makeeditable", "", "$saveto/$currentfile")."</font></p>";
356 error_reporting($CFG->debug);
358 $o = ''; // stores the HTML output to be echo-ed
359 unset($string);
360 include("$enlangdir/$currentfile");
361 $enstring = $string;
362 if ($currentlang != 'en' and $currentfile == 'moodle.php') {
363 $enstring['thislanguage'] = "<< TRANSLATORS: Specify the name of your language here. If possible use Unicode Numeric Character References >>";
364 $enstring['thischarset'] = "<< TRANSLATORS: Charset encoding - always use utf-8 >>";
365 $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. >>";
366 $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 >>";
368 ksort($enstring);
370 unset($string);
372 @include("$locallangdir/$currentfile");
373 $localstring = isset($string) ? $string : array();
374 unset($string);
376 @include("$langdir/$currentfile");
378 if ($editable) {
379 $o .= "<form id=\"$currentfile\" action=\"lang.php\" method=\"post\">";
380 $o .= '<div>';
382 $o .= "<table summary=\"\" width=\"100%\" class=\"translator\">";
383 $linescounter = 0;
384 $missingcounter = 0;
385 foreach ($enstring as $key => $envalue) {
386 $linescounter++ ;
387 if (LANG_SUBMIT_REPEAT && $editable && $linescounter % LANG_SUBMIT_REPEAT_EVERY == 0) {
388 $o .= '<tr><td>&nbsp;</td><td><br />';
389 $o .= '<input type="submit" name="update" value="'.get_string('savechanges').': '.$currentfile.'" />';
390 $o .= '<br />&nbsp;</td></tr>';
392 $envalue = nl2br(htmlspecialchars($envalue));
393 $envalue = preg_replace('/(\$a\-\&gt;[a-zA-Z0-9]*|\$a)/', '<b>$0</b>', $envalue); // Make variables bold.
394 $envalue = str_replace("%%","%",$envalue);
395 $envalue = str_replace("\\","",$envalue); // Delete all slashes
397 $o .= "\n\n".'<tr class="';
398 if ($linescounter % 2 == 0) {
399 $o .= 'r0';
400 } else {
401 $o .= 'r1';
403 $o .= '">';
404 $o .= '<td dir="ltr" lang="en">';
405 $o .= '<span class="stren">'.$envalue.'</span>';
406 $o .= '<br />'."\n";
407 $o .= '<span class="strkey">'.$key.'</span>';
408 $o .= '</td>'."\n";
410 // Missing array keys are not bugs here but missing strings
411 error_reporting(E_ALL ^ E_NOTICE);
412 if ($uselocal) {
413 $value = lang_fix_value_from_file($localstring[$key]);
414 $value2 = lang_fix_value_from_file($string[$key]);
415 if ($value == '') {
416 $value = $value2;
418 } else {
419 $value = lang_fix_value_from_file($string[$key]);
420 $value2 = lang_fix_value_from_file($localstring[$key]);
422 error_reporting($CFG->debug);
424 // Color highlighting:
425 // red #ef6868 - translation missing in both system and local pack
426 // yellow #feff7f - translation missing in system pack but is translated in local
427 // green #AAFFAA - translation present in both system and local but is different
428 if (!$value) {
429 if (!$value2) {
430 $cellcolour = 'class="bothmissing"';
431 } else {
432 $cellcolour = 'class="mastermissing"';
434 $missingcounter++;
435 if (LANG_DISPLAY_MISSING_LINKS) {
436 $missingtarget = '<a name="missing'.$missingcounter.'"></a>';
437 $missingnext = '<a href="#missing'.($missingcounter+1).'">'.
438 '<img src="' . $CFG->pixpath . '/t/down.gif" class="iconsmall" alt="'.$strnext.'" /></a>';
439 $missingprev = '<a href="#missing'.($missingcounter-1).'">'.
440 '<img src="' . $CFG->pixpath . '/t/up.gif" class="iconsmall" alt="'.$strprev.'" /></a>';
441 } else {
442 $missingtarget = '';
443 $missingnext = '';
444 $missingprev = '';
446 } else {
447 if ($value <> $value2 && $value2 <> '') {
448 $cellcolour = 'class="localdifferent"';
449 } else {
450 $cellcolour = '';
452 $missingtarget = '';
453 $missingnext = '';
454 $missingprev = '';
457 if ($editable) {
458 $o .= '<td '.$cellcolour.' valign="top">'. $missingprev . $missingtarget."\n";
459 if (isset($string[$key])) {
460 $valuelen = strlen($value);
461 } else {
462 $valuelen = strlen($envalue);
464 $cols=40;
465 if (strstr($value, "\r") or strstr($value, "\n") or $valuelen > $cols) {
466 $rows = ceil($valuelen / $cols);
467 $o .= '<textarea name="stringXXX'.lang_form_string_key($key).'" cols="'.$cols.'" rows="'.$rows.'">'.$value.'</textarea>'."\n";
468 } else {
469 if ($valuelen) {
470 $cols = $valuelen + 5;
472 $o .= '<input type="text" name="stringXXX'.lang_form_string_key($key).'" value="'.$value.'" size="'.$cols.'" />';
474 if ($value2 <> '' && $value <> $value2) {
475 $o .= '<br /><span style="font-size:small">'.$value2.'</span>';
477 $o .= $missingnext . '</td>';
479 } else {
480 $o .= '<td '.$cellcolour.' valign="top">'.$value.'</td>';
482 $o .= '</tr>'."\n";
484 if ($editable) {
485 $o .= '<tr><td>&nbsp;</td><td><br />';
486 $o .= '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
487 $o .= '<input type="hidden" name="currentfile" value="'.$currentfile.'" />';
488 $o .= '<input type="hidden" name="mode" value="compare" />';
489 $o .= '<input type="submit" name="update" value="'.get_string('savechanges').': '.$currentfile.'" />';
490 $o .= '</td></tr>';
492 $o .= '</table>';
493 if ($editable) {
494 $o .= '</div>';
495 $o .= '</form>';
498 if (LANG_DISPLAY_MISSING_LINKS) {
499 if ($missingcounter > 0) {
500 print_heading(get_string('numberofmissingstrings', 'admin', $missingcounter), '', 4);
501 if ($editable) {
502 print_heading('<a href="#missing1">'.$strgotofirst.'</a>', "", 4);
504 } else {
505 print_heading($strnomissingstrings, '', 4, 'notifysuccess');
508 echo $o;
510 } else {
511 // no $currentfile specified
512 // no useful information to display - maybe some help? instructions?
516 admin_externalpage_print_footer();
518 //////////////////////////////////////////////////////////////////////
521 * Save language translation file.
523 * Thanks to Petri Asikainen for the original version of code
524 * used to save language files.
526 * @uses $CFG
527 * @uses $USER
528 * @param string $path Full pathname to the directory to use
529 * @param string $file File to overwrite
530 * @param array $strings Array of strings to write
531 * @param bool $local Should *_local version be saved?
532 * @param array $packstrings Array of default langpack strings (needed if $local)
533 * @return bool Created successfully?
535 function lang_save_file($path, $file, $strings, $local, $packstrings) {
536 global $CFG, $USER;
537 if (LANG_KEEP_ORPHANS) {
538 // let us load the current content of the file
539 unset($string);
540 @include("$path/$file");
541 if (isset($string)) {
542 $orphans = $string;
543 unset($string);
544 } else {
545 $orphans = array();
548 // let us rewrite the file
549 if (!$f = @fopen("$path/$file","w")) {
550 return false;
553 fwrite($f, "<?PHP // \$Id\$ \n");
554 fwrite($f, " // $file - created with Moodle $CFG->release ($CFG->version)\n");
555 if ($local) {
556 fwrite($f, " // local modifications from $CFG->wwwroot\n");
558 fwrite($f, "\n\n");
559 ksort($strings);
560 foreach ($strings as $key => $value) {
561 @list($id, $stringname) = explode('XXX',$key);
562 $value = lang_fix_value_before_save($value);
563 if ($id == "string" and $value != ""){
564 if ((!$local) || (lang_fix_value_from_file($packstrings[$stringname]) <> lang_fix_value_from_file($value))) {
565 // Either we are saving the master language pack
566 // or we are saving local language pack and the strings differ.
567 fwrite($f,"\$string['$stringname'] = '$value';\n");
569 if (LANG_KEEP_ORPHANS && isset($orphans[$stringname])) {
570 unset($orphans[$stringname]);
574 if (LANG_KEEP_ORPHANS) {
575 // let us add orphaned strings, i.e. already translated strings without the English referential source
576 foreach ($orphans as $key => $value) {
577 fwrite($f,"\$string['$key'] = '".lang_fix_value_before_save($value)."'; // ORPHANED\n");
580 fwrite($f,"\n?>\n");
581 fclose($f);
582 return true;
586 * Fix value of the translated string after it is load from the file.
588 * These modifications are typically necessary to work with the same string coming from two sources.
589 * We need to compare the content of these sources and we want to have e.g. "This string\r\n"
590 * to be the same as " This string\n".
592 * @param string $value Original string from the file
593 * @return string Fixed value
595 function lang_fix_value_from_file($value='') {
596 $value = str_replace("\r","",$value); // Bad character caused by Windows
597 $value = preg_replace("/\n{3,}/", "\n\n", $value); // Collapse runs of blank lines
598 $value = trim($value); // Delete leading/trailing white space
599 $value = str_replace("\\","",$value); // Delete all slashes
600 $value = str_replace("%%","%",$value);
601 $value = str_replace("&","&amp;",$value); // Fixes MDL-9248
602 $value = str_replace("<","&lt;",$value);
603 $value = str_replace(">","&gt;",$value);
604 $value = str_replace('"',"&quot;",$value);
605 return $value;
609 * Fix value of the translated string before it is saved into the file
611 * @uses $CFG
612 * @param string $value Raw string to be saved into the lang pack
613 * @return string Fixed value
615 function lang_fix_value_before_save($value='') {
616 global $CFG;
617 if ($CFG->lang != "zh_hk" and $CFG->lang != "zh_tw") { // Some MB languages include backslash bytes
618 $value = str_replace("\\","",$value); // Delete all slashes
620 if (ini_get_bool('magic_quotes_sybase')) { // Unescape escaped sybase quotes
621 $value = str_replace("''", "'", $value);
623 $value = str_replace("'", "\\'", $value); // Add slashes for '
624 $value = str_replace('"', "\\\"", $value); // Add slashes for "
625 $value = str_replace("%","%%",$value); // Escape % characters
626 $value = str_replace("\r", "",$value); // Remove linefeed characters
627 $value = trim($value); // Delete leading/trailing white space
628 return $value;
632 * Try and create a new language directory.
634 * @uses $CFG
635 * @param string $directory full path to the directory under $langbase
636 * @return string|false Returns full path to directory if successful, false if not
638 function lang_make_directory($dir, $shownotices=true) {
639 global $CFG;
640 umask(0000);
641 if (! file_exists($dir)) {
642 if (! @mkdir($dir, $CFG->directorypermissions)) {
643 return false;
645 //@chmod($dir, $CFG->directorypermissions); // Just in case mkdir didn't do it
647 return $dir;
651 * Return the string key name for use in HTML form.
653 * Required because '.' in form input names get replaced by '_' by PHP.
655 * @param string $keyfromfile The key name containing '.'
656 * @return string The key name without '.'
658 function lang_form_string_key($keyfromfile) {
659 return str_replace('.', '##46#', $keyfromfile); /// Derived from &#46, the ascii value for a period.
663 * Return the string key name for use in file.
665 * Required because '.' in form input names get replaced by '_' by PHP.
667 * @param string $keyfromfile The key name without '.'
668 * @return string The key name containing '.'
670 function lang_file_string_key($keyfromform) {
671 return str_replace('##46#', '.', $keyfromform);
675 * Return the substring of the string and take care of XHTML compliance.
677 * There was a problem with pure substr() which could possibly produce XHTML parsing error:
678 * substr('Marks &amp; Spencer', 0, 9) -> 'Marks &am' ... is not XHTML compliance
679 * This function takes care of these cases. Fixes MDL-8852.
681 * Thanks to kovacsendre, the author of the function at http://php.net/substr
683 * @param string $str The original string
684 * @param int $start Start position in the $value string
685 * @param int $length Optional length of the returned substring
686 * @return string The substring as returned by substr() with XHTML compliance
687 * @todo Seems the function does not work with negative $start together with $length being set
689 function lang_xhtml_save_substr($str, $start, $length = NULL) {
690 if ($length === 0) {
691 //stop wasting our time ;)
692 return "";
695 //check if we can simply use the built-in functions
696 if (strpos($str, '&') === false) {
697 // No entities. Use built-in functions
698 if ($length === NULL) {
699 return substr($str, $start);
700 } else {
701 return substr($str, $start, $length);
705 // create our array of characters and html entities
706 $chars = preg_split('/(&[^;\s]+;)|/', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE);
707 $html_length = count($chars);
709 // check if we can predict the return value and save some processing time, i.e.:
710 // input string was empty OR
711 // $start is longer than the input string OR
712 // all characters would be omitted
713 if (($html_length === 0) or ($start >= $html_length) or (isset($length) and ($length <= -$html_length))) {
714 return '';
717 //calculate start position
718 if ($start >= 0) {
719 $real_start = $chars[$start][1];
720 } else {
721 //start'th character from the end of string
722 $start = max($start,-$html_length);
723 $real_start = $chars[$html_length+$start][1];
726 if (!isset($length)) {
727 // no $length argument passed, return all remaining characters
728 return substr($str, $real_start);
729 } elseif ($length > 0) {
730 // copy $length chars
731 if ($start+$length >= $html_length) {
732 // return all remaining characters
733 return substr($str, $real_start);
734 } else {
735 //return $length characters
736 return substr($str, $real_start, $chars[max($start,0)+$length][1] - $real_start);
738 } else {
739 //negative $length. Omit $length characters from end
740 return substr($str, $real_start, $chars[$html_length+$length][1] - $real_start);
745 * Finds all English string files in the standard lang/en_utf8 location.
747 * The English version of the file may be found in
748 * $CFG->dirroot/lang/en_utf8/filename
749 * The localised version of the found file should be saved into
750 * $CFG->dataroot/lang/current_lang[_local]/filename
751 * where "filename" is returned as a part of the file record.
753 * @return array Array of a file information. Compatible format with {@link lang_extra_locations()}
755 function lang_standard_locations() {
756 global $CFG;
757 $files = array();
758 // Standard location of master English string files.
759 $places = array($CFG->dirroot.'/lang/en_utf8');
760 foreach ($places as $place) {
761 foreach (get_directory_list($place, '', false) as $file) {
762 if ((substr($file, -4) == ".php") && ($file != "langconfig.php")) {
763 $fullpath = $place.'/'.$file;
764 $files[$fullpath] = array(
765 'filename' => $file,
766 'location' => '',
767 'plugin' => '',
768 'prefix' => '',
773 return $files;
777 * Finds all English string files in non-standard location.
779 * Searches for lang/en_utf8/*.php in various types of plugins (blocks, database presets, question types,
780 * 3rd party modules etc.) and returns an array of found files details.
782 * The English version of the file may be found in
783 * $CFG->dirroot/location/plugin/lang/en_utf8/filename
784 * The localised version of the found file should be saved into
785 * $CFG->dataroot/lang/current_lang[_local]/prefix_plugin.php
786 * where "location", "plugin", "prefix" and "filename" are returned as a part of the file record.
788 * @return array Array of a file information. Compatible format with {@link lang_standard_locations()}
790 function lang_extra_locations() {
791 global $CFG;
792 $files = array();
793 $places = places_to_search_for_lang_strings();
794 foreach ($places as $prefix => $directories) {
795 if ($prefix != '__exceptions') {
796 foreach ($directories as $directory) {
797 foreach (get_list_of_plugins($directory) as $plugin) {
798 $enlangdirlocation = $CFG->dirroot.'/'.$directory.'/'.$plugin.'/lang/en_utf8';
799 foreach (get_directory_list($enlangdirlocation, '', false) as $file) {
800 if ((substr($file, -4) == ".php") && ($file != "langconfig.php")) {
801 $fullpath = $enlangdirlocation.'/'.$file;
802 $files[$fullpath] = array(
803 'filename' => $file,
804 'location' => $directory,
805 'plugin' => $plugin,
806 'prefix' => $prefix,
814 return $files;