mirror of
https://github.com/moodle/moodle.git
synced 2025-04-16 22:15:24 +02:00
MDL-27236: Server files, improve tree view
- Removed 'Private files' from 'Server files' repository; - Show only non-empty directories (taking into account filetype filter); - If there is only one non-empty filearea within a module, do not show it and skip the extra subdirectory level; - If the user is not admin (does not have 'moodle/course:update' capability in system context), do not show course categories, just list available courses; - Also when retrieving the course files capability to managefiles is checked before retrieving the modules list for performance tuning on sites with a lot of courses
This commit is contained in:
parent
a2b30aa852
commit
e2652f1a31
@ -371,6 +371,12 @@ class file_info_context_course extends file_info {
|
||||
$children[] = $child;
|
||||
}
|
||||
|
||||
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
|
||||
return $children;
|
||||
}
|
||||
|
||||
// now list all modules
|
||||
$modinfo = get_fast_modinfo($this->course);
|
||||
foreach ($modinfo->cms as $cminfo) {
|
||||
|
@ -99,10 +99,6 @@ class file_info_context_system extends file_info {
|
||||
|
||||
$children = array();
|
||||
|
||||
if ($child = $this->browser->get_file_info(get_context_instance(CONTEXT_USER, $USER->id))) {
|
||||
$children[] = $child;
|
||||
}
|
||||
|
||||
$course_cats = $DB->get_records('course_categories', array('parent'=>0), 'sortorder', 'id,visible');
|
||||
foreach ($course_cats as $category) {
|
||||
$context = get_context_instance(CONTEXT_COURSECAT, $category->id);
|
||||
|
@ -18,15 +18,13 @@
|
||||
/**
|
||||
* repository_local class is used to browse moodle files
|
||||
*
|
||||
* @since 2.0
|
||||
* @package repository
|
||||
* @subpackage local
|
||||
* @since 2.0
|
||||
* @package repository_local
|
||||
* @copyright 2009 Dongsheng Cai <dongsheng@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
class repository_local extends repository {
|
||||
|
||||
/**
|
||||
* local plugin doesn't require login, so list all files
|
||||
* @return mixed
|
||||
@ -67,8 +65,11 @@ class repository_local extends repository {
|
||||
$component = null;
|
||||
if (!empty($this->context)) {
|
||||
list($context, $course, $cm) = get_context_info_array($this->context->id);
|
||||
$courseid = is_object($course) ? $course->id : SITEID;
|
||||
$context = get_context_instance(CONTEXT_COURSE, $courseid);
|
||||
if (is_object($course)) {
|
||||
$context = get_context_instance(CONTEXT_COURSE, $course->id);
|
||||
} else {
|
||||
$context = get_system_context();
|
||||
}
|
||||
} else {
|
||||
$context = get_system_context();
|
||||
}
|
||||
@ -76,73 +77,25 @@ class repository_local extends repository {
|
||||
|
||||
$browser = get_file_browser();
|
||||
|
||||
$list = array();
|
||||
if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) {
|
||||
// build path navigation
|
||||
$pathnodes = array();
|
||||
$encodedpath = base64_encode(serialize($fileinfo->get_params()));
|
||||
$pathnodes[] = array('name'=>$fileinfo->get_visible_name(), 'path'=>$encodedpath);
|
||||
$level = $fileinfo->get_parent();
|
||||
while ($level) {
|
||||
$encodedpath = base64_encode(serialize($level->get_params()));
|
||||
$pathnodes[] = array('name'=>$level->get_visible_name(), 'path'=>$encodedpath);
|
||||
$level = $level->get_parent();
|
||||
}
|
||||
if (!empty($pathnodes) && is_array($pathnodes)) {
|
||||
$pathnodes = array_reverse($pathnodes);
|
||||
$ret['path'] = $pathnodes;
|
||||
}
|
||||
// build file tree
|
||||
$children = $fileinfo->get_children();
|
||||
foreach ($children as $child) {
|
||||
if ($child->is_directory()) {
|
||||
if ($child->is_empty_area()) {
|
||||
continue;
|
||||
}
|
||||
$params = $child->get_params();
|
||||
$encodedpath = base64_encode(serialize($params));
|
||||
// hide user_private area from local plugin, user should
|
||||
// use private file plugin to access private files
|
||||
//if ($params['filearea'] == 'user_private') {
|
||||
//continue;
|
||||
//}
|
||||
$node = array(
|
||||
'title' => $child->get_visible_name(),
|
||||
'size' => 0,
|
||||
'date' => '',
|
||||
'path' => $encodedpath,
|
||||
'children'=>array(),
|
||||
'thumbnail' => $OUTPUT->pix_url('f/folder-32')->out(false)
|
||||
);
|
||||
$list[] = $node;
|
||||
} else {
|
||||
$encodedpath = base64_encode(serialize($child->get_params()));
|
||||
$node = array(
|
||||
'title' => $child->get_visible_name(),
|
||||
'size' => 0,
|
||||
'date' => '',
|
||||
'source'=> $encodedpath,
|
||||
'thumbnail' => $OUTPUT->pix_url(file_extension_icon($child->get_visible_name(), 32))->out(false)
|
||||
);
|
||||
$list[] = $node;
|
||||
}
|
||||
$element = repository_local_file::retrieve_file_info($fileinfo, $this);
|
||||
$nonemptychildren = $element->get_non_empty_children();
|
||||
foreach ($nonemptychildren as $child) {
|
||||
$list[] = (array)$child->get_node();
|
||||
}
|
||||
} else {
|
||||
// if file doesn't exist, build path nodes root of current context
|
||||
$pathnodes = array();
|
||||
$fileinfo = $browser->get_file_info($context, null, null, null, null, null);
|
||||
$encodedpath = base64_encode(serialize($fileinfo->get_params()));
|
||||
$pathnodes[] = array('name'=>$fileinfo->get_visible_name(), 'path'=>$encodedpath);
|
||||
$level = $fileinfo->get_parent();
|
||||
while ($level) {
|
||||
$encodedpath = base64_encode(serialize($level->get_params()));
|
||||
$pathnodes[] = array('name'=>$level->get_visible_name(), 'path'=>$encodedpath);
|
||||
$level = $level->get_parent();
|
||||
}
|
||||
// build path navigation
|
||||
$ret['path'] = array();
|
||||
$element = repository_local_file::retrieve_file_info($fileinfo, $this);
|
||||
for ($level = $element; $level; $level = $level->get_parent()) {
|
||||
if ($level == $element || !$level->can_skip()) {
|
||||
array_unshift($ret['path'], $level->get_node_path());
|
||||
}
|
||||
if (!empty($pathnodes) && is_array($pathnodes)) {
|
||||
$pathnodes = array_reverse($pathnodes);
|
||||
$ret['path'] = $pathnodes;
|
||||
}
|
||||
$list = array();
|
||||
}
|
||||
$ret['list'] = array_filter($list, array($this, 'filter'));
|
||||
return $ret;
|
||||
@ -166,3 +119,250 @@ class repository_local extends repository {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to cache some information about file
|
||||
*
|
||||
* This class is a wrapper to instances of file_info. It caches such information as
|
||||
* parent and list of children. It also stores an array of already retrieved elements.
|
||||
*
|
||||
* It also implements more comprehensive algorithm for checking if folder is empty
|
||||
* (taking into account the filtering of the files). To decrease number of levels
|
||||
* we check if some subfolders can be skipped from the tree.
|
||||
*
|
||||
* As a result we display in Server files repository only non-empty folders and skip
|
||||
* filearea folders if this is the only filearea in the module.
|
||||
* For non-admin the course categories are not shown as well (courses are shown as a list)
|
||||
*
|
||||
* @package repository_local
|
||||
* @copyright 2012 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class repository_local_file {
|
||||
/** @var array stores already retrieved files */
|
||||
private static $cachedfiles = array();
|
||||
/** @var file_info Stores the original file */
|
||||
public $fileinfo;
|
||||
/** @var bool whether this file is directory */
|
||||
private $isdir;
|
||||
/** @var array caches retrieved children */
|
||||
private $children = null;
|
||||
/** @var array caches retrieved information whether this file is an empty directory */
|
||||
protected $isempty = null;
|
||||
/** @var repository link to the container repository (for filtering the results) */
|
||||
private $repository;
|
||||
/** @var repository_local_file link to parent directory */
|
||||
protected $parent;
|
||||
/** @var bool caches calculated information on whether this directory must be skipped in the tree */
|
||||
private $skip = null;
|
||||
|
||||
/**
|
||||
* Creates (or retrieves from cache) the repository_local_file object for $file_info
|
||||
*
|
||||
* @param file_info $fileinfo
|
||||
* @param repository $repository
|
||||
* @param repository_local_file $parent
|
||||
* @return repository_local_file
|
||||
*/
|
||||
public static function retrieve_file_info(file_info $fileinfo, repository $repository, repository_local_file $parent = null) {
|
||||
$encodedpath = base64_encode(serialize($fileinfo->get_params()));
|
||||
if (!isset(self::$cachedfiles[$encodedpath])) {
|
||||
self::$cachedfiles[$encodedpath] = new repository_local_file($fileinfo, $repository, $parent);
|
||||
}
|
||||
return self::$cachedfiles[$encodedpath];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object
|
||||
*
|
||||
* @param file_info $fileinfo
|
||||
* @param repository $repository
|
||||
* @param repository_local_file $parent
|
||||
*/
|
||||
private function __construct(file_info $fileinfo, repository $repository, repository_local_file $parent = null) {
|
||||
$this->repository = $repository;
|
||||
$this->fileinfo = $fileinfo;
|
||||
$this->isdir = $fileinfo->is_directory();
|
||||
if (!$this->isdir) {
|
||||
$node = array('title' => $this->fileinfo->get_visible_name());
|
||||
$this->isempty = !$repository->filter($node);
|
||||
$this->skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns node for $ret['list']
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_node() {
|
||||
global $OUTPUT;
|
||||
$encodedpath = base64_encode(serialize($this->fileinfo->get_params()));
|
||||
$node = array(
|
||||
'title' => $this->fileinfo->get_visible_name(),
|
||||
'size' => 0,
|
||||
'date' => '');
|
||||
if ($this->isdir) {
|
||||
$node['path'] = $encodedpath;
|
||||
$node['thumbnail'] = $OUTPUT->pix_url('f/folder-32')->out(false);
|
||||
$node['children'] = array();
|
||||
} else {
|
||||
$node['source'] = $encodedpath;
|
||||
$node['thumbnail'] = $OUTPUT->pix_url(file_extension_icon($node['title'], 32))->out(false);
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns node for $ret['path']
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_node_path() {
|
||||
$encodedpath = base64_encode(serialize($this->fileinfo->get_params()));
|
||||
return array(
|
||||
'path' => $encodedpath,
|
||||
'name' => $this->fileinfo->get_visible_name()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this is a directory
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dir() {
|
||||
return $this->isdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns children of this element
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_children() {
|
||||
if (!$this->isdir) {
|
||||
return array();
|
||||
}
|
||||
if ($this->children === null) {
|
||||
$this->children = array();
|
||||
$children = $this->fileinfo->get_children();
|
||||
for ($i=0; $i<count($children); $i++) {
|
||||
$this->children[] = self::retrieve_file_info($children[$i], $this->repository, $this);
|
||||
}
|
||||
}
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this folder is empty (contains no non-empty children)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_empty() {
|
||||
if ($this->isempty === null) {
|
||||
$this->isempty = true;
|
||||
if (!$this->fileinfo->is_empty_area()) {
|
||||
// even if is_empty_area() returns false, element still may be empty
|
||||
$children = $this->get_children();
|
||||
if (!empty($children)) {
|
||||
// 1. Let's look at already retrieved children
|
||||
foreach ($children as $childnode) {
|
||||
if ($childnode->isempty === false) {
|
||||
// we already calculated isempty for a child, and it is not empty
|
||||
$this->isempty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($this->isempty) {
|
||||
// 2. now we know that this directory contains children that are either empty or we don't know
|
||||
foreach ($children as $childnode) {
|
||||
if (!$childnode->is_empty()) {
|
||||
$this->isempty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->isempty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent element
|
||||
*
|
||||
* @return repository_local_file
|
||||
*/
|
||||
public function get_parent() {
|
||||
if ($this->parent === null) {
|
||||
if ($parent = $this->fileinfo->get_parent()) {
|
||||
$this->parent = self::retrieve_file_info($parent, $this->repository);
|
||||
} else {
|
||||
$this->parent = false;
|
||||
}
|
||||
}
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wether this folder may be skipped in tree view
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_skip() {
|
||||
global $CFG;
|
||||
if ($this->skip === null) {
|
||||
$this->skip = false;
|
||||
if ($this->fileinfo instanceof file_info_stored) {
|
||||
$params = $this->fileinfo->get_params();
|
||||
if (strlen($params['filearea']) && $params['filepath'] == '/' && $params['filename'] == '.') {
|
||||
// This is a filearea inside an activity, it can be skipped if it has no non-empty siblings
|
||||
if ($parent = $this->get_parent()) {
|
||||
$siblings = $parent->get_children();
|
||||
$countnonempty = 0;
|
||||
foreach ($siblings as $sibling) {
|
||||
if (!$sibling->is_empty()) {
|
||||
$countnonempty++;
|
||||
if ($countnonempty > 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($countnonempty <= 1) {
|
||||
$this->skip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ($this->fileinfo instanceof file_info_context_coursecat) {
|
||||
// This is a course category. For non-admins we do not display categories
|
||||
$this->skip = empty($CFG->navshowmycoursecategories) &&
|
||||
!has_capability('moodle/course:update', get_context_instance(CONTEXT_SYSTEM));
|
||||
}
|
||||
}
|
||||
return $this->skip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of children who have any elmenets
|
||||
*
|
||||
* If a subfolder can be skipped - list children of subfolder instead
|
||||
* (recursive function)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_non_empty_children() {
|
||||
$children = $this->get_children();
|
||||
$nonemptychildren = array();
|
||||
foreach ($children as $child) {
|
||||
if (!$child->is_empty()) {
|
||||
if ($child->can_skip()) {
|
||||
$nonemptychildren = array_merge($nonemptychildren, $child->get_non_empty_children());
|
||||
} else {
|
||||
$nonemptychildren[] = $child;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $nonemptychildren;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user