MDL-40119 Forms: Remove cursor style override for calendar button
[moodle.git] / lib / filebrowser / file_info_stored.php
blob8c1069b65e02dec530a012dcabd7cc99fe07265a
1 <?php
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.
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/>.
18 /**
19 * Utility class for browsing of stored files.
21 * @package core_files
22 * @copyright 2008 Petr Skoda (http://skodak.org)
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die();
28 /**
29 * Represents an actual file or folder - a row in the file table in the tree navigated by {@link file_browser}.
31 * @package core_files
32 * @copyright 2008 Petr Skoda (http://skodak.org)
33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 class file_info_stored extends file_info {
36 /** @var stored_file|virtual_root_file stored_file or virtual_root_file instance */
37 protected $lf;
38 /** @var string the serving script */
39 protected $urlbase;
40 /** @var string the human readable name of this area */
41 protected $topvisiblename;
42 /** @var int|bool it's false if itemid is 0 and not included in URL */
43 protected $itemidused;
44 /** @var bool allow file reading */
45 protected $readaccess;
46 /** @var bool allow file write, delee */
47 protected $writeaccess;
48 /** @var string do not show links to parent context/area */
49 protected $areaonly;
51 /**
52 * Constructor
54 * @param file_browser $browser file browser instance
55 * @param stdClass $context context object
56 * @param stored_file|virtual_root_file $storedfile stored_file instance
57 * @param string $urlbase the serving script - usually the $CFG->wwwroot/.'pluginfile.php'
58 * @param string $topvisiblename the human readable name of this area
59 * @param int|bool $itemidused false if itemid always 0 and not included in URL
60 * @param bool $readaccess allow file reading
61 * @param bool $writeaccess allow file write, delete
62 * @param string $areaonly do not show links to parent context/area
64 public function __construct(file_browser $browser, $context, $storedfile, $urlbase, $topvisiblename, $itemidused, $readaccess, $writeaccess, $areaonly) {
65 parent::__construct($browser, $context);
67 $this->lf = $storedfile;
68 $this->urlbase = $urlbase;
69 $this->topvisiblename = $topvisiblename;
70 $this->itemidused = $itemidused;
71 $this->readaccess = $readaccess;
72 $this->writeaccess = $writeaccess;
73 $this->areaonly = $areaonly;
76 /**
77 * Returns list of standard virtual file/directory identification.
78 * The difference from stored_file parameters is that null values
79 * are allowed in all fields
81 * @return array with keys contextid, component, filearea, itemid, filepath and filename
83 public function get_params() {
84 return array('contextid'=>$this->context->id,
85 'component'=>$this->lf->get_component(),
86 'filearea' =>$this->lf->get_filearea(),
87 'itemid' =>$this->lf->get_itemid(),
88 'filepath' =>$this->lf->get_filepath(),
89 'filename' =>$this->lf->get_filename());
92 /**
93 * Returns localised visible name.
95 * @return string
97 public function get_visible_name() {
98 $filename = $this->lf->get_filename();
99 $filepath = $this->lf->get_filepath();
101 if ($filename !== '.') {
102 return $filename;
104 } else {
105 $dir = trim($filepath, '/');
106 $dir = explode('/', $dir);
107 $dir = array_pop($dir);
108 if ($dir === '') {
109 return $this->topvisiblename;
110 } else {
111 return $dir;
117 * Returns the localised human-readable name of the file together with virtual path
119 * @return string
121 public function get_readable_fullname() {
122 global $CFG;
123 // retrieve the readable path with all parents (excluding the top most 'System')
124 $fpath = array();
125 for ($parent = $this; $parent && $parent->get_parent(); $parent = $parent->get_parent()) {
126 array_unshift($fpath, $parent->get_visible_name());
129 if ($this->lf->get_component() == 'user' && $this->lf->get_filearea() == 'private') {
130 // use the special syntax for user private files - 'USERNAME Private files: PATH'
131 $username = array_shift($fpath);
132 array_shift($fpath); // get rid of "Private Files/" in the beginning of the path
133 return get_string('privatefilesof', 'repository', $username). ': '. join('/', $fpath);
134 } else {
135 // for all other files (except user private files) return 'Server files: PATH'
137 // first, get and cache the name of the repository_local (will be used as prefix for file names):
138 static $replocalname = null;
139 if ($replocalname === null) {
140 require_once($CFG->dirroot . "/repository/lib.php");
141 $instances = repository::get_instances(array('type' => 'local'));
142 if (count($instances)) {
143 $firstinstance = reset($instances);
144 $replocalname = $firstinstance->get_name();
145 } else if (get_string_manager()->string_exists('pluginname', 'repository_local')) {
146 $replocalname = get_string('pluginname', 'repository_local');
147 } else {
148 $replocalname = get_string('arearoot', 'repository');
152 return $replocalname. ': '. join('/', $fpath);
157 * Returns file download url
159 * @param bool $forcedownload Whether or not force download
160 * @param bool $https whether or not force https
161 * @return string url
163 public function get_url($forcedownload=false, $https=false) {
164 if (!$this->is_readable()) {
165 return null;
168 if ($this->is_directory()) {
169 return null;
172 $this->urlbase;
173 $contextid = $this->lf->get_contextid();
174 $component = $this->lf->get_component();
175 $filearea = $this->lf->get_filearea();
176 $itemid = $this->lf->get_itemid();
177 $filepath = $this->lf->get_filepath();
178 $filename = $this->lf->get_filename();
180 if ($this->itemidused) {
181 $path = '/'.$contextid.'/'.$component.'/'.$filearea.'/'.$itemid.$filepath.$filename;
182 } else {
183 $path = '/'.$contextid.'/'.$component.'/'.$filearea.$filepath.$filename;
185 return file_encode_url($this->urlbase, $path, $forcedownload, $https);
189 * Whether or not I can read content of this file or enter directory
191 * @return bool
193 public function is_readable() {
194 return $this->readaccess;
198 * Whether or not new files or directories can be added
200 * @return bool
202 public function is_writable() {
203 return $this->writeaccess;
207 * Whether or not this is an empty area
209 * @return bool
211 public function is_empty_area() {
212 if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') {
213 // test the emptiness only in the top most level, it does not make sense at lower levels
214 $fs = get_file_storage();
215 return $fs->is_area_empty($this->lf->get_contextid(), $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid());
216 } else {
217 return false;
222 * Returns file size in bytes, null for directories
224 * @return int bytes or null if not known
226 public function get_filesize() {
227 return $this->lf->get_filesize();
231 * Returns width, height and mimetype of the stored image, or false
233 * @see stored_file::get_imageinfo()
234 * @return array|false
236 public function get_imageinfo() {
237 return $this->lf->get_imageinfo();
241 * Returns mimetype
243 * @return string mimetype or null if not known
245 public function get_mimetype() {
246 return $this->lf->get_mimetype();
250 * Returns time created unix timestamp if known
252 * @return int timestamp or null
254 public function get_timecreated() {
255 return $this->lf->get_timecreated();
259 * Returns time modified unix timestamp if known
261 * @return int timestamp or null
263 public function get_timemodified() {
264 return $this->lf->get_timemodified();
268 * Whether or not this is a directory
270 * @return bool
272 public function is_directory() {
273 return $this->lf->is_directory();
277 * Returns the license type of the file
279 * @return string license short name or null
281 public function get_license() {
282 return $this->lf->get_license();
286 * Returns the author name of the file
288 * @return string author name or null
290 public function get_author() {
291 return $this->lf->get_author();
295 * Returns the source of the file
297 * @return string a source url or null
299 public function get_source() {
300 return $this->lf->get_source();
304 * Returns the sort order of the file
306 * @return int
308 public function get_sortorder() {
309 return $this->lf->get_sortorder();
313 * Whether or not this is a external resource
315 * @return bool
317 public function is_external_file() {
318 return $this->lf->is_external_file();
322 * Returns file status flag.
324 * @return int 0 means file OK, anything else is a problem and file can not be used
326 public function get_status() {
327 return $this->lf->get_status();
331 * Returns list of children.
333 * @return array of file_info instances
335 public function get_children() {
336 if (!$this->lf->is_directory()) {
337 return array();
340 $result = array();
341 $fs = get_file_storage();
343 $storedfiles = $fs->get_directory_files($this->context->id, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(),
344 $this->lf->get_filepath(), false, true, "filepath, filename");
345 foreach ($storedfiles as $file) {
346 $result[] = new file_info_stored($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
347 $this->itemidused, $this->readaccess, $this->writeaccess, false);
350 return $result;
354 * Returns list of children which are either files matching the specified extensions
355 * or folders that contain at least one such file.
357 * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
358 * @return array of file_info instances
360 public function get_non_empty_children($extensions = '*') {
361 $result = array();
362 if (!$this->lf->is_directory()) {
363 return $result;
366 $fs = get_file_storage();
368 $storedfiles = $fs->get_directory_files($this->context->id, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(),
369 $this->lf->get_filepath(), false, true, "filepath, filename");
370 foreach ($storedfiles as $file) {
371 $extension = core_text::strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
372 if ($file->is_directory() || $extensions === '*' || (!empty($extension) && in_array('.'.$extension, $extensions))) {
373 $fileinfo = new file_info_stored($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
374 $this->itemidused, $this->readaccess, $this->writeaccess, false);
375 if (!$file->is_directory() || $fileinfo->count_non_empty_children($extensions)) {
376 $result[] = $fileinfo;
381 return $result;
385 * Returns the number of children which are either files matching the specified extensions
386 * or folders containing at least one such file.
388 * @param string|array $extensions, for example '*' or array('.gif','.jpg')
389 * @param int $limit stop counting after at least $limit non-empty children are found
390 * @return int
392 public function count_non_empty_children($extensions = '*', $limit = 1) {
393 global $DB;
394 if (!$this->lf->is_directory()) {
395 return 0;
398 $filepath = $this->lf->get_filepath();
399 $length = core_text::strlen($filepath);
400 $sql = "SELECT filepath, filename
401 FROM {files} f
402 WHERE f.contextid = :contextid AND f.component = :component AND f.filearea = :filearea AND f.itemid = :itemid
403 AND ".$DB->sql_substr("f.filepath", 1, $length)." = :filepath
404 AND filename <> '.' ";
405 $params = array('contextid' => $this->context->id,
406 'component' => $this->lf->get_component(),
407 'filearea' => $this->lf->get_filearea(),
408 'itemid' => $this->lf->get_itemid(),
409 'filepath' => $filepath);
410 list($sql2, $params2) = $this->build_search_files_sql($extensions);
411 $rs = $DB->get_recordset_sql($sql.' '.$sql2, array_merge($params, $params2));
412 $children = array();
413 foreach ($rs as $record) {
414 // we don't need to check access to individual files here, since the user can access parent
415 if ($record->filepath === $filepath) {
416 $children[] = $record->filename;
417 } else {
418 $path = explode('/', core_text::substr($record->filepath, $length));
419 if (!in_array($path[0], $children)) {
420 $children[] = $path[0];
423 if (count($children) >= $limit) {
424 break;
427 $rs->close();
428 return count($children);
432 * Returns parent file_info instance
434 * @return file_info|null file_info instance or null for root
436 public function get_parent() {
437 if ($this->lf->get_filepath() === '/' and $this->lf->is_directory()) {
438 if ($this->areaonly) {
439 return null;
440 } else if ($this->itemidused) {
441 return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea());
442 } else {
443 return $this->browser->get_file_info($this->context);
447 if (!$this->lf->is_directory()) {
448 return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $this->lf->get_filepath(), '.');
451 $filepath = $this->lf->get_filepath();
452 $filepath = trim($filepath, '/');
453 $dirs = explode('/', $filepath);
454 array_pop($dirs);
455 $filepath = implode('/', $dirs);
456 $filepath = ($filepath === '') ? '/' : "/$filepath/";
458 return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, '.');
462 * Create new directory, may throw exception - make sure
463 * params are valid.
465 * @param string $newdirname name of new directory
466 * @param int $userid id of author, default $USER->id
467 * @return file_info|null new directory's file_info instance or null if failed
469 public function create_directory($newdirname, $userid = NULL) {
470 if (!$this->is_writable() or !$this->lf->is_directory()) {
471 return null;
474 $newdirname = clean_param($newdirname, PARAM_FILE);
475 if ($newdirname === '') {
476 return null;
479 $filepath = $this->lf->get_filepath().'/'.$newdirname.'/';
481 $fs = get_file_storage();
483 if ($file = $fs->create_directory($this->lf->get_contextid(), $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, $userid)) {
484 return $this->browser->get_file_info($this->context, $this->lf->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
486 return null;
491 * Create new file from string - make sure
492 * params are valid.
494 * @param string $newfilename name of new file
495 * @param string $content of file
496 * @param int $userid id of author, default $USER->id
497 * @return file_info|null new file's file_info instance or null if failed
499 public function create_file_from_string($newfilename, $content, $userid = NULL) {
500 if (!$this->is_writable() or !$this->lf->is_directory()) {
501 return null;
504 $newfilename = clean_param($newfilename, PARAM_FILE);
505 if ($newfilename === '') {
506 return null;
509 $fs = get_file_storage();
511 $now = time();
513 $newrecord = new stdClass();
514 $newrecord->contextid = $this->lf->get_contextid();
515 $newrecord->component = $this->lf->get_component();
516 $newrecord->filearea = $this->lf->get_filearea();
517 $newrecord->itemid = $this->lf->get_itemid();
518 $newrecord->filepath = $this->lf->get_filepath();
519 $newrecord->filename = $newfilename;
521 if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
522 // file already exists, sorry
523 return null;
526 $newrecord->timecreated = $now;
527 $newrecord->timemodified = $now;
528 $newrecord->mimetype = mimeinfo('type', $newfilename);
529 $newrecord->userid = $userid;
531 if ($file = $fs->create_file_from_string($newrecord, $content)) {
532 return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
534 return null;
538 * Create new file from pathname - make sure
539 * params are valid.
541 * @param string $newfilename name of new file
542 * @param string $pathname location of file
543 * @param int $userid id of author, default $USER->id
544 * @return file_info|null new file's file_info instance or null if failed
546 public function create_file_from_pathname($newfilename, $pathname, $userid = NULL) {
547 if (!$this->is_writable() or !$this->lf->is_directory()) {
548 return null;
551 $newfilename = clean_param($newfilename, PARAM_FILE);
552 if ($newfilename === '') {
553 return null;
556 $fs = get_file_storage();
558 $now = time();
560 $newrecord = new stdClass();
561 $newrecord->contextid = $this->lf->get_contextid();
562 $newrecord->component = $this->lf->get_component();
563 $newrecord->filearea = $this->lf->get_filearea();
564 $newrecord->itemid = $this->lf->get_itemid();
565 $newrecord->filepath = $this->lf->get_filepath();
566 $newrecord->filename = $newfilename;
568 if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
569 // file already exists, sorry
570 return null;
573 $newrecord->timecreated = $now;
574 $newrecord->timemodified = $now;
575 $newrecord->mimetype = mimeinfo('type', $newfilename);
576 $newrecord->userid = $userid;
578 if ($file = $fs->create_file_from_pathname($newrecord, $pathname)) {
579 return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
581 return null;
585 * Create new file from stored file - make sure
586 * params are valid.
588 * @param string $newfilename name of new file
589 * @param int|stored_file $fid file id or stored_file of file
590 * @param int $userid id of author, default $USER->id
591 * @return file_info|null new file's file_info instance or null if failed
593 public function create_file_from_storedfile($newfilename, $fid, $userid = NULL) {
594 if (!$this->is_writable() or $this->lf->get_filename() !== '.') {
595 return null;
598 $newfilename = clean_param($newfilename, PARAM_FILE);
599 if ($newfilename === '') {
600 return null;
603 $fs = get_file_storage();
605 $now = time();
607 $newrecord = new stdClass();
608 $newrecord->contextid = $this->lf->get_contextid();
609 $newrecord->component = $this->lf->get_component();
610 $newrecord->filearea = $this->lf->get_filearea();
611 $newrecord->itemid = $this->lf->get_itemid();
612 $newrecord->filepath = $this->lf->get_filepath();
613 $newrecord->filename = $newfilename;
615 if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
616 // file already exists, sorry
617 return null;
620 $newrecord->timecreated = $now;
621 $newrecord->timemodified = $now;
622 $newrecord->mimetype = mimeinfo('type', $newfilename);
623 $newrecord->userid = $userid;
625 if ($file = $fs->create_file_from_storedfile($newrecord, $fid)) {
626 return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
628 return null;
632 * Delete file, make sure file is deletable first.
634 * @return bool success
636 public function delete() {
637 if (!$this->is_writable()) {
638 return false;
641 if ($this->is_directory()) {
642 $filepath = $this->lf->get_filepath();
643 $fs = get_file_storage();
644 $storedfiles = $fs->get_area_files($this->context->id, $this->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid());
645 foreach ($storedfiles as $file) {
646 if (strpos($file->get_filepath(), $filepath) === 0) {
647 $file->delete();
652 return $this->lf->delete();
656 * Copy content of this file to local storage, overriding current file if needed.
658 * @param array|stdClass $filerecord contains contextid, component, filearea,
659 * itemid, filepath, filename and optionally other attributes of the new file
660 * @return bool success
662 public function copy_to_storage($filerecord) {
663 if (!$this->is_readable() or $this->is_directory()) {
664 return false;
666 $filerecord = (array)$filerecord;
668 $fs = get_file_storage();
669 if ($existing = $fs->get_file($filerecord['contextid'], $filerecord['component'], $filerecord['filearea'], $filerecord['itemid'], $filerecord['filepath'], $filerecord['filename'])) {
670 $existing->delete();
672 $fs->create_file_from_storedfile($filerecord, $this->lf);
674 return true;
678 * Copy content of this file to local storage, overriding current file if needed.
680 * @param string $pathname real local full file name
681 * @return bool success
683 public function copy_to_pathname($pathname) {
684 if (!$this->is_readable() or $this->is_directory()) {
685 return false;
688 if (file_exists($pathname)) {
689 if (!unlink($pathname)) {
690 return false;
694 $this->lf->copy_content_to($pathname);
696 return true;