Merge branch 'MDL-19263-21' of https://github.com/mackensen/moodle into MOODLE_21_STABLE
[moodle.git] / backup / lib.php
blob3a4e5da9778d505290d71d004232c323a22fef91
1 <?php
2 //This file contains all the general function needed (file manipulation...)
3 //not directly part of the backup/restore utility plus some constants
5 // Define "restoreto" options
6 define('RESTORETO_CURRENT_DELETING', 0);
7 define('RESTORETO_CURRENT_ADDING', 1);
8 define('RESTORETO_NEW_COURSE', 2);
9 define('RESTORETO_EXISTING_DELETING', 3);
10 define('RESTORETO_EXISTING_ADDING', 4);
12 require_once($CFG->libdir . '/completionlib.php');
14 //Sets a name/value pair in config_plugin table
15 function backup_set_config($name, $value) {
16 return set_config($name, $value, 'backup');
19 //Gets all the information from config_plugin table
20 function backup_get_config() {
21 $backup_config = get_config('backup');
22 return (object)$backup_config;
25 //Delete old data in backup tables (if exists)
26 //Four hours seem to be appropiate now that backup is stable
27 function backup_delete_old_data() {
28 global $CFG, $DB;
30 //Change this if you want !!
31 $hours = 4;
32 //End change this
33 $seconds = $hours * 60 * 60;
34 $delete_from = time()-$seconds;
35 //Now delete from tables
36 $status = $DB->execute("DELETE FROM {backup_ids}
37 WHERE backup_code < ?", array($delete_from));
38 if ($status) {
39 $status = $DB->execute("DELETE FROM {backup_files}
40 WHERE backup_code < ?", array($delete_from));
42 //Now, delete old directory (if exists)
43 if ($status) {
44 $status = backup_delete_old_dirs($delete_from);
46 return($status);
49 //Function to delete dirs/files into temp/backup directory
50 //older than $delete_from
51 function backup_delete_old_dirs($delete_from) {
53 global $CFG;
55 $status = true;
56 //Get files and directories in the temp backup dir witout descend
57 $list = get_directory_list($CFG->dataroot."/temp/backup", "", false, true, true);
58 foreach ($list as $file) {
59 $file_path = $CFG->dataroot."/temp/backup/".$file;
60 $moddate = filemtime($file_path);
61 if ($status && $moddate < $delete_from) {
62 //If directory, recurse
63 if (is_dir($file_path)) {
64 $status = delete_dir_contents($file_path);
65 //There is nothing, delete the directory itself
66 if ($status) {
67 $status = rmdir($file_path);
69 //If file
70 } else {
71 unlink("$file_path");
76 return $status;
79 //Function to check and create the needed dir to
80 //save all the backup
81 function check_and_create_backup_dir($backup_unique_code) {
82 global $CFG;
84 $status = check_dir_exists($CFG->dataroot."/temp",true);
85 if ($status) {
86 $status = check_dir_exists($CFG->dataroot."/temp/backup",true);
88 if ($status) {
89 $status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code,true);
92 return $status;
95 //Function to delete all the directory contents recursively
96 //it supports a excluded dit too
97 //Copied from the web !!
98 function delete_dir_contents ($dir,$excludeddir="") {
99 global $CFG;
101 if (!is_dir($dir)) {
102 // if we've been given a directory that doesn't exist yet, return true.
103 // this happens when we're trying to clear out a course that has only just
104 // been created.
105 return true;
107 $slash = "/";
109 // Create arrays to store files and directories
110 $dir_files = array();
111 $dir_subdirs = array();
113 // Make sure we can delete it
114 chmod($dir, $CFG->directorypermissions);
116 if ((($handle = opendir($dir))) == FALSE) {
117 // The directory could not be opened
118 return false;
121 // Loop through all directory entries, and construct two temporary arrays containing files and sub directories
122 while (false !== ($entry = readdir($handle))) {
123 if (is_dir($dir. $slash .$entry) && $entry != ".." && $entry != "." && $entry != $excludeddir) {
124 $dir_subdirs[] = $dir. $slash .$entry;
126 else if ($entry != ".." && $entry != "." && $entry != $excludeddir) {
127 $dir_files[] = $dir. $slash .$entry;
131 // Delete all files in the curent directory return false and halt if a file cannot be removed
132 $countdirfiles = count($dir_files);
133 for ($i=0; $i<$countdirfiles; $i++) {
134 chmod($dir_files[$i], $CFG->directorypermissions);
135 if (((unlink($dir_files[$i]))) == FALSE) {
136 return false;
140 // Empty sub directories and then remove the directory
141 $countdirsubdirs = count($dir_subdirs);
142 for($i=0; $i<$countdirsubdirs; $i++) {
143 chmod($dir_subdirs[$i], $CFG->directorypermissions);
144 if (delete_dir_contents($dir_subdirs[$i]) == FALSE) {
145 return false;
147 else {
148 if (remove_dir($dir_subdirs[$i]) == FALSE) {
149 return false;
154 // Close directory
155 closedir($handle);
157 // Success, every thing is gone return true
158 return true;
161 //Function to clear (empty) the contents of the backup_dir
162 function clear_backup_dir($backup_unique_code) {
163 global $CFG;
165 $rootdir = $CFG->dataroot."/temp/backup/".$backup_unique_code;
167 //Delete recursively
168 $status = delete_dir_contents($rootdir);
170 return $status;
173 //Returns the module type of a course_module's id in a course
174 function get_module_type ($courseid,$moduleid) {
175 global $DB;
177 $results = $DB->get_records_sql("SELECT cm.id, m.name
178 FROM {course_modules} cm, {modules} m
179 WHERE cm.course = ? AND cm.id = ? AND
180 m.id = cm.module", array($courseid, $moduleid));
182 if ($results) {
183 $name = $results[$moduleid]->name;
184 } else {
185 $name = false;
187 return $name;
190 //This function return the names of all directories under a give directory
191 //Not recursive
192 function list_directories ($rootdir) {
194 $results = null;
196 $dir = opendir($rootdir);
197 while (false !== ($file=readdir($dir))) {
198 if ($file=="." || $file=="..") {
199 continue;
201 if (is_dir($rootdir."/".$file)) {
202 $results[$file] = $file;
205 closedir($dir);
206 return $results;
209 //This function return the names of all directories and files under a give directory
210 //Not recursive
211 function list_directories_and_files ($rootdir) {
213 $results = "";
215 $dir = opendir($rootdir);
216 while (false !== ($file=readdir($dir))) {
217 if ($file=="." || $file=="..") {
218 continue;
220 $results[$file] = $file;
222 closedir($dir);
223 return $results;
226 //This function clean data from backup tables and
227 //delete all temp files used
228 function clean_temp_data ($preferences) {
229 global $CFG, $DB;
231 $status = true;
233 //true->do it, false->don't do it. To debug if necessary.
234 if (true) {
235 //Now delete from tables
236 $status = $DB->delete_records('backup_ids', array('backup_code'=>$preferences->backup_unique_code))
237 && $DB->delete_records('backup_files', array('backup_code'=>$preferences->backup_unique_code));
239 //Now, delete temp directory (if exists)
240 $file_path = $CFG->dataroot."/temp/backup/".$preferences->backup_unique_code;
241 if (is_dir($file_path)) {
242 $status = delete_dir_contents($file_path);
243 //There is nothing, delete the directory itself
244 if ($status) {
245 $status = rmdir($file_path);
249 return $status;
252 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
253 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
254 //This functions are used to copy any file or directory ($from_file)
255 //to a new file or directory ($to_file). It works recursively and
256 //mantains file perms.
257 //I've copied it from: http://www.php.net/manual/en/function.copy.php
258 //Little modifications done
260 function backup_copy_file ($from_file,$to_file,$log_clam=false) {
261 global $CFG;
263 if (is_file($from_file)) {
264 //echo "<br />Copying ".$from_file." to ".$to_file; //Debug
265 //$perms=fileperms($from_file);
266 //return copy($from_file,$to_file) && chmod($to_file,$perms);
267 umask(0000);
268 if (copy($from_file,$to_file)) {
269 chmod($to_file,$CFG->directorypermissions);
270 if (!empty($log_clam)) {
271 //clam_log_upload($to_file,null,true);
273 return true;
275 return false;
277 else if (is_dir($from_file)) {
278 return backup_copy_dir($from_file,$to_file);
280 else{
281 //echo "<br />Error: not file or dir ".$from_file; //Debug
282 return false;
286 function backup_copy_dir($from_file,$to_file) {
287 global $CFG;
289 $status = true; // Initialize this, next code will change its value if needed
291 if (!is_dir($to_file)) {
292 //echo "<br />Creating ".$to_file; //Debug
293 umask(0000);
294 $status = mkdir($to_file,$CFG->directorypermissions);
296 $dir = opendir($from_file);
297 while (false !== ($file=readdir($dir))) {
298 if ($file=="." || $file=="..") {
299 continue;
301 $status = backup_copy_file ("$from_file/$file","$to_file/$file");
303 closedir($dir);
304 return $status;
306 ///Ends copy file/dirs functions
307 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
308 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
311 * Are we restoring a backup that was made on the same site that we are restoring to?
312 * This relies on some information that was only added to backup files in January 2009.
313 * For older backup files, fall back to guessing based on wwwroot. MDL-16614 explains
314 * when this guess could give the wrong answer.
315 * @return boolean true if the backup was made on the same site we are restoring to.
317 function backup_is_same_site(&$restore) {
318 global $CFG;
319 static $hashedsiteid = null;
320 if (is_null($hashedsiteid)) {
321 $hashedsiteid = md5(get_site_identifier());
323 if (!empty($restore->original_siteidentifier)) {
324 return $restore->original_siteidentifier == $hashedsiteid;
325 } else {
326 return $restore->original_wwwroot == $CFG->wwwroot;
330 //This function is used to insert records in the backup_ids table
331 //If the info field is greater than max_db_storage, then its info
332 //is saved to filesystem
333 function backup_putid($backup_unique_code, $table, $old_id, $new_id, $info="") {
334 global $CFG, $DB;
336 $max_db_storage = 128; //Max bytes to save to db, else save to file
338 $status = true;
340 //First delete to avoid PK duplicates
341 $status = backup_delid($backup_unique_code, $table, $old_id);
343 //Now, serialize info
344 $info_ser = serialize($info);
346 //Now, if the size of $info_ser > $max_db_storage, save it to filesystem and
347 //insert a "infile" in the info field
349 if (strlen($info_ser) > $max_db_storage) {
350 //Calculate filename (in current_backup_dir, $backup_unique_code_$table_$old_id.info)
351 $filename = $CFG->dataroot."/temp/backup/".$backup_unique_code."/".$backup_unique_code."_".$table."_".$old_id.".info";
352 //Save data to file
353 $status = backup_data2file($filename,$info_ser);
354 //Set info_to save
355 $info_to_save = "infile";
356 } else {
357 //Saving to db
358 $info_to_save = $info_ser;
361 //Now, insert the record
362 if ($status) {
363 //Build the record
364 $rec = new stdClass();
365 $rec->backup_code = $backup_unique_code;
366 $rec->table_name = $table;
367 $rec->old_id = $old_id;
368 $rec->new_id = ($new_id === null? 0 : $new_id);
369 $rec->info = $info_to_save;
371 $DB->insert_record('backup_ids', $rec, false);
373 return $status;
376 //This function is used to delete recods from the backup_ids table
377 //If the info field is "infile" then the file is deleted too
378 function backup_delid ($backup_unique_code, $table, $old_id) {
379 global $DB;
380 return $DB->delete_records('backup_ids', array('backup_code'=>$backup_unique_code, 'table_name'=>$table, 'old_id'=>$old_id));
383 //This function is used to get a record from the backup_ids table
384 //If the info field is "infile" then its info
385 //is read from filesystem
386 function backup_getid ($backup_unique_code, $table, $old_id) {
387 global $CFG, $DB;
389 $status = true;
390 $status2 = true;
392 $status = $DB->get_record("backup_ids", array("backup_code"=>$backup_unique_code,
393 "table_name"=>$table, "old_id"=>$old_id));
395 //If info field = "infile", get file contents
396 if (!empty($status->info) && $status->info == "infile") {
397 $filename = $CFG->dataroot."/temp/backup/".$backup_unique_code."/".$backup_unique_code."_".$table."_".$old_id.".info";
398 //Read data from file
399 $status2 = backup_file2data($filename,$info);
400 if ($status2) {
401 //unserialize data
402 $status->info = unserialize($info);
403 } else {
404 $status = false;
406 } else {
407 //Only if status (record exists)
408 if (!empty($status->info)) {
409 if ($status->info === 'needed') {
410 // TODO: ugly hack - fix before 1.9.1
411 debugging('Incorrect string "needed" in $status->info, please fix the code (table:'.$table.'; old_id:'.$old_id.').', DEBUG_DEVELOPER);
412 } else {
413 ////First strip slashes
414 $temp = $status->info;
415 //Now unserialize
416 $status->info = unserialize($temp);
421 return $status;
424 //This function is used to add slashes (and decode from UTF-8 if needed)
425 //It's used intensivelly when restoring modules and saving them in db
426 function backup_todb ($data) {
427 // MDL-10770
428 if ($data === '$@NULL@$') {
429 return null;
430 } else {
431 return restore_decode_absolute_links($data);
435 //This function is used to check that every necessary function to
436 //backup/restore exists in the current php installation. Thanks to
437 //gregb@crowncollege.edu by the idea.
438 function backup_required_functions($justcheck=false) {
440 if(!function_exists('utf8_encode')) {
441 if (empty($justcheck)) {
442 print_error('needphpext', '', '', 'XML');
443 } else {
444 return false;
448 return true;
451 //This function send n white characters to the browser and flush the
452 //output buffer. Used to avoid browser timeouts and to show the progress.
453 function backup_flush($n=0,$time=false) {
454 if (defined('RESTORE_SILENTLY_NOFLUSH')) {
455 return;
457 if ($time) {
458 $ti = strftime("%X",time());
459 } else {
460 $ti = "";
462 echo str_repeat(" ", $n) . $ti . "\n";
463 flush();
466 //This function creates the filename and write data to it
467 //returning status as result
468 function backup_data2file ($file,&$data) {
470 $status = true;
471 $status2 = true;
473 $f = fopen($file,"w");
474 $status = fwrite($f,$data);
475 $status2 = fclose($f);
477 return ($status && $status2);
480 //This function read the filename and read data from it
481 function backup_file2data ($file,&$data) {
483 $status = true;
484 $status2 = true;
486 $f = fopen($file,"r");
487 $data = fread ($f,filesize($file));
488 $status2 = fclose($f);
490 return ($status && $status2);
493 function add_to_backup_log($starttime,$courseid,$message, $backuptype) {
494 global $DB;
495 $log = new stdClass();
496 $log->courseid = $courseid;
497 $log->time = time();
498 $log->laststarttime = $starttime;
499 $log->info = $message;
500 $log->backuptype = $backuptype;
501 $DB->insert_record('backup_log', $log);