Merge branch 'wip-MDL-27236-master' of git://github.com/marinaglancy/moodle
authorDan Poltawski <dan@moodle.com>
Wed, 21 Mar 2012 06:51:40 +0000 (14:51 +0800)
committerDan Poltawski <dan@moodle.com>
Wed, 21 Mar 2012 06:51:40 +0000 (14:51 +0800)
1  2 
repository/local/lib.php

@@@ -166,3 -119,250 +119,250 @@@ class repository_local extends reposito
          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;
+     }
++}