mirror of
https://github.com/moodle/moodle.git
synced 2025-05-05 07:48:21 +02:00
Finished the install_component class. It allows do do all the work
just with 2 calls. See embebed documentation about usage. Next step, add some error strings and implement it both in pear and environmental actions.
This commit is contained in:
parent
a445d5e8b2
commit
d70c9bda55
@ -33,21 +33,25 @@
|
||||
// - It only can check, download and install items under moodledata.
|
||||
// - Every downloadeable item must be one zip file.
|
||||
// - The zip file root content must be 1 directory, i.e, everything
|
||||
// stored under 1 directory.
|
||||
// is stored under 1 directory.
|
||||
// - Zip file name and root directory must have the same name (but
|
||||
// the .zip, of course).
|
||||
// the .zip extension, of course).
|
||||
// - Every .zip file must be defined in one .md5 file that will be
|
||||
// stored in the same remote directory that the .zip file.
|
||||
// stored in the same remote directory than the .zip file.
|
||||
// - The name of such .md5 file is free, although it's recommended
|
||||
// to use the same name than the .zip (that's the default
|
||||
// assumption if no specified).
|
||||
// - Every .md5 file will be a comma separated (CVS) file where each
|
||||
// line will follow this formar:
|
||||
// - Every remote .md5 file will be a comma separated (CVS) file where each
|
||||
// line will follow this format:
|
||||
// - Field 1: name of the zip file (without extension). Mandatory.
|
||||
// - Field 2: md5 of the zip file. Mandatory.
|
||||
// - Field 3: whatever you want. Optional.
|
||||
// - Field 3: whatever you want (or need). Optional.
|
||||
// -Every local .md5 file will:
|
||||
// - Have the zip file name (without the extension) plus -md5
|
||||
// - Will reside inside the expanded zip file dir
|
||||
// - Will contain the installed md5
|
||||
// With all these details present, the process will perform this tasks:
|
||||
// - Perform security checks. Only admins are allowed to use this.
|
||||
// - Perform security checks. Only admins are allowed to use this for now.
|
||||
// - Perform server checks. fopen must allow to open remote URLs.
|
||||
// - Read the .md5 file from source (1).
|
||||
// - Extract the correct line for the .zip being requested.
|
||||
@ -66,10 +70,61 @@
|
||||
// reach here ever.
|
||||
// - If fopen is not available, a message text about how to do
|
||||
// the process manually will be generated.
|
||||
//
|
||||
// General Usage:
|
||||
//
|
||||
// To install one component:
|
||||
//
|
||||
// require_once($CFG->libdir.'/componentlib.class.php');
|
||||
// if ($cd = new component_installer('http://download.moodle.org', 'lang16',
|
||||
// 'es_utf8.zip', 'languages.md5', 'lang')) {
|
||||
// $status = $cd->install(); //returns ERROR | UPTODATE | INSTALLED
|
||||
// switch ($status) {
|
||||
// case ERROR:
|
||||
// error(get_string($cd->get_error()));
|
||||
// break;
|
||||
// case UPTODATE:
|
||||
// //Print error string or whatever you want to do
|
||||
// break;
|
||||
// case INSTALLED:
|
||||
// //Print/do whatever you want
|
||||
// break;
|
||||
// default:
|
||||
// //We wouldn't reach this point
|
||||
// }
|
||||
// } else {
|
||||
// error(get_string($cd->get_error()));
|
||||
// }
|
||||
//
|
||||
// To switch of component (maintaining the rest of settings):
|
||||
//
|
||||
// $status = $cd->change_zip_file('en_utf8.zip'); //returns boolean false on error
|
||||
//
|
||||
// To retrieve all the components in one remote md5 file
|
||||
//
|
||||
// $components = $cd->get_all_components_md5(); //returns boolean false on error, array instead
|
||||
//
|
||||
// To check if current component needs to be updated
|
||||
//
|
||||
// $status = $cd->need_upgrade(); //returns ERROR | UPTODATE | NEEDUPDATE
|
||||
//
|
||||
// To get the 3rd field of the md5 file (optional)
|
||||
//
|
||||
// $field = $cd->get_extra_md5_field(); //returns string (empty if not exists)
|
||||
//
|
||||
// For all the error situations the $cd->get_error() method should return always the key of the
|
||||
// error to be retrieved by one standard get_string() call against the error.php lang file.
|
||||
//
|
||||
// That's all!
|
||||
|
||||
// Some needed constants
|
||||
define('ERROR', 0);
|
||||
define('UPTODATE', 1);
|
||||
define('NEEDUPDATE', 2);
|
||||
define('INSTALLED', 3);
|
||||
|
||||
/**
|
||||
* This class is used to chack, download and install items from
|
||||
* This class is used to check, download and install items from
|
||||
* download.moodle.org to the moodledata directory. It always
|
||||
* return true/false in all their public methods to say if
|
||||
* execution has ended succesfuly or not. If there is any problem
|
||||
@ -93,6 +148,9 @@ class component_installer {
|
||||
var $extramd5info; /// Contents of the optional third field in the .md5 file.
|
||||
var $requisitesok; /// Flag to see if requisites check has been passed ok.
|
||||
|
||||
var $cachedmd5components; /// Array of cached components to avoid to
|
||||
/// download the same md5 file more than once per request.
|
||||
|
||||
/**
|
||||
* Standard constructor of the class. It will initialize all attributes.
|
||||
* without performing any check at all.
|
||||
@ -118,6 +176,13 @@ class component_installer {
|
||||
$this->errorstring = '';
|
||||
$this->extramd5info = '';
|
||||
$this->requisitesok = false;
|
||||
$this->cachedmd5components = array();
|
||||
|
||||
if (!$this->check_requisites()) {
|
||||
return false;
|
||||
} else {
|
||||
return($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,6 +194,9 @@ class component_installer {
|
||||
* @return boolean true/false (plus detailed error in errorstring)
|
||||
*/
|
||||
function check_requisites() {
|
||||
global $CFG;
|
||||
|
||||
$this->requisitesok = false;
|
||||
|
||||
/// Check for admin (this will be out in the future)
|
||||
if (!isadmin()) {
|
||||
@ -156,12 +224,19 @@ class component_installer {
|
||||
$this->errorstring='wrongzipfilename';
|
||||
return false;
|
||||
}
|
||||
/// Check that exists under dataroot
|
||||
if (!empty($this->destpath)) {
|
||||
if (!file_exists($CFG->dataroot.'/'.$this->destpath)) {
|
||||
$this->errorstring='wrongdestpath';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/// Calculate the componentnamea
|
||||
$pos = stripos($this->zipfilename, '.zip');
|
||||
$this->componentname = substr($this->zipfilename, 0, $pos+1);
|
||||
$this->componentname = substr($this->zipfilename, 0, $pos);
|
||||
/// Calculate md5filename if it's empty
|
||||
if (empty($this->md5filename)) {
|
||||
$this->md5filename = $this->componentname.'md5';
|
||||
$this->md5filename = $this->componentname.'.md5';
|
||||
}
|
||||
/// Set the requisites passed flag
|
||||
$this->requisitesok = true;
|
||||
@ -169,45 +244,262 @@ class component_installer {
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will download the specified md5 file, looking for the
|
||||
* current componentname, returning its md5 field and storing extramd5info
|
||||
* if present
|
||||
* This function will perform the full installation if needed, i.e.
|
||||
* compare md5 values, download, unzip, install and regenerate
|
||||
* local md5 file
|
||||
*
|
||||
* @return mixed md5 present in server (or false if error)
|
||||
* @return int ERROR | UPTODATE | INSTALLED
|
||||
*/
|
||||
function get_remote_md5() {
|
||||
function install() {
|
||||
|
||||
global $CFG;
|
||||
|
||||
/// Check requisites are passed
|
||||
if (!$this->requisitesok) {
|
||||
return ERROR;
|
||||
}
|
||||
/// Confirm we need upgrade
|
||||
if ($this->need_upgrade() === ERROR) {
|
||||
return ERROR;
|
||||
} else if ($this->need_upgrade() === UPTODATE) {
|
||||
$this->errorstring='componentisuptodate';
|
||||
return UPTODATE;
|
||||
}
|
||||
/// Create temp directory if necesary
|
||||
if (!make_upload_directory($CFG->dataroot.'/temp', false)) {
|
||||
$this->errorstring='cannotcreatetempdir';
|
||||
return ERROR;
|
||||
}
|
||||
/// Download zip file and save it to temp
|
||||
$source = $this->sourcebase.'/'.$this->zippath.'/'.$this->zipfilename;
|
||||
$zipfile= $CFG->dataroot.'/temp/'.$this->zipfilename;
|
||||
if ($contents = file_get_contents($source)) {
|
||||
if ($file = fopen($zipfile, 'w')) {
|
||||
if (!fwrite($file, $contents)) {
|
||||
fclose($file);
|
||||
$this->errorstring='cannotsavezipfile';
|
||||
return ERROR;
|
||||
}
|
||||
} else {
|
||||
$this->errorstring='cannotsavezipfile';
|
||||
return ERROR;
|
||||
}
|
||||
fclose($file);
|
||||
} else {
|
||||
$this->errorstring='cannotdownloadzipfile';
|
||||
return ERROR;
|
||||
}
|
||||
/// Calculate its md5
|
||||
$new_md5 = md5($contents);
|
||||
/// Compare it with the remote md5 to check if we have the correct zip file
|
||||
if (!$remote_md5 = $this->get_component_md5()) {
|
||||
return ERROR;
|
||||
}
|
||||
if ($new_md5 != $remote_md5) {
|
||||
$this->errorstring='downloadedfilecheckfailed';
|
||||
return ERROR;
|
||||
}
|
||||
/// Move current revision to a safe place
|
||||
$destinationdir = $CFG->dataroot.'/'.$this->destpath;
|
||||
$destinationcomponent = $destinationdir.'/'.$this->componentname;
|
||||
@remove_dir($destinationcomponent.'_old'); //Deleting possible old components before
|
||||
@rename ($destinationcomponent, $destinationcomponent.'_old'); //Moving to a safe place
|
||||
/// Unzip new version
|
||||
if (!unzip_file($zipfile, $destinationdir, false)) {
|
||||
/// Error so, go back to the older
|
||||
@remove_dir($destinationcomponent);
|
||||
@rename ($destinationcomponent.'_old', $destinationcomponent);
|
||||
$this->errorstring='cannotunzipfile';
|
||||
return ERROR;
|
||||
}
|
||||
/// Delete old component version
|
||||
@remove_dir($destinationcomponent.'_old');
|
||||
/// Create local md5
|
||||
if ($file = fopen($destinationcomponent.'/'.$this->componentname.'.md5', 'w')) {
|
||||
if (!fwrite($file, $new_md5)) {
|
||||
fclose($file);
|
||||
$this->errorstring='cannotsavemd5file';
|
||||
return ERROR;
|
||||
}
|
||||
} else {
|
||||
$this->errorstring='cannotsavemd5file';
|
||||
return ERROR;
|
||||
}
|
||||
fclose($file);
|
||||
/// Delete temp zip file
|
||||
@unlink($zipfile);
|
||||
|
||||
return INSTALLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will detect if remote component needs to be installed
|
||||
* because it's different from the local one
|
||||
*
|
||||
* @return int ERROR | UPTODATE | NEEDUPDATE
|
||||
*/
|
||||
function need_upgrade() {
|
||||
|
||||
/// Check requisites are passed
|
||||
if (!$this->requisitesok) {
|
||||
return ERROR;
|
||||
}
|
||||
/// Get local md5
|
||||
$local_md5 = $this->get_local_md5();
|
||||
/// Get remote md5
|
||||
if (!$remote_md5 = $this->get_component_md5()) {
|
||||
return ERROR;
|
||||
}
|
||||
/// Return result
|
||||
if ($local_md5 == $remote_md5) {
|
||||
return UPTODATE;
|
||||
} else {
|
||||
return NEEDUPDATE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will change the zip file to install on the fly
|
||||
* to allow the class to process different components of the
|
||||
* same md5 file without intantiating more objects.
|
||||
*
|
||||
* @param string New zip filename to process
|
||||
* @return boolean true/false
|
||||
*/
|
||||
function change_zip_file($newzipfilename) {
|
||||
|
||||
$this->zipfilename = $newzipfilename;
|
||||
return $this->check_requisites();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will get the local md5 value of the installed
|
||||
* component.
|
||||
*
|
||||
* @return string md5 of the local component (false on error)
|
||||
*/
|
||||
function get_local_md5() {
|
||||
global $CFG;
|
||||
|
||||
/// Check requisites are passed
|
||||
if (!$this->requisitesok) {
|
||||
$this->errorstring='requisitesnotpassed';
|
||||
return false;
|
||||
}
|
||||
|
||||
$return_value = 'needtobeinstalled'; /// Fake value to force new installation
|
||||
|
||||
/// Calculate source to read
|
||||
$source = $CFG->dataroot.'/'.$this->destpath.'/'.$this->componentname.'/'.$this->componentname.'.md5';
|
||||
/// Read md5 value stored (if exists)
|
||||
if (file_exists($source)) {
|
||||
if ($temp = file_get_contents($source)) {
|
||||
$return_value = $temp;
|
||||
}
|
||||
}
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will download the specified md5 file, looking for the
|
||||
* current componentname, returning its md5 field and storing extramd5info
|
||||
* if present. Also it caches results to cachedmd5components for better
|
||||
* performance in the same request.
|
||||
*
|
||||
* @return mixed md5 present in server (or false if error)
|
||||
*/
|
||||
function get_component_md5() {
|
||||
|
||||
/// Check requisites are passed
|
||||
if (!$this->requisitesok) {
|
||||
return false;
|
||||
}
|
||||
/// Get all components of md5 file
|
||||
if (!$comp_arr = $this->get_all_components_md5()) {
|
||||
$this->errorstring='cannotdownloadcomponents';
|
||||
return false;
|
||||
}
|
||||
/// Search for the componentname component
|
||||
if (empty($comp_arr[$this->componentname]) || !$component = $comp_arr[$this->componentname]) {
|
||||
$this->errorstring='cannotfindcomponent';
|
||||
return false;
|
||||
}
|
||||
/// Check we have a valid md5
|
||||
if (empty($component[1]) || strlen($component[1]) != 32) {
|
||||
$this->errorstring='invalidmd5';
|
||||
return false;
|
||||
}
|
||||
/// Set the extramd5info field
|
||||
if (!empty($component[2])) {
|
||||
$this->extramd5info = $component[2];
|
||||
}
|
||||
return $component[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* This function allows you to retrieve the complete array of components found in
|
||||
* the md5filename
|
||||
*
|
||||
* @return array array of components in md5 file or false if error
|
||||
*/
|
||||
function get_all_components_md5() {
|
||||
|
||||
/// Check requisites are passed
|
||||
if (!$this->requisitesok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Initialize components array
|
||||
$comp_arr = array();
|
||||
|
||||
/// Define and retrieve the full md5 file
|
||||
$source = $this->sourcebase.'/'.$this->zippath.'/'.$this->md5filename;
|
||||
$availablecomponents = array();
|
||||
if ($fp = fopen($source, 'r')) {
|
||||
/// Read from URL, each line will be one component
|
||||
while(!feof ($fp)) {
|
||||
$availablecomponents[] = split(',', fgets($fp,1024));
|
||||
}
|
||||
fclose($fp);
|
||||
/// If no components have been found, return error
|
||||
if (empty($availablecomponents)) {
|
||||
$this->errorstring='cannotdownloadmd5file';
|
||||
|
||||
/// Check if we have downloaded the md5 file before (per request cache)
|
||||
if (!empty($this->cachedmd5components[$source])) {
|
||||
$comp_arr = $this->cachedmd5components[$source];
|
||||
} else {
|
||||
/// Not downloaded, let's do it now
|
||||
$availablecomponents = array();
|
||||
if ($fp = fopen($source, 'r')) {
|
||||
/// Read from URL, each line will be one component
|
||||
while(!feof ($fp)) {
|
||||
$availablecomponents[] = split(',', fgets($fp,1024));
|
||||
}
|
||||
fclose($fp);
|
||||
/// If no components have been found, return error
|
||||
if (empty($availablecomponents)) {
|
||||
$this->errorstring='cannotdownloadcomponent';
|
||||
return false;
|
||||
}
|
||||
/// Build an associative array of components for easily search
|
||||
/// applying trim to avoid linefeeds and other...
|
||||
$comp_arr = array();
|
||||
foreach ($availablecomponents as $component) {
|
||||
/// Avoid sometimes empty lines
|
||||
if (empty($component[0])) {
|
||||
continue;
|
||||
}
|
||||
$component[0]=trim($component[0]);
|
||||
$component[1]=trim($component[1]);
|
||||
if (!empty($component[2])) {
|
||||
$component[2]=trim($component[2]);
|
||||
}
|
||||
$comp_arr[$component[0]] = $component;
|
||||
}
|
||||
/// Cache components
|
||||
$this->cachedmd5components[$source] = $comp_arr;
|
||||
} else {
|
||||
/// Return error
|
||||
$this->errorstring='cannotdownloadcomponents';
|
||||
return false;
|
||||
}
|
||||
/// Build an associative array of components, storing it in the
|
||||
/// Look for our expected componentname
|
||||
|
||||
print_object($availablecomponents);
|
||||
|
||||
} else {
|
||||
/// Return error
|
||||
$this->errorstring='cannotdownloadcomponent';
|
||||
return false;
|
||||
}
|
||||
|
||||
/// If there is no commponents, error
|
||||
if (empty($comp_arr)) {
|
||||
$this->errorstring='cannotdownloadcomponents';
|
||||
return false;
|
||||
}
|
||||
return $comp_arr;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,10 +507,17 @@ class component_installer {
|
||||
*
|
||||
* @return string the error string
|
||||
*/
|
||||
function get_error_string() {
|
||||
function get_error() {
|
||||
return $this->errorstring;
|
||||
}
|
||||
|
||||
/** This function returns the extramd5 field (optional in md5 file)
|
||||
*
|
||||
* @return string the extramd5 field
|
||||
*/
|
||||
function get_extra_md5_field() {
|
||||
return $this->extramd5info;
|
||||
}
|
||||
|
||||
} /// End of component_installer class
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user