172dd12c |
1 | <?php //$Id$ |
2 | |
3 | require_once("$CFG->libdir/file/stored_file.php"); |
4 | |
5 | class file_storage { |
6 | private $filedir; |
7 | |
8 | /** |
9 | * Contructor |
10 | * @param string $filedir full path to pool directory |
11 | */ |
12 | public function __construct() { |
13 | global $CFG; |
14 | if (isset($CFG->filedir)) { |
15 | $this->filedir = $CFG->filedir; |
16 | } else { |
17 | $this->filedir = $CFG->dataroot.'/filedir'; |
18 | } |
19 | |
20 | // make sure the file pool directory exists |
21 | if (!is_dir($this->filedir)) { |
22 | if (!check_dir_exists($this->filedir, true, true)) { |
23 | throw new file_exception('localfilecannotcreatefiledirs'); // permission trouble |
24 | } |
25 | // place warning file in file pool root |
26 | $fp = fopen($this->filedir.'/warning.txt', 'w'); |
27 | fwrite($fp, '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.'); |
28 | fclose($fp); |
29 | unset($fp); |
30 | } |
31 | } |
32 | |
33 | /** |
34 | * Calculates sha1 hash of unique full path name information |
35 | * @param int $contextid |
36 | * @param string $filearea |
37 | * @param int $itemid |
38 | * @param string $filepath |
39 | * @param string $filename |
40 | * @return string |
41 | */ |
42 | public static function get_pathname_hash($contextid, $filearea, $itemid, $filepath, $filename) { |
43 | return sha1($contextid.$filearea.$itemid.$filepath.$filename); |
44 | } |
45 | |
46 | /** |
47 | * Does this file exist? |
48 | * @param int $contextid |
49 | * @param string $filearea |
50 | * @param int $itemid |
51 | * @param string $filepath |
52 | * @param string $filename |
53 | * @return bool |
54 | */ |
55 | public function file_exists($contextid, $filearea, $itemid, $filepath, $filename) { |
56 | $filepath = clean_param($filepath, PARAM_PATH); |
57 | $filename = clean_param($filename, PARAM_FILE); |
58 | |
59 | if ($filename === '') { |
60 | $filename = '.'; |
61 | } |
62 | |
63 | $pathnamehash = $this->get_pathname_hash($contextid, $filearea, $itemid, $filepath, $filename); |
64 | return $this->file_exists_by_hash($pathnamehash); |
65 | } |
66 | |
67 | /** |
68 | * Does this file exist? |
69 | * @param string $pathnamehash |
70 | * @return bool |
71 | */ |
72 | public function file_exists_by_hash($pathnamehash) { |
73 | global $DB; |
74 | |
75 | return $DB->record_exists('files', array('pathnamehash'=>$pathnamehash)); |
76 | } |
77 | |
78 | /** |
79 | * Fetch file using local file id |
80 | * @param int $fileid |
81 | * @return mixed stored_file instance if exists, false if not |
82 | */ |
83 | public function get_file_by_id($fileid) { |
84 | global $DB; |
85 | |
86 | if ($file_record = $DB->get_record('files', array('id'=>$fileid))) { |
87 | return new stored_file($this, $file_record); |
88 | } else { |
89 | return false; |
90 | } |
91 | } |
92 | |
93 | /** |
94 | * Fetch file using local file full pathname hash |
95 | * @param string $pathnamehash |
96 | * @return mixed stored_file instance if exists, false if not |
97 | */ |
98 | public function get_file_by_hash($pathnamehash) { |
99 | global $DB; |
100 | |
101 | if ($file_record = $DB->get_record('files', array('pathnamehash'=>$pathnamehash))) { |
102 | return new stored_file($this, $file_record); |
103 | } else { |
104 | return false; |
105 | } |
106 | } |
107 | |
108 | /** |
109 | * Fetch file |
110 | * @param int $contextid |
111 | * @param string $filearea |
112 | * @param int $itemid |
113 | * @param string $filepath |
114 | * @param string $filename |
115 | * @return mixed stored_file instance if exists, false if not |
116 | */ |
117 | public function get_file($contextid, $filearea, $itemid, $filepath, $filename) { |
118 | global $DB; |
119 | |
120 | $filepath = clean_param($filepath, PARAM_PATH); |
121 | $filename = clean_param($filename, PARAM_FILE); |
122 | |
123 | if ($filename === '') { |
124 | $filename = '.'; |
125 | } |
126 | |
127 | $pathnamehash = $this->get_pathname_hash($contextid, $filearea, $itemid, $filepath, $filename); |
128 | return $this->get_file_by_hash($pathnamehash); |
129 | } |
130 | |
131 | /** |
132 | * Returns all area files (optionally limited by itemid) |
133 | * @param int $contextid |
134 | * @param string $filearea |
135 | * @param int $itemid (all files if not specified) |
136 | * @param string $sort |
137 | * @param bool $includedirs |
138 | * @return array of stored_files |
139 | */ |
140 | public function get_area_files($contextid, $filearea, $itemid=false, $sort="itemid, filepath, filename", $inludedirs=true) { |
141 | global $DB; |
142 | |
143 | $conditions = array('contextid'=>$contextid, 'filearea'=>$filearea); |
144 | if ($itemid !== false) { |
145 | $conditions['itemid'] = $itemid; |
146 | } |
147 | |
148 | $result = array(); |
149 | $file_records = $DB->get_records('files', $conditions, $sort); |
150 | foreach ($file_records as $file_record) { |
151 | if (!$inludedirs and $file_record->filename === '.') { |
152 | continue; |
153 | } |
154 | $result[] = new stored_file($this, $file_record); |
155 | } |
156 | return $result; |
157 | } |
158 | |
159 | /** |
160 | * Delete all area files (optionally limited by itemid) |
161 | * @param int $contextid |
162 | * @param string $filearea |
163 | * @param int $itemid (all files if not specified) |
164 | * @return success |
165 | */ |
166 | public function delete_area_files($contextid, $filearea, $itemid=false) { |
167 | global $DB; |
168 | |
169 | $conditions = array('contextid'=>$contextid, 'filearea'=>$filearea); |
170 | if ($itemid !== false) { |
171 | $conditions['itemid'] = $itemid; |
172 | } |
173 | |
174 | $success = true; |
175 | |
176 | $file_records = $DB->get_records('files', $conditions); |
177 | foreach ($file_records as $file_record) { |
178 | $stored_file = new stored_file($this, $file_record); |
179 | $success = $stored_file->delete() && $success; |
180 | } |
181 | |
182 | return $success; |
183 | } |
184 | |
185 | /** |
186 | * Recursively creates director |
187 | * @param int $contextid |
188 | * @param string $filearea |
189 | * @param int $itemid |
190 | * @param string $filepath |
191 | * @param string $filename |
192 | * @return bool success |
193 | */ |
194 | public function create_directory($contextid, $filearea, $itemid, $filepath, $userid=null) { |
195 | global $DB; |
196 | |
197 | // validate all parameters, we do not want any rubbish stored in database, right? |
198 | if (!is_number($contextid) or $contextid < 1) { |
199 | throw new file_exception('localfileproblem', 'Invalid contextid'); |
200 | } |
201 | |
202 | $filearea = clean_param($filearea, PARAM_SAFEDIR); |
203 | if ($filearea === '') { |
204 | throw new file_exception('localfileproblem', 'Invalid filearea'); |
205 | } |
206 | |
207 | if (!is_number($itemid) or $itemid < 0) { |
208 | throw new file_exception('localfileproblem', 'Invalid itemid'); |
209 | } |
210 | |
211 | $filepath = clean_param($filepath, PARAM_PATH); |
212 | if (strpos($filepath, '/') !== 0 or strrpos($filepath, '/') !== strlen($filepath)-1) { |
213 | // path must start and end with '/' |
214 | throw new file_exception('localfileproblem', 'Invalid file path'); |
215 | } |
216 | |
217 | $pathnamehash = $this->get_pathname_hash($contextid, $filearea, $itemid, $filepath, '.'); |
218 | |
219 | if ($dir_info = $this->get_file_by_hash($pathnamehash)) { |
220 | return $dir_info; |
221 | } |
222 | |
223 | static $contenthash = null; |
224 | if (!$contenthash) { |
225 | $this->add_to_pool_string(''); |
226 | $contenthash = sha1(''); |
227 | } |
228 | |
229 | $now = time(); |
230 | |
231 | $dir_record = new object(); |
232 | $dir_record->contextid = $contextid; |
233 | $dir_record->filearea = $filearea; |
234 | $dir_record->itemid = $itemid; |
235 | $dir_record->filepath = $filepath; |
236 | $dir_record->filename = '.'; |
237 | $dir_record->contenthash = $contenthash; |
238 | $dir_record->filesize = 0; |
239 | |
240 | $dir_record->timecreated = $now; |
241 | $dir_record->timemodified = $now; |
242 | $dir_record->mimetype = null; |
243 | $dir_record->userid = $userid; |
244 | |
245 | $dir_record->pathnamehash = $pathnamehash; |
246 | |
247 | $DB->insert_record('files', $dir_record); |
248 | $dir_info = $this->get_file_by_hash($pathnamehash); |
249 | |
250 | if ($filepath !== '/') { |
251 | //recurse to parent dirs |
252 | $filepath = trim($filepath, '/'); |
253 | $filepath = explode('/', $filepath); |
254 | array_pop($filepath); |
255 | $filepath = implode('/', $filepath); |
256 | $filepath = ($filepath === '') ? '/' : "/$filepath/"; |
257 | $this->create_directory($contextid, $filearea, $itemid, $filepath, $userid); |
258 | } |
259 | |
260 | return $dir_info; |
261 | } |
262 | |
263 | /** |
264 | * Add new local file based on existing local file |
265 | * @param mixed $file_record object or array describing changes |
266 | * @param int $fid id of existing local file |
267 | * @return object stored_file instance |
268 | */ |
269 | public function create_file_from_localfile($file_record, $fid) { |
270 | global $DB; |
271 | |
272 | $file_record = (array)$file_record; // we support arrays too |
273 | unset($file_record['id']); |
274 | unset($file_record['filesize']); |
275 | unset($file_record['contenthash']); |
276 | |
277 | $now = time(); |
278 | |
279 | if ($newrecord = $DB->get_record('files', array('id'=>$fid))) { |
280 | throw new file_exception('localfileproblem', 'File does not exist'); |
281 | } |
282 | |
283 | unset($newrecord->id); |
284 | |
285 | foreach ($file_record as $key=>$value) { |
286 | // validate all parameters, we do not want any rubbish stored in database, right? |
287 | if ($key == 'contextid' and (!is_number($value) or $value < 1)) { |
288 | throw new file_exception('localfileproblem', 'Invalid contextid'); |
289 | } |
290 | |
291 | if ($key == 'filearea') { |
292 | $value = clean_param($value, PARAM_SAFEDIR); |
293 | if ($value === '') { |
294 | throw new file_exception('localfileproblem', 'Invalid filearea'); |
295 | } |
296 | } |
297 | |
298 | if ($key == 'itemid' and (!is_number($value) or $value < 0)) { |
299 | throw new file_exception('localfileproblem', 'Invalid itemid'); |
300 | } |
301 | |
302 | |
303 | if ($key == 'filepath') { |
304 | $value = clean_param($value, PARAM_PATH); |
305 | if (strpos($value, '/') !== 0 or strpos($value, '/') !== strlen($value)-1) { |
306 | // path must start and end with '/' |
307 | throw new file_exception('localfileproblem', 'Invalid file path'); |
308 | } |
309 | } |
310 | |
311 | if ($key == 'filename') { |
312 | $value = clean_param($value, PARAM_FILE); |
313 | if ($value === '') { |
314 | // path must start and end with '/' |
315 | throw new file_exception('localfileproblem', 'Invalid file name'); |
316 | } |
317 | } |
318 | |
319 | $newrecord->$key = $value; |
320 | } |
321 | |
322 | $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename); |
323 | |
324 | try { |
325 | $newrecord->id = $DB->insert_record('files', $newrecord); |
326 | } catch (database_exception $e) { |
327 | $newrecord->id = false; |
328 | } |
329 | |
330 | if (!$newrecord->id) { |
331 | throw new stored_file_creation_exception($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, |
332 | $newrecord->filepath, $newrecord->filename); |
333 | } |
334 | |
335 | $this->create_directory($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); |
336 | |
337 | return new stored_file($this, $newrecord); |
338 | } |
339 | |
340 | /** |
341 | * Add new local file |
342 | * @param mixed $file_record object or array describing file |
343 | * @param string $path path to file or content of file |
344 | * @return object stored_file instance |
345 | */ |
346 | public function create_file_from_pathname($file_record, $pathname) { |
347 | global $DB; |
348 | |
349 | $file_record = (object)$file_record; // we support arrays too |
350 | |
351 | // validate all parameters, we do not want any rubbish stored in database, right? |
352 | if (!is_number($file_record->contextid) or $file_record->contextid < 1) { |
353 | throw new file_exception('localfileproblem', 'Invalid contextid'); |
354 | } |
355 | |
356 | $file_record->filearea = clean_param($file_record->filearea, PARAM_SAFEDIR); |
357 | if ($file_record->filearea === '') { |
358 | throw new file_exception('localfileproblem', 'Invalid filearea'); |
359 | } |
360 | |
361 | if (!is_number($file_record->itemid) or $file_record->itemid < 0) { |
362 | throw new file_exception('localfileproblem', 'Invalid itemid'); |
363 | } |
364 | |
365 | $file_record->filepath = clean_param($file_record->filepath, PARAM_PATH); |
366 | if (strpos($file_record->filepath, '/') !== 0 or strrpos($file_record->filepath, '/') !== strlen($file_record->filepath)-1) { |
367 | // path must start and end with '/' |
368 | throw new file_exception('localfileproblem', 'Invalid file path'); |
369 | } |
370 | |
371 | $file_record->filename = clean_param($file_record->filename, PARAM_FILE); |
372 | if ($file_record->filename === '') { |
373 | // path must start and end with '/' |
374 | throw new file_exception('localfileproblem', 'Invalid file name'); |
375 | } |
376 | |
377 | $now = time(); |
378 | |
379 | $newrecord = new object(); |
380 | |
381 | $newrecord->contextid = $file_record->contextid; |
382 | $newrecord->filearea = $file_record->filearea; |
383 | $newrecord->itemid = $file_record->itemid; |
384 | $newrecord->filepath = $file_record->filepath; |
385 | $newrecord->filename = $file_record->filename; |
386 | |
387 | $newrecord->timecreated = empty($file_record->timecreated) ? $now : $file_record->timecreated; |
388 | $newrecord->timemodified = empty($file_record->timemodified) ? $now : $file_record->timemodified; |
389 | $newrecord->mimetype = empty($file_record->mimetype) ? mimeinfo('type', $file_record->filename) : $file_record->mimetype; |
390 | $newrecord->userid = empty($file_record->userid) ? null : $file_record->userid; |
391 | |
392 | list($newrecord->contenthash, $newrecord->filesize, $newfile) = $this->add_to_pool_pathname($pathname); |
393 | |
394 | $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename); |
395 | |
396 | try { |
397 | $newrecord->id = $DB->insert_record('files', $newrecord); |
398 | } catch (database_exception $e) { |
399 | $newrecord->id = false; |
400 | } |
401 | |
402 | if (!$newrecord->id) { |
403 | if ($newfile) { |
404 | $this->mark_delete_candidate($newrecord->contenthash); |
405 | } |
406 | throw new stored_file_creation_exception($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, |
407 | $newrecord->filepath, $newrecord->filename); |
408 | } |
409 | |
410 | $this->create_directory($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); |
411 | |
412 | return new stored_file($this, $newrecord); |
413 | } |
414 | |
415 | /** |
416 | * Add new local file |
417 | * @param mixed $file_record object or array describing file |
418 | * @param string $content content of file |
419 | * @return object stored_file instance |
420 | */ |
421 | public function create_file_from_string($file_record, $content) { |
422 | global $DB; |
423 | |
424 | $file_record = (object)$file_record; // we support arrays too |
425 | |
426 | // validate all parameters, we do not want any rubbish stored in database, right? |
427 | if (!is_number($file_record->contextid) or $file_record->contextid < 1) { |
428 | throw new file_exception('localfileproblem', 'Invalid contextid'); |
429 | } |
430 | |
431 | $file_record->filearea = clean_param($file_record->filearea, PARAM_SAFEDIR); |
432 | if ($file_record->filearea === '') { |
433 | throw new file_exception('localfileproblem', 'Invalid filearea'); |
434 | } |
435 | |
436 | if (!is_number($file_record->itemid) or $file_record->itemid < 0) { |
437 | throw new file_exception('localfileproblem', 'Invalid itemid'); |
438 | } |
439 | |
440 | $file_record->filepath = clean_param($file_record->filepath, PARAM_PATH); |
441 | if (strpos($file_record->filepath, '/') !== 0 or strrpos($file_record->filepath, '/') !== strlen($file_record->filepath)-1) { |
442 | // path must start and end with '/' |
443 | throw new file_exception('localfileproblem', 'Invalid file path'); |
444 | } |
445 | |
446 | $file_record->filename = clean_param($file_record->filename, PARAM_FILE); |
447 | if ($file_record->filename === '') { |
448 | // path must start and end with '/' |
449 | throw new file_exception('localfileproblem', 'Invalid file name'); |
450 | } |
451 | |
452 | $now = time(); |
453 | |
454 | $newrecord = new object(); |
455 | |
456 | $newrecord->contextid = $file_record->contextid; |
457 | $newrecord->filearea = $file_record->filearea; |
458 | $newrecord->itemid = $file_record->itemid; |
459 | $newrecord->filepath = $file_record->filepath; |
460 | $newrecord->filename = $file_record->filename; |
461 | |
462 | $newrecord->timecreated = empty($file_record->timecreated) ? $now : $file_record->timecreated; |
463 | $newrecord->timemodified = empty($file_record->timemodified) ? $now : $file_record->timemodified; |
464 | $newrecord->mimetype = empty($file_record->mimetype) ? mimeinfo('type', $file_record->filename) : $file_record->mimetype; |
465 | $newrecord->userid = empty($file_record->userid) ? null : $file_record->userid; |
466 | |
467 | list($newrecord->contenthash, $newrecord->filesize, $newfile) = $this->add_to_pool_string($content); |
468 | |
469 | $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename); |
470 | |
471 | try { |
472 | $newrecord->id = $DB->insert_record('files', $newrecord); |
473 | } catch (database_exception $e) { |
474 | $newrecord->id = false; |
475 | } |
476 | |
477 | if (!$newrecord->id) { |
478 | if ($newfile) { |
479 | $this->mark_delete_candidate($newrecord->contenthash); |
480 | } |
481 | throw new stored_file_creation_exception($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, |
482 | $newrecord->filepath, $newrecord->filename); |
483 | } |
484 | |
485 | $this->create_directory($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); |
486 | |
487 | return new stored_file($this, $newrecord); |
488 | } |
489 | |
490 | /** |
491 | * Add file content to sha1 pool |
492 | * @param string $pathname path to file |
493 | * @param string sha1 hash of content if known (performance only) |
494 | * @return array(contenthash, filesize, newfile) |
495 | */ |
496 | public function add_to_pool_pathname($pathname, $contenthash=null) { |
497 | if (!is_readable($pathname)) { |
498 | throw new file_exception('localfilecannotread'); |
499 | } |
500 | |
501 | if (is_null($contenthash)) { |
502 | $contenthash = sha1_file($pathname); |
503 | } |
504 | |
505 | $filesize = filesize($pathname); |
506 | |
507 | $hashpath = $this->path_from_hash($contenthash); |
508 | $hashfile = "$hashpath/$contenthash"; |
509 | |
510 | if (file_exists($hashfile)) { |
511 | if (filesize($hashfile) !== $filesize) { |
512 | throw new file_pool_content_exception($contenthash); |
513 | } |
514 | $newfile = false; |
515 | |
516 | } else { |
517 | if (!check_dir_exists($hashpath, true, true)) { |
518 | throw new file_exception('localfilecannotcreatefiledirs'); // permission trouble |
519 | } |
520 | $newfile = true; |
521 | |
522 | $fs = fopen($pathname, 'rb'); |
523 | $fp = fopen($hashfile, 'wb'); |
524 | while(!feof($fs)) { |
525 | $buf = fread($fs, 65536); |
526 | if ($buf === false) { |
527 | throw new file_exception('localfilecannotread'); |
528 | } |
529 | fwrite($fp, $buf); |
530 | } |
531 | fclose($fp); |
532 | fclose($fs); |
533 | |
534 | if (filesize($hashfile) !== $filesize) { |
535 | @unlink($hashfile); |
536 | throw new file_pool_content_exception($contenthash); |
537 | } |
538 | } |
539 | |
540 | |
541 | return array($contenthash, $filesize, $newfile); |
542 | } |
543 | |
544 | /** |
545 | * Add string content to sha1 pool |
546 | * @param string $content file content - binary string |
547 | * @return array(contenthash, filesize, newfile) |
548 | */ |
549 | public function add_to_pool_string($content) { |
550 | $contenthash = sha1($content); |
551 | $filesize = strlen($content); // binary length |
552 | |
553 | $hashpath = $this->path_from_hash($contenthash); |
554 | $hashfile = "$hashpath/$contenthash"; |
555 | |
556 | |
557 | if (file_exists($hashfile)) { |
558 | if (filesize($hashfile) !== $filesize) { |
559 | throw new file_pool_content_exception($contenthash); |
560 | } |
561 | $newfile = false; |
562 | |
563 | } else { |
564 | if (!check_dir_exists($hashpath, true, true)) { |
565 | throw new file_exception('localfilecannotcreatefiledirs'); // permission trouble |
566 | } |
567 | $newfile = true; |
568 | |
569 | $fp = fopen($hashfile, 'wb'); |
570 | |
571 | fwrite($fp, $content); |
572 | fclose($fp); |
573 | |
574 | if (filesize($hashfile) !== $filesize) { |
575 | @unlink($hashfile); |
576 | throw new file_pool_content_exception($contenthash); |
577 | } |
578 | } |
579 | |
580 | return array($contenthash, $filesize, $newfile); |
581 | } |
582 | |
583 | /** |
584 | * Return path to file with given hash |
585 | * |
586 | * DO NOT USE - should be protected, but protected is dumb in PHP |
587 | * |
588 | * @param string $contenthash |
589 | * @return string expected file location |
590 | */ |
591 | public function path_from_hash($contenthash) { |
592 | $l1 = $contenthash[0].$contenthash[1]; |
593 | $l2 = $contenthash[2].$contenthash[3]; |
594 | $l3 = $contenthash[4].$contenthash[5]; |
595 | return "$this->filedir/$l1/$l2/$l3"; |
596 | } |
597 | |
598 | /** |
599 | * Marks pool file as candidate for deleting |
600 | * @param string $contenthash |
601 | */ |
602 | public function mark_delete_candidate($contenthash) { |
603 | global $DB; |
604 | |
605 | if ($DB->record_exists('files_cleanup', array('contenthash'=>$contenthash))) { |
606 | return; |
607 | } |
608 | $rec = new object(); |
609 | $rec->contenthash = $contenthash; |
610 | $DB->insert_record('files_cleanup', $rec); |
611 | } |
612 | |
613 | /** |
614 | * Cron cleanup job. |
615 | */ |
616 | public function cron() { |
617 | global $DB; |
618 | |
619 | //TODO: there is a small chance that reused files might be deleted |
620 | // if this function takes too long we should add some table locking here |
621 | |
622 | $sql = "SELECT 1 AS id, fc.contenthash |
623 | FROM {files_cleanup} fc |
624 | LEFT JOIN {files} f ON f.contenthash = fc.contenthash |
625 | WHERE f.id IS NULL"; |
626 | while ($hash = $DB->get_record_sql($sql, null, true)) { |
627 | $file = $this->path_from_hash($hash->contenthash).'/'.$hash->contenthash; |
628 | @unlink($file); |
629 | $DB->delete_records('files_cleanup', array('contenthash'=>$hash->contenthash)); |
630 | } |
631 | } |
632 | } |