weekly release 2.6dev
[moodle.git] / lib / filestorage / stored_file.php
CommitLineData
16a95e8f 1<?php
16a95e8f 2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17
18/**
8496fdac 19 * Definition of a class stored_file.
16a95e8f 20 *
d2b7803e
DC
21 * @package core_files
22 * @copyright 2008 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
16a95e8f 24 */
25
64f93798
PS
26defined('MOODLE_INTERNAL') || die();
27
71ab7e51 28require_once($CFG->dirroot . '/lib/filestorage/file_progress.php');
29
172dd12c 30/**
8496fdac
PS
31 * Class representing local files stored in a sha1 file pool.
32 *
33 * Since Moodle 2.0 file contents are stored in sha1 pool and
34 * all other file information is stored in new "files" database table.
35 *
d2b7803e
DC
36 * @package core_files
37 * @category files
8496fdac
PS
38 * @copyright 2008 Petr Skoda {@link http://skodak.org}
39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 * @since Moodle 2.0
172dd12c 41 */
42class stored_file {
8496fdac 43 /** @var file_storage file storage pool instance */
172dd12c 44 private $fs;
04e3b007 45 /** @var stdClass record from the files table left join files_reference table */
172dd12c 46 private $file_record;
693ef3a8
PS
47 /** @var string location of content files */
48 private $filedir;
67233725 49 /** @var repository repository plugin instance */
291a3d1f 50 private $repository;
172dd12c 51
52 /**
8496fdac
PS
53 * Constructor, this constructor should be called ONLY from the file_storage class!
54 *
55 * @param file_storage $fs file storage instance
d2b7803e
DC
56 * @param stdClass $file_record description of file
57 * @param string $filedir location of file directory with sh1 named content files
172dd12c 58 */
693ef3a8 59 public function __construct(file_storage $fs, stdClass $file_record, $filedir) {
67233725 60 global $DB, $CFG;
8496fdac
PS
61 $this->fs = $fs;
62 $this->file_record = clone($file_record); // prevent modifications
693ef3a8 63 $this->filedir = $filedir; // keep secret, do not expose!
67233725
DC
64
65 if (!empty($file_record->repositoryid)) {
b99065be 66 require_once("$CFG->dirroot/repository/lib.php");
67233725
DC
67 $this->repository = repository::get_repository_by_id($file_record->repositoryid, SYSCONTEXTID);
68 if ($this->repository->supported_returntypes() & FILE_REFERENCE != FILE_REFERENCE) {
69 // Repository cannot do file reference.
70 throw new moodle_exception('error');
71 }
72 } else {
73 $this->repository = null;
74 }
42aa6e15
MG
75 // make sure all reference fields exist in file_record even when it is not a reference
76 foreach (array('referencelastsync', 'referencelifetime', 'referencefileid', 'reference', 'repositoryid') as $key) {
77 if (empty($this->file_record->$key)) {
78 $this->file_record->$key = null;
79 }
80 }
67233725
DC
81 }
82
83 /**
84 * Whether or not this is a external resource
85 *
86 * @return bool
87 */
88 public function is_external_file() {
89 return !empty($this->repository);
90 }
91
92 /**
93 * Update some file record fields
94 * NOTE: Must remain protected
95 *
96 * @param stdClass $dataobject
97 */
98 protected function update($dataobject) {
99 global $DB;
100 $keys = array_keys((array)$this->file_record);
101 foreach ($dataobject as $field => $value) {
102 if (in_array($field, $keys)) {
103 if ($field == 'contextid' and (!is_number($value) or $value < 1)) {
104 throw new file_exception('storedfileproblem', 'Invalid contextid');
105 }
106
107 if ($field == 'component') {
108 $value = clean_param($value, PARAM_COMPONENT);
109 if (empty($value)) {
110 throw new file_exception('storedfileproblem', 'Invalid component');
111 }
112 }
113
114 if ($field == 'filearea') {
115 $value = clean_param($value, PARAM_AREA);
116 if (empty($value)) {
117 throw new file_exception('storedfileproblem', 'Invalid filearea');
118 }
119 }
120
121 if ($field == 'itemid' and (!is_number($value) or $value < 0)) {
122 throw new file_exception('storedfileproblem', 'Invalid itemid');
123 }
124
125
126 if ($field == 'filepath') {
127 $value = clean_param($value, PARAM_PATH);
128 if (strpos($value, '/') !== 0 or strrpos($value, '/') !== strlen($value)-1) {
129 // path must start and end with '/'
130 throw new file_exception('storedfileproblem', 'Invalid file path');
131 }
132 }
133
134 if ($field == 'filename') {
fc4e8034
DC
135 // folder has filename == '.', so we pass this
136 if ($value != '.') {
137 $value = clean_param($value, PARAM_FILE);
138 }
67233725
DC
139 if ($value === '') {
140 throw new file_exception('storedfileproblem', 'Invalid file name');
141 }
142 }
143
144 if ($field === 'timecreated' or $field === 'timemodified') {
145 if (!is_number($value)) {
146 throw new file_exception('storedfileproblem', 'Invalid timestamp');
147 }
148 if ($value < 0) {
149 $value = 0;
150 }
151 }
152
42aa6e15 153 if ($field === 'referencefileid') {
1c0da849
DM
154 if (!is_null($value) and !is_number($value)) {
155 throw new file_exception('storedfileproblem', 'Invalid reference info');
156 }
67233725
DC
157 }
158
159 // adding the field
160 $this->file_record->$field = $value;
161 } else {
162 throw new coding_exception("Invalid field name, $field doesn't exist in file record");
163 }
164 }
8177b7b9 165 // Validate mimetype field
70c1bc84
DC
166 // we don't use {@link stored_file::get_content_file_location()} here becaues it will try to update file_record
167 $pathname = $this->get_pathname_by_contenthash();
8177b7b9
DC
168 // try to recover the content from trash
169 if (!is_readable($pathname)) {
170 if (!$this->fs->try_content_recovery($this) or !is_readable($pathname)) {
171 throw new file_exception('storedfilecannotread', '', $pathname);
172 }
173 }
efa28f30 174 $mimetype = $this->fs->mimetype($pathname, $this->file_record->filename);
8177b7b9
DC
175 $this->file_record->mimetype = $mimetype;
176
67233725
DC
177 $DB->update_record('files', $this->file_record);
178 }
179
180 /**
181 * Rename filename
182 *
183 * @param string $filepath file path
184 * @param string $filename file name
185 */
186 public function rename($filepath, $filename) {
7051415c
DC
187 if ($this->fs->file_exists($this->get_contextid(), $this->get_component(), $this->get_filearea(), $this->get_itemid(), $filepath, $filename)) {
188 throw new file_exception('storedfilenotcreated', '', 'file exists, cannot rename');
189 }
67233725
DC
190 $filerecord = new stdClass;
191 $filerecord->filepath = $filepath;
192 $filerecord->filename = $filename;
193 // populate the pathname hash
194 $filerecord->pathnamehash = $this->fs->get_pathname_hash($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $filepath, $filename);
195 $this->update($filerecord);
196 }
197
198 /**
199 * Replace the content by providing another stored_file instance
200 *
201 * @param stored_file $storedfile
202 */
203 public function replace_content_with(stored_file $storedfile) {
204 $contenthash = $storedfile->get_contenthash();
205 $this->set_contenthash($contenthash);
14b7e500 206 $this->set_filesize($storedfile->get_filesize());
67233725
DC
207 }
208
e9e32b1d
MG
209 /**
210 * Replaces the fields that might have changed when file was overriden in filepicker:
6dd92c02 211 * reference, contenthash, filesize, userid
e9e32b1d 212 *
935429af
MG
213 * Note that field 'source' must be updated separately because
214 * it has different format for draft and non-draft areas and
215 * this function will usually be used to replace non-draft area
216 * file with draft area file.
e9e32b1d
MG
217 *
218 * @param stored_file $newfile
219 * @throws coding_exception
220 */
221 public function replace_file_with(stored_file $newfile) {
222 if ($newfile->get_referencefileid() &&
223 $this->fs->get_references_count_by_storedfile($this)) {
224 // The new file is a reference.
225 // The current file has other local files referencing to it.
226 // Double reference is not allowed.
227 throw new moodle_exception('errordoublereference', 'repository');
228 }
229
230 $filerecord = new stdClass;
231 $contenthash = $newfile->get_contenthash();
232 if ($this->fs->content_exists($contenthash)) {
233 $filerecord->contenthash = $contenthash;
234 } else {
235 throw new file_exception('storedfileproblem', 'Invalid contenthash, content must be already in filepool', $contenthash);
236 }
237 $filerecord->filesize = $newfile->get_filesize();
238 $filerecord->referencefileid = $newfile->get_referencefileid();
6dd92c02 239 $filerecord->userid = $newfile->get_userid();
e9e32b1d
MG
240 $this->update($filerecord);
241 }
242
67233725 243 /**
2fa8aea2 244 * Unlink the stored file from the referenced file
67233725 245 *
2fa8aea2
DM
246 * This methods destroys the link to the record in files_reference table. This effectively
247 * turns the stored file from being an alias to a plain copy. However, the caller has
248 * to make sure that the actual file's content has beed synced prior to calling this method.
67233725
DC
249 */
250 public function delete_reference() {
251 global $DB;
61506a0a 252
2fa8aea2
DM
253 if (!$this->is_external_file()) {
254 throw new coding_exception('An attempt to unlink a non-reference file.');
255 }
256
e3c02118
DC
257 $transaction = $DB->start_delegated_transaction();
258
1c0da849
DM
259 // Are we the only one referring to the original file? If so, delete the
260 // referenced file record. Note we do not use file_storage::search_references_count()
261 // here because we want to count draft files too and we are at a bit lower access level here.
262 $countlinks = $DB->count_records('files',
263 array('referencefileid' => $this->file_record->referencefileid));
264 if ($countlinks == 1) {
265 $DB->delete_records('files_reference', array('id' => $this->file_record->referencefileid));
266 }
61506a0a 267
1c0da849
DM
268 // Update the underlying record in the database.
269 $update = new stdClass();
270 $update->referencefileid = null;
1c0da849 271 $this->update($update);
61506a0a 272
e3c02118
DC
273 $transaction->allow_commit();
274
1c0da849
DM
275 // Update our properties and the record in the memory.
276 $this->repository = null;
277 $this->file_record->repositoryid = null;
278 $this->file_record->reference = null;
279 $this->file_record->referencefileid = null;
280 $this->file_record->referencelastsync = null;
281 $this->file_record->referencelifetime = null;
172dd12c 282 }
283
284 /**
285 * Is this a directory?
8496fdac
PS
286 *
287 * Directories are only emulated, internally they are stored as empty
288 * files with a "." instead of name - this means empty directory contains
289 * exactly one empty file with name dot.
290 *
291 * @return bool true means directory, false means file
172dd12c 292 */
293 public function is_directory() {
8496fdac 294 return ($this->file_record->filename === '.');
172dd12c 295 }
296
297 /**
8496fdac
PS
298 * Delete file from files table.
299 *
300 * The content of files stored in sha1 pool is reclaimed
301 * later - the occupied disk space is reclaimed much later.
302 *
303 * @return bool always true or exception if error occurred
172dd12c 304 */
305 public function delete() {
306 global $DB;
e3c02118 307
e029dff4
PS
308 if ($this->is_directory()) {
309 // Directories can not be referenced, just delete the record.
310 $DB->delete_records('files', array('id'=>$this->file_record->id));
311
312 } else {
313 $transaction = $DB->start_delegated_transaction();
e3c02118 314
e029dff4
PS
315 // If there are other files referring to this file, convert them to copies.
316 if ($files = $this->fs->get_references_by_storedfile($this)) {
317 foreach ($files as $file) {
318 $this->fs->import_external_file($file);
319 }
67233725 320 }
3b2465fe 321
e029dff4
PS
322 // If this file is a reference (alias) to another file, unlink it first.
323 if ($this->is_external_file()) {
324 $this->delete_reference();
325 }
3b2465fe 326
e029dff4
PS
327 // Now delete the file record.
328 $DB->delete_records('files', array('id'=>$this->file_record->id));
e3c02118 329
e029dff4
PS
330 $transaction->allow_commit();
331 }
e3c02118 332
e029dff4 333 // Move pool file to trash if content not needed any more.
1aa01caf 334 $this->fs->deleted_file_cleanup($this->file_record->contenthash);
8496fdac 335 return true; // BC only
172dd12c 336 }
337
338 /**
70c1bc84 339 * Get file pathname by contenthash
8496fdac 340 *
70c1bc84
DC
341 * NOTE, this function is not calling sync_external_file, it assume the contenthash is current
342 * Protected - developers must not gain direct access to this function.
8496fdac
PS
343 *
344 * @return string full path to pool file with file content
70c1bc84
DC
345 */
346 protected function get_pathname_by_contenthash() {
67233725 347 // Detect is local file or not.
17d9269f 348 $contenthash = $this->file_record->contenthash;
349 $l1 = $contenthash[0].$contenthash[1];
350 $l2 = $contenthash[2].$contenthash[3];
693ef3a8 351 return "$this->filedir/$l1/$l2/$contenthash";
172dd12c 352 }
353
70c1bc84
DC
354 /**
355 * Get file pathname by given contenthash, this method will try to sync files
356 *
357 * Protected - developers must not gain direct access to this function.
358 *
359 * NOTE: do not make this public, we must not modify or delete the pool files directly! ;-)
360 *
361 * @return string full path to pool file with file content
362 **/
363 protected function get_content_file_location() {
364 $this->sync_external_file();
365 return $this->get_pathname_by_contenthash();
366 }
367
5035a8b4 368 /**
369 * adds this file path to a curl request (POST only)
370 *
371 * @param curl $curlrequest the curl request object
372 * @param string $key what key to use in the POST request
8496fdac 373 * @return void
5035a8b4 374 */
375 public function add_to_curl_request(&$curlrequest, $key) {
376 $curlrequest->_tmp_file_post_params[$key] = '@' . $this->get_content_file_location();
377 }
378
172dd12c 379 /**
380 * Returns file handle - read only mode, no writing allowed into pool files!
8496fdac
PS
381 *
382 * When you want to modify a file, create a new file and delete the old one.
383 *
384 * @return resource file handle
172dd12c 385 */
386 public function get_content_file_handle() {
387 $path = $this->get_content_file_location();
388 if (!is_readable($path)) {
1aa01caf 389 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
d610cb89 390 throw new file_exception('storedfilecannotread', '', $path);
1aa01caf 391 }
172dd12c 392 }
67233725 393 return fopen($path, 'rb'); // Binary reading only!!
172dd12c 394 }
395
396 /**
8496fdac 397 * Dumps file content to page.
172dd12c 398 */
399 public function readfile() {
400 $path = $this->get_content_file_location();
401 if (!is_readable($path)) {
1aa01caf 402 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
d610cb89 403 throw new file_exception('storedfilecannotread', '', $path);
1aa01caf 404 }
172dd12c 405 }
137885b7 406 readfile_allow_large($path, $this->get_filesize());
172dd12c 407 }
408
409 /**
8496fdac
PS
410 * Returns file content as string.
411 *
172dd12c 412 * @return string content
413 */
414 public function get_content() {
415 $path = $this->get_content_file_location();
416 if (!is_readable($path)) {
1aa01caf 417 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
d610cb89 418 throw new file_exception('storedfilecannotread', '', $path);
1aa01caf 419 }
172dd12c 420 }
421 return file_get_contents($this->get_content_file_location());
422 }
423
6c0e2d08 424 /**
8496fdac
PS
425 * Copy content of file to given pathname.
426 *
427 * @param string $pathname real path to the new file
6c0e2d08 428 * @return bool success
429 */
430 public function copy_content_to($pathname) {
431 $path = $this->get_content_file_location();
432 if (!is_readable($path)) {
1aa01caf 433 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
d610cb89 434 throw new file_exception('storedfilecannotread', '', $path);
1aa01caf 435 }
6c0e2d08 436 }
437 return copy($path, $pathname);
438 }
439
4e782b32
RT
440 /**
441 * Copy content of file to temporary folder and returns file path
442 *
443 * @param string $dir name of the temporary directory
444 * @param string $fileprefix prefix of temporary file.
445 * @return string|bool path of temporary file or false.
446 */
447 public function copy_content_to_temp($dir = 'files', $fileprefix = 'tempup_') {
448 $tempfile = false;
449 if (!$dir = make_temp_directory($dir)) {
450 return false;
451 }
452 if (!$tempfile = tempnam($dir, $fileprefix)) {
453 return false;
454 }
455 if (!$this->copy_content_to($tempfile)) {
456 // something went wrong
457 @unlink($tempfile);
458 return false;
459 }
460 return $tempfile;
461 }
462
17d9269f 463 /**
8496fdac
PS
464 * List contents of archive.
465 *
d2b7803e 466 * @param file_packer $packer file packer instance
c78a0558 467 * @return array of file infos
468 */
469 public function list_files(file_packer $packer) {
470 $archivefile = $this->get_content_file_location();
471 return $packer->list_files($archivefile);
472 }
473
474 /**
8496fdac
PS
475 * Extract file to given file path (real OS filesystem), existing files are overwritten.
476 *
d2b7803e 477 * @param file_packer $packer file packer instance
0b0bfa93 478 * @param string $pathname target directory
71ab7e51 479 * @param file_progress $progress Progress indicator callback or null if not required
8496fdac 480 * @return array|bool list of processed files; false if error
17d9269f 481 */
71ab7e51 482 public function extract_to_pathname(file_packer $packer, $pathname,
483 file_progress $progress = null) {
0b0bfa93 484 $archivefile = $this->get_content_file_location();
71ab7e51 485 return $packer->extract_to_pathname($archivefile, $pathname, null, $progress);
17d9269f 486 }
487
488 /**
8496fdac
PS
489 * Extract file to given file path (real OS filesystem), existing files are overwritten.
490 *
d2b7803e
DC
491 * @param file_packer $packer file packer instance
492 * @param int $contextid context ID
493 * @param string $component component
494 * @param string $filearea file area
495 * @param int $itemid item ID
496 * @param string $pathbase path base
497 * @param int $userid user ID
71ab7e51 498 * @param file_progress $progress Progress indicator callback or null if not required
8496fdac 499 * @return array|bool list of processed files; false if error
17d9269f 500 */
71ab7e51 501 public function extract_to_storage(file_packer $packer, $contextid,
502 $component, $filearea, $itemid, $pathbase, $userid = null, file_progress $progress = null) {
0b0bfa93 503 $archivefile = $this->get_content_file_location();
71ab7e51 504 return $packer->extract_to_storage($archivefile, $contextid,
505 $component, $filearea, $itemid, $pathbase, $userid, $progress);
17d9269f 506 }
507
b1897a6d 508 /**
8496fdac
PS
509 * Add file/directory into archive.
510 *
d2b7803e 511 * @param file_archive $filearch file archive instance
c78a0558 512 * @param string $archivepath pathname in archive
b1897a6d 513 * @return bool success
514 */
0b0bfa93 515 public function archive_file(file_archive $filearch, $archivepath) {
b1897a6d 516 if ($this->is_directory()) {
0b0bfa93 517 return $filearch->add_directory($archivepath);
b1897a6d 518 } else {
519 $path = $this->get_content_file_location();
520 if (!is_readable($path)) {
521 return false;
522 }
0b0bfa93 523 return $filearch->add_file_from_pathname($archivepath, $path);
b1897a6d 524 }
525 }
526
797f19e8 527 /**
528 * Returns information about image,
529 * information is determined from the file content
d2b7803e 530 *
797f19e8 531 * @return mixed array with width, height and mimetype; false if not an image
532 */
533 public function get_imageinfo() {
b7725e30
MG
534 $path = $this->get_content_file_location();
535 if (!is_readable($path)) {
536 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
537 throw new file_exception('storedfilecannotread', '', $path);
538 }
539 }
540 $mimetype = $this->get_mimetype();
541 if (!preg_match('|^image/|', $mimetype) || !filesize($path) || !($imageinfo = getimagesize($path))) {
797f19e8 542 return false;
543 }
544 $image = array('width'=>$imageinfo[0], 'height'=>$imageinfo[1], 'mimetype'=>image_type_to_mime_type($imageinfo[2]));
545 if (empty($image['width']) or empty($image['height']) or empty($image['mimetype'])) {
546 // gd can not parse it, sorry
547 return false;
548 }
549 return $image;
550 }
551
552 /**
553 * Verifies the file is a valid web image - gif, png and jpeg only.
8496fdac 554 *
797f19e8 555 * It should be ok to serve this image from server without any other security workarounds.
8496fdac 556 *
797f19e8 557 * @return bool true if file ok
558 */
559 public function is_valid_image() {
560 $mimetype = $this->get_mimetype();
559276b1 561 if (!file_mimetype_in_typegroup($mimetype, 'web_image')) {
797f19e8 562 return false;
563 }
564 if (!$info = $this->get_imageinfo()) {
565 return false;
566 }
567 if ($info['mimetype'] !== $mimetype) {
568 return false;
569 }
570 // ok, GD likes this image
571 return true;
572 }
573
4b6b5ce7 574 /**
8496fdac
PS
575 * Returns parent directory, creates missing parents if needed.
576 *
577 * @return stored_file
4b6b5ce7 578 */
579 public function get_parent_directory() {
580 if ($this->file_record->filepath === '/' and $this->file_record->filename === '.') {
581 //root dir does not have parent
582 return null;
583 }
584
585 if ($this->file_record->filename !== '.') {
64f93798 586 return $this->fs->create_directory($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $this->file_record->filepath);
4b6b5ce7 587 }
588
589 $filepath = $this->file_record->filepath;
590 $filepath = trim($filepath, '/');
591 $dirs = explode('/', $filepath);
592 array_pop($dirs);
593 $filepath = implode('/', $dirs);
594 $filepath = ($filepath === '') ? '/' : "/$filepath/";
595
64f93798 596 return $this->fs->create_directory($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $filepath);
4b6b5ce7 597 }
598
16a95e8f 599 /**
0b2bfbd1 600 * Synchronize file if it is a reference and needs synchronizing
67233725 601 *
0b2bfbd1 602 * Updates contenthash and filesize
67233725
DC
603 */
604 public function sync_external_file() {
0b2bfbd1
MG
605 global $CFG;
606 if (!empty($this->file_record->referencefileid)) {
67233725 607 require_once($CFG->dirroot.'/repository/lib.php');
0b2bfbd1 608 repository::sync_external_file($this);
67233725 609 }
67233725
DC
610 }
611
612 /**
613 * Returns context id of the file
8496fdac 614 *
16a95e8f 615 * @return int context id
616 */
172dd12c 617 public function get_contextid() {
618 return $this->file_record->contextid;
619 }
620
16a95e8f 621 /**
64f93798
PS
622 * Returns component name - this is the owner of the areas,
623 * nothing else is allowed to read or modify the files directly!!
624 *
625 * @return string
626 */
627 public function get_component() {
628 return $this->file_record->component;
629 }
630
631 /**
632 * Returns file area name, this divides files of one component into groups with different access control.
633 * All files in one area have the same access control.
8496fdac 634 *
16a95e8f 635 * @return string
636 */
172dd12c 637 public function get_filearea() {
638 return $this->file_record->filearea;
639 }
640
16a95e8f 641 /**
8496fdac
PS
642 * Returns returns item id of file.
643 *
16a95e8f 644 * @return int
645 */
172dd12c 646 public function get_itemid() {
647 return $this->file_record->itemid;
648 }
649
16a95e8f 650 /**
651 * Returns file path - starts and ends with /, \ are not allowed.
8496fdac 652 *
16a95e8f 653 * @return string
654 */
172dd12c 655 public function get_filepath() {
656 return $this->file_record->filepath;
657 }
658
16a95e8f 659 /**
660 * Returns file name or '.' in case of directories.
8496fdac 661 *
16a95e8f 662 * @return string
663 */
172dd12c 664 public function get_filename() {
665 return $this->file_record->filename;
666 }
667
16a95e8f 668 /**
669 * Returns id of user who created the file.
8496fdac 670 *
16a95e8f 671 * @return int
672 */
172dd12c 673 public function get_userid() {
674 return $this->file_record->userid;
675 }
676
16a95e8f 677 /**
678 * Returns the size of file in bytes.
8496fdac 679 *
16a95e8f 680 * @return int bytes
681 */
172dd12c 682 public function get_filesize() {
67233725 683 $this->sync_external_file();
172dd12c 684 return $this->file_record->filesize;
685 }
686
61506a0a
DC
687 /**
688 * Returns the size of file in bytes.
689 *
690 * @param int $filesize bytes
691 */
692 public function set_filesize($filesize) {
693 $filerecord = new stdClass;
694 $filerecord->filesize = $filesize;
695 $this->update($filerecord);
696 }
697
16a95e8f 698 /**
8496fdac
PS
699 * Returns mime type of file.
700 *
16a95e8f 701 * @return string
702 */
172dd12c 703 public function get_mimetype() {
704 return $this->file_record->mimetype;
705 }
706
16a95e8f 707 /**
8496fdac
PS
708 * Returns unix timestamp of file creation date.
709 *
16a95e8f 710 * @return int
711 */
172dd12c 712 public function get_timecreated() {
713 return $this->file_record->timecreated;
714 }
715
16a95e8f 716 /**
8496fdac
PS
717 * Returns unix timestamp of last file modification.
718 *
16a95e8f 719 * @return int
720 */
172dd12c 721 public function get_timemodified() {
67233725 722 $this->sync_external_file();
172dd12c 723 return $this->file_record->timemodified;
724 }
6c0e2d08 725
67233725
DC
726 /**
727 * set timemodified
728 *
729 * @param int $timemodified
730 */
731 public function set_timemodified($timemodified) {
732 $filerecord = new stdClass;
733 $filerecord->timemodified = $timemodified;
734 $this->update($filerecord);
735 }
736
16a95e8f 737 /**
8496fdac
PS
738 * Returns file status flag.
739 *
16a95e8f 740 * @return int 0 means file OK, anything else is a problem and file can not be used
741 */
6c0e2d08 742 public function get_status() {
743 return $this->file_record->status;
744 }
ee03a651 745
16a95e8f 746 /**
8496fdac
PS
747 * Returns file id.
748 *
16a95e8f 749 * @return int
750 */
ee03a651 751 public function get_id() {
752 return $this->file_record->id;
753 }
4284e1cc 754
16a95e8f 755 /**
8496fdac
PS
756 * Returns sha1 hash of file content.
757 *
16a95e8f 758 * @return string
759 */
4284e1cc 760 public function get_contenthash() {
67233725 761 $this->sync_external_file();
4284e1cc 762 return $this->file_record->contenthash;
763 }
6ed19c74 764
67233725
DC
765 /**
766 * Set contenthash
767 *
768 * @param string $contenthash
769 */
770 protected function set_contenthash($contenthash) {
771 // make sure the content exists in moodle file pool
772 if ($this->fs->content_exists($contenthash)) {
773 $filerecord = new stdClass;
774 $filerecord->contenthash = $contenthash;
775 $this->update($filerecord);
776 } else {
777 throw new file_exception('storedfileproblem', 'Invalid contenthash, content must be already in filepool', $contenthash);
778 }
779 }
780
16a95e8f 781 /**
64f93798 782 * Returns sha1 hash of all file path components sha1("contextid/component/filearea/itemid/dir/dir/filename.ext").
8496fdac 783 *
16a95e8f 784 * @return string
785 */
6ed19c74 786 public function get_pathnamehash() {
787 return $this->file_record->pathnamehash;
788 }
1dce6261
DC
789
790 /**
8496fdac
PS
791 * Returns the license type of the file, it is a short name referred from license table.
792 *
1dce6261
DC
793 * @return string
794 */
795 public function get_license() {
796 return $this->file_record->license;
797 }
798
67233725
DC
799 /**
800 * Set license
801 *
802 * @param string $license license
803 */
804 public function set_license($license) {
805 $filerecord = new stdClass;
806 $filerecord->license = $license;
807 $this->update($filerecord);
808 }
809
1dce6261 810 /**
8496fdac
PS
811 * Returns the author name of the file.
812 *
1dce6261
DC
813 * @return string
814 */
815 public function get_author() {
31cd5fe8 816 return $this->file_record->author;
1dce6261
DC
817 }
818
67233725
DC
819 /**
820 * Set author
821 *
822 * @param string $author
823 */
824 public function set_author($author) {
825 $filerecord = new stdClass;
826 $filerecord->author = $author;
827 $this->update($filerecord);
828 }
829
1dce6261 830 /**
8496fdac
PS
831 * Returns the source of the file, usually it is a url.
832 *
1dce6261
DC
833 * @return string
834 */
835 public function get_source() {
836 return $this->file_record->source;
837 }
8496fdac 838
67233725
DC
839 /**
840 * Set license
841 *
842 * @param string $license license
843 */
844 public function set_source($source) {
845 $filerecord = new stdClass;
846 $filerecord->source = $source;
847 $this->update($filerecord);
848 }
849
850
f79321f1
DC
851 /**
852 * Returns the sort order of file
853 *
854 * @return int
855 */
856 public function get_sortorder() {
857 return $this->file_record->sortorder;
858 }
67233725
DC
859
860 /**
861 * Set file sort order
862 *
863 * @param int $sortorder
864 * @return int
865 */
866 public function set_sortorder($sortorder) {
867 $filerecord = new stdClass;
868 $filerecord->sortorder = $sortorder;
869 $this->update($filerecord);
870 }
871
872 /**
873 * Returns repository id
874 *
875 * @return int|null
876 */
877 public function get_repository_id() {
878 if (!empty($this->repository)) {
879 return $this->repository->id;
880 } else {
881 return null;
882 }
883 }
884
885 /**
886 * get reference file id
887 * @return int
888 */
889 public function get_referencefileid() {
890 return $this->file_record->referencefileid;
891 }
892
893 /**
894 * Get reference last sync time
895 * @return int
896 */
897 public function get_referencelastsync() {
898 return $this->file_record->referencelastsync;
899 }
900
901 /**
902 * Get reference last sync time
903 * @return int
904 */
905 public function get_referencelifetime() {
906 return $this->file_record->referencelifetime;
907 }
908 /**
909 * Returns file reference
910 *
911 * @return string
912 */
913 public function get_reference() {
914 return $this->file_record->reference;
915 }
916
917 /**
918 * Get human readable file reference information
919 *
920 * @return string
921 */
922 public function get_reference_details() {
0b2bfbd1
MG
923 return $this->repository->get_reference_details($this->get_reference(), $this->get_status());
924 }
925
926 /**
927 * Called after reference-file has been synchronized with the repository
928 *
929 * We update contenthash, filesize and status in files table if changed
930 * and we always update lastsync in files_reference table
931 *
14b7e500
MG
932 * @param string $contenthash
933 * @param int $filesize
934 * @param int $status
935 * @param int $lifetime the life time of this synchronisation results
0b2bfbd1 936 */
14b7e500 937 public function set_synchronized($contenthash, $filesize, $status = 0, $lifetime = null) {
0b2bfbd1
MG
938 global $DB;
939 if (!$this->is_external_file()) {
940 return;
941 }
942 $now = time();
14b7e500
MG
943 if ($contenthash != $this->file_record->contenthash) {
944 $oldcontenthash = $this->file_record->contenthash;
0b2bfbd1 945 }
14b7e500
MG
946 if ($lifetime === null) {
947 $lifetime = $this->file_record->referencelifetime;
0b2bfbd1 948 }
14b7e500
MG
949 // this will update all entries in {files} that have the same filereference id
950 $this->fs->update_references($this->file_record->referencefileid, $now, $lifetime, $contenthash, $filesize, $status);
951 // we don't need to call update() for this object, just set the values of changed fields
952 $this->file_record->contenthash = $contenthash;
953 $this->file_record->filesize = $filesize;
954 $this->file_record->status = $status;
955 $this->file_record->referencelastsync = $now;
956 $this->file_record->referencelifetime = $lifetime;
957 if (isset($oldcontenthash)) {
958 $this->fs->deleted_file_cleanup($oldcontenthash);
0b2bfbd1 959 }
0b2bfbd1
MG
960 }
961
14b7e500
MG
962 /**
963 * Sets the error status for a file that could not be synchronised
964 *
965 * @param int $lifetime the life time of this synchronisation results
966 */
967 public function set_missingsource($lifetime = null) {
968 $this->set_synchronized($this->get_contenthash(), $this->get_filesize(), 666, $lifetime);
67233725
DC
969 }
970
971 /**
972 * Send file references
973 *
974 * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours)
975 * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only
976 * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin
977 * @param array $options additional options affecting the file serving
978 */
979 public function send_file($lifetime, $filter, $forcedownload, $options) {
980 $this->repository->send_file($this, $lifetime, $filter, $forcedownload, $options);
981 }
bc6f241c
MG
982
983 /**
984 * Imports the contents of an external file into moodle filepool.
985 *
986 * @throws moodle_exception if file could not be downloaded or is too big
987 * @param int $maxbytes throw an exception if file size is bigger than $maxbytes (0 means no limit)
988 */
989 public function import_external_file_contents($maxbytes = 0) {
990 if ($this->repository) {
991 $this->repository->import_external_file_contents($this, $maxbytes);
992 }
993 }
361a47d4
DM
994
995 /**
996 * Gets a file relative to this file in the repository and sends it to the browser.
997 * Checks the function repository::supports_relative_file() to make sure it can be used.
998 *
999 * @param string $relativepath the relative path to the file we are trying to access
1000 */
1001 public function send_relative_file($relativepath) {
1002 if ($this->repository && $this->repository->supports_relative_file()) {
1003 $relativepath = clean_param($relativepath, PARAM_PATH);
1004 $this->repository->send_relative_file($this, $relativepath);
1005 } else {
1006 send_file_not_found();
1007 }
1008 }
f79321f1 1009}