weekly release 2.3dev
[moodle.git] / repository / local / lib.php
CommitLineData
6a391ebf 1<?php
aedb0396
DC
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
6a391ebf 17
96297ca2 18/**
aedb0396 19 * repository_local class is used to browse moodle files
96297ca2 20 *
e2652f1a
MG
21 * @since 2.0
22 * @package repository_local
d078f6d3
PS
23 * @copyright 2009 Dongsheng Cai <dongsheng@moodle.com>
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
96297ca2 25 */
aedb0396 26
be236ef5 27class repository_local extends repository {
96297ca2 28 /**
acb70a9b 29 * local plugin doesn't require login, so list all files
cd312233 30 * @return mixed
96297ca2 31 */
5f9087f3 32 public function print_login() {
6a391ebf 33 return $this->get_listing();
34 }
5fd3e8f7 35
96297ca2 36 /**
aedb0396 37 * Get file listing
96297ca2 38 *
cd312233 39 * @param string $encodedpath
cd312233 40 * @return mixed
96297ca2 41 */
58eb9b9f 42 public function get_listing($encodedpath = '', $page = '') {
390baf46 43 global $CFG, $USER, $OUTPUT;
cd312233 44 $ret = array();
45 $ret['dynload'] = true;
d1bfc05e 46 $ret['nosearch'] = true;
03896eb7 47 $ret['nologin'] = true;
cd312233 48 $list = array();
21942470 49
a6058fed 50 if (!empty($encodedpath)) {
51 $params = unserialize(base64_decode($encodedpath));
52 if (is_array($params)) {
aff24313
PS
53 $component = is_null($params['component']) ? NULL : clean_param($params['component'], PARAM_COMPONENT);
54 $filearea = is_null($params['filearea']) ? NULL : clean_param($params['filearea'], PARAM_AREA);
3136dc16
PS
55 $itemid = is_null($params['itemid']) ? NULL : clean_param($params['itemid'], PARAM_INT);
56 $filepath = is_null($params['filepath']) ? NULL : clean_param($params['filepath'], PARAM_PATH);;
57 $filename = is_null($params['filename']) ? NULL : clean_param($params['filename'], PARAM_FILE);
58 $context = get_context_instance_by_id(clean_param($params['contextid'], PARAM_INT));
a6058fed 59 }
60 } else {
61 $itemid = null;
62 $filename = null;
63 $filearea = null;
64 $filepath = null;
16f61c70 65 $component = null;
be85f7ab
DC
66 if (!empty($this->context)) {
67 list($context, $course, $cm) = get_context_info_array($this->context->id);
e2652f1a
MG
68 if (is_object($course)) {
69 $context = get_context_instance(CONTEXT_COURSE, $course->id);
70 } else {
71 $context = get_system_context();
72 }
be85f7ab
DC
73 } else {
74 $context = get_system_context();
75 }
a6058fed 76 }
77
955b6e09
DC
78 $browser = get_file_browser();
79
e2652f1a 80 $list = array();
16f61c70 81 if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) {
955b6e09 82 // build file tree
e2652f1a
MG
83 $element = repository_local_file::retrieve_file_info($fileinfo, $this);
84 $nonemptychildren = $element->get_non_empty_children();
85 foreach ($nonemptychildren as $child) {
86 $list[] = (array)$child->get_node();
cd312233 87 }
eed6f2e7
DC
88 } else {
89 // if file doesn't exist, build path nodes root of current context
eed6f2e7 90 $fileinfo = $browser->get_file_info($context, null, null, null, null, null);
e2652f1a
MG
91 }
92 // build path navigation
93 $ret['path'] = array();
94 $element = repository_local_file::retrieve_file_info($fileinfo, $this);
95 for ($level = $element; $level; $level = $level->get_parent()) {
96 if ($level == $element || !$level->can_skip()) {
97 array_unshift($ret['path'], $level->get_node_path());
eed6f2e7 98 }
6a391ebf 99 }
87628a67 100 $ret['list'] = array_filter($list, array($this, 'filter'));
cd312233 101 return $ret;
6a391ebf 102 }
dbc01944 103
aedb0396
DC
104 /**
105 * Local file don't support to link to external links
106 *
2f67a9b3 107 * @return int
aedb0396 108 */
41076c58
DC
109 public function supported_returntypes() {
110 return FILE_INTERNAL;
111 }
c7e4621e 112
acb70a9b 113 /**
f392caba 114 * Does this repository used to browse moodle files?
acb70a9b 115 *
f392caba 116 * @return boolean
acb70a9b 117 */
f392caba
DC
118 public function has_moodle_files() {
119 return true;
f892da72 120 }
acb70a9b 121}
e2652f1a
MG
122
123/**
124 * Class to cache some information about file
125 *
126 * This class is a wrapper to instances of file_info. It caches such information as
127 * parent and list of children. It also stores an array of already retrieved elements.
128 *
129 * It also implements more comprehensive algorithm for checking if folder is empty
130 * (taking into account the filtering of the files). To decrease number of levels
131 * we check if some subfolders can be skipped from the tree.
132 *
133 * As a result we display in Server files repository only non-empty folders and skip
134 * filearea folders if this is the only filearea in the module.
135 * For non-admin the course categories are not shown as well (courses are shown as a list)
136 *
137 * @package repository_local
138 * @copyright 2012 Marina Glancy
139 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
140 */
141class repository_local_file {
142 /** @var array stores already retrieved files */
143 private static $cachedfiles = array();
144 /** @var file_info Stores the original file */
145 public $fileinfo;
146 /** @var bool whether this file is directory */
147 private $isdir;
148 /** @var array caches retrieved children */
149 private $children = null;
150 /** @var array caches retrieved information whether this file is an empty directory */
151 protected $isempty = null;
152 /** @var repository link to the container repository (for filtering the results) */
153 private $repository;
154 /** @var repository_local_file link to parent directory */
155 protected $parent;
156 /** @var bool caches calculated information on whether this directory must be skipped in the tree */
157 private $skip = null;
158
159 /**
160 * Creates (or retrieves from cache) the repository_local_file object for $file_info
161 *
162 * @param file_info $fileinfo
163 * @param repository $repository
164 * @param repository_local_file $parent
165 * @return repository_local_file
166 */
167 public static function retrieve_file_info(file_info $fileinfo, repository $repository, repository_local_file $parent = null) {
168 $encodedpath = base64_encode(serialize($fileinfo->get_params()));
169 if (!isset(self::$cachedfiles[$encodedpath])) {
170 self::$cachedfiles[$encodedpath] = new repository_local_file($fileinfo, $repository, $parent);
171 }
172 return self::$cachedfiles[$encodedpath];
173 }
174
175 /**
176 * Creates an object
177 *
178 * @param file_info $fileinfo
179 * @param repository $repository
180 * @param repository_local_file $parent
181 */
182 private function __construct(file_info $fileinfo, repository $repository, repository_local_file $parent = null) {
183 $this->repository = $repository;
184 $this->fileinfo = $fileinfo;
185 $this->isdir = $fileinfo->is_directory();
186 if (!$this->isdir) {
187 $node = array('title' => $this->fileinfo->get_visible_name());
188 $this->isempty = !$repository->filter($node);
189 $this->skip = false;
190 }
191 }
192
193 /**
194 * Returns node for $ret['list']
195 *
196 * @return array
197 */
198 public function get_node() {
199 global $OUTPUT;
200 $encodedpath = base64_encode(serialize($this->fileinfo->get_params()));
201 $node = array(
202 'title' => $this->fileinfo->get_visible_name(),
203 'size' => 0,
204 'date' => '');
205 if ($this->isdir) {
206 $node['path'] = $encodedpath;
207 $node['thumbnail'] = $OUTPUT->pix_url('f/folder-32')->out(false);
208 $node['children'] = array();
209 } else {
210 $node['source'] = $encodedpath;
211 $node['thumbnail'] = $OUTPUT->pix_url(file_extension_icon($node['title'], 32))->out(false);
212 }
213 return $node;
214 }
215
216 /**
217 * Returns node for $ret['path']
218 *
219 * @return array
220 */
221 public function get_node_path() {
222 $encodedpath = base64_encode(serialize($this->fileinfo->get_params()));
223 return array(
224 'path' => $encodedpath,
225 'name' => $this->fileinfo->get_visible_name()
226 );
227 }
228
229 /**
230 * Checks if this is a directory
231 *
232 * @return bool
233 */
234 public function is_dir() {
235 return $this->isdir;
236 }
237
238 /**
239 * Returns children of this element
240 *
241 * @return array
242 */
243 public function get_children() {
244 if (!$this->isdir) {
245 return array();
246 }
247 if ($this->children === null) {
248 $this->children = array();
249 $children = $this->fileinfo->get_children();
250 for ($i=0; $i<count($children); $i++) {
251 $this->children[] = self::retrieve_file_info($children[$i], $this->repository, $this);
252 }
253 }
254 return $this->children;
255 }
256
257 /**
258 * Checks if this folder is empty (contains no non-empty children)
259 *
260 * @return bool
261 */
262 public function is_empty() {
263 if ($this->isempty === null) {
264 $this->isempty = true;
265 if (!$this->fileinfo->is_empty_area()) {
266 // even if is_empty_area() returns false, element still may be empty
267 $children = $this->get_children();
268 if (!empty($children)) {
269 // 1. Let's look at already retrieved children
270 foreach ($children as $childnode) {
271 if ($childnode->isempty === false) {
272 // we already calculated isempty for a child, and it is not empty
273 $this->isempty = false;
274 break;
275 }
276 }
277 if ($this->isempty) {
278 // 2. now we know that this directory contains children that are either empty or we don't know
279 foreach ($children as $childnode) {
280 if (!$childnode->is_empty()) {
281 $this->isempty = false;
282 break;
283 }
284 }
285 }
286 }
287 }
288 }
289 return $this->isempty;
290 }
291
292 /**
293 * Returns the parent element
294 *
295 * @return repository_local_file
296 */
297 public function get_parent() {
298 if ($this->parent === null) {
299 if ($parent = $this->fileinfo->get_parent()) {
300 $this->parent = self::retrieve_file_info($parent, $this->repository);
301 } else {
302 $this->parent = false;
303 }
304 }
305 return $this->parent;
306 }
307
308 /**
309 * Wether this folder may be skipped in tree view
310 *
311 * @return bool
312 */
313 public function can_skip() {
314 global $CFG;
315 if ($this->skip === null) {
316 $this->skip = false;
317 if ($this->fileinfo instanceof file_info_stored) {
318 $params = $this->fileinfo->get_params();
319 if (strlen($params['filearea']) && $params['filepath'] == '/' && $params['filename'] == '.') {
320 // This is a filearea inside an activity, it can be skipped if it has no non-empty siblings
321 if ($parent = $this->get_parent()) {
322 $siblings = $parent->get_children();
323 $countnonempty = 0;
324 foreach ($siblings as $sibling) {
325 if (!$sibling->is_empty()) {
326 $countnonempty++;
327 if ($countnonempty > 1) {
328 break;
329 }
330 }
331 }
332 if ($countnonempty <= 1) {
333 $this->skip = true;
334 }
335 }
336 }
337 } else if ($this->fileinfo instanceof file_info_context_coursecat) {
338 // This is a course category. For non-admins we do not display categories
339 $this->skip = empty($CFG->navshowmycoursecategories) &&
340 !has_capability('moodle/course:update', get_context_instance(CONTEXT_SYSTEM));
341 }
342 }
343 return $this->skip;
344 }
345
346 /**
347 * Returns array of children who have any elmenets
348 *
349 * If a subfolder can be skipped - list children of subfolder instead
350 * (recursive function)
351 *
352 * @return array
353 */
354 public function get_non_empty_children() {
355 $children = $this->get_children();
356 $nonemptychildren = array();
357 foreach ($children as $child) {
358 if (!$child->is_empty()) {
359 if ($child->can_skip()) {
360 $nonemptychildren = array_merge($nonemptychildren, $child->get_non_empty_children());
361 } else {
362 $nonemptychildren[] = $child;
363 }
364 }
365 }
366 return $nonemptychildren;
367 }
727dac2f 368}