Commit | Line | Data |
---|---|---|
16a95e8f | 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 | /** | |
8496fdac | 20 | * Definition of a class stored_file. |
16a95e8f | 21 | * |
64f93798 PS |
22 | * @package core |
23 | * @subpackage filestorage | |
8496fdac | 24 | * @copyright 2008 Petr Skoda {@link http://skodak.org} |
64a19b38 | 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
16a95e8f | 26 | */ |
27 | ||
64f93798 PS |
28 | defined('MOODLE_INTERNAL') || die(); |
29 | ||
30 | require_once("$CFG->libdir/filestorage/stored_file.php"); | |
172dd12c | 31 | |
32 | /** | |
8496fdac PS |
33 | * Class representing local files stored in a sha1 file pool. |
34 | * | |
35 | * Since Moodle 2.0 file contents are stored in sha1 pool and | |
36 | * all other file information is stored in new "files" database table. | |
37 | * | |
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 | */ |
42 | class stored_file { | |
8496fdac | 43 | /** @var file_storage file storage pool instance */ |
172dd12c | 44 | private $fs; |
8496fdac | 45 | /** @var object record from the files table */ |
172dd12c | 46 | private $file_record; |
693ef3a8 PS |
47 | /** @var string location of content files */ |
48 | private $filedir; | |
172dd12c | 49 | |
50 | /** | |
8496fdac PS |
51 | * Constructor, this constructor should be called ONLY from the file_storage class! |
52 | * | |
53 | * @param file_storage $fs file storage instance | |
172dd12c | 54 | * @param object $file_record description of file |
693ef3a8 | 55 | * @param string $filepool location of file directory with sh1 named content files |
172dd12c | 56 | */ |
693ef3a8 | 57 | public function __construct(file_storage $fs, stdClass $file_record, $filedir) { |
8496fdac PS |
58 | $this->fs = $fs; |
59 | $this->file_record = clone($file_record); // prevent modifications | |
693ef3a8 | 60 | $this->filedir = $filedir; // keep secret, do not expose! |
172dd12c | 61 | } |
62 | ||
63 | /** | |
64 | * Is this a directory? | |
8496fdac PS |
65 | * |
66 | * Directories are only emulated, internally they are stored as empty | |
67 | * files with a "." instead of name - this means empty directory contains | |
68 | * exactly one empty file with name dot. | |
69 | * | |
70 | * @return bool true means directory, false means file | |
172dd12c | 71 | */ |
72 | public function is_directory() { | |
8496fdac | 73 | return ($this->file_record->filename === '.'); |
172dd12c | 74 | } |
75 | ||
76 | /** | |
8496fdac PS |
77 | * Delete file from files table. |
78 | * | |
79 | * The content of files stored in sha1 pool is reclaimed | |
80 | * later - the occupied disk space is reclaimed much later. | |
81 | * | |
82 | * @return bool always true or exception if error occurred | |
172dd12c | 83 | */ |
84 | public function delete() { | |
85 | global $DB; | |
1aa01caf | 86 | $DB->delete_records('files', array('id'=>$this->file_record->id)); |
87 | // moves pool file to trash if content not needed any more | |
88 | $this->fs->deleted_file_cleanup($this->file_record->contenthash); | |
8496fdac | 89 | return true; // BC only |
172dd12c | 90 | } |
91 | ||
92 | /** | |
8496fdac PS |
93 | * Protected - developers must not gain direct access to this function. |
94 | * | |
17d9269f | 95 | * NOTE: do not make this public, we must not modify or delete the pool files directly! ;-) |
8496fdac PS |
96 | * |
97 | * @return string full path to pool file with file content | |
172dd12c | 98 | **/ |
99 | protected function get_content_file_location() { | |
17d9269f | 100 | $contenthash = $this->file_record->contenthash; |
101 | $l1 = $contenthash[0].$contenthash[1]; | |
102 | $l2 = $contenthash[2].$contenthash[3]; | |
693ef3a8 | 103 | return "$this->filedir/$l1/$l2/$contenthash"; |
172dd12c | 104 | } |
105 | ||
5035a8b4 | 106 | /** |
107 | * adds this file path to a curl request (POST only) | |
108 | * | |
109 | * @param curl $curlrequest the curl request object | |
110 | * @param string $key what key to use in the POST request | |
8496fdac | 111 | * @return void |
5035a8b4 | 112 | */ |
113 | public function add_to_curl_request(&$curlrequest, $key) { | |
114 | $curlrequest->_tmp_file_post_params[$key] = '@' . $this->get_content_file_location(); | |
115 | } | |
116 | ||
172dd12c | 117 | /** |
118 | * Returns file handle - read only mode, no writing allowed into pool files! | |
8496fdac PS |
119 | * |
120 | * When you want to modify a file, create a new file and delete the old one. | |
121 | * | |
122 | * @return resource file handle | |
172dd12c | 123 | */ |
124 | public function get_content_file_handle() { | |
125 | $path = $this->get_content_file_location(); | |
126 | if (!is_readable($path)) { | |
1aa01caf | 127 | if (!$this->fs->try_content_recovery($this) or !is_readable($path)) { |
128 | throw new file_exception('storedfilecannotread'); | |
129 | } | |
172dd12c | 130 | } |
131 | return fopen($path, 'rb'); //binary reading only!! | |
132 | } | |
133 | ||
134 | /** | |
8496fdac PS |
135 | * Dumps file content to page. |
136 | * | |
137 | * @return void | |
172dd12c | 138 | */ |
139 | public function readfile() { | |
140 | $path = $this->get_content_file_location(); | |
141 | if (!is_readable($path)) { | |
1aa01caf | 142 | if (!$this->fs->try_content_recovery($this) or !is_readable($path)) { |
143 | throw new file_exception('storedfilecannotread'); | |
144 | } | |
172dd12c | 145 | } |
146 | readfile($path); | |
147 | } | |
148 | ||
149 | /** | |
8496fdac PS |
150 | * Returns file content as string. |
151 | * | |
172dd12c | 152 | * @return string content |
153 | */ | |
154 | public function get_content() { | |
155 | $path = $this->get_content_file_location(); | |
156 | if (!is_readable($path)) { | |
1aa01caf | 157 | if (!$this->fs->try_content_recovery($this) or !is_readable($path)) { |
158 | throw new file_exception('storedfilecannotread'); | |
159 | } | |
172dd12c | 160 | } |
161 | return file_get_contents($this->get_content_file_location()); | |
162 | } | |
163 | ||
6c0e2d08 | 164 | /** |
8496fdac PS |
165 | * Copy content of file to given pathname. |
166 | * | |
167 | * @param string $pathname real path to the new file | |
6c0e2d08 | 168 | * @return bool success |
169 | */ | |
170 | public function copy_content_to($pathname) { | |
171 | $path = $this->get_content_file_location(); | |
172 | if (!is_readable($path)) { | |
1aa01caf | 173 | if (!$this->fs->try_content_recovery($this) or !is_readable($path)) { |
174 | throw new file_exception('storedfilecannotread'); | |
175 | } | |
6c0e2d08 | 176 | } |
177 | return copy($path, $pathname); | |
178 | } | |
179 | ||
17d9269f | 180 | /** |
8496fdac PS |
181 | * List contents of archive. |
182 | * | |
183 | * @param file_packer $file_packer | |
c78a0558 | 184 | * @return array of file infos |
185 | */ | |
186 | public function list_files(file_packer $packer) { | |
187 | $archivefile = $this->get_content_file_location(); | |
188 | return $packer->list_files($archivefile); | |
189 | } | |
190 | ||
191 | /** | |
8496fdac PS |
192 | * Extract file to given file path (real OS filesystem), existing files are overwritten. |
193 | * | |
194 | * @param file_packer $file_packer | |
0b0bfa93 | 195 | * @param string $pathname target directory |
8496fdac | 196 | * @return array|bool list of processed files; false if error |
17d9269f | 197 | */ |
0b0bfa93 | 198 | public function extract_to_pathname(file_packer $packer, $pathname) { |
199 | $archivefile = $this->get_content_file_location(); | |
200 | return $packer->extract_to_pathname($archivefile, $pathname); | |
17d9269f | 201 | } |
202 | ||
203 | /** | |
8496fdac PS |
204 | * Extract file to given file path (real OS filesystem), existing files are overwritten. |
205 | * | |
206 | * @param file_packer $file_packer | |
17d9269f | 207 | * @param int $contextid |
64f93798 | 208 | * @param string $component |
17d9269f | 209 | * @param string $filearea |
210 | * @param int $itemid | |
211 | * @param string $pathbase | |
212 | * @param int $userid | |
8496fdac | 213 | * @return array|bool list of processed files; false if error |
17d9269f | 214 | */ |
64f93798 | 215 | public function extract_to_storage(file_packer $packer, $contextid, $component, $filearea, $itemid, $pathbase, $userid = NULL) { |
0b0bfa93 | 216 | $archivefile = $this->get_content_file_location(); |
64f93798 | 217 | return $packer->extract_to_storage($archivefile, $contextid, $component, $filearea, $itemid, $pathbase); |
17d9269f | 218 | } |
219 | ||
b1897a6d | 220 | /** |
8496fdac PS |
221 | * Add file/directory into archive. |
222 | * | |
223 | * @param file_archive $filearch | |
c78a0558 | 224 | * @param string $archivepath pathname in archive |
b1897a6d | 225 | * @return bool success |
226 | */ | |
0b0bfa93 | 227 | public function archive_file(file_archive $filearch, $archivepath) { |
b1897a6d | 228 | if ($this->is_directory()) { |
0b0bfa93 | 229 | return $filearch->add_directory($archivepath); |
b1897a6d | 230 | } else { |
231 | $path = $this->get_content_file_location(); | |
232 | if (!is_readable($path)) { | |
233 | return false; | |
234 | } | |
0b0bfa93 | 235 | return $filearch->add_file_from_pathname($archivepath, $path); |
b1897a6d | 236 | } |
237 | } | |
238 | ||
797f19e8 | 239 | /** |
240 | * Returns information about image, | |
241 | * information is determined from the file content | |
242 | * @return mixed array with width, height and mimetype; false if not an image | |
243 | */ | |
244 | public function get_imageinfo() { | |
245 | if (!$imageinfo = getimagesize($this->get_content_file_location())) { | |
246 | return false; | |
247 | } | |
248 | $image = array('width'=>$imageinfo[0], 'height'=>$imageinfo[1], 'mimetype'=>image_type_to_mime_type($imageinfo[2])); | |
249 | if (empty($image['width']) or empty($image['height']) or empty($image['mimetype'])) { | |
250 | // gd can not parse it, sorry | |
251 | return false; | |
252 | } | |
253 | return $image; | |
254 | } | |
255 | ||
256 | /** | |
257 | * Verifies the file is a valid web image - gif, png and jpeg only. | |
8496fdac | 258 | * |
797f19e8 | 259 | * It should be ok to serve this image from server without any other security workarounds. |
8496fdac | 260 | * |
797f19e8 | 261 | * @return bool true if file ok |
262 | */ | |
263 | public function is_valid_image() { | |
264 | $mimetype = $this->get_mimetype(); | |
265 | if ($mimetype !== 'image/gif' and $mimetype !== 'image/jpeg' and $mimetype !== 'image/png') { | |
266 | return false; | |
267 | } | |
268 | if (!$info = $this->get_imageinfo()) { | |
269 | return false; | |
270 | } | |
271 | if ($info['mimetype'] !== $mimetype) { | |
272 | return false; | |
273 | } | |
274 | // ok, GD likes this image | |
275 | return true; | |
276 | } | |
277 | ||
4b6b5ce7 | 278 | /** |
8496fdac PS |
279 | * Returns parent directory, creates missing parents if needed. |
280 | * | |
281 | * @return stored_file | |
4b6b5ce7 | 282 | */ |
283 | public function get_parent_directory() { | |
284 | if ($this->file_record->filepath === '/' and $this->file_record->filename === '.') { | |
285 | //root dir does not have parent | |
286 | return null; | |
287 | } | |
288 | ||
289 | if ($this->file_record->filename !== '.') { | |
64f93798 | 290 | 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 | 291 | } |
292 | ||
293 | $filepath = $this->file_record->filepath; | |
294 | $filepath = trim($filepath, '/'); | |
295 | $dirs = explode('/', $filepath); | |
296 | array_pop($dirs); | |
297 | $filepath = implode('/', $dirs); | |
298 | $filepath = ($filepath === '') ? '/' : "/$filepath/"; | |
299 | ||
64f93798 | 300 | return $this->fs->create_directory($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $filepath); |
4b6b5ce7 | 301 | } |
302 | ||
16a95e8f | 303 | /** |
8496fdac PS |
304 | * Returns context id of the file- |
305 | * | |
16a95e8f | 306 | * @return int context id |
307 | */ | |
172dd12c | 308 | public function get_contextid() { |
309 | return $this->file_record->contextid; | |
310 | } | |
311 | ||
16a95e8f | 312 | /** |
64f93798 PS |
313 | * Returns component name - this is the owner of the areas, |
314 | * nothing else is allowed to read or modify the files directly!! | |
315 | * | |
316 | * @return string | |
317 | */ | |
318 | public function get_component() { | |
319 | return $this->file_record->component; | |
320 | } | |
321 | ||
322 | /** | |
323 | * Returns file area name, this divides files of one component into groups with different access control. | |
324 | * All files in one area have the same access control. | |
8496fdac | 325 | * |
16a95e8f | 326 | * @return string |
327 | */ | |
172dd12c | 328 | public function get_filearea() { |
329 | return $this->file_record->filearea; | |
330 | } | |
331 | ||
16a95e8f | 332 | /** |
8496fdac PS |
333 | * Returns returns item id of file. |
334 | * | |
16a95e8f | 335 | * @return int |
336 | */ | |
172dd12c | 337 | public function get_itemid() { |
338 | return $this->file_record->itemid; | |
339 | } | |
340 | ||
16a95e8f | 341 | /** |
342 | * Returns file path - starts and ends with /, \ are not allowed. | |
8496fdac | 343 | * |
16a95e8f | 344 | * @return string |
345 | */ | |
172dd12c | 346 | public function get_filepath() { |
347 | return $this->file_record->filepath; | |
348 | } | |
349 | ||
16a95e8f | 350 | /** |
351 | * Returns file name or '.' in case of directories. | |
8496fdac | 352 | * |
16a95e8f | 353 | * @return string |
354 | */ | |
172dd12c | 355 | public function get_filename() { |
356 | return $this->file_record->filename; | |
357 | } | |
358 | ||
16a95e8f | 359 | /** |
360 | * Returns id of user who created the file. | |
8496fdac | 361 | * |
16a95e8f | 362 | * @return int |
363 | */ | |
172dd12c | 364 | public function get_userid() { |
365 | return $this->file_record->userid; | |
366 | } | |
367 | ||
16a95e8f | 368 | /** |
369 | * Returns the size of file in bytes. | |
8496fdac | 370 | * |
16a95e8f | 371 | * @return int bytes |
372 | */ | |
172dd12c | 373 | public function get_filesize() { |
374 | return $this->file_record->filesize; | |
375 | } | |
376 | ||
16a95e8f | 377 | /** |
8496fdac PS |
378 | * Returns mime type of file. |
379 | * | |
16a95e8f | 380 | * @return string |
381 | */ | |
172dd12c | 382 | public function get_mimetype() { |
383 | return $this->file_record->mimetype; | |
384 | } | |
385 | ||
16a95e8f | 386 | /** |
8496fdac PS |
387 | * Returns unix timestamp of file creation date. |
388 | * | |
16a95e8f | 389 | * @return int |
390 | */ | |
172dd12c | 391 | public function get_timecreated() { |
392 | return $this->file_record->timecreated; | |
393 | } | |
394 | ||
16a95e8f | 395 | /** |
8496fdac PS |
396 | * Returns unix timestamp of last file modification. |
397 | * | |
16a95e8f | 398 | * @return int |
399 | */ | |
172dd12c | 400 | public function get_timemodified() { |
401 | return $this->file_record->timemodified; | |
402 | } | |
6c0e2d08 | 403 | |
16a95e8f | 404 | /** |
8496fdac PS |
405 | * Returns file status flag. |
406 | * | |
16a95e8f | 407 | * @return int 0 means file OK, anything else is a problem and file can not be used |
408 | */ | |
6c0e2d08 | 409 | public function get_status() { |
410 | return $this->file_record->status; | |
411 | } | |
ee03a651 | 412 | |
16a95e8f | 413 | /** |
8496fdac PS |
414 | * Returns file id. |
415 | * | |
16a95e8f | 416 | * @return int |
417 | */ | |
ee03a651 | 418 | public function get_id() { |
419 | return $this->file_record->id; | |
420 | } | |
4284e1cc | 421 | |
16a95e8f | 422 | /** |
8496fdac PS |
423 | * Returns sha1 hash of file content. |
424 | * | |
16a95e8f | 425 | * @return string |
426 | */ | |
4284e1cc | 427 | public function get_contenthash() { |
428 | return $this->file_record->contenthash; | |
429 | } | |
6ed19c74 | 430 | |
16a95e8f | 431 | /** |
64f93798 | 432 | * Returns sha1 hash of all file path components sha1("contextid/component/filearea/itemid/dir/dir/filename.ext"). |
8496fdac | 433 | * |
16a95e8f | 434 | * @return string |
435 | */ | |
6ed19c74 | 436 | public function get_pathnamehash() { |
437 | return $this->file_record->pathnamehash; | |
438 | } | |
1dce6261 DC |
439 | |
440 | /** | |
8496fdac PS |
441 | * Returns the license type of the file, it is a short name referred from license table. |
442 | * | |
1dce6261 DC |
443 | * @return string |
444 | */ | |
445 | public function get_license() { | |
446 | return $this->file_record->license; | |
447 | } | |
448 | ||
449 | /** | |
8496fdac PS |
450 | * Returns the author name of the file. |
451 | * | |
1dce6261 DC |
452 | * @return string |
453 | */ | |
454 | public function get_author() { | |
455 | return $this->file_record->license; | |
456 | } | |
457 | ||
458 | /** | |
8496fdac PS |
459 | * Returns the source of the file, usually it is a url. |
460 | * | |
1dce6261 DC |
461 | * @return string |
462 | */ | |
463 | public function get_source() { | |
464 | return $this->file_record->source; | |
465 | } | |
8496fdac | 466 | |
f79321f1 DC |
467 | /** |
468 | * Returns the sort order of file | |
469 | * | |
470 | * @return int | |
471 | */ | |
472 | public function get_sortorder() { | |
473 | return $this->file_record->sortorder; | |
474 | } | |
475 | } |