lang.php 58.8 KB
Newer Older
martin's avatar
 
martin committed
1
<?PHP // $Id$
2
3
    /**
    * Display the admin/language menu and process strings translation.
4
5
6
7
    *
    * @param string $mode the mode of the script: null, "compare", "missing"
    * @param string $currentfile the filename of the English file to edit (if mode==compare)
    * @param bool $uselocal save translations into *_local pack?
8
    */
martin's avatar
 
martin committed
9

mudrd8mz's avatar
mudrd8mz committed
10
11
    $dbg = '';  // debug output;

skodak's avatar
skodak committed
12
    require_once('../config.php');
13
    require_once($CFG->libdir.'/adminlib.php');
14

15
    admin_externalpage_setup('langedit');
martin's avatar
 
martin committed
16

17
    $context = get_context_instance(CONTEXT_SYSTEM);
18

19
20
21
22
    define('LANG_SUBMIT_REPEAT', 1);            // repeat displaying submit button?
    define('LANG_SUBMIT_REPEAT_EVERY', 20);     // if so, after how many lines?
    define('LANG_DISPLAY_MISSING_LINKS', 1);    // display "go to first/next missing string" links?
    define('LANG_DEFAULT_FILE', '');            // default file to translate. Empty allowed
mudrd8mz's avatar
mudrd8mz committed
23
    define('LANG_DEFAULT_HELPFILE', '');        // default helpfile to translate. Empty allowed
24
25
    define('LANG_LINK_MISSING_STRINGS', 1);     // create links from "missing" page to "compare" page?
    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
30

mudrd8mz's avatar
mudrd8mz committed
31
32
33
34
35
36
37
38
    $mode = optional_param('mode', '', PARAM_ALPHA);
    if ($mode == 'helpfiles') {
        // use different PARAM_ options according to mode
        $currentfile = optional_param('currentfile', LANG_DEFAULT_HELPFILE, PARAM_PATH);
    } else {
        $currentfile = optional_param('currentfile', LANG_DEFAULT_FILE, PARAM_FILE);
    }
    $uselocal = optional_param('uselocal', -1, PARAM_INT);
39
40
41
42
43
44
45
46
47
48

    if ($uselocal == -1) {
        if (isset($SESSION->langtranslateintolocal)) {
            $uselocal = $SESSION->langtranslateintolocal;
        } else {
            $uselocal = LANG_DEFAULT_USELOCAL;
        }
    } else {
        $SESSION->langtranslateintolocal = $uselocal;
    }
49

50
51
52
53
    if (!has_capability('moodle/site:langeditmaster', $context, $USER->id, false)) {
        // Force using _local
        $uselocal = 1;
    }
54

55
    if (!has_capability('moodle/site:langeditmaster', $context, $USER->id, false) && (!$uselocal)) {
dongsheng's avatar
dongsheng committed
56
        print_error('cannoteditmasterlang', 'error');
57
58
    }

59
    if ((!has_capability('moodle/site:langeditlocal', $context, $USER->id, false)) && ($uselocal)) {
dongsheng's avatar
dongsheng committed
60
        print_error('cannotcustomizelocallang', 'error');
61
    }
62

63
    $strlanguage = get_string("language");
64
65
    $strcurrentlanguage = get_string("currentlanguage");
    $strmissingstrings = get_string("missingstrings");
66
67
    $streditstrings = get_string("editstrings", 'admin');
    $stredithelpdocs = get_string("edithelpdocs", 'admin');
68
    $strthislanguage = get_string("thislanguage");
moodler's avatar
moodler committed
69
    $strgotofirst = get_string('gotofirst','admin');
moodler's avatar
moodler committed
70
71
72
73
    $strfilestoredin = get_string('filestoredin', 'admin');
    $strfilestoredinhelp = get_string('filestoredinhelp', 'admin');
    $strswitchlang = get_string('switchlang', 'admin');
    $strchoosefiletoedit = get_string('choosefiletoedit', 'admin');
74
    $streditennotallowed = get_string('langnoeditenglish', 'admin');
moodler's avatar
moodler committed
75
    $strfilecreated = get_string('filecreated', 'admin');
76
77
    $strprev = get_string('previous');
    $strnext = get_string('next');
78
79
80
    $strlocalstringcustomization = get_string('localstringcustomization', 'admin');
    $strlangpackmaintaining = get_string('langpackmaintaining', 'admin');
    $strnomissingstrings = get_string('nomissingstrings', 'admin');
81
    $streditingnoncorelangfile = get_string('editingnoncorelangfile', 'admin');
mudrd8mz's avatar
mudrd8mz committed
82
83
84
    $strlanglocalpackage = get_string('langlocalpackage', 'admin');
    $strlangmasterpackage = get_string('langmasterpackage', 'admin');
    $strlangmasterenglish = get_string('langmasterenglish', 'admin');
85

86
    $currentlang = current_language();
skodak's avatar
skodak committed
87

88
89
    switch ($mode) {
        case "missing":
90
91
            // Missing array keys are not bugs here but missing strings
            error_reporting(E_ALL ^ E_NOTICE);
92
            $title = $strmissingstrings;
93
94
            break;
        case "compare":
95
            $title = $streditstrings;
96
            break;
mudrd8mz's avatar
mudrd8mz committed
97
98
        case "helpfiles":
            $title = $stredithelpdocs;
99
        default:
100
            $title = $strlanguage;
101
102
            break;
    }
103
    $navlinks[] = array('name' => $strlanguage, 'link' => "$CFG->wwwroot/$CFG->admin/lang.php", 'type' => 'misc');
104
    $navigation = build_navigation($navlinks);
