MDL-12531, make the new member value available to all the affected plugins, thanks...
[moodle-linuxchix.git] / admin / lang.php
blobe9bcff11fd0d5100254e5d0ed9f8445c0ddc3451
1 <?PHP // $Id$
2 /**
3 * Display the admin/language menu and process strings translation.
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?
8 */
10 $dbg = ''; // debug output;
12 require_once('../config.php');
13 require_once($CFG->libdir.'/adminlib.php');
15 admin_externalpage_setup('langedit');
17 $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
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
23 define('LANG_DEFAULT_HELPFILE', ''); // default helpfile to translate. Empty allowed
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?
26 define('LANG_MISSING_TEXT_MAX_LEN', 60); // maximum length of the missing text to display
27 define('LANG_KEEP_ORPHANS', 1); // keep orphaned strings (i.e. strings w/o English reference)
28 define('LANG_SEARCH_EXTRA', 1); // search lang files in extra locations
30 $mode = optional_param('mode', '', PARAM_ALPHA);
31 if ($mode == 'helpfiles') {
32 // use different PARAM_ options according to mode
33 $currentfile = optional_param('currentfile', LANG_DEFAULT_HELPFILE, PARAM_PATH);
34 } else {
35 $currentfile = optional_param('currentfile', LANG_DEFAULT_FILE, PARAM_FILE);
37 $uselocal = optional_param('uselocal', -1, PARAM_INT);
39 if ($uselocal == -1) {
40 if (isset($SESSION->langtranslateintolocal)) {
41 $uselocal = $SESSION->langtranslateintolocal;
42 } else {
43 $uselocal = LANG_DEFAULT_USELOCAL;
45 } else {
46 $SESSION->langtranslateintolocal = $uselocal;
49 if (!has_capability('moodle/site:langeditmaster', $context, $USER->id, false)) {
50 // Force using _local
51 $uselocal = 1;
54 if (!has_capability('moodle/site:langeditmaster', $context, $USER->id, false) && (!$uselocal)) {
55 print_error('cannoteditmasterlang');
58 if ((!has_capability('moodle/site:langeditlocal', $context, $USER->id, false)) && ($uselocal)) {
59 print_error('cannotcustomizelocallang');
62 $strlanguage = get_string("language");
63 $strcurrentlanguage = get_string("currentlanguage");
64 $strmissingstrings = get_string("missingstrings");
65 $streditstrings = get_string("editstrings", 'admin');
66 $stredithelpdocs = get_string("edithelpdocs", 'admin');
67 $strthislanguage = get_string("thislanguage");
68 $strgotofirst = get_string('gotofirst','admin');
69 $strfilestoredin = get_string('filestoredin', 'admin');
70 $strfilestoredinhelp = get_string('filestoredinhelp', 'admin');
71 $strswitchlang = get_string('switchlang', 'admin');
72 $strchoosefiletoedit = get_string('choosefiletoedit', 'admin');
73 $streditennotallowed = get_string('langnoeditenglish', 'admin');
74 $strfilecreated = get_string('filecreated', 'admin');
75 $strprev = get_string('previous');
76 $strnext = get_string('next');
77 $strlocalstringcustomization = get_string('localstringcustomization', 'admin');
78 $strlangpackmaintaining = get_string('langpackmaintaining', 'admin');
79 $strnomissingstrings = get_string('nomissingstrings', 'admin');
80 $streditingnoncorelangfile = get_string('editingnoncorelangfile', 'admin');
81 $strlanglocalpackage = get_string('langlocalpackage', 'admin');
82 $strlangmasterpackage = get_string('langmasterpackage', 'admin');
83 $strlangmasterenglish = get_string('langmasterenglish', 'admin');
85 $currentlang = current_language();
87 switch ($mode) {
88 case "missing":
89 // Missing array keys are not bugs here but missing strings
90 error_reporting(E_ALL ^ E_NOTICE);
91 $title = $strmissingstrings;
92 break;
93 case "compare":
94 $title = $streditstrings;
95 break;
96 case "helpfiles":
97 $title = $stredithelpdocs;
98 default:
99 $title = $strlanguage;
100 break;
102 $navlinks[] = array('name' => $strlanguage, 'link' => "$CFG->wwwroot/$CFG->admin/lang.php", 'type' => 'misc');
103 $navigation = build_navigation($navlinks);
105 admin_externalpage_print_header();
107 // Prepare and render menu tabs
108 $firstrow = array();
109 $secondrow = array();
110 $inactive = NULL;
111 $activated = NULL;
112 $currenttab = $mode;
113 if ($uselocal) {
114 $inactive = array('uselocal');
115 $activated = array('uselocal');
116 } else {
117 $inactive = array('usemaster');
118 $activated = array('usemaster');
120 if (has_capability('moodle/site:langeditlocal', $context, $USER->id, false)) {
121 $firstrow[] = new tabobject('uselocal',
122 "$CFG->wwwroot/$CFG->admin/lang.php?mode=$mode&amp;currentfile=$currentfile&amp;uselocal=1",
123 $strlocalstringcustomization );
125 if (has_capability('moodle/site:langeditmaster', $context, $USER->id, false)) {
126 $firstrow[] = new tabobject('usemaster',
127 "$CFG->wwwroot/$CFG->admin/lang.php?mode=$mode&amp;currentfile=$currentfile&amp;uselocal=0",
128 $strlangpackmaintaining );
130 $secondrow[] = new tabobject('missing', "$CFG->wwwroot/$CFG->admin/lang.php?mode=missing", $strmissingstrings );
131 $secondrow[] = new tabobject('compare', "$CFG->wwwroot/$CFG->admin/lang.php?mode=compare", $streditstrings );
132 $secondrow[] = new tabobject('helpfiles', "$CFG->wwwroot/$CFG->admin/lang.php?mode=helpfiles", $stredithelpdocs );
133 $tabs = array($firstrow, $secondrow);
134 print_tabs($tabs, $currenttab, $inactive, $activated);
137 if (!$mode) {
138 // TODO this is a very nice place to put some translation statistics
139 print_box_start();
140 $currlang = current_language();
141 $langs = get_list_of_languages(false, true);
142 popup_form ("$CFG->wwwroot/$CFG->admin/lang.php?lang=", $langs, "chooselang", $currlang, "", "", "", false, 'self', $strcurrentlanguage.':');
143 print_box_end();
144 admin_externalpage_print_footer();
145 exit;
148 // Get a list of all the root files in the English directory
150 $langbase = $CFG->dataroot . '/lang';
151 $enlangdir = "$CFG->dirroot/lang/en_utf8";
152 if ($currentlang == 'en_utf8') {
153 $langdir = $enlangdir;
154 } else {
155 $langdir = "$langbase/$currentlang";
157 $locallangdir = "$langbase/{$currentlang}_local";
158 $saveto = $uselocal ? $locallangdir : $langdir;
160 if (($mode == 'missing') || ($mode == 'compare')) {
161 // get the list of all English stringfiles
162 $stringfiles = lang_standard_locations();
163 if (LANG_SEARCH_EXTRA) {
164 $stringfiles += lang_extra_locations();
166 if (count($stringfiles) == 0) {
167 error("Could not find English language pack!");
169 } elseif ($mode == 'helpfiles') {
170 $helpfiles = lang_help_standard_locations();
171 if (LANG_SEARCH_EXTRA) {
172 $helpfiles += lang_help_extra_locations();
174 if (count($helpfiles) == 0) {
175 error("Could not find help files in the English language pack!");
181 if ($mode == 'missing') {
182 if (!file_exists($langdir)) {
183 error ('to edit this language pack, you need to put it in '.$CFG->dataroot.'/lang');
186 // Following variables store the HTML output to be echo-ed
187 $m = '';
188 $o = '';
190 $m_x = false;
192 // Total number of strings and missing strings
193 $totalcounter->strings = 0;
194 $totalcounter->missing = 0;
196 // For each file, check that a counterpart exists, then check all the strings
197 foreach ($stringfiles as $stringfile) {
198 $location = $stringfile['location'];
199 $plugin = $stringfile['plugin'];
200 $prefix = $stringfile['prefix'];
201 $filename = $stringfile['filename'];
202 unset($string);
204 // Get some information about file locations:
205 // $enfilepath = the path to the English file distributed either in the core space or in plugin space
206 // $trfilepath = the path to the translated file distributed either in the lang pack or in plugin space
207 // $lcfilepath = the path to the _local customization
208 // $trfilename = the filename of the translated version of the file (including prefix for non-core files)
209 if ($location || $plugin) {
210 // non-core file in an extra location
211 $enfilepath = "$CFG->dirroot/$location/$plugin/lang/en_utf8/$filename";
212 $trfilepath = "$CFG->dirroot/$location/$plugin/lang/$currentlang/$filename";
213 $lcfilepath = "$locallangdir/$filename";
214 $trfilename = $filename;
215 if (!$m_x) {
216 $m .= '<hr />';
217 $m_x = true;
219 } else {
220 // core file in standard location
221 $enfilepath = "$CFG->dirroot/lang/en_utf8/$filename";
222 $trfilepath = "$langdir/$filename";
223 $lcfilepath = "$locallangdir/$filename";
224 $trfilename = $filename;
226 // $enstring = English strings distributed either in the core space or in plugin space
227 include($enfilepath);
228 $enstring = isset($string) ? $string : array();
229 unset($string);
230 ksort($enstring);
232 //$lcstring = local customizations
233 $lcstring = array();
234 if (file_exists($lcfilepath)) {
235 include($lcfilepath);
236 $localfileismissing = 0;
237 if (isset($string) && is_array($string)) {
238 $lcstring = $string;
240 unset($string);
241 ksort($lcstring);
242 } else {
243 $localfileismissing = 1;
246 // $string = translated strings distibuted either in core lang pack or in plugin space
247 $string = array();
248 if (file_exists($trfilepath)) {
249 include($trfilepath);
250 $fileismissing = 0;
251 } else {
252 $fileismissing = 1;
253 $o .= notify(get_string("filemissing", "", $trfilepath), "notifyproblem", "center", true);
256 $missingcounter = 0;
258 $first = true; // first missing string found in the file
259 // For all English strings in the current file check distributed translations and _local customizations
260 foreach ($enstring as $key => $value) {
261 $totalcounter->strings++;
262 $missingstring = false;
263 $missinglocalstring = false;
264 $translationsdiffer = false;
265 if (empty($string[$key]) and $string[$key] != "0") { // MDL-4735
266 // string is missing in distributed pack
267 $missingstring = true;
269 if (empty($lcstring[$key]) and $lcstring[$key] != "0") { // MDL-4735
270 // string is missing in _local customization
271 $missinglocalstring = true;
273 if (!$missingstring && !$missinglocalstring && ($lcstring[$key] != $string[$key])) {
274 $translationsdiffer = true;
276 if ($missingstring || $translationsdiffer) {
277 $value = htmlspecialchars($value);
278 $value = str_replace("$"."a", "\\$"."a", $value);
279 $value = str_replace("%%","%",$value);
280 if ($first) {
281 $m .= "<a href=\"lang.php?mode=missing#$trfilename\">$trfilename";
282 $m .= $fileismissing ? '*' : '';
283 $m .= '</a> &nbsp; ';
284 $o .= "<p><a name=\"$trfilename\"></a><b>".
285 get_string("stringsnotset","", $trfilepath)."</b></p><pre>";
286 $first = false;
287 $somethingfound = true;
289 if ($missingstring) {
290 $missingcounter++;
291 $totalcounter->missing++;
293 if ($translationsdiffer) {
294 $missingcounter++;
296 if (LANG_LINK_MISSING_STRINGS && ($missingstring || $translationsdiffer)) {
297 $missinglinkstart = "<a href=\"lang.php?mode=compare&amp;currentfile=$filename#missing$missingcounter\">";
298 $missinglinkend = '</a>';
299 } else {
300 $missinglinkstart = '';
301 $missinglinkend = '';
303 if (strlen($value) > LANG_MISSING_TEXT_MAX_LEN) {
304 $value = lang_xhtml_save_substr($value, 0, LANG_MISSING_TEXT_MAX_LEN) . ' ...'; // MDL-8852
306 if ($translationsdiffer) {
307 $o .= '// ';
309 $o .= "$"."string['".$missinglinkstart.$key.$missinglinkend."'] = \"$value\";";
310 if ($translationsdiffer) {
311 $o .= ' // differs from the translation in _local';
312 } elseif (!$missinglocalstring) {
313 $o .= ' // translated only in _local';
315 $o .= "\n";
318 if (!$first) {
319 $o .= '</pre><hr />';
323 if ($totalcounter->missing > 0) {
324 $totalcounter->missingpercent = sprintf('%02.1f', ($totalcounter->missing / $totalcounter->strings * 100));
325 print_heading(get_string('numberofstrings', 'admin', $totalcounter), '', 4);
326 } else {
327 print_heading($strnomissingstrings, '', 4, 'notifysuccess');
330 if ($m <> '') {
331 print_box($m, 'filenames');
334 echo $o;
336 if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/help", "CVS")) {
337 error("Could not find English language help files!");
340 foreach ($files as $filekey => $file) { // check all the help files.
341 if (!file_exists("$langdir/help/$file")) {
342 notify(get_string("filemissing", "", "$langdir/help/$file"), 'notifyproblem');
343 $somethingfound = true;
344 continue;
348 if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/docs", "CVS")) {
349 error("Could not find English language docs files!");
351 foreach ($files as $filekey => $file) { // check all the docs files.
352 if (!file_exists("$langdir/docs/$file")) {
353 notify(get_string("filemissing", "", "$langdir/docs/$file"), 'notifyproblem');
354 $somethingfound = true;
355 continue;
359 if (!empty($somethingfound)) {
360 print_continue("lang.php");
361 } else {
362 notice(get_string("languagegood"), "lang.php" );
365 } else if ($mode == 'compare') {
367 if (!file_exists($langbase) ){
368 if (!lang_make_directory($langbase) ){
369 error('ERROR: Could not create base lang directory ' . $langbase);
370 } else {
371 echo '<div class="notifysuccess">Created directory '.
372 $langbase .'</div>'."<br />\n";
375 if (!$uselocal && !file_exists($langdir)) {
376 if (!lang_make_directory($langdir)) {
377 error('ERROR: Could not create directory '.$langdir);
378 } else {
379 echo '<div class="notifysuccess">Created directory '.
380 $langdir .'</div>'."<br />\n";
383 if ($uselocal && !file_exists($locallangdir)) {
384 if (!lang_make_directory($locallangdir)) {
385 echo '<div class="notifyproblem">ERROR: Could not create directory '.
386 $locallangdir .'</div>'."<br />\n";
387 $uselocal = 0;
388 } else {
389 echo '<div class="notifysuccess">Created directory '.
390 $locallangdir .'</div>'."<br />\n";
394 if ($currentfile <> '') {
395 if (!$fileinfo = lang_get_file_info($currentfile, $stringfiles)) {
396 error('Unable to find info for: '.$currentfile);
398 // check the filename is set up correctly, prevents bugs similar to MDL-10920
399 $location = $fileinfo['location'];
400 $plugin = $fileinfo['plugin'];
401 $prefix = $fileinfo['prefix'];
402 $filename = $fileinfo['filename'];
403 if ($location || $plugin) {
404 // file in an extra location
405 if ($currentfile != "{$prefix}{$plugin}.php") {
406 error("Non-core filename mismatch. The file $currentfile should be {$prefix}{$plugin}.php");
408 if (!$uselocal) {
409 notify($streditingnoncorelangfile);
410 $editable = false;
412 } else {
413 // file in standard location
414 if ($currentfile != $filename) {
415 error("Core filename mismatch. The file $currentfile should be $filename");
419 // Get some information about file locations:
420 // $enfilepath = the path to the English file distributed either in the core space or in plugin space
421 // $trfilepath = the path to the translated file distributed either in the lang pack or in plugin space
422 // $lcfilepath = the path to the _local customization
423 // $trfilename = the filename of the translated version of the file (including prefix for non-core files)
424 if ($location || $plugin) {
425 // non-core file in an extra location
426 $enfilepath = "$CFG->dirroot/$location/$plugin/lang/en_utf8/$filename";
427 $trfilepath = "$CFG->dirroot/$location/$plugin/lang/$currentlang/$filename";
428 $lcfilepath = "$locallangdir/$filename";
429 $trfilename = $filename;
430 } else {
431 // core file in standard location
432 $enfilepath = "$CFG->dirroot/lang/en_utf8/$filename";
433 $trfilepath = "$langdir/$filename";
434 $lcfilepath = "$locallangdir/$filename";
435 $trfilename = $filename;
439 if (isset($_POST['currentfile'])){ // Save a file
440 if (!confirm_sesskey()) {
441 print_error('confirmsesskeybad', 'error');
444 $newstrings = array();
446 foreach ($_POST as $postkey => $postval) {
447 $stringkey = lang_file_string_key($postkey);
448 $newstrings[$stringkey] = $postval;
451 unset($newstrings['currentfile']);
453 $packstring = array();
454 $saveinto = $langdir;
455 if ($uselocal) {
456 if(file_exists($trfilepath)) {
457 include($trfilepath);
458 if (isset($string)) {
459 $packstring = $string;
461 unset($string);
463 $saveinto = $locallangdir;
466 if (lang_save_file($saveinto, $currentfile, $newstrings, $uselocal, $packstring)) {
467 notify(get_string("changessaved")." ($saveinto/$currentfile)", "notifysuccess");
468 } else {
469 error("Could not save the file '$saveinto/$currentfile'!", "lang.php?mode=compare&amp;currentfile=$currentfile");
471 unset($packstring);
474 print_box_start('generalbox editstrings');
475 $menufiles = array();
476 $menufiles_coregrp = 1;
477 foreach ($stringfiles as $stringfile) {
478 $item_key = $stringfile['filename'];
479 $item_label = $stringfile['filename'];
480 if ($stringfile['location'] != '' && $stringfile['plugin'] != '') {
481 $item_label .= ' ('.$stringfile['location'].'/'.$stringfile['plugin'].')';
482 if ($menufiles_coregrp == 1) {
483 $menufiles['extra'] = '------------';
484 $menufiles_coregrp = 0;
487 $menufiles[$item_key] = $item_label;
489 $selectionlabel = '<code class="path">';
490 //$selectionlabel .= $strfilestoredin;
491 $selectionlabel .= $uselocal ? "{$currentlang}_local" : $currentlang;
492 $selectionlabel .= '/</code>';
493 popup_form("$CFG->wwwroot/$CFG->admin/lang.php?mode=compare&amp;currentfile=", $menufiles, "choosefile",
494 $currentfile, $strchoosefiletoedit, '', '', false, 'self', $selectionlabel);
495 helpbutton('langswitchstorage', $strfilestoredinhelp, 'moodle');
496 print_box_end();
498 if ($currentfile <> '') {
499 error_reporting(0);
500 if (!isset($editable) || $editable) {
501 if (!file_exists("$saveto/$currentfile")) {
502 if (!@touch("$saveto/$currentfile")) {
503 print_heading(get_string("filemissing", "", "$saveto/$currentfile"), '', 4, 'error');
504 } else {
505 print_heading($strfilecreated, '', 4, 'notifysuccess');
508 if ($currentlang == "en_utf8" && !$uselocal) {
509 $editable = false;
510 print_heading($streditennotallowed, '', 4);
511 } elseif ($f = fopen("$saveto/$currentfile","r+")) {
512 $editable = true;
513 fclose($f);
514 } else {
515 $editable = false;
516 notify(get_string("makeeditable", "", "$saveto/$currentfile"), 'notifyproblem');
519 error_reporting($CFG->debug);
521 $o = ''; // stores the HTML output to be echo-ed
523 unset($string);
524 include($enfilepath);
525 $enstring = isset($string) ? $string : array();
527 // Following strings have moved into langconfig.php, but keep the here for backward compatibility
529 if ($currentlang != 'en' and $currentfile == 'moodle.php') {
530 $enstring['thislanguage'] = "<< TRANSLATORS: Specify the name of your language here. If possible use Unicode Numeric Character References >>";
531 $enstring['thischarset'] = "<< TRANSLATORS: Charset encoding - always use utf-8 >>";
532 $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. >>";
533 $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 >>";
535 unset($string);
536 ksort($enstring);
538 @include($lcfilepath);
539 $localstring = isset($string) ? $string : array();
540 unset($string);
541 ksort($localstring);
543 @include($trfilepath);
544 $string = isset($string) ? $string : array();
545 ksort($string);
547 if ($editable) {
548 $o .= "<form id=\"$currentfile\" action=\"lang.php\" method=\"post\">";
549 $o .= '<div>';
551 $o .= "<table summary=\"\" width=\"100%\" class=\"translator\">";
552 $linescounter = 0;
553 $missingcounter = 0;
554 foreach ($enstring as $key => $envalue) {
555 $linescounter++ ;
556 if (LANG_SUBMIT_REPEAT && $editable && $linescounter % LANG_SUBMIT_REPEAT_EVERY == 0) {
557 $o .= '<tr><td>&nbsp;</td><td><br />';
558 $o .= '<input type="submit" name="update" value="'.get_string('savechanges').': '.$currentfile.'" />';
559 $o .= '<br />&nbsp;</td></tr>';
561 $envalue = nl2br(htmlspecialchars($envalue));
562 $envalue = preg_replace('/(\$a\-\&gt;[a-zA-Z0-9]*|\$a)/', '<b>$0</b>', $envalue); // Make variables bold.
563 $envalue = str_replace("%%","%",$envalue);
564 $envalue = str_replace("\\","",$envalue); // Delete all slashes
566 $o .= "\n\n".'<tr class="';
567 if ($linescounter % 2 == 0) {
568 $o .= 'r0';
569 } else {
570 $o .= 'r1';
572 $o .= '">';
573 $o .= '<td dir="ltr" lang="en">';
574 $o .= '<span class="stren">'.$envalue.'</span>';
575 $o .= '<br />'."\n";
576 $o .= '<span class="strkey">'.$key.'</span>';
577 $o .= '</td>'."\n";
579 // Missing array keys are not bugs here but missing strings
580 error_reporting(E_ALL ^ E_NOTICE);
581 if ($uselocal) {
582 $value = lang_fix_value_from_file($localstring[$key]);
583 $value2 = lang_fix_value_from_file($string[$key]);
584 if ($value == '') {
585 $value = $value2;
587 } else {
588 $value = lang_fix_value_from_file($string[$key]);
589 $value2 = lang_fix_value_from_file($localstring[$key]);
591 error_reporting($CFG->debug);
592 $missingtarget = '';
593 $missingnext = '';
594 $missingprev = '';
595 $cellcolour = '';
596 $usetabindex = false;
597 if (!$value) {
598 // the string is not present in the pack being processed
599 if (!$value2) {
600 $cellcolour = 'class="bothmissing"';
601 $usetabindex = true;
602 } else {
603 $cellcolour = 'class="mastermissing"';
604 $usetabindex = true;
606 $missingcounter++;
607 if (LANG_DISPLAY_MISSING_LINKS) {
608 $missingtarget = '<a name="missing'.$missingcounter.'"></a>';
609 $missingnext = '<a href="#missing'.($missingcounter+1).'">'.
610 '<img src="' . $CFG->pixpath . '/t/down.gif" class="iconsmall" alt="'.$strnext.'" /></a>';
611 $missingprev = '<a href="#missing'.($missingcounter-1).'">'.
612 '<img src="' . $CFG->pixpath . '/t/up.gif" class="iconsmall" alt="'.$strprev.'" /></a>';
614 } else {
615 // the string is translated in the pack being processed
616 if ($value <> $value2 && ($uselocal || $value2 <> '')) {
617 $cellcolour = 'class="localdifferent"';
618 $usetabindex = true;
619 $missingcounter++;
620 if (LANG_DISPLAY_MISSING_LINKS) {
621 $missingtarget = '<a name="missing'.$missingcounter.'"></a>';
622 $missingnext = '<a href="#missing'.($missingcounter+1).'">'.
623 '<img src="' . $CFG->pixpath . '/t/down.gif" class="iconsmall" alt="'.$strnext.'" /></a>';
624 $missingprev = '<a href="#missing'.($missingcounter-1).'">'.
625 '<img src="' . $CFG->pixpath . '/t/up.gif" class="iconsmall" alt="'.$strprev.'" /></a>';
630 if ($editable) {
631 $o .= '<td '.$cellcolour.' valign="top">';
632 if ($missingcounter > 1) {
633 $o .= $missingprev;
635 $o .= $missingtarget."\n";
636 if (isset($string[$key])) {
637 $valuelen = strlen($value);
638 } else {
639 $valuelen = strlen($envalue);
641 $cols=40;
642 if ($usetabindex) {
643 $tabindex = 'tabindex="'.$missingcounter.'"';
644 } else {
645 $tabindex = '';
647 if (strstr($value, "\r") or strstr($value, "\n") or $valuelen > $cols) {
648 $rows = ceil($valuelen / $cols);
649 $o .= '<textarea name="stringXXX'.lang_form_string_key($key).'" cols="'.$cols.'" rows="'.$rows.'" '.$tabindex.'>'.$value.'</textarea>'."\n";
650 } else {
651 if ($valuelen) {
652 $cols = $valuelen + 5;
654 $o .= '<input type="text" name="stringXXX'.lang_form_string_key($key).'" value="'.$value.'" size="'.$cols.'" '.$tabindex.' />';
656 if ($value2 <> '' && $value <> $value2) {
657 $o .= '<br /><span style="font-size:small">'.$value2.'</span>';
659 $o .= $missingnext . '</td>';
661 } else {
662 $o .= '<td '.$cellcolour.' valign="top">'.$value.'<br />'.$value2.'</td>';
664 $o .= '</tr>'."\n";
666 if ($editable) {
667 $o .= '<tr><td>&nbsp;</td><td><br />';
668 $o .= '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
669 $o .= '<input type="hidden" name="currentfile" value="'.$currentfile.'" />';
670 $o .= '<input type="hidden" name="mode" value="compare" />';
671 $o .= '<input type="submit" name="update" value="'.get_string('savechanges').': '.$currentfile.'" />';
672 $o .= '</td></tr>';
674 $o .= '</table>';
675 if ($editable) {
676 $o .= '</div>';
677 $o .= '</form>';
680 if (LANG_DISPLAY_MISSING_LINKS) {
681 if ($missingcounter > 0) {
682 print_heading(get_string('numberofmissingstrings', 'admin', $missingcounter), '', 4);
683 if ($editable) {
684 print_heading('<a href="#missing1">'.$strgotofirst.'</a>', "", 4);
686 } else {
687 print_heading($strnomissingstrings, '', 4, 'notifysuccess');
690 echo $o;
692 } else {
693 // no $currentfile specified
694 // no useful information to display - maybe some help? instructions?
697 } elseif ($mode == 'helpfiles') {
699 $saveto = $saveto.'/help'; // the edited content will be saved to
700 $enlangdir = $enlangdir.'/help'; // English master help files localtion
701 $langdir = $langdir.'/help'; // current language master help files location
702 $locallangdir = $locallangdir.'/help'; // local modifications of help files location
703 $altdir = $uselocal ? $langdir : $locallangdir; // alternative to $saveto
705 $fileeditorrows = 10; // number of textareas' rows
706 $fileeditorcols = 100; // dtto cols
707 $filemissingmark = ' (***)'; // mark to add to non-existing or zero-length files
708 $fileoldmark = ' (old?)'; // mark to add to filenames in selection form if the English version is newer
709 $filetemplate = ''; // template for new files, e.g. CVS identification
711 if (isset($_POST['currentfile'])) { // Save a file
712 if (!confirm_sesskey()) {
713 print_error('confirmsesskeybad', 'error');
715 if (lang_help_save_file($saveto, $currentfile, $_POST['filedata'])) {
716 notify(get_string("changessaved")." ($saveto/$currentfile)", "notifysuccess");
717 } else {
718 error("Could not save the file '$currentfile'!", "lang.php?mode=helpfiles&amp;currentfile=$currentfile&amp;sesskey=$USER->sesskey");
722 print_box_start('generalbox editstrings');
723 $menufiles = array();
724 $menufiles_coregrp = 1;
725 $origlocation = ''; // the location of the currentfile's English source will be stored here
726 $origplugin = ''; // dtto plugin
727 foreach ($helpfiles as $helppath => $helpfile) {
728 $item_key = $helpfile['filename'];
729 $item_label = $helpfile['filename'];
730 if ((!file_exists($saveto.'/'.$helpfile['filename'])) || (filesize($saveto.'/'.$helpfile['filename']) == 0)) {
731 $item_label .= $filemissingmark;
732 } else {
733 if (filemtime($saveto.'/'.$helpfile['filename']) < filemtime($helppath)) {
734 $item_label .= $fileoldmark;
736 if ($helpfile['location'] != '' && $helpfile['plugin'] != '') {
737 $item_label .= ' ('.$helpfile['location'].'/'.$helpfile['plugin'].')';
738 if ($menufiles_coregrp == 1) {
739 $menufiles['extra'] = '------------';
740 $menufiles_coregrp = 0;
744 $menufiles[$item_key] = $item_label;
745 if ($currentfile == $helpfile['filename']) {
746 $origlocation = $helpfile['location'];
747 $origplugin = $helpfile['plugin'];
750 $selectionlabel = '<code class="path">';
751 //$selectionlabel .= $strfilestoredin;
752 $selectionlabel .= $uselocal ? "{$currentlang}_local" : $currentlang;
753 $selectionlabel .= '/help/</code>';
754 popup_form("$CFG->wwwroot/$CFG->admin/lang.php?mode=helpfiles&amp;currentfile=", $menufiles, "choosefile",
755 $currentfile, $strchoosefiletoedit, '', '', false, 'self', $selectionlabel);
756 helpbutton('langswitchstorage', $strfilestoredinhelp, 'moodle');
757 print_box_end();
759 if (!empty($currentfile)) {
761 if (!file_exists("$saveto/$currentfile")) {
762 $dbg .= "File does not exist: $saveto/$currentfile\n";
763 //check if directory exist
764 if (!file_exists(dirname("$saveto/$currentfile"))) {
765 if(!lang_make_directory(dirname("$saveto/$currentfile"))) {
766 echo ('Cannot create directory: '.dirname("$saveto/$currentfile"));
770 // file doesn't exist - let's check webserver's permission to create it
772 if (!@touch("$saveto/$currentfile")) {
774 // webserver is unable to create new file
776 notify(get_string('filemissing', '', "$saveto/$currentfile" ));
777 notify(get_string('makeeditable', '', "$saveto/$currentfile"));
778 $editable = false;
779 } else {
781 // webserver can create new file - we can delete it now and let
782 // it create again if its filesize() > 0
784 $editable = true;
785 unlink("$saveto/$currentfile");
787 } elseif (is_writable("$saveto/$currentfile")) {
788 $editable = true;
789 } else {
791 // file exists but it is not writeable by web server process :-(
793 $editable = false;
794 notify(get_string('makeeditable', '', "$saveto/$currentfile"));
797 // master en_utf8 in dataroot is not editable
798 if ((!$uselocal) && ($currentlang == 'en_utf8')) {
799 $editable = false;
802 echo '<div>';
804 if ($uselocal) {
805 $strsavetotitle = $strlanglocalpackage . helpbutton('langpackages', $strlanglocalpackage, 'moodle', true, false, '', true);
806 $straltdirtitle = $strlangmasterpackage . helpbutton('langpackages', $strlangmasterpackage, 'moodle', true, false, '', true);
807 } else {
808 $straltdirtitle = $strlanglocalpackage . helpbutton('langpackages', $strlanglocalpackage, 'moodle', true, false, '', true);
809 $strsavetotitle = $strlangmasterpackage . helpbutton('langpackages', $strlangmasterpackage, 'moodle', true, false, '', true);
813 if ($editable) {
814 // generate an editor for the current help file in $saveto
815 echo '<fieldset><legend>'.$strsavetotitle.'</legend>';
816 echo "<form id=\"helpfileeditor\" action=\"lang.php\" method=\"post\">";
817 echo '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
818 echo '<input type="hidden" name="currentfile" value="'.$currentfile.'" />';
819 echo '<input type="hidden" name="mode" value="helpfiles" />';
820 echo "<div align=\"center\">\n";
821 echo "<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"filedata\">";
822 if (file_exists("$saveto/$currentfile")) {
823 echo htmlspecialchars(file_get_contents("$saveto/$currentfile"));
824 } else {
825 echo ($filetemplate);
827 echo "</textarea>\n</div>\n";
828 echo '<div align="center"><input type="submit" value="'.get_string('savechanges').'" /></div>';
829 echo '</form>';
830 $preview_url = lang_help_preview_url($currentfile, !$uselocal);
831 if ($preview_url) {
832 link_to_popup_window($preview_url, 'popup', get_string('preview'));
834 echo '</fieldset>';
837 if (is_readable("$altdir/$currentfile")) {
838 // show the content of the same help file in alternative location
839 echo '<fieldset><legend>'.$straltdirtitle.'</legend>';
840 echo "<div align=\"center\">\n";
841 echo "<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"\">";
842 if (file_exists("$altdir/$currentfile")) {
843 echo htmlspecialchars(file_get_contents("$altdir/$currentfile"));
844 } else {
845 echo ($filetemplate);
847 echo "</textarea>\n</div>\n";
848 $preview_url = lang_help_preview_url($currentfile, $uselocal);
849 if ($preview_url) {
850 link_to_popup_window($preview_url, 'popup', get_string('preview'));
852 echo '</fieldset>';
855 // show the content of the original English file either in core space or plugin space
856 if ($origlocation != '' && $origplugin != '') {
857 // non-core help file
858 $ensrc = "$CFG->dirroot/$origlocation/$origplugin/lang/en_utf8/help/$currentfile";
859 } else {
860 // core help file
861 $ensrc = "$enlangdir/$currentfile";
863 if (is_readable($ensrc)) {
864 echo '<fieldset><legend>'.$strlangmasterenglish;
865 helpbutton('langpackages', $strlangmasterenglish);
866 echo '</legend>';
867 echo "<div align=\"center\">\n<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"\">";
868 echo htmlspecialchars(file_get_contents($ensrc));
869 echo "</textarea>\n</div>\n";
870 $preview_url = lang_help_preview_url($currentfile, true, 'en_utf8'); // do not display en_utf8_local
871 if ($preview_url) {
872 link_to_popup_window($preview_url, 'popup', get_string('preview'));
874 echo '</fieldset>';
877 echo '</div>'; // translator box
878 error_reporting($CFG->debug);
881 if (false && $CFG->debugdisplay && debugging('', DEBUG_DEVELOPER) ) {
882 echo '<hr />';
883 print_heading('Debugging info');
884 echo '<pre class="notifytiny">';
885 print_r($dbg);
886 print_r("\n\$currentfile = $currentfile");
887 print_r("\n\$enlangdir = $enlangdir");
888 print_r("\n\$langdir = $langdir");
889 print_r("\n\$locallangdir = $locallangdir");
890 print_r("\n\$saveto = $saveto");
891 print_r("\n\$altdir = $altdir");
892 print_r("\n\$origlocation = $origlocation");
893 print_r("\n\$origplugin = $origplugin");
894 print_r("\n\$ensrc = $ensrc");
895 print_r("\n\$helpfiles = ");
896 print_r($helpfiles);
897 echo '</pre>';
900 } // fi $mode == 'helpfiles'
903 admin_externalpage_print_footer();
905 //////////////////////////////////////////////////////////////////////
908 * Save language translation file.
910 * Thanks to Petri Asikainen for the original version of code
911 * used to save language files.
913 * @uses $CFG
914 * @uses $USER
915 * @param string $path Full pathname to the directory to use
916 * @param string $file File to overwrite
917 * @param array $strings Array of strings to write
918 * @param bool $local Should *_local version be saved?
919 * @param array $packstrings Array of default langpack strings (needed if $local)
920 * @return bool Created successfully?
922 function lang_save_file($path, $file, $strings, $local, $packstrings) {
923 global $CFG, $USER;
924 if (LANG_KEEP_ORPHANS) {
925 // let us load the current content of the file
926 unset($string);
927 @include("$path/$file");
928 if (isset($string)) {
929 $orphans = $string;
930 unset($string);
931 } else {
932 $orphans = array();
935 // let us rewrite the file
936 if (!$f = @fopen("$path/$file","w")) {
937 return false;
940 fwrite($f, "<?PHP // \$Id\$ \n");
941 fwrite($f, " // $file - created with Moodle $CFG->release ($CFG->version)\n");
942 if ($local) {
943 fwrite($f, " // local modifications from $CFG->wwwroot\n");
945 fwrite($f, "\n\n");
946 ksort($strings);
947 foreach ($strings as $key => $value) {
948 @list($id, $stringname) = explode('XXX',$key);
949 $value = lang_fix_value_before_save($value);
950 if ($id == "string" and $value != ""){
951 if ((!$local) || (!isset($packstrings[$stringname])) || (lang_fix_value_from_file($packstrings[$stringname]) <> lang_fix_value_from_file($value))) {
952 // Either we are saving the master language pack
953 // or the string is not saved in packstring - fixes PHP notices about missing key
954 // or we are saving local language pack and the strings differ.
955 fwrite($f,"\$string['$stringname'] = '$value';\n");
957 if (LANG_KEEP_ORPHANS && isset($orphans[$stringname])) {
958 unset($orphans[$stringname]);
962 if (LANG_KEEP_ORPHANS) {
963 // let us add orphaned strings, i.e. already translated strings without the English referential source
964 foreach ($orphans as $key => $value) {
965 fwrite($f,"\$string['$key'] = '".lang_fix_value_before_save($value)."'; // ORPHANED\n");
968 fwrite($f,"\n?>\n");
969 fclose($f);
970 return true;
974 * Fix value of the translated string after it is load from the file.
976 * These modifications are typically necessary to work with the same string coming from two sources.
977 * We need to compare the content of these sources and we want to have e.g. "This string\r\n"
978 * to be the same as " This string\n".
980 * @param string $value Original string from the file
981 * @return string Fixed value
983 function lang_fix_value_from_file($value='') {
984 $value = str_replace("\r","",$value); // Bad character caused by Windows
985 $value = preg_replace("/\n{3,}/", "\n\n", $value); // Collapse runs of blank lines
986 $value = trim($value); // Delete leading/trailing white space
987 $value = str_replace("\\","",$value); // Delete all slashes
988 $value = str_replace("%%","%",$value);
989 $value = str_replace("&","&amp;",$value); // Fixes MDL-9248
990 $value = str_replace("<","&lt;",$value);
991 $value = str_replace(">","&gt;",$value);
992 $value = str_replace('"',"&quot;",$value);
993 return $value;
997 * Fix value of the translated string before it is saved into the file
999 * @uses $CFG
1000 * @param string $value Raw string to be saved into the lang pack
1001 * @return string Fixed value
1003 function lang_fix_value_before_save($value='') {
1004 global $CFG;
1005 if ($CFG->lang != "zh_hk" and $CFG->lang != "zh_tw") { // Some MB languages include backslash bytes
1006 $value = str_replace("\\","",$value); // Delete all slashes
1008 if (ini_get_bool('magic_quotes_sybase')) { // Unescape escaped sybase quotes
1009 $value = str_replace("''", "'", $value);
1011 $value = str_replace("'", "\\'", $value); // Add slashes for '
1012 $value = str_replace('"', "\\\"", $value); // Add slashes for "
1013 $value = str_replace("%","%%",$value); // Escape % characters
1014 $value = str_replace("\r", "",$value); // Remove linefeed characters
1015 $value = trim($value); // Delete leading/trailing white space
1016 return $value;
1020 * Try and create a new language directory.
1022 * Uses PHP>=5.0 syntax of mkdir and tries to create directories recursively.
1024 * @uses $CFG
1025 * @param string $directory full path to the directory under $langbase
1026 * @return string|false Returns full path to directory if successful, false if not
1028 function lang_make_directory($dir, $shownotices=true) {
1029 global $CFG;
1030 umask(0000);
1031 if (! file_exists($dir)) {
1032 if (! @mkdir($dir, $CFG->directorypermissions, true)) { // recursive=true; PHP>=5.0 needed
1033 return false;
1035 //@chmod($dir, $CFG->directorypermissions); // Just in case mkdir didn't do it
1037 return $dir;
1041 * Return the string key name for use in HTML form.
1043 * Required because '.' in form input names get replaced by '_' by PHP.
1045 * @param string $keyfromfile The key name containing '.'
1046 * @return string The key name without '.'
1048 function lang_form_string_key($keyfromfile) {
1049 return str_replace('.', '##46#', $keyfromfile); /// Derived from &#46, the ascii value for a period.
1053 * Return the string key name for use in file.
1055 * Required because '.' in form input names get replaced by '_' by PHP.
1057 * @param string $keyfromfile The key name without '.'
1058 * @return string The key name containing '.'
1060 function lang_file_string_key($keyfromform) {
1061 return str_replace('##46#', '.', $keyfromform);
1065 * Return the substring of the string and take care of XHTML compliance.
1067 * There was a problem with pure substr() which could possibly produce XHTML parsing error:
1068 * substr('Marks &amp; Spencer', 0, 9) -> 'Marks &am' ... is not XHTML compliance
1069 * This function takes care of these cases. Fixes MDL-8852.
1071 * Thanks to kovacsendre, the author of the function at http://php.net/substr
1073 * @param string $str The original string
1074 * @param int $start Start position in the $value string
1075 * @param int $length Optional length of the returned substring
1076 * @return string The substring as returned by substr() with XHTML compliance
1077 * @todo Seems the function does not work with negative $start together with $length being set
1079 function lang_xhtml_save_substr($str, $start, $length = NULL) {
1080 if ($length === 0) {
1081 //stop wasting our time ;)
1082 return "";
1085 //check if we can simply use the built-in functions
1086 if (strpos($str, '&') === false) {
1087 // No entities. Use built-in functions
1088 if ($length === NULL) {
1089 return substr($str, $start);
1090 } else {
1091 return substr($str, $start, $length);
1095 // create our array of characters and html entities
1096 $chars = preg_split('/(&[^;\s]+;)|/', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE);
1097 $html_length = count($chars);
1099 // check if we can predict the return value and save some processing time, i.e.:
1100 // input string was empty OR
1101 // $start is longer than the input string OR
1102 // all characters would be omitted
1103 if (($html_length === 0) or ($start >= $html_length) or (isset($length) and ($length <= -$html_length))) {
1104 return '';
1107 //calculate start position
1108 if ($start >= 0) {
1109 $real_start = $chars[$start][1];
1110 } else {
1111 //start'th character from the end of string
1112 $start = max($start,-$html_length);
1113 $real_start = $chars[$html_length+$start][1];
1116 if (!isset($length)) {
1117 // no $length argument passed, return all remaining characters
1118 return substr($str, $real_start);
1119 } elseif ($length > 0) {
1120 // copy $length chars
1121 if ($start+$length >= $html_length) {
1122 // return all remaining characters
1123 return substr($str, $real_start);
1124 } else {
1125 //return $length characters
1126 return substr($str, $real_start, $chars[max($start,0)+$length][1] - $real_start);
1128 } else {
1129 //negative $length. Omit $length characters from end
1130 return substr($str, $real_start, $chars[$html_length+$length][1] - $real_start);
1135 * Finds all English string files in the standard lang/en_utf8 location.
1137 * Core lang files should always be stored here and not in the module space (MDL-10920).
1138 * The English version of the file may be found in
1139 * $CFG->dirroot/lang/en_utf8/filename
1140 * The localised version of the found file should be saved into
1141 * $CFG->dataroot/lang/currentlang[_local]/filename
1142 * where "filename" is returned as a part of the file record.
1144 * @return array Array of a file information. Compatible format with {@link lang_extra_locations()}
1146 function lang_standard_locations() {
1147 global $CFG;
1148 $files = array();
1149 // Standard location of master English string files.
1150 $places = array($CFG->dirroot.'/lang/en_utf8');
1151 foreach ($places as $place) {
1152 foreach (get_directory_list($place, '', false) as $file) {
1153 if ((substr($file, -4) == ".php") && ($file != "langconfig.php")) {
1154 $fullpath = $place.'/'.$file;
1155 $files[$fullpath] = array(
1156 'filename' => $file,
1157 'location' => '',
1158 'plugin' => '',
1159 'prefix' => '',
1164 return $files;
1168 * Finds all English string files in non-standard location.
1170 * Searches for lang/en_utf8/*.php in various types of plugins (blocks, database presets, question types,
1171 * 3rd party modules etc.) and returns an array of found files details.
1173 * The English version of the file may be found in
1174 * $CFG->dirroot/location/plugin/lang/en_utf8/filename
1175 * The localised version of the found file should be saved into
1176 * $CFG->dataroot/lang/currentlang[_local]/prefix_plugin.php
1177 * where "location", "plugin", "prefix" and "filename" are returned as a part of the file record.
1179 * @return array Array of a file information. Compatible format with {@link lang_standard_locations()}
1181 function lang_extra_locations() {
1182 global $CFG;
1183 $files = array();
1184 $places = places_to_search_for_lang_strings();
1185 foreach ($places as $prefix => $directories) {
1186 if ($prefix != '__exceptions') {
1187 foreach ($directories as $directory) {
1188 foreach (get_list_of_plugins($directory) as $plugin) {
1189 $enlangdirlocation = $CFG->dirroot.'/'.$directory.'/'.$plugin.'/lang/en_utf8';
1190 foreach (get_directory_list($enlangdirlocation, '', false) as $file) {
1191 if ((substr($file, -4) == ".php") && ($file != "langconfig.php")) {
1192 $fullpath = $enlangdirlocation.'/'.$file;
1193 $files[$fullpath] = array(
1194 'filename' => $file,
1195 'location' => $directory,
1196 'plugin' => $plugin,
1197 'prefix' => $prefix,
1205 return $files;
1209 * Lookup for a stringfile details.
1211 * English files can be stored in several places (core space or module/plugin space). Their translations
1212 * go into the one directory - the current language pack. Therefore, the name of the stringfile may be
1213 * considered as a key of the list of all stringfiles.
1215 * @param string $currentfile the filename
1216 * @param array $stringfiles the array of file info returned by {@link lang_extra_locations()}
1217 * @return array Array of a file information (filename, location, plugin, prefix) or null.
1219 function lang_get_file_info($currentfile, $stringfiles) {
1220 $found = false;
1221 foreach ($stringfiles as $path=>$stringfile) {
1222 if ($stringfile['filename'] == $currentfile) {
1223 $found = true;
1224 $ret = $stringfile;
1225 $ret['fullpath'] = $path;
1226 break;
1229 if ($found) {
1230 return $ret;
1231 } else {
1232 return null;
1237 * Returns all English help files in the standard lang/en_utf8/help location.
1239 * Core help files should always be stored here and not in the module space (MDL-10920).
1240 * The English version of the file may be found in
1241 * $CFG->dirroot/lang/en_utf8/help/filename
1242 * The localised version of the found file should be saved into
1243 * $CFG->dataroot/lang/currentlang[_local]/help/filename
1244 * where "filename" is returned as a part of the file record.
1246 * @return array Array of a file information. Compatible format with {@link lang_extra_locations()}
1248 function lang_help_standard_locations() {
1249 global $CFG;
1250 $files = array();
1251 // Standard location of master English help files.
1252 $places = array($CFG->dirroot.'/lang/en_utf8/help');
1253 foreach ($places as $place) {
1254 foreach (get_directory_list($place, 'CVS') as $file) {
1255 if ((substr($file, -5) == '.html') || (substr($file, -4) == '.txt' )) {
1256 $fullpath = $place.'/'.$file;
1257 $files[$fullpath] = array(
1258 'filename' => $file,
1259 'location' => '',
1260 'plugin' => '',
1261 'prefix' => '',
1266 return $files;
1270 * Returns all English help files in non-standard location.
1272 * Searches for lang/en_utf8/help/* files in various types of plugins (blocks, database presets, question types,
1273 * 3rd party modules etc.) and returns an array of found files details.
1275 * The English version of the file may be found in
1276 * $CFG->dirroot/location/plugin/lang/en_utf8/help/filename
1277 * The localised version of the found file should be saved into
1278 * $CFG->dataroot/lang/currentlang[_local]/help/prefix_plugin/filename (XXX is "prefix" here right?)
1279 * where "location", "plugin", "prefix" and "filename" are returned as a part of the file record.
1281 * @return array Array of a file information. Compatible format with {@link lang_standard_locations()}
1283 function lang_help_extra_locations() {
1284 global $CFG;
1285 $files = array();
1286 $places = places_to_search_for_lang_strings();
1287 foreach ($places as $prefix => $directories) {
1288 if ($prefix != '__exceptions') {
1289 foreach ($directories as $directory) {
1290 foreach (get_list_of_plugins($directory) as $plugin) {
1291 $enlangdirlocation = $CFG->dirroot.'/'.$directory.'/'.$plugin.'/lang/en_utf8/help';
1292 foreach (get_directory_list($enlangdirlocation, 'CVS') as $file) {
1293 if ((substr($file, -5) == '.html') || (substr($file, -4) == '.txt' )) {
1294 $fullpath = $enlangdirlocation.'/'.$file;
1295 $files[$fullpath] = array(
1296 'filename' => $file,
1297 'location' => $directory,
1298 'plugin' => $plugin,
1299 'prefix' => $prefix,
1307 return $files;
1311 * Return a preview URL for help file, if available.
1313 * @param string $currentfile The relative path to the help file, e.g. "assignment/types.html" - MDL-12291
1314 * @param bool $skiplocal Force displaying the helpfile from a master lang pack
1315 * @param string $forcelang Force language of the help, e.g. "en_utf8"
1316 * @return string $url
1318 function lang_help_preview_url($currentfile, $skiplocal=false, $forcelang = '') {
1319 $currentpathexp = explode('/', $currentfile);
1320 if (count($currentpathexp) > 1) {
1321 $url = '/help.php?module='.implode('/',array_slice($currentpathexp,0,count($currentpathexp)-1)).'&amp;file='.end($currentpathexp);
1322 } else {
1323 $url = '/help.php?module=moodle&amp;file='.$currentfile;
1325 if ($skiplocal) {
1326 $url .= '&amp;skiplocal=1';
1328 if ($forcelang) {
1329 $url .= '&amp;forcelang='.$forcelang;
1331 return $url;
1336 * Saves (overwrites) translated help file.
1338 * @param string $helproot The path to the "help" folder
1339 * @param string $file The relative path to the html help file
1340 * @param string $content HTML data to be saved
1341 * @return bool False if save failed, true otherwise
1343 function lang_help_save_file($helproot, $file, $content) {
1344 global $CFG, $USER;
1346 $content = str_replace("\r", "",$content); // Remove linefeed characters
1347 $content = preg_replace("/\n{3,}/", "\n\n", $content); // Collapse runs of blank lines
1348 $content = trim($content); // Delete leading/trailing whitespace
1349 if (is_readable("$helproot/$file") && filesize("$helproot/$file") > 0 && $content == '') {
1350 notify(get_string('langrmyourself', 'admin'));
1351 return true;
1354 error_reporting(0);
1355 if (!$f = fopen("$helproot/$file","w")) {
1356 error_reporting($CFG->debug);
1357 return false;
1359 error_reporting($CFG->debug);
1361 fwrite($f, stripslashes($content));
1362 fclose($f);
1364 // Remove file if its empty
1365 if (filesize("$helproot/$file") == 0) {
1366 unlink("$helproot/$file");
1369 return true;