MDL-23984 using standard checkdirexists for lang packs
[moodle.git] / admin / langimport.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Fetches language packages from download.moodle.org server
20  *
21  * Language packages are available at http://download.moodle.org/langpack/2.0/
22  * in ZIP format together with a file languages.md5 containing thir hashes
23  * and meta info.
24  * Locally, language packs are saved into $CFG->dataroot/lang/
25  *
26  * @package   core
27  * @copyright 2005 Yu Zhang
28  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29  */
31 require_once(dirname(dirname(__FILE__)).'/config.php');
32 require_once($CFG->libdir.'/adminlib.php');
33 require_once($CFG->libdir.'/filelib.php');
34 require_once($CFG->libdir.'/componentlib.class.php');
36 $thisversion = '2.0'; // TODO this information should be taken from version.php or similar source
38 admin_externalpage_setup('langimport');
40 if (!empty($CFG->skiplangupgrade)) {
41     echo $OUTPUT->header();
42     echo $OUTPUT->box(get_string('langimportdisabled', 'admin'));
43     echo $OUTPUT->footer();
44     die;
45 }
47 $mode          = optional_param('mode', 0, PARAM_INT);              // action
48 $pack          = optional_param('pack', array(), PARAM_SAFEDIR);    // pack to install
49 $uninstalllang = optional_param('uninstalllang', '', PARAM_LANG);   // installed pack to uninstall
50 $confirm       = optional_param('confirm', 0, PARAM_BOOL);          // uninstallation confirmation
52 define('INSTALLATION_OF_SELECTED_LANG', 2);
53 define('DELETION_OF_SELECTED_LANG', 4);
54 define('UPDATE_ALL_LANG', 5);
56 //reset and diagnose lang cache permissions
57 remove_dir($CFG->dataroot.'/cache/languages');
58 if (file_exists($CFG->dataroot.'/cache/languages')) {
59     print_error('cannotdeletelangcache', 'error');
60 }
61 get_string_manager()->reset_caches();
63 $notice_ok    = array();
64 $notice_error = array();
66 if (($mode == INSTALLATION_OF_SELECTED_LANG) and confirm_sesskey() and !empty($pack)) {
67     set_time_limit(0);
68     check_dir_exists($CFG->dataroot.'/temp/');
69     check_dir_exists($CFG->dataroot.'/lang/');
71     if (is_array($pack)) {
72         $packs = $pack;
73     } else {
74         $packs = array($pack);
75     }
77     foreach ($packs as $pack) {
78         if ($cd = new component_installer('http://download.moodle.org', 'langpack/'.$thisversion, $pack.'.zip', 'languages.md5', 'lang')) {
79             $status = $cd->install();
80             switch ($status) {
81             case COMPONENT_ERROR:
82                 if ($cd->get_error() == 'remotedownloaderror') {
83                     $a = new object();
84                     $a->url = 'http://download.moodle.org/langpack/'.$thisversion.'/'.$pack.'.zip';
85                     $a->dest = $CFG->dataroot.'/lang';
86                     print_error($cd->get_error(), 'error', 'langimport.php', $a);
87                 } else {
88                     print_error($cd->get_error(), 'error', 'langimport.php');
89                 }
90                 break;
91             case COMPONENT_INSTALLED:
92                 $notice_ok[] = get_string('langpackinstalled','admin',$pack);
93                 if ($parentlang = get_parent_language($pack)) {
94                     // install also parent pack if specified
95                     if ($cd = new component_installer('http://download.moodle.org', 'langpack/'.$thisversion,
96                             $parentlang.'.zip', 'languages.md5', 'lang')) {
97                         $cd->install();
98                     }
99                 }
100                 break;
101             case COMPONENT_UPTODATE:
102                 break;
103             }
104         } else {
105             echo $OUTPUT->notification('Had an unspecified error with the component installer, sorry.');
106         }
107     }
110 if ($mode == DELETION_OF_SELECTED_LANG and !empty($uninstalllang)) {
111     if ($uninstalllang == 'en') {
112         $notice_error[] = 'English language pack can not be uninstalled';
114     } else if (!$confirm and confirm_sesskey()) {
115         echo $OUTPUT->header();
116         echo $OUTPUT->confirm(get_string('uninstallconfirm', 'admin', $uninstalllang),
117                      'langimport.php?mode='.DELETION_OF_SELECTED_LANG.'&uninstalllang='.$uninstalllang.'&confirm=1',
118                      'langimport.php');
119         echo $OUTPUT->footer();
120         die;
122     } else if (confirm_sesskey()) {
123         $dest1 = $CFG->dataroot.'/lang/'.$uninstalllang;
124         $dest2 = $CFG->dirroot.'/lang/'.$uninstalllang;
125         $rm1 = false;
126         $rm2 = false;
127         if (file_exists($dest1)){
128             $rm1 = remove_dir($dest1);
129         }
130         if (file_exists($dest2)){
131             $rm2 = remove_dir($dest2);
132         }
133         if ($rm1 or $rm2) {
134             $notice_ok[] = get_string('langpackremoved','admin');
135         } else {    //nothing deleted, possibly due to permission error
136             $notice_error[] = 'An error has occurred, language pack is not completely uninstalled, please check file permissions';
137         }
138     }
141 if ($mode == UPDATE_ALL_LANG) {
142     set_time_limit(0);
144     if (!$availablelangs = get_remote_list_of_languages()) {
145         print_error('cannotdownloadlanguageupdatelist', 'error');
146     }
147     $md5array = array();    // (string)langcode => (string)md5
148     foreach ($availablelangs as $alang) {
149         $md5array[$alang[0]] = $alang[1];
150     }
152     // filter out unofficial packs
153     $currentlangs = array_keys(get_string_manager()->get_list_of_translations(true));
154     $updateablelangs = array();
155     foreach ($currentlangs as $clang) {
156         if (!array_key_exists($clang, $md5array)) {
157             $notice_ok[] = get_string('langpackupdateskipped', 'admin', $clang);
158             continue;
159         }
160         $dest1 = $CFG->dataroot.'/lang/'.$clang;
161         $dest2 = $CFG->dirroot.'/lang/'.$clang;
163         if (file_exists($dest1.'/langconfig.php') || file_exists($dest2.'/langconfig.php')){
164             $updateablelangs[] = $clang;
165         }
166     }
168     // then filter out packs that have the same md5 key
169     $neededlangs = array();   // all the packs that needs updating
170     foreach ($updateablelangs as $ulang) {
171         if (!is_installed_lang($ulang, $md5array[$ulang])) {
172             $neededlangs[] = $ulang;
173         }
174     }
176     check_dir_exists($CFG->dataroot.'/temp/');
177     check_dir_exists($CFG->dataroot.'/lang/');
179     $updated = false;       // any packs updated?
180     foreach ($neededlangs as $pack) {
181         if ($pack == 'en') {
182             continue;
183         }
185         // delete old directories
186         $dest1 = $CFG->dataroot.'/lang/'.$pack;
187         $dest2 = $CFG->dirroot.'/lang/'.$pack;
188         $rm1 = false;
189         $rm2 = false;
190         if (file_exists($dest1)) {
191             if (!remove_dir($dest1)) {
192                 $notice_error[] = 'Could not delete old directory '.$dest1.', update of '.$pack.' failed, please check permissions.';
193                 continue;
194             }
195         }
196         if (file_exists($dest2)) {
197             if (!remove_dir($dest2)) {
198                 $notice_error[] = 'Could not delete old directory '.$dest2.', update of '.$pack.' failed, please check permissions.';
199                 continue;
200             }
201         }
203         // copy and unzip new version
204         if ($cd = new component_installer('http://download.moodle.org', 'langpack/'.$thisversion, $pack.'.zip', 'languages.md5', 'lang')) {
205         $status = $cd->install();
206         switch ($status) {
208             case COMPONENT_ERROR:
209                 if ($cd->get_error() == 'remotedownloaderror') {
210                     $a = new stdClass();
211                     $a->url = 'http://download.moodle.org/langpack/'.$thisversion.'/'.$pack.'.zip';
212                     $a->dest = $CFG->dataroot.'/lang';
213                     print_error($cd->get_error(), 'error', 'langimport.php', $a);
214                 } else {
215                     print_error($cd->get_error(), 'error', 'langimport.php');
216                 }
217                 break;
219             case COMPONENT_UPTODATE:
220                 // should not get here
221                 break;
223             case COMPONENT_INSTALLED:
224                 $notice_ok[] = get_string('langpackupdated', 'admin', $pack);
225                 $updated = true;
226                 break;
227             }
228         }
229     }
231     if ($updated) {
232         $notice_ok[] = get_string('langupdatecomplete','admin');
233     } else {
234         $notice_ok[] = get_string('nolangupdateneeded','admin');
235     }
237 get_string_manager()->reset_caches();
239 echo $OUTPUT->header();
240 echo $OUTPUT->heading(get_string('langimport', 'admin'));
242 $installedlangs = get_string_manager()->get_list_of_translations(true);
244 $missingparents = array();
245 foreach ($installedlangs as $installedlang => $unused) {
246     $parent = get_parent_language($installedlang);
247     if (empty($parent) or ($parent === 'en')) {
248         continue;
249     }
250     if (!isset($installedlangs[$parent])) {
251         $missingparents[$installedlang] = $parent;
252     }
255 if ($availablelangs = get_remote_list_of_languages()) {
256     $remote = true;
257 } else {
258     $remote = false;
259     $availablelangs = array();
260     echo $OUTPUT->box_start();
261     print_string('remotelangnotavailable', 'admin', $CFG->dataroot.'/lang/');
262     echo $OUTPUT->box_end();
265 if ($notice_ok) {
266     $info = implode('<br />', $notice_ok);
267     echo $OUTPUT->notification($info, 'notifysuccess');
270 if ($notice_error) {
271     $info = implode('<br />', $notice_error);
272     echo $OUTPUT->notification($info, 'notifyproblem');
275 if ($missingparents) {
276     foreach ($missingparents as $l=>$parent) {
277         $a = new object();
278         $a->lang   = $installedlangs[$l];
279         $a->parent = $parent;
280         foreach ($availablelangs as $alang) {
281             if ($alang[0] == $parent) {
282                 $shortlang = $alang[0];
283                 $a->parent = $alang[2].' ('.$shortlang.')';
284             }
285         }
286         $info = get_string('missinglangparent', 'admin', $a);
287         echo $OUTPUT->notification($info, 'notifyproblem');
288     }
291 echo $OUTPUT->box_start();
293 echo html_writer::start_tag('table');
294 echo html_writer::start_tag('tr');
296 // list of installed languages
297 $url = new moodle_url('/admin/langimport.php', array('mode' => DELETION_OF_SELECTED_LANG));
298 echo html_writer::start_tag('td', array('valign' => 'top'));
299 echo html_writer::start_tag('form', array('id' => 'uninstallform', 'action' => $url->out(), 'method' => 'post'));
300 echo html_writer::start_tag('fieldset');
301 echo html_writer::label(get_string('installedlangs','admin'), 'uninstalllang');
302 echo html_writer::empty_tag('br');
303 echo html_writer::select($installedlangs, 'uninstalllang', '', false, array('size' => 15));
304 echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
305 echo html_writer::empty_tag('br');
306 echo html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('uninstall','admin')));
307 echo html_writer::end_tag('fieldset');
308 echo html_writer::end_tag('form');
309 if ($remote) {
310     $url = new moodle_url('/admin/langimport.php', array('mode' => UPDATE_ALL_LANG));
311     echo html_writer::start_tag('form', array('id' => 'updateform', 'action' => $url->out(), 'method' => 'post'));
312     echo html_writer::tag('fieldset', html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('updatelangs','admin'))));
313     echo html_writer::end_tag('form');
315 echo html_writer::end_tag('td');
317 // list of available languages
318 $options = array();
319 foreach ($availablelangs as $alang) {
320     if (!empty($alang[0]) and trim($alang[0]) !== 'en' and !is_installed_lang($alang[0], $alang[1])) {
321         $options[$alang[0]] = $alang[2].' ('.$alang[0].')';
322     }
324 if (!empty($options)) {
325     echo html_writer::start_tag('td', array('valign' => 'top'));
326     $url = new moodle_url('/admin/langimport.php', array('mode' => INSTALLATION_OF_SELECTED_LANG));
327     echo html_writer::start_tag('form', array('id' => 'installform', 'action' => $url->out(), 'method' => 'post'));
328     echo html_writer::start_tag('fieldset');
329     echo html_writer::label(get_string('availablelangs','install'), 'pack');
330     echo html_writer::empty_tag('br');
331     echo html_writer::select($options, 'pack[]', '', false, array('size' => 15, 'multiple' => 'multiple'));
332     echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
333     echo html_writer::empty_tag('br');
334     echo html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('install','admin')));
335     echo html_writer::end_tag('fieldset');
336     echo html_writer::end_tag('form');
337     echo html_writer::end_tag('td');
340 echo html_writer::end_tag('tr');
341 echo html_writer::end_tag('table');
342 echo $OUTPUT->box_end();
343 echo $OUTPUT->footer();
344 die();
346 ////////////////////////////////////////////////////////////////////////////////
347 // Local functions /////////////////////////////////////////////////////////////
348 ////////////////////////////////////////////////////////////////////////////////
350 /**
351  * checks the md5 of the zip file, grabbed from download.moodle.org,
352  * against the md5 of the local language file from last update
353  * @param string $lang
354  * @param string $md5check
355  * @return bool
356  */
357 function is_installed_lang($lang, $md5check) {
358     global $CFG;
359     $md5file = $CFG->dataroot.'/lang/'.$lang.'/'.$lang.'.md5';
360     if (file_exists($md5file)){
361         return (file_get_contents($md5file) == $md5check);
362     }
363     return false;
366 /**
367  * Returns the list of available language packs from download.moodle.org
368  *
369  * @return array|bool false if can not download
370  */
371 function get_remote_list_of_languages() {
372     $source = 'http://download.moodle.org/langpack/2.0/languages.md5';
373     $availablelangs = array();
375     if ($content = download_file_content($source)) {
376         $alllines = explode("\n", $content);
377         foreach($alllines as $line) {
378             if (!empty($line)){
379                 $availablelangs[] = explode(',', $line);
380             }
381         }
382         return $availablelangs;
384     } else {
385         return false;
386     }