105

106
    admin_externalpage_print_header();
107
108
109
110
111
112
113
114
115
116
117
118
119
120

    // Prepare and render menu tabs
    $firstrow = array();
    $secondrow = array();
    $inactive = NULL;
    $activated = NULL;
    $currenttab = $mode;
    if ($uselocal) {
        $inactive = array('uselocal');
        $activated = array('uselocal');
    } else {
        $inactive = array('usemaster');
        $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
125
126
127
            $strlocalstringcustomization );
    }
    if (has_capability('moodle/site:langeditmaster', $context, $USER->id, false)) {
        $firstrow[] = new tabobject('usemaster',
128
            "$CFG->wwwroot/$CFG->admin/lang.php?mode=$mode&amp;currentfile=$currentfile&amp;uselocal=0",
129
130
            $strlangpackmaintaining );
    }
131
132
133
    $secondrow[] = new tabobject('missing', "$CFG->wwwroot/$CFG->admin/lang.php?mode=missing", $strmissingstrings );
    $secondrow[] = new tabobject('compare', "$CFG->wwwroot/$CFG->admin/lang.php?mode=compare", $streditstrings );
    $secondrow[] = new tabobject('helpfiles', "$CFG->wwwroot/$CFG->admin/lang.php?mode=helpfiles", $stredithelpdocs );
134
135
    $tabs = array($firstrow, $secondrow);
    print_tabs($tabs, $currenttab, $inactive, $activated);
136

137
138

    if (!$mode) {
mudrd8mz's avatar
mudrd8mz committed
139
        // TODO this is a very nice place to put some translation statistics
moodler's avatar
moodler committed
140
        print_box_start();
141
        $currlang = current_language();
142
        $langs = get_list_of_languages(false, true);
moodler's avatar
moodler committed
143
144
        popup_form ("$CFG->wwwroot/$CFG->admin/lang.php?lang=", $langs, "chooselang", $currlang, "", "", "", false, 'self', $strcurrentlanguage.':');
        print_box_end();
145
        echo $OUTPUT->footer();
146
147
        exit;
    }
martin's avatar
 
martin committed
148

149
    // Get a list of all the root files in the English directory
martin's avatar
 
martin committed
150

151
    $langbase = $CFG->dataroot . '/lang';
toyomoyo's avatar
toyomoyo committed
152
    $enlangdir = "$CFG->dirroot/lang/en_utf8";
153
154
155
    if ($currentlang == 'en_utf8') {
        $langdir = $enlangdir;
    } else {
156
        $langdir = "$langbase/$currentlang";
157
    }
158
    $locallangdir = "$langbase/{$currentlang}_local";
mudrd8mz's avatar
mudrd8mz committed
159
    $saveto = $uselocal ? $locallangdir : $langdir;
martin's avatar
 
martin committed
160

mudrd8mz's avatar
mudrd8mz committed
161
162
163
164
165
166
167
    if (($mode == 'missing') || ($mode == 'compare')) {
        // get the list of all English stringfiles
        $stringfiles = lang_standard_locations();
        if (LANG_SEARCH_EXTRA) {
            $stringfiles += lang_extra_locations();
        }
        if (count($stringfiles) == 0) {
dongsheng's avatar
dongsheng committed
168
            print_error('cannotfindlang', 'error', '', 'English');
mudrd8mz's avatar
mudrd8mz committed
169
170
171
172
173
174
175
        }
    } elseif ($mode == 'helpfiles') {
        $helpfiles = lang_help_standard_locations();
        if (LANG_SEARCH_EXTRA) {
            $helpfiles += lang_help_extra_locations();
        }
        if (count($helpfiles) == 0) {
dongsheng's avatar
dongsheng committed
176
            print_error("cannotfindhelp", 'error', '', 'English');
mudrd8mz's avatar
mudrd8mz committed
177
        }
178
    }
martin's avatar
 
martin committed
179

