| [ Index ] |
PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008] |
[Summary view] [Print] [Text view]
1 <?php //$Id: componentlib.class.php,v 1.7.2.6 2008/04/02 06:10:01 dongsheng Exp $ 2 3 /////////////////////////////////////////////////////////////////////////// 4 // // 5 // NOTICE OF COPYRIGHT // 6 // // 7 // Moodle - Modular Object-Oriented Dynamic Learning Environment // 8 // http://moodle.com // 9 // // 10 // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com // 11 // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com // 12 // // 13 // This program is free software; you can redistribute it and/or modify // 14 // it under the terms of the GNU General Public License as published by // 15 // the Free Software Foundation; either version 2 of the License, or // 16 // (at your option) any later version. // 17 // // 18 // This program is distributed in the hope that it will be useful, // 19 // but WITHOUT ANY WARRANTY; without even the implied warranty of // 20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 21 // GNU General Public License for more details: // 22 // // 23 // http://www.gnu.org/copyleft/gpl.html // 24 // // 25 /////////////////////////////////////////////////////////////////////////// 26 27 // This library includes all the necessary stuff to use the one-click 28 // download and install feature of Moodle, used to keep updated some 29 // items like languages, pear, enviroment... i.e, components. 30 // 31 // It has been developed harcoding some important limits that are 32 // explained below: 33 // - It only can check, download and install items under moodledata. 34 // - Every downloadeable item must be one zip file. 35 // - The zip file root content must be 1 directory, i.e, everything 36 // is stored under 1 directory. 37 // - Zip file name and root directory must have the same name (but 38 // the .zip extension, of course). 39 // - Every .zip file must be defined in one .md5 file that will be 40 // stored in the same remote directory than the .zip file. 41 // - The name of such .md5 file is free, although it's recommended 42 // to use the same name than the .zip (that's the default 43 // assumption if no specified). 44 // - Every remote .md5 file will be a comma separated (CVS) file where each 45 // line will follow this format: 46 // - Field 1: name of the zip file (without extension). Mandatory. 47 // - Field 2: md5 of the zip file. Mandatory. 48 // - Field 3: whatever you want (or need). Optional. 49 // -Every local .md5 file will: 50 // - Have the zip file name (without the extension) plus -md5 51 // - Will reside inside the expanded zip file dir 52 // - Will contain the md5 od the latest installed component 53 // With all these details present, the process will perform this tasks: 54 // - Perform security checks. Only admins are allowed to use this for now. 55 // - Read the .md5 file from source (1). 56 // - Extract the correct line for the .zip being requested. 57 // - Compare it with the local .md5 file (2). 58 // - If different: 59 // - Download the newer .zip file from source. 60 // - Calculate its md5 (3). 61 // - Compare (1) and (3). 62 // - If equal: 63 // - Delete old directory. 64 // - Uunzip the newer .zip file. 65 // - Create the new local .md5 file. 66 // - Delete the .zip file. 67 // - If different: 68 // - ERROR. Old package won't be modified. We shouldn't 69 // reach here ever. 70 // - If component download is not possible, a message text about how to do 71 // the process manually (remotedownloaderror) must be displayed to explain it. 72 // 73 // General Usage: 74 // 75 // To install one component: 76 // 77 // require_once($CFG->libdir.'/componentlib.class.php'); 78 // if ($cd = new component_installer('http://download.moodle.org', 'lang16', 79 // 'es_utf8.zip', 'languages.md5', 'lang')) { 80 // $status = $cd->install(); //returns COMPONENT_(ERROR | UPTODATE | INSTALLED) 81 // switch ($status) { 82 // case COMPONENT_ERROR: 83 // if ($cd->get_error() == 'remotedownloaderror') { 84 // $a = new stdClass(); 85 // $a->url = 'http://download.moodle.org/lang16/es_utf8.zip'; 86 // $a->dest= $CFG->dataroot.'/lang'; 87 // print_error($cd->get_error(), 'error', '', $a); 88 // } else { 89 // print_error($cd->get_error(), 'error'); 90 // } 91 // break; 92 // case COMPONENT_UPTODATE: 93 // //Print error string or whatever you want to do 94 // break; 95 // case COMPONENT_INSTALLED: 96 // //Print/do whatever you want 97 // break; 98 // default: 99 // //We shouldn't reach this point 100 // } 101 // } else { 102 // //We shouldn't reach this point 103 // } 104 // 105 // To switch of component (maintaining the rest of settings): 106 // 107 // $status = $cd->change_zip_file('en_utf8.zip'); //returns boolean false on error 108 // 109 // To retrieve all the components in one remote md5 file 110 // 111 // $components = $cd->get_all_components_md5(); //returns boolean false on error, array instead 112 // 113 // To check if current component needs to be updated 114 // 115 // $status = $cd->need_upgrade(); //returns COMPONENT_(ERROR | UPTODATE | NEEDUPDATE) 116 // 117 // To get the 3rd field of the md5 file (optional) 118 // 119 // $field = $cd->get_extra_md5_field(); //returns string (empty if not exists) 120 // 121 // For all the error situations the $cd->get_error() method should return always the key of the 122 // error to be retrieved by one standard get_string() call against the error.php lang file. 123 // 124 // That's all! 125 global $CFG; 126 require_once($CFG->libdir.'/filelib.php'); 127 128 // Some needed constants 129 define('COMPONENT_ERROR', 0); 130 define('COMPONENT_UPTODATE', 1); 131 define('COMPONENT_NEEDUPDATE', 2); 132 define('COMPONENT_INSTALLED', 3); 133 134 /** 135 * This class is used to check, download and install items from 136 * download.moodle.org to the moodledata directory. It always 137 * return true/false in all their public methods to say if 138 * execution has ended succesfuly or not. If there is any problem 139 * its getError() method can be called, returning one error string 140 * to be used with the standard get/print_string() functions. 141 */ 142 class component_installer { 143 144 var $sourcebase; /// Full http URL, base for downloadable items 145 var $zippath; /// Relative path (from sourcebase) where the 146 /// downloadeable item resides. 147 var $zipfilename; /// Name of the .zip file to be downloaded 148 var $md5filename; /// Name of the .md5 file to be read 149 var $componentname;/// Name of the component. Must be the zip name without 150 /// the extension. And it defines a lot of things: 151 /// the md5 line to search for, the default m5 file name 152 /// and the name of the root dir stored inside the zip file 153 var $destpath; /// Relative path (from moodledata) where the .zip 154 /// file will be expanded. 155 var $errorstring; /// Latest error produced. It will contain one lang string key. 156 var $extramd5info; /// Contents of the optional third field in the .md5 file. 157 var $requisitesok; /// Flag to see if requisites check has been passed ok. 158 159 var $cachedmd5components; /// Array of cached components to avoid to 160 /// download the same md5 file more than once per request. 161 162 /** 163 * Standard constructor of the class. It will initialize all attributes. 164 * without performing any check at all. 165 * 166 * @param string Full http URL, base for downloadeable items 167 * @param string Relative path (from sourcebase) where the 168 * downloadeable item resides 169 * @param string Name of the .zip file to be downloaded 170 * @param string Name of the .md5 file to be read (default '' = same 171 * than zipfilename) 172 * @param string Relative path (from moodledata) where the .zip file will 173 * be expanded (default='' = moodledataitself) 174 * @return object 175 */ 176 function component_installer ($sourcebase, $zippath, $zipfilename, $md5filename='', $destpath='') { 177 178 $this->sourcebase = $sourcebase; 179 $this->zippath = $zippath; 180 $this->zipfilename = $zipfilename; 181 $this->md5filename = $md5filename; 182 $this->componentname= ''; 183 $this->destpath = $destpath; 184 $this->errorstring = ''; 185 $this->extramd5info = ''; 186 $this->requisitesok = false; 187 $this->cachedmd5components = array(); 188 189 $this->check_requisites(); 190 } 191 192 /** 193 * This function will check if everything is properly set to begin 194 * one installation. Also, it will check for required settings 195 * and will fill everything as needed. 196 * 197 * @return boolean true/false (plus detailed error in errorstring) 198 */ 199 function check_requisites() { 200 global $CFG; 201 202 $this->requisitesok = false; 203 204 /// Check that everything we need is present 205 if (empty($this->sourcebase) || empty($this->zippath) || empty($this->zipfilename)) { 206 $this->errorstring='missingrequiredfield'; 207 return false; 208 } 209 /// Check for correct sourcebase (this will be out in the future) 210 if ($this->sourcebase != 'http://download.moodle.org') { 211 $this->errorstring='wrongsourcebase'; 212 return false; 213 } 214 /// Check the zip file is a correct one (by extension) 215 if (stripos($this->zipfilename, '.zip') === false) { 216 $this->errorstring='wrongzipfilename'; 217 return false; 218 } 219 /// Check that exists under dataroot 220 if (!empty($this->destpath)) { 221 if (!file_exists($CFG->dataroot.'/'.$this->destpath)) { 222 $this->errorstring='wrongdestpath'; 223 return false; 224 } 225 } 226 /// Calculate the componentname 227 $pos = stripos($this->zipfilename, '.zip'); 228 $this->componentname = substr($this->zipfilename, 0, $pos); 229 /// Calculate md5filename if it's empty 230 if (empty($this->md5filename)) { 231 $this->md5filename = $this->componentname.'.md5'; 232 } 233 /// Set the requisites passed flag 234 $this->requisitesok = true; 235 return true; 236 } 237 238 /** 239 * This function will perform the full installation if needed, i.e. 240 * compare md5 values, download, unzip, install and regenerate 241 * local md5 file 242 * 243 * @return int COMPONENT_(ERROR | UPTODATE | INSTALLED) 244 */ 245 function install() { 246 247 global $CFG; 248 249 /// Check requisites are passed 250 if (!$this->requisitesok) { 251 return COMPONENT_ERROR; 252 } 253 /// Confirm we need upgrade 254 if ($this->need_upgrade() === COMPONENT_ERROR) { 255 return COMPONENT_ERROR; 256 } else if ($this->need_upgrade() === COMPONENT_UPTODATE) { 257 $this->errorstring='componentisuptodate'; 258 return COMPONENT_UPTODATE; 259 } 260 /// Create temp directory if necesary 261 if (!make_upload_directory('temp', false)) { 262 $this->errorstring='cannotcreatetempdir'; 263 return COMPONENT_ERROR; 264 } 265 /// Download zip file and save it to temp 266 $source = $this->sourcebase.'/'.$this->zippath.'/'.$this->zipfilename; 267 $zipfile= $CFG->dataroot.'/temp/'.$this->zipfilename; 268 269 if($contents = download_file_content($source)) { 270 if ($file = fopen($zipfile, 'w')) { 271 if (!fwrite($file, $contents)) { 272 fclose($file); 273 $this->errorstring='cannotsavezipfile'; 274 return COMPONENT_ERROR; 275 } 276 } else { 277 $this->errorstring='cannotsavezipfile'; 278 return COMPONENT_ERROR; 279 } 280 fclose($file); 281 } else { 282 $this->errorstring='cannotdownloadzipfile'; 283 return COMPONENT_ERROR; 284 } 285 /// Calculate its md5 286 $new_md5 = md5($contents); 287 /// Compare it with the remote md5 to check if we have the correct zip file 288 if (!$remote_md5 = $this->get_component_md5()) { 289 return COMPONENT_ERROR; 290 } 291 if ($new_md5 != $remote_md5) { 292 $this->errorstring='downloadedfilecheckfailed'; 293 return COMPONENT_ERROR; 294 } 295 /// Move current revision to a safe place 296 $destinationdir = $CFG->dataroot.'/'.$this->destpath; 297 $destinationcomponent = $destinationdir.'/'.$this->componentname; 298 @remove_dir($destinationcomponent.'_old'); //Deleting possible old components before 299 @rename ($destinationcomponent, $destinationcomponent.'_old'); //Moving to a safe place 300 /// Unzip new version 301 if (!unzip_file($zipfile, $destinationdir, false)) { 302 /// Error so, go back to the older 303 @remove_dir($destinationcomponent); 304 @rename ($destinationcomponent.'_old', $destinationcomponent); 305 $this->errorstring='cannotunzipfile'; 306 return COMPONENT_ERROR; 307 } 308 /// Delete old component version 309 @remove_dir($destinationcomponent.'_old'); 310 /// Create local md5 311 if ($file = fopen($destinationcomponent.'/'.$this->componentname.'.md5', 'w')) { 312 if (!fwrite($file, $new_md5)) { 313 fclose($file); 314 $this->errorstring='cannotsavemd5file'; 315 return COMPONENT_ERROR; 316 } 317 } else { 318 $this->errorstring='cannotsavemd5file'; 319 return COMPONENT_ERROR; 320 } 321 fclose($file); 322 /// Delete temp zip file 323 @unlink($zipfile); 324 325 return COMPONENT_INSTALLED; 326 } 327 328 /** 329 * This function will detect if remote component needs to be installed 330 * because it's different from the local one 331 * 332 * @return int COMPONENT_(ERROR | UPTODATE | NEEDUPDATE) 333 */ 334 function need_upgrade() { 335 336 /// Check requisites are passed 337 if (!$this->requisitesok) { 338 return COMPONENT_ERROR; 339 } 340 /// Get local md5 341 $local_md5 = $this->get_local_md5(); 342 /// Get remote md5 343 if (!$remote_md5 = $this->get_component_md5()) { 344 return COMPONENT_ERROR; 345 } 346 /// Return result 347 if ($local_md5 == $remote_md5) { 348 return COMPONENT_UPTODATE; 349 } else { 350 return COMPONENT_NEEDUPDATE; 351 } 352 } 353 354 /** 355 * This function will change the zip file to install on the fly 356 * to allow the class to process different components of the 357 * same md5 file without intantiating more objects. 358 * 359 * @param string New zip filename to process 360 * @return boolean true/false 361 */ 362 function change_zip_file($newzipfilename) { 363 364 $this->zipfilename = $newzipfilename; 365 return $this->check_requisites(); 366 } 367 368 /** 369 * This function will get the local md5 value of the installed 370 * component. 371 * 372 * @return string md5 of the local component (false on error) 373 */ 374 function get_local_md5() { 375 global $CFG; 376 377 /// Check requisites are passed 378 if (!$this->requisitesok) { 379 return false; 380 } 381 382 $return_value = 'needtobeinstalled'; /// Fake value to force new installation 383 384 /// Calculate source to read 385 $source = $CFG->dataroot.'/'.$this->destpath.'/'.$this->componentname.'/'.$this->componentname.'.md5'; 386 /// Read md5 value stored (if exists) 387 if (file_exists($source)) { 388 if ($temp = file_get_contents($source)) { 389 $return_value = $temp; 390 } 391 } 392 return $return_value; 393 } 394 395 /** 396 * This function will download the specified md5 file, looking for the 397 * current componentname, returning its md5 field and storing extramd5info 398 * if present. Also it caches results to cachedmd5components for better 399 * performance in the same request. 400 * 401 * @return mixed md5 present in server (or false if error) 402 */ 403 function get_component_md5() { 404 405 /// Check requisites are passed 406 if (!$this->requisitesok) { 407 return false; 408 } 409 /// Get all components of md5 file 410 if (!$comp_arr = $this->get_all_components_md5()) { 411 if (empty($this->errorstring)) { 412 $this->errorstring='cannotdownloadcomponents'; 413 } 414 return false; 415 } 416 /// Search for the componentname component 417 if (empty($comp_arr[$this->componentname]) || !$component = $comp_arr[$this->componentname]) { 418 $this->errorstring='cannotfindcomponent'; 419 return false; 420 } 421 /// Check we have a valid md5 422 if (empty($component[1]) || strlen($component[1]) != 32) { 423 $this->errorstring='invalidmd5'; 424 return false; 425 } 426 /// Set the extramd5info field 427 if (!empty($component[2])) { 428 $this->extramd5info = $component[2]; 429 } 430 return $component[1]; 431 } 432 433 /** 434 * This function allows you to retrieve the complete array of components found in 435 * the md5filename 436 * 437 * @return array array of components in md5 file or false if error 438 */ 439 function get_all_components_md5() { 440 441 /// Check requisites are passed 442 if (!$this->requisitesok) { 443 return false; 444 } 445 446 /// Initialize components array 447 $comp_arr = array(); 448 449 /// Define and retrieve the full md5 file 450 $source = $this->sourcebase.'/'.$this->zippath.'/'.$this->md5filename; 451 452 /// Check if we have downloaded the md5 file before (per request cache) 453 if (!empty($this->cachedmd5components[$source])) { 454 $comp_arr = $this->cachedmd5components[$source]; 455 } else { 456 /// Not downloaded, let's do it now 457 $availablecomponents = array(); 458 459 if ($contents = download_file_content($source)) { 460 /// Split text into lines 461 $lines=preg_split('/\r?\n/',$contents); 462 /// Each line will be one component 463 foreach($lines as $line) { 464 $availablecomponents[] = split(',', $line); 465 } 466 /// If no components have been found, return error 467 if (empty($availablecomponents)) { 468 $this->errorstring='cannotdownloadcomponents'; 469 return false; 470 } 471 /// Build an associative array of components for easily search 472 /// applying trim to avoid linefeeds and other... 473 $comp_arr = array(); 474 foreach ($availablecomponents as $component) { 475 /// Avoid sometimes empty lines 476 if (empty($component[0])) { 477 continue; 478 } 479 $component[0]=trim($component[0]); 480 $component[1]=trim($component[1]); 481 if (!empty($component[2])) { 482 $component[2]=trim($component[2]); 483 } 484 $comp_arr[$component[0]] = $component; 485 } 486 /// Cache components 487 $this->cachedmd5components[$source] = $comp_arr; 488 } else { 489 /// Return error 490 $this->errorstring='remotedownloaderror'; 491 return false; 492 } 493 } 494 /// If there is no commponents or erros found, error 495 if (!empty($this->errorstring)) { 496 return false; 497 498 } else if (empty($comp_arr)) { 499 $this->errorstring='cannotdownloadcomponents'; 500 return false; 501 } 502 return $comp_arr; 503 } 504 505 /** 506 * This function returns the errorstring 507 * 508 * @return string the error string 509 */ 510 function get_error() { 511 return $this->errorstring; 512 } 513 514 /** This function returns the extramd5 field (optional in md5 file) 515 * 516 * @return string the extramd5 field 517 */ 518 function get_extra_md5_field() { 519 return $this->extramd5info; 520 } 521 522 } /// End of component_installer class 523 524 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Jan 14 11:33:29 2009 | Cross-referenced by PHPXref 0.7 |