"MDL-20587, chat module, show sessions with just one users, based on Daniel Neis...
[moodle.git] / pluginfile.php
CommitLineData
d92e7c4d 1<?php
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/>.
17
18/**
19 * This script delegates file serving to individual plugins
20 *
21 * @package moodlecore
22 * @subpackage file
23 * @copyright 2008 Petr Skoda (http://skodak.org)
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
2e9b772f
PS
27// disable moodle specific debug messages and any errors in output
28define('NO_DEBUG_DISPLAY', true);
29
d92e7c4d 30require_once('config.php');
31require_once('lib/filelib.php');
32
d92e7c4d 33$relativepath = get_file_argument();
34$forcedownload = optional_param('forcedownload', 0, PARAM_BOOL);
35
36// relative path must start with '/'
37if (!$relativepath) {
38 print_error('invalidargorconf');
29afd52b 39} else if ($relativepath[0] != '/') {
d92e7c4d 40 print_error('pathdoesnotstartslash');
41}
42
43// extract relative path components
44$args = explode('/', ltrim($relativepath, '/'));
45
46if (count($args) == 0) { // always at least user id
47 print_error('invalidarguments');
48}
49
50$contextid = (int)array_shift($args);
51$filearea = array_shift($args);
52
49375088 53if (!$context = get_context_instance_by_id($contextid)) {
54 send_file_not_found();
55}
d92e7c4d 56$fs = get_file_storage();
57
58
59if ($context->contextlevel == CONTEXT_SYSTEM) {
1c7b8b93 60 if ($filearea === 'blog_attachment' || $filearea === 'blog_post') {
d92e7c4d 61
62 if (empty($CFG->bloglevel)) {
63 print_error('siteblogdisable', 'blog');
64 }
65 if ($CFG->bloglevel < BLOG_GLOBAL_LEVEL) {
66 require_login();
67 if (isguestuser()) {
68 print_error('noguest');
69 }
70 if ($CFG->bloglevel == BLOG_USER_LEVEL) {
71 if ($USER->id != $entry->userid) {
72 send_file_not_found();
73 }
74 }
75 }
76 $entryid = (int)array_shift($args);
77 if (!$entry = $DB->get_record('post', array('module'=>'blog', 'id'=>$entryid))) {
78 send_file_not_found();
79 }
80 if ('publishstate' === 'public') {
81 if ($CFG->forcelogin) {
82 require_login();
83 }
172dd12c 84
d92e7c4d 85 } else if ('publishstate' === 'site') {
86 require_login();
87 //ok
88 } else if ('publishstate' === 'draft') {
89 require_login();
90 if ($USER->id != $entry->userid) {
91 send_file_not_found();
92 }
93 }
172dd12c 94
d92e7c4d 95 //TODO: implement shared course and shared group access
172dd12c 96
d92e7c4d 97 $relativepath = '/'.implode('/', $args);
1c7b8b93 98 $fullpath = $context->id.$filearea.$entryid.$relativepath;
172dd12c 99
d92e7c4d 100 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
101 send_file_not_found();
102 }
172dd12c 103
d92e7c4d 104 send_stored_file($file, 10*60, 0, true); // download MUST be forced - security!
8bdc9cac
SH
105 } else if ($filearea === 'grade_outcome' || $filearea === 'grade_scale') { // CONTEXT_SYSTEM
106 if ($CFG->forcelogin) {
107 require_login();
108 }
109
110 $fullpath = $context->id.$filearea.implode('/', $args);
111
112 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
113 send_file_not_found();
114 }
115
116 session_get_instance()->write_close(); // unlock session during fileserving
117 send_stored_file($file, 60*60, 0, $forcedownload); // TODO: change timeout?
118
119 } else if ($filearea === 'tag_description') { // CONTEXT_SYSTEM
120
121 // All tag descriptions are going to be public but we still need to respect forcelogin
122 if ($CFG->forcelogin) {
123 require_login();
124 }
125
126 $fullpath = $context->id.$filearea.implode('/', $args);
127
128 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
129 send_file_not_found();
130 }
131
132 session_get_instance()->write_close(); // unlock session during fileserving
133 send_stored_file($file, 60*60, 0, true); // TODO: change timeout?
134
76d9df3f
SH
135 } else if ($filearea === 'calendar_event_description') { // CONTEXT_SYSTEM
136
137 // All events here are public the one requirement is that we respect forcelogin
138 if ($CFG->forcelogin) {
139 require_login();
140 }
141
142 $fullpath = $context->id.$filearea.implode('/', $args);
143
144 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
145 send_file_not_found();
146 }
147
148 session_get_instance()->write_close(); // unlock session during fileserving
149 send_stored_file($file, 60*60, 0, $forcedownload); // TODO: change timeout?
172dd12c 150
d92e7c4d 151 } else {
152 send_file_not_found();
172dd12c 153 }
154
d92e7c4d 155} else if ($context->contextlevel == CONTEXT_USER) {
76d9df3f
SH
156
157 if ($filearea === 'calendar_event_description') { // CONTEXT_USER
158
159 // Must be logged in, if they are not then they obviously can't be this user
160 require_login();
03221650 161
76d9df3f
SH
162 // Don't want guests here, potentially saves a DB call
163 if (isguestuser()) {
164 send_file_not_found();
165 }
166
167 // Get the event if from the args array
168 $eventid = array_shift($args);
169 if ((int)$eventid <= 0) {
170 send_file_not_found();
171 }
172
173 // Load the event from the database
174 $event = $DB->get_record('event', array('id'=>(int)$eventid));
175 // Check that we got an event and that it's userid is that of the user
176 if (!$event || $event->userid !== $USER->id) {
177 send_file_not_found();
178 }
179
180 // Get the file and serve if succesfull
181 $fullpath = $context->id.$filearea.$eventid.'/'.implode('/', $args);
182 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
183 send_file_not_found();
184 }
185
186 session_get_instance()->write_close(); // unlock session during fileserving
187 send_stored_file($file, 60*60, 0, $forcedownload); // TODO: change timeout?
8bdc9cac
SH
188 } else if ($filearea === 'user_profile') { // CONTEXT_USER
189
190 if ($CFG->forcelogin) {
191 require_login();
192 }
193
194 $userid = array_shift($args);
195 if ((int)$userid <= 0) {
196 send_file_not_found();
197 }
198
199 if (!empty($CFG->forceloginforprofiles)) {
200 require_login();
201 if (isguestuser()) {
202 send_file_not_found();
203 }
204
205 if ($USER->id !== $userid) {
206 $usercontext = get_context_instance(CONTEXT_USER, $userid);
207 // The browsing user is not the current user
208 if (!isteacherinanycourse() && !isteacherinanycourse($userid) && !has_capability('moodle/user:viewdetails', $usercontext)) {
209 send_file_not_found();
210 }
211
212 $canview = false;
213 if (has_capability('moodle/user:viewdetails', $usercontext)) {
214 $canview = true;
215 } else {
216 $courses = get_my_courses($USER->id);
217 }
218
219 while (!$canview && count($courses) > 0) {
220 $course = array_shift($courses);
221 if (has_capability('moodle/user:viewdetails', get_context_instance(CONTEXT_COURSE, $course->id))) {
222 $canview = true;
223 }
224 }
225 }
226 }
227
228 $fullpath = $context->id.$filearea.$userid.'/'.implode('/', $args);
229
230 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
231 send_file_not_found();
232 }
233
234 session_get_instance()->write_close(); // unlock session during fileserving
235 send_stored_file($file, 60*60, 0, true);
76d9df3f
SH
236 }
237
d92e7c4d 238 send_file_not_found();
172dd12c 239
240
d92e7c4d 241} else if ($context->contextlevel == CONTEXT_COURSECAT) {
242 if ($filearea !== 'coursecat_intro') {
243 send_file_not_found();
244 }
172dd12c 245
d92e7c4d 246 if ($CFG->forcelogin) {
247 // no login necessary - unless login forced everywhere
248 require_login();
249 }
172dd12c 250
d92e7c4d 251 $relativepath = '/'.implode('/', $args);
252 $fullpath = $context->id.'coursecat_intro0'.$relativepath;
172dd12c 253
d92e7c4d 254 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->get_filename() == '.') {
255 send_file_not_found();
256 }
172dd12c 257
d92e7c4d 258 session_get_instance()->write_close(); // unlock session during fileserving
259 send_stored_file($file, 60*60, 0, $forcedownload);
172dd12c 260
172dd12c 261
d92e7c4d 262} else if ($context->contextlevel == CONTEXT_COURSE) {
263 if (!$course = $DB->get_record('course', array('id'=>$context->instanceid))) {
264 print_error('invalidcourseid');
265 }
172dd12c 266
d92e7c4d 267 if ($filearea === 'course_backup') {
268 require_login($course);
269 require_capability('moodle/site:backupdownload', $context);
172dd12c 270
d92e7c4d 271 $relativepath = '/'.implode('/', $args);
272 $fullpath = $context->id.'course_backup0'.$relativepath;
172dd12c 273
d92e7c4d 274 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
9e5fa330 275 send_file_not_found();
172dd12c 276 }
277
d92e7c4d 278 session_get_instance()->write_close(); // unlock session during fileserving
279 send_stored_file($file, 0, 0, true);
280
281 } else if ($filearea === 'course_intro') {
172dd12c 282 if ($CFG->forcelogin) {
172dd12c 283 require_login();
284 }
285
286 $relativepath = '/'.implode('/', $args);
d92e7c4d 287 $fullpath = $context->id.'course_intro0'.$relativepath;
172dd12c 288
d92e7c4d 289 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
9e5fa330 290 send_file_not_found();
172dd12c 291 }
292
56949c17 293 session_get_instance()->write_close(); // unlock session during fileserving
d92e7c4d 294 send_stored_file($file, 60*60, 0, false); // TODO: change timeout?
172dd12c 295
8bdc9cac
SH
296 } else if ($filearea === 'course_summary') {
297
298 if ($CFG->forcelogin) {
299 require_login();
300 }
301
302 $fullpath = $context->id.$filearea.implode('/', $args);
03221650 303
8bdc9cac
SH
304 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
305 send_file_not_found();
306 }
307
308 session_get_instance()->write_close(); // unlock session during fileserving
309 send_stored_file($file, 60*60, 0, $forcedownload); // TODO: change timeout?
310
311 } else if ($filearea === 'course_grade_tree_feedback') {
312
313 if ($CFG->forcelogin || $course->id !== SITEID) {
314 require_login($course);
315 }
316
317 $fullpath = $context->id.$filearea.implode('/', $args);
318
319 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
320 send_file_not_found();
321 }
322
323 session_get_instance()->write_close(); // unlock session during fileserving
324 send_stored_file($file, 60*60, 0, $forcedownload); // TODO: change timeout?
325
76d9df3f
SH
326 } else if ($filearea === 'calendar_event_description') { // CONTEXT_COURSE
327
328 // This is for content used in course and group events
329
330 // Respect forcelogin and require login unless this is the site.... it probably
331 // should NEVER be the site
332 if ($CFG->forcelogin || $course->id !== SITEID) {
333 require_login($course);
334 }
335
336 // Must be able to at least view the course
337 if (!has_capability('moodle/course:view', $context)) {
338 send_file_not_found();
339 }
340
341 // Get the event id
342 $eventid = array_shift($args);
343 if ((int)$eventid <= 0) {
344 send_file_not_found();
345 }
346
347 // Load the event from the database we need to check whether it is
348 // a) valid
349 // b) a group event
350 // Group events use the course context (there is no group context)
351 $event = $DB->get_record('event', array('id'=>(int)$eventid));
352 if (!$event || $event->userid !== $USER->id) {
353 send_file_not_found();
354 }
355
356 // If its a group event require either membership of manage groups capability
357 if ($event->eventtype === 'group' && !has_capability('moodle/course:managegroups', $context) && !groups_is_member($event->groupid, $USER->id)) {
358 send_file_not_found();
359 }
360
361 // If we get this far we can serve the file
362 $fullpath = $context->id.$filearea.$eventid.'/'.implode('/', $args);
363 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
364 send_file_not_found();
365 }
366
367 session_get_instance()->write_close(); // unlock session during fileserving
368 send_stored_file($file, 60*60, 0, $forcedownload); // TODO: change timeout?
369
d92e7c4d 370 } else if ($filearea === 'course_section') {
371 if ($CFG->forcelogin) {
172dd12c 372 require_login($course);
d92e7c4d 373 } else if ($course->id !== SITEID) {
374 require_login($course);
375 }
106f3b67 376
d92e7c4d 377 $sectionid = (int)array_shift($args);
106f3b67 378
d92e7c4d 379 if ($course->numsections < $sectionid) {
380 if (!has_capability('moodle/course:update', $context)) {
381 // disable access to invisible sections if can not edit course
382 // this is going to break some ugly hacks, but is necessary
106f3b67 383 send_file_not_found();
384 }
d92e7c4d 385 }
106f3b67 386
d92e7c4d 387 $relativepath = '/'.implode('/', $args);
388 $fullpath = $context->id.'course_section'.$sectionid.$relativepath;
172dd12c 389
d92e7c4d 390 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
391 send_file_not_found();
392 }
172dd12c 393
d92e7c4d 394 session_get_instance()->write_close(); // unlock session during fileserving
395 send_stored_file($file, 60*60, 0, false); // TODO: change timeout?
172dd12c 396
d92e7c4d 397 } else if ($filearea === 'user_profile') {
398 $userid = (int)array_shift($args);
399 $usercontext = get_context_instance(CONTEXT_USER, $userid);
3156b8ca 400
d92e7c4d 401 if (!empty($CFG->forceloginforprofiles)) {
402 require_login();
403 if (isguestuser()) {
404 print_error('noguest');
3156b8ca 405 }
406
d92e7c4d 407 if (!isteacherinanycourse()
408 and !isteacherinanycourse($userid)
409 and !has_capability('moodle/user:viewdetails', $usercontext)) {
410 print_error('usernotavailable');
3156b8ca 411 }
d92e7c4d 412 if (!has_capability('moodle/user:viewdetails', $context) &&
413 !has_capability('moodle/user:viewdetails', $usercontext)) {
414 print_error('cannotviewprofile');
3156b8ca 415 }
d92e7c4d 416 if (!has_capability('moodle/course:view', $context, $userid, false)) {
417 print_error('notenrolledprofile');
106f3b67 418 }
d92e7c4d 419 if (groups_get_course_groupmode($course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
420 print_error('groupnotamember');
106f3b67 421 }
d92e7c4d 422 }
106f3b67 423
d92e7c4d 424 $relativepath = '/'.implode('/', $args);
425 $fullpath = $usercontext->id.'user_profile0'.$relativepath;
106f3b67 426
d92e7c4d 427 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
106f3b67 428 send_file_not_found();
429 }
172dd12c 430
d92e7c4d 431 session_get_instance()->write_close(); // unlock session during fileserving
432 send_stored_file($file, 0, 0, true); // must force download - security!
106f3b67 433
d92e7c4d 434 } else {
435 send_file_not_found();
436 }
437
438} else if ($context->contextlevel == CONTEXT_MODULE) {
439
440 if (!$coursecontext = get_context_instance_by_id(get_parent_contextid($context))) {
441 send_file_not_found();
442 }
443
444 if (!$course = $DB->get_record('course', array('id'=>$coursecontext->instanceid))) {
445 send_file_not_found();
446 }
447 $modinfo = get_fast_modinfo($course);
448 if (empty($modinfo->cms[$context->instanceid])) {
449 send_file_not_found();
450 }
451
452 $cminfo = $modinfo->cms[$context->instanceid];
453 $modname = $cminfo->modname;
454 $libfile = "$CFG->dirroot/mod/$modname/lib.php";
455 if (!file_exists($libfile)) {
456 send_file_not_found();
457 }
172dd12c 458
d92e7c4d 459 require_once($libfile);
460 if ($filearea === $modname.'_intro') {
461 if (!plugin_supports('mod', $modname, FEATURE_MOD_INTRO, true)) {
9e5fa330 462 send_file_not_found();
172dd12c 463 }
57956cc3
PS
464 if (!$cm = get_coursemodule_from_instance($modname, $cminfo->instance, $course->id)) {
465 send_file_not_found();
466 }
467 require_course_login($course, true, $cm);
468
d92e7c4d 469 if (!$cminfo->uservisible) {
9e5fa330 470 send_file_not_found();
172dd12c 471 }
d92e7c4d 472 // all users may access it
473 $relativepath = '/'.implode('/', $args);
474 $fullpath = $context->id.$filearea.'0'.$relativepath;
172dd12c 475
d92e7c4d 476 $fs = get_file_storage();
477 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
dc5c2bd9 478 send_file_not_found();
479 }
480
d92e7c4d 481 $lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400;
dc5c2bd9 482
d92e7c4d 483 // finally send the file
484 send_stored_file($file, $lifetime, 0);
485 }
486
487 $filefunction = $modname.'_pluginfile';
488 if (function_exists($filefunction)) {
520199ea 489 // if the function exists, it must send the file and terminate. Whatever it returns leads to "not found"
490 $filefunction($course, $cminfo, $context, $filearea, $args, $forcedownload);
b287ea7a 491 }
520199ea 492
493 send_file_not_found();
172dd12c 494
d92e7c4d 495} else if ($context->contextlevel == CONTEXT_BLOCK) {
496 //not supported yet
497 send_file_not_found();
172dd12c 498
499
d92e7c4d 500} else {
501 send_file_not_found();
502}