Automatic installer.php lang files by installer_builder (20081206)
[moodle.git] / admin / lang.php
blob2a6dd824d5e74fa044a905da9a86dd9e13893056
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);
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
29 define('LANG_ALWAYS_TEXTAREA', 1); // always use <textarea> even for short strings MDL-15738
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);
38 $uselocal = optional_param('uselocal', -1, PARAM_INT);
40 if ($uselocal == -1) {
41 if (isset($SESSION->langtranslateintolocal)) {
42 $uselocal = $SESSION->langtranslateintolocal;
43 } else {
44 $uselocal = LANG_DEFAULT_USELOCAL;
46 } else {
47 $SESSION->langtranslateintolocal = $uselocal;
50 if (!has_capability('moodle/site:langeditmaster', $context, $USER->id, false)) {
51 // Force using _local
52 $uselocal = 1;
55 if (!has_capability('moodle/site:langeditmaster', $context, $USER->id, false) && (!$uselocal)) {
56 print_error('cannoteditmasterlang');
59 if ((!has_capability('moodle/site:langeditlocal', $context, $USER->id, false)) && ($uselocal)) {
60 print_error('cannotcustomizelocallang');
63 $strlanguage = get_string("language");
64 $strcurrentlanguage = get_string("currentlanguage");
65 $strmissingstrings = get_string("missingstrings");
66 $streditstrings = get_string("editstrings", 'admin');
67 $stredithelpdocs = get_string("edithelpdocs", 'admin');
68 $strthislanguage = get_string("thislanguage");
69 $strgotofirst = get_string('gotofirst','admin');
70 $strfilestoredin = get_string('filestoredin', 'admin');
71 $strfilestoredinhelp = get_string('filestoredinhelp', 'admin');
72 $strswitchlang = get_string('switchlang', 'admin');
73 $strchoosefiletoedit = get_string('choosefiletoedit', 'admin');
74 $streditennotallowed = get_string('langnoeditenglish', 'admin');
75 $strfilecreated = get_string('filecreated', 'admin');
76 $strprev = get_string('previous');
77 $strnext = get_string('next');
78 $strlocalstringcustomization = get_string('localstringcustomization', 'admin');
79 $strlangpackmaintaining = get_string('langpackmaintaining', 'admin');
80 $strnomissingstrings = get_string('nomissingstrings', 'admin');
81 $streditingnoncorelangfile = get_string('editingnoncorelangfile', 'admin');
82 $strlanglocalpackage = get_string('langlocalpackage', 'admin');
83 $strlangmasterpackage = get_string('langmasterpackage', 'admin');
84 $strlangmasterenglish = get_string('langmasterenglish', 'admin');
86 $currentlang = current_language();
88 switch ($mode) {
89 case "missing":
90 // Missing array keys are not bugs here but missing strings
91 error_reporting(E_ALL ^ E_NOTICE);
92 $title = $strmissingstrings;
93 break;
94 case "compare":
95 $title = $streditstrings;
96 break;
97 case "helpfiles":
98 $title = $stredithelpdocs;
99 default:
100 $title = $strlanguage;
101 break;
103 $navlinks[] = array('name' => $strlanguage, 'link' => "$CFG->wwwroot/$CFG->admin/lang.php", 'type' => 'misc');
104 $navigation = build_navigation($navlinks);
106 admin_externalpage_print_header();
108 // Prepare and render menu tabs
109 $firstrow = array();
110 $secondrow = array();
111 $inactive = NULL;
112 $activated = NULL;
113 $currenttab = $mode;
114 if ($uselocal) {
115 $inactive = array('uselocal');
116 $activated = array('uselocal');
117 } else {
118 $inactive = array('usemaster');
119 $activated = array('usemaster');
121 if (has_capability('moodle/site:langeditlocal', $context, $USER->id, false)) {
122 $firstrow[] = new tabobject('uselocal',
123 "$CFG->wwwroot/$CFG->admin/lang.php?mode=$mode&amp;currentfile=$currentfile&amp;uselocal=1",
124 $strlocalstringcustomization );
126 if (has_capability('moodle/site:langeditmaster', $context, $USER->id, false)) {
127 $firstrow[] = new tabobject('usemaster',
128 "$CFG->wwwroot/$CFG->admin/lang.php?mode=$mode&amp;currentfile=$currentfile&amp;uselocal=0",
129 $strlangpackmaintaining );
131 $secondrow[] = new tabobject('missing', "$CFG->wwwroot/$CFG->admin/lang.php?mode=missing", $strmissingstrings );
132 $secondrow[] = new tabobject('compare', "$CFG->wwwroot/$CFG->admin/lang.php?mode=compare", $streditstrings );
133 $secondrow[] = new tabobject('helpfiles', "$CFG->wwwroot/$CFG->admin/lang.php?mode=helpfiles", $stredithelpdocs );
134 $tabs = array($firstrow, $secondrow);
135 print_tabs($tabs, $currenttab, $inactive, $activated);
138 if (!$mode) {
139 // TODO this is a very nice place to put some translation statistics
140 print_box_start();
141 $currlang = current_language();
142 $langs = get_list_of_languages(false, true);
143 popup_form ("$CFG->wwwroot/$CFG->admin/lang.php?lang=", $langs, "chooselang", $currlang, "", "", "", false, 'self', $strcurrentlanguage.':');
144 print_box_end();
145 admin_externalpage_print_footer();
146 exit;
149 // Get a list of all the root files in the English directory
151 $langbase = $CFG->dataroot . '/lang';
152 $enlangdir = "$CFG->dirroot/lang/en_utf8";
153 if ($currentlang == 'en_utf8') {
154 $langdir = $enlangdir;
155 } else {
156 $langdir = "$langbase/$currentlang";
158 $locallangdir = "$langbase/{$currentlang}_local";
159 $saveto = $uselocal ? $locallangdir : $langdir;
161 if (($mode == 'missing') || ($mode == 'compare')) {
162 // get the list of all English stringfiles
163 $stringfiles = lang_standard_locations();
164 if (LANG_SEARCH_EXTRA) {
165 $stringfiles += lang_extra_locations();
167 if (count($stringfiles) == 0) {
168 error("Could not find English language pack!");
170 } elseif ($mode == 'helpfiles') {
171 $helpfiles = lang_help_standard_locations();
172 if (LANG_SEARCH_EXTRA) {
173 $helpfiles += lang_help_extra_locations();
175 if (count($helpfiles) == 0) {
176 error("Could not find help files in the English language pack!");
182 if ($mode == 'missing') {
183 if (!file_exists($langdir)) {
184 error ('to edit this language pack, you need to put it in '.$CFG->dataroot.'/lang');
187 // Following variables store the HTML output to be echo-ed
188 $m = '';
189 $o = '';
191 $m_x = false;
193 // Total number of strings and missing strings
194 $totalcounter->strings = 0;
195 $totalcounter->missing = 0;
197 // For each file, check that a counterpart exists, then check all the strings
198 foreach ($stringfiles as $stringfile) {
199 $location = $stringfile['location'];
200 $plugin = $stringfile['plugin'];
201 $prefix = $stringfile['prefix'];
202 $filename = $stringfile['filename'];
203 unset($string);
205 // Get some information about file locations:
206 // $enfilepath = the path to the English file distributed either in the core space or in plugin space
207 // $trfilepath = the path to the translated file distributed either in the lang pack or in plugin space
208 // $lcfilepath = the path to the _local customization
209 // $trfilename = the filename of the translated version of the file (including prefix for non-core files)
210 if ($location || $plugin) {
211 // non-core file in an extra location
212 $enfilepath = "$CFG->dirroot/$location/$plugin/lang/en_utf8/$filename";
213 $trfilepath = "$CFG->dirroot/$location/$plugin/lang/$currentlang/$filename";
214 $lcfilepath = "$locallangdir/$filename";
215 $trfilename = $filename;
216 if (!$m_x) {
217 $m .= '<hr />';
218 $m_x = true;
220 } else {
221 // core file in standard location
222 $enfilepath = "$CFG->dirroot/lang/en_utf8/$filename";
223 $trfilepath = "$langdir/$filename";
224 $lcfilepath = "$locallangdir/$filename";
225 $trfilename = $filename;
227 // $enstring = English strings distributed either in the core space or in plugin space
228 include($enfilepath);
229 $enstring = isset($string) ? $string : array();
230 unset($string);
231 ksort($enstring);
233 //$lcstring = local customizations
234 $lcstring = array();
235 if (file_exists($lcfilepath)) {
236 include($lcfilepath);
237 $localfileismissing = 0;
238 if (isset($string) && is_array($string)) {
239 $lcstring = $string;
241 unset($string);
242 ksort($lcstring);
243 } else {
244 $localfileismissing = 1;
247 // $string = translated strings distibuted either in core lang pack or in plugin space
248 $string = array();
249 if (file_exists($trfilepath)) {
250 include($trfilepath);
251 $fileismissing = 0;
252 } else {
253 $fileismissing = 1;
254 $o .= notify(get_string("filemissing", "", $trfilepath), "notifyproblem", "center", true);
257 $missingcounter = 0;
259 $first = true; // first missing string found in the file
260 // For all English strings in the current file check distributed translations and _local customizations
261 foreach ($enstring as $key => $value) {
262 $totalcounter->strings++;
263 $missingstring = false;
264 $missinglocalstring = false;
265 $translationsdiffer = false;
266 if (empty($string[$key]) and $string[$key] != "0") { // MDL-4735
267 // string is missing in distributed pack
268 $missingstring = true;
270 if (empty($lcstring[$key]) and $lcstring[$key] != "0") { // MDL-4735
271 // string is missing in _local customization
272 $missinglocalstring = true;
274 if (!$missingstring && !$missinglocalstring && ($lcstring[$key] != $string[$key])) {
275 $translationsdiffer = true;
277 if ($missingstring || $translationsdiffer) {
278 $value = htmlspecialchars($value);
279 $value = str_replace("$"."a", "\\$"."a", $value);
280 $value = str_replace("%%","%",$value);
281 if ($first) {
282 $m .= "<a href=\"lang.php?mode=missing#$trfilename\">$trfilename";
283 $m .= $fileismissing ? '*' : '';
284 $m .= '</a> &nbsp; ';
285 $o .= "<p><a name=\"$trfilename\"></a><b>".
286 get_string("stringsnotset","", $trfilepath)."</b></p><pre>";
287 $first = false;
288 $somethingfound = true;
290 if ($missingstring) {
291 $missingcounter++;
292 $totalcounter->missing++;
294 if ($translationsdiffer) {
295 $missingcounter++;
297 if (LANG_LINK_MISSING_STRINGS && ($missingstring || $translationsdiffer)) {
298 $missinglinkstart = "<a href=\"lang.php?mode=compare&amp;currentfile=$filename#$key\">";
299 $missinglinkend = '</a>';
300 } else {
301 $missinglinkstart = '';
302 $missinglinkend = '';
304 if (strlen($value) > LANG_MISSING_TEXT_MAX_LEN) {
305 $value = lang_xhtml_save_substr($value, 0, LANG_MISSING_TEXT_MAX_LEN) . ' ...'; // MDL-8852
307 if ($translationsdiffer) {
308 $o .= '// ';
310 $o .= "$"."string['".$missinglinkstart.$key.$missinglinkend."'] = \"$value\";";
311 if ($translationsdiffer) {
312 $o .= ' // differs from the translation in _local';
313 } elseif (!$missinglocalstring) {
314 $o .= ' // translated only in _local';
316 $o .= "\n";
319 if (!$first) {
320 $o .= '</pre><hr />';
324 if ($totalcounter->missing > 0) {
325 $totalcounter->missingpercent = sprintf('%02.1f', ($totalcounter->missing / $totalcounter->strings * 100));
326 print_heading(get_string('numberofstrings', 'admin', $totalcounter), '', 4);
327 } else {
328 print_heading($strnomissingstrings, '', 4, 'notifysuccess');
331 if ($m <> '') {
332 print_box($m, 'filenames');
335 echo $o;
337 if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/help", "CVS")) {
338 error("Could not find English language help files!");
341 foreach ($files as $filekey => $file) { // check all the help files.
342 if (!file_exists("$langdir/help/$file")) {
343 notify(get_string("filemissing", "", "$langdir/help/$file"), 'notifyproblem');
344 $somethingfound = true;
345 continue;
349 if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/docs", "CVS")) {
350 error("Could not find English language docs files!");
352 foreach ($files as $filekey => $file) { // check all the docs files.
353 if (!file_exists("$langdir/docs/$file")) {
354 notify(get_string("filemissing", "", "$langdir/docs/$file"), 'notifyproblem');
355 $somethingfound = true;
356 continue;
360 if (!empty($somethingfound)) {
361 print_continue("lang.php");
362 } else {
363 notice(get_string("languagegood"), "lang.php" );
366 } else if ($mode == 'compare') {
368 if (!file_exists($langbase) ){
369 if (!lang_make_directory($langbase) ){
370 error('ERROR: Could not create base lang directory ' . $langbase);
371 } else {
372 echo '<div class="notifysuccess">Created directory '.
373 $langbase .'</div>'."<br />\n";
376 if (!$uselocal && !file_exists($langdir)) {
377 if (!lang_make_directory($langdir)) {
378 error('ERROR: Could not create directory '.$langdir);
379 } else {
380 echo '<div class="notifysuccess">Created directory '.
381 $langdir .'</div>'."<br />\n";
384 if ($uselocal && !file_exists($locallangdir)) {
385 if (!lang_make_directory($locallangdir)) {
386 echo '<div class="notifyproblem">ERROR: Could not create directory '.
387 $locallangdir .'</div>'."<br />\n";
388 $uselocal = 0;
389 } else {
390 echo '<div class="notifysuccess">Created directory '.
391 $locallangdir .'</div>'."<br />\n";
395 if ($currentfile <> '') {
396 if (!$fileinfo = lang_get_file_info($currentfile, $stringfiles)) {
397 error('Unable to find info for: '.$currentfile);
399 // check the filename is set up correctly, prevents bugs similar to MDL-10920
400 $location = $fileinfo['location'];
401 $plugin = $fileinfo['plugin'];
402 $prefix = $fileinfo['prefix'];
403 $filename = $fileinfo['filename'];
404 if ($location || $plugin) {
405 // file in an extra location
406 if ($currentfile != "{$prefix}{$plugin}.php") {
407 error("Non-core filename mismatch. The file $currentfile should be {$prefix}{$plugin}.php");
409 if (!$uselocal) {
410 notify($streditingnoncorelangfile);
411 $editable = false;
413 } else {
414 // file in standard location
415 if ($currentfile != $filename) {
416 error("Core filename mismatch. The file $currentfile should be $filename");
420 // Get some information about file locations:
421 // $enfilepath = the path to the English file distributed either in the core space or in plugin space
422 // $trfilepath = the path to the translated file distributed either in the lang pack or in plugin space
423 // $lcfilepath = the path to the _local customization
424 // $trfilename = the filename of the translated version of the file (including prefix for non-core files)
425 if ($location || $plugin) {
426 // non-core file in an extra location
427 $enfilepath = "$CFG->dirroot/$location/$plugin/lang/en_utf8/$filename";
428 $trfilepath = "$CFG->dirroot/$location/$plugin/lang/$currentlang/$filename";
429 $lcfilepath = "$locallangdir/$filename";
430 $trfilename = $filename;
431 } else {
432 // core file in standard location
433 $enfilepath = "$CFG->dirroot/lang/en_utf8/$filename";
434 $trfilepath = "$langdir/$filename";
435 $lcfilepath = "$locallangdir/$filename";
436 $trfilename = $filename;
440 if (isset($_POST['currentfile'])){ // Save a file
441 if (!confirm_sesskey()) {
442 print_error('confirmsesskeybad', 'error');
445 $newstrings = array();
447 foreach ($_POST as $postkey => $postval) {
448 $stringkey = lang_file_string_key($postkey);
449 $newstrings[$stringkey] = $postval;
452 unset($newstrings['currentfile']);
454 $packstring = array();
455 $saveinto = $langdir;
456 if ($uselocal) {
457 if(file_exists($trfilepath)) {
458 include($trfilepath);
459 if (isset($string)) {
460 $packstring = $string;
462 unset($string);
464 $saveinto = $locallangdir;
467 if (lang_save_file($saveinto, $currentfile, $newstrings, $uselocal, $packstring)) {
468 notify(get_string("changessaved")." ($saveinto/$currentfile)", "notifysuccess");
469 } else {
470 error("Could not save the file '$saveinto/$currentfile'!", "lang.php?mode=compare&amp;currentfile=$currentfile");
472 unset($packstring);
475 print_box_start('generalbox editstrings');
476 $menufiles = array();
477 $menufiles_coregrp = 1;
478 foreach ($stringfiles as $stringfile) {
479 $item_key = $stringfile['filename'];
480 $item_label = $stringfile['filename'];
481 if ($stringfile['location'] != '' && $stringfile['plugin'] != '') {
482 $item_label .= ' ('.$stringfile['location'].'/'.$stringfile['plugin'].')';
483 if ($menufiles_coregrp == 1) {
484 $menufiles['extra'] = '------------';
485 $menufiles_coregrp = 0;
488 $menufiles[$item_key] = $item_label;
490 $selectionlabel = '<code class="path">';
491 //$selectionlabel .= $strfilestoredin;
492 $selectionlabel .= $uselocal ? "{$currentlang}_local" : $currentlang;
493 $selectionlabel .= '/</code>';
494 popup_form("$CFG->wwwroot/$CFG->admin/lang.php?mode=compare&amp;currentfile=", $menufiles, "choosefile",
495 $currentfile, $strchoosefiletoedit, '', '', false, 'self', $selectionlabel);
496 helpbutton('langswitchstorage', $strfilestoredinhelp, 'moodle');
497 print_box_end();
499 if ($currentfile <> '') {
500 error_reporting(0);
501 if (!isset($editable) || $editable) {
502 if (!file_exists("$saveto/$currentfile")) {
503 if (!@touch("$saveto/$currentfile")) {
504 print_heading(get_string("filemissing", "", "$saveto/$currentfile"), '', 4, 'error');
505 } else {
506 print_heading($strfilecreated, '', 4, 'notifysuccess');
509 if ($currentlang == "en_utf8" && !$uselocal) {
510 $editable = false;
511 print_heading($streditennotallowed, '', 4);
512 } elseif ($f = fopen("$saveto/$currentfile","r+")) {
513 $editable = true;
514 fclose($f);
515 } else {
516 $editable = false;
517 notify(get_string("makeeditable", "", "$saveto/$currentfile"), 'notifyproblem');
520 error_reporting($CFG->debug);
522 $o = ''; // stores the HTML output to be echo-ed
524 unset($string);
525 include($enfilepath);
526 $enstring = isset($string) ? $string : array();
528 // Following strings have moved into langconfig.php, but keep the here for backward compatibility
530 if ($currentlang != 'en' and $currentfile == 'moodle.php') {
531 $enstring['thislanguage'] = "<< TRANSLATORS: Specify the name of your language here. If possible use Unicode Numeric Character References >>";
532 $enstring['thischarset'] = "<< TRANSLATORS: Charset encoding - always use utf-8 >>";
533 $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. >>";
534 $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 >>";
536 unset($string);
537 ksort($enstring);
539 @include($lcfilepath);
540 $localstring = isset($string) ? $string : array();
541 unset($string);
542 ksort($localstring);
544 @include($trfilepath);
545 $string = isset($string) ? $string : array();
546 ksort($string);
548 if ($editable) {
549 $o .= "<form id=\"$currentfile\" action=\"lang.php\" method=\"post\">";
550 $o .= '<div>';
552 $o .= "<table summary=\"\" width=\"100%\" class=\"translator\">";
553 $linescounter = 0;
554 $missingcounter = 0;
555 foreach ($enstring as $key => $envalue) {
556 $linescounter++ ;
557 if (LANG_SUBMIT_REPEAT && $editable && $linescounter % LANG_SUBMIT_REPEAT_EVERY == 0) {
558 $o .= '<tr><td>&nbsp;</td><td><br />';
559 $o .= '<input type="submit" tabindex="'.$missingcounter.'" name="update" value="'.get_string('savechanges').': '.$currentfile.'" />';
560 $o .= '<br />&nbsp;</td></tr>';
562 $envalue = nl2br(htmlspecialchars($envalue));
563 $envalue = preg_replace('/(\$a\-\&gt;[a-zA-Z0-9]*|\$a)/', '<b>$0</b>', $envalue); // Make variables bold.
564 $envalue = str_replace("%%","%",$envalue);
565 $envalue = str_replace("\\","",$envalue); // Delete all slashes
567 $o .= "\n\n".'<tr class="';
568 if ($linescounter % 2 == 0) {
569 $o .= 'r0';
570 } else {
571 $o .= 'r1';
573 $o .= '">';
574 $o .= '<td dir="ltr" lang="en">';
575 $o .= '<span id="'.$key.'" class="stren">'.$envalue.'</span>';
576 $o .= '<br />'."\n";
577 $o .= '<span class="strkey">'.$key.'</span>';
578 $o .= '</td>'."\n";
580 // Missing array keys are not bugs here but missing strings
581 error_reporting(E_ALL ^ E_NOTICE);
582 if ($uselocal) {
583 $value = lang_fix_value_from_file($localstring[$key]);
584 $value2 = lang_fix_value_from_file($string[$key]);
585 if ($value == '') {
586 $value = $value2;
588 } else {
589 $value = lang_fix_value_from_file($string[$key]);
590 $value2 = lang_fix_value_from_file($localstring[$key]);
592 error_reporting($CFG->debug);
593 $missingtarget = '';
594 $missingnext = '';
595 $missingprev = '';
596 $cellcolour = '';
597 $usetabindex = false;
598 if (!$value) {
599 // the string is not present in the pack being processed
600 if (!$value2) {
601 $cellcolour = 'class="bothmissing"';
602 $usetabindex = true;
603 } else {
604 $cellcolour = 'class="mastermissing"';
605 $usetabindex = true;
607 $missingcounter++;
608 if (LANG_DISPLAY_MISSING_LINKS) {
609 $missingtarget = '<a name="missing'.$missingcounter.'"></a>';
610 $missingnext = '<a href="#missing'.($missingcounter+1).'">'.
611 '<img src="' . $CFG->pixpath . '/t/down.gif" class="iconsmall" alt="'.$strnext.'" /></a>';
612 $missingprev = '<a href="#missing'.($missingcounter-1).'">'.
613 '<img src="' . $CFG->pixpath . '/t/up.gif" class="iconsmall" alt="'.$strprev.'" /></a>';
615 } else {
616 // the string is translated in the pack being processed
617 if ($value <> $value2 && ($value2 <> '')) {
618 $cellcolour = 'class="localdifferent"';
619 $usetabindex = true;
620 $missingcounter++;
621 if (LANG_DISPLAY_MISSING_LINKS) {
622 $missingtarget = '<a name="missing'.$missingcounter.'"></a>';
623 $missingnext = '<a href="#missing'.($missingcounter+1).'">'.
624 '<img src="' . $CFG->pixpath . '/t/down.gif" class="iconsmall" alt="'.$strnext.'" /></a>';
625 $missingprev = '<a href="#missing'.($missingcounter-1).'">'.
626 '<img src="' . $CFG->pixpath . '/t/up.gif" class="iconsmall" alt="'.$strprev.'" /></a>';
631 if ($editable) {
632 $o .= '<td '.$cellcolour.' valign="top">';
633 if ($missingcounter > 1) {
634 $o .= $missingprev;
636 $o .= $missingtarget."\n";
637 if (isset($string[$key])) {
638 $valuelen = strlen($value);
639 } else {
640 $valuelen = strlen($envalue);
642 $cols=40;
643 if ($usetabindex) {
644 $tabindex = 'tabindex="'.$missingcounter.'"';
645 } else {
646 $tabindex = '';
648 if (strstr($value, "\r") or strstr($value, "\n") or $valuelen > $cols) {
649 $rows = ceil($valuelen / $cols);
650 $o .= '<textarea name="stringXXX'.lang_form_string_key($key).'" cols="'.$cols.'" rows="'.$rows.'" '.$tabindex.'>'.$value.'</textarea>'."\n";
651 } else {
652 if ($valuelen) {
653 $cols = $valuelen + 5;
655 if (LANG_ALWAYS_TEXTAREA) {
656 $o .= '<textarea name="stringXXX'.lang_form_string_key($key).'" cols="'.$cols.'" rows="1" '.$tabindex.'>'.$value.'</textarea>'."\n";
657 } else {
658 $o .= '<input type="text" name="stringXXX'.lang_form_string_key($key).'" value="'.$value.'" size="'.$cols.'" '.$tabindex.' />';
661 if ($value2 <> '' && $value <> $value2) {
662 $o .= '<br /><span style="font-size:small">'.$value2.'</span>';
664 $o .= $missingnext . '</td>';
666 } else {
667 $o .= '<td '.$cellcolour.' valign="top">'.$value.'<br />'.$value2.'</td>';
669 $o .= '</tr>'."\n";
671 if ($editable) {
672 $o .= '<tr><td>&nbsp;</td><td><br />';
673 $o .= '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
674 $o .= '<input type="hidden" name="currentfile" value="'.$currentfile.'" />';
675 $o .= '<input type="hidden" name="mode" value="compare" />';
676 $o .= '<input type="submit" name="update" tabindex="'.$missingcounter.'" value="'.get_string('savechanges').': '.$currentfile.'" />';
677 $o .= '</td></tr>';
679 $o .= '</table>';
680 if ($editable) {
681 $o .= '</div>';
682 $o .= '</form>';
685 if (LANG_DISPLAY_MISSING_LINKS) {
686 if ($missingcounter > 0) {
687 print_heading(get_string('numberofmissingstrings', 'admin', $missingcounter), '', 4);
688 if ($editable) {
689 print_heading('<a href="#missing1">'.$strgotofirst.'</a>', "", 4);
691 } else {
692 print_heading($strnomissingstrings, '', 4, 'notifysuccess');
695 echo $o;
697 } else {
698 // no $currentfile specified
699 // no useful information to display - maybe some help? instructions?
702 } elseif ($mode == 'helpfiles') {
704 $saveto = $saveto.'/help'; // the edited content will be saved to
705 $enlangdir = $enlangdir.'/help'; // English master help files localtion
706 $langdir = $langdir.'/help'; // current language master help files location
707 $locallangdir = $locallangdir.'/help'; // local modifications of help files location
708 $altdir = $uselocal ? $langdir : $locallangdir; // alternative to $saveto
710 $fileeditorrows = 10; // number of textareas' rows
711 $fileeditorcols = 100; // dtto cols
712 $filemissingmark = ' (***)'; // mark to add to non-existing or zero-length files
713 $fileoldmark = ' (old?)'; // mark to add to filenames in selection form if the English version is newer
714 $filetemplate = ''; // template for new files, e.g. CVS identification
716 if (isset($_POST['currentfile'])) { // Save a file
717 if (!confirm_sesskey()) {
718 print_error('confirmsesskeybad', 'error');
720 if (lang_help_save_file($saveto, $currentfile, $_POST['filedata'])) {
721 notify(get_string("changessaved")." ($saveto/$currentfile)", "notifysuccess");
722 } else {
723 error("Could not save the file '$currentfile'!", "lang.php?mode=helpfiles&amp;currentfile=$currentfile&amp;sesskey=$USER->sesskey");
727 print_box_start('generalbox editstrings');
728 $menufiles = array();
729 $menufiles_coregrp = 1;
730 $origlocation = ''; // the location of the currentfile's English source will be stored here
731 $origplugin = ''; // dtto plugin
732 foreach ($helpfiles as $helppath => $helpfile) {
733 $item_key = $helpfile['filename'];
734 $item_label = $helpfile['filename'];
735 if ((!file_exists($saveto.'/'.$helpfile['filename'])) || (filesize($saveto.'/'.$helpfile['filename']) == 0)) {
736 $item_label .= $filemissingmark;
737 } else {
738 if (filemtime($saveto.'/'.$helpfile['filename']) < filemtime($helppath)) {
739 $item_label .= $fileoldmark;
741 if ($helpfile['location'] != '' && $helpfile['plugin'] != '') {
742 $item_label .= ' ('.$helpfile['location'].'/'.$helpfile['plugin'].')';
743 if ($menufiles_coregrp == 1) {
744 $menufiles['extra'] = '------------';
745 $menufiles_coregrp = 0;
749 $menufiles[$item_key] = $item_label;
750 if ($currentfile == $helpfile['filename']) {
751 $origlocation = $helpfile['location'];
752 $origplugin = $helpfile['plugin'];
755 $selectionlabel = '<code class="path">';
756 //$selectionlabel .= $strfilestoredin;
757 $selectionlabel .= $uselocal ? "{$currentlang}_local" : $currentlang;
758 $selectionlabel .= '/help/</code>';
759 popup_form("$CFG->wwwroot/$CFG->admin/lang.php?mode=helpfiles&amp;currentfile=", $menufiles, "choosefile",
760 $currentfile, $strchoosefiletoedit, '', '', false, 'self', $selectionlabel);
761 helpbutton('langswitchstorage', $strfilestoredinhelp, 'moodle');
762 print_box_end();
764 if (!empty($currentfile)) {
766 if (!file_exists("$saveto/$currentfile")) {
767 $dbg .= "File does not exist: $saveto/$currentfile\n";
768 //check if directory exist
769 if (!file_exists(dirname("$saveto/$currentfile"))) {
770 if(!lang_make_directory(dirname("$saveto/$currentfile"))) {
771 echo ('Cannot create directory: '.dirname("$saveto/$currentfile"));
775 // file doesn't exist - let's check webserver's permission to create it
777 if (!@touch("$saveto/$currentfile")) {
779 // webserver is unable to create new file
781 notify(get_string('filemissing', '', "$saveto/$currentfile" ));
782 notify(get_string('makeeditable', '', "$saveto/$currentfile"));
783 $editable = false;
784 } else {
786 // webserver can create new file - we can delete it now and let
787 // it create again if its filesize() > 0
789 $editable = true;
790 unlink("$saveto/$currentfile");
792 } elseif (is_writable("$saveto/$currentfile")) {
793 $editable = true;
794 } else {
796 // file exists but it is not writeable by web server process :-(
798 $editable = false;
799 notify(get_string('makeeditable', '', "$saveto/$currentfile"));
802 // master en_utf8 in dataroot is not editable
803 if ((!$uselocal) && ($currentlang == 'en_utf8')) {
804 $editable = false;
807 echo '<div>';
809 if ($uselocal) {
810 $strsavetotitle = $strlanglocalpackage . helpbutton('langpackages', $strlanglocalpackage, 'moodle', true, false, '', true);
811 $straltdirtitle = $strlangmasterpackage . helpbutton('langpackages', $strlangmasterpackage, 'moodle', true, false, '', true);
812 } else {
813 $straltdirtitle = $strlanglocalpackage . helpbutton('langpackages', $strlanglocalpackage, 'moodle', true, false, '', true);
814 $strsavetotitle = $strlangmasterpackage . helpbutton('langpackages', $strlangmasterpackage, 'moodle', true, false, '', true);
818 if ($editable) {
819 // generate an editor for the current help file in $saveto
820 echo '<fieldset><legend>'.$strsavetotitle.'</legend>';
821 echo "<form id=\"helpfileeditor\" action=\"lang.php\" method=\"post\">";
822 echo '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
823 echo '<input type="hidden" name="currentfile" value="'.$currentfile.'" />';
824 echo '<input type="hidden" name="mode" value="helpfiles" />';
825 echo "<div align=\"center\">\n";
826 echo "<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"filedata\">";
827 if (file_exists("$saveto/$currentfile")) {
828 echo htmlspecialchars(file_get_contents("$saveto/$currentfile"));
829 } else {
830 echo ($filetemplate);
832 echo "</textarea>\n</div>\n";
833 echo '<div align="center"><input type="submit" value="'.get_string('savechanges').'" /></div>';
834 echo '</form>';
835 $preview_url = lang_help_preview_url($currentfile, !$uselocal);
836 if ($preview_url) {
837 link_to_popup_window($preview_url, 'popup', get_string('preview'));
839 echo '</fieldset>';
842 if (is_readable("$altdir/$currentfile")) {
843 // show the content of the same help file in alternative location
844 echo '<fieldset><legend>'.$straltdirtitle.'</legend>';
845 echo "<div align=\"center\">\n";
846 echo "<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"\">";
847 if (file_exists("$altdir/$currentfile")) {
848 echo htmlspecialchars(file_get_contents("$altdir/$currentfile"));
849 } else {
850 echo ($filetemplate);
852 echo "</textarea>\n</div>\n";
853 $preview_url = lang_help_preview_url($currentfile, $uselocal);
854 if ($preview_url) {
855 link_to_popup_window($preview_url, 'popup', get_string('preview'));
857 echo '</fieldset>';
860 // show the content of the original English file either in core space or plugin space
861 if ($origlocation != '' && $origplugin != '') {
862 // non-core help file
863 $ensrc = "$CFG->dirroot/$origlocation/$origplugin/lang/en_utf8/help/$currentfile";
864 } else {
865 // core help file
866 $ensrc = "$enlangdir/$currentfile";
868 if (is_readable($ensrc)) {
869 echo '<fieldset><legend>'.$strlangmasterenglish;
870 helpbutton('langpackages', $strlangmasterenglish);
871 echo '</legend>';
872 echo "<div align=\"center\">\n<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"\">";
873 echo htmlspecialchars(file_get_contents($ensrc));
874 echo "</textarea>\n</div>\n";
875 $preview_url = lang_help_preview_url($currentfile, true, 'en_utf8'); // do not display en_utf8_local
876 if ($preview_url) {
877 link_to_popup_window($preview_url, 'popup', get_string('preview'));
879 echo '</fieldset>';
882 echo '</div>'; // translator box
883 error_reporting($CFG->debug);
886 if (false && $CFG->debugdisplay && debugging('', DEBUG_DEVELOPER) ) {
887 echo '<hr />';
888 print_heading('Debugging info');
889 echo '<pre class="notifytiny">';
890 print_r($dbg);
891 print_r("\n\$currentfile = $currentfile");
892 print_r("\n\$enlangdir = $enlangdir");
893 print_r("\n\$langdir = $langdir");
894 print_r("\n\$locallangdir = $locallangdir");
895 print_r("\n\$saveto = $saveto");
896 print_r("\n\$altdir = $altdir");
897 print_r("\n\$origlocation = $origlocation");
898 print_r("\n\$origplugin = $origplugin");
899 print_r("\n\$ensrc = $ensrc");
900 print_r("\n\$helpfiles = ");
901 print_r($helpfiles);
902 echo '</pre>';
905 } // fi $mode == 'helpfiles'
908 admin_externalpage_print_footer();
910 //////////////////////////////////////////////////////////////////////
913 * Save language translation file.
915 * Thanks to Petri Asikainen for the original version of code
916 * used to save language files.
918 * @uses $CFG
919 * @uses $USER
920 * @param string $path Full pathname to the directory to use
921 * @param string $file File to overwrite
922 * @param array $strings Array of strings to write
923 * @param bool $local Should *_local version be saved?
924 * @param array $packstrings Array of default langpack strings (needed if $local)
925 * @return bool Created successfully?
927 function lang_save_file($path, $file, $strings, $local, $packstrings) {
928 global $CFG, $USER;
929 if (LANG_KEEP_ORPHANS) {
930 // let us load the current content of the file
931 unset($string);
932 @include("$path/$file");
933 if (isset($string)) {
934 $orphans = $string;
935 unset($string);
936 } else {
937 $orphans = array();
940 // let us rewrite the file
941 if (!$f = @fopen("$path/$file","w")) {
942 return false;
945 fwrite($f, "<?PHP // \$Id\$ \n");
946 fwrite($f, " // $file - created with Moodle $CFG->release ($CFG->version)\n");
947 if ($local) {
948 fwrite($f, " // local modifications from $CFG->wwwroot\n");
950 fwrite($f, "\n\n");
951 ksort($strings);
952 foreach ($strings as $key => $value) {
953 @list($id, $stringname) = explode('XXX',$key);
954 $value = lang_fix_value_before_save($value);
955 if ($id == "string" and $value != ""){
956 if ((!$local) || (!isset($packstrings[$stringname])) || (lang_fix_value_from_file($packstrings[$stringname]) <> lang_fix_value_from_file($value))) {
957 // Either we are saving the master language pack
958 // or the string is not saved in packstring - fixes PHP notices about missing key
959 // or we are saving local language pack and the strings differ.
960 fwrite($f,"\$string['$stringname'] = '$value';\n");
962 if (LANG_KEEP_ORPHANS && isset($orphans[$stringname])) {
963 unset($orphans[$stringname]);
967 if (LANG_KEEP_ORPHANS) {
968 // let us add orphaned strings, i.e. already translated strings without the English referential source
969 foreach ($orphans as $key => $value) {
970 fwrite($f,"\$string['$key'] = '".lang_fix_value_before_save($value)."'; // ORPHANED\n");
973 fwrite($f,"\n?>\n");
974 fclose($f);
975 return true;
979 * Fix value of the translated string after it is load from the file.
981 * These modifications are typically necessary to work with the same string coming from two sources.
982 * We need to compare the content of these sources and we want to have e.g. "This string\r\n"
983 * to be the same as " This string\n".
985 * @param string $value Original string from the file
986 * @return string Fixed value
988 function lang_fix_value_from_file($value='') {
989 $value = str_replace("\r","",$value); // Bad character caused by Windows
990 $value = preg_replace("/\n{3,}/", "\n\n", $value); // Collapse runs of blank lines
991 $value = trim($value); // Delete leading/trailing white space
992 $value = str_replace("\\","",$value); // Delete all slashes
993 $value = str_replace("%%","%",$value);
994 $value = str_replace("&","&amp;",$value); // Fixes MDL-9248
995 $value = str_replace("<","&lt;",$value);
996 $value = str_replace(">","&gt;",$value);
997 $value = str_replace('"',"&quot;",$value);
998 return $value;
1002 * Fix value of the translated string before it is saved into the file
1004 * @uses $CFG
1005 * @param string $value Raw string to be saved into the lang pack
1006 * @return string Fixed value
1008 function lang_fix_value_before_save($value='') {
1009 global $CFG;
1010 if ($CFG->lang != "zh_hk" and $CFG->lang != "zh_tw") { // Some MB languages include backslash bytes
1011 $value = str_replace("\\","",$value); // Delete all slashes
1013 if (ini_get_bool('magic_quotes_sybase')) { // Unescape escaped sybase quotes
1014 $value = str_replace("''", "'", $value);
1016 $value = str_replace("'", "\\'", $value); // Add slashes for '
1017 $value = str_replace('"', "\\\"", $value); // Add slashes for "
1018 $value = str_replace("%","%%",$value); // Escape % characters
1019 $value = str_replace("\r", "",$value); // Remove linefeed characters
1020 $value = trim($value); // Delete leading/trailing white space
1021 return $value;
1025 * Try and create a new language directory.
1027 * Uses PHP>=5.0 syntax of mkdir and tries to create directories recursively.
1029 * @uses $CFG
1030 * @param string $directory full path to the directory under $langbase
1031 * @return string|false Returns full path to directory if successful, false if not
1033 function lang_make_directory($dir, $shownotices=true) {
1034 global $CFG;
1035 umask(0000);
1036 if (! file_exists($dir)) {
1037 if (! @mkdir($dir, $CFG->directorypermissions, true)) { // recursive=true; PHP>=5.0 needed
1038 return false;
1040 //@chmod($dir, $CFG->directorypermissions); // Just in case mkdir didn't do it
1042 return $dir;
1046 * Return the string key name for use in HTML form.
1048 * Required because '.' in form input names get replaced by '_' by PHP.
1050 * @param string $keyfromfile The key name containing '.'
1051 * @return string The key name without '.'
1053 function lang_form_string_key($keyfromfile) {
1054 return str_replace('.', '##46#', $keyfromfile); /// Derived from &#46, the ascii value for a period.
1058 * Return the string key name for use in file.
1060 * Required because '.' in form input names get replaced by '_' by PHP.
1062 * @param string $keyfromfile The key name without '.'
1063 * @return string The key name containing '.'
1065 function lang_file_string_key($keyfromform) {
1066 return str_replace('##46#', '.', $keyfromform);
1070 * Return the substring of the string and take care of XHTML compliance.
1072 * There was a problem with pure substr() which could possibly produce XHTML parsing error:
1073 * substr('Marks &amp; Spencer', 0, 9) -> 'Marks &am' ... is not XHTML compliance
1074 * This function takes care of these cases. Fixes MDL-8852.
1076 * Thanks to kovacsendre, the author of the function at http://php.net/substr
1078 * @param string $str The original string
1079 * @param int $start Start position in the $value string
1080 * @param int $length Optional length of the returned substring
1081 * @return string The substring as returned by substr() with XHTML compliance
1082 * @todo Seems the function does not work with negative $start together with $length being set
1084 function lang_xhtml_save_substr($str, $start, $length = NULL) {
1085 if ($length === 0) {
1086 //stop wasting our time ;)
1087 return "";
1090 //check if we can simply use the built-in functions
1091 if (strpos($str, '&') === false) {
1092 // No entities. Use built-in functions
1093 if ($length === NULL) {
1094 return substr($str, $start);
1095 } else {
1096 return substr($str, $start, $length);
1100 // create our array of characters and html entities
1101 $chars = preg_split('/(&[^;\s]+;)|/', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE);
1102 $html_length = count($chars);
1104 // check if we can predict the return value and save some processing time, i.e.:
1105 // input string was empty OR
1106 // $start is longer than the input string OR
1107 // all characters would be omitted
1108 if (($html_length === 0) or ($start >= $html_length) or (isset($length) and ($length <= -$html_length))) {
1109 return '';
1112 //calculate start position
1113 if ($start >= 0) {
1114 $real_start = $chars[$start][1];
1115 } else {
1116 //start'th character from the end of string
1117 $start = max($start,-$html_length);
1118 $real_start = $chars[$html_length+$start][1];
1121 if (!isset($length)) {
1122 // no $length argument passed, return all remaining characters
1123 return substr($str, $real_start);
1124 } elseif ($length > 0) {
1125 // copy $length chars
1126 if ($start+$length >= $html_length) {
1127 // return all remaining characters
1128 return substr($str, $real_start);
1129 } else {
1130 //return $length characters
1131 return substr($str, $real_start, $chars[max($start,0)+$length][1] - $real_start);
1133 } else {
1134 //negative $length. Omit $length characters from end
1135 return substr($str, $real_start, $chars[$html_length+$length][1] - $real_start);
1140 * Finds all English string files in the standard lang/en_utf8 location.
1142 * Core lang files should always be stored here and not in the module space (MDL-10920).
1143 * The English version of the file may be found in
1144 * $CFG->dirroot/lang/en_utf8/filename
1145 * The localised version of the found file should be saved into
1146 * $CFG->dataroot/lang/currentlang[_local]/filename
1147 * where "filename" is returned as a part of the file record.
1149 * @return array Array of a file information. Compatible format with {@link lang_extra_locations()}
1151 function lang_standard_locations() {
1152 global $CFG;
1153 $files = array();
1154 // Standard location of master English string files.
1155 $places = array($CFG->dirroot.'/lang/en_utf8');
1156 foreach ($places as $place) {
1157 foreach (get_directory_list($place, '', false) as $file) {
1158 if ((substr($file, -4) == ".php") && ($file != "langconfig.php")) {
1159 $fullpath = $place.'/'.$file;
1160 $files[$fullpath] = array(
1161 'filename' => $file,
1162 'location' => '',
1163 'plugin' => '',
1164 'prefix' => '',
1169 return $files;
1173 * Finds all English string files in non-standard location.
1175 * Searches for lang/en_utf8/*.php in various types of plugins (blocks, database presets, question types,
1176 * 3rd party modules etc.) and returns an array of found files details.
1178 * The English version of the file may be found in
1179 * $CFG->dirroot/location/plugin/lang/en_utf8/filename
1180 * The localised version of the found file should be saved into
1181 * $CFG->dataroot/lang/currentlang[_local]/prefix_plugin.php
1182 * where "location", "plugin", "prefix" and "filename" are returned as a part of the file record.
1184 * @return array Array of a file information. Compatible format with {@link lang_standard_locations()}
1186 function lang_extra_locations() {
1187 global $CFG;
1188 $files = array();
1189 $places = places_to_search_for_lang_strings();
1190 foreach ($places as $prefix => $directories) {
1191 if ($prefix != '__exceptions') {
1192 foreach ($directories as $directory) {
1193 foreach (get_list_of_plugins($directory) as $plugin) {
1194 $enlangdirlocation = $CFG->dirroot.'/'.$directory.'/'.$plugin.'/lang/en_utf8';
1195 foreach (get_directory_list($enlangdirlocation, '', false) as $file) {
1196 if ((substr($file, -4) == ".php") && ($file != "langconfig.php")) {
1197 $fullpath = $enlangdirlocation.'/'.$file;
1198 $files[$fullpath] = array(
1199 'filename' => $file,
1200 'location' => $directory,
1201 'plugin' => $plugin,
1202 'prefix' => $prefix,
1210 return $files;
1214 * Lookup for a stringfile details.
1216 * English files can be stored in several places (core space or module/plugin space). Their translations
1217 * go into the one directory - the current language pack. Therefore, the name of the stringfile may be
1218 * considered as a key of the list of all stringfiles.
1220 * @param string $currentfile the filename
1221 * @param array $stringfiles the array of file info returned by {@link lang_extra_locations()}
1222 * @return array Array of a file information (filename, location, plugin, prefix) or null.
1224 function lang_get_file_info($currentfile, $stringfiles) {
1225 $found = false;
1226 foreach ($stringfiles as $path=>$stringfile) {
1227 if ($stringfile['filename'] == $currentfile) {
1228 $found = true;
1229 $ret = $stringfile;
1230 $ret['fullpath'] = $path;
1231 break;
1234 if ($found) {
1235 return $ret;
1236 } else {
1237 return null;
1242 * Returns all English help files in the standard lang/en_utf8/help location.
1244 * Core help files should always be stored here and not in the module space (MDL-10920).
1245 * The English version of the file may be found in
1246 * $CFG->dirroot/lang/en_utf8/help/filename
1247 * The localised version of the found file should be saved into
1248 * $CFG->dataroot/lang/currentlang[_local]/help/filename
1249 * where "filename" is returned as a part of the file record.
1251 * @return array Array of a file information. Compatible format with {@link lang_extra_locations()}
1253 function lang_help_standard_locations() {
1254 global $CFG;
1255 $files = array();
1256 // Standard location of master English help files.
1257 $places = array($CFG->dirroot.'/lang/en_utf8/help');
1258 foreach ($places as $place) {
1259 foreach (get_directory_list($place, 'CVS') as $file) {
1260 if ((substr($file, -5) == '.html') || (substr($file, -4) == '.txt' )) {
1261 $fullpath = $place.'/'.$file;
1262 $files[$fullpath] = array(
1263 'filename' => $file,
1264 'location' => '',
1265 'plugin' => '',
1266 'prefix' => '',
1271 return $files;
1275 * Returns all English help files in non-standard location.
1277 * Searches for lang/en_utf8/help/* files in various types of plugins (blocks, database presets, question types,
1278 * 3rd party modules etc.) and returns an array of found files details.
1280 * The English version of the file may be found in
1281 * $CFG->dirroot/location/plugin/lang/en_utf8/help/filename
1282 * The localised version of the found file should be saved into
1283 * $CFG->dataroot/lang/currentlang[_local]/help/prefix_plugin/filename (XXX is "prefix" here right?)
1284 * where "location", "plugin", "prefix" and "filename" are returned as a part of the file record.
1286 * @return array Array of a file information. Compatible format with {@link lang_standard_locations()}
1288 function lang_help_extra_locations() {
1289 global $CFG;
1290 $files = array();
1291 $places = places_to_search_for_lang_strings();
1292 foreach ($places as $prefix => $directories) {
1293 if ($prefix != '__exceptions') {
1294 foreach ($directories as $directory) {
1295 foreach (get_list_of_plugins($directory) as $plugin) {
1296 $enlangdirlocation = $CFG->dirroot.'/'.$directory.'/'.$plugin.'/lang/en_utf8/help';
1297 foreach (get_directory_list($enlangdirlocation, 'CVS') as $file) {
1298 if ((substr($file, -5) == '.html') || (substr($file, -4) == '.txt' )) {
1299 $fullpath = $enlangdirlocation.'/'.$file;
1300 $files[$fullpath] = array(
1301 'filename' => $file,
1302 'location' => $directory,
1303 'plugin' => $plugin,
1304 'prefix' => $prefix,
1312 return $files;
1316 * Return a preview URL for help file, if available.
1318 * @param string $currentfile The relative path to the help file, e.g. "assignment/types.html" - MDL-12291
1319 * @param bool $skiplocal Force displaying the helpfile from a master lang pack
1320 * @param string $forcelang Force language of the help, e.g. "en_utf8"
1321 * @return string $url
1323 function lang_help_preview_url($currentfile, $skiplocal=false, $forcelang = '') {
1324 $currentpathexp = explode('/', $currentfile);
1325 if (count($currentpathexp) > 1) {
1326 $url = '/help.php?module='.implode('/',array_slice($currentpathexp,0,count($currentpathexp)-1)).'&amp;file='.end($currentpathexp);
1327 } else {
1328 $url = '/help.php?module=moodle&amp;file='.$currentfile;
1330 if ($skiplocal) {
1331 $url .= '&amp;skiplocal=1';
1333 if ($forcelang) {
1334 $url .= '&amp;forcelang='.$forcelang;
1336 return $url;
1341 * Saves (overwrites) translated help file.
1343 * @param string $helproot The path to the "help" folder
1344 * @param string $file The relative path to the html help file
1345 * @param string $content HTML data to be saved
1346 * @return bool False if save failed, true otherwise
1348 function lang_help_save_file($helproot, $file, $content) {
1349 global $CFG, $USER;
1351 $content = str_replace("\r", "",$content); // Remove linefeed characters
1352 $content = preg_replace("/\n{3,}/", "\n\n", $content); // Collapse runs of blank lines
1353 $content = trim($content); // Delete leading/trailing whitespace
1354 if (is_readable("$helproot/$file") && filesize("$helproot/$file") > 0 && $content == '') {
1355 notify(get_string('langrmyourself', 'admin'));
1356 return true;
1359 error_reporting(0);
1360 if (!$f = fopen("$helproot/$file","w")) {
1361 error_reporting($CFG->debug);
1362 return false;
1364 error_reporting($CFG->debug);
1366 fwrite($f, stripslashes($content));
1367 fclose($f);
1369 // Remove file if its empty
1370 if (filesize("$helproot/$file") == 0) {
1371 unlink("$helproot/$file");
1374 return true;