Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]
1 <?php 2 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/>. 17 18 /** 19 * This library includes all the necessary stuff to use the one-click 20 * download and install feature of Moodle, used to keep updated some 21 * items like languages, pear, enviroment... i.e, components. 22 * 23 * It has been developed harcoding some important limits that are 24 * explained below: 25 * - It only can check, download and install items under moodledata. 26 * - Every downloadeable item must be one zip file. 27 * - The zip file root content must be 1 directory, i.e, everything 28 * is stored under 1 directory. 29 * - Zip file name and root directory must have the same name (but 30 * the .zip extension, of course). 31 * - Every .zip file must be defined in one .md5 file that will be 32 * stored in the same remote directory than the .zip file. 33 * - The name of such .md5 file is free, although it's recommended 34 * to use the same name than the .zip (that's the default 35 * assumption if no specified). 36 * - Every remote .md5 file will be a comma separated (CVS) file where each 37 * line will follow this format: 38 * - Field 1: name of the zip file (without extension). Mandatory. 39 * - Field 2: md5 of the zip file. Mandatory. 40 * - Field 3: whatever you want (or need). Optional. 41 * -Every local .md5 file will: 42 * - Have the zip file name (without the extension) plus -md5 43 * - Will reside inside the expanded zip file dir 44 * - Will contain the md5 od the latest installed component 45 * With all these details present, the process will perform this tasks: 46 * - Perform security checks. Only admins are allowed to use this for now. 47 * - Read the .md5 file from source (1). 48 * - Extract the correct line for the .zip being requested. 49 * - Compare it with the local .md5 file (2). 50 * - If different: 51 * - Download the newer .zip file from source. 52 * - Calculate its md5 (3). 53 * - Compare (1) and (3). 54 * - If equal: 55 * - Delete old directory. 56 * - Uunzip the newer .zip file. 57 * - Create the new local .md5 file. 58 * - Delete the .zip file. 59 * - If different: 60 * - ERROR. Old package won't be modified. We shouldn't 61 * reach here ever. 62 * - If component download is not possible, a message text about how to do 63 * the process manually (remotedownloaderror) must be displayed to explain it. 64 * 65 * General Usage: 66 * 67 * To install one component: 68 * <code> 69 * require_once($CFG->libdir.'/componentlib.class.php'); 70 * if ($cd = new component_installer('https://download.moodle.org', 'langpack/2.0', 71 * 'es.zip', 'languages.md5', 'lang')) { 72 * $status = $cd->install(); //returns COMPONENT_(ERROR | UPTODATE | INSTALLED) 73 * switch ($status) { 74 * case COMPONENT_ERROR: 75 * if ($cd->get_error() == 'remotedownloaderror') { 76 * $a = new stdClass(); 77 * $a->url = 'https://download.moodle.org/langpack/2.0/es.zip'; 78 * $a->dest= $CFG->dataroot.'/lang'; 79 * print_error($cd->get_error(), 'error', '', $a); 80 * } else { 81 * print_error($cd->get_error(), 'error'); 82 * } 83 * break; 84 * case COMPONENT_UPTODATE: 85 * //Print error string or whatever you want to do 86 * break; 87 * case COMPONENT_INSTALLED: 88 * //Print/do whatever you want 89 * break; 90 * default: 91 * //We shouldn't reach this point 92 * } 93 * } else { 94 * //We shouldn't reach this point 95 * } 96 * </code> 97 * 98 * To switch of component (maintaining the rest of settings): 99 * <code> 100 * $status = $cd->change_zip_file('en.zip'); //returns boolean false on error 101 * </code> 102 * 103 * To retrieve all the components in one remote md5 file 104 * <code> 105 * $components = $cd->get_all_components_md5(); //returns boolean false on error, array instead 106 * </code> 107 * 108 * To check if current component needs to be updated 109 * <code> 110 * $status = $cd->need_upgrade(); //returns COMPONENT_(ERROR | UPTODATE | NEEDUPDATE) 111 * </code> 112 * 113 * To get the 3rd field of the md5 file (optional) 114 * <code> 115 * $field = $cd->get_extra_md5_field(); //returns string (empty if not exists) 116 * </code> 117 * 118 * For all the error situations the $cd->get_error() method should return always the key of the 119 * error to be retrieved by one standard get_string() call against the error.php lang file. 120 * 121 * That's all! 122 * 123 * @package core 124 * @copyright (C) 2001-3001 Eloy Lafuente (stronk7) {@link http://contiento.com} 125 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 126 */ 127 128 defined('MOODLE_INTERNAL') || die(); 129 130 /** 131 * @global object $CFG 132 * @name $CFG 133 */ 134 global $CFG; 135 require_once($CFG->libdir.'/filelib.php'); 136 137 // Some needed constants 138 define('COMPONENT_ERROR', 0); 139 define('COMPONENT_UPTODATE', 1); 140 define('COMPONENT_NEEDUPDATE', 2); 141 define('COMPONENT_INSTALLED', 3); 142 143 /** 144 * This class is used to check, download and install items from 145 * download.moodle.org to the moodledata directory. 146 * 147 * It always return true/false in all their public methods to say if 148 * execution has ended succesfuly or not. If there is any problem 149 * its getError() method can be called, returning one error string 150 * to be used with the standard get/print_string() functions. 151 * 152 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 153 * @package moodlecore 154 */ 155 class component_installer { 156 /** 157 * @var string 158 */ 159 var $sourcebase; /// Full http URL, base for downloadable items 160 var $zippath; /// Relative path (from sourcebase) where the 161 /// downloadeable item resides. 162 var $zipfilename; /// Name of the .zip file to be downloaded 163 var $md5filename; /// Name of the .md5 file to be read 164 var $componentname;/// Name of the component. Must be the zip name without 165 /// the extension. And it defines a lot of things: 166 /// the md5 line to search for, the default m5 file name 167 /// and the name of the root dir stored inside the zip file 168 var $destpath; /// Relative path (from moodledata) where the .zip 169 /// file will be expanded. 170 var $errorstring; /// Latest error produced. It will contain one lang string key. 171 var $extramd5info; /// Contents of the optional third field in the .md5 file. 172 var $requisitesok; /// Flag to see if requisites check has been passed ok. 173 /** 174 * @var array 175 */ 176 var $cachedmd5components; /// Array of cached components to avoid to 177 /// download the same md5 file more than once per request. 178 179 /** 180 * Standard constructor of the class. It will initialize all attributes. 181 * without performing any check at all. 182 * 183 * @param string $sourcebase Full http URL, base for downloadeable items 184 * @param string $zippath Relative path (from sourcebase) where the 185 * downloadeable item resides 186 * @param string $zipfilename Name of the .zip file to be downloaded 187 * @param string $md5filename Name of the .md5 file to be read (default '' = same 188 * than zipfilename) 189 * @param string $destpath Relative path (from moodledata) where the .zip file will 190 * be expanded (default='' = moodledataitself) 191 * @return object 192 */ 193 public function __construct($sourcebase, $zippath, $zipfilename, $md5filename='', $destpath='') { 194 195 $this->sourcebase = $sourcebase; 196 $this->zippath = $zippath; 197 $this->zipfilename = $zipfilename; 198 $this->md5filename = $md5filename; 199 $this->componentname= ''; 200 $this->destpath = $destpath; 201 $this->errorstring = ''; 202 $this->extramd5info = ''; 203 $this->requisitesok = false; 204 $this->cachedmd5components = array(); 205 206 $this->check_requisites(); 207 } 208 209 /** 210 * Old syntax of class constructor. Deprecated in PHP7. 211 * 212 * @deprecated since Moodle 3.1 213 */ 214 public function component_installer($sourcebase, $zippath, $zipfilename, $md5filename='', $destpath='') { 215 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 216 self::__construct($sourcebase, $zippath, $zipfilename, $md5filename, $destpath); 217 } 218 219 /** 220 * This function will check if everything is properly set to begin 221 * one installation. Also, it will check for required settings 222 * and will fill everything as needed. 223 * 224 * @global object 225 * @return boolean true/false (plus detailed error in errorstring) 226 */ 227 function check_requisites() { 228 global $CFG; 229 230 $this->requisitesok = false; 231 232 /// Check that everything we need is present 233 if (empty($this->sourcebase) || empty($this->zipfilename)) { 234 $this->errorstring='missingrequiredfield'; 235 return false; 236 } 237 /// Check for correct sourcebase (this will be out in the future) 238 if (!PHPUNIT_TEST and $this->sourcebase != 'https://download.moodle.org') { 239 $this->errorstring='wrongsourcebase'; 240 return false; 241 } 242 /// Check the zip file is a correct one (by extension) 243 if (stripos($this->zipfilename, '.zip') === false) { 244 $this->errorstring='wrongzipfilename'; 245 return false; 246 } 247 /// Check that exists under dataroot 248 if (!empty($this->destpath)) { 249 if (!file_exists($CFG->dataroot.'/'.$this->destpath)) { 250 $this->errorstring='wrongdestpath'; 251 return false; 252 } 253 } 254 /// Calculate the componentname 255 $pos = stripos($this->zipfilename, '.zip'); 256 $this->componentname = substr($this->zipfilename, 0, $pos); 257 /// Calculate md5filename if it's empty 258 if (empty($this->md5filename)) { 259 $this->md5filename = $this->componentname.'.md5'; 260 } 261 /// Set the requisites passed flag 262 $this->requisitesok = true; 263 return true; 264 } 265 266 /** 267 * This function will perform the full installation if needed, i.e. 268 * compare md5 values, download, unzip, install and regenerate 269 * local md5 file 270 * 271 * @uses COMPONENT_ERROR 272 * @uses COMPONENT_UPTODATE 273 * @uses COMPONENT_ERROR 274 * @uses COMPONENT_INSTALLED 275 * @return int COMPONENT_(ERROR | UPTODATE | INSTALLED) 276 */ 277 public function install() { 278 global $CFG; 279 280 /// Check requisites are passed 281 if (!$this->requisitesok) { 282 return COMPONENT_ERROR; 283 } 284 /// Confirm we need upgrade 285 if ($this->need_upgrade() === COMPONENT_ERROR) { 286 return COMPONENT_ERROR; 287 } else if ($this->need_upgrade() === COMPONENT_UPTODATE) { 288 $this->errorstring='componentisuptodate'; 289 return COMPONENT_UPTODATE; 290 } 291 /// Create temp directory if necesary 292 if (!make_temp_directory('', false)) { 293 $this->errorstring='cannotcreatetempdir'; 294 return COMPONENT_ERROR; 295 } 296 /// Download zip file and save it to temp 297 if ($this->zippath) { 298 $source = $this->sourcebase.'/'.$this->zippath.'/'.$this->zipfilename; 299 } else { 300 $source = $this->sourcebase.'/'.$this->zipfilename; 301 } 302 303 $zipfile= $CFG->tempdir.'/'.$this->zipfilename; 304 305 if($contents = download_file_content($source)) { 306 if ($file = fopen($zipfile, 'w')) { 307 if (!fwrite($file, $contents)) { 308 fclose($file); 309 $this->errorstring='cannotsavezipfile'; 310 return COMPONENT_ERROR; 311 } 312 } else { 313 $this->errorstring='cannotsavezipfile'; 314 return COMPONENT_ERROR; 315 } 316 fclose($file); 317 } else { 318 $this->errorstring='cannotdownloadzipfile'; 319 return COMPONENT_ERROR; 320 } 321 /// Calculate its md5 322 $new_md5 = md5($contents); 323 /// Compare it with the remote md5 to check if we have the correct zip file 324 if (!$remote_md5 = $this->get_component_md5()) { 325 return COMPONENT_ERROR; 326 } 327 if ($new_md5 != $remote_md5) { 328 $this->errorstring='downloadedfilecheckfailed'; 329 return COMPONENT_ERROR; 330 } 331 332 // Move current revision to a safe place. 333 $destinationdir = $CFG->dataroot . '/' . $this->destpath; 334 $destinationcomponent = $destinationdir . '/' . $this->componentname; 335 $destinationcomponentold = $destinationcomponent . '_old'; 336 @remove_dir($destinationcomponentold); // Deleting a possible old version. 337 338 // Moving to a safe place. 339 @rename($destinationcomponent, $destinationcomponentold); 340 341 // Unzip new version. 342 $packer = get_file_packer('application/zip'); 343 $unzipsuccess = $packer->extract_to_pathname($zipfile, $destinationdir, null, null, true); 344 if (!$unzipsuccess) { 345 @remove_dir($destinationcomponent); 346 @rename($destinationcomponentold, $destinationcomponent); 347 $this->errorstring = 'cannotunzipfile'; 348 return COMPONENT_ERROR; 349 } 350 351 // Delete old component version. 352 @remove_dir($destinationcomponentold); 353 354 // Create local md5. 355 if ($file = fopen($destinationcomponent.'/'.$this->componentname.'.md5', 'w')) { 356 if (!fwrite($file, $new_md5)) { 357 fclose($file); 358 $this->errorstring='cannotsavemd5file'; 359 return COMPONENT_ERROR; 360 } 361 } else { 362 $this->errorstring='cannotsavemd5file'; 363 return COMPONENT_ERROR; 364 } 365 fclose($file); 366 /// Delete temp zip file 367 @unlink($zipfile); 368 369 return COMPONENT_INSTALLED; 370 } 371 372 /** 373 * This function will detect if remote component needs to be installed 374 * because it's different from the local one 375 * 376 * @uses COMPONENT_ERROR 377 * @uses COMPONENT_UPTODATE 378 * @uses COMPONENT_NEEDUPDATE 379 * @return int COMPONENT_(ERROR | UPTODATE | NEEDUPDATE) 380 */ 381 function need_upgrade() { 382 383 /// Check requisites are passed 384 if (!$this->requisitesok) { 385 return COMPONENT_ERROR; 386 } 387 /// Get local md5 388 $local_md5 = $this->get_local_md5(); 389 /// Get remote md5 390 if (!$remote_md5 = $this->get_component_md5()) { 391 return COMPONENT_ERROR; 392 } 393 /// Return result 394 if ($local_md5 == $remote_md5) { 395 return COMPONENT_UPTODATE; 396 } else { 397 return COMPONENT_NEEDUPDATE; 398 } 399 } 400 401 /** 402 * This function will change the zip file to install on the fly 403 * to allow the class to process different components of the 404 * same md5 file without intantiating more objects. 405 * 406 * @param string $newzipfilename New zip filename to process 407 * @return boolean true/false 408 */ 409 function change_zip_file($newzipfilename) { 410 411 $this->zipfilename = $newzipfilename; 412 return $this->check_requisites(); 413 } 414 415 /** 416 * This function will get the local md5 value of the installed 417 * component. 418 * 419 * @global object 420 * @return bool|string md5 of the local component (false on error) 421 */ 422 function get_local_md5() { 423 global $CFG; 424 425 /// Check requisites are passed 426 if (!$this->requisitesok) { 427 return false; 428 } 429 430 $return_value = 'needtobeinstalled'; /// Fake value to force new installation 431 432 /// Calculate source to read 433 $source = $CFG->dataroot.'/'.$this->destpath.'/'.$this->componentname.'/'.$this->componentname.'.md5'; 434 /// Read md5 value stored (if exists) 435 if (file_exists($source)) { 436 if ($temp = file_get_contents($source)) { 437 $return_value = $temp; 438 } 439 } 440 return $return_value; 441 } 442 443 /** 444 * This function will download the specified md5 file, looking for the 445 * current componentname, returning its md5 field and storing extramd5info 446 * if present. Also it caches results to cachedmd5components for better 447 * performance in the same request. 448 * 449 * @return mixed md5 present in server (or false if error) 450 */ 451 function get_component_md5() { 452 453 /// Check requisites are passed 454 if (!$this->requisitesok) { 455 return false; 456 } 457 /// Get all components of md5 file 458 if (!$comp_arr = $this->get_all_components_md5()) { 459 if (empty($this->errorstring)) { 460 $this->errorstring='cannotdownloadcomponents'; 461 } 462 return false; 463 } 464 /// Search for the componentname component 465 if (empty($comp_arr[$this->componentname]) || !$component = $comp_arr[$this->componentname]) { 466 $this->errorstring='cannotfindcomponent'; 467 return false; 468 } 469 /// Check we have a valid md5 470 if (empty($component[1]) || strlen($component[1]) != 32) { 471 $this->errorstring='invalidmd5'; 472 return false; 473 } 474 /// Set the extramd5info field 475 if (!empty($component[2])) { 476 $this->extramd5info = $component[2]; 477 } 478 return $component[1]; 479 } 480 481 /** 482 * This function allows you to retrieve the complete array of components found in 483 * the md5filename 484 * 485 * @return bool|array array of components in md5 file or false if error 486 */ 487 function get_all_components_md5() { 488 489 /// Check requisites are passed 490 if (!$this->requisitesok) { 491 return false; 492 } 493 494 /// Initialize components array 495 $comp_arr = array(); 496 497 /// Define and retrieve the full md5 file 498 if ($this->zippath) { 499 $source = $this->sourcebase.'/'.$this->zippath.'/'.$this->md5filename; 500 } else { 501 $source = $this->sourcebase.'/'.$this->md5filename; 502 } 503 504 /// Check if we have downloaded the md5 file before (per request cache) 505 if (!empty($this->cachedmd5components[$source])) { 506 $comp_arr = $this->cachedmd5components[$source]; 507 } else { 508 /// Not downloaded, let's do it now 509 $availablecomponents = array(); 510 511 if ($contents = download_file_content($source)) { 512 /// Split text into lines 513 $lines=preg_split('/\r?\n/',$contents); 514 /// Each line will be one component 515 foreach($lines as $line) { 516 $availablecomponents[] = explode(',', $line); 517 } 518 /// If no components have been found, return error 519 if (empty($availablecomponents)) { 520 $this->errorstring='cannotdownloadcomponents'; 521 return false; 522 } 523 /// Build an associative array of components for easily search 524 /// applying trim to avoid linefeeds and other... 525 $comp_arr = array(); 526 foreach ($availablecomponents as $component) { 527 /// Avoid sometimes empty lines 528 if (empty($component[0])) { 529 continue; 530 } 531 $component[0]=trim($component[0]); 532 if (!empty($component[1])) { 533 $component[1]=trim($component[1]); 534 } 535 if (!empty($component[2])) { 536 $component[2]=trim($component[2]); 537 } 538 $comp_arr[$component[0]] = $component; 539 } 540 /// Cache components 541 $this->cachedmd5components[$source] = $comp_arr; 542 } else { 543 /// Return error 544 $this->errorstring='remotedownloaderror'; 545 return false; 546 } 547 } 548 /// If there is no commponents or erros found, error 549 if (!empty($this->errorstring)) { 550 return false; 551 552 } else if (empty($comp_arr)) { 553 $this->errorstring='cannotdownloadcomponents'; 554 return false; 555 } 556 return $comp_arr; 557 } 558 559 /** 560 * This function returns the errorstring 561 * 562 * @return string the error string 563 */ 564 function get_error() { 565 return $this->errorstring; 566 } 567 568 /** This function returns the extramd5 field (optional in md5 file) 569 * 570 * @return string the extramd5 field 571 */ 572 function get_extra_md5_field() { 573 return $this->extramd5info; 574 } 575 576 } /// End of component_installer class 577 578 579 /** 580 * Language packs installer 581 * 582 * This class wraps the functionality provided by {@link component_installer} 583 * and adds support for installing a set of language packs. 584 * 585 * Given an array of required language packs, this class fetches them all 586 * and installs them. It detects eventual dependencies and installs 587 * all parent languages, too. 588 * 589 * @copyright 2011 David Mudrak <david@moodle.com> 590 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 591 */ 592 class lang_installer { 593 594 /** lang pack was successfully downloaded and deployed */ 595 const RESULT_INSTALLED = 'installed'; 596 /** lang pack was up-to-date so no download was needed */ 597 const RESULT_UPTODATE = 'uptodate'; 598 /** there was a problem with downloading the lang pack */ 599 const RESULT_DOWNLOADERROR = 'downloaderror'; 600 601 /** @var array of languages to install */ 602 protected $queue = array(); 603 /** @var string the code of language being currently installed */ 604 protected $current; 605 /** @var array of languages already installed by this instance */ 606 protected $done = array(); 607 /** @var string this Moodle major version */ 608 protected $version; 609 610 /** 611 * Prepare the installer 612 * 613 * @param string|array $langcode a code of the language to install 614 */ 615 public function __construct($langcode = '') { 616 global $CFG; 617 618 $this->set_queue($langcode); 619 $this->version = moodle_major_version(true); 620 621 if (!empty($CFG->langotherroot) and $CFG->langotherroot !== $CFG->dataroot . '/lang') { 622 debugging('The in-built language pack installer does not support alternative location ' . 623 'of languages root directory. You are supposed to install and update your language '. 624 'packs on your own.'); 625 } 626 } 627 628 /** 629 * Sets the queue of language packs to be installed 630 * 631 * @param string|array $langcodes language code like 'cs' or a list of them 632 */ 633 public function set_queue($langcodes) { 634 if (is_array($langcodes)) { 635 $this->queue = $langcodes; 636 } else if (!empty($langcodes)) { 637 $this->queue = array($langcodes); 638 } 639 } 640 641 /** 642 * Runs the installer 643 * 644 * This method calls {@link self::install_language_pack} for every language in the 645 * queue. If a dependency is detected, the parent language is added to the queue. 646 * 647 * @return array results, array of self::RESULT_xxx constants indexed by language code 648 */ 649 public function run() { 650 651 $results = array(); 652 653 while ($this->current = array_shift($this->queue)) { 654 655 if ($this->was_processed($this->current)) { 656 // do not repeat yourself 657 continue; 658 } 659 660 if ($this->current === 'en') { 661 $this->mark_processed($this->current); 662 continue; 663 } 664 665 $results[$this->current] = $this->install_language_pack($this->current); 666 667 if (in_array($results[$this->current], array(self::RESULT_INSTALLED, self::RESULT_UPTODATE))) { 668 if ($parentlang = $this->get_parent_language($this->current)) { 669 if (!$this->is_queued($parentlang) and !$this->was_processed($parentlang)) { 670 $this->add_to_queue($parentlang); 671 } 672 } 673 } 674 675 $this->mark_processed($this->current); 676 } 677 678 return $results; 679 } 680 681 /** 682 * Returns the URL where a given language pack can be downloaded 683 * 684 * Alternatively, if the parameter is empty, returns URL of the page with the 685 * list of all available language packs. 686 * 687 * @param string $langcode language code like 'cs' or empty for unknown 688 * @return string URL 689 */ 690 public function lang_pack_url($langcode = '') { 691 692 if (empty($langcode)) { 693 return 'https://download.moodle.org/langpack/'.$this->version.'/'; 694 } else { 695 return 'https://download.moodle.org/download.php/langpack/'.$this->version.'/'.$langcode.'.zip'; 696 } 697 } 698 699 /** 700 * Returns the list of available language packs from download.moodle.org 701 * 702 * @return array|bool false if can not download 703 */ 704 public function get_remote_list_of_languages() { 705 $source = 'https://download.moodle.org/langpack/' . $this->version . '/languages.md5'; 706 $availablelangs = array(); 707 708 if ($content = download_file_content($source)) { 709 $alllines = explode("\n", $content); 710 foreach($alllines as $line) { 711 if (!empty($line)){ 712 $availablelangs[] = explode(',', $line); 713 } 714 } 715 return $availablelangs; 716 717 } else { 718 return false; 719 } 720 } 721 722 // Internal implementation ///////////////////////////////////////////////// 723 724 /** 725 * Adds a language pack (or a list of them) to the queue 726 * 727 * @param string|array $langcodes code of the language to install or a list of them 728 */ 729 protected function add_to_queue($langcodes) { 730 if (is_array($langcodes)) { 731 $this->queue = array_merge($this->queue, $langcodes); 732 } else if (!empty($langcodes)) { 733 $this->queue[] = $langcodes; 734 } 735 } 736 737 /** 738 * Checks if the given language is queued or if the queue is empty 739 * 740 * @example $installer->is_queued('es'); // is Spanish going to be installed? 741 * @example $installer->is_queued(); // is there a language queued? 742 * 743 * @param string $langcode language code or empty string for "any" 744 * @return boolean 745 */ 746 protected function is_queued($langcode = '') { 747 748 if (empty($langcode)) { 749 return !empty($this->queue); 750 751 } else { 752 return in_array($langcode, $this->queue); 753 } 754 } 755 756 /** 757 * Checks if the given language has already been processed by this instance 758 * 759 * @see self::mark_processed() 760 * @param string $langcode 761 * @return boolean 762 */ 763 protected function was_processed($langcode) { 764 return isset($this->done[$langcode]); 765 } 766 767 /** 768 * Mark the given language pack as processed 769 * 770 * @see self::was_processed() 771 * @param string $langcode 772 */ 773 protected function mark_processed($langcode) { 774 $this->done[$langcode] = 1; 775 } 776 777 /** 778 * Returns a parent language of the given installed language 779 * 780 * @param string $langcode 781 * @return string parent language's code 782 */ 783 protected function get_parent_language($langcode) { 784 return get_parent_language($langcode); 785 } 786 787 /** 788 * Perform the actual language pack installation 789 * 790 * @uses component_installer 791 * @param string $langcode 792 * @return int return status 793 */ 794 protected function install_language_pack($langcode) { 795 796 // initialise new component installer to process this language 797 $installer = new component_installer('https://download.moodle.org', 'download.php/direct/langpack/' . $this->version, 798 $langcode . '.zip', 'languages.md5', 'lang'); 799 800 if (!$installer->requisitesok) { 801 throw new lang_installer_exception('installer_requisites_check_failed'); 802 } 803 804 $status = $installer->install(); 805 806 if ($status == COMPONENT_ERROR) { 807 if ($installer->get_error() === 'remotedownloaderror') { 808 return self::RESULT_DOWNLOADERROR; 809 } else { 810 throw new lang_installer_exception($installer->get_error(), $langcode); 811 } 812 813 } else if ($status == COMPONENT_UPTODATE) { 814 return self::RESULT_UPTODATE; 815 816 } else if ($status == COMPONENT_INSTALLED) { 817 return self::RESULT_INSTALLED; 818 819 } else { 820 throw new lang_installer_exception('unexpected_installer_result', $status); 821 } 822 } 823 } 824 825 826 /** 827 * Exception thrown by {@link lang_installer} 828 * 829 * @copyright 2011 David Mudrak <david@moodle.com> 830 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 831 */ 832 class lang_installer_exception extends moodle_exception { 833 834 public function __construct($errorcode, $debuginfo = null) { 835 parent::__construct($errorcode, 'error', '', null, $debuginfo); 836 } 837 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body