PageRenderTime 60ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/componentlib.class.php

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