3 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
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
26 * Base abstract class for all the helper classes providing various operations
28 * TODO: Finish phpdocs
30 abstract class backup_helper {
33 * Given one backupid, create all the needed dirs to have one backup temp dir available
35 static public function check_and_create_backup_dir($backupid) {
37 if (!check_dir_exists($CFG->dataroot . '/temp/backup/' . $backupid, true, true)) {
38 throw new backup_helper_exception('cannot_create_backup_temp_dir');
43 * Given one backupid, ensure its temp dir is completely empty
45 static public function clear_backup_dir($backupid) {
47 if (!self::delete_dir_contents($CFG->dataroot . '/temp/backup/' . $backupid)) {
48 throw new backup_helper_exception('cannot_empty_backup_temp_dir');
54 * Given one backupid, delete completely its temp dir
56 static public function delete_backup_dir($backupid) {
58 self::clear_backup_dir($backupid);
59 return rmdir($CFG->dataroot . '/temp/backup/' . $backupid);
63 * Given one fullpath to directory, delete its contents recursively
64 * Copied originally from somewhere in the net.
65 * TODO: Modernise this
67 static public function delete_dir_contents($dir, $excludeddir='') {
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
76 // Create arrays to store files and directories
78 $dir_subdirs = array();
80 // Make sure we can delete it
83 if ((($handle = opendir($dir))) == false) {
84 // The directory could not be opened
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;
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) {
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) {
112 if (remove_dir($dir_subdirs[$i]) == false) {
121 // Success, every thing is gone return true
126 * Delete all the temp dirs older than the time specified
128 static public function delete_old_backup_dirs($deletefrom) {
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 $status = self::delete_backup_dir($file_path);
148 throw new backup_helper_exception('problem_deleting_old_backup_temp_dirs');
153 * This function will be invoked by any log() method in backup/restore, acting
154 * as a simple forwarder to the standard loggers but also, if the $display
155 * parameter is true, supporting translation via get_string() and sending to
158 static public function log($message, $level, $a, $depth, $display, $logger) {
159 // Send to standard loggers
160 $logmessage = $message;
161 $options = empty($depth) ? array() : array('depth' => $depth);
163 $logmessage = $logmessage . ' ' . implode(', ', (array)$a);
165 $logger->process($logmessage, $level, $options);
167 // If $display specified, send translated string to output_controller
169 output_controller::get_instance()->output($message, 'backup', $a, $depth);
174 * Given one backupid and the (FS) final generated file, perform its final storage
175 * into Moodle file storage
177 static public function store_backup_file($backupid, $filepath) {
179 // First of all, get some information from the backup_controller to help us decide
180 list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($backupid);
182 // Extract useful information to decide
183 $hasusers = (bool)$sinfo['users']->value; // Backup has users
184 $isannon = (bool)$sinfo['anonymize']->value; // Backup is annonymzed
185 $backupmode= $dinfo[0]->mode; // Backup mode backup::MODE_GENERAL/IMPORT/HUB
186 $backuptype= $dinfo[0]->type; // Backup type backup::TYPE_1ACTIVITY/SECTION/COURSE
187 $userid = $dinfo[0]->userid; // User->id executing the backup
188 $id = $dinfo[0]->id; // Id of activity/section/course (depends of type)
189 $courseid = $dinfo[0]->courseid; // Id of the course
191 // Backups of type IMPORT aren't stored ever
192 if ($backupmode == backup::MODE_IMPORT) {
196 // Calculate file storage options of id being backup
200 switch ($backuptype) {
201 case backup::TYPE_1ACTIVITY:
202 $ctxid = get_context_instance(CONTEXT_MODULE, $id)->id;
203 $filearea = 'activity_backup';
206 case backup::TYPE_1SECTION:
207 $ctxid = get_context_instance(CONTEXT_COURSE, $courseid)->id;
208 $filearea = 'section_backup';
211 case backup::TYPE_1COURSE:
212 $ctxid = get_context_instance(CONTEXT_COURSE, $courseid)->id;
213 $filearea = 'course_backup';
218 // Backups of type HUB (by definition never have user info)
219 // are sent to user's "user_tohub" file area. The upload process
220 // will be responsible for cleaning that filearea once finished
221 if ($backupmode == backup::MODE_HUB) {
222 $ctxid = get_context_instance(CONTEXT_USER, $userid)->id;
223 $filearea = 'user_tohub';
227 // Backups without user info are sent to user's "user_backup"
228 // file area. Maintenance of such area is responsibility of
229 // the user via corresponding file manager frontend
230 if ($backupmode == backup::MODE_GENERAL && !$hasusers) {
231 $ctxid = get_context_instance(CONTEXT_USER, $userid)->id;
232 $filearea = 'user_backup';
236 // Let's send the file to file storage, everything already defined
237 $fs = get_file_storage();
239 'contextid' => $ctxid,
240 'filearea' => $filearea,
243 'filename' => basename($filepath),
245 'timecreated' => time(),
246 'timemodified'=> time());
247 // If file already exists, delete if before
248 // creating it again. This is BC behaviour - copy()
249 // overwrites by default
250 if ($fs->file_exists($fr['contextid'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename'])) {
251 $pathnamehash = $fs->get_pathname_hash($fr['contextid'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename']);
252 $sf = $fs->get_file_by_hash($pathnamehash);
255 return $fs->create_file_from_pathname($fr, $filepath);
260 * Exception class used by all the @helper stuff
262 class backup_helper_exception extends backup_exception {
264 public function __construct($errorcode, $a=NULL, $debuginfo=null) {
265 parent::__construct($errorcode, $a, $debuginfo);