Commit | Line | Data |
---|---|---|
69dd0c8c EL |
1 | <?php |
2 | ||
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/>. | |
17 | ||
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 | */ | |
24 | ||
25 | /** | |
26 | * Base abstract class for all the helper classes providing various operations | |
27 | * | |
28 | * TODO: Finish phpdocs | |
29 | */ | |
30 | abstract class backup_helper { | |
31 | ||
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 | } | |
41 | ||
42 | /** | |
2de3539b | 43 | * Given one backupid, ensure its temp dir is completely empty |
69dd0c8c EL |
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 | } | |
2de3539b | 50 | return true; |
69dd0c8c EL |
51 | } |
52 | ||
2de3539b EL |
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 | } | |
61 | ||
2589eb89 | 62 | /** |
69dd0c8c EL |
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='') { | |
4756e9c9 PS |
68 | global $CFG; |
69 | ||
69dd0c8c EL |
70 | if (!is_dir($dir)) { |
71 | // if we've been given a directory that doesn't exist yet, return true. | |
72 | // this happens when we're trying to clear out a course that has only just | |
73 | // been created. | |
74 | return true; | |
75 | } | |
76 | $slash = "/"; | |
77 | ||
78 | // Create arrays to store files and directories | |
79 | $dir_files = array(); | |
80 | $dir_subdirs = array(); | |
81 | ||
82 | // Make sure we can delete it | |
4756e9c9 | 83 | chmod($dir, $CFG->directorypermissions); |
69dd0c8c EL |
84 | |
85 | if ((($handle = opendir($dir))) == false) { | |
86 | // The directory could not be opened | |
87 | return false; | |
88 | } | |
89 | ||
90 | // Loop through all directory entries, and construct two temporary arrays containing files and sub directories | |
91 | while (false !== ($entry = readdir($handle))) { | |
92 | if (is_dir($dir. $slash .$entry) && $entry != ".." && $entry != "." && $entry != $excludeddir) { | |
93 | $dir_subdirs[] = $dir. $slash .$entry; | |
94 | ||
95 | } else if ($entry != ".." && $entry != "." && $entry != $excludeddir) { | |
96 | $dir_files[] = $dir. $slash .$entry; | |
97 | } | |
98 | } | |
99 | ||
100 | // Delete all files in the curent directory return false and halt if a file cannot be removed | |
101 | for ($i=0; $i<count($dir_files); $i++) { | |
4756e9c9 | 102 | chmod($dir_files[$i], $CFG->directorypermissions); |
69dd0c8c EL |
103 | if (((unlink($dir_files[$i]))) == false) { |
104 | return false; | |
105 | } | |
106 | } | |
107 | ||
108 | // Empty sub directories and then remove the directory | |
109 | for ($i=0; $i<count($dir_subdirs); $i++) { | |
4756e9c9 | 110 | chmod($dir_subdirs[$i], $CFG->directorypermissions); |
69dd0c8c EL |
111 | if (self::delete_dir_contents($dir_subdirs[$i]) == false) { |
112 | return false; | |
113 | } else { | |
114 | if (remove_dir($dir_subdirs[$i]) == false) { | |
115 | return false; | |
116 | } | |
117 | } | |
118 | } | |
119 | ||
120 | // Close directory | |
121 | closedir($handle); | |
122 | ||
123 | // Success, every thing is gone return true | |
124 | return true; | |
125 | } | |
126 | ||
127 | /** | |
128 | * Delete all the temp dirs older than the time specified | |
129 | */ | |
130 | static public function delete_old_backup_dirs($deletefrom) { | |
131 | global $CFG; | |
132 | ||
133 | $status = true; | |
134 | // Get files and directories in the temp backup dir witout descend | |
135 | $list = get_directory_list($CFG->dataroot . '/temp/backup', '', false, true, true); | |
136 | foreach ($list as $file) { | |
137 | $file_path = $CFG->dataroot . '/temp/backup/' . $file; | |
138 | $moddate = filemtime($file_path); | |
139 | if ($status && $moddate < $deletefrom) { | |
140 | //If directory, recurse | |
141 | if (is_dir($file_path)) { | |
2589eb89 SH |
142 | // $file is really the backupid |
143 | $status = self::delete_backup_dir($file); | |
69dd0c8c EL |
144 | //If file |
145 | } else { | |
146 | unlink($file_path); | |
147 | } | |
148 | } | |
149 | } | |
150 | if (!$status) { | |
151 | throw new backup_helper_exception('problem_deleting_old_backup_temp_dirs'); | |
152 | } | |
153 | } | |
154 | ||
155 | /** | |
156 | * This function will be invoked by any log() method in backup/restore, acting | |
157 | * as a simple forwarder to the standard loggers but also, if the $display | |
158 | * parameter is true, supporting translation via get_string() and sending to | |
159 | * standard output. | |
160 | */ | |
161 | static public function log($message, $level, $a, $depth, $display, $logger) { | |
162 | // Send to standard loggers | |
163 | $logmessage = $message; | |
164 | $options = empty($depth) ? array() : array('depth' => $depth); | |
165 | if (!empty($a)) { | |
166 | $logmessage = $logmessage . ' ' . implode(', ', (array)$a); | |
167 | } | |
168 | $logger->process($logmessage, $level, $options); | |
169 | ||
170 | // If $display specified, send translated string to output_controller | |
171 | if ($display) { | |
172 | output_controller::get_instance()->output($message, 'backup', $a, $depth); | |
173 | } | |
174 | } | |
ce937f99 EL |
175 | |
176 | /** | |
177 | * Given one backupid and the (FS) final generated file, perform its final storage | |
dc1e4cce | 178 | * into Moodle file storage. For stored files it returns the complete file_info object |
ce937f99 EL |
179 | */ |
180 | static public function store_backup_file($backupid, $filepath) { | |
181 | ||
182 | // First of all, get some information from the backup_controller to help us decide | |
183 | list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($backupid); | |
184 | ||
185 | // Extract useful information to decide | |
186 | $hasusers = (bool)$sinfo['users']->value; // Backup has users | |
64f93798 | 187 | $isannon = (bool)$sinfo['anonymize']->value; // Backup is anonymised |
9eeaea5f | 188 | $filename = $sinfo['filename']->value; // Backup filename |
ce937f99 | 189 | $backupmode= $dinfo[0]->mode; // Backup mode backup::MODE_GENERAL/IMPORT/HUB |
cd0034d8 | 190 | $backuptype= $dinfo[0]->type; // Backup type backup::TYPE_1ACTIVITY/SECTION/COURSE |
ce937f99 | 191 | $userid = $dinfo[0]->userid; // User->id executing the backup |
cd0034d8 EL |
192 | $id = $dinfo[0]->id; // Id of activity/section/course (depends of type) |
193 | $courseid = $dinfo[0]->courseid; // Id of the course | |
ce937f99 | 194 | |
cf10078d | 195 | // Quick hack. If for any reason, filename is blank, fix it here. |
dc1e4cce | 196 | // TODO: This hack will be out once MDL-22142 - P26 gets fixed |
cf10078d EL |
197 | if (empty($filename)) { |
198 | $filename = backup_plan_dbops::get_default_backup_filename('moodle2', $backuptype, $id, $hasusers, $isannon); | |
199 | } | |
200 | ||
ce937f99 EL |
201 | // Backups of type IMPORT aren't stored ever |
202 | if ($backupmode == backup::MODE_IMPORT) { | |
dc1e4cce | 203 | return false; |
ce937f99 EL |
204 | } |
205 | ||
cd0034d8 | 206 | // Calculate file storage options of id being backup |
64f93798 PS |
207 | $ctxid = 0; |
208 | $filearea = ''; | |
209 | $component = ''; | |
210 | $itemid = 0; | |
cd0034d8 EL |
211 | switch ($backuptype) { |
212 | case backup::TYPE_1ACTIVITY: | |
64f93798 PS |
213 | $ctxid = get_context_instance(CONTEXT_MODULE, $id)->id; |
214 | $component = 'backup'; | |
215 | $filearea = 'activity'; | |
216 | $itemid = 0; | |
cd0034d8 EL |
217 | break; |
218 | case backup::TYPE_1SECTION: | |
64f93798 PS |
219 | $ctxid = get_context_instance(CONTEXT_COURSE, $courseid)->id; |
220 | $component = 'backup'; | |
221 | $filearea = 'section'; | |
222 | $itemid = $id; | |
cd0034d8 EL |
223 | break; |
224 | case backup::TYPE_1COURSE: | |
64f93798 PS |
225 | $ctxid = get_context_instance(CONTEXT_COURSE, $courseid)->id; |
226 | $component = 'backup'; | |
227 | $filearea = 'course'; | |
228 | $itemid = 0; | |
cd0034d8 EL |
229 | break; |
230 | } | |
231 | ||
ce937f99 EL |
232 | // Backups of type HUB (by definition never have user info) |
233 | // are sent to user's "user_tohub" file area. The upload process | |
234 | // will be responsible for cleaning that filearea once finished | |
235 | if ($backupmode == backup::MODE_HUB) { | |
64f93798 PS |
236 | $ctxid = get_context_instance(CONTEXT_USER, $userid)->id; |
237 | $component = 'user'; | |
238 | $filearea = 'tohub'; | |
239 | $itemid = 0; | |
cd0034d8 EL |
240 | } |
241 | ||
64f93798 | 242 | // Backups without user info or with the anonymise functionality |
1386bc09 | 243 | // enabled are sent to user's "user_backup" |
cd0034d8 EL |
244 | // file area. Maintenance of such area is responsibility of |
245 | // the user via corresponding file manager frontend | |
1386bc09 | 246 | if ($backupmode == backup::MODE_GENERAL && (!$hasusers || $isannon)) { |
64f93798 PS |
247 | $ctxid = get_context_instance(CONTEXT_USER, $userid)->id; |
248 | $component = 'user'; | |
249 | $filearea = 'backup'; | |
250 | $itemid = 0; | |
cd0034d8 EL |
251 | } |
252 | ||
253 | // Let's send the file to file storage, everything already defined | |
254 | $fs = get_file_storage(); | |
255 | $fr = array( | |
256 | 'contextid' => $ctxid, | |
64f93798 | 257 | 'component' => $component, |
cd0034d8 EL |
258 | 'filearea' => $filearea, |
259 | 'itemid' => $itemid, | |
260 | 'filepath' => '/', | |
9eeaea5f | 261 | 'filename' => $filename, |
cd0034d8 EL |
262 | 'userid' => $userid, |
263 | 'timecreated' => time(), | |
264 | 'timemodified'=> time()); | |
265 | // If file already exists, delete if before | |
266 | // creating it again. This is BC behaviour - copy() | |
267 | // overwrites by default | |
64f93798 PS |
268 | if ($fs->file_exists($fr['contextid'], $fr['component'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename'])) { |
269 | $pathnamehash = $fs->get_pathname_hash($fr['contextid'], $fr['component'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename']); | |
cd0034d8 EL |
270 | $sf = $fs->get_file_by_hash($pathnamehash); |
271 | $sf->delete(); | |
ce937f99 | 272 | } |
cd0034d8 | 273 | return $fs->create_file_from_pathname($fr, $filepath); |
ce937f99 | 274 | } |
c0bd6249 EL |
275 | |
276 | /** | |
277 | * This function simply marks one param to be considered as straight sql | |
278 | * param, so it won't be searched in the structure tree nor converted at | |
279 | * all. Useful for better integration of definition of sources in structure | |
280 | * and DB stuff | |
281 | */ | |
282 | public static function is_sqlparam($value) { | |
283 | return array('sqlparam' => $value); | |
284 | } | |
482aac65 EL |
285 | |
286 | /** | |
287 | * This function returns one array of itemnames that are being handled by | |
288 | * inforef.xml files. Used both by backup and restore | |
289 | */ | |
290 | public static function get_inforef_itemnames() { | |
767cb7f0 | 291 | return array('user', 'grouping', 'group', 'role', 'file', 'scale', 'outcome', 'grade_item', 'question_category'); |
482aac65 | 292 | } |
69dd0c8c EL |
293 | } |
294 | ||
295 | /* | |
296 | * Exception class used by all the @helper stuff | |
297 | */ | |
298 | class backup_helper_exception extends backup_exception { | |
299 | ||
300 | public function __construct($errorcode, $a=NULL, $debuginfo=null) { | |
301 | parent::__construct($errorcode, $a, $debuginfo); | |
302 | } | |
303 | } |