mudrd8mz's avatar
mudrd8mz committed
180
181
182


    if ($mode == 'missing') {
183
        if (!file_exists($langdir)) {
184
            print_error('invalidlangpack');
185
186
        }

187
188
189
190
        // Following variables store the HTML output to be echo-ed
        $m = '';
        $o = '';

191
192
193
194
195
196
        $m_x = false;

        // Total number of strings and missing strings
        $totalcounter->strings = 0;
        $totalcounter->missing = 0;       

197
        // For each file, check that a counterpart exists, then check all the strings
198
199
200
201
202
        foreach ($stringfiles as $stringfile) {
            $location = $stringfile['location'];
            $plugin = $stringfile['plugin'];
            $prefix = $stringfile['prefix'];
            $filename = $stringfile['filename'];
203
            unset($string);
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
            
            // Get some information about file locations:
            //  $enfilepath = the path to the English file distributed either in the core space or in plugin space
            //  $trfilepath = the path to the translated file distributed either in the lang pack or in plugin space
            //  $lcfilepath = the path to the _local customization
            //  $trfilename = the filename of the translated version of the file (including prefix for non-core files)
            if ($location || $plugin) {
                // non-core file in an extra location
                $enfilepath = "$CFG->dirroot/$location/$plugin/lang/en_utf8/$filename";
                $trfilepath = "$CFG->dirroot/$location/$plugin/lang/$currentlang/$filename";
                $lcfilepath = "$locallangdir/$filename";
                $trfilename = $filename;
                if (!$m_x) {
                    $m .= '<hr />';
                    $m_x = true;
                }
            } else {
                // core file in standard location
                $enfilepath = "$CFG->dirroot/lang/en_utf8/$filename";
                $trfilepath = "$langdir/$filename";
                $lcfilepath = "$locallangdir/$filename";
                $trfilename = $filename;
            }
            // $enstring = English strings distributed either in the core space or in plugin space
            include($enfilepath);
229
            $enstring = isset($string) ? $string : array();
230
            unset($string);
231
232
233
234
235
236
237
            ksort($enstring);
            
            //$lcstring = local customizations
            $lcstring = array();
            if (file_exists($lcfilepath)) {
                include($lcfilepath);
                $localfileismissing = 0;
238
                if (isset($string) && is_array($string)) {
239
240
241
242
243
244
245
                    $lcstring = $string;
                }
                unset($string);
                ksort($lcstring);
            } else {
                $localfileismissing = 1;
            }
246

247
248
249
250
            // $string = translated strings distibuted either in core lang pack or in plugin space
            $string = array();
            if (file_exists($trfilepath)) {
                include($trfilepath);
251
                $fileismissing = 0;
252
            } else {
253
                $fileismissing = 1;
254
                $o .= notify(get_string("filemissing", "", $trfilepath), "notifyproblem", "center", true);
255
            }
256
257

            $missingcounter = 0;
258

259
260
            $first = true; // first missing string found in the file
            // For all English strings in the current file check distributed translations and _local customizations
261
            foreach ($enstring as $key => $value) {
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
                $totalcounter->strings++;
                $missingstring = false;
                $missinglocalstring = false;
                $translationsdiffer = false;
                if (empty($string[$key]) and $string[$key] != "0") { // MDL-4735
                    // string is missing in distributed pack
                    $missingstring = true;
                }
                if (empty($lcstring[$key]) and $lcstring[$key] != "0") { // MDL-4735
                    // string is missing in _local customization
                    $missinglocalstring = true;
                }
                if (!$missingstring && !$missinglocalstring && ($lcstring[$key] != $string[$key])) {
                    $translationsdiffer = true;
                }
                if ($missingstring || $translationsdiffer) {
moodler's avatar
   
moodler committed
278
                    $value = htmlspecialchars($value);
279
                    $value = str_replace("$"."a", "\\$"."a", $value);
moodler's avatar
   
moodler committed
280
                    $value = str_replace("%%","%",$value);
281
                    if ($first) {
282
                        $m .= "<a href=\"lang.php?mode=missing#$trfilename\">$trfilename";
283
284
                        $m .= $fileismissing ? '*' : '';
                        $m .= '</a> &nbsp; ';
285
286
                        $o .= "<p><a name=\"$trfilename\"></a><b>".
                            get_string("stringsnotset","", $trfilepath)."</b></p><pre>";
287
288
289
                        $first = false;
                        $somethingfound = true;
                    }
290
291
292
293
                    if ($missingstring) {
                        $missingcounter++;
                        $totalcounter->missing++;
                    }
294
295
296
297
                    if ($translationsdiffer) {
                        $missingcounter++;
                    }
                    if (LANG_LINK_MISSING_STRINGS && ($missingstring || $translationsdiffer)) {
mudrd8mz's avatar
mudrd8mz committed
298
                        $missinglinkstart = "<a href=\"lang.php?mode=compare&amp;currentfile=$filename#$key\">";
299
300
301
302
303
                        $missinglinkend = '</a>';
                    } else {
                        $missinglinkstart = '';
                        $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
306
                    }
307
308
309
310
311
312
313
314
315
316
                    if ($translationsdiffer) {
                        $o .= '// ';
                    }
                    $o .= "$"."string['".$missinglinkstart.$key.$missinglinkend."'] = \"$value\";";
                    if ($translationsdiffer) {
                        $o .= '    // differs from the translation in _local';
                    } elseif (!$missinglocalstring) {
                        $o .= '    // translated only in _local';
                    }
                    $o .= "\n";
317
318
                }
            }
defacer's avatar
defacer committed
319
            if (!$first) {
320
                $o .= '</pre><hr />';
defacer's avatar
defacer committed
321
            }
322
        }
323

324
325
        if ($totalcounter->missing > 0) {
            $totalcounter->missingpercent = sprintf('%02.1f', ($totalcounter->missing / $totalcounter->strings * 100));
326
            echo $OUTPUT->heading(get_string('numberofstrings', 'admin', $totalcounter), 4);
327
        } else {
328
            echo $OUTPUT->heading($strnomissingstrings, 4, 'notifysuccess');
329
330
        }

331
        if ($m <> '') {
moodler's avatar
moodler committed
332
            print_box($m, 'filenames');
333
        }
334

335
336
        echo $o;

toyomoyo's avatar
toyomoyo committed
337
        if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/help", "CVS")) {
dongsheng's avatar
dongsheng committed
338
            print_error("cannotfindhelp", 'error', '', 'English');
339
        }
340

341
342
        foreach ($files as $filekey => $file) {    // check all the help files.
            if (!file_exists("$langdir/help/$file")) {
343
                notify(get_string("filemissing", "", "$langdir/help/$file"), 'notifyproblem');
344
345
346
347
                $somethingfound = true;
                continue;
            }
        }
348

toyomoyo's avatar
toyomoyo committed
349
        if (! $files = get_directory_list("$CFG->dirroot/lang/en_utf8/docs", "CVS")) {
dongsheng's avatar
dongsheng committed
350
            print_error("cannotfinddoc", 'error', '', 'English');
351
352
353
        }
        foreach ($files as $filekey => $file) {    // check all the docs files.
            if (!file_exists("$langdir/docs/$file")) {
354
                notify(get_string("filemissing", "", "$langdir/docs/$file"), 'notifyproblem');
355
356
357
358
                $somethingfound = true;
                continue;
            }
        }
