See Release Notes
Long Term Support Release
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. 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 /** 19 * Utility class for browsing of stored files. 20 * 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 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 /** 29 * Represents an actual file or folder - a row in the file table in the tree navigated by {@link file_browser}. 30 * 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 34 */ 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; 50 51 /** 52 * Constructor 53 * 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 63 */ 64 public function __construct(file_browser $browser, $context, $storedfile, $urlbase, $topvisiblename, $itemidused, $readaccess, $writeaccess, $areaonly) { 65 parent::__construct($browser, $context); 66 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; 74 } 75 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 80 * 81 * @return array with keys contextid, component, filearea, itemid, filepath and filename 82 */ 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()); 90 } 91 92 /** 93 * Returns localised visible name. 94 * 95 * @return string 96 */ 97 public function get_visible_name() { 98 $filename = $this->lf->get_filename(); 99 $filepath = $this->lf->get_filepath(); 100 101 if ($filename !== '.') { 102 return $filename; 103 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; 112 } 113 } 114 } 115 116 /** 117 * Returns the localised human-readable name of the file together with virtual path 118 * 119 * @return string 120 */ 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()); 127 } 128 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' 136 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'); 149 } 150 } 151 152 return $replocalname. ': '. join('/', $fpath); 153 } 154 } 155 156 /** 157 * Returns file download url 158 * 159 * @param bool $forcedownload Whether or not force download 160 * @param bool $https whether or not force https 161 * @return string url 162 */ 163 public function get_url($forcedownload=false, $https=false) { 164 if (!$this->is_readable()) { 165 return null; 166 } 167 168 if ($this->is_directory()) { 169 return null; 170 } 171 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(); 179 180 if ($this->itemidused) { 181 $path = '/'.$contextid.'/'.$component.'/'.$filearea.'/'.$itemid.$filepath.$filename; 182 } else { 183 $path = '/'.$contextid.'/'.$component.'/'.$filearea.$filepath.$filename; 184 } 185 return file_encode_url($this->urlbase, $path, $forcedownload, $https); 186 } 187 188 /** 189 * Whether or not I can read content of this file or enter directory 190 * 191 * @return bool 192 */ 193 public function is_readable() { 194 return $this->readaccess; 195 } 196 197 /** 198 * Whether or not new files or directories can be added 199 * 200 * @return bool 201 */ 202 public function is_writable() { 203 return $this->writeaccess; 204 } 205 206 /** 207 * Whether or not this is an empty area 208 * 209 * @return bool 210 */ 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; 218 } 219 } 220 221 /** 222 * Returns file size in bytes, null for directories 223 * 224 * @return int bytes or null if not known 225 */ 226 public function get_filesize() { 227 return $this->lf->get_filesize(); 228 } 229 230 /** 231 * Returns width, height and mimetype of the stored image, or false 232 * 233 * @see stored_file::get_imageinfo() 234 * @return array|false 235 */ 236 public function get_imageinfo() { 237 return $this->lf->get_imageinfo(); 238 } 239 240 /** 241 * Returns mimetype 242 * 243 * @return string mimetype or null if not known 244 */ 245 public function get_mimetype() { 246 return $this->lf->get_mimetype(); 247 } 248 249 /** 250 * Returns time created unix timestamp if known 251 * 252 * @return int timestamp or null 253 */ 254 public function get_timecreated() { 255 return $this->lf->get_timecreated(); 256 } 257 258 /** 259 * Returns time modified unix timestamp if known 260 * 261 * @return int timestamp or null 262 */ 263 public function get_timemodified() { 264 return $this->lf->get_timemodified(); 265 } 266 267 /** 268 * Whether or not this is a directory 269 * 270 * @return bool 271 */ 272 public function is_directory() { 273 return $this->lf->is_directory(); 274 } 275 276 /** 277 * Returns the license type of the file 278 * 279 * @return string license short name or null 280 */ 281 public function get_license() { 282 return $this->lf->get_license(); 283 } 284 285 /** 286 * Returns the author name of the file 287 * 288 * @return string author name or null 289 */ 290 public function get_author() { 291 return $this->lf->get_author(); 292 } 293 294 /** 295 * Returns the source of the file 296 * 297 * @return string a source url or null 298 */ 299 public function get_source() { 300 return $this->lf->get_source(); 301 } 302 303 /** 304 * Returns the sort order of the file 305 * 306 * @return int 307 */ 308 public function get_sortorder() { 309 return $this->lf->get_sortorder(); 310 } 311 312 /** 313 * Whether or not this is a external resource 314 * 315 * @return bool 316 */ 317 public function is_external_file() { 318 return $this->lf->is_external_file(); 319 } 320 321 /** 322 * Returns file status flag. 323 * 324 * @return int 0 means file OK, anything else is a problem and file can not be used 325 */ 326 public function get_status() { 327 return $this->lf->get_status(); 328 } 329 330 /** 331 * Returns list of children. 332 * 333 * @return array of file_info instances 334 */ 335 public function get_children() { 336 if (!$this->lf->is_directory()) { 337 return array(); 338 } 339 340 $result = array(); 341 $fs = get_file_storage(); 342 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); 348 } 349 350 return $result; 351 } 352 353 /** 354 * Returns list of children which are either files matching the specified extensions 355 * or folders that contain at least one such file. 356 * 357 * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg') 358 * @return array of file_info instances 359 */ 360 public function get_non_empty_children($extensions = '*') { 361 $result = array(); 362 if (!$this->lf->is_directory()) { 363 return $result; 364 } 365 366 $fs = get_file_storage(); 367 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; 377 } 378 } 379 } 380 381 return $result; 382 } 383 384 /** 385 * Returns the number of children which are either files matching the specified extensions 386 * or folders containing at least one such file. 387 * 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 391 */ 392 public function count_non_empty_children($extensions = '*', $limit = 1) { 393 global $DB; 394 if (!$this->lf->is_directory()) { 395 return 0; 396 } 397 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]; 421 } 422 } 423 if (count($children) >= $limit) { 424 break; 425 } 426 } 427 $rs->close(); 428 return count($children); 429 } 430 431 /** 432 * Returns parent file_info instance 433 * 434 * @return file_info|null file_info instance or null for root 435 */ 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); 444 } 445 } 446 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(), '.'); 449 } 450 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/"; 457 458 return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, '.'); 459 } 460 461 /** 462 * Create new directory, may throw exception - make sure 463 * params are valid. 464 * 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 468 */ 469 public function create_directory($newdirname, $userid = NULL) { 470 if (!$this->is_writable() or !$this->lf->is_directory()) { 471 return null; 472 } 473 474 $newdirname = clean_param($newdirname, PARAM_FILE); 475 if ($newdirname === '') { 476 return null; 477 } 478 479 $filepath = $this->lf->get_filepath().'/'.$newdirname.'/'; 480 481 $fs = get_file_storage(); 482 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()); 485 } 486 return null; 487 } 488 489 490 /** 491 * Create new file from string - make sure 492 * params are valid. 493 * 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 498 */ 499 public function create_file_from_string($newfilename, $content, $userid = NULL) { 500 if (!$this->is_writable() or !$this->lf->is_directory()) { 501 return null; 502 } 503 504 $newfilename = clean_param($newfilename, PARAM_FILE); 505 if ($newfilename === '') { 506 return null; 507 } 508 509 $fs = get_file_storage(); 510 511 $now = time(); 512 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; 520 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; 524 } 525 526 $newrecord->timecreated = $now; 527 $newrecord->timemodified = $now; 528 $newrecord->mimetype = mimeinfo('type', $newfilename); 529 $newrecord->userid = $userid; 530 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()); 533 } 534 return null; 535 } 536 537 /** 538 * Create new file from pathname - make sure 539 * params are valid. 540 * 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 545 */ 546 public function create_file_from_pathname($newfilename, $pathname, $userid = NULL) { 547 if (!$this->is_writable() or !$this->lf->is_directory()) { 548 return null; 549 } 550 551 $newfilename = clean_param($newfilename, PARAM_FILE); 552 if ($newfilename === '') { 553 return null; 554 } 555 556 $fs = get_file_storage(); 557 558 $now = time(); 559 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; 567 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; 571 } 572 573 $newrecord->timecreated = $now; 574 $newrecord->timemodified = $now; 575 $newrecord->mimetype = mimeinfo('type', $newfilename); 576 $newrecord->userid = $userid; 577 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()); 580 } 581 return null; 582 } 583 584 /** 585 * Create new file from stored file - make sure 586 * params are valid. 587 * 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 592 */ 593 public function create_file_from_storedfile($newfilename, $fid, $userid = NULL) { 594 if (!$this->is_writable() or $this->lf->get_filename() !== '.') { 595 return null; 596 } 597 598 $newfilename = clean_param($newfilename, PARAM_FILE); 599 if ($newfilename === '') { 600 return null; 601 } 602 603 $fs = get_file_storage(); 604 605 $now = time(); 606 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; 614 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; 618 } 619 620 $newrecord->timecreated = $now; 621 $newrecord->timemodified = $now; 622 $newrecord->mimetype = mimeinfo('type', $newfilename); 623 $newrecord->userid = $userid; 624 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()); 627 } 628 return null; 629 } 630 631 /** 632 * Delete file, make sure file is deletable first. 633 * 634 * @return bool success 635 */ 636 public function delete() { 637 if (!$this->is_writable()) { 638 return false; 639 } 640 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(); 648 } 649 } 650 } 651 652 return $this->lf->delete(); 653 } 654 655 /** 656 * Copy content of this file to local storage, overriding current file if needed. 657 * 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 661 */ 662 public function copy_to_storage($filerecord) { 663 if (!$this->is_readable() or $this->is_directory()) { 664 return false; 665 } 666 $filerecord = (array)$filerecord; 667 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(); 671 } 672 $fs->create_file_from_storedfile($filerecord, $this->lf); 673 674 return true; 675 } 676 677 /** 678 * Copy content of this file to local storage, overriding current file if needed. 679 * 680 * @param string $pathname real local full file name 681 * @return bool success 682 */ 683 public function copy_to_pathname($pathname) { 684 if (!$this->is_readable() or $this->is_directory()) { 685 return false; 686 } 687 688 if (file_exists($pathname)) { 689 if (!unlink($pathname)) { 690 return false; 691 } 692 } 693 694 $this->lf->copy_content_to($pathname); 695 696 return true; 697 } 698 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body