Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Lang import controller
  19   *
  20   * @package    tool_langimport
  21   * @copyright  2014 Dan Poltawski <dan@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace tool_langimport;
  26  
  27  use moodle_url;
  28  
  29  defined('MOODLE_INTERNAL') || die();
  30  require_once($CFG->libdir.'/filelib.php');
  31  require_once($CFG->libdir.'/componentlib.class.php');
  32  
  33  /**
  34   * Lang import controller
  35   *
  36   * @package    tool_langimport
  37   * @copyright  2014 Dan Poltawski <dan@moodle.com>
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class controller {
  41      /** @var array list of informational messages */
  42      public $info;
  43      /** @var array  list of error messages */
  44      public $errors;
  45      /** @var \lang_installer */
  46      private $installer;
  47      /** @var array languages available on the remote server */
  48      public $availablelangs;
  49  
  50      /**
  51       * Constructor.
  52       */
  53      public function __construct() {
  54          make_temp_directory('');
  55          make_upload_directory('lang');
  56  
  57          $this->info = array();
  58          $this->errors = array();
  59          $this->installer = new \lang_installer();
  60  
  61          $this->availablelangs = $this->installer->get_remote_list_of_languages();
  62      }
  63  
  64      /**
  65       * Redirect to the specified url, outputting any required messages.
  66       *
  67       * @param moodle_url $url
  68       */
  69      public function redirect(moodle_url $url): void {
  70          if ($this->info) {
  71              $info = implode('<br />', $this->info);
  72              \core\notification::success($info);
  73          }
  74  
  75          if ($this->errors) {
  76              $info = implode('<br />', $this->errors);
  77              \core\notification::error($info);
  78          }
  79  
  80          redirect($url);
  81      }
  82  
  83      /**
  84       * Install language packs provided
  85       *
  86       * @param string|array $langs array of langcodes or individual langcodes
  87       * @param bool $updating true if updating the langpacks
  88       * @return int false if an error encountered or
  89       * @throws \moodle_exception when error is encountered installing langpack
  90       */
  91      public function install_languagepacks($langs, $updating = false) {
  92          global $CFG;
  93  
  94          $this->installer->set_queue($langs);
  95          $results = $this->installer->run();
  96  
  97          $updatedpacks = 0;
  98  
  99          foreach ($results as $langcode => $langstatus) {
 100              switch ($langstatus) {
 101                  case \lang_installer::RESULT_DOWNLOADERROR:
 102                      $a       = new \stdClass();
 103                      $a->url  = $this->installer->lang_pack_url($langcode);
 104                      $a->dest = $CFG->dataroot.'/lang';
 105                      $this->errors[] = get_string('remotedownloaderror', 'error', $a);
 106                      throw new \moodle_exception('remotedownloaderror', 'error', '', $a);
 107                      break;
 108                  case \lang_installer::RESULT_INSTALLED:
 109                      $updatedpacks++;
 110                      if ($updating) {
 111                          event\langpack_updated::event_with_langcode($langcode)->trigger();
 112                          $this->info[] = get_string('langpackupdated', 'tool_langimport', $langcode);
 113                      } else {
 114                          $this->info[] = get_string('langpackinstalled', 'tool_langimport', $langcode);
 115                          event\langpack_imported::event_with_langcode($langcode)->trigger();
 116                      }
 117                      break;
 118                  case \lang_installer::RESULT_UPTODATE:
 119                      $this->info[] = get_string('langpackuptodate', 'tool_langimport', $langcode);
 120                      break;
 121              }
 122          }
 123  
 124          return $updatedpacks;
 125      }
 126  
 127      /**
 128       * Uninstall language pack
 129       *
 130       * @param string $lang language code
 131       * @return bool true if language succesfull installed
 132       */
 133      public function uninstall_language($lang) {
 134          global $CFG;
 135  
 136          $lang = clean_param($lang, PARAM_LANG);
 137          if ($lang === '') {
 138              // Do not allow uninstallation of invalid languages.
 139              // Note: PARAM_LANG returns an empty string for invalid validation.
 140              return false;
 141          }
 142  
 143          if ($lang === 'en') {
 144              // Never allow removal of the default langauge.
 145              return false;
 146          }
 147  
 148          $dest1 = $CFG->dataroot.'/lang/'.$lang;
 149          $dest2 = $CFG->dirroot.'/lang/'.$lang;
 150          $rm1 = false;
 151          $rm2 = false;
 152          if (file_exists($dest1)) {
 153              $rm1 = remove_dir($dest1);
 154          }
 155          if (file_exists($dest2)) {
 156              $rm2 = remove_dir($dest2);
 157          }
 158  
 159          if ($rm1 or $rm2) {
 160              // Set the default site language to en if the deleted language pack is the default site language.
 161              if ($CFG->lang === $lang) {
 162                  set_config('lang', 'en');
 163                  // Fix the user's current language to the default site language.
 164                  fix_current_language($CFG->lang);
 165              }
 166              $this->info[] = get_string('langpackremoved', 'tool_langimport', $lang);
 167              event\langpack_removed::event_with_langcode($lang)->trigger();
 168              return true;
 169          } else {    // Nothing deleted, possibly due to permission error.
 170              $this->errors[] = get_string('langpacknotremoved', 'tool_langimport', $lang);
 171              return false;
 172          }
 173      }
 174  
 175      /**
 176       * Updated all install language packs with the latest found on servre
 177       *
 178       * @return bool true if languages succesfully updated.
 179       */
 180      public function update_all_installed_languages() {
 181          global $CFG;
 182  
 183          if (!$availablelangs = $this->installer->get_remote_list_of_languages()) {
 184              $this->errors[] = get_string('cannotdownloadlanguageupdatelist', 'error');
 185              return false;
 186          }
 187  
 188          $md5array = array();    // Convert to (string)langcode => (string)md5.
 189          foreach ($availablelangs as $alang) {
 190              $md5array[$alang[0]] = $alang[1];
 191          }
 192  
 193          // Filter out unofficial packs.
 194          $currentlangs = array_keys(get_string_manager()->get_list_of_translations(true));
 195          $updateablelangs = array();
 196          foreach ($currentlangs as $clang) {
 197              if (!array_key_exists($clang, $md5array)) {
 198                  $this->info[] = get_string('langpackupdateskipped', 'tool_langimport', $clang);
 199                  continue;
 200              }
 201              $dest1 = $CFG->dataroot.'/lang/'.$clang;
 202              $dest2 = $CFG->dirroot.'/lang/'.$clang;
 203  
 204              if (file_exists($dest1.'/langconfig.php') || file_exists($dest2.'/langconfig.php')) {
 205                  $updateablelangs[] = $clang;
 206              }
 207          }
 208  
 209          // Filter out packs that have the same md5 key.
 210          $neededlangs = array();
 211          foreach ($updateablelangs as $ulang) {
 212              if (!$this->is_installed_lang($ulang, $md5array[$ulang])) {
 213                  $neededlangs[] = $ulang;
 214              }
 215          }
 216  
 217          try {
 218              $updated = $this->install_languagepacks($neededlangs, true);
 219          } catch (\moodle_exception $e) {
 220              $this->errors[] = 'An exception occurred while installing language packs: ' . $e->getMessage();
 221              return false;
 222          }
 223  
 224          if ($updated) {
 225              $this->info[] = get_string('langupdatecomplete', 'tool_langimport');
 226              // The strings have been changed so we need to purge their cache to ensure users see the changes.
 227              get_string_manager()->reset_caches();
 228          } else {
 229              $this->info[] = get_string('nolangupdateneeded', 'tool_langimport');
 230          }
 231  
 232          return true;
 233      }
 234  
 235      /**
 236       * checks the md5 of the zip file, grabbed from download.moodle.org,
 237       * against the md5 of the local language file from last update
 238       * @param string $lang language code
 239       * @param string $md5check md5 to check
 240       * @return bool true if installed
 241       */
 242      public function is_installed_lang($lang, $md5check) {
 243          global $CFG;
 244          $md5file = $CFG->dataroot.'/lang/'.$lang.'/'.$lang.'.md5';
 245          if (file_exists($md5file)) {
 246              return (file_get_contents($md5file) == $md5check);
 247          }
 248          return false;
 249      }
 250  
 251      /**
 252       * Returns the URL where a given language pack can be downloaded
 253       *
 254       * Alternatively, if the parameter is empty, returns URL of the page with the
 255       * list of all available language packs.
 256       *
 257       * @param string $langcode language code like 'cs' or empty for unknown
 258       * @return string URL
 259       */
 260      public function lang_pack_url($langcode = '') {
 261          return $this->installer->lang_pack_url($langcode);
 262      }
 263  
 264      /**
 265       * Schedule installation of the given language packs asynchronously via ad hoc task.
 266       *
 267       * @param string|array $langs array of langcodes or individual langcodes
 268       */
 269      public function schedule_languagepacks_installation($langs): void {
 270          global $USER;
 271  
 272          $task = new \tool_langimport\task\install_langpacks();
 273          $task->set_userid($USER->id);
 274          $task->set_custom_data([
 275              'langs' => $langs,
 276          ]);
 277  
 278          \core\task\manager::queue_adhoc_task($task, true);
 279  
 280          $this->info[] = get_string('installscheduled', 'tool_langimport');
 281      }
 282  }