359

moodler's avatar
moodler committed
360
        if (!empty($somethingfound)) {
361
362
            print_continue("lang.php");
        } else {
363
            notice(get_string("languagegood"), "lang.php" );
martin's avatar
 
martin committed
364
365
        }

mudrd8mz's avatar
mudrd8mz committed
366
    } else if ($mode == 'compare') {
367

368
369
        if (!file_exists($langbase) ){
            if (!lang_make_directory($langbase) ){
dongsheng's avatar
dongsheng committed
370
                print_error('cannotcreatelangbase', 'error');
371
            } else {
372
                echo '<div class="notifysuccess">Created directory '.
373
374
                                                     $langbase .'</div>'."<br />\n";
            }
375
        }
376
377
        if (!$uselocal && !file_exists($langdir)) {
            if (!lang_make_directory($langdir)) {
dongsheng's avatar
dongsheng committed
378
                print_error('cannotcreatelangdir', 'error');
379
            } else {
380
                echo '<div class="notifysuccess">Created directory '.
381
382
                                                     $langdir .'</div>'."<br />\n";
            }
383
        }
384
385
        if ($uselocal && !file_exists($locallangdir)) {
            if (!lang_make_directory($locallangdir)) {
386
                echo '<div class="notifyproblem">ERROR: Could not create directory '.
387
388
389
                                     $locallangdir .'</div>'."<br />\n";
                $uselocal = 0;
            } else {
390
                echo '<div class="notifysuccess">Created directory '.
391
392
393
                                                     $locallangdir .'</div>'."<br />\n";
            }
        }
394

395
396
        if ($currentfile <> '') {
            if (!$fileinfo = lang_get_file_info($currentfile, $stringfiles)) {
dongsheng's avatar
dongsheng committed
397
                print_error('cannotfindinfo', 'error', '', $currentfile);
398
399
400
401
402
403
404
405
406
            }
            // check the filename is set up correctly, prevents bugs similar to MDL-10920
            $location = $fileinfo['location'];
            $plugin = $fileinfo['plugin'];
            $prefix = $fileinfo['prefix'];
            $filename = $fileinfo['filename'];
            if ($location || $plugin) {
                // file in an extra location
                if ($currentfile != "{$prefix}{$plugin}.php") {
407
                    print_error('filemismatch', 'error', '', (object)array('current'=>$currectfile, 'file'=>$prefix.$plugin.'.php'));
408
409
410
411
412
413
414
415
                }
                if (!$uselocal) {
                    notify($streditingnoncorelangfile);
                    $editable = false;
                }
            } else {
                // file in standard location
                if ($currentfile != $filename) {
416
                    print_error('filemismatch', 'error', '', (object)array('current'=>$currectfile, 'file'=>$filename.'.php'));
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
                }
            }

            // Get some information about file locations:
            //  $enfilepath = the path to the English file distributed either in the core space or in plugin space
            //  $trfilepath = the path to the translated file distributed either in the lang pack or in plugin space
            //  $lcfilepath = the path to the _local customization
            //  $trfilename = the filename of the translated version of the file (including prefix for non-core files)
            if ($location || $plugin) {
                // non-core file in an extra location
                $enfilepath = "$CFG->dirroot/$location/$plugin/lang/en_utf8/$filename";
                $trfilepath = "$CFG->dirroot/$location/$plugin/lang/$currentlang/$filename";
                $lcfilepath = "$locallangdir/$filename";
                $trfilename = $filename;
            } else {
                // core file in standard location
                $enfilepath = "$CFG->dirroot/lang/en_utf8/$filename";
                $trfilepath = "$langdir/$filename";
                $lcfilepath = "$locallangdir/$filename";
                $trfilename = $filename;
            }
        }

440
        if (isset($_POST['currentfile'])){   // Save a file
441
            if (!confirm_sesskey()) {
442
                print_error('confirmsesskeybad', 'error');
443
            }
444

445
            $newstrings = array();
446

447
448
449
450
            foreach ($_POST as $postkey => $postval) {
                $stringkey = lang_file_string_key($postkey);
                $newstrings[$stringkey] = $postval;
            }
451

452
            unset($newstrings['currentfile']);
453

454
455
            $packstring = array();
            $saveinto = $langdir;
456
            if ($uselocal) {
457
458
459
460
461
462
                if(file_exists($trfilepath)) {
                    include($trfilepath);
                    if (isset($string)) {
                        $packstring = $string;
                    }
                    unset($string);
463
464
                }
                $saveinto = $locallangdir;
465
            }
466
             
467
            if (lang_save_file($saveinto, $currentfile, $newstrings, $uselocal, $packstring)) {
mudrd8mz's avatar
mudrd8mz committed
468
                notify(get_string("changessaved")." ($saveinto/$currentfile)", "notifysuccess");
469
            } else {
470
                print_error('cannotsavefile', 'error', 'lang.php?mode=compare&amp;currentfile=$currentfile', $saveinto.'/'.$currentfile);
471
472
            }
            unset($packstring);
473
        }
474

moodler's avatar
moodler committed
475
        print_box_start('generalbox editstrings');
476
        $menufiles = array();
477
478
479
480
481
482
483
484
485
486
487
488
        $menufiles_coregrp = 1;
        foreach ($stringfiles as $stringfile) {
            $item_key = $stringfile['filename'];
            $item_label = $stringfile['filename'];
            if ($stringfile['location'] != '' && $stringfile['plugin'] != '') {
                $item_label .= ' ('.$stringfile['location'].'/'.$stringfile['plugin'].')';
                if ($menufiles_coregrp == 1) {
                    $menufiles['extra'] = '------------';
                    $menufiles_coregrp = 0;
                }
            }
            $menufiles[$item_key] = $item_label;
489
        }
mudrd8mz's avatar
mudrd8mz committed
490
491
492
493
        $selectionlabel = '<code class="path">';
        //$selectionlabel .= $strfilestoredin;
        $selectionlabel .= $uselocal ? "{$currentlang}_local" : $currentlang;
        $selectionlabel .= '/</code>';
494
        popup_form("$CFG->wwwroot/$CFG->admin/lang.php?mode=compare&amp;currentfile=", $menufiles, "choosefile",
mudrd8mz's avatar
mudrd8mz committed
495
            $currentfile, $strchoosefiletoedit, '', '', false, 'self', $selectionlabel);
496
        helpbutton('langswitchstorage', $strfilestoredinhelp, 'moodle');
moodler's avatar
moodler committed
497
        print_box_end();
mudrd8mz's avatar
mudrd8mz committed
498
        
499
        if ($currentfile <> '') {
500
            error_reporting(0);
501
502
503
            if (!isset($editable) || $editable) {
                if (!file_exists("$saveto/$currentfile")) {
                    if (!@touch("$saveto/$currentfile")) {
504
                        echo $OUTPUT->heading(get_string("filemissing", "", "$saveto/$currentfile"), 4, 'error');
505
                    } else {
506
                        echo $OUTPUT->heading($strfilecreated, 4, 'notifysuccess');
507
508
509
510
                    }
                }
                if ($currentlang == "en_utf8" && !$uselocal) {
                    $editable = false;
511
                    echo $OUTPUT->heading($streditennotallowed, 4);
512
513
514
                } elseif ($f = fopen("$saveto/$currentfile","r+")) {
                    $editable = true;
                    fclose($f);
515
                } else {
516
517
                    $editable = false;
                    notify(get_string("makeeditable", "", "$saveto/$currentfile"), 'notifyproblem');
518
                }
519
            }
520
            error_reporting($CFG->debug);
521

522
            $o = '';    // stores the HTML output to be echo-ed
523
            
524
            unset($string);
525
            include($enfilepath);
526
            $enstring = isset($string) ? $string : array();
527
            //
528
            // Following strings have moved into langconfig.php, but keep the here for backward compatibility
529
            //
530
531
            if ($currentlang != 'en' and $currentfile == 'moodle.php') {
                $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
534
535
536
                $enstring['thisdirection'] = "<< TRANSLATORS: This string specifies the direction of your text, either left-to-right or right-to-left.  Insert either 'ltr' or 'rtl' here. >>";
                $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 >>";
            }
            unset($string);
537
            ksort($enstring);
538

539
            @include($lcfilepath);
540
541
            $localstring = isset($string) ? $string : array();
            unset($string);
542
            ksort($localstring);
543

544
545
546
            @include($trfilepath);
            $string = isset($string) ? $string : array();
            ksort($string);
547

548
            if ($editable) {
549
550
                $o .= "<form id=\"$currentfile\" action=\"lang.php\" method=\"post\">";
                $o .= '<div>';
551
            }
552
            $o .= "<table summary=\"\" width=\"100%\" class=\"translator\">";
553
554
555
556
557
            $linescounter = 0;
            $missingcounter = 0;
            foreach ($enstring as $key => $envalue) {
                $linescounter++ ;
                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>';
561
562
                }
                $envalue = nl2br(htmlspecialchars($envalue));
563
                $envalue = preg_replace('/(\$a\-\&gt;[a-zA-Z0-9]*|\$a)/', '<b>$0</b>', $envalue);  // Make variables bold.
564
565
566
                $envalue = str_replace("%%","%",$envalue);
                $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';
572
                }
573
574
                $o .= '">';
                $o .= '<td dir="ltr" lang="en">';
mudrd8mz's avatar
mudrd8mz committed
575
                $o .= '<span id="'.$key.'" class="stren">'.$envalue.'</span>';
576
577
578
                $o .= '<br />'."\n";
                $o .= '<span class="strkey">'.$key.'</span>';
                $o .= '</td>'."\n";
579

580
581
                // Missing array keys are not bugs here but missing strings
                error_reporting(E_ALL ^ E_NOTICE);
582
583
584
585
586
587
                if ($uselocal) {
                    $value = lang_fix_value_from_file($localstring[$key]);
                    $value2 = lang_fix_value_from_file($string[$key]);
                    if ($value == '') {
                        $value = $value2;
                    }
588
                } else {
589
590
                    $value = lang_fix_value_from_file($string[$key]);
                    $value2 = lang_fix_value_from_file($localstring[$key]);
martin's avatar
 
martin committed
591
                }
592
                error_reporting($CFG->debug);
593
594
595
596
597
                $missingtarget = '';
                $missingnext = '';
                $missingprev = '';
                $cellcolour = '';
                $usetabindex = false;
598
                if (!$value) {
599
                    // the string is not present in the pack being processed
600
                    if (!$value2) {
601
                        $cellcolour = 'class="bothmissing"';
mudrd8mz's avatar
mudrd8mz committed
602
                        $usetabindex = true;
603
                    } else {
604
                        $cellcolour = 'class="mastermissing"';
mudrd8mz's avatar
mudrd8mz committed
605
                        $usetabindex = true;
606
607
608
609
610
                    }
                    $missingcounter++;
                    if (LANG_DISPLAY_MISSING_LINKS) {
                        $missingtarget = '<a name="missing'.$missingcounter.'"></a>';
                        $missingnext = '<a href="#missing'.($missingcounter+1).'">'.
611
                        '<img src="' . $OUTPUT->old_icon_url('t/down') . '" class="iconsmall" alt="'.$strnext.'" /></a>';
612
                        $missingprev = '<a href="#missing'.($missingcounter-1).'">'.
613
                        '<img src="' . $OUTPUT->old_icon_url('t/up') . '" class="iconsmall" alt="'.$strprev.'" /></a>';
614
                    }
615
                } else {
616
                    // the string is translated in the pack being processed
mudrd8mz's avatar
mudrd8mz committed
617
                    if ($value <> $value2 && ($value2 <> '')) {
618
                        $cellcolour = 'class="localdifferent"';
mudrd8mz's avatar
mudrd8mz committed
619
                        $usetabindex = true;
620
621
622
623
                        $missingcounter++;
                        if (LANG_DISPLAY_MISSING_LINKS) {
                            $missingtarget = '<a name="missing'.$missingcounter.'"></a>';
                            $missingnext = '<a href="#missing'.($missingcounter+1).'">'.
624
                            '<img src="' . $OUTPUT->old_icon_url('t/down') . '" class="iconsmall" alt="'.$strnext.'" /></a>';
625
                            $missingprev = '<a href="#missing'.($missingcounter-1).'">'.
626
                            '<img src="' . $OUTPUT->old_icon_url('t/up') . '" class="iconsmall" alt="'.$strprev.'" /></a>';
627
                        }
628
629
630
                    }
                }

631
                if ($editable) {
632
633
634
635
636
                    $o .= '<td '.$cellcolour.' valign="top">';
                    if ($missingcounter > 1) {
                        $o .= $missingprev;
                    }
                    $o .= $missingtarget."\n";
637
638
639
640
641
                    if (isset($string[$key])) {
                        $valuelen = strlen($value);
                    } else {
                        $valuelen = strlen($envalue);
                    }
642
                    $cols=40;
mudrd8mz's avatar
mudrd8mz committed
643
644
645
646
647
                    if ($usetabindex) {
                        $tabindex = 'tabindex="'.$missingcounter.'"';
                    } else {
                        $tabindex = '';
                    }
648
649
                    if (strstr($value, "\r") or strstr($value, "\n") or $valuelen > $cols) {
                        $rows = ceil($valuelen / $cols);
mudrd8mz's avatar
mudrd8mz committed
650
                        $o .= '<textarea name="stringXXX'.lang_form_string_key($key).'" cols="'.$cols.'" rows="'.$rows.'" '.$tabindex.'>'.$value.'</textarea>'."\n";
651
652
653
654
                    } else {
                        if ($valuelen) {
                            $cols = $valuelen + 5;
                        }
655
656
657
658
659
                        if (LANG_ALWAYS_TEXTAREA) {
                            $o .= '<textarea name="stringXXX'.lang_form_string_key($key).'" cols="'.$cols.'" rows="1" '.$tabindex.'>'.$value.'</textarea>'."\n";
                        } else {
                            $o .= '<input type="text" name="stringXXX'.lang_form_string_key($key).'" value="'.$value.'" size="'.$cols.'" '.$tabindex.' />';
                        }
660
661
                    }
                    if ($value2 <> '' && $value <> $value2) {
662
                        $o .= '<br /><span style="font-size:small">'.$value2.'</span>';
663
                    }
664
                    $o .= $missingnext . '</td>';
665
666

                } else {
667
                    $o .= '<td '.$cellcolour.' valign="top">'.$value.'<br />'.$value2.'</td>';
668
                }
669
                $o .= '</tr>'."\n";
martin's avatar
 
martin committed
670
            }
