MDL-25637 Fixed whitespace
[moodle.git] / backup / lib.php
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');
17     }
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;
23     }
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));
41         }
42         //Now, delete old directory (if exists)
43         if ($status) {
44             $status = backup_delete_old_dirs($delete_from);
45         }
46         return($status);
47     }
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);
68                     }
69                 //If file
70                 } else {
71                     unlink("$file_path");
72                 }
73             }
74         }
76         return $status;
77     }
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);
87         }
88         if ($status) {
89             $status = check_dir_exists($CFG->dataroot."/temp/backup/".$backup_unique_code,true);
90         }
92         return $status;
93     }
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;
106         }
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;
119         }
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;
125             }
126             else if ($entry != ".." && $entry != "." && $entry != $excludeddir) {
127                 $dir_files[] = $dir. $slash .$entry;
128             }
129         }
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;
137             }
138         }
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;
146             }
147             else {
148                 if (remove_dir($dir_subdirs[$i]) == FALSE) {
149                 return false;
150                 }
151             }
152         }
154         // Close directory
155         closedir($handle);
157         // Success, every thing is gone return true
158         return true;
159     }
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;
171     }
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;
186         }
187         return $name;
188     }
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;
200             }
201             if (is_dir($rootdir."/".$file)) {
202                 $results[$file] = $file;
203             }
204         }
205         closedir($dir);
206         return $results;
207     }
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;
219             }
220             $results[$file] = $file;
221         }
222         closedir($dir);
223         return $results;
224     }
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);
246                 }
247             }
248         }
249         return $status;
250     }
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);
272                 }
273                 return true;
274             }
275             return false;
276         }
277         else if (is_dir($from_file)) {
278             return backup_copy_dir($from_file,$to_file);
279         }
280         else{
281             //echo "<br />Error: not file or dir ".$from_file;               //Debug
282             return false;
283         }
284     }
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);
295         }
296         $dir = opendir($from_file);
297         while (false !== ($file=readdir($dir))) {
298             if ($file=="." || $file=="..") {
299                 continue;
300             }
301             $status = backup_copy_file ("$from_file/$file","$to_file/$file");
302         }
303         closedir($dir);
304         return $status;
305     }
306     ///Ends copy file/dirs functions
307     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
308     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
310     /**
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.
316      */
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());
322         }
323         if (!empty($restore->original_siteidentifier)) {
324             return $restore->original_siteidentifier == $hashedsiteid;
325         } else {
326             return $restore->original_wwwroot == $CFG->wwwroot;
327         }
328     }
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;
359         }
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);
372         }
373         return $status;
374     }
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));
381     }
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;
405             }
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);
417                 }
418             }
419         }
421         return $status;
422     }
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);
432         }
433     }
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;
445             }
446         }
448         return true;
449     }
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;
456         }
457         if ($time) {
458             $ti = strftime("%X",time());
459         } else {
460             $ti = "";
461         }
462         echo str_repeat(" ", $n) . $ti . "\n";
463         flush();
464     }
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);
478     }
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);
491     }
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);
502     }