first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+237
View File
@@ -0,0 +1,237 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Utility class for browsing of files.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once("$CFG->libdir/filebrowser/file_info.php");
// general area types
require_once("$CFG->libdir/filebrowser/file_info_stored.php");
require_once("$CFG->libdir/filebrowser/virtual_root_file.php");
// description of available areas in each context level
require_once("$CFG->libdir/filebrowser/file_info_context_system.php");
require_once("$CFG->libdir/filebrowser/file_info_context_user.php");
require_once("$CFG->libdir/filebrowser/file_info_context_coursecat.php");
require_once("$CFG->libdir/filebrowser/file_info_context_course.php");
require_once("$CFG->libdir/filebrowser/file_info_context_module.php");
/**
* This class provides the main entry point for other code wishing to get information about files.
*
* The whole file storage for a Moodle site can be seen as a huge virtual tree.
* The spine of the tree is the tree of contexts (system, course-categories,
* courses, modules, also users). Then, within each context, there may be any number of
* file areas, and a file area contains folders and files. The various file_info
* subclasses return info about the things in this tree. They should be obtained
* from an instance of this class.
*
* This virtual tree is different for each user depending of his/her current permissions.
* Some branches such as draft areas are hidden, but accessible.
*
* Always use this abstraction when you need to access module files from core code.
*
* @package core_files
* @category files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_browser {
/** @var array cached list of enrolled courses. */
protected $enrolledcourses = null;
/**
* Looks up file_info instance
*
* @param stdClass $context context object
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
public function get_file_info($context = NULL, $component = NULL, $filearea = NULL, $itemid = NULL, $filepath = NULL, $filename = NULL) {
if (!$context) {
$context = context_system::instance();
}
switch ($context->contextlevel) {
case CONTEXT_SYSTEM:
return $this->get_file_info_context_system($context, $component, $filearea, $itemid, $filepath, $filename);
case CONTEXT_USER:
return $this->get_file_info_context_user($context, $component, $filearea, $itemid, $filepath, $filename);
case CONTEXT_COURSECAT:
return $this->get_file_info_context_coursecat($context, $component, $filearea, $itemid, $filepath, $filename);
case CONTEXT_COURSE:
return $this->get_file_info_context_course($context, $component, $filearea, $itemid, $filepath, $filename);
case CONTEXT_MODULE:
return $this->get_file_info_context_module($context, $component, $filearea, $itemid, $filepath, $filename);
}
return null;
}
/**
* Returns info about the files at System context
* @todo MDL-33372 - Provide a way of displaying recent files for blog entries.
*
* @param object $context context object
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info instance or null if not found or access not allowed
*/
private function get_file_info_context_system($context, $component, $filearea, $itemid, $filepath, $filename) {
$level = new file_info_context_system($this, $context);
return $level->get_file_info($component, $filearea, $itemid, $filepath, $filename);
// nothing supported at this context yet
}
/**
* Returns info about the files at User context
*
* @param stdClass $context context object
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
private function get_file_info_context_user($context, $component, $filearea, $itemid, $filepath, $filename) {
global $DB, $USER;
if ($context->instanceid == $USER->id) {
$user = $USER;
} else {
$user = $DB->get_record('user', array('id'=>$context->instanceid));
}
if (isguestuser($user)) {
// guests do not have any files
return null;
}
if ($user->deleted) {
return null;
}
$level = new file_info_context_user($this, $context, $user);
return $level->get_file_info($component, $filearea, $itemid, $filepath, $filename);
}
/**
* Returns info about the files at Course category context
*
* @param stdClass $context context object
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
private function get_file_info_context_coursecat($context, $component, $filearea, $itemid, $filepath, $filename) {
global $DB;
if (!$category = $DB->get_record('course_categories', array('id'=>$context->instanceid))) {
return null;
}
$level = new file_info_context_coursecat($this, $context, $category);
return $level->get_file_info($component, $filearea, $itemid, $filepath, $filename);
}
/**
* Returns info about the files at Course category context
*
* @param stdClass $context context object
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
private function get_file_info_context_course($context, $component, $filearea, $itemid, $filepath, $filename) {
global $DB, $COURSE;
if ($context->instanceid == $COURSE->id) {
$course = $COURSE;
} else if (!$course = $DB->get_record('course', array('id'=>$context->instanceid))) {
return null;
}
$level = new file_info_context_course($this, $context, $course);
return $level->get_file_info($component, $filearea, $itemid, $filepath, $filename);
}
/**
* Returns info about the files at Course category context
*
* @param context $context context object
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
private function get_file_info_context_module($context, $component, $filearea, $itemid, $filepath, $filename) {
if (!($context instanceof context_module)) {
return null;
}
$coursecontext = $context->get_course_context();
$modinfo = get_fast_modinfo($coursecontext->instanceid);
$cm = $modinfo->get_cm($context->instanceid);
if (empty($cm->uservisible)) {
return null;
}
$level = new file_info_context_module($this, $context, $cm->get_course(), $cm, $cm->modname);
return $level->get_file_info($component, $filearea, $itemid, $filepath, $filename);
}
/**
* Check if user is enrolled into the course
*
* This function keeps a cache of enrolled courses because it may be called multiple times for many courses in one request
*
* @param int $courseid
* @return bool
*/
public function is_enrolled($courseid) {
if ($this->enrolledcourses === null || PHPUNIT_TEST) {
// Since get_file_browser() returns a statically cached object we can't rely on cache
// inside the file_browser class in the unittests.
// TODO MDL-59964 remove this caching when it's implemented inside enrol_get_my_courses().
$this->enrolledcourses = enrol_get_my_courses(['id']);
}
return array_key_exists($courseid, $this->enrolledcourses);
}
}
+455
View File
@@ -0,0 +1,455 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Base for all file browsing classes.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Base class for things in the tree navigated by {@link file_browser}.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class file_info {
/** @var context File context */
protected $context;
/** @var file_browser File browser instance */
protected $browser;
/**
* Constructor
*
* @param file_browser $browser file_browser instance
* @param stdClass $context
*/
public function __construct($browser, $context) {
$this->browser = $browser;
$this->context = $context;
}
/**
* Returns list of standard virtual file/directory identification.
* The difference from stored_file parameters is that null values
* are allowed in all fields
*
* @return array with keys contextid, component, filearea, itemid, filepath and filename
*/
public function get_params() {
return array('contextid' => $this->context->id,
'component' => null,
'filearea' => null,
'itemid' => null,
'filepath' => null,
'filename' => null);
}
/**
* Returns localised visible name.
*
* @return string
*/
abstract public function get_visible_name();
/**
* Whether or not this is a directory
*
* @return bool
*/
abstract public function is_directory();
/**
* Returns list of children.
*
* @return array of file_info instances
*/
abstract public function get_children();
/**
* Builds SQL sub query (WHERE clause) for selecting files with the specified extensions
*
* If $extensions == '*' (any file), the result is array('', array())
* otherwise the result is something like array('AND filename ...', array(...))
*
* @param string|array $extensions - either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
* @param string $prefix prefix for DB table files in the query (empty by default)
* @return array of two elements: $sql - sql where clause and $params - array of parameters
*/
protected function build_search_files_sql($extensions, $prefix = null) {
global $DB;
if ($prefix && strlen($prefix)) {
$prefix = $prefix . '.';
} else {
$prefix = '';
}
$sql = '';
$params = array();
if (is_array($extensions) && !in_array('*', $extensions)) {
$likes = array();
$cnt = 0;
foreach ($extensions as $ext) {
$cnt++;
$likes[] = $DB->sql_like($prefix.'filename', ':filename'.$cnt, false);
$params['filename'.$cnt] = '%'.$ext;
}
$sql .= ' AND (' . join(' OR ', $likes) . ')';
}
return array($sql, $params);
}
/**
* Returns list of children which are either files matching the specified extensions
* or folders that contain at least one such file.
*
* It is recommended to overwrite this function so it uses a proper SQL
* query and does not create unnecessary file_info objects (might require a lot of time
* and memory usage on big sites).
*
* @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
* @return array of file_info instances
*/
public function get_non_empty_children($extensions = '*') {
$list = $this->get_children();
$nonemptylist = array();
foreach ($list as $fileinfo) {
if ($fileinfo->is_directory()) {
if ($fileinfo->count_non_empty_children($extensions)) {
$nonemptylist[] = $fileinfo;
}
} else if ($extensions === '*') {
$nonemptylist[] = $fileinfo;
} else {
$filename = $fileinfo->get_visible_name();
$extension = core_text::strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (!empty($extension) && in_array('.' . $extension, $extensions)) {
$nonemptylist[] = $fileinfo;
}
}
}
return $nonemptylist;
}
/**
* Returns the number of children which are either files matching the specified extensions
* or folders containing at least one such file.
*
* We usually don't need the exact number of non empty children if it is >=2 (see param $limit)
* This function is used by repository_local to evaluate if the folder is empty. But
* it also can be used to check if folder has only one subfolder because in some cases
* this subfolder can be skipped.
*
* It is strongly recommended to overwrite this function so it uses a proper SQL
* query and does not create file_info objects (later might require a lot of time
* and memory usage on big sites).
*
* @param string|array $extensions, for example '*' or array('.gif','.jpg')
* @param int $limit stop counting after at least $limit non-empty children are found
* @return int
*/
public function count_non_empty_children($extensions = '*', $limit = 1) {
$list = $this->get_children();
$cnt = 0;
// first loop through files
foreach ($list as $fileinfo) {
if (!$fileinfo->is_directory()) {
if ($extensions !== '*') {
$filename = $fileinfo->get_visible_name();
$extension = core_text::strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (empty($extension) || !in_array('.' . $extension, $extensions)) {
continue;
}
}
if ((++$cnt) >= $limit) {
return $cnt;
}
}
}
// now loop through directories
foreach ($list as $fileinfo) {
if ($fileinfo->is_directory() && $fileinfo->count_non_empty_children($extensions)) {
if ((++$cnt) >= $limit) {
return $cnt;
}
}
}
return $cnt;
}
/**
* Returns parent file_info instance
*
* @return file_info or null for root
*/
abstract public function get_parent();
/**
* Returns array of url encoded params.
*
* @return array with numeric keys
*/
public function get_params_rawencoded() {
$params = $this->get_params();
$encoded = array();
$encoded[] = 'contextid=' . $params['contextid'];
$encoded[] = 'component=' . $params['component'];
$encoded[] = 'filearea=' . $params['filearea'];
$encoded[] = 'itemid=' . (is_null($params['itemid']) ? -1 : $params['itemid']);
$encoded[] = 'filepath=' . (is_null($params['filepath']) ? '' : rawurlencode($params['filepath']));
$encoded[] = 'filename=' . ((is_null($params['filename']) or $params['filename'] === '.') ? '' : rawurlencode($params['filename']));
return $encoded;
}
/**
* Returns file download url
*
* @param bool $forcedownload whether or not force download
* @param bool $https whether or not force https
* @return string url
*/
public function get_url($forcedownload=false, $https=false) {
return null;
}
/**
* Whether or not I can read content of this file or enter directory
*
* @return bool
*/
public function is_readable() {
return true;
}
/**
* Whether or not new files or directories can be added
*
* @return bool
*/
public function is_writable() {
return true;
}
/**
* Is this info area and is it "empty"? Are there any files in subfolders?
*
* This is used mostly in repositories to reduce the
* number of empty folders. This method may be very slow,
* use with care.
*
* @return bool
*/
public function is_empty_area() {
return false;
}
/**
* Returns file size in bytes, null for directories
*
* @return int bytes or null if not known
*/
public function get_filesize() {
return null;
}
/**
* Returns mimetype
*
* @return string mimetype or null if not known
*/
public function get_mimetype() {
return null;
}
/**
* Returns time created unix timestamp if known
*
* @return int timestamp or null
*/
public function get_timecreated() {
return null;
}
/**
* Returns time modified unix timestamp if known
*
* @return int timestamp or null
*/
public function get_timemodified() {
return null;
}
/**
* Returns the license type of the file
* @return string license short name or null
*/
public function get_license() {
return null;
}
/**
* Returns the author name of the file
*
* @return string author name or null
*/
public function get_author() {
return null;
}
/**
* Returns the source of the file
*
* @return string a source url or null
*/
public function get_source() {
return null;
}
/**
* Returns the sort order of the file
*
* @return int
*/
public function get_sortorder() {
return 0;
}
/**
* Whether or not this is a external resource
*
* @return bool
*/
public function is_external_file() {
return false;
}
/**
* Returns file status flag.
*
* @return int 0 means file OK, anything else is a problem and file can not be used
*/
public function get_status() {
return 0;
}
/**
* Returns the localised human-readable name of the file together with virtual path
*
* @see file_info_stored::get_readable_fullname()
* @return string
*/
public function get_readable_fullname() {
return null;
}
/**
* Create new directory, may throw exception - make sure
* params are valid.
*
* @param string $newdirname name of new directory
* @param int $userid id of author, default $USER->id
* @return file_info new directory
*/
public function create_directory($newdirname, $userid = NULL) {
return null;
}
/**
* Create new file from string - make sure
* params are valid.
*
* @param string $newfilename name of new file
* @param string $content of file
* @param int $userid id of author, default $USER->id
* @return file_info new file
*/
public function create_file_from_string($newfilename, $content, $userid = NULL) {
return null;
}
/**
* Create new file from pathname - make sure
* params are valid.
*
* @param string $newfilename name of new file
* @param string $pathname location of file
* @param int $userid id of author, default $USER->id
* @return file_info new file
*/
public function create_file_from_pathname($newfilename, $pathname, $userid = NULL) {
return null;
}
/**
* Create new file from stored file - make sure
* params are valid.
*
* @param string $newfilename name of new file
* @param int|stored_file $fid id or stored_file of file
* @param int $userid id of author, default $USER->id
* @return file_info new file
*/
public function create_file_from_storedfile($newfilename, $fid, $userid = NULL) {
return null;
}
/**
* Delete file, make sure file is deletable first.
*
* @return bool success
*/
public function delete() {
return false;
}
/**
* Copy content of this file to local storage, overriding current file if needed.
*
* @param array|stdClass $filerecord contains contextid, component, filearea,
* itemid, filepath, filename and optionally other attributes of the new file
* @return bool success
*/
public function copy_to_storage($filerecord) {
return false;
}
/**
* Copy content of this file to local storage, overriding current file if needed.
*
* @todo MDL-31068 implement move() rename() unzip() zip()
* @param string $pathname real local full file name
* @return boolean success
*/
public function copy_to_pathname($pathname) {
return false;
}
//TODO: following methods are not implemented yet ;-)
//public abstract function move(location params);
//public abstract function rename(new name);
//public abstract function unzip(location params);
//public abstract function zip(zip file, file info);
}
@@ -0,0 +1,997 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Utility class for browsing of course files.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Represents a course context in the tree navigated by {@link file_browser}.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_info_context_course extends file_info {
/** @var stdClass course object */
protected $course;
/** @var file_info_context_module[] cached child modules. See {@link get_child_module()} */
protected $childrenmodules = [];
/**
* Constructor
*
* @param file_browser $browser file browser instance
* @param stdClass $context context object
* @param stdClass $course course object
*/
public function __construct($browser, $context, $course) {
parent::__construct($browser, $context);
$this->course = $course;
}
/**
* Return information about this specific context level
*
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
public function get_file_info($component, $filearea, $itemid, $filepath, $filename) {
// try to emulate require_login() tests here
if (!isloggedin()) {
return null;
}
if (!$this->course->visible and !has_capability('moodle/course:viewhiddencourses', $this->context)) {
return null;
}
if (!is_viewing($this->context) and !$this->browser->is_enrolled($this->course->id)) {
// no peaking here if not enrolled or inspector
return null;
}
if (empty($component)) {
return $this;
}
$methodname = "get_area_{$component}_{$filearea}";
if (method_exists($this, $methodname)) {
return $this->$methodname($itemid, $filepath, $filename);
}
return null;
}
/**
* Returns list of areas inside this course
*
* @param string $extensions Only return areas that have files with these extensions
* @param bool $returnemptyfolders return all areas always, if true it will ignore the previous argument
* @return array
*/
protected function get_course_areas($extensions = '*', $returnemptyfolders = false) {
global $DB;
$allareas = [
'course_summary',
'course_overviewfiles',
'course_section',
'backup_section',
'backup_course',
'backup_automated',
'course_legacy'
];
if ($returnemptyfolders) {
return $allareas;
}
$params1 = ['contextid' => $this->context->id, 'emptyfilename' => '.'];
$sql1 = "SELECT " . $DB->sql_concat('f.component', "'_'", 'f.filearea') . "
FROM {files} f
WHERE f.filename <> :emptyfilename AND f.contextid = :contextid ";
$sql3 = ' GROUP BY f.component, f.filearea';
list($sql2, $params2) = $this->build_search_files_sql($extensions);
$areaswithfiles = $DB->get_fieldset_sql($sql1 . $sql2 . $sql3, array_merge($params1, $params2));
return array_intersect($allareas, $areaswithfiles);
}
/**
* Gets a stored file for the course summary filearea directory
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
protected function get_area_course_summary($itemid, $filepath, $filename) {
global $CFG;
if (!has_capability('moodle/course:update', $this->context)) {
return null;
}
if (is_null($itemid)) {
return $this;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'course', 'summary', 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'course', 'summary', 0);
} else {
// not found
return null;
}
}
$urlbase = $CFG->wwwroot.'/pluginfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('areacourseintro', 'repository'), false, true, true, false);
}
/**
* Gets a stored file for the course images filearea directory
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
protected function get_area_course_overviewfiles($itemid, $filepath, $filename) {
global $CFG;
if (!has_capability('moodle/course:update', $this->context)) {
return null;
}
if (is_null($itemid)) {
return $this;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'course', 'overviewfiles', 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'course', 'overviewfiles', 0);
} else {
// not found
return null;
}
}
$urlbase = $CFG->wwwroot.'/pluginfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('areacourseoverviewfiles', 'repository'), false, true, true, false);
}
/**
* Gets a stored file for the course section filearea directory
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
protected function get_area_course_section($itemid, $filepath, $filename) {
global $CFG, $DB;
if (!has_capability('moodle/course:update', $this->context)) {
return null;
}
if (empty($itemid)) {
// list all sections
return new file_info_area_course_section($this->browser, $this->context, $this->course, $this);
}
if (!$section = $DB->get_record('course_sections', array('course'=>$this->course->id, 'id'=>$itemid))) {
return null; // does not exist
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'course', 'section', $itemid, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'course', 'section', $itemid);
} else {
// not found
return null;
}
}
$urlbase = $CFG->wwwroot.'/pluginfile.php';
require_once($CFG->dirroot.'/course/lib.php');
$sectionname = get_section_name($this->course, $section);
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, $sectionname, true, true, true, false);
}
/**
* Gets a stored file for the course legacy filearea directory
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
protected function get_area_course_legacy($itemid, $filepath, $filename) {
if (!has_capability('moodle/course:managefiles', $this->context)) {
return null;
}
if ($this->course->id != SITEID and $this->course->legacyfiles != 2) {
// bad luck, legacy course files not used any more
}
if (is_null($itemid)) {
return $this;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'course', 'legacy', 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'course', 'legacy', 0);
} else {
// not found
return null;
}
}
return new file_info_area_course_legacy($this->browser, $this->context, $storedfile);
}
/**
* Gets a stored file for the backup course filearea directory
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
protected function get_area_backup_course($itemid, $filepath, $filename) {
global $CFG;
if (!has_capability('moodle/backup:backupcourse', $this->context) and !has_capability('moodle/restore:restorecourse', $this->context)) {
return null;
}
if (is_null($itemid)) {
return $this;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'backup', 'course', 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'backup', 'course', 0);
} else {
// not found
return null;
}
}
$downloadable = has_capability('moodle/backup:downloadfile', $this->context);
$uploadable = has_capability('moodle/restore:uploadfile', $this->context);
$urlbase = $CFG->wwwroot.'/pluginfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('coursebackup', 'repository'), false, $downloadable, $uploadable, false);
}
/**
* Gets a stored file for the automated backup filearea directory
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null
*/
protected function get_area_backup_automated($itemid, $filepath, $filename) {
global $CFG;
if (!has_capability('moodle/restore:viewautomatedfilearea', $this->context)) {
return null;
}
if (is_null($itemid)) {
return $this;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'backup', 'automated', 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'backup', 'automated', 0);
} else {
// not found
return null;
}
}
// Automated backup files are only downloadable if the user has both 'backup:downloadfile and 'restore:userinfo'.
$downloadable = has_capability('moodle/backup:downloadfile', $this->context) &&
has_capability('moodle/restore:userinfo', $this->context);
$uploadable = false;
$urlbase = $CFG->wwwroot.'/pluginfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('automatedbackup', 'repository'), true, $downloadable, $uploadable, false);
}
/**
* Gets a stored file for the backup section filearea directory
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
protected function get_area_backup_section($itemid, $filepath, $filename) {
global $CFG, $DB;
if (!has_capability('moodle/backup:backupcourse', $this->context) and !has_capability('moodle/restore:restorecourse', $this->context)) {
return null;
}
if (empty($itemid)) {
// list all sections
return new file_info_area_backup_section($this->browser, $this->context, $this->course, $this);
}
if (!$section = $DB->get_record('course_sections', array('course'=>$this->course->id, 'id'=>$itemid))) {
return null; // does not exist
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'backup', 'section', $itemid, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'backup', 'section', $itemid);
} else {
// not found
return null;
}
}
$downloadable = has_capability('moodle/backup:downloadfile', $this->context);
$uploadable = has_capability('moodle/restore:uploadfile', $this->context);
$urlbase = $CFG->wwwroot.'/pluginfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, $section->id, true, $downloadable, $uploadable, false);
}
/**
* Returns localised visible name.
*
* @return string
*/
public function get_visible_name() {
return ($this->course->id == SITEID) ? get_string('frontpage', 'admin') : format_string(get_course_display_name_for_list($this->course), true, array('context'=>$this->context));
}
/**
* Whether or not new files or directories can be added
*
* @return bool
*/
public function is_writable() {
return false;
}
/**
* Whether or not this is a directory
*
* @return bool
*/
public function is_directory() {
return true;
}
/**
* Returns list of children.
*
* @return array of file_info instances
*/
public function get_children() {
return $this->get_filtered_children('*', false, true);
}
/**
* Returns the child module if it is accessible by the current user
*
* @param cm_info|int $cm
* @return file_info_context_module|null
*/
protected function get_child_module($cm) {
$cmid = is_object($cm) ? $cm->id : $cm;
if (!array_key_exists($cmid, $this->childrenmodules)) {
$this->childrenmodules[$cmid] = null;
if (!($cm instanceof cm_info)) {
$cms = get_fast_modinfo($this->course)->cms;
$cm = array_key_exists($cmid, $cms) ? $cms[$cmid] : null;
}
if ($cm && $cm->uservisible) {
$this->childrenmodules[$cmid] = new file_info_context_module($this->browser,
$cm->context, $this->course, $cm, $cm->modname);
}
}
return $this->childrenmodules[$cmid];
}
/**
* Help function to return files matching extensions or their count
*
* @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
* @param bool|int $countonly if false returns the children, if an int returns just the
* count of children but stops counting when $countonly number of children is reached
* @param bool $returnemptyfolders if true returns items that don't have matching files inside
* @return array|int array of file_info instances or the count
*/
private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
$children = array();
$courseareas = $this->get_course_areas($extensions, $returnemptyfolders);
foreach ($courseareas as $areaname) {
$area = explode('_', $areaname, 2);
if ($child = $this->get_file_info($area[0], $area[1], 0, '/', '.')) {
$children[] = $child;
if (($countonly !== false) && count($children) >= $countonly) {
return $countonly;
}
}
}
$cnt = count($children);
if (!has_capability('moodle/course:managefiles', $this->context)) {
// 'managefiles' capability is checked in every activity module callback.
// Don't even waste time on retrieving the modules if we can't browse the files anyway
} else {
if ($returnemptyfolders) {
$modinfo = get_fast_modinfo($this->course);
foreach ($modinfo->cms as $cminfo) {
if ($child = $this->get_child_module($cminfo)) {
$children[] = $child;
$cnt++;
}
}
} else if ($moduleareas = $this->get_module_areas_with_files($extensions)) {
// We found files in some of the modules.
// Create array of children modules ordered with the same way as cms in modinfo.
$modulechildren = array_fill_keys(array_keys(get_fast_modinfo($this->course)->get_cms()), null);
foreach ($moduleareas as $area) {
if ($modulechildren[$area->cmid]) {
// We already found non-empty area within the same module, do not analyse other areas.
continue;
}
if ($child = $this->get_child_module($area->cmid)) {
if ($child->get_file_info($area->component, $area->filearea, $area->itemid, null, null)) {
$modulechildren[$area->cmid] = $child;
$cnt++;
if (($countonly !== false) && $cnt >= $countonly) {
return $cnt;
}
}
}
}
$children = array_merge($children, array_values(array_filter($modulechildren)));
}
}
if ($countonly !== false) {
return count($children);
}
return $children;
}
/**
* Returns list of areas inside the course modules that have files with the given extension
*
* @param string $extensions
* @return array
*/
protected function get_module_areas_with_files($extensions = '*') {
global $DB;
$params1 = ['contextid' => $this->context->id,
'emptyfilename' => '.',
'contextlevel' => CONTEXT_MODULE,
'course' => $this->course->id];
$ctxfieldsas = context_helper::get_preload_record_columns_sql('ctx');
$ctxfields = implode(', ', array_keys(context_helper::get_preload_record_columns('ctx')));
$sql1 = "SELECT
ctx.id AS contextid,
f.component,
f.filearea,
f.itemid,
ctx.instanceid AS cmid,
{$ctxfieldsas}
FROM {files} f
INNER JOIN {context} ctx ON ctx.id = f.contextid
INNER JOIN {course_modules} cm ON cm.id = ctx.instanceid
WHERE f.filename <> :emptyfilename
AND cm.course = :course
AND ctx.contextlevel = :contextlevel";
$sql3 = "
GROUP BY ctx.id, f.component, f.filearea, f.itemid, {$ctxfields}
ORDER BY ctx.id, f.component, f.filearea, f.itemid";
list($sql2, $params2) = $this->build_search_files_sql($extensions);
$areas = [];
if ($rs = $DB->get_recordset_sql($sql1. $sql2 . $sql3, array_merge($params1, $params2))) {
foreach ($rs as $record) {
context_helper::preload_from_record($record);
$areas[] = $record;
}
$rs->close();
}
// Sort areas so 'backup' and 'intro' are in the beginning of the list, they are the easiest to check access to.
usort($areas, function($a, $b) {
$aeasy = ($a->filearea === 'intro' && substr($a->component, 0, 4) === 'mod_') ||
($a->filearea === 'activity' && $a->component === 'backup');
$beasy = ($b->filearea === 'intro' && substr($b->component, 0, 4) === 'mod_') ||
($b->filearea === 'activity' && $b->component === 'backup');
return $aeasy == $beasy ? 0 : ($aeasy ? -1 : 1);
});
return $areas;
}
/**
* Returns list of children which are either files matching the specified extensions
* or folders that contain at least one such file.
*
* @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
* @return array of file_info instances
*/
public function get_non_empty_children($extensions = '*') {
return $this->get_filtered_children($extensions, false);
}
/**
* Returns the number of children which are either files matching the specified extensions
* or folders containing at least one such file.
*
* @param string|array $extensions, for example '*' or array('.gif','.jpg')
* @param int $limit stop counting after at least $limit non-empty children are found
* @return int
*/
public function count_non_empty_children($extensions = '*', $limit = 1) {
return $this->get_filtered_children($extensions, $limit);
}
/**
* Returns parent file_info instance
*
* @return file_info or null for root
*/
public function get_parent() {
$parent = $this->context->get_parent_context();
return $this->browser->get_file_info($parent);
}
}
/**
* Subclass of file_info_stored for files in the course files area.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_info_area_course_legacy extends file_info_stored {
/**
* Constructor
*
* @param file_browser $browser file browser instance
* @param stdClass $context context object
* @param stored_file $storedfile stored_file instance
*/
public function __construct($browser, $context, $storedfile) {
global $CFG;
$urlbase = $CFG->wwwroot.'/file.php';
parent::__construct($browser, $context, $storedfile, $urlbase, get_string('coursefiles'), false, true, true, false);
}
/**
* Returns file download url
*
* @param bool $forcedownload whether or not force download
* @param bool $https whether or not force https
* @return string url
*/
public function get_url($forcedownload=false, $https=false) {
if (!$this->is_readable()) {
return null;
}
if ($this->lf->is_directory()) {
return null;
}
$filepath = $this->lf->get_filepath();
$filename = $this->lf->get_filename();
$courseid = $this->context->instanceid;
$path = '/'.$courseid.$filepath.$filename;
return file_encode_url($this->urlbase, $path, $forcedownload, $https);
}
/**
* Returns list of children.
*
* @return array of file_info instances
*/
public function get_children() {
if (!$this->lf->is_directory()) {
return array();
}
$result = array();
$fs = get_file_storage();
$storedfiles = $fs->get_directory_files($this->context->id, 'course', 'legacy', 0, $this->lf->get_filepath(), false, true, "filepath ASC, filename ASC");
foreach ($storedfiles as $file) {
$result[] = new file_info_area_course_legacy($this->browser, $this->context, $file);
}
return $result;
}
/**
* Returns list of children which are either files matching the specified extensions
* or folders that contain at least one such file.
*
* @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
* @return array of file_info instances
*/
public function get_non_empty_children($extensions = '*') {
if (!$this->lf->is_directory()) {
return array();
}
$result = array();
$fs = get_file_storage();
$storedfiles = $fs->get_directory_files($this->context->id, 'course', 'legacy', 0,
$this->lf->get_filepath(), false, true, "filepath, filename");
foreach ($storedfiles as $file) {
$extension = core_text::strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
if ($file->is_directory() || $extensions === '*' || (!empty($extension) && in_array('.'.$extension, $extensions))) {
$fileinfo = new file_info_area_course_legacy($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
$this->itemidused, $this->readaccess, $this->writeaccess, false);
if (!$file->is_directory() || $fileinfo->count_non_empty_children($extensions)) {
$result[] = $fileinfo;
}
}
}
return $result;
}
}
/**
* Represents a course category context in the tree navigated by {@link file_browser}.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_info_area_course_section extends file_info {
/** @var stdClass course object */
protected $course;
/** @var file_info_context_course course file info object */
protected $courseinfo;
/**
* Constructor
*
* @param file_browser $browser file browser instance
* @param stdClass $context context object
* @param stdClass $course course object
* @param file_info_context_course $courseinfo file info instance
*/
public function __construct($browser, $context, $course, file_info_context_course $courseinfo) {
parent::__construct($browser, $context);
$this->course = $course;
$this->courseinfo = $courseinfo;
}
/**
* Returns list of standard virtual file/directory identification.
* The difference from stored_file parameters is that null values
* are allowed in all fields
*
* @return array with keys contextid, filearea, itemid, filepath and filename
*/
public function get_params() {
return array('contextid' => $this->context->id,
'component' => 'course',
'filearea' => 'section',
'itemid' => null,
'filepath' => null,
'filename' => null);
}
/**
* Returns localised visible name.
*
* @return string
*/
public function get_visible_name() {
//$format = $this->course->format;
$sectionsname = get_string("coursesectionsummaries");
return $sectionsname;
}
/**
* Return whether or not new files or directories can be added
*
* @return bool
*/
public function is_writable() {
return false;
}
/**
* Return whether or not this is a empty area
*
* @return bool
*/
public function is_empty_area() {
$fs = get_file_storage();
return $fs->is_area_empty($this->context->id, 'course', 'section');
}
/**
* Return whether or not this is a empty area
*
* @return bool
*/
public function is_directory() {
return true;
}
/**
* Returns list of children.
*
* @return array of file_info instances
*/
public function get_children() {
global $DB;
$children = array();
$course_sections = $DB->get_records('course_sections', array('course'=>$this->course->id), 'section');
foreach ($course_sections as $section) {
if ($child = $this->courseinfo->get_file_info('course', 'section', $section->id, '/', '.')) {
$children[] = $child;
}
}
return $children;
}
/**
* Returns the number of children which are either files matching the specified extensions
* or folders containing at least one such file.
*
* @param string|array $extensions, for example '*' or array('.gif','.jpg')
* @param int $limit stop counting after at least $limit non-empty children are found
* @return int
*/
public function count_non_empty_children($extensions = '*', $limit = 1) {
global $DB;
$params1 = array(
'courseid' => $this->course->id,
'contextid' => $this->context->id,
'component' => 'course',
'filearea' => 'section',
'emptyfilename' => '.');
$sql1 = "SELECT DISTINCT cs.id FROM {files} f, {course_sections} cs
WHERE cs.course = :courseid
AND f.contextid = :contextid
AND f.component = :component
AND f.filearea = :filearea
AND f.itemid = cs.id
AND f.filename <> :emptyfilename";
list($sql2, $params2) = $this->build_search_files_sql($extensions);
$rs = $DB->get_recordset_sql($sql1. ' '. $sql2, array_merge($params1, $params2));
$cnt = 0;
foreach ($rs as $record) {
if ((++$cnt) >= $limit) {
break;
}
}
$rs->close();
return $cnt;
}
/**
* Returns parent file_info instance
*
* @return file_info|null file_info or null for root
*/
public function get_parent() {
return $this->courseinfo;
}
}
/**
* Implementation of course section backup area
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_info_area_backup_section extends file_info {
/** @var stdClass course object */
protected $course;
/** @var file_info_context_course course file info object */
protected $courseinfo;
/**
* Constructor
*
* @param file_browser $browser file browser instance
* @param stdClass $context context object
* @param stdClass $course course object
* @param file_info_context_course $courseinfo file info instance
*/
public function __construct($browser, $context, $course, file_info_context_course $courseinfo) {
parent::__construct($browser, $context);
$this->course = $course;
$this->courseinfo = $courseinfo;
}
/**
* Returns list of standard virtual file/directory identification.
* The difference from stored_file parameters is that null values
* are allowed in all fields
*
* @return array with keys contextid, component, filearea, itemid, filepath and filename
*/
public function get_params() {
return array('contextid' => $this->context->id,
'component' => 'backup',
'filearea' => 'section',
'itemid' => null,
'filepath' => null,
'filename' => null);
}
/**
* Returns localised visible name.
*
* @return string
*/
public function get_visible_name() {
return get_string('sectionbackup', 'repository');
}
/**
* Return whether or not new files and directories can be added
*
* @return bool
*/
public function is_writable() {
return false;
}
/**
* Whether or not this is an empty area
*
* @return bool
*/
public function is_empty_area() {
$fs = get_file_storage();
return $fs->is_area_empty($this->context->id, 'backup', 'section');
}
/**
* Return whether or not this is a directory
*
* @return bool
*/
public function is_directory() {
return true;
}
/**
* Returns list of children.
*
* @return array of file_info instances
*/
public function get_children() {
global $DB;
$children = array();
$course_sections = $DB->get_records('course_sections', array('course'=>$this->course->id), 'section');
foreach ($course_sections as $section) {
if ($child = $this->courseinfo->get_file_info('backup', 'section', $section->id, '/', '.')) {
$children[] = $child;
}
}
return $children;
}
/**
* Returns the number of children which are either files matching the specified extensions
* or folders containing at least one such file.
*
* @param string|array $extensions, for example '*' or array('.gif','.jpg')
* @param int $limit stop counting after at least $limit non-empty children are found
* @return int
*/
public function count_non_empty_children($extensions = '*', $limit = 1) {
global $DB;
$params1 = array(
'courseid' => $this->course->id,
'contextid' => $this->context->id,
'component' => 'backup',
'filearea' => 'section',
'emptyfilename' => '.');
$sql1 = "SELECT DISTINCT cs.id AS sectionid FROM {files} f, {course_sections} cs
WHERE cs.course = :courseid
AND f.contextid = :contextid
AND f.component = :component
AND f.filearea = :filearea
AND f.itemid = cs.id
AND f.filename <> :emptyfilename";
list($sql2, $params2) = $this->build_search_files_sql($extensions);
$rs = $DB->get_recordset_sql($sql1. ' '. $sql2, array_merge($params1, $params2));
$cnt = 0;
foreach ($rs as $record) {
if ((++$cnt) >= $limit) {
break;
}
}
$rs->close();
return $cnt;
}
/**
* Returns parent file_info instance
*
* @return file_info or null for root
*/
public function get_parent() {
return $this->browser->get_file_info($this->context);
}
}
@@ -0,0 +1,327 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Utility class for browsing of curse category files.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Represents a course category context in the tree navigated by {@link file_browser}.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_info_context_coursecat extends file_info {
/** @var stdClass Category object */
protected $category;
/**
* Constructor
*
* @param file_browser $browser file browser instance
* @param stdClass $context context object
* @param stdClass $category category object
*/
public function __construct($browser, $context, $category) {
parent::__construct($browser, $context);
$this->category = $category;
}
/**
* Return information about this specific context level
*
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return fileinfo|null
*/
public function get_file_info($component, $filearea, $itemid, $filepath, $filename) {
global $DB;
if (!core_course_category::can_view_category($this->category)) {
if (empty($component)) {
// we can not list the category contents, so try parent, or top system
if ($this->category->parent and $pc = $DB->get_record('course_categories', array('id'=>$this->category->parent))) {
$parent = context_coursecat::instance($pc->id, IGNORE_MISSING);
return $this->browser->get_file_info($parent);
} else {
return $this->browser->get_file_info();
}
}
return null;
}
if (empty($component)) {
return $this;
}
$methodname = "get_area_{$component}_{$filearea}";
if (method_exists($this, $methodname)) {
return $this->$methodname($itemid, $filepath, $filename);
}
return null;
}
/**
* Return a file from course category description area
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return fileinfo|null
*/
protected function get_area_coursecat_description($itemid, $filepath, $filename) {
global $CFG;
if (!$this->category->id) {
// No coursecat description area for "system".
return null;
}
if (!core_course_category::can_view_category($this->category)) {
return null;
}
if (!has_capability('moodle/category:manage', $this->context)) {
return null;
}
if (is_null($itemid)) {
return $this;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
$urlbase = $CFG->wwwroot.'/pluginfile.php';
if (!$storedfile = $fs->get_file($this->context->id, 'coursecat', 'description', 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'coursecat', 'description', 0);
} else {
// not found
return null;
}
}
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('areacategoryintro', 'repository'), false, true, true, false);
}
/**
* Returns localised visible name.
*
* @return string
*/
public function get_visible_name() {
return format_string($this->category->name, true, array('context'=>$this->context));
}
/**
* Whether or not new files or directories can be added
*
* @return bool
*/
public function is_writable() {
return false;
}
/**
* Whether or not this is a directory
*
* @return bool
*/
public function is_directory() {
return true;
}
/**
* Returns list of children.
*
* @return array of file_info instances
*/
public function get_children() {
$children = array();
if ($child = $this->get_area_coursecat_description(0, '/', '.')) {
$children[] = $child;
}
list($coursecats, $hiddencats) = $this->get_categories();
foreach ($coursecats as $category) {
$context = context_coursecat::instance($category->id);
$children[] = new self($this->browser, $context, $category);
}
$courses = $this->get_courses($hiddencats);
foreach ($courses as $course) {
$children[] = $this->get_child_course($course);
}
return array_filter($children);
}
/**
* List of courses in this category and in hidden subcategories
*
* @param array $hiddencats list of categories that are hidden from current user and returned by {@link get_categories()}
* @return array list of courses
*/
protected function get_courses($hiddencats) {
global $DB, $CFG;
require_once($CFG->libdir.'/modinfolib.php');
// Let's retrieve only minimum number of fields from course table -
// what is needed to check access or call get_fast_modinfo().
$coursefields = array_merge(['id', 'visible', 'sortorder'], \course_modinfo::$cachedfields);
$fields = 'c.' . join(',c.', $coursefields) . ', ' .
\context_helper::get_preload_record_columns_sql('ctx');
// First statement uses only category.
$sql1 = "SELECT $fields
FROM {course} c
JOIN {context} ctx ON (ctx.instanceid = c.id) AND (ctx.contextlevel = :contextlevel1)
WHERE c.category = :categoryid";
$params = ['categoryid' => $this->category->id, 'contextlevel1' => CONTEXT_COURSE];
if (empty($hiddencats)) {
return $DB->get_records_sql($sql1, $params);
}
// Second statement uses only context paths.
$orcond = [];
foreach ($hiddencats as $category) {
$catcontext = context_coursecat::instance($category->id);
// Case- and accent-sensitive search is not necessary for paths.
// If we do without it, this will lead to an enormous performance boost on large scale tables.
$orcond[] = $DB->sql_like('path', ':path' . $category->id, false, false);
$params['path' . $category->id] = $catcontext->path . '/%';
}
$sql2 = "SELECT $fields
FROM {course} c
JOIN {context} ctx ON (ctx.instanceid = c.id) AND (ctx.contextlevel = :contextlevel2)
WHERE (" . implode(' OR ', $orcond) . ")";
$params['contextlevel2'] = CONTEXT_COURSE;
// Combine with UNION.
$sql = "SELECT *
FROM (($sql1) UNION ($sql2)) d
ORDER BY d.sortorder";
return $DB->get_records_sql($sql, $params);
}
/**
* Finds accessible and non-accessible direct subcategories
*
* @return array [$coursecats, $hiddencats] - child categories that are visible to the current user and not visible
*/
protected function get_categories() {
global $DB;
$fields = 'c.*, ' . context_helper::get_preload_record_columns_sql('ctx');
$coursecats = $DB->get_records_sql('SELECT ' . $fields . ' FROM {course_categories} c
LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)
WHERE c.parent = :parent ORDER BY c.sortorder',
array('parent' => $this->category->id, 'contextlevel' => CONTEXT_COURSECAT));
$hiddencats = [];
foreach ($coursecats as $id => &$category) {
context_helper::preload_from_record($category);
if (!core_course_category::can_view_category($category)) {
$hiddencats[$id] = $coursecats[$id];
unset($coursecats[$id]);
}
}
return [$coursecats, $hiddencats];
}
/**
* Returns the file info element for a given course or null if course is not accessible
*
* @param stdClass $course may contain context fields for preloading
* @return file_info_context_course|null
*/
protected function get_child_course($course) {
context_helper::preload_from_record($course);
$context = context_course::instance($course->id);
$child = new file_info_context_course($this->browser, $context, $course);
return $child->get_file_info(null, null, null, null, null);
}
/**
* Returns the number of children which are either files matching the specified extensions
* or folders containing at least one such file.
*
* @param string|array $extensions, for example '*' or array('.gif','.jpg')
* @param int $limit stop counting after at least $limit non-empty children are found
* @return int
*/
public function count_non_empty_children($extensions = '*', $limit = 1) {
$cnt = 0;
if ($child = $this->get_area_coursecat_description(0, '/', '.')) {
$cnt += $child->count_non_empty_children($extensions) ? 1 : 0;
if ($cnt >= $limit) {
return $cnt;
}
}
list($coursecats, $hiddencats) = $this->get_categories();
foreach ($coursecats as $category) {
$context = context_coursecat::instance($category->id);
$child = new file_info_context_coursecat($this->browser, $context, $category);
$cnt += $child->count_non_empty_children($extensions) ? 1 : 0;
if ($cnt >= $limit) {
return $cnt;
}
}
$courses = $this->get_courses($hiddencats);
foreach ($courses as $course) {
if ($child = $this->get_child_course($course)) {
$cnt += $child->count_non_empty_children($extensions) ? 1 : 0;
if ($cnt >= $limit) {
return $cnt;
}
}
}
return $cnt;
}
/**
* Returns parent file_info instance
*
* @return file_info|null fileinfo instance or null for root directory
*/
public function get_parent() {
$parent = $this->context->get_parent_context();
return $this->browser->get_file_info($parent);
}
}
@@ -0,0 +1,347 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Utility class for browsing of module files.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Represents a module context in the tree navigated by {@link file_browser}.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_info_context_module extends file_info {
/** @var stdClass Course object */
protected $course;
/** @var cm_info Course module object */
protected $cm;
/** @var string Module name */
protected $modname;
/** @var array Available file areas */
protected $areas;
/** @var array caches the result of last call to get_non_empty_children() */
protected $nonemptychildren;
/**
* Constructor
*
* @param file_browser $browser file browser instance
* @param stdClass $context context object
* @param stdClass $course course object
* @param stdClass $cm course module object
* @param string $modname module name
*/
public function __construct($browser, $context, $course, $cm, $modname) {
global $CFG;
parent::__construct($browser, $context);
$this->course = $course;
$this->cm = cm_info::create($cm);
$this->modname = $this->cm->modname;
$this->nonemptychildren = null;
if ($functionname = component_callback_exists('mod_'.$modname, 'get_file_areas')) {
$cm = $this->cm->get_course_module_record();
$this->areas = $functionname($course, $cm, $context);
} else {
$this->areas = array();
}
unset($this->areas['intro']); // hardcoded, ignore attempts to override it
}
/**
* Return information about this specific context level
*
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null
*/
public function get_file_info($component, $filearea, $itemid, $filepath, $filename) {
// try to emulate require_login() tests here
if (!isloggedin()) {
return null;
}
$coursecontext = $this->context->get_course_context(true);
if (!$this->course->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
return null;
}
if (!is_viewing($this->context) and !$this->browser->is_enrolled($this->course->id)) {
// no peaking here if not enrolled or inspector
return null;
}
if (!$this->cm->uservisible) {
// activity hidden sorry
return null;
}
if (empty($component)) {
return $this;
}
if ($component == 'mod_'.$this->modname and $filearea === 'intro') {
return $this->get_area_intro($itemid, $filepath, $filename);
} else if ($component == 'backup' and $filearea === 'activity') {
return $this->get_area_backup($itemid, $filepath, $filename);
}
if ($functionname = component_callback_exists('mod_'.$this->modname, 'get_file_info')) {
$cm = $this->cm->get_course_module_record();
return $functionname($this->browser, $this->areas, $this->course, $cm,
$this->context, $filearea, $itemid, $filepath, $filename);
}
return null;
}
/**
* Get a file from module intro area
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null
*/
protected function get_area_intro($itemid, $filepath, $filename) {
global $CFG;
if (!plugin_supports('mod', $this->modname, FEATURE_MOD_INTRO, true) or !has_capability('moodle/course:managefiles', $this->context)) {
return null;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'mod_'.$this->modname, 'intro', 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'mod_'.$this->modname, 'intro', 0);
} else {
// not found
return null;
}
}
$urlbase = $CFG->wwwroot.'/pluginfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('moduleintro'), false, true, true, false);
}
/**
* Get a file from module backup area
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null
*/
protected function get_area_backup($itemid, $filepath, $filename) {
global $CFG;
if (!has_capability('moodle/backup:backupactivity', $this->context)) {
return null;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'backup', 'activity', 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'backup', 'activity', 0);
} else {
// not found
return null;
}
}
$downloadable = has_capability('moodle/backup:downloadfile', $this->context);
$uploadable = has_capability('moodle/restore:uploadfile', $this->context);
$urlbase = $CFG->wwwroot.'/pluginfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('activitybackup', 'repository'), false, $downloadable, $uploadable, false);
}
/**
* Returns localised visible name.
*
* @return string
*/
public function get_visible_name() {
return $this->cm->get_formatted_name().' ('.$this->cm->get_module_type_name().')';
}
/**
* Whether or not files or directories can be added
*
* @return bool
*/
public function is_writable() {
return false;
}
/**
* Whether or not this is an emtpy area
*
* @return bool
*/
public function is_empty_area() {
if ($child = $this->get_area_backup(0, '/', '.')) {
if (!$child->is_empty_area()) {
return false;
}
}
if ($child = $this->get_area_intro(0, '/', '.')) {
if (!$child->is_empty_area()) {
return false;
}
}
foreach ($this->areas as $area=>$desctiption) {
if ($child = $this->get_file_info('mod_'.$this->modname, $area, null, null, null)) {
if (!$child->is_empty_area()) {
return false;
}
}
}
return true;
}
/**
* Whether or not this is a directory
*
* @return bool
*/
public function is_directory() {
return true;
}
/**
* Returns list of children.
*
* @return array of file_info instances
*/
public function get_children() {
return $this->get_filtered_children('*', false, true);
}
/**
* Help function to return files matching extensions or their count
*
* @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
* @param bool|int $countonly if false returns the children, if an int returns just the
* count of children but stops counting when $countonly number of children is reached
* @param bool $returnemptyfolders if true returns items that don't have matching files inside
* @return array|int array of file_info instances or the count
*/
private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
global $DB;
// prepare list of areas including intro and backup
$areas = array(
array('mod_'.$this->modname, 'intro'),
array('backup', 'activity')
);
foreach ($this->areas as $area => $desctiption) {
$areas[] = array('mod_'.$this->modname, $area);
}
$params1 = array('contextid' => $this->context->id, 'emptyfilename' => '.');
list($sql2, $params2) = $this->build_search_files_sql($extensions);
$children = array();
foreach ($areas as $area) {
if (!$returnemptyfolders) {
// fast pre-check if there are any files in the filearea
$params1['component'] = $area[0];
$params1['filearea'] = $area[1];
if (!$DB->record_exists_sql('SELECT 1 from {files}
WHERE contextid = :contextid
AND filename <> :emptyfilename
AND component = :component
AND filearea = :filearea '.$sql2,
array_merge($params1, $params2))) {
continue;
}
}
if ($child = $this->get_file_info($area[0], $area[1], null, null, null)) {
if ($returnemptyfolders || $child->count_non_empty_children($extensions)) {
$children[] = $child;
if ($countonly !== false && count($children) >= $countonly) {
break;
}
}
}
}
if ($countonly !== false) {
return count($children);
}
return $children;
}
/**
* Returns list of children which are either files matching the specified extensions
* or folders that contain at least one such file.
*
* @param string|array $extensions either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
* @return array of file_info instances
*/
public function get_non_empty_children($extensions = '*') {
if ($this->nonemptychildren !== null) {
return $this->nonemptychildren;
}
$this->nonemptychildren = $this->get_filtered_children($extensions);
return $this->nonemptychildren;
}
/**
* Returns the number of children which are either files matching the specified extensions
* or folders containing at least one such file.
*
* @param string|array $extensions for example '*' or array('.gif','.jpg')
* @param int $limit stop counting after at least $limit non-empty children are found
* @return int
*/
public function count_non_empty_children($extensions = '*', $limit = 1) {
if ($this->nonemptychildren !== null) {
return count($this->nonemptychildren);
}
return $this->get_filtered_children($extensions, $limit);
}
/**
* Returns parent file_info instance
*
* @return file_info|null file_info or null for root
*/
public function get_parent() {
$parent = $this->context->get_parent_context();
return $this->browser->get_file_info($parent);
}
}
@@ -0,0 +1,152 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Utility class for browsing of system files.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/filebrowser/file_info_context_coursecat.php');
/**
* Represents the system context in the tree navigated by {@link file_browser}.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_info_context_system extends file_info_context_coursecat {
/**
* Constructor
*
* @param file_browser $browser file_browser instance
* @param stdClass $context context object
*/
public function __construct($browser, $context) {
parent::__construct($browser, $context, (object)['id' => 0, 'parent' => 0, 'visible' => 1]);
}
/**
* Return information about this specific part of context level
*
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
public function get_file_info($component, $filearea, $itemid, $filepath, $filename) {
if (empty($component)) {
return $this;
}
$methodname = "get_area_{$component}_{$filearea}";
if (method_exists($this, $methodname)) {
return $this->$methodname($itemid, $filepath, $filename);
}
return null;
}
/**
* Gets a stored file for the backup course filearea directory.
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null file_info instance or null if not found or access not allowed
*/
protected function get_area_backup_course($itemid, $filepath, $filename) {
global $CFG;
if (!isloggedin()) {
return null;
}
if (!has_any_capability(array('moodle/backup:backupcourse', 'moodle/restore:restorecourse'), $this->context)) {
return null;
}
if (is_null($itemid)) {
return $this;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'backup', 'course', 0, $filepath, $filename)) {
if ($filepath === '/' && $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'backup', 'course', 0);
} else {
// Not found.
return null;
}
}
$downloadable = has_capability('moodle/backup:downloadfile', $this->context);
$uploadable = has_capability('moodle/restore:uploadfile', $this->context);
$urlbase = $CFG->wwwroot . '/pluginfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase,
get_string('coursebackup', 'repository'), false, $downloadable, $uploadable, false);
}
/**
* Returns localised visible name.
*
* @return string
*/
public function get_visible_name() {
return get_string('arearoot', 'repository');
}
/**
* Whether or not new files or directories can be added
*
* @return bool
*/
public function is_writable() {
return false;
}
/**
* Whether or not this is a directory
*
* @return bool
*/
public function is_directory() {
return true;
}
/**
* Returns parent file_info instance
*
* @return file_info|null file_info instance or null for root
*/
public function get_parent() {
return null;
}
}
+306
View File
@@ -0,0 +1,306 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Utility class for browsing of user files.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Represents a user context in the tree navigated by {@link file_browser}.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_info_context_user extends file_info {
/** @var stdClass User object */
protected $user;
/**
* Constructor
*
* @param file_browser $browser
* @param stdClass $context
* @param stdClass $user
*/
public function __construct($browser, $context, $user) {
parent::__construct($browser, $context);
$this->user = $user;
}
/**
* Return information about this specific context level
*
* @param string $component componet
* @param string $filearea file area
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null
*/
public function get_file_info($component, $filearea, $itemid, $filepath, $filename) {
global $USER;
if (!isloggedin() or isguestuser()) {
return null;
}
if (empty($component)) {
// access control: list areas only for myself
if ($this->user->id != $USER->id) {
// no list of areas for other users
return null;
}
return $this;
}
$methodname = "get_area_{$component}_{$filearea}";
if (method_exists($this, $methodname)) {
return $this->$methodname($itemid, $filepath, $filename);
}
return null;
}
/**
* Get a file from user private area
*
* @todo MDL-31070 this method should respect $CFG->userquota
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null
*/
protected function get_area_user_private($itemid, $filepath, $filename) {
global $USER, $CFG;
// access control: only my files, nobody else
if ($this->user->id != $USER->id) {
return null;
}
if (is_null($itemid)) {
// go to parent, we do not use itemids here in private area
return $this;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'user', 'private', 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
// root dir does not exist yet
$storedfile = new virtual_root_file($this->context->id, 'user', 'private', 0);
} else {
// not found
return null;
}
}
$urlbase = $CFG->wwwroot.'/pluginfile.php';
//TODO: user quota from $CFG->userquota
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('areauserpersonal', 'repository'), false, true, true, false);
}
/**
* Get a file from user profile area
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null
*/
protected function get_area_user_profile($itemid, $filepath, $filename) {
global $CFG;
$readaccess = has_capability('moodle/user:update', $this->context);
$writeaccess = has_capability('moodle/user:viewalldetails', $this->context);
if (!$readaccess and !$writeaccess) {
// the idea here is that only admins should be able to list/modify files in user profile, the rest has to use profile page
return null;
}
if (is_null($itemid)) {
// go to parent, we do not use itemids here in profile area
return $this;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'user', 'profile', 0, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'user', 'profile', 0);
} else {
// not found
return null;
}
}
$urlbase = $CFG->wwwroot.'/pluginfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase,
get_string('areauserprofile', 'repository'), false, $readaccess, $writeaccess, false);
}
/**
* Get a file from user draft area
*
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null
*/
protected function get_area_user_draft($itemid, $filepath, $filename) {
global $USER, $CFG;
// access control: only my files
if ($this->user->id != $USER->id) {
return null;
}
if (empty($itemid)) {
// do not browse itemids - you must know the draftid to see what is there
return null;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'user', 'draft', $itemid, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'user', 'draft', $itemid);
} else {
// not found
return null;
}
}
$urlbase = $CFG->wwwroot.'/draftfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('areauserdraft', 'repository'), true, true, true, true);
}
/**
* Get a file from user backup area
*
* @todo MDL-31091 maybe we need new caability for access control
* @param int $itemid item ID
* @param string $filepath file path
* @param string $filename file name
* @return file_info|null
*/
protected function get_area_user_backup($itemid, $filepath, $filename) {
global $USER, $CFG;
// access control: only my files, nobody else - TODO: maybe we need new capability here
if ($this->context->instanceid != $USER->id) {
return null;
}
if (is_null($itemid)) {
// go to parent, we do not use itemids here in profile area
return $this;
}
$fs = get_file_storage();
$filepath = is_null($filepath) ? '/' : $filepath;
$filename = is_null($filename) ? '.' : $filename;
if (!$storedfile = $fs->get_file($this->context->id, 'user', 'backup', $itemid, $filepath, $filename)) {
if ($filepath === '/' and $filename === '.') {
$storedfile = new virtual_root_file($this->context->id, 'user', 'backup', 0);
} else {
// not found
return null;
}
}
$urlbase = $CFG->wwwroot.'/pluginfile.php';
return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('areauserbackup', 'repository'), false, true, true, false);
}
/**
* Returns localised visible name.
*
* @return string
*/
public function get_visible_name() {
return fullname($this->user, true);
}
/**
* Whether or not new files or directories can be added
*
* @return bool
*/
public function is_writable() {
return false;
}
/**
* Whether or not this is a directory
*
* @return bool
*/
public function is_directory() {
return true;
}
/**
* Returns list of children.
*
* @return array of file_info instances
*/
public function get_children() {
$children = array();
if ($child = $this->get_area_user_private(0, '/', '.')) {
$children[] = $child;
}
/*
if ($child = $this->get_area_user_profile(0, '/', '.')) {
$children[] = $child;
}
*/
if ($child = $this->get_area_user_backup(0, '/', '.')) {
$children[] = $child;
}
// do not list draft area here - it is browsable only if you know the draft itemid ;-)
return $children;
}
/**
* Returns parent file_info instance
*
* @return file_info|null file_info instance or null for root
*/
public function get_parent() {
return $this->browser->get_file_info();
}
}
+698
View File
@@ -0,0 +1,698 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Utility class for browsing of stored files.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Represents an actual file or folder - a row in the file table in the tree navigated by {@link file_browser}.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_info_stored extends file_info {
/** @var stored_file|virtual_root_file stored_file or virtual_root_file instance */
protected $lf;
/** @var string the serving script */
protected $urlbase;
/** @var string the human readable name of this area */
protected $topvisiblename;
/** @var int|bool it's false if itemid is 0 and not included in URL */
protected $itemidused;
/** @var bool allow file reading */
protected $readaccess;
/** @var bool allow file write, delee */
protected $writeaccess;
/** @var string do not show links to parent context/area */
protected $areaonly;
/**
* Constructor
*
* @param file_browser $browser file browser instance
* @param stdClass $context context object
* @param stored_file|virtual_root_file $storedfile stored_file instance
* @param string $urlbase the serving script - usually the $CFG->wwwroot/.'pluginfile.php'
* @param string $topvisiblename the human readable name of this area
* @param int|bool $itemidused false if itemid always 0 and not included in URL
* @param bool $readaccess allow file reading
* @param bool $writeaccess allow file write, delete
* @param string $areaonly do not show links to parent context/area
*/
public function __construct(file_browser $browser, $context, $storedfile, $urlbase, $topvisiblename, $itemidused, $readaccess, $writeaccess, $areaonly) {
parent::__construct($browser, $context);
$this->lf = $storedfile;
$this->urlbase = $urlbase;
$this->topvisiblename = $topvisiblename;
$this->itemidused = $itemidused;
$this->readaccess = $readaccess;
$this->writeaccess = $writeaccess;
$this->areaonly = $areaonly;
}
/**
* Returns list of standard virtual file/directory identification.
* The difference from stored_file parameters is that null values
* are allowed in all fields
*
* @return array with keys contextid, component, filearea, itemid, filepath and filename
*/
public function get_params() {
return array('contextid'=>$this->context->id,
'component'=>$this->lf->get_component(),
'filearea' =>$this->lf->get_filearea(),
'itemid' =>$this->lf->get_itemid(),
'filepath' =>$this->lf->get_filepath(),
'filename' =>$this->lf->get_filename());
}
/**
* Returns localised visible name.
*
* @return string
*/
public function get_visible_name() {
$filename = $this->lf->get_filename();
$filepath = $this->lf->get_filepath();
if ($filename !== '.') {
return $filename;
} else {
$dir = trim($filepath, '/');
$dir = explode('/', $dir);
$dir = array_pop($dir);
if ($dir === '') {
return $this->topvisiblename;
} else {
return $dir;
}
}
}
/**
* Returns the localised human-readable name of the file together with virtual path
*
* @return string
*/
public function get_readable_fullname() {
global $CFG;
// retrieve the readable path with all parents (excluding the top most 'System')
$fpath = array();
for ($parent = $this; $parent && $parent->get_parent(); $parent = $parent->get_parent()) {
array_unshift($fpath, $parent->get_visible_name());
}
if ($this->lf->get_component() == 'user' && $this->lf->get_filearea() == 'private') {
// use the special syntax for user private files - 'USERNAME Private files: PATH'
$username = array_shift($fpath);
array_shift($fpath); // get rid of "Private Files/" in the beginning of the path
return get_string('privatefilesof', 'repository', $username). ': '. join('/', $fpath);
} else {
// for all other files (except user private files) return 'Server files: PATH'
// first, get and cache the name of the repository_local (will be used as prefix for file names):
static $replocalname = null;
if ($replocalname === null) {
require_once($CFG->dirroot . "/repository/lib.php");
$instances = repository::get_instances(array('type' => 'local'));
if (count($instances)) {
$firstinstance = reset($instances);
$replocalname = $firstinstance->get_name();
} else if (get_string_manager()->string_exists('pluginname', 'repository_local')) {
$replocalname = get_string('pluginname', 'repository_local');
} else {
$replocalname = get_string('arearoot', 'repository');
}
}
return $replocalname. ': '. join('/', $fpath);
}
}
/**
* Returns file download url
*
* @param bool $forcedownload Whether or not force download
* @param bool $https whether or not force https
* @return string url
*/
public function get_url($forcedownload=false, $https=false) {
if (!$this->is_readable()) {
return null;
}
if ($this->is_directory()) {
return null;
}
$this->urlbase;
$contextid = $this->lf->get_contextid();
$component = $this->lf->get_component();
$filearea = $this->lf->get_filearea();
$itemid = $this->lf->get_itemid();
$filepath = $this->lf->get_filepath();
$filename = $this->lf->get_filename();
if ($this->itemidused) {
$path = '/'.$contextid.'/'.$component.'/'.$filearea.'/'.$itemid.$filepath.$filename;
} else {
$path = '/'.$contextid.'/'.$component.'/'.$filearea.$filepath.$filename;
}
return file_encode_url($this->urlbase, $path, $forcedownload, $https);
}
/**
* Whether or not I can read content of this file or enter directory
*
* @return bool
*/
public function is_readable() {
return $this->readaccess;
}
/**
* Whether or not new files or directories can be added
*
* @return bool
*/
public function is_writable() {
return $this->writeaccess;
}
/**
* Whether or not this is an empty area
*
* @return bool
*/
public function is_empty_area() {
if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') {
// test the emptiness only in the top most level, it does not make sense at lower levels
$fs = get_file_storage();
return $fs->is_area_empty($this->lf->get_contextid(), $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid());
} else {
return false;
}
}
/**
* Returns file size in bytes, null for directories
*
* @return int bytes or null if not known
*/
public function get_filesize() {
return $this->lf->get_filesize();
}
/**
* Returns width, height and mimetype of the stored image, or false
*
* @see stored_file::get_imageinfo()
* @return array|false
*/
public function get_imageinfo() {
return $this->lf->get_imageinfo();
}
/**
* Returns mimetype
*
* @return string mimetype or null if not known
*/
public function get_mimetype() {
return $this->lf->get_mimetype();
}
/**
* Returns time created unix timestamp if known
*
* @return int timestamp or null
*/
public function get_timecreated() {
return $this->lf->get_timecreated();
}
/**
* Returns time modified unix timestamp if known
*
* @return int timestamp or null
*/
public function get_timemodified() {
return $this->lf->get_timemodified();
}
/**
* Whether or not this is a directory
*
* @return bool
*/
public function is_directory() {
return $this->lf->is_directory();
}
/**
* Returns the license type of the file
*
* @return string license short name or null
*/
public function get_license() {
return $this->lf->get_license();
}
/**
* Returns the author name of the file
*
* @return string author name or null
*/
public function get_author() {
return $this->lf->get_author();
}
/**
* Returns the source of the file
*
* @return string a source url or null
*/
public function get_source() {
return $this->lf->get_source();
}
/**
* Returns the sort order of the file
*
* @return int
*/
public function get_sortorder() {
return $this->lf->get_sortorder();
}
/**
* Whether or not this is a external resource
*
* @return bool
*/
public function is_external_file() {
return $this->lf->is_external_file();
}
/**
* Returns file status flag.
*
* @return int 0 means file OK, anything else is a problem and file can not be used
*/
public function get_status() {
return $this->lf->get_status();
}
/**
* Returns list of children.
*
* @return array of file_info instances
*/
public function get_children() {
if (!$this->lf->is_directory()) {
return array();
}
$result = array();
$fs = get_file_storage();
$storedfiles = $fs->get_directory_files($this->context->id, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(),
$this->lf->get_filepath(), false, true, "filepath, filename");
foreach ($storedfiles as $file) {
$result[] = new file_info_stored($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
$this->itemidused, $this->readaccess, $this->writeaccess, false);
}
return $result;
}
/**
* Returns list of children which are either files matching the specified extensions
* or folders that contain at least one such file.
*
* @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
* @return array of file_info instances
*/
public function get_non_empty_children($extensions = '*') {
$result = array();
if (!$this->lf->is_directory()) {
return $result;
}
$fs = get_file_storage();
$storedfiles = $fs->get_directory_files($this->context->id, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(),
$this->lf->get_filepath(), false, true, "filepath, filename");
foreach ($storedfiles as $file) {
$extension = core_text::strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
if ($file->is_directory() || $extensions === '*' || (!empty($extension) && in_array('.'.$extension, $extensions))) {
$fileinfo = new file_info_stored($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
$this->itemidused, $this->readaccess, $this->writeaccess, false);
if (!$file->is_directory() || $fileinfo->count_non_empty_children($extensions)) {
$result[] = $fileinfo;
}
}
}
return $result;
}
/**
* Returns the number of children which are either files matching the specified extensions
* or folders containing at least one such file.
*
* @param string|array $extensions, for example '*' or array('.gif','.jpg')
* @param int $limit stop counting after at least $limit non-empty children are found
* @return int
*/
public function count_non_empty_children($extensions = '*', $limit = 1) {
global $DB;
if (!$this->lf->is_directory()) {
return 0;
}
$filepath = $this->lf->get_filepath();
$length = core_text::strlen($filepath);
$sql = "SELECT filepath, filename
FROM {files} f
WHERE f.contextid = :contextid AND f.component = :component AND f.filearea = :filearea AND f.itemid = :itemid
AND ".$DB->sql_substr("f.filepath", 1, $length)." = :filepath
AND filename <> '.' ";
$params = array('contextid' => $this->context->id,
'component' => $this->lf->get_component(),
'filearea' => $this->lf->get_filearea(),
'itemid' => $this->lf->get_itemid(),
'filepath' => $filepath);
list($sql2, $params2) = $this->build_search_files_sql($extensions);
$rs = $DB->get_recordset_sql($sql.' '.$sql2, array_merge($params, $params2));
$children = array();
foreach ($rs as $record) {
// we don't need to check access to individual files here, since the user can access parent
if ($record->filepath === $filepath) {
$children[] = $record->filename;
} else {
$path = explode('/', core_text::substr($record->filepath, $length));
if (!in_array($path[0], $children)) {
$children[] = $path[0];
}
}
if (count($children) >= $limit) {
break;
}
}
$rs->close();
return count($children);
}
/**
* Returns parent file_info instance
*
* @return file_info|null file_info instance or null for root
*/
public function get_parent() {
if ($this->lf->get_filepath() === '/' and $this->lf->is_directory()) {
if ($this->areaonly) {
return null;
} else if ($this->itemidused) {
return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea());
} else {
return $this->browser->get_file_info($this->context);
}
}
if (!$this->lf->is_directory()) {
return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $this->lf->get_filepath(), '.');
}
$filepath = $this->lf->get_filepath();
$filepath = trim($filepath, '/');
$dirs = explode('/', $filepath);
array_pop($dirs);
$filepath = implode('/', $dirs);
$filepath = ($filepath === '') ? '/' : "/$filepath/";
return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, '.');
}
/**
* Create new directory, may throw exception - make sure
* params are valid.
*
* @param string $newdirname name of new directory
* @param int $userid id of author, default $USER->id
* @return file_info|null new directory's file_info instance or null if failed
*/
public function create_directory($newdirname, $userid = NULL) {
if (!$this->is_writable() or !$this->lf->is_directory()) {
return null;
}
$newdirname = clean_param($newdirname, PARAM_FILE);
if ($newdirname === '') {
return null;
}
$filepath = $this->lf->get_filepath().'/'.$newdirname.'/';
$fs = get_file_storage();
if ($file = $fs->create_directory($this->lf->get_contextid(), $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, $userid)) {
return $this->browser->get_file_info($this->context, $this->lf->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
}
return null;
}
/**
* Create new file from string - make sure
* params are valid.
*
* @param string $newfilename name of new file
* @param string $content of file
* @param int $userid id of author, default $USER->id
* @return file_info|null new file's file_info instance or null if failed
*/
public function create_file_from_string($newfilename, $content, $userid = NULL) {
if (!$this->is_writable() or !$this->lf->is_directory()) {
return null;
}
$newfilename = clean_param($newfilename, PARAM_FILE);
if ($newfilename === '') {
return null;
}
$fs = get_file_storage();
$now = time();
$newrecord = new stdClass();
$newrecord->contextid = $this->lf->get_contextid();
$newrecord->component = $this->lf->get_component();
$newrecord->filearea = $this->lf->get_filearea();
$newrecord->itemid = $this->lf->get_itemid();
$newrecord->filepath = $this->lf->get_filepath();
$newrecord->filename = $newfilename;
if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
// file already exists, sorry
return null;
}
$newrecord->timecreated = $now;
$newrecord->timemodified = $now;
$newrecord->mimetype = mimeinfo('type', $newfilename);
$newrecord->userid = $userid;
if ($file = $fs->create_file_from_string($newrecord, $content)) {
return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
}
return null;
}
/**
* Create new file from pathname - make sure
* params are valid.
*
* @param string $newfilename name of new file
* @param string $pathname location of file
* @param int $userid id of author, default $USER->id
* @return file_info|null new file's file_info instance or null if failed
*/
public function create_file_from_pathname($newfilename, $pathname, $userid = NULL) {
if (!$this->is_writable() or !$this->lf->is_directory()) {
return null;
}
$newfilename = clean_param($newfilename, PARAM_FILE);
if ($newfilename === '') {
return null;
}
$fs = get_file_storage();
$now = time();
$newrecord = new stdClass();
$newrecord->contextid = $this->lf->get_contextid();
$newrecord->component = $this->lf->get_component();
$newrecord->filearea = $this->lf->get_filearea();
$newrecord->itemid = $this->lf->get_itemid();
$newrecord->filepath = $this->lf->get_filepath();
$newrecord->filename = $newfilename;
if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
// file already exists, sorry
return null;
}
$newrecord->timecreated = $now;
$newrecord->timemodified = $now;
$newrecord->mimetype = mimeinfo('type', $newfilename);
$newrecord->userid = $userid;
if ($file = $fs->create_file_from_pathname($newrecord, $pathname)) {
return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
}
return null;
}
/**
* Create new file from stored file - make sure
* params are valid.
*
* @param string $newfilename name of new file
* @param int|stored_file $fid file id or stored_file of file
* @param int $userid id of author, default $USER->id
* @return file_info|null new file's file_info instance or null if failed
*/
public function create_file_from_storedfile($newfilename, $fid, $userid = NULL) {
if (!$this->is_writable() or $this->lf->get_filename() !== '.') {
return null;
}
$newfilename = clean_param($newfilename, PARAM_FILE);
if ($newfilename === '') {
return null;
}
$fs = get_file_storage();
$now = time();
$newrecord = new stdClass();
$newrecord->contextid = $this->lf->get_contextid();
$newrecord->component = $this->lf->get_component();
$newrecord->filearea = $this->lf->get_filearea();
$newrecord->itemid = $this->lf->get_itemid();
$newrecord->filepath = $this->lf->get_filepath();
$newrecord->filename = $newfilename;
if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
// file already exists, sorry
return null;
}
$newrecord->timecreated = $now;
$newrecord->timemodified = $now;
$newrecord->mimetype = mimeinfo('type', $newfilename);
$newrecord->userid = $userid;
if ($file = $fs->create_file_from_storedfile($newrecord, $fid)) {
return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
}
return null;
}
/**
* Delete file, make sure file is deletable first.
*
* @return bool success
*/
public function delete() {
if (!$this->is_writable()) {
return false;
}
if ($this->is_directory()) {
$filepath = $this->lf->get_filepath();
$fs = get_file_storage();
$storedfiles = $fs->get_area_files($this->context->id, $this->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid());
foreach ($storedfiles as $file) {
if (strpos($file->get_filepath(), $filepath) === 0) {
$file->delete();
}
}
}
return $this->lf->delete();
}
/**
* Copy content of this file to local storage, overriding current file if needed.
*
* @param array|stdClass $filerecord contains contextid, component, filearea,
* itemid, filepath, filename and optionally other attributes of the new file
* @return bool success
*/
public function copy_to_storage($filerecord) {
if (!$this->is_readable() or $this->is_directory()) {
return false;
}
$filerecord = (array)$filerecord;
$fs = get_file_storage();
if ($existing = $fs->get_file($filerecord['contextid'], $filerecord['component'], $filerecord['filearea'], $filerecord['itemid'], $filerecord['filepath'], $filerecord['filename'])) {
$existing->delete();
}
$fs->create_file_from_storedfile($filerecord, $this->lf);
return true;
}
/**
* Copy content of this file to local storage, overriding current file if needed.
*
* @param string $pathname real local full file name
* @return bool success
*/
public function copy_to_pathname($pathname) {
if (!$this->is_readable() or $this->is_directory()) {
return false;
}
if (file_exists($pathname)) {
if (!unlink($pathname)) {
return false;
}
}
$this->lf->copy_content_to($pathname);
return true;
}
}
+266
View File
@@ -0,0 +1,266 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
use file_info_context_course;
use file_info_context_coursecat;
use file_info_context_module;
use file_info_stored;
use stdClass;
/**
* Unit tests for file browser
*
* @package core
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class file_browser_test extends \advanced_testcase {
/** @var int */
protected $initialnonempty;
/** @var int */
protected $initialcategories;
/** @var int */
protected $initialcourses;
/** @var int */
protected $initialjpg;
/** @var stdClass */
protected $course1;
/** @var stdClass */
protected $course2;
/** @var stdClass */
protected $module1;
/** @var stdClass */
protected $module2;
/** @var array */
protected $course1filerecord;
/** @var stdClass */
protected $teacher;
/** @var stdClass */
protected $teacherrole;
/**
* Set up
*/
public function setUp(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$browser = get_file_browser();
$fileinfo = $browser->get_file_info(\context_system::instance());
$this->initialnonempty = $fileinfo->count_non_empty_children();
$this->initialcategories = count(array_filter($fileinfo->get_children(), function($a) {
return $a instanceof file_info_context_coursecat;
}));
$this->initialcourses = count(array_filter($fileinfo->get_children(), function($a) {
return $a instanceof file_info_context_course;
}));
$this->initialcourses -= 1; // This includes the site course by default.
$this->initialjpg = $fileinfo->count_non_empty_children(['.jpg']);
$this->getDataGenerator()->create_category(); // Empty category.
$this->course1 = $this->getDataGenerator()->create_course(); // Empty course.
$this->course2 = $this->getDataGenerator()->create_course();
// Add a file to course1 summary.
$coursecontext1 = \context_course::instance($this->course1->id);
$this->course1filerecord = array('contextid' => $coursecontext1->id,
'component' => 'course',
'filearea' => 'summary',
'itemid' => '0',
'filepath' => '/',
'filename' => 'summaryfile.jpg');
$fs = get_file_storage();
$fs->create_file_from_string($this->course1filerecord, 'IMG');
$this->module1 = $this->getDataGenerator()->create_module('resource', ['course' => $this->course2->id]); // Contains 1 file.
$this->module2 = $this->getDataGenerator()->create_module('assign', ['course' => $this->course2->id]); // Contains no files.
$this->teacher = $this->getDataGenerator()->create_user();
$this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
// Make sure we're testing what should be the default capabilities.
assign_capability('moodle/restore:viewautomatedfilearea', CAP_ALLOW, $this->teacherrole->id, $coursecontext1);
$this->getDataGenerator()->enrol_user($this->teacher->id, $this->course1->id, $this->teacherrole->id);
$this->getDataGenerator()->enrol_user($this->teacher->id, $this->course2->id, $this->teacherrole->id);
$this->setUser($this->teacher);
}
/**
* Test "Server files" from the system context
*/
public function test_file_info_context_system(): void {
// There is one non-empty category child and two category children.
$browser = get_file_browser();
$fileinfo = $browser->get_file_info(\context_system::instance());
$this->assertNotEmpty($fileinfo->count_non_empty_children());
$this->assertEquals($this->initialnonempty + 1, count($fileinfo->get_non_empty_children()));
$categorychildren = array_filter($fileinfo->get_children(), function($a) {
return $a instanceof file_info_context_coursecat;
});
$this->assertEquals($this->initialcategories + 1, count($categorychildren));
}
/**
* Test "Server files" from the system context, hide Misc category
*/
public function test_file_info_context_system_hidden(): void {
// Hide the course category that contains our two courses. Teacher does not have cap to view hidden categories.
\core_course_category::get($this->course1->category)->update(['visible' => 0]);
// We should have two non-empty children in system context (courses).
$browser = get_file_browser();
$fileinfo = $browser->get_file_info(\context_system::instance());
$this->assertNotEmpty($fileinfo->count_non_empty_children());
$this->assertEquals($this->initialnonempty + 2, count($fileinfo->get_non_empty_children()));
// Should be 1 category children (empty category).
$categorychildren = array_filter($fileinfo->get_children(), function($a) {
return $a instanceof file_info_context_coursecat;
});
$this->assertEquals($this->initialcategories, count($categorychildren));
// Should be 2 course children - courses that belonged to hidden subcategory are now direct children of "System".
$coursechildren = array_filter($fileinfo->get_children(), function($a) {
return $a instanceof file_info_context_course;
});
$this->assertEquals($this->initialcourses + 2, count($coursechildren));
}
/**
* Test "Server files" from the course category context
*/
public function test_file_info_context_coursecat(): void {
// There are two non-empty courses.
$browser = get_file_browser();
$fileinfo = $browser->get_file_info(\context_coursecat::instance($this->course2->category));
$this->assertNotEmpty($fileinfo->count_non_empty_children());
$this->assertEquals(2, count($fileinfo->get_non_empty_children()));
$coursechildren = array_filter($fileinfo->get_children(), function($a) {
return $a instanceof file_info_context_course;
});
$this->assertEquals($this->initialcourses + 2, count($coursechildren));
}
/**
* Test "Server files" from the course category context, only look for .jpg
*/
public function test_file_info_context_coursecat_jpg(): void {
// There is one non-empty category child and two category children.
$browser = get_file_browser();
$fileinfo = $browser->get_file_info(\context_system::instance());
$this->assertNotEmpty($fileinfo->count_non_empty_children(['.jpg']));
$this->assertEquals($this->initialjpg + 1, count($fileinfo->get_non_empty_children(['.jpg'])));
}
/**
* Test "Server files" from the course context (course1)
*/
public function test_file_info_context_course_1(): void {
$browser = get_file_browser();
$fileinfo = $browser->get_file_info(\context_course::instance($this->course1->id));
// Fileinfo element has only one non-empty child - "Course summary" file area.
$this->assertNotEmpty($fileinfo->count_non_empty_children());
$nonemptychildren = $fileinfo->get_non_empty_children();
$this->assertEquals(1, count($nonemptychildren));
$child = reset($nonemptychildren);
$this->assertTrue($child instanceof file_info_stored);
$this->assertEquals(['filename' => '.'] + $this->course1filerecord, $child->get_params());
// Filearea "Course summary" has a child that is the actual image file.
$this->assertEquals($this->course1filerecord, $child->get_children()[0]->get_params());
// There are seven course-level file areas available to teachers with default caps and no modules in this course.
$allchildren = $fileinfo->get_children();
$this->assertEquals(7, count($allchildren));
$modulechildren = array_filter($allchildren, function($a) {
return $a instanceof file_info_context_module;
});
$this->assertEquals(0, count($modulechildren));
// Admin can see seven course-level file areas.
$this->setAdminUser();
$fileinfo = $browser->get_file_info(\context_course::instance($this->course1->id));
$this->assertEquals(7, count($fileinfo->get_children()));
}
/**
* Test "Server files" from the course context (course1)
*/
public function test_file_info_context_course_2(): void {
// 2. Start from the course level.
$browser = get_file_browser();
$fileinfo = $browser->get_file_info(\context_course::instance($this->course2->id));
$this->assertNotEmpty($fileinfo->count_non_empty_children());
$nonemptychildren = $fileinfo->get_non_empty_children();
$this->assertEquals(1, count($nonemptychildren));
$child = reset($nonemptychildren);
$this->assertTrue($child instanceof file_info_context_module);
$this->assertEquals($this->module1->name.' (File)', $child->get_visible_name());
$this->assertEquals(1, count($child->get_non_empty_children()));
$this->assertEquals(1, $child->count_non_empty_children());
$modulechildren = array_filter($fileinfo->get_children(), function($a) {
return $a instanceof file_info_context_module;
});
$this->assertEquals(2, count($modulechildren));
}
/**
* Test "Server files" from the course context (module1)
*/
public function test_file_info_context_module_1(): void {
$module1context = \context_module::instance($this->module1->cmid);
$browser = get_file_browser();
$fileinfo = $browser->get_file_info($module1context);
$this->assertEquals($this->module1->name . ' (File)', $fileinfo->get_visible_name());
$this->assertNotEmpty($fileinfo->count_non_empty_children());
$nonemptychildren = $fileinfo->get_non_empty_children();
$this->assertEquals(1, count($nonemptychildren));
$child = reset($nonemptychildren);
$this->assertTrue($child instanceof file_info_stored);
}
/**
* Test "Server files" from the course context (module1)
*/
public function test_file_info_context_module_2(): void {
$module2context = \context_module::instance($this->module2->cmid);
$browser = get_file_browser();
$fileinfo = $browser->get_file_info($module2context);
$this->assertEquals($this->module2->name.' (Assignment)', $fileinfo->get_visible_name());
$this->assertEmpty($fileinfo->count_non_empty_children());
$nonemptychildren = $fileinfo->get_non_empty_children();
$this->assertEquals(0, count($nonemptychildren));
}
}
+352
View File
@@ -0,0 +1,352 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class simulating empty directories.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Represents the root directory of an empty file area in the tree navigated by {@link file_browser}.
*
* @package core_files
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class virtual_root_file {
/** @var int context id */
protected $contextid;
/** @var string file component */
protected $component;
/** @var string file area */
protected $filearea;
/** @var int file itemid */
protected $itemid;
/**
* Constructor
*
* @param int $contextid context ID
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
*/
public function __construct($contextid, $component, $filearea, $itemid) {
$this->contextid = $contextid;
$this->component = $component;
$this->filearea = $filearea;
$this->itemid = $itemid;
}
/**
* Whether or not this is a directory
*
* @return bool
*/
public function is_directory() {
return true;
}
/**
* Delete file
*
* @return success
*/
public function delete() {
return true;
}
/**
* adds this file path to a curl request (POST only)
*
* @param curl $curlrequest the curl request object
* @param string $key what key to use in the POST request
*/
public function add_to_curl_request(&$curlrequest, $key) {
return;
}
/**
* Returns file handle - read only mode, no writing allowed into pool files!
*
* @return resource file handle
*/
public function get_content_file_handle() {
return null;
}
/**
* Dumps file content to page
*
* @return resource file handle
*/
public function readfile() {
return;
}
/**
* Returns file content as string
*
* @return string content
*/
public function get_content() {
return '';
}
/**
* Copy content of file to given pathname
*
* @param string $pathname real path to new file
* @return bool success
*/
public function copy_content_to($pathname) {
return false;
}
/**
* List contents of archive
*
* @param file_packer $packer file packer instance
* @return array of file infos
*/
public function list_files(file_packer $packer) {
return null;
}
/**
* Extract file to given file path (real OS filesystem), existing files are overwrited
*
* @param file_packer $packer file packer instance
* @param string $pathname target directory
* @return mixed list of processed files; false if error
*/
public function extract_to_pathname(file_packer $packer, $pathname) {
return false;
}
/**
* Extract file to given file path (real OS filesystem), existing files are overwrited
*
* @param file_packer $packer file packer instance
* @param int $contextid context ID
* @param string $component component
* @param string $filearea file area
* @param int $itemid item ID
* @param string $pathbase path base
* @param int $userid user ID
* @return mixed list of processed files; false if error
*/
public function extract_to_storage(file_packer $packer, $contextid, $component, $filearea, $itemid, $pathbase, $userid = NULL) {
return false;
}
/**
* Add file/directory into archive
*
* @param file_archive $filearch file archive instance
* @param string $archivepath pathname in archive
* @return bool success
*/
public function archive_file(file_archive $filearch, $archivepath) {
return false;
}
/**
* Returns parent directory
*
* @return stored_file
*/
public function get_parent_directory() {
return null;
}
/**
* Returns context ID
*
* @return int context ID
*/
public function get_contextid() {
return $this->contextid;
}
/**
* Returns file component
*
* @return string component
*/
public function get_component() {
return $this->component;
}
/**
* Returns file area
*
* @return string filearea
*/
public function get_filearea() {
return $this->filearea;
}
/**
* Returns file itemid
*
* @return int itemid
*/
public function get_itemid() {
return $this->itemid;
}
/**
* Returns file path
*
* @return string filepath
*/
public function get_filepath() {
return '/';
}
/**
* Returns file name
*
* @return string filename
*/
public function get_filename() {
return '.';
}
/**
* Returns user ID
*
* @return int userid
*/
public function get_userid() {
return null;
}
/**
* Returns file size
*
* @return int filesize
*/
public function get_filesize() {
return 0;
}
/**
* Returns mimetype
*
* @return string mimetype
*/
public function get_mimetype() {
return null;
}
/**
* Returns time created
*
* @return int
*/
public function get_timecreated() {
return 0;
}
/**
* Returns time modified
*
* @return int
*/
public function get_timemodified() {
return 0;
}
/**
* Returns status
*
* @return int
*/
public function get_status() {
return 0;
}
/**
* Returns ID
*
* @return int
*/
public function get_id() {
return 0;
}
/**
* Returns sha1 hash code
*
* @return string
*/
public function get_contenthash() {
return sha1('');
}
/**
* Returns path name hash
*
* @return string
*/
public function get_pathnamehash() {
return sha1('/'.$this->get_contextid().'/'.$this->get_component().'/'.$this->get_filearea().'/'.$this->get_itemid().$this->get_filepath().$this->get_filename());
}
/**
* Returns license
*
* @return string
*/
public function get_license() {
return null;
}
/**
* Returns file's author
*
* @return string
*/
public function get_author() {
return null;
}
/**
* Returns file source
*
* @return string
*/
public function get_source() {
return null;
}
/**
* Returns file sort order
*
* @return int
*/
public function get_sortorder() {
return null;
}
}