671
            if ($editable) {
672
                $o .= '<tr><td>&nbsp;</td><td><br />';
skodak's avatar
skodak committed
673
                $o .= '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
674
675
                $o .= '<input type="hidden" name="currentfile" value="'.$currentfile.'" />';
                $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>';
678
            }
679
            $o .= '</table>';
680
            if ($editable) {
681
                $o .= '</div>';
682
683
684
685
686
                $o .= '</form>';
            }

            if (LANG_DISPLAY_MISSING_LINKS) {
                if ($missingcounter > 0) {
687
                    echo $OUTPUT->heading(get_string('numberofmissingstrings', 'admin', $missingcounter), 4);
688
                    if ($editable) {
689
                        echo $OUTPUT->heading('<a href="#missing1">'.$strgotofirst.'</a>', 4);
690
691
                    }
                } else {
692
                    echo $OUTPUT->heading($strnomissingstrings, 4, 'notifysuccess');
693
                }
694
            }
695
            echo $o;
martin's avatar
 
martin committed
696

697
698
        } else {
            // no $currentfile specified
699
            // no useful information to display - maybe some help? instructions?
700
        }
mudrd8mz's avatar
mudrd8mz committed
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717

    } elseif ($mode == 'helpfiles') {

        $saveto = $saveto.'/help';                      // the edited content will be saved to
        $enlangdir = $enlangdir.'/help';                // English master help files localtion
        $langdir = $langdir.'/help';                    // current language master help files location
        $locallangdir = $locallangdir.'/help';          // local modifications of help files location
        $altdir = $uselocal ? $langdir : $locallangdir; // alternative to $saveto

        $fileeditorrows = 10;           // number of textareas' rows
        $fileeditorcols = 100;          // dtto cols
        $filemissingmark = ' (***)';    // mark to add to non-existing or zero-length files
        $fileoldmark = ' (old?)';       // mark to add to filenames in selection form if the English version is newer
        $filetemplate = '';             // template for new files, e.g. CVS identification

        if (isset($_POST['currentfile'])) {  // Save a file
            if (!confirm_sesskey()) {
718
                print_error('confirmsesskeybad', 'error');
mudrd8mz's avatar
mudrd8mz committed
719
720
721
722
            }
            if (lang_help_save_file($saveto, $currentfile, $_POST['filedata'])) {
                notify(get_string("changessaved")." ($saveto/$currentfile)", "notifysuccess");
            } else {
dongsheng's avatar
dongsheng committed
723
                print_error('cannotsavefile', 'error', 'lang.php?mode=compare&amp;currentfile=$currentfile', array($saveinto, $currentfile));
mudrd8mz's avatar
mudrd8mz committed
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
            }
        }

        print_box_start('generalbox editstrings');
        $menufiles = array();
        $menufiles_coregrp = 1;
        $origlocation = ''; // the location of the currentfile's English source will be stored here
        $origplugin = '';   // dtto plugin
        foreach ($helpfiles as $helppath => $helpfile) {
            $item_key = $helpfile['filename'];
            $item_label = $helpfile['filename'];
            if ((!file_exists($saveto.'/'.$helpfile['filename'])) || (filesize($saveto.'/'.$helpfile['filename']) == 0)) {
                $item_label .= $filemissingmark;
            } else {
                if (filemtime($saveto.'/'.$helpfile['filename']) < filemtime($helppath)) {
                    $item_label .= $fileoldmark;
                }
                if ($helpfile['location'] != '' && $helpfile['plugin'] != '') {
                    $item_label .= ' ('.$helpfile['location'].'/'.$helpfile['plugin'].')';
                    if ($menufiles_coregrp == 1) {
                        $menufiles['extra'] = '------------';
                        $menufiles_coregrp = 0;
                    }
                }
            }
            $menufiles[$item_key] = $item_label;
            if ($currentfile == $helpfile['filename']) {
                $origlocation = $helpfile['location'];
                $origplugin = $helpfile['plugin'];
            }
        }
        $selectionlabel = '<code class="path">';
        //$selectionlabel .= $strfilestoredin;
        $selectionlabel .= $uselocal ? "{$currentlang}_local" : $currentlang;
        $selectionlabel .= '/help/</code>';
        popup_form("$CFG->wwwroot/$CFG->admin/lang.php?mode=helpfiles&amp;currentfile=", $menufiles, "choosefile",
            $currentfile, $strchoosefiletoedit, '', '', false, 'self', $selectionlabel);
        helpbutton('langswitchstorage', $strfilestoredinhelp, 'moodle');
        print_box_end();

        if (!empty($currentfile)) {

            if (!file_exists("$saveto/$currentfile")) {
                $dbg .= "File does not exist: $saveto/$currentfile\n";
                //check if directory exist
                if (!file_exists(dirname("$saveto/$currentfile"))) {
                     if(!lang_make_directory(dirname("$saveto/$currentfile"))) {
                         echo ('Cannot create directory: '.dirname("$saveto/$currentfile"));
                     }
                }
                //
                // file doesn't exist - let's check webserver's permission to create it
                //
                if (!@touch("$saveto/$currentfile")) {
                    //
                    // webserver is unable to create new file
                    //
                    notify(get_string('filemissing', '', "$saveto/$currentfile" ));
                    notify(get_string('makeeditable', '', "$saveto/$currentfile"));
                    $editable = false;
                } else {
                    //
                    // webserver can create new file - we can delete it now and let
                    // it create again if its filesize() > 0
                    //
                    $editable = true;
                    unlink("$saveto/$currentfile");
                }
            } elseif (is_writable("$saveto/$currentfile")) {
                $editable = true;
            } else {
                //
                // file exists but it is not writeable by web server process :-(
                //
                $editable = false;
                notify(get_string('makeeditable', '', "$saveto/$currentfile"));
            }

            // master en_utf8 in dataroot is not editable
            if ((!$uselocal) && ($currentlang == 'en_utf8')) {
                $editable = false;
            }

            echo '<div>';

            if ($uselocal) {
                $strsavetotitle = $strlanglocalpackage . helpbutton('langpackages', $strlanglocalpackage, 'moodle', true, false, '', true);
                $straltdirtitle = $strlangmasterpackage . helpbutton('langpackages', $strlangmasterpackage, 'moodle', true, false, '', true);
            } else {
                $straltdirtitle = $strlanglocalpackage . helpbutton('langpackages', $strlanglocalpackage, 'moodle', true, false, '', true);
                $strsavetotitle = $strlangmasterpackage . helpbutton('langpackages', $strlangmasterpackage, 'moodle', true, false, '', true);

            }

            if ($editable) {
                // generate an editor for the current help file in $saveto
                echo '<fieldset><legend>'.$strsavetotitle.'</legend>';
                echo "<form id=\"helpfileeditor\" action=\"lang.php\" method=\"post\">";
skodak's avatar
skodak committed
822
                echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
mudrd8mz's avatar
mudrd8mz committed
823
824
                echo '<input type="hidden" name="currentfile" value="'.$currentfile.'" />';
                echo '<input type="hidden" name="mode" value="helpfiles" />';
825
                echo "<div class='mdl-align'>\n";
mudrd8mz's avatar
mudrd8mz committed
826
827
828
829
830
831
832
                echo "<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"filedata\">";
                if (file_exists("$saveto/$currentfile")) {
                    echo htmlspecialchars(file_get_contents("$saveto/$currentfile"));
                } else {
                    echo ($filetemplate);
                }
                echo "</textarea>\n</div>\n";
833
                echo '<div class="mdl-align"><input type="submit" value="'.get_string('savechanges').'" /></div>';
mudrd8mz's avatar
mudrd8mz committed
834
835
836
837
838
839
840
841
842
843
844
                echo '</form>';
                $preview_url = lang_help_preview_url($currentfile, !$uselocal);
                if ($preview_url) {
                    link_to_popup_window($preview_url, 'popup', get_string('preview'));
                }
                echo '</fieldset>';
            }

            if (is_readable("$altdir/$currentfile")) {
                // show the content of the same help file in alternative location
                echo '<fieldset><legend>'.$straltdirtitle.'</legend>';
845
                echo "<div class='mdl-align'>\n";
mudrd8mz's avatar
mudrd8mz committed
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
                echo "<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"\">";
                if (file_exists("$altdir/$currentfile")) {
                    echo htmlspecialchars(file_get_contents("$altdir/$currentfile"));
                } else {
                    echo ($filetemplate);
                }
                echo "</textarea>\n</div>\n";
                $preview_url = lang_help_preview_url($currentfile, $uselocal);
                if ($preview_url) {
                    link_to_popup_window($preview_url, 'popup', get_string('preview'));
                }
                echo '</fieldset>';
            }

            // show the content of the original English file either in core space or plugin space
            if ($origlocation != '' && $origplugin != '') {
                // non-core help file
                $ensrc = "$CFG->dirroot/$origlocation/$origplugin/lang/en_utf8/help/$currentfile";
            } else {
                // core help file
                $ensrc = "$enlangdir/$currentfile";
            }
            if (is_readable($ensrc)) {
                echo '<fieldset><legend>'.$strlangmasterenglish;
                helpbutton('langpackages', $strlangmasterenglish);
                echo '</legend>';
872
                echo "<div class='mdl-align'>\n<textarea rows=\"$fileeditorrows\" cols=\"$fileeditorcols\" name=\"\">";
mudrd8mz's avatar
mudrd8mz committed
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
                echo htmlspecialchars(file_get_contents($ensrc));
                echo "</textarea>\n</div>\n";
                $preview_url = lang_help_preview_url($currentfile, true, 'en_utf8');   // do not display en_utf8_local
                if ($preview_url) {
                    link_to_popup_window($preview_url, 'popup', get_string('preview'));
                }
                echo '</fieldset>';
            }

            echo '</div>'; // translator box
            error_reporting($CFG->debug);
        }

        if (false && $CFG->debugdisplay && debugging('', DEBUG_DEVELOPER) ) {
            echo '<hr />';
888
            echo $OUTPUT->heading('Debugging info');
mudrd8mz's avatar
mudrd8mz committed
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
            echo '<pre class="notifytiny">';
            print_r($dbg);
            print_r("\n\$currentfile = $currentfile");
            print_r("\n\$enlangdir = $enlangdir");
            print_r("\n\$langdir = $langdir");
            print_r("\n\$locallangdir = $locallangdir");
            print_r("\n\$saveto = $saveto");
            print_r("\n\$altdir = $altdir");
            print_r("\n\$origlocation = $origlocation");
            print_r("\n\$origplugin = $origplugin");
            print_r("\n\$ensrc = $ensrc");
            print_r("\n\$helpfiles = ");
            print_r($helpfiles);
            echo '</pre>';
        }

    } // fi $mode == 'helpfiles'

