MDL-24144 hiding of empty folders in repository/local
[moodle.git] / lib / filebrowser / file_info_stored.php
1 <?php
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/>.
19 /**
20  * Utility class for browsing of stored files.
21  *
22  * @package    core
23  * @subpackage filebrowser
24  * @copyright  2008 Petr Skoda (http://skodak.org)
25  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26  */
28 defined('MOODLE_INTERNAL') || die();
30 /**
31  * Represents an actual file or folder - a row in the file table -
32  * in the tree navigated by @see{file_browser}.
33  *
34  * @package    core
35  * @subpackage filebrowser
36  * @copyright  2008 Petr Skoda (http://skodak.org)
37  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38  */
39 class file_info_stored extends file_info {
40     protected $lf;
41     protected $urlbase;
42     protected $topvisiblename;
43     protected $itemidused;
44     protected $readaccess;
45     protected $writeaccess;
46     protected $areaonly;
48     /**
49      * Constructor
50      *
51      * @param file_browser $browser
52      * @param object $context
53      * @param stored_file $storedfile
54      * @param string $urlbase the serving script - usually the $CFG->wwwroot/.'pluginfile.php'
55      * @param string $topvisiblename the human readable name of this area
56      * @param string $itemidused false if itemid  always 0 and not included in URL
57      * @param string $readaccess allow file reading
58      * @param string $writeaccess allow file write, delete
59      * @param string $areaonly do not show links to parent context/area
60      */
61     public function __construct(file_browser $browser, $context, $storedfile, $urlbase, $topvisiblename, $itemidused, $readaccess, $writeaccess, $areaonly) {
62         parent::__construct($browser, $context);
64         $this->lf             = $storedfile;
65         $this->urlbase        = $urlbase;
66         $this->topvisiblename = $topvisiblename;
67         $this->itemidused     = $itemidused;
68         $this->readaccess     = $readaccess;
69         $this->writeaccess    = $writeaccess;
70         $this->areaonly       = $areaonly;
71     }
73     /**
74      * Returns list of standard virtual file/directory identification.
75      * The difference from stored_file parameters is that null values
76      * are allowed in all fields
77      * @return array with keys contextid, component, filearea, itemid, filepath and filename
78      */
79     public function get_params() {
80         return array('contextid'=>$this->context->id,
81                      'component'=>$this->lf->get_component(),
82                      'filearea' =>$this->lf->get_filearea(),
83                      'itemid'   =>$this->lf->get_itemid(),
84                      'filepath' =>$this->lf->get_filepath(),
85                      'filename' =>$this->lf->get_filename());
86     }
88     /**
89      * Returns localised visible name.
90      * @return string
91      */
92     public function get_visible_name() {
93         $filename = $this->lf->get_filename();
94         $filepath = $this->lf->get_filepath();
96         if ($filename !== '.') {
97             return $filename;
99         } else {
100             $dir = trim($filepath, '/');
101             $dir = explode('/', $dir);
102             $dir = array_pop($dir);
103             if ($dir === '') {
104                 return $this->topvisiblename;
105             } else {
106                 return $dir;
107             }
108         }
109     }
111     /**
112      * Returns file download url
113      * @param bool $forcedownload
114      * @param bool $htts force https
115      * @return string url
116      */
117     public function get_url($forcedownload=false, $https=false) {
118         if (!$this->is_readable()) {
119             return null;
120         }
122         if ($this->is_directory()) {
123             return null;
124         }
126         $this->urlbase;
127         $contextid = $this->lf->get_contextid();
128         $component = $this->lf->get_component();
129         $filearea  = $this->lf->get_filearea();
130         $itemid    = $this->lf->get_itemid();
131         $filepath  = $this->lf->get_filepath();
132         $filename  = $this->lf->get_filename();
134         if ($this->itemidused) {
135             $path = '/'.$contextid.'/'.$component.'/'.$filearea.'/'.$itemid.$filepath.$filename;
136         } else {
137             $path = '/'.$contextid.'/'.$component.'/'.$filearea.$filepath.$filename;
138         }
139         return file_encode_url($this->urlbase, $path, $forcedownload, $https);
140     }
142     /**
143      * Can I read content of this file or enter directory?
144      * @return bool
145      */
146     public function is_readable() {
147         return $this->readaccess;
148     }
150     /**
151      * Can I add new files or directories?
152      * @return bool
153      */
154     public function is_writable() {
155         return $this->writeaccess;
156     }
158     /**
159      * Is this top of empty area?
160      *
161      * @return bool
162      */
163     public function is_empty_area() {
164         if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') {
165             // test the emptiness only in the top most level, it does not make sense at lower levels
166             $fs = get_file_storage();
167             return $fs->is_area_empty($this->lf->get_contextid(), $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid());
168         } else {
169             return false;
170         }
171     }
173     /**
174      * Returns file size in bytes, null for directories
175      * @return int bytes or null if not known
176      */
177     public function get_filesize() {
178         return $this->lf->get_filesize();
179     }
181     /**
182      * Returns mimetype
183      * @return string mimetype or null if not known
184      */
185     public function get_mimetype() {
186         return $this->lf->get_mimetype();
187     }
189     /**
190      * Returns time created unix timestamp if known
191      * @return int timestamp or null
192      */
193     public function get_timecreated() {
194         return $this->lf->get_timecreated();
195     }
197     /**
198      * Returns time modified unix timestamp if known
199      * @return int timestamp or null
200      */
201     public function get_timemodified() {
202         return $this->lf->get_timemodified();
203     }
205     /**
206      * Is directory?
207      * @return bool
208      */
209     public function is_directory() {
210         return $this->lf->is_directory();
211     }
213     /**
214      * Returns the license type of the file
215      * @return string license short name or null
216      */
217     public function get_license() {
218         return $this->lf->get_license();
219     }
221     /**
222      * Returns the author name of the file
223      * @return string author name or null
224      */
225     public function get_author() {
226         return $this->lf->get_author();
227     }
229     /**
230      * Returns the source of the file
231      * @return string a source url or null
232      */
233     public function get_source() {
234         return $this->lf->get_source();
235     }
237     /**
238      * Returns the sort order of the file
239      * @return int
240      */
241     public function get_sortorder() {
242         return $this->lf->get_sortorder();
243     }
245     /**
246      * Returns list of children.
247      * @return array of file_info instances
248      */
249     public function get_children() {
250         if (!$this->lf->is_directory()) {
251             return array();
252         }
254         $result = array();
255         $fs = get_file_storage();
257         $storedfiles = $fs->get_directory_files($this->context->id, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(),
258                                                 $this->lf->get_filepath(), false, true, "filepath, filename");
259         foreach ($storedfiles as $file) {
260             $result[] = new file_info_stored($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
261                                              $this->itemidused, $this->readaccess, $this->writeaccess, false);
262         }
264         return $result;
265     }
267     /**
268      * Returns parent file_info instance
269      * @return file_info or null for root
270      */
271     public function get_parent() {
272         if ($this->lf->get_filepath() === '/' and $this->lf->is_directory()) {
273             if ($this->areaonly) {
274                 return null;
275             } else if ($this->itemidused) {
276                 return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea());
277             } else {
278                 return $this->browser->get_file_info($this->context);
279             }
280         }
282         if (!$this->lf->is_directory()) {
283             return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $this->lf->get_filepath(), '.');
284         }
286         $filepath = $this->lf->get_filepath();
287         $filepath = trim($filepath, '/');
288         $dirs = explode('/', $filepath);
289         array_pop($dirs);
290         $filepath = implode('/', $dirs);
291         $filepath = ($filepath === '') ? '/' : "/$filepath/";
293         return $this->browser->get_file_info($this->context, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, '.');
294     }
296     /**
297      * Create new directory, may throw exception - make sure
298      * params are valid.
299      * @param string $newdirname name of new directory
300      * @param int id of author, default $USER->id
301      * @return file_info new directory
302      */
303     public function create_directory($newdirname, $userid = NULL) {
304         if (!$this->is_writable() or !$this->lf->is_directory()) {
305             return null;
306         }
308         $newdirname = clean_param($newdirname, PARAM_FILE);
309         if ($newdirname === '') {
310             return null;
311         }
313         $filepath = $this->lf->get_filepath().'/'.$newdirname.'/';
315         $fs = get_file_storage();
317         if ($file = $fs->create_directory($this->lf->get_contextid(), $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, $userid)) {
318             return $this->browser->get_file_info($this->context, $this->lf->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
319         }
320         return null;
321     }
324     /**
325      * Create new file from string - make sure
326      * params are valid.
327      * @param string $newfilename name of new file
328      * @param string $content of file
329      * @param int id of author, default $USER->id
330      * @return file_info new file
331      */
332     public function create_file_from_string($newfilename, $content, $userid = NULL) {
333         if (!$this->is_writable() or !$this->lf->is_directory()) {
334             return null;
335         }
337         $newfilename = clean_param($newfilename, PARAM_FILE);
338         if ($newfilename === '') {
339             return null;
340         }
342         $fs = get_file_storage();
344         $now = time();
346         $newrecord = new object();
347         $newrecord->contextid = $this->lf->get_contextid();
348         $newrecord->component = $this->lf->get_component();
349         $newrecord->filearea  = $this->lf->get_filearea();
350         $newrecord->itemid    = $this->lf->get_itemid();
351         $newrecord->filepath  = $this->lf->get_filepath();
352         $newrecord->filename  = $newfilename;
354         if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
355             // file already exists, sorry
356             return null;
357         }
359         $newrecord->timecreated  = $now;
360         $newrecord->timemodified = $now;
361         $newrecord->mimetype     = mimeinfo('type', $newfilename);
362         $newrecord->userid       = $userid;
364         if ($file = $fs->create_file_from_string($newrecord, $content)) {
365             return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
366         }
367         return null;
368     }
370     /**
371      * Create new file from pathname - make sure
372      * params are valid.
373      * @param string $newfilename name of new file
374      * @param string $pathname location of file
375      * @param int id of author, default $USER->id
376      * @return file_info new file
377      */
378     public function create_file_from_pathname($newfilename, $pathname, $userid = NULL) {
379         if (!$this->is_writable() or !$this->lf->is_directory()) {
380             return null;
381         }
383         $newfilename = clean_param($newfilename, PARAM_FILE);
384         if ($newfilename === '') {
385             return null;
386         }
388         $fs = get_file_storage();
390         $now = time();
392         $newrecord = new object();
393         $newrecord->contextid = $this->lf->get_contextid();
394         $newrecord->component = $this->lf->get_component();
395         $newrecord->filearea  = $this->lf->get_filearea();
396         $newrecord->itemid    = $this->lf->get_itemid();
397         $newrecord->filepath  = $this->lf->get_filepath();
398         $newrecord->filename  = $newfilename;
400         if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
401             // file already exists, sorry
402             return null;
403         }
405         $newrecord->timecreated  = $now;
406         $newrecord->timemodified = $now;
407         $newrecord->mimetype     = mimeinfo('type', $newfilename);
408         $newrecord->userid       = $userid;
410         if ($file = $fs->create_file_from_pathname($newrecord, $pathname)) {
411             return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
412         }
413         return null;
414     }
416     /**
417      * Create new file from stored file - make sure
418      * params are valid.
419      * @param string $newfilename name of new file
420      * @param mixed dile id or stored_file of file
421      * @param int id of author, default $USER->id
422      * @return file_info new file
423      */
424     public function create_file_from_storedfile($newfilename, $fid, $userid = NULL) {
425         if (!$this->is_writable() or $this->lf->get_filename() !== '.') {
426             return null;
427         }
429         $newfilename = clean_param($newfilename, PARAM_FILE);
430         if ($newfilename === '') {
431             return null;
432         }
434         $fs = get_file_storage();
436         $now = time();
438         $newrecord = new object();
439         $newrecord->contextid = $this->lf->get_contextid();
440         $newrecord->component = $this->lf->get_component();
441         $newrecord->filearea  = $this->lf->get_filearea();
442         $newrecord->itemid    = $this->lf->get_itemid();
443         $newrecord->filepath  = $this->lf->get_filepath();
444         $newrecord->filename  = $newfilename;
446         if ($fs->file_exists($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename)) {
447             // file already exists, sorry
448             return null;
449         }
451         $newrecord->timecreated  = $now;
452         $newrecord->timemodified = $now;
453         $newrecord->mimetype     = mimeinfo('type', $newfilename);
454         $newrecord->userid       = $userid;
456         if ($file = $fs->create_file_from_storedfile($newrecord, $fid)) {
457             return $this->browser->get_file_info($this->context, $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
458         }
459         return null;
460     }
462     /**
463      * Delete file, make sure file is deletable first.
464      * @return bool success
465      */
466     public function delete() {
467         if (!$this->is_writable()) {
468             return false;
469         }
471         if ($this->is_directory()) {
472             $filepath = $this->lf->get_filepath();
473             $fs = get_file_storage();
474             $storedfiles = $fs->get_area_files($this->context->id, $file->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(), "");
475             foreach ($storedfiles as $file) {
476                 if (strpos($file->get_filepath(), $filepath) === 0) {
477                     $file->delete();
478                 }
479             }
480         }
482         return $this->lf->delete();
483     }
485     /**
486      * Copy content of this file to local storage, overriding current file if needed.
487      * @param int $contextid
488      * @param string $filearea
489      * @param int $itemid
490      * @param string $filepath
491      * @param string $filename
492      * @return boolean success
493      */
494     public function copy_to_storage($contextid, $component, $filearea, $itemid, $filepath, $filename) {
495         if (!$this->is_readable() or $this->is_directory()) {
496             return false;
497         }
499         $fs = get_file_storage();
500         if ($existing = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename)) {
501             $existing->delete();
502         }
503         $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'filepath'=>$filepath, 'filename'=>$filename);
504         $fs->create_file_from_storedfile($file_record, $this->lf);
506         return true;
507     }
509     /**
510      * Copy content of this file to local storage, overriding current file if needed.
511      * @param string $pathname real local full file name
512      * @return boolean success
513      */
514     public function copy_to_pathname($pathname) {
515         if (!$this->is_readable() or $this->is_directory()) {
516             return false;
517         }
519         if (file_exists($pathname)) {
520             if (!unlink($pathname)) {
521                 return false;
522             }
523         }
525         $this->lf->copy_content_to($pathname);
527         return true;
528     }