MDL-21915 fixing remaining chmod and mkdir to use moodle file permissions
[moodle.git] / backup / util / helper / backup_helper.class.php
CommitLineData
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 */
30abstract 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 */
298class backup_helper_exception extends backup_exception {
299
300 public function __construct($errorcode, $a=NULL, $debuginfo=null) {
301 parent::__construct($errorcode, $a, $debuginfo);
302 }
303}