78d9acf4c47a9b2eb98bbe1392addce0c4bff506
[moodle.git] / backup / util / helper / backup_helper.class.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * @package    moodlecore
20  * @subpackage backup-helper
21  * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 /**
26  * Base abstract class for all the helper classes providing various operations
27  *
28  * TODO: Finish phpdocs
29  */
30 abstract class backup_helper {
32     /**
33      * Given one backupid, create all the needed dirs to have one backup temp dir available
34      */
35     static public function check_and_create_backup_dir($backupid) {
36         global $CFG;
37         if (!check_dir_exists($CFG->dataroot . '/temp/backup/' . $backupid, true, true)) {
38             throw new backup_helper_exception('cannot_create_backup_temp_dir');
39         }
40     }
42     /**
43      * Given one backupid, ensure its temp dir is completely empty
44      */
45     static public function clear_backup_dir($backupid) {
46         global $CFG;
47         if (!self::delete_dir_contents($CFG->dataroot . '/temp/backup/' . $backupid)) {
48             throw new backup_helper_exception('cannot_empty_backup_temp_dir');
49         }
50         return true;
51     }
53     /**
54      * Given one backupid, delete completely its temp dir
55      */
56      static public function delete_backup_dir($backupid) {
57          global $CFG;
58          self::clear_backup_dir($backupid);
59          return rmdir($CFG->dataroot . '/temp/backup/' . $backupid);
60      }
62      /**
63      * Given one fullpath to directory, delete its contents recursively
64      * Copied originally from somewhere in the net.
65      * TODO: Modernise this
66      */
67     static public function delete_dir_contents($dir, $excludeddir='') {
68         if (!is_dir($dir)) {
69             // if we've been given a directory that doesn't exist yet, return true.
70             // this happens when we're trying to clear out a course that has only just
71             // been created.
72             return true;
73         }
74         $slash = "/";
76         // Create arrays to store files and directories
77         $dir_files      = array();
78         $dir_subdirs    = array();
80         // Make sure we can delete it
81         chmod($dir, 0777);
83         if ((($handle = opendir($dir))) == false) {
84             // The directory could not be opened
85             return false;
86         }
88         // Loop through all directory entries, and construct two temporary arrays containing files and sub directories
89         while (false !== ($entry = readdir($handle))) {
90             if (is_dir($dir. $slash .$entry) && $entry != ".." && $entry != "." && $entry != $excludeddir) {
91                 $dir_subdirs[] = $dir. $slash .$entry;
93             } else if ($entry != ".." && $entry != "." && $entry != $excludeddir) {
94                 $dir_files[] = $dir. $slash .$entry;
95             }
96         }
98         // Delete all files in the curent directory return false and halt if a file cannot be removed
99         for ($i=0; $i<count($dir_files); $i++) {
100             chmod($dir_files[$i], 0777);
101             if (((unlink($dir_files[$i]))) == false) {
102                 return false;
103             }
104         }
106         // Empty sub directories and then remove the directory
107         for ($i=0; $i<count($dir_subdirs); $i++) {
108             chmod($dir_subdirs[$i], 0777);
109             if (self::delete_dir_contents($dir_subdirs[$i]) == false) {
110                 return false;
111             } else {
112                 if (remove_dir($dir_subdirs[$i]) == false) {
113                     return false;
114                 }
115             }
116         }
118         // Close directory
119         closedir($handle);
121         // Success, every thing is gone return true
122         return true;
123     }
125     /**
126      * Delete all the temp dirs older than the time specified
127      */
128     static public function delete_old_backup_dirs($deletefrom) {
129         global $CFG;
131         $status = true;
132         // Get files and directories in the temp backup dir witout descend
133         $list = get_directory_list($CFG->dataroot . '/temp/backup', '', false, true, true);
134         foreach ($list as $file) {
135             $file_path = $CFG->dataroot . '/temp/backup/' . $file;
136             $moddate = filemtime($file_path);
137             if ($status && $moddate < $deletefrom) {
138                 //If directory, recurse
139                 if (is_dir($file_path)) {
140                     // $file is really the backupid
141                     $status = self::delete_backup_dir($file);
142                 //If file
143                 } else {
144                     unlink($file_path);
145                 }
146             }
147         }
148         if (!$status) {
149             throw new backup_helper_exception('problem_deleting_old_backup_temp_dirs');
150         }
151     }
153     /**
154      * This function will be invoked by any log() method in backup/restore, acting
155      * as a simple forwarder to the standard loggers but also, if the $display
156      * parameter is true, supporting translation via get_string() and sending to
157      * standard output.
158      */
159     static public function log($message, $level, $a, $depth, $display, $logger) {
160         // Send to standard loggers
161         $logmessage = $message;
162         $options = empty($depth) ? array() : array('depth' => $depth);
163         if (!empty($a)) {
164             $logmessage = $logmessage . ' ' . implode(', ', (array)$a);
165         }
166         $logger->process($logmessage, $level, $options);
168         // If $display specified, send translated string to output_controller
169         if ($display) {
170             output_controller::get_instance()->output($message, 'backup', $a, $depth);
171         }
172     }
174     /**
175      * Given one backupid and the (FS) final generated file, perform its final storage
176      * into Moodle file storage. For stored files it returns the complete file_info object
177      */
178     static public function store_backup_file($backupid, $filepath) {
180         // First of all, get some information from the backup_controller to help us decide
181         list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($backupid);
183         // Extract useful information to decide
184         $hasusers  = (bool)$sinfo['users']->value;     // Backup has users
185         $isannon   = (bool)$sinfo['anonymize']->value; // Backup is anonymised
186         $filename  = $sinfo['filename']->value;        // Backup filename
187         $backupmode= $dinfo[0]->mode;                  // Backup mode backup::MODE_GENERAL/IMPORT/HUB
188         $backuptype= $dinfo[0]->type;                  // Backup type backup::TYPE_1ACTIVITY/SECTION/COURSE
189         $userid    = $dinfo[0]->userid;                // User->id executing the backup
190         $id        = $dinfo[0]->id;                    // Id of activity/section/course (depends of type)
191         $courseid  = $dinfo[0]->courseid;              // Id of the course
193         // Quick hack. If for any reason, filename is blank, fix it here.
194         // TODO: This hack will be out once MDL-22142 - P26 gets fixed
195         if (empty($filename)) {
196             $filename = backup_plan_dbops::get_default_backup_filename('moodle2', $backuptype, $id, $hasusers, $isannon);
197         }
199         // Backups of type IMPORT aren't stored ever
200         if ($backupmode == backup::MODE_IMPORT) {
201             return false;
202         }
204         // Calculate file storage options of id being backup
205         $ctxid     = 0;
206         $filearea  = '';
207         $component = '';
208         $itemid    = 0;
209         switch ($backuptype) {
210             case backup::TYPE_1ACTIVITY:
211                 $ctxid     = get_context_instance(CONTEXT_MODULE, $id)->id;
212                 $component = 'backup';
213                 $filearea  = 'activity';
214                 $itemid    = 0;
215                 break;
216             case backup::TYPE_1SECTION:
217                 $ctxid     = get_context_instance(CONTEXT_COURSE, $courseid)->id;
218                 $component = 'backup';
219                 $filearea  = 'section';
220                 $itemid    = $id;
221                 break;
222             case backup::TYPE_1COURSE:
223                 $ctxid     = get_context_instance(CONTEXT_COURSE, $courseid)->id;
224                 $component = 'backup';
225                 $filearea  = 'course';
226                 $itemid    = 0;
227                 break;
228         }
230         // Backups of type HUB (by definition never have user info)
231         // are sent to user's "user_tohub" file area. The upload process
232         // will be responsible for cleaning that filearea once finished
233         if ($backupmode == backup::MODE_HUB) {
234             $ctxid     = get_context_instance(CONTEXT_USER, $userid)->id;
235             $component = 'user';
236             $filearea  = 'tohub';
237             $itemid    = 0;
238         }
240         // Backups without user info or with the anonymise functionality
241         // enabled are sent to user's "user_backup"
242         // file area. Maintenance of such area is responsibility of
243         // the user via corresponding file manager frontend
244         if ($backupmode == backup::MODE_GENERAL && (!$hasusers || $isannon)) {
245             $ctxid     = get_context_instance(CONTEXT_USER, $userid)->id;
246             $component = 'user';
247             $filearea  = 'backup';
248             $itemid    = 0;
249         }
251         // Let's send the file to file storage, everything already defined
252         $fs = get_file_storage();
253         $fr = array(
254             'contextid'   => $ctxid,
255             'component'   => $component,
256             'filearea'    => $filearea,
257             'itemid'      => $itemid,
258             'filepath'    => '/',
259             'filename'    => $filename,
260             'userid'      => $userid,
261             'timecreated' => time(),
262             'timemodified'=> time());
263         // If file already exists, delete if before
264         // creating it again. This is BC behaviour - copy()
265         // overwrites by default
266         if ($fs->file_exists($fr['contextid'], $fr['component'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename'])) {
267             $pathnamehash = $fs->get_pathname_hash($fr['contextid'], $fr['component'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename']);
268             $sf = $fs->get_file_by_hash($pathnamehash);
269             $sf->delete();
270         }
271         return $fs->create_file_from_pathname($fr, $filepath);
272     }
274     /**
275      * This function simply marks one param to be considered as straight sql
276      * param, so it won't be searched in the structure tree nor converted at
277      * all. Useful for better integration of definition of sources in structure
278      * and DB stuff
279      */
280     public static function is_sqlparam($value) {
281         return array('sqlparam' => $value);
282     }
284     /**
285      * This function returns one array of itemnames that are being handled by
286      * inforef.xml files. Used both by backup and restore
287      */
288     public static function get_inforef_itemnames() {
289         return array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item', 'question_category');
290     }
293 /*
294  * Exception class used by all the @helper stuff
295  */
296 class backup_helper_exception extends backup_exception {
298     public function __construct($errorcode, $a=NULL, $debuginfo=null) {
299         parent::__construct($errorcode, $a, $debuginfo);
300     }