369 lines
13 KiB
PHP
Raw Normal View History

<?php
2009-12-04 09:19:54 +00:00
// 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/>.
/**
2009-12-04 09:19:54 +00:00
* repository_local class is used to browse moodle files
*
* @since 2.0
* @package repository_local
2010-09-06 11:29:21 +00:00
* @copyright 2009 Dongsheng Cai <dongsheng@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
2009-12-04 09:19:54 +00:00
class repository_local extends repository {
/**
* local plugin doesn't require login, so list all files
* @return mixed
*/
public function print_login() {
return $this->get_listing();
}
/**
2009-12-04 09:19:54 +00:00
* Get file listing
*
* @param string $encodedpath
* @return mixed
*/
public function get_listing($encodedpath = '', $page = '') {
global $CFG, $USER, $OUTPUT;
$ret = array();
$ret['dynload'] = true;
$ret['nosearch'] = true;
2010-05-19 02:49:18 +00:00
$ret['nologin'] = true;
$list = array();
if (!empty($encodedpath)) {
$params = unserialize(base64_decode($encodedpath));
if (is_array($params)) {
$component = is_null($params['component']) ? NULL : clean_param($params['component'], PARAM_COMPONENT);
$filearea = is_null($params['filearea']) ? NULL : clean_param($params['filearea'], PARAM_AREA);
$itemid = is_null($params['itemid']) ? NULL : clean_param($params['itemid'], PARAM_INT);
$filepath = is_null($params['filepath']) ? NULL : clean_param($params['filepath'], PARAM_PATH);;
$filename = is_null($params['filename']) ? NULL : clean_param($params['filename'], PARAM_FILE);
$context = get_context_instance_by_id(clean_param($params['contextid'], PARAM_INT));
}
} else {
$itemid = null;
$filename = null;
$filearea = null;
$filepath = null;
2010-07-06 07:15:08 +00:00
$component = null;
if (!empty($this->context)) {
list($context, $course, $cm) = get_context_info_array($this->context->id);
if (is_object($course)) {
$context = get_context_instance(CONTEXT_COURSE, $course->id);
} else {
$context = get_system_context();
}
} else {
$context = get_system_context();
}
}
$browser = get_file_browser();
$list = array();
2010-07-06 07:15:08 +00:00
if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) {
// build file tree
$element = repository_local_file::retrieve_file_info($fileinfo, $this);
$nonemptychildren = $element->get_non_empty_children();
foreach ($nonemptychildren as $child) {
$list[] = (array)$child->get_node();
}
2010-07-20 04:54:57 +00:00
} else {
// if file doesn't exist, build path nodes root of current context
$fileinfo = $browser->get_file_info($context, null, null, null, null, null);
}
// 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());
2010-07-20 04:54:57 +00:00
}
}
$ret['list'] = array_filter($list, array($this, 'filter'));
return $ret;
}
2009-12-04 09:19:54 +00:00
/**
* Local file don't support to link to external links
*
* @return int
2009-12-04 09:19:54 +00:00
*/
public function supported_returntypes() {
return FILE_INTERNAL;
}
/**
* Does this repository used to browse moodle files?
*
* @return boolean
*/
public function has_moodle_files() {
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;
}
}