3 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
20 * Definition of a class stored_file.
23 * @subpackage filestorage
24 * @copyright 2008 Petr Skoda {@link http://skodak.org}
25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28 defined('MOODLE_INTERNAL') || die();
30 require_once("$CFG->libdir/filestorage/stored_file.php");
33 * Class representing local files stored in a sha1 file pool.
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.
38 * @copyright 2008 Petr Skoda {@link http://skodak.org}
39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43 /** @var file_storage file storage pool instance */
45 /** @var object record from the files table */
49 * Constructor, this constructor should be called ONLY from the file_storage class!
51 * @param file_storage $fs file storage instance
52 * @param object $file_record description of file
54 public function __construct(file_storage $fs, stdClass $file_record) {
56 $this->file_record = clone($file_record); // prevent modifications
60 * Is this a directory?
62 * Directories are only emulated, internally they are stored as empty
63 * files with a "." instead of name - this means empty directory contains
64 * exactly one empty file with name dot.
66 * @return bool true means directory, false means file
68 public function is_directory() {
69 return ($this->file_record->filename === '.');
73 * Delete file from files table.
75 * The content of files stored in sha1 pool is reclaimed
76 * later - the occupied disk space is reclaimed much later.
78 * @return bool always true or exception if error occurred
80 public function delete() {
82 $DB->delete_records('files', array('id'=>$this->file_record->id));
83 // moves pool file to trash if content not needed any more
84 $this->fs->deleted_file_cleanup($this->file_record->contenthash);
85 return true; // BC only
89 * Protected - developers must not gain direct access to this function.
91 * NOTE: do not make this public, we must not modify or delete the pool files directly! ;-)
93 * @return string full path to pool file with file content
95 protected function get_content_file_location() {
96 $filedir = $this->fs->get_filedir();
97 $contenthash = $this->file_record->contenthash;
98 $l1 = $contenthash[0].$contenthash[1];
99 $l2 = $contenthash[2].$contenthash[3];
100 return "$filedir/$l1/$l2/$contenthash";
104 * adds this file path to a curl request (POST only)
106 * @param curl $curlrequest the curl request object
107 * @param string $key what key to use in the POST request
110 public function add_to_curl_request(&$curlrequest, $key) {
111 $curlrequest->_tmp_file_post_params[$key] = '@' . $this->get_content_file_location();
115 * Returns file handle - read only mode, no writing allowed into pool files!
117 * When you want to modify a file, create a new file and delete the old one.
119 * @return resource file handle
121 public function get_content_file_handle() {
122 $path = $this->get_content_file_location();
123 if (!is_readable($path)) {
124 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
125 throw new file_exception('storedfilecannotread');
128 return fopen($path, 'rb'); //binary reading only!!
132 * Dumps file content to page.
136 public function readfile() {
137 $path = $this->get_content_file_location();
138 if (!is_readable($path)) {
139 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
140 throw new file_exception('storedfilecannotread');
147 * Returns file content as string.
149 * @return string content
151 public function get_content() {
152 $path = $this->get_content_file_location();
153 if (!is_readable($path)) {
154 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
155 throw new file_exception('storedfilecannotread');
158 return file_get_contents($this->get_content_file_location());
162 * Copy content of file to given pathname.
164 * @param string $pathname real path to the new file
165 * @return bool success
167 public function copy_content_to($pathname) {
168 $path = $this->get_content_file_location();
169 if (!is_readable($path)) {
170 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
171 throw new file_exception('storedfilecannotread');
174 return copy($path, $pathname);
178 * List contents of archive.
180 * @param file_packer $file_packer
181 * @return array of file infos
183 public function list_files(file_packer $packer) {
184 $archivefile = $this->get_content_file_location();
185 return $packer->list_files($archivefile);
189 * Extract file to given file path (real OS filesystem), existing files are overwritten.
191 * @param file_packer $file_packer
192 * @param string $pathname target directory
193 * @return array|bool list of processed files; false if error
195 public function extract_to_pathname(file_packer $packer, $pathname) {
196 $archivefile = $this->get_content_file_location();
197 return $packer->extract_to_pathname($archivefile, $pathname);
201 * Extract file to given file path (real OS filesystem), existing files are overwritten.
203 * @param file_packer $file_packer
204 * @param int $contextid
205 * @param string $component
206 * @param string $filearea
208 * @param string $pathbase
210 * @return array|bool list of processed files; false if error
212 public function extract_to_storage(file_packer $packer, $contextid, $component, $filearea, $itemid, $pathbase, $userid = NULL) {
213 $archivefile = $this->get_content_file_location();
214 return $packer->extract_to_storage($archivefile, $contextid, $component, $filearea, $itemid, $pathbase);
218 * Add file/directory into archive.
220 * @param file_archive $filearch
221 * @param string $archivepath pathname in archive
222 * @return bool success
224 public function archive_file(file_archive $filearch, $archivepath) {
225 if ($this->is_directory()) {
226 return $filearch->add_directory($archivepath);
228 $path = $this->get_content_file_location();
229 if (!is_readable($path)) {
232 return $filearch->add_file_from_pathname($archivepath, $path);
237 * Returns information about image,
238 * information is determined from the file content
239 * @return mixed array with width, height and mimetype; false if not an image
241 public function get_imageinfo() {
242 if (!$imageinfo = getimagesize($this->get_content_file_location())) {
245 $image = array('width'=>$imageinfo[0], 'height'=>$imageinfo[1], 'mimetype'=>image_type_to_mime_type($imageinfo[2]));
246 if (empty($image['width']) or empty($image['height']) or empty($image['mimetype'])) {
247 // gd can not parse it, sorry
254 * Verifies the file is a valid web image - gif, png and jpeg only.
256 * It should be ok to serve this image from server without any other security workarounds.
258 * @return bool true if file ok
260 public function is_valid_image() {
261 $mimetype = $this->get_mimetype();
262 if ($mimetype !== 'image/gif' and $mimetype !== 'image/jpeg' and $mimetype !== 'image/png') {
265 if (!$info = $this->get_imageinfo()) {
268 if ($info['mimetype'] !== $mimetype) {
271 // ok, GD likes this image
276 * Returns parent directory, creates missing parents if needed.
278 * @return stored_file
280 public function get_parent_directory() {
281 if ($this->file_record->filepath === '/' and $this->file_record->filename === '.') {
282 //root dir does not have parent
286 if ($this->file_record->filename !== '.') {
287 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);
290 $filepath = $this->file_record->filepath;
291 $filepath = trim($filepath, '/');
292 $dirs = explode('/', $filepath);
294 $filepath = implode('/', $dirs);
295 $filepath = ($filepath === '') ? '/' : "/$filepath/";
297 return $this->fs->create_directory($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $filepath);
301 * Returns context id of the file-
303 * @return int context id
305 public function get_contextid() {
306 return $this->file_record->contextid;
310 * Returns component name - this is the owner of the areas,
311 * nothing else is allowed to read or modify the files directly!!
315 public function get_component() {
316 return $this->file_record->component;
320 * Returns file area name, this divides files of one component into groups with different access control.
321 * All files in one area have the same access control.
325 public function get_filearea() {
326 return $this->file_record->filearea;
330 * Returns returns item id of file.
334 public function get_itemid() {
335 return $this->file_record->itemid;
339 * Returns file path - starts and ends with /, \ are not allowed.
343 public function get_filepath() {
344 return $this->file_record->filepath;
348 * Returns file name or '.' in case of directories.
352 public function get_filename() {
353 return $this->file_record->filename;
357 * Returns id of user who created the file.
361 public function get_userid() {
362 return $this->file_record->userid;
366 * Returns the size of file in bytes.
370 public function get_filesize() {
371 return $this->file_record->filesize;
375 * Returns mime type of file.
379 public function get_mimetype() {
380 return $this->file_record->mimetype;
384 * Returns unix timestamp of file creation date.
388 public function get_timecreated() {
389 return $this->file_record->timecreated;
393 * Returns unix timestamp of last file modification.
397 public function get_timemodified() {
398 return $this->file_record->timemodified;
402 * Returns file status flag.
404 * @return int 0 means file OK, anything else is a problem and file can not be used
406 public function get_status() {
407 return $this->file_record->status;
415 public function get_id() {
416 return $this->file_record->id;
420 * Returns sha1 hash of file content.
424 public function get_contenthash() {
425 return $this->file_record->contenthash;
429 * Returns sha1 hash of all file path components sha1("contextid/component/filearea/itemid/dir/dir/filename.ext").
433 public function get_pathnamehash() {
434 return $this->file_record->pathnamehash;
438 * Returns the license type of the file, it is a short name referred from license table.
442 public function get_license() {
443 return $this->file_record->license;
447 * Returns the author name of the file.
451 public function get_author() {
452 return $this->file_record->license;
456 * Returns the source of the file, usually it is a url.
460 public function get_source() {
461 return $this->file_record->source;
465 * Returns the sort order of file
469 public function get_sortorder() {
470 return $this->file_record->sortorder;