11fb7f4348acd2125761d8a843daf84e9696d46d
[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 completelly 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     }
52     /**
53      * Given one fullpath to directory, delete its contents recursively
54      * Copied originally from somewhere in the net.
55      * TODO: Modernise this
56      */
57     static public function delete_dir_contents($dir, $excludeddir='') {
58         if (!is_dir($dir)) {
59             // if we've been given a directory that doesn't exist yet, return true.
60             // this happens when we're trying to clear out a course that has only just
61             // been created.
62             return true;
63         }
64         $slash = "/";
66         // Create arrays to store files and directories
67         $dir_files      = array();
68         $dir_subdirs    = array();
70         // Make sure we can delete it
71         chmod($dir, 0777);
73         if ((($handle = opendir($dir))) == false) {
74             // The directory could not be opened
75             return false;
76         }
78         // Loop through all directory entries, and construct two temporary arrays containing files and sub directories
79         while (false !== ($entry = readdir($handle))) {
80             if (is_dir($dir. $slash .$entry) && $entry != ".." && $entry != "." && $entry != $excludeddir) {
81                 $dir_subdirs[] = $dir. $slash .$entry;
83             } else if ($entry != ".." && $entry != "." && $entry != $excludeddir) {
84                 $dir_files[] = $dir. $slash .$entry;
85             }
86         }
88         // Delete all files in the curent directory return false and halt if a file cannot be removed
89         for ($i=0; $i<count($dir_files); $i++) {
90             chmod($dir_files[$i], 0777);
91             if (((unlink($dir_files[$i]))) == false) {
92                 return false;
93             }
94         }
96         // Empty sub directories and then remove the directory
97         for ($i=0; $i<count($dir_subdirs); $i++) {
98             chmod($dir_subdirs[$i], 0777);
99             if (self::delete_dir_contents($dir_subdirs[$i]) == false) {
100                 return false;
101             } else {
102                 if (remove_dir($dir_subdirs[$i]) == false) {
103                     return false;
104                 }
105             }
106         }
108         // Close directory
109         closedir($handle);
111         // Success, every thing is gone return true
112         return true;
113     }
115     /**
116      * Delete all the temp dirs older than the time specified
117      */
118     static public function delete_old_backup_dirs($deletefrom) {
119         global $CFG;
121         $status = true;
122         // Get files and directories in the temp backup dir witout descend
123         $list = get_directory_list($CFG->dataroot . '/temp/backup', '', false, true, true);
124         foreach ($list as $file) {
125             $file_path = $CFG->dataroot . '/temp/backup/' . $file;
126             $moddate = filemtime($file_path);
127             if ($status && $moddate < $deletefrom) {
128                 //If directory, recurse
129                 if (is_dir($file_path)) {
130                     $status = self::delete_dir_contents($file_path);
131                     //There is nothing, delete the directory itself
132                     if ($status) {
133                         $status = rmdir($file_path);
134                     }
135                 //If file
136                 } else {
137                     unlink($file_path);
138                 }
139             }
140         }
141         if (!$status) {
142             throw new backup_helper_exception('problem_deleting_old_backup_temp_dirs');
143         }
144     }
146     /**
147      * This function will be invoked by any log() method in backup/restore, acting
148      * as a simple forwarder to the standard loggers but also, if the $display
149      * parameter is true, supporting translation via get_string() and sending to
150      * standard output.
151      */
152     static public function log($message, $level, $a, $depth, $display, $logger) {
153         // Send to standard loggers
154         $logmessage = $message;
155         $options = empty($depth) ? array() : array('depth' => $depth);
156         if (!empty($a)) {
157             $logmessage = $logmessage . ' ' . implode(', ', (array)$a);
158         }
159         $logger->process($logmessage, $level, $options);
161         // If $display specified, send translated string to output_controller
162         if ($display) {
163             output_controller::get_instance()->output($message, 'backup', $a, $depth);
164         }
165     }
167     /**
168      * Given one backupid and the (FS) final generated file, perform its final storage
169      * into Moodle file storage
170      */
171     static public function store_backup_file($backupid, $filepath) {
173         // First of all, get some information from the backup_controller to help us decide
174         list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($backupid);
176         // Extract useful information to decide
177         $hasusers  = (bool)$sinfo['users']->value;     // Backup has users
178         $isannon   = (bool)$sinfo['anonymize']->value; // Backup is annonymzed
179         $backupmode= $dinfo[0]->mode;                  // Backup mode backup::MODE_GENERAL/IMPORT/HUB
180         $backuptype= $dinfo[0]->type;                  // Backup type backup::TYPE_1ACTIVITY/SECTION/COURSE
181         $userid    = $dinfo[0]->userid;                // User->id executing the backup
182         $id        = $dinfo[0]->id;                    // Id of activity/section/course (depends of type)
183         $courseid  = $dinfo[0]->courseid;              // Id of the course
185         // Backups of type IMPORT aren't stored ever
186         if ($backupmode == backup::MODE_IMPORT) {
187             return true;
188         }
190         // Calculate file storage options of id being backup
191         $ctxid    = 0;
192         $filearea = '';
193         $itemid   = 0;
194         switch ($backuptype) {
195             case backup::TYPE_1ACTIVITY:
196                 $ctxid    = get_context_instance(CONTEXT_MODULE, $id)->id;
197                 $filearea = 'activity_backup';
198                 $itemid   = 0;
199                 break;
200             case backup::TYPE_1SECTION:
201                 $ctxid    = get_context_instance(CONTEXT_COURSE, $courseid)->id;
202                 $filearea = 'section_backup';
203                 $itemid   = $id;
204                 break;
205             case backup::TYPE_1COURSE:
206                 $ctxid    = get_context_instance(CONTEXT_COURSE, $courseid)->id;
207                 $filearea = 'course_backup';
208                 $itemid   = 0;
209                 break;
210         }
212         // Backups of type HUB (by definition never have user info)
213         // are sent to user's "user_tohub" file area. The upload process
214         // will be responsible for cleaning that filearea once finished
215         if ($backupmode == backup::MODE_HUB) {
216             $ctxid = get_context_instance(CONTEXT_USER, $userid)->id;
217             $filearea = 'user_tohub';
218             $itemid   = 0;
219         }
221         // Backups without user info are sent to user's "user_backup"
222         // file area. Maintenance of such area is responsibility of
223         // the user via corresponding file manager frontend
224         if ($backupmode == backup::MODE_GENERAL && !$hasusers) {
225             $ctxid = get_context_instance(CONTEXT_USER, $userid)->id;
226             $filearea = 'user_backup';
227             $itemid   = 0;
228         }
230         // Let's send the file to file storage, everything already defined
231         $fs = get_file_storage();
232         $fr = array(
233             'contextid'   => $ctxid,
234             'filearea'    => $filearea,
235             'itemid'      => $itemid,
236             'filepath'    => '/',
237             'filename'    => basename($filepath),
238             'userid'      => $userid,
239             'timecreated' => time(),
240             'timemodified'=> time());
241         // If file already exists, delete if before
242         // creating it again. This is BC behaviour - copy()
243         // overwrites by default
244         if ($fs->file_exists($fr['contextid'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename'])) {
245             $pathnamehash = $fs->get_pathname_hash($fr['contextid'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename']);
246             $sf = $fs->get_file_by_hash($pathnamehash);
247             $sf->delete();
248         }
249         return $fs->create_file_from_pathname($fr, $filepath);
250     }
253 /*
254  * Exception class used by all the @helper stuff
255  */
256 class backup_helper_exception extends backup_exception {
258     public function __construct($errorcode, $a=NULL, $debuginfo=null) {
259         parent::__construct($errorcode, $a, $debuginfo);
260     }