MDL-34290 repository API: add timeout to get_file and throw exception
[moodle.git] / repository / filesystem / lib.php
CommitLineData
4317f92f 1<?php
10d53fd3
DC
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
67233725
DC
17/**
18 * This plugin is used to access files on server file system
19 *
20 * @since 2.0
21 * @package repository_filesystem
22 * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25require_once($CFG->dirroot . '/repository/lib.php');
26require_once($CFG->libdir . '/filelib.php');
27
fdcf5320 28/**
29 * repository_filesystem class
67233725 30 *
fdcf5320 31 * Create a repository from your local filesystem
32 * *NOTE* for security issue, we use a fixed repository path
33 * which is %moodledata%/repository
34 *
d078f6d3 35 * @package repository
67233725 36 * @copyright 2009 Dongsheng Cai {@link http://dongsheng.org}
d078f6d3 37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
fdcf5320 38 */
520de343 39class repository_filesystem extends repository {
67233725
DC
40
41 /**
42 * Constructor
43 *
44 * @param int $repositoryid repository ID
45 * @param int $context context ID
46 * @param array $options
47 */
447c7a19 48 public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array()) {
fdcf5320 49 global $CFG;
520de343 50 parent::__construct($repositoryid, $context, $options);
873f2e0f
DC
51 $root = $CFG->dataroot.'/repository/';
52 $subdir = $this->get_option('fs_path');
53 $this->root_path = $root . $subdir . '/';
e437618b 54 if (!empty($options['ajax'])) {
fdcf5320 55 if (!is_dir($this->root_path)) {
8512eebe 56 $created = mkdir($this->root_path, $CFG->directorypermissions, true);
520de343 57 $ret = array();
58 $ret['msg'] = get_string('invalidpath', 'repository_filesystem');
59 $ret['nosearch'] = true;
93e9aa27 60 if ($options['ajax'] && !$created) {
fdcf5320 61 echo json_encode($ret);
62 exit;
520de343 63 }
64 }
520de343 65 }
66 }
67 public function get_listing($path = '', $page = '') {
390baf46 68 global $CFG, $OUTPUT;
520de343 69 $list = array();
70 $list['list'] = array();
71 // process breacrumb trail
72 $list['path'] = array(
5bdf63cc 73 array('name'=>get_string('root', 'repository_filesystem'), 'path'=>'')
520de343 74 );
75 $trail = '';
76 if (!empty($path)) {
77 $parts = explode('/', $path);
78 if (count($parts) > 1) {
79 foreach ($parts as $part) {
4a9aff79 80 if (!empty($part)) {
81 $trail .= ('/'.$part);
82 $list['path'][] = array('name'=>$part, 'path'=>$trail);
83 }
520de343 84 }
85 } else {
86 $list['path'][] = array('name'=>$path, 'path'=>$path);
87 }
88 $this->root_path .= ($path.'/');
89 }
520de343 90 $list['manage'] = false;
520de343 91 $list['dynload'] = true;
520de343 92 $list['nologin'] = true;
520de343 93 $list['nosearch'] = true;
7355640a
MG
94 // retrieve list of files and directories and sort them
95 $fileslist = array();
96 $dirslist = array();
520de343 97 if ($dh = opendir($this->root_path)) {
98 while (($file = readdir($dh)) != false) {
99 if ( $file != '.' and $file !='..') {
7355640a
MG
100 if (is_file($this->root_path.$file)) {
101 $fileslist[] = $file;
520de343 102 } else {
7355640a 103 $dirslist[] = $file;
520de343 104 }
105 }
106 }
107 }
7355640a
MG
108 collatorlib::asort($fileslist, collatorlib::SORT_STRING);
109 collatorlib::asort($dirslist, collatorlib::SORT_STRING);
110 // fill the $list['list']
111 foreach ($dirslist as $file) {
112 if (!empty($path)) {
113 $current_path = $path . '/'. $file;
114 } else {
115 $current_path = $file;
116 }
117 $list['list'][] = array(
118 'title' => $file,
119 'children' => array(),
120 'datecreated' => filectime($this->root_path.$file),
121 'datemodified' => filemtime($this->root_path.$file),
122 'thumbnail' => $OUTPUT->pix_url(file_folder_icon(90))->out(false),
123 'path' => $current_path
124 );
125 }
126 foreach ($fileslist as $file) {
127 $list['list'][] = array(
128 'title' => $file,
129 'source' => $path.'/'.$file,
130 'size' => filesize($this->root_path.$file),
131 'datecreated' => filectime($this->root_path.$file),
132 'datemodified' => filemtime($this->root_path.$file),
133 'thumbnail' => $OUTPUT->pix_url(file_extension_icon($file, 90))->out(false),
134 'icon' => $OUTPUT->pix_url(file_extension_icon($file, 24))->out(false)
135 );
136 }
fb4ee704 137 $list['list'] = array_filter($list['list'], array($this, 'filter'));
520de343 138 return $list;
139 }
520de343 140 public function check_login() {
141 return true;
142 }
520de343 143 public function print_login() {
144 return true;
145 }
520de343 146 public function global_search() {
147 return false;
148 }
67233725 149
951d2d55
DC
150 /**
151 * Return file path
152 * @return array
153 */
520de343 154 public function get_file($file, $title = '') {
155 global $CFG;
156 if ($file{0} == '/') {
157 $file = $this->root_path.substr($file, 1, strlen($file)-1);
951d2d55
DC
158 } else {
159 $file = $this->root_path.$file;
520de343 160 }
161 // this is a hack to prevent move_to_file deleteing files
162 // in local repository
163 $CFG->repository_no_delete = true;
85b5ed5d 164 return array('path'=>$file, 'url'=>'');
520de343 165 }
166
d6453211
DC
167 /**
168 * Return the source information
169 *
170 * @param stdClass $filepath
171 * @return string|null
172 */
173 public function get_file_source_info($filepath) {
174 return $filepath;
175 }
176
520de343 177 public function logout() {
178 return true;
179 }
180
181 public static function get_instance_option_names() {
93e9aa27 182 return array('fs_path');
520de343 183 }
184
b2f8adf4 185 public function set_option($options = array()) {
186 $options['fs_path'] = clean_param($options['fs_path'], PARAM_PATH);
187 $ret = parent::set_option($options);
188 return $ret;
189 }
8e5af6cf 190
4a126f17 191 public static function instance_config_form($mform) {
49d20def 192 global $CFG, $PAGE;
8e5af6cf 193 if (has_capability('moodle/site:config', get_system_context())) {
49d20def
DC
194 $path = $CFG->dataroot . '/repository/';
195 if (!is_dir($path)) {
8512eebe 196 mkdir($path, $CFG->directorypermissions, true);
93e9aa27 197 }
49d20def
DC
198 if ($handle = opendir($path)) {
199 $fieldname = get_string('path', 'repository_filesystem');
200 $choices = array();
201 while (false !== ($file = readdir($handle))) {
202 if (is_dir($path.$file) && $file != '.' && $file!= '..') {
203 $choices[$file] = $file;
204 $fieldname = '';
205 }
206 }
207 if (empty($choices)) {
208 $mform->addElement('static', '', '', get_string('nosubdir', 'repository_filesystem', $path));
6b172cdc 209 $mform->addElement('hidden', 'fs_path', '');
49d20def
DC
210 } else {
211 $mform->addElement('select', 'fs_path', $fieldname, $choices);
212 $mform->addElement('static', null, '', get_string('information','repository_filesystem', $path));
213 }
214 closedir($handle);
873f2e0f 215 }
49d20def
DC
216 } else {
217 $mform->addElement('static', null, '', get_string('nopermissions', 'error', get_string('configplugin', 'repository_filesystem')));
218 return false;
93e9aa27 219 }
93e9aa27 220 }
8e5af6cf 221
49d20def
DC
222 public static function create($type, $userid, $context, $params, $readonly=0) {
223 global $PAGE;
8e5af6cf 224 if (has_capability('moodle/site:config', get_system_context())) {
49d20def
DC
225 return parent::create($type, $userid, $context, $params, $readonly);
226 } else {
8e5af6cf 227 require_capability('moodle/site:config', get_system_context());
49d20def
DC
228 return false;
229 }
230 }
6b172cdc
DC
231 public static function instance_form_validation($mform, $data, $errors) {
232 if (empty($data['fs_path'])) {
233 $errors['fs_path'] = get_string('invalidadminsettingname', 'error', 'fs_path');
234 }
235 return $errors;
236 }
67233725
DC
237
238 /**
239 * User cannot use the external link to dropbox
240 *
241 * @return int
242 */
243 public function supported_returntypes() {
244 return FILE_INTERNAL | FILE_REFERENCE;
245 }
246
247 /**
0b2bfbd1 248 * Returns information about file in this repository by reference
67233725
DC
249 * {@link repository::get_file_reference()}
250 * {@link repository::get_file()}
251 *
0b2bfbd1
MG
252 * Returns null if file not found or is not readable
253 *
67233725 254 * @param stdClass $reference file reference db record
0b2bfbd1
MG
255 * @return stdClass|null contains one of the following:
256 * - 'contenthash' and 'filesize'
257 * - 'filepath'
258 * - 'handle'
259 * - 'content'
67233725
DC
260 */
261 public function get_file_by_reference($reference) {
262 $ref = $reference->reference;
263 if ($ref{0} == '/') {
264 $filepath = $this->root_path.substr($ref, 1, strlen($ref)-1);
265 } else {
266 $filepath = $this->root_path.$ref;
267 }
0b2bfbd1
MG
268 if (file_exists($filepath) && is_readable($filepath)) {
269 return (object)array('filepath' => $filepath);
270 } else {
271 return null;
272 }
67233725
DC
273 }
274
275 /**
0b2bfbd1
MG
276 * Repository method to serve the referenced file
277 *
278 * @see send_stored_file
67233725 279 *
0b2bfbd1 280 * @param stored_file $storedfile the file that contains the reference
67233725
DC
281 * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours)
282 * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only
283 * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin
284 * @param array $options additional options affecting the file serving
285 */
286 public function send_file($storedfile, $lifetime=86400 , $filter=0, $forcedownload=false, array $options = null) {
287 $reference = $storedfile->get_reference();
288 if ($reference{0} == '/') {
289 $file = $this->root_path.substr($reference, 1, strlen($reference)-1);
290 } else {
291 $file = $this->root_path.$reference;
292 }
0b2bfbd1
MG
293 if (is_readable($file)) {
294 $filename = $storedfile->get_filename();
295 if ($options && isset($options['filename'])) {
296 $filename = $options['filename'];
297 }
298 $dontdie = ($options && isset($options['dontdie']));
299 send_file($file, $filename, $lifetime , $filter, false, $forcedownload, '', $dontdie);
300 } else {
301 send_file_not_found();
302 }
67233725 303 }
520de343 304}