Commit | Line | Data |
---|---|---|
25aebf09 | 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 | /** | |
20 | * Core file storage class definition. | |
21 | * | |
64a19b38 | 22 | * @package moodlecore |
23 | * @subpackage file-storage | |
bf9ffe27 | 24 | * @copyright 2008 Petr Skoda {@link http://skodak.org} |
64a19b38 | 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
25aebf09 | 26 | */ |
172dd12c | 27 | |
28 | require_once("$CFG->libdir/file/stored_file.php"); | |
29 | ||
25aebf09 | 30 | /** |
31 | * File storage class used for low level access to stored files. | |
bf9ffe27 | 32 | * |
25aebf09 | 33 | * Only owner of file area may use this class to access own files, |
34 | * for example only code in mod/assignment/* may access assignment | |
bf9ffe27 PS |
35 | * attachments. When some other part of moodle needs to access |
36 | * files of modules it has to use file_browser class instead or there | |
37 | * has to be some callback API. | |
38 | * | |
39 | * @copyright 2008 Petr Skoda {@link http://skodak.org} | |
40 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
41 | * @since Moodle 2.0 | |
25aebf09 | 42 | */ |
172dd12c | 43 | class file_storage { |
bf9ffe27 | 44 | /** @var string Directory with file contents */ |
172dd12c | 45 | private $filedir; |
bf9ffe27 | 46 | /** @var string Contents of deleted files not needed any more */ |
1aa01caf | 47 | private $trashdir; |
3a1055a5 PS |
48 | /** @var string tempdir */ |
49 | private $tempdir; | |
bf9ffe27 | 50 | /** @var int Permissions for new directories */ |
1aa01caf | 51 | private $dirpermissions; |
bf9ffe27 | 52 | /** @var int Permissions for new files */ |
1aa01caf | 53 | private $filepermissions; |
bf9ffe27 | 54 | |
172dd12c | 55 | /** |
bf9ffe27 PS |
56 | * Constructor - do not use directly use @see get_file_storage() call instead. |
57 | * | |
172dd12c | 58 | * @param string $filedir full path to pool directory |
bf9ffe27 | 59 | * @param string $trashdir temporary storage of deleted area |
3a1055a5 | 60 | * @param string $tempdir temporary storage of various files |
bf9ffe27 PS |
61 | * @param int $dirpermissions new directory permissions |
62 | * @param int $filepermissions new file permissions | |
172dd12c | 63 | */ |
3a1055a5 | 64 | public function __construct($filedir, $trashdir, $tempdir, $dirpermissions, $filepermissions) { |
1aa01caf | 65 | $this->filedir = $filedir; |
66 | $this->trashdir = $trashdir; | |
3a1055a5 | 67 | $this->tempdir = $tempdir; |
1aa01caf | 68 | $this->dirpermissions = $dirpermissions; |
69 | $this->filepermissions = $filepermissions; | |
172dd12c | 70 | |
71 | // make sure the file pool directory exists | |
72 | if (!is_dir($this->filedir)) { | |
1aa01caf | 73 | if (!mkdir($this->filedir, $this->dirpermissions, true)) { |
145a0a31 | 74 | throw new file_exception('storedfilecannotcreatefiledirs'); // permission trouble |
172dd12c | 75 | } |
76 | // place warning file in file pool root | |
1aa01caf | 77 | if (!file_exists($this->filedir.'/warning.txt')) { |
78 | file_put_contents($this->filedir.'/warning.txt', | |
79 | 'This directory contains the content of uploaded files and is controlled by Moodle code. Do not manually move, change or rename any of the files and subdirectories here.'); | |
80 | } | |
81 | } | |
82 | // make sure the file pool directory exists | |
83 | if (!is_dir($this->trashdir)) { | |
84 | if (!mkdir($this->trashdir, $this->dirpermissions, true)) { | |
85 | throw new file_exception('storedfilecannotcreatefiledirs'); // permission trouble | |
86 | } | |
172dd12c | 87 | } |
88 | } | |
89 | ||
744b64ff | 90 | /** |
bf9ffe27 PS |
91 | * Returns location of filedir (file pool). |
92 | * | |
93 | * Do not use, this method is intended for stored_file instances only!!! | |
94 | * | |
6e73ac42 | 95 | * @return string pathname |
744b64ff | 96 | */ |
97 | public function get_filedir() { | |
98 | return $this->filedir; | |
99 | } | |
100 | ||
172dd12c | 101 | /** |
bf9ffe27 PS |
102 | * Calculates sha1 hash of unique full path name information. |
103 | * | |
104 | * This hash is a unique file identifier - it is used to improve | |
105 | * performance and overcome db index size limits. | |
106 | * | |
172dd12c | 107 | * @param int $contextid |
108 | * @param string $filearea | |
109 | * @param int $itemid | |
110 | * @param string $filepath | |
111 | * @param string $filename | |
bf9ffe27 | 112 | * @return string sha1 hash |
172dd12c | 113 | */ |
114 | public static function get_pathname_hash($contextid, $filearea, $itemid, $filepath, $filename) { | |
115 | return sha1($contextid.$filearea.$itemid.$filepath.$filename); | |
116 | } | |
117 | ||
118 | /** | |
119 | * Does this file exist? | |
bf9ffe27 | 120 | * |
172dd12c | 121 | * @param int $contextid |
122 | * @param string $filearea | |
123 | * @param int $itemid | |
124 | * @param string $filepath | |
125 | * @param string $filename | |
126 | * @return bool | |
127 | */ | |
128 | public function file_exists($contextid, $filearea, $itemid, $filepath, $filename) { | |
129 | $filepath = clean_param($filepath, PARAM_PATH); | |
130 | $filename = clean_param($filename, PARAM_FILE); | |
131 | ||
132 | if ($filename === '') { | |
133 | $filename = '.'; | |
134 | } | |
135 | ||
136 | $pathnamehash = $this->get_pathname_hash($contextid, $filearea, $itemid, $filepath, $filename); | |
137 | return $this->file_exists_by_hash($pathnamehash); | |
138 | } | |
139 | ||
140 | /** | |
141 | * Does this file exist? | |
bf9ffe27 | 142 | * |
172dd12c | 143 | * @param string $pathnamehash |
144 | * @return bool | |
145 | */ | |
146 | public function file_exists_by_hash($pathnamehash) { | |
147 | global $DB; | |
148 | ||
149 | return $DB->record_exists('files', array('pathnamehash'=>$pathnamehash)); | |
150 | } | |
151 | ||
152 | /** | |
25aebf09 | 153 | * Fetch file using local file id. |
bf9ffe27 | 154 | * |
25aebf09 | 155 | * Please do not rely on file ids, it is usually easier to use |
156 | * pathname hashes instead. | |
bf9ffe27 | 157 | * |
172dd12c | 158 | * @param int $fileid |
bf9ffe27 | 159 | * @return stored_file instance if exists, false if not |
172dd12c | 160 | */ |
161 | public function get_file_by_id($fileid) { | |
162 | global $DB; | |
163 | ||
164 | if ($file_record = $DB->get_record('files', array('id'=>$fileid))) { | |
165 | return new stored_file($this, $file_record); | |
166 | } else { | |
167 | return false; | |
168 | } | |
169 | } | |
170 | ||
171 | /** | |
172 | * Fetch file using local file full pathname hash | |
bf9ffe27 | 173 | * |
172dd12c | 174 | * @param string $pathnamehash |
bf9ffe27 | 175 | * @return stored_file instance if exists, false if not |
172dd12c | 176 | */ |
177 | public function get_file_by_hash($pathnamehash) { | |
178 | global $DB; | |
179 | ||
180 | if ($file_record = $DB->get_record('files', array('pathnamehash'=>$pathnamehash))) { | |
181 | return new stored_file($this, $file_record); | |
182 | } else { | |
183 | return false; | |
184 | } | |
185 | } | |
186 | ||
187 | /** | |
bf9ffe27 PS |
188 | * Fetch locally stored file. |
189 | * | |
172dd12c | 190 | * @param int $contextid |
191 | * @param string $filearea | |
192 | * @param int $itemid | |
193 | * @param string $filepath | |
194 | * @param string $filename | |
bf9ffe27 | 195 | * @return stored_file instance if exists, false if not |
172dd12c | 196 | */ |
197 | public function get_file($contextid, $filearea, $itemid, $filepath, $filename) { | |
198 | global $DB; | |
199 | ||
200 | $filepath = clean_param($filepath, PARAM_PATH); | |
201 | $filename = clean_param($filename, PARAM_FILE); | |
202 | ||
203 | if ($filename === '') { | |
204 | $filename = '.'; | |
205 | } | |
206 | ||
207 | $pathnamehash = $this->get_pathname_hash($contextid, $filearea, $itemid, $filepath, $filename); | |
208 | return $this->get_file_by_hash($pathnamehash); | |
209 | } | |
210 | ||
211 | /** | |
212 | * Returns all area files (optionally limited by itemid) | |
bf9ffe27 | 213 | * |
172dd12c | 214 | * @param int $contextid |
215 | * @param string $filearea | |
216 | * @param int $itemid (all files if not specified) | |
217 | * @param string $sort | |
218 | * @param bool $includedirs | |
cd5be217 | 219 | * @return array of stored_files indexed by pathanmehash |
172dd12c | 220 | */ |
f79321f1 | 221 | public function get_area_files($contextid, $filearea, $itemid=false, $sort="sortorder, itemid, filepath, filename", $includedirs = true) { |
172dd12c | 222 | global $DB; |
223 | ||
224 | $conditions = array('contextid'=>$contextid, 'filearea'=>$filearea); | |
225 | if ($itemid !== false) { | |
226 | $conditions['itemid'] = $itemid; | |
227 | } | |
228 | ||
229 | $result = array(); | |
230 | $file_records = $DB->get_records('files', $conditions, $sort); | |
231 | foreach ($file_records as $file_record) { | |
46fcbcf4 | 232 | if (!$includedirs and $file_record->filename === '.') { |
172dd12c | 233 | continue; |
234 | } | |
cd5be217 | 235 | $result[$file_record->pathnamehash] = new stored_file($this, $file_record); |
172dd12c | 236 | } |
237 | return $result; | |
238 | } | |
239 | ||
752b9f42 | 240 | /** |
241 | * Returns array based tree structure of area files | |
bf9ffe27 | 242 | * |
752b9f42 | 243 | * @param int $contextid |
244 | * @param string $filearea | |
245 | * @param int $itemid | |
246 | * @return array each dir represented by dirname, subdirs, files and dirfile array elements | |
247 | */ | |
248 | public function get_area_tree($contextid, $filearea, $itemid) { | |
249 | $result = array('dirname'=>'', 'dirfile'=>null, 'subdirs'=>array(), 'files'=>array()); | |
f79321f1 | 250 | $files = $this->get_area_files($contextid, $filearea, $itemid, $sort="sortorder, itemid, filepath, filename", true); |
752b9f42 | 251 | // first create directory structure |
252 | foreach ($files as $hash=>$dir) { | |
253 | if (!$dir->is_directory()) { | |
254 | continue; | |
255 | } | |
256 | unset($files[$hash]); | |
257 | if ($dir->get_filepath() === '/') { | |
258 | $result['dirfile'] = $dir; | |
259 | continue; | |
260 | } | |
261 | $parts = explode('/', trim($dir->get_filepath(),'/')); | |
262 | $pointer =& $result; | |
263 | foreach ($parts as $part) { | |
3b607678 | 264 | if ($part === '') { |
265 | continue; | |
266 | } | |
752b9f42 | 267 | if (!isset($pointer['subdirs'][$part])) { |
268 | $pointer['subdirs'][$part] = array('dirname'=>$part, 'dirfile'=>null, 'subdirs'=>array(), 'files'=>array()); | |
269 | } | |
270 | $pointer =& $pointer['subdirs'][$part]; | |
271 | } | |
272 | $pointer['dirfile'] = $dir; | |
273 | unset($pointer); | |
274 | } | |
275 | foreach ($files as $hash=>$file) { | |
276 | $parts = explode('/', trim($file->get_filepath(),'/')); | |
277 | $pointer =& $result; | |
278 | foreach ($parts as $part) { | |
3b607678 | 279 | if ($part === '') { |
280 | continue; | |
281 | } | |
752b9f42 | 282 | $pointer =& $pointer['subdirs'][$part]; |
283 | } | |
284 | $pointer['files'][$file->get_filename()] = $file; | |
285 | unset($pointer); | |
286 | } | |
287 | return $result; | |
288 | } | |
289 | ||
ee03a651 | 290 | /** |
bf9ffe27 PS |
291 | * Returns all files and optionally directories |
292 | * | |
ee03a651 | 293 | * @param int $contextid |
294 | * @param string $filearea | |
295 | * @param int $itemid | |
296 | * @param int $filepath directory path | |
297 | * @param bool $recursive include all subdirectories | |
46fcbcf4 | 298 | * @param bool $includedirs include files and directories |
ee03a651 | 299 | * @param string $sort |
cd5be217 | 300 | * @return array of stored_files indexed by pathanmehash |
ee03a651 | 301 | */ |
bf9ffe27 | 302 | public function get_directory_files($contextid, $filearea, $itemid, $filepath, $recursive = false, $includedirs = true, $sort = "filepath, filename") { |
ee03a651 | 303 | global $DB; |
304 | ||
305 | if (!$directory = $this->get_file($contextid, $filearea, $itemid, $filepath, '.')) { | |
306 | return array(); | |
307 | } | |
308 | ||
309 | if ($recursive) { | |
310 | ||
46fcbcf4 | 311 | $dirs = $includedirs ? "" : "AND filename <> '.'"; |
ee03a651 | 312 | $length = textlib_get_instance()->strlen($filepath); |
313 | ||
314 | $sql = "SELECT * | |
315 | FROM {files} | |
316 | WHERE contextid = :contextid AND filearea = :filearea AND itemid = :itemid | |
655bbf51 | 317 | AND ".$DB->sql_substr("filepath", 1, $length)." = :filepath |
ee03a651 | 318 | AND id <> :dirid |
319 | $dirs | |
320 | ORDER BY $sort"; | |
321 | $params = array('contextid'=>$contextid, 'filearea'=>$filearea, 'itemid'=>$itemid, 'filepath'=>$filepath, 'dirid'=>$directory->get_id()); | |
322 | ||
323 | $files = array(); | |
324 | $dirs = array(); | |
325 | $file_records = $DB->get_records_sql($sql, $params); | |
326 | foreach ($file_records as $file_record) { | |
327 | if ($file_record->filename == '.') { | |
cd5be217 | 328 | $dirs[$file_record->pathnamehash] = new stored_file($this, $file_record); |
ee03a651 | 329 | } else { |
cd5be217 | 330 | $files[$file_record->pathnamehash] = new stored_file($this, $file_record); |
ee03a651 | 331 | } |
332 | } | |
333 | $result = array_merge($dirs, $files); | |
334 | ||
335 | } else { | |
336 | $result = array(); | |
337 | $params = array('contextid'=>$contextid, 'filearea'=>$filearea, 'itemid'=>$itemid, 'filepath'=>$filepath, 'dirid'=>$directory->get_id()); | |
338 | ||
339 | $length = textlib_get_instance()->strlen($filepath); | |
340 | ||
46fcbcf4 | 341 | if ($includedirs) { |
ee03a651 | 342 | $sql = "SELECT * |
343 | FROM {files} | |
344 | WHERE contextid = :contextid AND filearea = :filearea | |
345 | AND itemid = :itemid AND filename = '.' | |
655bbf51 | 346 | AND ".$DB->sql_substr("filepath", 1, $length)." = :filepath |
ee03a651 | 347 | AND id <> :dirid |
348 | ORDER BY $sort"; | |
349 | $reqlevel = substr_count($filepath, '/') + 1; | |
350 | $file_records = $DB->get_records_sql($sql, $params); | |
351 | foreach ($file_records as $file_record) { | |
352 | if (substr_count($file_record->filepath, '/') !== $reqlevel) { | |
353 | continue; | |
354 | } | |
cd5be217 | 355 | $result[$file_record->pathnamehash] = new stored_file($this, $file_record); |
ee03a651 | 356 | } |
357 | } | |
358 | ||
359 | $sql = "SELECT * | |
360 | FROM {files} | |
361 | WHERE contextid = :contextid AND filearea = :filearea AND itemid = :itemid | |
362 | AND filepath = :filepath AND filename <> '.' | |
363 | ORDER BY $sort"; | |
364 | ||
365 | $file_records = $DB->get_records_sql($sql, $params); | |
366 | foreach ($file_records as $file_record) { | |
cd5be217 | 367 | $result[$file_record->pathnamehash] = new stored_file($this, $file_record); |
ee03a651 | 368 | } |
369 | } | |
370 | ||
371 | return $result; | |
372 | } | |
373 | ||
172dd12c | 374 | /** |
bf9ffe27 PS |
375 | * Delete all area files (optionally limited by itemid). |
376 | * | |
172dd12c | 377 | * @param int $contextid |
6311eb61 | 378 | * @param string $filearea (all areas in context if not specified) |
172dd12c | 379 | * @param int $itemid (all files if not specified) |
bf9ffe27 | 380 | * @return bool success |
172dd12c | 381 | */ |
bf9ffe27 | 382 | public function delete_area_files($contextid, $filearea = false, $itemid = false) { |
172dd12c | 383 | global $DB; |
384 | ||
6311eb61 | 385 | $conditions = array('contextid'=>$contextid); |
386 | if ($filearea !== false) { | |
387 | $conditions['filearea'] = $filearea; | |
388 | } | |
172dd12c | 389 | if ($itemid !== false) { |
390 | $conditions['itemid'] = $itemid; | |
391 | } | |
392 | ||
172dd12c | 393 | $file_records = $DB->get_records('files', $conditions); |
394 | foreach ($file_records as $file_record) { | |
395 | $stored_file = new stored_file($this, $file_record); | |
bf9ffe27 | 396 | $stored_file->delete(); |
172dd12c | 397 | } |
398 | ||
bf9ffe27 | 399 | return true; // BC only |
172dd12c | 400 | } |
401 | ||
402 | /** | |
bf9ffe27 PS |
403 | * Recursively creates directory. |
404 | * | |
172dd12c | 405 | * @param int $contextid |
406 | * @param string $filearea | |
407 | * @param int $itemid | |
408 | * @param string $filepath | |
409 | * @param string $filename | |
410 | * @return bool success | |
411 | */ | |
bf9ffe27 | 412 | public function create_directory($contextid, $filearea, $itemid, $filepath, $userid = null) { |
172dd12c | 413 | global $DB; |
414 | ||
415 | // validate all parameters, we do not want any rubbish stored in database, right? | |
416 | if (!is_number($contextid) or $contextid < 1) { | |
145a0a31 | 417 | throw new file_exception('storedfileproblem', 'Invalid contextid'); |
172dd12c | 418 | } |
419 | ||
6c0e2d08 | 420 | $filearea = clean_param($filearea, PARAM_ALPHAEXT); |
172dd12c | 421 | if ($filearea === '') { |
145a0a31 | 422 | throw new file_exception('storedfileproblem', 'Invalid filearea'); |
172dd12c | 423 | } |
424 | ||
425 | if (!is_number($itemid) or $itemid < 0) { | |
145a0a31 | 426 | throw new file_exception('storedfileproblem', 'Invalid itemid'); |
172dd12c | 427 | } |
428 | ||
429 | $filepath = clean_param($filepath, PARAM_PATH); | |
430 | if (strpos($filepath, '/') !== 0 or strrpos($filepath, '/') !== strlen($filepath)-1) { | |
431 | // path must start and end with '/' | |
145a0a31 | 432 | throw new file_exception('storedfileproblem', 'Invalid file path'); |
172dd12c | 433 | } |
434 | ||
435 | $pathnamehash = $this->get_pathname_hash($contextid, $filearea, $itemid, $filepath, '.'); | |
436 | ||
437 | if ($dir_info = $this->get_file_by_hash($pathnamehash)) { | |
438 | return $dir_info; | |
439 | } | |
440 | ||
441 | static $contenthash = null; | |
442 | if (!$contenthash) { | |
b48f3e06 | 443 | $this->add_string_to_pool(''); |
172dd12c | 444 | $contenthash = sha1(''); |
445 | } | |
446 | ||
447 | $now = time(); | |
448 | ||
449 | $dir_record = new object(); | |
450 | $dir_record->contextid = $contextid; | |
451 | $dir_record->filearea = $filearea; | |
452 | $dir_record->itemid = $itemid; | |
453 | $dir_record->filepath = $filepath; | |
454 | $dir_record->filename = '.'; | |
455 | $dir_record->contenthash = $contenthash; | |
456 | $dir_record->filesize = 0; | |
457 | ||
458 | $dir_record->timecreated = $now; | |
459 | $dir_record->timemodified = $now; | |
460 | $dir_record->mimetype = null; | |
461 | $dir_record->userid = $userid; | |
462 | ||
463 | $dir_record->pathnamehash = $pathnamehash; | |
464 | ||
465 | $DB->insert_record('files', $dir_record); | |
466 | $dir_info = $this->get_file_by_hash($pathnamehash); | |
467 | ||
468 | if ($filepath !== '/') { | |
469 | //recurse to parent dirs | |
470 | $filepath = trim($filepath, '/'); | |
471 | $filepath = explode('/', $filepath); | |
472 | array_pop($filepath); | |
473 | $filepath = implode('/', $filepath); | |
474 | $filepath = ($filepath === '') ? '/' : "/$filepath/"; | |
475 | $this->create_directory($contextid, $filearea, $itemid, $filepath, $userid); | |
476 | } | |
477 | ||
478 | return $dir_info; | |
479 | } | |
480 | ||
481 | /** | |
bf9ffe27 PS |
482 | * Add new local file based on existing local file. |
483 | * | |
172dd12c | 484 | * @param mixed $file_record object or array describing changes |
72d0aed6 | 485 | * @param mixed $fileorid id or stored_file instance of the existing local file |
bf9ffe27 | 486 | * @return stored_file instance of newly created file |
172dd12c | 487 | */ |
72d0aed6 | 488 | public function create_file_from_storedfile($file_record, $fileorid) { |
4fb2306e | 489 | global $DB; |
172dd12c | 490 | |
72d0aed6 | 491 | if ($fileorid instanceof stored_file) { |
492 | $fid = $fileorid->get_id(); | |
493 | } else { | |
494 | $fid = $fileorid; | |
8eb1e0a1 | 495 | } |
496 | ||
ec8b711f | 497 | $file_record = (array)$file_record; // we support arrays too, do not modify the submitted record! |
498 | ||
172dd12c | 499 | unset($file_record['id']); |
500 | unset($file_record['filesize']); | |
501 | unset($file_record['contenthash']); | |
8eb1e0a1 | 502 | unset($file_record['pathnamehash']); |
172dd12c | 503 | |
504 | $now = time(); | |
505 | ||
ebcac6c6 | 506 | if (!$newrecord = $DB->get_record('files', array('id'=>$fid))) { |
145a0a31 | 507 | throw new file_exception('storedfileproblem', 'File does not exist'); |
172dd12c | 508 | } |
509 | ||
510 | unset($newrecord->id); | |
511 | ||
512 | foreach ($file_record as $key=>$value) { | |
513 | // validate all parameters, we do not want any rubbish stored in database, right? | |
514 | if ($key == 'contextid' and (!is_number($value) or $value < 1)) { | |
145a0a31 | 515 | throw new file_exception('storedfileproblem', 'Invalid contextid'); |
172dd12c | 516 | } |
517 | ||
518 | if ($key == 'filearea') { | |
6c0e2d08 | 519 | $value = clean_param($value, PARAM_ALPHAEXT); |
172dd12c | 520 | if ($value === '') { |
145a0a31 | 521 | throw new file_exception('storedfileproblem', 'Invalid filearea'); |
172dd12c | 522 | } |
523 | } | |
524 | ||
525 | if ($key == 'itemid' and (!is_number($value) or $value < 0)) { | |
145a0a31 | 526 | throw new file_exception('storedfileproblem', 'Invalid itemid'); |
172dd12c | 527 | } |
528 | ||
529 | ||
530 | if ($key == 'filepath') { | |
531 | $value = clean_param($value, PARAM_PATH); | |
00c32c54 | 532 | if (strpos($value, '/') !== 0 or strrpos($value, '/') !== strlen($value)-1) { |
172dd12c | 533 | // path must start and end with '/' |
145a0a31 | 534 | throw new file_exception('storedfileproblem', 'Invalid file path'); |
172dd12c | 535 | } |
536 | } | |
537 | ||
538 | if ($key == 'filename') { | |
539 | $value = clean_param($value, PARAM_FILE); | |
540 | if ($value === '') { | |
541 | // path must start and end with '/' | |
145a0a31 | 542 | throw new file_exception('storedfileproblem', 'Invalid file name'); |
172dd12c | 543 | } |
544 | } | |
545 | ||
546 | $newrecord->$key = $value; | |
547 | } | |
548 | ||
549 | $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename); | |
550 | ||
cd5be217 | 551 | if ($newrecord->filename === '.') { |
552 | // special case - only this function supports directories ;-) | |
553 | $directory = $this->create_directory($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); | |
554 | // update the existing directory with the new data | |
555 | $newrecord->id = $directory->get_id(); | |
b8ac7ece | 556 | $DB->update_record('files', $newrecord); |
cd5be217 | 557 | return new stored_file($this, $newrecord); |
558 | } | |
559 | ||
172dd12c | 560 | try { |
561 | $newrecord->id = $DB->insert_record('files', $newrecord); | |
562 | } catch (database_exception $e) { | |
563 | $newrecord->id = false; | |
564 | } | |
565 | ||
566 | if (!$newrecord->id) { | |
567 | throw new stored_file_creation_exception($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, | |
cd5be217 | 568 | $newrecord->filepath, $newrecord->filename); |
172dd12c | 569 | } |
570 | ||
571 | $this->create_directory($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); | |
572 | ||
573 | return new stored_file($this, $newrecord); | |
574 | } | |
575 | ||
6e73ac42 | 576 | /** |
bf9ffe27 PS |
577 | * Add new local file. |
578 | * | |
6e73ac42 | 579 | * @param mixed $file_record object or array describing file |
580 | * @param string $path path to file or content of file | |
581 | * @param array $options @see download_file_content() options | |
3a1055a5 | 582 | * @param bool $usetempfile use temporary file for download, may prevent out of memory problems |
bf9ffe27 | 583 | * @return stored_file instance |
6e73ac42 | 584 | */ |
3a1055a5 | 585 | public function create_file_from_url($file_record, $url, array $options = NULL, $usetempfile = false) { |
ec8b711f | 586 | |
587 | $file_record = (array)$file_record; //do not modify the submitted record, this cast unlinks objects | |
6e73ac42 | 588 | $file_record = (object)$file_record; // we support arrays too |
589 | ||
590 | $headers = isset($options['headers']) ? $options['headers'] : null; | |
591 | $postdata = isset($options['postdata']) ? $options['postdata'] : null; | |
592 | $fullresponse = isset($options['fullresponse']) ? $options['fullresponse'] : false; | |
593 | $timeout = isset($options['timeout']) ? $options['timeout'] : 300; | |
594 | $connecttimeout = isset($options['connecttimeout']) ? $options['connecttimeout'] : 20; | |
595 | $skipcertverify = isset($options['skipcertverify']) ? $options['skipcertverify'] : false; | |
596 | ||
6e73ac42 | 597 | if (!isset($file_record->filename)) { |
598 | $parts = explode('/', $url); | |
599 | $filename = array_pop($parts); | |
600 | $file_record->filename = clean_param($filename, PARAM_FILE); | |
601 | } | |
1dce6261 DC |
602 | $source = !empty($file_record->source) ? $file_record->source : $url; |
603 | $file_record->source = clean_param($source, PARAM_URL); | |
6e73ac42 | 604 | |
3a1055a5 PS |
605 | if ($usetempfile) { |
606 | check_dir_exists($this->tempdir, true, true); | |
607 | $tmpfile = tempnam($this->tempdir, 'newfromurl'); | |
608 | $content = download_file_content($url, $headers, $postdata, $fullresponse, $timeout, $connecttimeout, $skipcertverify, $tmpfile); | |
609 | if ($content === false) { | |
610 | throw new file_exception('storedfileproblem', 'Can not fetch file form URL'); | |
611 | } | |
612 | try { | |
613 | $newfile = $this->create_file_from_pathname($file_record, $tmpfile); | |
614 | @unlink($tmpfile); | |
615 | return $newfile; | |
616 | } catch (Exception $e) { | |
617 | @unlink($tmpfile); | |
618 | throw $e; | |
619 | } | |
620 | ||
621 | } else { | |
622 | $content = download_file_content($url, $headers, $postdata, $fullresponse, $timeout, $connecttimeout, $skipcertverify); | |
623 | if ($content === false) { | |
624 | throw new file_exception('storedfileproblem', 'Can not fetch file form URL'); | |
625 | } | |
626 | return $this->create_file_from_string($file_record, $content); | |
627 | } | |
6e73ac42 | 628 | } |
629 | ||
172dd12c | 630 | /** |
bf9ffe27 PS |
631 | * Add new local file. |
632 | * | |
172dd12c | 633 | * @param mixed $file_record object or array describing file |
634 | * @param string $path path to file or content of file | |
bf9ffe27 | 635 | * @return stored_file instance |
172dd12c | 636 | */ |
637 | public function create_file_from_pathname($file_record, $pathname) { | |
4fb2306e | 638 | global $DB; |
172dd12c | 639 | |
ec8b711f | 640 | $file_record = (array)$file_record; //do not modify the submitted record, this cast unlinks objects |
172dd12c | 641 | $file_record = (object)$file_record; // we support arrays too |
642 | ||
643 | // validate all parameters, we do not want any rubbish stored in database, right? | |
644 | if (!is_number($file_record->contextid) or $file_record->contextid < 1) { | |
145a0a31 | 645 | throw new file_exception('storedfileproblem', 'Invalid contextid'); |
172dd12c | 646 | } |
647 | ||
6c0e2d08 | 648 | $file_record->filearea = clean_param($file_record->filearea, PARAM_ALPHAEXT); |
172dd12c | 649 | if ($file_record->filearea === '') { |
145a0a31 | 650 | throw new file_exception('storedfileproblem', 'Invalid filearea'); |
172dd12c | 651 | } |
652 | ||
653 | if (!is_number($file_record->itemid) or $file_record->itemid < 0) { | |
145a0a31 | 654 | throw new file_exception('storedfileproblem', 'Invalid itemid'); |
172dd12c | 655 | } |
656 | ||
f79321f1 DC |
657 | if (!empty($file_record->sortorder)) { |
658 | if (!is_number($file_record->sortorder) or $file_record->sortorder < 0) { | |
659 | $file_record->sortorder = 0; | |
660 | } | |
661 | } else { | |
662 | $file_record->sortorder = 0; | |
663 | } | |
664 | ||
172dd12c | 665 | $file_record->filepath = clean_param($file_record->filepath, PARAM_PATH); |
666 | if (strpos($file_record->filepath, '/') !== 0 or strrpos($file_record->filepath, '/') !== strlen($file_record->filepath)-1) { | |
667 | // path must start and end with '/' | |
145a0a31 | 668 | throw new file_exception('storedfileproblem', 'Invalid file path'); |
172dd12c | 669 | } |
670 | ||
671 | $file_record->filename = clean_param($file_record->filename, PARAM_FILE); | |
672 | if ($file_record->filename === '') { | |
e1dcb950 | 673 | // filename must not be empty |
145a0a31 | 674 | throw new file_exception('storedfileproblem', 'Invalid file name'); |
172dd12c | 675 | } |
676 | ||
677 | $now = time(); | |
678 | ||
679 | $newrecord = new object(); | |
680 | ||
681 | $newrecord->contextid = $file_record->contextid; | |
682 | $newrecord->filearea = $file_record->filearea; | |
683 | $newrecord->itemid = $file_record->itemid; | |
684 | $newrecord->filepath = $file_record->filepath; | |
685 | $newrecord->filename = $file_record->filename; | |
686 | ||
687 | $newrecord->timecreated = empty($file_record->timecreated) ? $now : $file_record->timecreated; | |
688 | $newrecord->timemodified = empty($file_record->timemodified) ? $now : $file_record->timemodified; | |
689 | $newrecord->mimetype = empty($file_record->mimetype) ? mimeinfo('type', $file_record->filename) : $file_record->mimetype; | |
690 | $newrecord->userid = empty($file_record->userid) ? null : $file_record->userid; | |
4fb2306e PS |
691 | $newrecord->source = empty($file_record->source) ? null : $file_record->source; |
692 | $newrecord->author = empty($file_record->author) ? null : $file_record->author; | |
693 | $newrecord->license = empty($file_record->license) ? null : $file_record->license; | |
f79321f1 | 694 | $newrecord->sortorder = $file_record->sortorder; |
172dd12c | 695 | |
b48f3e06 | 696 | list($newrecord->contenthash, $newrecord->filesize, $newfile) = $this->add_file_to_pool($pathname); |
172dd12c | 697 | |
698 | $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename); | |
699 | ||
700 | try { | |
701 | $newrecord->id = $DB->insert_record('files', $newrecord); | |
702 | } catch (database_exception $e) { | |
703 | $newrecord->id = false; | |
704 | } | |
705 | ||
706 | if (!$newrecord->id) { | |
707 | if ($newfile) { | |
ead14290 | 708 | $this->deleted_file_cleanup($newrecord->contenthash); |
172dd12c | 709 | } |
710 | throw new stored_file_creation_exception($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, | |
711 | $newrecord->filepath, $newrecord->filename); | |
712 | } | |
713 | ||
714 | $this->create_directory($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); | |
715 | ||
716 | return new stored_file($this, $newrecord); | |
717 | } | |
718 | ||
719 | /** | |
bf9ffe27 PS |
720 | * Add new local file. |
721 | * | |
172dd12c | 722 | * @param mixed $file_record object or array describing file |
723 | * @param string $content content of file | |
bf9ffe27 | 724 | * @return stored_file instance |
172dd12c | 725 | */ |
726 | public function create_file_from_string($file_record, $content) { | |
4fb2306e | 727 | global $DB; |
172dd12c | 728 | |
ec8b711f | 729 | $file_record = (array)$file_record; //do not modify the submitted record, this cast unlinks objects |
172dd12c | 730 | $file_record = (object)$file_record; // we support arrays too |
731 | ||
732 | // validate all parameters, we do not want any rubbish stored in database, right? | |
733 | if (!is_number($file_record->contextid) or $file_record->contextid < 1) { | |
145a0a31 | 734 | throw new file_exception('storedfileproblem', 'Invalid contextid'); |
172dd12c | 735 | } |
736 | ||
6c0e2d08 | 737 | $file_record->filearea = clean_param($file_record->filearea, PARAM_ALPHAEXT); |
172dd12c | 738 | if ($file_record->filearea === '') { |
145a0a31 | 739 | throw new file_exception('storedfileproblem', 'Invalid filearea'); |
172dd12c | 740 | } |
741 | ||
742 | if (!is_number($file_record->itemid) or $file_record->itemid < 0) { | |
145a0a31 | 743 | throw new file_exception('storedfileproblem', 'Invalid itemid'); |
172dd12c | 744 | } |
745 | ||
f79321f1 DC |
746 | if (!empty($file_record->sortorder)) { |
747 | if (!is_number($file_record->sortorder) or $file_record->sortorder < 0) { | |
748 | $file_record->sortorder = 0; | |
749 | } | |
750 | } else { | |
751 | $file_record->sortorder = 0; | |
752 | } | |
753 | ||
172dd12c | 754 | $file_record->filepath = clean_param($file_record->filepath, PARAM_PATH); |
755 | if (strpos($file_record->filepath, '/') !== 0 or strrpos($file_record->filepath, '/') !== strlen($file_record->filepath)-1) { | |
756 | // path must start and end with '/' | |
145a0a31 | 757 | throw new file_exception('storedfileproblem', 'Invalid file path'); |
172dd12c | 758 | } |
759 | ||
760 | $file_record->filename = clean_param($file_record->filename, PARAM_FILE); | |
761 | if ($file_record->filename === '') { | |
762 | // path must start and end with '/' | |
145a0a31 | 763 | throw new file_exception('storedfileproblem', 'Invalid file name'); |
172dd12c | 764 | } |
765 | ||
766 | $now = time(); | |
767 | ||
768 | $newrecord = new object(); | |
769 | ||
770 | $newrecord->contextid = $file_record->contextid; | |
771 | $newrecord->filearea = $file_record->filearea; | |
772 | $newrecord->itemid = $file_record->itemid; | |
773 | $newrecord->filepath = $file_record->filepath; | |
774 | $newrecord->filename = $file_record->filename; | |
775 | ||
776 | $newrecord->timecreated = empty($file_record->timecreated) ? $now : $file_record->timecreated; | |
777 | $newrecord->timemodified = empty($file_record->timemodified) ? $now : $file_record->timemodified; | |
778 | $newrecord->mimetype = empty($file_record->mimetype) ? mimeinfo('type', $file_record->filename) : $file_record->mimetype; | |
779 | $newrecord->userid = empty($file_record->userid) ? null : $file_record->userid; | |
4fb2306e PS |
780 | $newrecord->source = empty($file_record->source) ? null : $file_record->source; |
781 | $newrecord->author = empty($file_record->author) ? null : $file_record->author; | |
782 | $newrecord->license = empty($file_record->license) ? null : $file_record->license; | |
f79321f1 | 783 | $newrecord->sortorder = $file_record->sortorder; |
1dce6261 | 784 | |
b48f3e06 | 785 | list($newrecord->contenthash, $newrecord->filesize, $newfile) = $this->add_string_to_pool($content); |
172dd12c | 786 | |
787 | $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename); | |
788 | ||
789 | try { | |
790 | $newrecord->id = $DB->insert_record('files', $newrecord); | |
791 | } catch (database_exception $e) { | |
792 | $newrecord->id = false; | |
793 | } | |
794 | ||
795 | if (!$newrecord->id) { | |
796 | if ($newfile) { | |
ead14290 | 797 | $this->deleted_file_cleanup($newrecord->contenthash); |
172dd12c | 798 | } |
799 | throw new stored_file_creation_exception($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, | |
800 | $newrecord->filepath, $newrecord->filename); | |
801 | } | |
802 | ||
803 | $this->create_directory($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); | |
804 | ||
805 | return new stored_file($this, $newrecord); | |
806 | } | |
807 | ||
797f19e8 | 808 | /** |
809 | * Creates new image file from existing. | |
bf9ffe27 | 810 | * |
797f19e8 | 811 | * @param mixed $file_record object or array describing new file |
812 | * @param mixed file id or stored file object | |
813 | * @param int $newwidth in pixels | |
814 | * @param int $newheight in pixels | |
815 | * @param bool $keepaspectratio | |
bf9ffe27 PS |
816 | * @param int $quality depending on image type 0-100 for jpeg, 0-9 (0 means no compression) for png |
817 | * @return stored_file instance | |
797f19e8 | 818 | */ |
bf9ffe27 | 819 | public function convert_image($file_record, $fid, $newwidth = NULL, $newheight = NULL, $keepaspectratio = true, $quality = NULL) { |
797f19e8 | 820 | global $DB; |
821 | ||
822 | if ($fid instanceof stored_file) { | |
823 | $fid = $fid->get_id(); | |
824 | } | |
825 | ||
826 | $file_record = (array)$file_record; // we support arrays too, do not modify the submitted record! | |
827 | ||
828 | if (!$file = $this->get_file_by_id($fid)) { // make sure file really exists and we we correct data | |
829 | throw new file_exception('storedfileproblem', 'File does not exist'); | |
830 | } | |
831 | ||
832 | if (!$imageinfo = $file->get_imageinfo()) { | |
833 | throw new file_exception('storedfileproblem', 'File is not an image'); | |
834 | } | |
835 | ||
836 | if (!isset($file_record['filename'])) { | |
837 | $file_record['filename'] == $file->get_filename(); | |
838 | } | |
839 | ||
840 | if (!isset($file_record['mimetype'])) { | |
841 | $file_record['mimetype'] = mimeinfo('type', $file_record['filename']); | |
842 | } | |
843 | ||
844 | $width = $imageinfo['width']; | |
845 | $height = $imageinfo['height']; | |
846 | $mimetype = $imageinfo['mimetype']; | |
847 | ||
848 | if ($keepaspectratio) { | |
849 | if (0 >= $newwidth and 0 >= $newheight) { | |
850 | // no sizes specified | |
851 | $newwidth = $width; | |
852 | $newheight = $height; | |
853 | ||
854 | } else if (0 < $newwidth and 0 < $newheight) { | |
855 | $xheight = ($newwidth*($height/$width)); | |
856 | if ($xheight < $newheight) { | |
857 | $newheight = (int)$xheight; | |
858 | } else { | |
859 | $newwidth = (int)($newheight*($width/$height)); | |
860 | } | |
861 | ||
862 | } else if (0 < $newwidth) { | |
863 | $newheight = (int)($newwidth*($height/$width)); | |
864 | ||
865 | } else { //0 < $newheight | |
866 | $newwidth = (int)($newheight*($width/$height)); | |
867 | } | |
868 | ||
869 | } else { | |
870 | if (0 >= $newwidth) { | |
871 | $newwidth = $width; | |
872 | } | |
873 | if (0 >= $newheight) { | |
874 | $newheight = $height; | |
875 | } | |
876 | } | |
877 | ||
878 | $img = imagecreatefromstring($file->get_content()); | |
879 | if ($height != $newheight or $width != $newwidth) { | |
880 | $newimg = imagecreatetruecolor($newwidth, $newheight); | |
881 | if (!imagecopyresized($newimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height)) { | |
882 | // weird | |
883 | throw new file_exception('storedfileproblem', 'Can not resize image'); | |
884 | } | |
885 | imagedestroy($img); | |
886 | $img = $newimg; | |
887 | } | |
888 | ||
889 | ob_start(); | |
890 | switch ($file_record['mimetype']) { | |
891 | case 'image/gif': | |
892 | imagegif($img); | |
893 | break; | |
894 | ||
895 | case 'image/jpeg': | |
896 | if (is_null($quality)) { | |
897 | imagejpeg($img); | |
898 | } else { | |
899 | imagejpeg($img, NULL, $quality); | |
900 | } | |
901 | break; | |
902 | ||
903 | case 'image/png': | |
8bd49ec0 | 904 | $quality = (int)$quality; |
797f19e8 | 905 | imagepng($img, NULL, $quality, NULL); |
906 | break; | |
907 | ||
908 | default: | |
909 | throw new file_exception('storedfileproblem', 'Unsupported mime type'); | |
910 | } | |
911 | ||
912 | $content = ob_get_contents(); | |
913 | ob_end_clean(); | |
914 | imagedestroy($img); | |
915 | ||
916 | if (!$content) { | |
917 | throw new file_exception('storedfileproblem', 'Can not convert image'); | |
918 | } | |
919 | ||
920 | return $this->create_file_from_string($file_record, $content); | |
921 | } | |
922 | ||
172dd12c | 923 | /** |
bf9ffe27 PS |
924 | * Add file content to sha1 pool. |
925 | * | |
172dd12c | 926 | * @param string $pathname path to file |
bf9ffe27 PS |
927 | * @param string $contenthash sha1 hash of content if known (performance only) |
928 | * @return array (contenthash, filesize, newfile) | |
172dd12c | 929 | */ |
bf9ffe27 | 930 | public function add_file_to_pool($pathname, $contenthash = NULL) { |
172dd12c | 931 | if (!is_readable($pathname)) { |
145a0a31 | 932 | throw new file_exception('storedfilecannotread'); |
172dd12c | 933 | } |
934 | ||
935 | if (is_null($contenthash)) { | |
936 | $contenthash = sha1_file($pathname); | |
937 | } | |
938 | ||
939 | $filesize = filesize($pathname); | |
940 | ||
941 | $hashpath = $this->path_from_hash($contenthash); | |
942 | $hashfile = "$hashpath/$contenthash"; | |
943 | ||
944 | if (file_exists($hashfile)) { | |
945 | if (filesize($hashfile) !== $filesize) { | |
946 | throw new file_pool_content_exception($contenthash); | |
947 | } | |
948 | $newfile = false; | |
949 | ||
950 | } else { | |
1aa01caf | 951 | if (!is_dir($hashpath)) { |
952 | if (!mkdir($hashpath, $this->dirpermissions, true)) { | |
953 | throw new file_exception('storedfilecannotcreatefiledirs'); // permission trouble | |
954 | } | |
172dd12c | 955 | } |
956 | $newfile = true; | |
957 | ||
6c0e2d08 | 958 | if (!copy($pathname, $hashfile)) { |
145a0a31 | 959 | throw new file_exception('storedfilecannotread'); |
172dd12c | 960 | } |
172dd12c | 961 | |
962 | if (filesize($hashfile) !== $filesize) { | |
963 | @unlink($hashfile); | |
964 | throw new file_pool_content_exception($contenthash); | |
965 | } | |
1aa01caf | 966 | chmod($hashfile, $this->filepermissions); // fix permissions if needed |
172dd12c | 967 | } |
968 | ||
969 | ||
970 | return array($contenthash, $filesize, $newfile); | |
971 | } | |
972 | ||
973 | /** | |
bf9ffe27 PS |
974 | * Add string content to sha1 pool. |
975 | * | |
172dd12c | 976 | * @param string $content file content - binary string |
bf9ffe27 | 977 | * @return array (contenthash, filesize, newfile) |
172dd12c | 978 | */ |
b48f3e06 | 979 | public function add_string_to_pool($content) { |
172dd12c | 980 | $contenthash = sha1($content); |
981 | $filesize = strlen($content); // binary length | |
982 | ||
983 | $hashpath = $this->path_from_hash($contenthash); | |
984 | $hashfile = "$hashpath/$contenthash"; | |
985 | ||
986 | ||
987 | if (file_exists($hashfile)) { | |
988 | if (filesize($hashfile) !== $filesize) { | |
989 | throw new file_pool_content_exception($contenthash); | |
990 | } | |
991 | $newfile = false; | |
992 | ||
993 | } else { | |
1aa01caf | 994 | if (!is_dir($hashpath)) { |
995 | if (!mkdir($hashpath, $this->dirpermissions, true)) { | |
996 | throw new file_exception('storedfilecannotcreatefiledirs'); // permission trouble | |
997 | } | |
172dd12c | 998 | } |
999 | $newfile = true; | |
1000 | ||
6c0e2d08 | 1001 | file_put_contents($hashfile, $content); |
172dd12c | 1002 | |
1003 | if (filesize($hashfile) !== $filesize) { | |
1004 | @unlink($hashfile); | |
1005 | throw new file_pool_content_exception($contenthash); | |
1006 | } | |
1aa01caf | 1007 | chmod($hashfile, $this->filepermissions); // fix permissions if needed |
172dd12c | 1008 | } |
1009 | ||
1010 | return array($contenthash, $filesize, $newfile); | |
1011 | } | |
1012 | ||
1013 | /** | |
bf9ffe27 | 1014 | * Return path to file with given hash. |
172dd12c | 1015 | * |
17d9269f | 1016 | * NOTE: must not be public, files in pool must not be modified |
172dd12c | 1017 | * |
1018 | * @param string $contenthash | |
1019 | * @return string expected file location | |
1020 | */ | |
17d9269f | 1021 | protected function path_from_hash($contenthash) { |
172dd12c | 1022 | $l1 = $contenthash[0].$contenthash[1]; |
1023 | $l2 = $contenthash[2].$contenthash[3]; | |
1024 | $l3 = $contenthash[4].$contenthash[5]; | |
1025 | return "$this->filedir/$l1/$l2/$l3"; | |
1026 | } | |
1027 | ||
1aa01caf | 1028 | /** |
bf9ffe27 | 1029 | * Return path to file with given hash. |
1aa01caf | 1030 | * |
1031 | * NOTE: must not be public, files in pool must not be modified | |
1032 | * | |
1033 | * @param string $contenthash | |
1034 | * @return string expected file location | |
1035 | */ | |
1036 | protected function trash_path_from_hash($contenthash) { | |
1037 | $l1 = $contenthash[0].$contenthash[1]; | |
1038 | $l2 = $contenthash[2].$contenthash[3]; | |
1039 | $l3 = $contenthash[4].$contenthash[5]; | |
1040 | return "$this->trashdir/$l1/$l2/$l3"; | |
1041 | } | |
1042 | ||
1043 | /** | |
bf9ffe27 PS |
1044 | * Tries to recover missing content of file from trash. |
1045 | * | |
1aa01caf | 1046 | * @param object $file_record |
1047 | * @return bool success | |
1048 | */ | |
1049 | public function try_content_recovery($file) { | |
1050 | $contenthash = $file->get_contenthash(); | |
1051 | $trashfile = $this->trash_path_from_hash($contenthash).'/'.$contenthash; | |
1052 | if (!is_readable($trashfile)) { | |
1053 | if (!is_readable($this->trashdir.'/'.$contenthash)) { | |
1054 | return false; | |
1055 | } | |
1056 | // nice, at least alternative trash file in trash root exists | |
1057 | $trashfile = $this->trashdir.'/'.$contenthash; | |
1058 | } | |
1059 | if (filesize($trashfile) != $file->get_filesize() or sha1_file($trashfile) != $contenthash) { | |
1060 | //weird, better fail early | |
1061 | return false; | |
1062 | } | |
1063 | $contentdir = $this->path_from_hash($contenthash); | |
1064 | $contentfile = $contentdir.'/'.$contenthash; | |
1065 | if (file_exists($contentfile)) { | |
1066 | //strange, no need to recover anything | |
1067 | return true; | |
1068 | } | |
1069 | if (!is_dir($contentdir)) { | |
1070 | if (!mkdir($contentdir, $this->dirpermissions, true)) { | |
1071 | return false; | |
1072 | } | |
1073 | } | |
1074 | return rename($trashfile, $contentfile); | |
1075 | } | |
1076 | ||
172dd12c | 1077 | /** |
bf9ffe27 PS |
1078 | * Marks pool file as candidate for deleting. |
1079 | * | |
1080 | * DO NOT call directly - reserved for core!! | |
1081 | * | |
172dd12c | 1082 | * @param string $contenthash |
1aa01caf | 1083 | * @return void |
172dd12c | 1084 | */ |
1aa01caf | 1085 | public function deleted_file_cleanup($contenthash) { |
172dd12c | 1086 | global $DB; |
1087 | ||
1aa01caf | 1088 | //Note: this section is critical - in theory file could be reused at the same |
1089 | // time, if this happens we can still recover the file from trash | |
1090 | if ($DB->record_exists('files', array('contenthash'=>$contenthash))) { | |
1091 | // file content is still used | |
1092 | return; | |
1093 | } | |
1094 | //move content file to trash | |
1095 | $contentfile = $this->path_from_hash($contenthash).'/'.$contenthash; | |
1096 | if (!file_exists($contentfile)) { | |
1097 | //weird, but no problem | |
172dd12c | 1098 | return; |
1099 | } | |
1aa01caf | 1100 | $trashpath = $this->trash_path_from_hash($contenthash); |
1101 | $trashfile = $trashpath.'/'.$contenthash; | |
1102 | if (file_exists($trashfile)) { | |
1103 | // we already have this content in trash, no need to move it there | |
1104 | unlink($contentfile); | |
1105 | return; | |
1106 | } | |
1107 | if (!is_dir($trashpath)) { | |
1108 | mkdir($trashpath, $this->dirpermissions, true); | |
1109 | } | |
1110 | rename($contentfile, $trashfile); | |
1111 | chmod($trashfile, $this->filepermissions); // fix permissions if needed | |
172dd12c | 1112 | } |
1113 | ||
1114 | /** | |
1115 | * Cron cleanup job. | |
bf9ffe27 PS |
1116 | * |
1117 | * @return void | |
172dd12c | 1118 | */ |
1119 | public function cron() { | |
a881f970 | 1120 | global $CFG, $DB; |
1aa01caf | 1121 | // remove trash pool files once a day |
1122 | // if you want to disable purging of trash put $CFG->fileslastcleanup=time(); into config.php | |
1123 | if (empty($CFG->fileslastcleanup) or $CFG->fileslastcleanup < time() - 60*60*24) { | |
1124 | require_once($CFG->libdir.'/filelib.php'); | |
a881f970 SH |
1125 | // Delete files that are associated with a context that no longer exists. |
1126 | mtrace('Cleaning up files from deleted contexts... ', ''); | |
1127 | $sql = "SELECT DISTINCT f.contextid | |
1128 | FROM {files} f | |
1129 | LEFT OUTER JOIN {context} c ON f.contextid = c.id | |
1130 | WHERE c.id IS NULL"; | |
1131 | if ($rs = $DB->get_recordset_sql($sql)) { | |
1132 | $fs = get_file_storage(); | |
1133 | foreach ($rs as $ctx) { | |
1134 | $fs->delete_area_files($ctx->contextid); | |
1135 | } | |
1136 | } | |
1137 | mtrace('done.'); | |
1138 | ||
1aa01caf | 1139 | mtrace('Deleting trash files... ', ''); |
1140 | fulldelete($this->trashdir); | |
1141 | set_config('fileslastcleanup', time()); | |
1142 | mtrace('done.'); | |
172dd12c | 1143 | } |
1144 | } | |
1145 | } | |
bf9ffe27 | 1146 |