martin's avatar
 
martin committed
907

908
    echo $OUTPUT->footer();
martin's avatar
 
martin committed
909

910
911
//////////////////////////////////////////////////////////////////////

912
913
914
/**
 * Save language translation file.
 *
915
 * Thanks to Petri Asikainen for the original version of code
916
917
918
919
920
921
922
923
924
925
 * used to save language files.
 *
 * @uses $CFG
 * @uses $USER
 * @param string $path Full pathname to the directory to use
 * @param string $file File to overwrite
 * @param array $strings Array of strings to write
 * @param bool $local Should *_local version be saved?
 * @param array $packstrings Array of default langpack strings (needed if $local)
 * @return bool Created successfully?
926
 */
927
function lang_save_file($path, $file, $strings, $local, $packstrings) {
928
    global $CFG, $USER;
929
930
931
932
933
934
935
936
937
938
939
940
    if (LANG_KEEP_ORPHANS) {
        // let us load the current content of the file
        unset($string);
        @include("$path/$file");
        if (isset($string)) {
            $orphans = $string;
            unset($string);
        } else {
            $orphans = array();
        }
    }
    // let us rewrite the file
941
    if (!$f = @fopen("$path/$file","w")) {
942
943
944
945
        return false;
    }

    fwrite($f, "<?PHP // \$Id\$ \n");
946
947
948
949
950
    fwrite($f, "      // $file - created with Moodle $CFG->release ($CFG->version)\n");
    if ($local) {
        fwrite($f, "      // local modifications from $CFG->wwwroot\n");
    }
    fwrite($f, "\n\n");
951
952
    ksort($strings);
    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");
961
962
963
            }
            if (LANG_KEEP_ORPHANS && isset($orphans[$stringname])) {
                unset($orphans[$stringname]);
964
            }
965
966
        }
    }
967
968
969
970
971
972
    if (LANG_KEEP_ORPHANS) {
        // let us add orphaned strings, i.e. already translated strings without the English referential source
        foreach ($orphans as $key => $value) {
            fwrite($f,"\$string['$key'] = '".lang_fix_value_before_save($value)."'; // ORPHANED\n");
        }
    }
973
    fwrite($f,"\n?>\n");
974
975
976
977
    fclose($f);
    return true;
}

978
/**
979
980
981
982
 * Fix value of the translated string after it is load from the file.
 *
 * These modifications are typically necessary to work with the same string coming from two sources.
 * 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".
mudrd8mz's avatar