MDL-28646 Web service: core_course_get_contents()

1. Implement core_course_get_contents web service
2. Implement callbacks in folder, resource, page and url modules
3. Implement download script for web service
This commit is contained in:
Dongsheng Cai 2011-11-25 14:44:13 +08:00 committed by Jerome Mouneyrac
parent 1de9151c6f
commit ec0d6ea2a9
13 changed files with 1354 additions and 768 deletions

View File

@ -33,6 +33,209 @@ require_once("$CFG->libdir/externallib.php");
*/
class core_course_external extends external_api {
/**
* Returns description of method parameters
* @return external_function_parameters
*/
public static function get_course_contents_parameters() {
return new external_function_parameters(
array('courseid' => new external_value(PARAM_INT, 'course id'),
'options' => new external_multiple_structure (
new external_single_structure(
array('name' => new external_value(PARAM_ALPHANUM, 'option name'),
'value' => new external_value(PARAM_RAW, 'the value of the option, this param is personaly validated in the external function.')
)
), 'Options, not used yet, might be used in later version', VALUE_DEFAULT, array())
)
);
}
/**
* Get course contents
* @param int $courseid
* @param array $options, not used yet, might be used in later version
* @return array
*/
public static function get_course_contents($courseid, $options) {
global $CFG, $DB;
require_once($CFG->dirroot . "/course/lib.php");
//validate parameter
$params = self::validate_parameters(self::get_course_contents_parameters(),
array('courseid' => $courseid, 'options' => $options));
//retrieve the course
$course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
//check course format exist
if (!file_exists($CFG->dirroot . '/course/format/' . $course->format . '/lib.php')) {
throw new moodle_exception('cannotgetcoursecontents', 'webservice', '', null, get_string('courseformatnotfound', 'error', '', $course->format));
} else {
require_once($CFG->dirroot . '/course/format/' . $course->format . '/lib.php');
}
// now security checks
$context = get_context_instance(CONTEXT_COURSE, $course->id);
try {
self::validate_context($context);
} catch (Exception $e) {
$exceptionparam = new stdClass();
$exceptionparam->message = $e->getMessage();
$exceptionparam->courseid = $course->id;
throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
}
$canupdatecourse = has_capability('moodle/course:update', $context);
//create return value
$coursecontents = array();
if ($canupdatecourse or $course->visible
or has_capability('moodle/course:viewhiddencourses', $context)) {
//retrieve sections
$modinfo = get_fast_modinfo($course);
$sections = get_all_sections($course->id);
//for each sections (first displayed to last displayed)
foreach ($sections as $key => $section) {
$showsection = (has_capability('moodle/course:viewhiddensections', $context) or $section->visible or !$course->hiddensections);
if (!$showsection) {
continue;
}
// reset $sectioncontents
$sectionvalues = array();
$sectionvalues['id'] = $section->id;
$sectionvalues['name'] = get_section_name($course, $section);
$summary = file_rewrite_pluginfile_urls($section->summary, 'webservice/pluginfile.php', $context->id, 'course', 'section', $section->id);
$sectionvalues['visible'] = $section->visible;
$sectionvalues['summary'] = format_text($summary, $section->summaryformat);
$sectioncontents = array();
//for each module of the section
foreach ($modinfo->sections[$section->section] as $cmid) { //matching /course/lib.php:print_section() logic
$cm = $modinfo->cms[$cmid];
// stop here if the module is not visible to the user
if (!$cm->uservisible) {
continue;
}
$module = array();
//common info (for people being able to see the module or availability dates)
$module['id'] = $cm->id;
$module['name'] = format_string($cm->name, true);
$module['modname'] = $cm->modname;
$module['modplural'] = $cm->modplural;
$module['modicon'] = $cm->get_icon_url()->out(false);
$module['indent'] = $cm->indent;
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
if (!empty($cm->showdescription)) {
$module['description'] = $cm->get_content();
}
//url of the module
$url = $cm->get_url();
if ($url) { //labels don't have url
$module['url'] = $cm->get_url()->out();
}
$canviewhidden = has_capability('moodle/course:viewhiddenactivities',
get_context_instance(CONTEXT_MODULE, $cm->id));
//user that can view hidden module should know about the visibility
$module['visible'] = $cm->visible;
//availability date (also send to user who can see hidden module when the showavailabilyt is ON)
if ($canupdatecourse or ($CFG->enableavailability && $canviewhidden && $cm->showavailability)) {
$module['availablefrom'] = $cm->availablefrom;
$module['availableuntil'] = $cm->availableuntil;
}
$baseurl = 'webservice/pluginfile.php';
//call $modulename_export_contents
//(each module callback take care about checking the capabilities)
require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
$getcontentfunction = $cm->modname.'_export_contents';
if (function_exists($getcontentfunction)) {
if ($contents = $getcontentfunction($cm, $baseurl)) {
$module['contents'] = $contents;
}
}
//assign result to $sectioncontents
$sectioncontents[] = $module;
}
$sectionvalues['modules'] = $sectioncontents;
// assign result to $coursecontents
$coursecontents[] = $sectionvalues;
}
}
return $coursecontents;
}
/**
* Returns description of method result value
* @return external_description
*/
public static function get_course_contents_returns() {
return new external_multiple_structure(
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'Section ID'),
'name' => new external_value(PARAM_TEXT, 'Section name'),
'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
'summary' => new external_value(PARAM_RAW, 'Section description'),
'modules' => new external_multiple_structure(
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'activity id'),
'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
'name' => new external_value(PARAM_TEXT, 'activity module name'),
'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
'modicon' => new external_value(PARAM_URL, 'activity icon url'),
'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
'availablefrom' => new external_value(PARAM_INT, 'module availability start date', VALUE_OPTIONAL),
'availableuntil' => new external_value(PARAM_INT, 'module availability en date', VALUE_OPTIONAL),
'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
'contents' => new external_multiple_structure(
new external_single_structure(
array(
// content info
'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
'filename'=> new external_value(PARAM_FILE, 'filename'),
'filepath'=> new external_value(PARAM_PATH, 'filepath'),
'filesize'=> new external_value(PARAM_INT, 'filesize'),
'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
'timecreated' => new external_value(PARAM_INT, 'Time created'),
'timemodified' => new external_value(PARAM_INT, 'Time modified'),
'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
// copyright related info
'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
'author' => new external_value(PARAM_TEXT, 'Content owner'),
'license' => new external_value(PARAM_TEXT, 'Content license'),
)
), VALUE_DEFAULT, array()
)
)
), 'list of module'
)
)
)
);
}
/**
* Returns description of method parameters
* @return external_function_parameters

View File

@ -164,6 +164,7 @@ $string['couldnotassignrole'] = 'A serious but unspecified error occurred while
$string['couldnotupdatenoexistinguser'] = 'Cannot update the user - user doesn\'t exist';
$string['countriesphpempty'] = 'Error: The file countries.php in language pack {$a} is empty or missing.';
$string['coursedoesnotbelongtocategory'] = 'The course doesn\'t belong to this category';
$string['courseformatnotfound'] = 'The course format \'{$a}\' doesn\'t exist or is not recognized';
$string['coursegroupunknown'] = 'Course corresponding to group {$a} not specified';
$string['courseidnotfound'] = 'Course id doesn\'t exist';
$string['coursemisconf'] = 'Course is misconfigured';

View File

@ -43,6 +43,7 @@ $string['apiexplorernotavalaible'] = 'API explorer not available yet.';
$string['arguments'] = 'Arguments';
$string['authmethod'] = 'Authentication method';
$string['cannotcreatetoken'] = 'No permission to create web service token for the service {$a}.';
$string['cannotgetcoursecontents'] = 'Cannot get course contents';
$string['configwebserviceplugins'] = 'For security reasons, only protocols that are in use should be enabled.';
$string['context'] = 'Context';
$string['createservicedescription'] = 'A service is a set of web service functions. You will allow the user to access to a new service. On the <strong>Add service</strong> page check \'Enable\' and \'Authorised users\' options. Select \'No required capability\'.';
@ -74,6 +75,7 @@ $string['enableprotocols'] = 'Enable protocols';
$string['enableprotocolsdescription'] = 'At least one protocol should be enabled. For security reasons, only protocols that are to be used should be enabled.';
$string['enablews'] = 'Enable web services';
$string['enablewsdescription'] = 'Web services must be enabled in Advanced features.';
$string['enabledirectdownload'] = 'Web service file downloading must be enabled in external service settings';
$string['entertoken'] = 'Enter a security key/token:';
$string['error'] = 'Error: {$a}';
$string['errorcatcontextnotvalid'] = 'You cannot execute functions in the category context (category id:{$a->catid}). The context error message was: {$a->message}';
@ -177,6 +179,7 @@ $string['token'] = 'Token';
$string['tokenauthlog'] = 'Token authentication';
$string['tokencreatedbyadmin'] = 'Can only be reset by administrator (*)';
$string['tokencreator'] = 'Creator';
$string['unknownoptionkey'] = 'Unknown option key ({$a})';
$string['updateusersettings'] = 'Update';
$string['userasclients'] = 'Users as clients with token';
$string['userasclientsdescription'] = 'The following steps help you to set up the Moodle web service for users as clients. These steps also help to set up the recommended token (security keys) authentication method. In this use case, the user will generate his token from the security keys page via My profile settings.';

View File

@ -404,6 +404,15 @@ $functions = array(
'capabilities'=> 'moodle/course:create,moodle/course:visibility',
),
'core_course_get_contents' => array(
'classname' => 'core_course_external',
'methodname' => 'get_course_contents',
'classpath' => 'course/externallib.php',
'description' => 'Get course contents',
'type' => 'read',
'capabilities'=> 'moodle/course:update,moodle/course:viewhiddencourses',
),
// === message related functions ===
'moodle_message_send_instantmessages' => array(
@ -474,7 +483,8 @@ $services = array(
'moodle_notes_create_notes',
'moodle_user_get_course_participants_by_id',
'moodle_user_get_users_by_courseid',
'moodle_message_send_instantmessages'),
'moodle_message_send_instantmessages',
'core_course_get_contents'),
'enabled' => 0,
'restrictedusers' => 0,
'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE,

View File

@ -3137,3 +3137,769 @@ class filetype_parser {
return $this->result;
}
}
/**
* This function delegates file serving to individual plugins
*
* @param string $relativepath
* @param bool $forcedownload
*
* @package core
* @subpackage file
* @copyright 2008 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function file_pluginfile($relativepath, $forcedownload) {
global $DB, $CFG, $USER;
// relative path must start with '/'
if (!$relativepath) {
print_error('invalidargorconf');
} else if ($relativepath[0] != '/') {
print_error('pathdoesnotstartslash');
}
// extract relative path components
$args = explode('/', ltrim($relativepath, '/'));
if (count($args) < 3) { // always at least context, component and filearea
print_error('invalidarguments');
}
$contextid = (int)array_shift($args);
$component = clean_param(array_shift($args), PARAM_COMPONENT);
$filearea = clean_param(array_shift($args), PARAM_AREA);
list($context, $course, $cm) = get_context_info_array($contextid);
$fs = get_file_storage();
// ========================================================================================================================
if ($component === 'blog') {
// Blog file serving
if ($context->contextlevel != CONTEXT_SYSTEM) {
send_file_not_found();
}
if ($filearea !== 'attachment' and $filearea !== 'post') {
send_file_not_found();
}
if (empty($CFG->bloglevel)) {
print_error('siteblogdisable', 'blog');
}
if ($CFG->bloglevel < BLOG_GLOBAL_LEVEL) {
require_login();
if (isguestuser()) {
print_error('noguest');
}
if ($CFG->bloglevel == BLOG_USER_LEVEL) {
if ($USER->id != $entry->userid) {
send_file_not_found();
}
}
}
$entryid = (int)array_shift($args);
if (!$entry = $DB->get_record('post', array('module'=>'blog', 'id'=>$entryid))) {
send_file_not_found();
}
if ('publishstate' === 'public') {
if ($CFG->forcelogin) {
require_login();
}
} else if ('publishstate' === 'site') {
require_login();
//ok
} else if ('publishstate' === 'draft') {
require_login();
if ($USER->id != $entry->userid) {
send_file_not_found();
}
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, $entryid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
send_stored_file($file, 10*60, 0, true); // download MUST be forced - security!
// ========================================================================================================================
} else if ($component === 'grade') {
if (($filearea === 'outcome' or $filearea === 'scale') and $context->contextlevel == CONTEXT_SYSTEM) {
// Global gradebook files
if ($CFG->forcelogin) {
require_login();
}
$fullpath = "/$context->id/$component/$filearea/".implode('/', $args);
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'feedback' and $context->contextlevel == CONTEXT_COURSE) {
//TODO: nobody implemented this yet in grade edit form!!
send_file_not_found();
if ($CFG->forcelogin || $course->id != SITEID) {
require_login($course);
}
$fullpath = "/$context->id/$component/$filearea/".implode('/', $args);
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'tag') {
if ($filearea === 'description' and $context->contextlevel == CONTEXT_SYSTEM) {
// All tag descriptions are going to be public but we still need to respect forcelogin
if ($CFG->forcelogin) {
require_login();
}
$fullpath = "/$context->id/tag/description/".implode('/', $args);
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, true);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'calendar') {
if ($filearea === 'event_description' and $context->contextlevel == CONTEXT_SYSTEM) {
// All events here are public the one requirement is that we respect forcelogin
if ($CFG->forcelogin) {
require_login();
}
// Get the event if from the args array
$eventid = array_shift($args);
// Load the event from the database
if (!$event = $DB->get_record('event', array('id'=>(int)$eventid, 'eventtype'=>'site'))) {
send_file_not_found();
}
// Check that we got an event and that it's userid is that of the user
// Get the file and serve if successful
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, $eventid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'event_description' and $context->contextlevel == CONTEXT_USER) {
// Must be logged in, if they are not then they obviously can't be this user
require_login();
// Don't want guests here, potentially saves a DB call
if (isguestuser()) {
send_file_not_found();
}
// Get the event if from the args array
$eventid = array_shift($args);
// Load the event from the database - user id must match
if (!$event = $DB->get_record('event', array('id'=>(int)$eventid, 'userid'=>$USER->id, 'eventtype'=>'user'))) {
send_file_not_found();
}
// Get the file and serve if successful
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, $eventid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'event_description' and $context->contextlevel == CONTEXT_COURSE) {
// Respect forcelogin and require login unless this is the site.... it probably
// should NEVER be the site
if ($CFG->forcelogin || $course->id != SITEID) {
require_login($course);
}
// Must be able to at least view the course
if (!is_enrolled($context) and !is_viewing($context)) {
//TODO: hmm, do we really want to block guests here?
send_file_not_found();
}
// Get the event id
$eventid = array_shift($args);
// Load the event from the database we need to check whether it is
// a) valid course event
// b) a group event
// Group events use the course context (there is no group context)
if (!$event = $DB->get_record('event', array('id'=>(int)$eventid, 'courseid'=>$course->id))) {
send_file_not_found();
}
// If its a group event require either membership of view all groups capability
if ($event->eventtype === 'group') {
if (!has_capability('moodle/site:accessallgroups', $context) && !groups_is_member($event->groupid, $USER->id)) {
send_file_not_found();
}
} else if ($event->eventtype === 'course') {
//ok
} else {
// some other type
send_file_not_found();
}
// If we get this far we can serve the file
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, $eventid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'user') {
if ($filearea === 'icon' and $context->contextlevel == CONTEXT_USER) {
$redirect = false;
if (count($args) == 1) {
$themename = theme_config::DEFAULT_THEME;
$filename = array_shift($args);
} else {
$themename = array_shift($args);
$filename = array_shift($args);
}
if ((!empty($CFG->forcelogin) and !isloggedin())) {
// protect images if login required and not logged in;
// do not use require_login() because it is expensive and not suitable here anyway
$redirect = true;
}
if (!$redirect and ($filename !== 'f1' and $filename !== 'f2')) {
$filename = 'f1';
$redirect = true;
}
if (!$redirect && !$file = $fs->get_file($context->id, 'user', 'icon', 0, '/', $filename.'/.png')) {
if (!$file = $fs->get_file($context->id, 'user', 'icon', 0, '/', $filename.'/.jpg')) {
$redirect = true;
}
}
if ($redirect) {
$theme = theme_config::load($themename);
redirect($theme->pix_url('u/'.$filename, 'moodle'));
}
send_stored_file($file, 60*60*24); // enable long caching, there are many images on each page
} else if ($filearea === 'private' and $context->contextlevel == CONTEXT_USER) {
require_login();
if (isguestuser()) {
send_file_not_found();
}
if ($USER->id !== $context->instanceid) {
send_file_not_found();
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, true); // must force download - security!
} else if ($filearea === 'profile' and $context->contextlevel == CONTEXT_USER) {
if ($CFG->forcelogin) {
require_login();
}
$userid = $context->instanceid;
if ($USER->id == $userid) {
// always can access own
} else if (!empty($CFG->forceloginforprofiles)) {
require_login();
if (isguestuser()) {
send_file_not_found();
}
// we allow access to site profile of all course contacts (usually teachers)
if (!has_coursecontact_role($userid) && !has_capability('moodle/user:viewdetails', $context)) {
send_file_not_found();
}
$canview = false;
if (has_capability('moodle/user:viewdetails', $context)) {
$canview = true;
} else {
$courses = enrol_get_my_courses();
}
while (!$canview && count($courses) > 0) {
$course = array_shift($courses);
if (has_capability('moodle/user:viewdetails', get_context_instance(CONTEXT_COURSE, $course->id))) {
$canview = true;
}
}
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, true); // must force download - security!
} else if ($filearea === 'profile' and $context->contextlevel == CONTEXT_COURSE) {
$userid = (int)array_shift($args);
$usercontext = get_context_instance(CONTEXT_USER, $userid);
if ($CFG->forcelogin) {
require_login();
}
if (!empty($CFG->forceloginforprofiles)) {
require_login();
if (isguestuser()) {
print_error('noguest');
}
//TODO: review this logic of user profile access prevention
if (!has_coursecontact_role($userid) and !has_capability('moodle/user:viewdetails', $usercontext)) {
print_error('usernotavailable');
}
if (!has_capability('moodle/user:viewdetails', $context) && !has_capability('moodle/user:viewdetails', $usercontext)) {
print_error('cannotviewprofile');
}
if (!is_enrolled($context, $userid)) {
print_error('notenrolledprofile');
}
if (groups_get_course_groupmode($course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
print_error('groupnotamember');
}
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($usercontext->id, 'user', 'profile', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, true); // must force download - security!
} else if ($filearea === 'backup' and $context->contextlevel == CONTEXT_USER) {
require_login();
if (isguestuser()) {
send_file_not_found();
}
$userid = $context->instanceid;
if ($USER->id != $userid) {
send_file_not_found();
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'user', 'backup', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, true); // must force download - security!
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'coursecat') {
if ($context->contextlevel != CONTEXT_COURSECAT) {
send_file_not_found();
}
if ($filearea === 'description') {
if ($CFG->forcelogin) {
// no login necessary - unless login forced everywhere
require_login();
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'coursecat', 'description', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'course') {
if ($context->contextlevel != CONTEXT_COURSE) {
send_file_not_found();
}
if ($filearea === 'summary') {
if ($CFG->forcelogin) {
require_login();
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'course', 'summary', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'section') {
if ($CFG->forcelogin) {
require_login($course);
} else if ($course->id != SITEID) {
require_login($course);
}
$sectionid = (int)array_shift($args);
if (!$section = $DB->get_record('course_sections', array('id'=>$sectionid, 'course'=>$course->id))) {
send_file_not_found();
}
if ($course->numsections < $section->section) {
if (!has_capability('moodle/course:update', $context)) {
// block access to unavailable sections if can not edit course
send_file_not_found();
}
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'course', 'section', $sectionid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else {
send_file_not_found();
}
} else if ($component === 'group') {
if ($context->contextlevel != CONTEXT_COURSE) {
send_file_not_found();
}
require_course_login($course, true, null, false);
$groupid = (int)array_shift($args);
$group = $DB->get_record('groups', array('id'=>$groupid, 'courseid'=>$course->id), '*', MUST_EXIST);
if (($course->groupmodeforce and $course->groupmode == SEPARATEGROUPS) and !has_capability('moodle/site:accessallgroups', $context) and !groups_is_member($group->id, $USER->id)) {
// do not allow access to separate group info if not member or teacher
send_file_not_found();
}
if ($filearea === 'description') {
require_login($course);
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'group', 'description', $group->id, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'icon') {
$filename = array_pop($args);
if ($filename !== 'f1' and $filename !== 'f2') {
send_file_not_found();
}
if (!$file = $fs->get_file($context->id, 'group', 'icon', $group->id, '/', $filename.'.png')) {
if (!$file = $fs->get_file($context->id, 'group', 'icon', $group->id, '/', $filename.'.jpg')) {
send_file_not_found();
}
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60);
} else {
send_file_not_found();
}
} else if ($component === 'grouping') {
if ($context->contextlevel != CONTEXT_COURSE) {
send_file_not_found();
}
require_login($course);
$groupingid = (int)array_shift($args);
// note: everybody has access to grouping desc images for now
if ($filearea === 'description') {
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'grouping', 'description', $groupingid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'backup') {
if ($filearea === 'course' and $context->contextlevel == CONTEXT_COURSE) {
require_login($course);
require_capability('moodle/backup:downloadfile', $context);
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'backup', 'course', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, $forcedownload);
} else if ($filearea === 'section' and $context->contextlevel == CONTEXT_COURSE) {
require_login($course);
require_capability('moodle/backup:downloadfile', $context);
$sectionid = (int)array_shift($args);
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'backup', 'section', $sectionid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close();
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'activity' and $context->contextlevel == CONTEXT_MODULE) {
require_login($course, false, $cm);
require_capability('moodle/backup:downloadfile', $context);
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'backup', 'activity', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close();
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'automated' and $context->contextlevel == CONTEXT_COURSE) {
// Backup files that were generated by the automated backup systems.
require_login($course);
require_capability('moodle/site:config', $context);
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'backup', 'automated', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, $forcedownload);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'question') {
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'question', $filearea, $args, $forcedownload);
send_file_not_found();
// ========================================================================================================================
} else if ($component === 'grading') {
if ($filearea === 'description') {
// files embedded into the form definition description
if ($context->contextlevel == CONTEXT_SYSTEM) {
require_login();
} else if ($context->contextlevel >= CONTEXT_COURSE) {
require_login($course, false, $cm);
} else {
send_file_not_found();
}
$formid = (int)array_shift($args);
$sql = "SELECT ga.id
FROM {grading_areas} ga
JOIN {grading_definitions} gd ON (gd.areaid = ga.id)
WHERE gd.id = ? AND ga.contextid = ?";
$areaid = $DB->get_field_sql($sql, array($formid, $context->id), IGNORE_MISSING);
if (!$areaid) {
send_file_not_found();
}
$fullpath = "/$context->id/$component/$filearea/$formid/".implode('/', $args);
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
}
// ========================================================================================================================
} else if (strpos($component, 'mod_') === 0) {
$modname = substr($component, 4);
if (!file_exists("$CFG->dirroot/mod/$modname/lib.php")) {
send_file_not_found();
}
require_once("$CFG->dirroot/mod/$modname/lib.php");
if ($context->contextlevel == CONTEXT_MODULE) {
if ($cm->modname !== $modname) {
// somebody tries to gain illegal access, cm type must match the component!
send_file_not_found();
}
}
if ($filearea === 'intro') {
if (!plugin_supports('mod', $modname, FEATURE_MOD_INTRO, true)) {
send_file_not_found();
}
require_course_login($course, true, $cm);
// all users may access it
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'mod_'.$modname, 'intro', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
$lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400;
// finally send the file
send_stored_file($file, $lifetime, 0);
}
$filefunction = $component.'_pluginfile';
$filefunctionold = $modname.'_pluginfile';
if (function_exists($filefunction)) {
// if the function exists, it must send the file and terminate. Whatever it returns leads to "not found"
$filefunction($course, $cm, $context, $filearea, $args, $forcedownload);
} else if (function_exists($filefunctionold)) {
// if the function exists, it must send the file and terminate. Whatever it returns leads to "not found"
$filefunctionold($course, $cm, $context, $filearea, $args, $forcedownload);
}
send_file_not_found();
// ========================================================================================================================
} else if (strpos($component, 'block_') === 0) {
$blockname = substr($component, 6);
// note: no more class methods in blocks please, that is ....
if (!file_exists("$CFG->dirroot/blocks/$blockname/lib.php")) {
send_file_not_found();
}
require_once("$CFG->dirroot/blocks/$blockname/lib.php");
if ($context->contextlevel == CONTEXT_BLOCK) {
$birecord = $DB->get_record('block_instances', array('id'=>$context->instanceid), '*',MUST_EXIST);
if ($birecord->blockname !== $blockname) {
// somebody tries to gain illegal access, cm type must match the component!
send_file_not_found();
}
} else {
$birecord = null;
}
$filefunction = $component.'_pluginfile';
if (function_exists($filefunction)) {
// if the function exists, it must send the file and terminate. Whatever it returns leads to "not found"
$filefunction($course, $birecord, $context, $filearea, $args, $forcedownload);
}
send_file_not_found();
} else if (strpos($component, '_') === false) {
// all core subsystems have to be specified above, no more guessing here!
send_file_not_found();
} else {
// try to serve general plugin file in arbitrary context
$dir = get_component_directory($component);
if (!file_exists("$dir/lib.php")) {
send_file_not_found();
}
include_once("$dir/lib.php");
$filefunction = $component.'_pluginfile';
if (function_exists($filefunction)) {
// if the function exists, it must send the file and terminate. Whatever it returns leads to "not found"
$filefunction($course, $cm, $context, $filearea, $args, $forcedownload);
}
send_file_not_found();
}
}

View File

@ -348,3 +348,36 @@ function folder_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-folder-*'=>get_string('page-mod-folder-x', 'folder'));
return $module_pagetype;
}
/**
* Export folder resource contents
*
* @return array of file content
*/
function folder_export_contents($cm, $baseurl) {
global $CFG, $DB;
$contents = array();
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
$folder = $DB->get_record('folder', array('id'=>$cm->instance), '*', MUST_EXIST);
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'mod_folder', 'content', 0, 'sortorder DESC, id ASC', false);
foreach ($files as $fileinfo) {
$file = array();
$file['type'] = 'file';
$file['filename'] = $fileinfo->get_filename();
$file['filepath'] = $fileinfo->get_filepath();
$file['filesize'] = $fileinfo->get_filesize();
$file['fileurl'] = file_encode_url("$CFG->wwwroot/" . $baseurl, '/'.$context->id.'/mod_folder/content/'.$folder->revision.$fileinfo->get_filepath().$fileinfo->get_filename(), true);
$file['timecreated'] = $fileinfo->get_timecreated();
$file['timemodified'] = $fileinfo->get_timemodified();
$file['sortorder'] = $fileinfo->get_sortorder();
$file['userid'] = $fileinfo->get_userid();
$file['author'] = $fileinfo->get_author();
$file['license'] = $fileinfo->get_license();
$contents[] = $file;
}
return $contents;
}

View File

@ -369,8 +369,27 @@ function page_pluginfile($course, $cm, $context, $filearea, $args, $forcedownloa
return false;
}
array_shift($args); // ignore revision - designed to prevent caching problems only
// $arg could be revision number or index.html
$arg = array_shift($args);
if ($arg == 'index.html' || $arg == 'index.htm') {
// serve page content
$filename = $arg;
if (!$page = $DB->get_record('page', array('id'=>$cm->instance), '*', MUST_EXIST)) {
return false;
}
// remove @@PLUGINFILE@@/
$content = str_replace('@@PLUGINFILE@@/', '', $page->content);
$formatoptions = new stdClass;
$formatoptions->noclean = true;
$formatoptions->overflowdiv = true;
$formatoptions->context = $context;
$content = format_text($content, $page->contentformat, $formatoptions);
send_file($content, $filename, 0, 0, true, true);
} else {
$fs = get_file_storage();
$relativepath = implode('/', $args);
$fullpath = "/$context->id/mod_page/$filearea/0/$relativepath";
@ -390,6 +409,7 @@ function page_pluginfile($course, $cm, $context, $filearea, $args, $forcedownloa
// finally send the file
send_stored_file($file, 86400, 0, $forcedownload);
}
}
/**
@ -422,3 +442,54 @@ function page_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-page-*'=>get_string('page-mod-page-x', 'page'));
return $module_pagetype;
}
/**
* Export page resource contents
*
* @return array of file content
*/
function page_export_contents($cm, $baseurl) {
global $CFG, $DB;
$contents = array();
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
$page = $DB->get_record('page', array('id'=>$cm->instance), '*', MUST_EXIST);
// page contents
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'mod_page', 'content', 0, 'sortorder DESC, id ASC', false);
foreach ($files as $fileinfo) {
$file = array();
$file['type'] = 'file';
$file['filename'] = $fileinfo->get_filename();
$file['filepath'] = $fileinfo->get_filepath();
$file['filesize'] = $fileinfo->get_filesize();
$file['fileurl'] = file_encode_url("$CFG->wwwroot/" . $baseurl, '/'.$context->id.'/mod_page/content/'.$page->revision.$fileinfo->get_filepath().$fileinfo->get_filename(), true);
$file['timecreated'] = $fileinfo->get_timecreated();
$file['timemodified'] = $fileinfo->get_timemodified();
$file['sortorder'] = $fileinfo->get_sortorder();
$file['userid'] = $fileinfo->get_userid();
$file['author'] = $fileinfo->get_author();
$file['license'] = $fileinfo->get_license();
$contents[] = $file;
}
// page html conent
$filename = 'index.html';
$pagefile = array();
$pagefile['type'] = 'file';
$pagefile['filename'] = $filename;
$pagefile['filepath'] = '/';
$pagefile['filesize'] = 0;
$pagefile['fileurl'] = file_encode_url("$CFG->wwwroot/" . $baseurl, '/'.$context->id.'/mod_page/content/' . $filename, true);
$pagefile['timecreated'] = null;
$pagefile['timemodified'] = $page->timemodified;
// make this file as main file
$pagefile['sortorder'] = 1;
$pagefile['userid'] = null;
$pagefile['author'] = null;
$pagefile['license'] = null;
$contents[] = $pagefile;
return $contents;
}

View File

@ -446,3 +446,36 @@ function resource_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-resource-*'=>get_string('page-mod-resource-x', 'resource'));
return $module_pagetype;
}
/**
* Export file resource contents
*
* @return array of file content
*/
function resource_export_contents($cm, $baseurl) {
global $CFG, $DB;
$contents = array();
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
$resource = $DB->get_record('resource', array('id'=>$cm->instance), '*', MUST_EXIST);
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'mod_resource', 'content', 0, 'sortorder DESC, id ASC', false);
foreach ($files as $fileinfo) {
$file = array();
$file['type'] = 'file';
$file['filename'] = $fileinfo->get_filename();
$file['filepath'] = $fileinfo->get_filepath();
$file['filesize'] = $fileinfo->get_filesize();
$file['fileurl'] = file_encode_url("$CFG->wwwroot/" . $baseurl, '/'.$context->id.'/mod_resource/content/'.$resource->revision.$fileinfo->get_filepath().$fileinfo->get_filename(), true);
$file['timecreated'] = $fileinfo->get_timecreated();
$file['timemodified'] = $fileinfo->get_timemodified();
$file['sortorder'] = $fileinfo->get_sortorder();
$file['userid'] = $fileinfo->get_userid();
$file['author'] = $fileinfo->get_author();
$file['license'] = $fileinfo->get_license();
$contents[] = $file;
}
return $contents;
}

View File

@ -329,3 +329,40 @@ function url_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-url-*'=>get_string('page-mod-url-x', 'url'));
return $module_pagetype;
}
/**
* Export URL resource contents
*
* @return array of file content
*/
function url_export_contents($cm, $baseurl) {
global $CFG, $DB;
require_once("$CFG->dirroot/mod/url/locallib.php");
$contents = array();
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
$course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST);
$url = $DB->get_record('url', array('id'=>$cm->instance), '*', MUST_EXIST);
$fullurl = str_replace('&amp;', '&', url_get_full_url($url, $cm, $course));
$isurl = clean_param($fullurl, PARAM_URL);
if (empty($isurl)) {
return null;
}
$url = array();
$url['type'] = 'url';
$url['filename'] = $url->name;
$url['filepath'] = null;
$url['filesize'] = 0;
$url['fileurl'] = $fullurl;
$url['timecreated'] = null;
$url['timemodified'] = $url->timemodified;
$url['sortorder'] = null;
$url['userid'] = null;
$url['author'] = null;
$url['license'] = null;
$contents[] = $url;
return $contents;
}

View File

@ -34,753 +34,4 @@ require_once('lib/filelib.php');
$relativepath = get_file_argument();
$forcedownload = optional_param('forcedownload', 0, PARAM_BOOL);
// relative path must start with '/'
if (!$relativepath) {
print_error('invalidargorconf');
} else if ($relativepath[0] != '/') {
print_error('pathdoesnotstartslash');
}
// extract relative path components
$args = explode('/', ltrim($relativepath, '/'));
if (count($args) < 3) { // always at least context, component and filearea
print_error('invalidarguments');
}
$contextid = (int)array_shift($args);
$component = clean_param(array_shift($args), PARAM_COMPONENT);
$filearea = clean_param(array_shift($args), PARAM_AREA);
list($context, $course, $cm) = get_context_info_array($contextid);
$fs = get_file_storage();
// ========================================================================================================================
if ($component === 'blog') {
// Blog file serving
if ($context->contextlevel != CONTEXT_SYSTEM) {
send_file_not_found();
}
if ($filearea !== 'attachment' and $filearea !== 'post') {
send_file_not_found();
}
if (empty($CFG->bloglevel)) {
print_error('siteblogdisable', 'blog');
}
if ($CFG->bloglevel < BLOG_GLOBAL_LEVEL) {
require_login();
if (isguestuser()) {
print_error('noguest');
}
if ($CFG->bloglevel == BLOG_USER_LEVEL) {
if ($USER->id != $entry->userid) {
send_file_not_found();
}
}
}
$entryid = (int)array_shift($args);
if (!$entry = $DB->get_record('post', array('module'=>'blog', 'id'=>$entryid))) {
send_file_not_found();
}
if ('publishstate' === 'public') {
if ($CFG->forcelogin) {
require_login();
}
} else if ('publishstate' === 'site') {
require_login();
//ok
} else if ('publishstate' === 'draft') {
require_login();
if ($USER->id != $entry->userid) {
send_file_not_found();
}
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, $entryid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
send_stored_file($file, 10*60, 0, true); // download MUST be forced - security!
// ========================================================================================================================
} else if ($component === 'grade') {
if (($filearea === 'outcome' or $filearea === 'scale') and $context->contextlevel == CONTEXT_SYSTEM) {
// Global gradebook files
if ($CFG->forcelogin) {
require_login();
}
$fullpath = "/$context->id/$component/$filearea/".implode('/', $args);
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'feedback' and $context->contextlevel == CONTEXT_COURSE) {
//TODO: nobody implemented this yet in grade edit form!!
send_file_not_found();
if ($CFG->forcelogin || $course->id != SITEID) {
require_login($course);
}
$fullpath = "/$context->id/$component/$filearea/".implode('/', $args);
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'tag') {
if ($filearea === 'description' and $context->contextlevel == CONTEXT_SYSTEM) {
// All tag descriptions are going to be public but we still need to respect forcelogin
if ($CFG->forcelogin) {
require_login();
}
$fullpath = "/$context->id/tag/description/".implode('/', $args);
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, true);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'calendar') {
if ($filearea === 'event_description' and $context->contextlevel == CONTEXT_SYSTEM) {
// All events here are public the one requirement is that we respect forcelogin
if ($CFG->forcelogin) {
require_login();
}
// Get the event if from the args array
$eventid = array_shift($args);
// Load the event from the database
if (!$event = $DB->get_record('event', array('id'=>(int)$eventid, 'eventtype'=>'site'))) {
send_file_not_found();
}
// Check that we got an event and that it's userid is that of the user
// Get the file and serve if successful
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, $eventid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'event_description' and $context->contextlevel == CONTEXT_USER) {
// Must be logged in, if they are not then they obviously can't be this user
require_login();
// Don't want guests here, potentially saves a DB call
if (isguestuser()) {
send_file_not_found();
}
// Get the event if from the args array
$eventid = array_shift($args);
// Load the event from the database - user id must match
if (!$event = $DB->get_record('event', array('id'=>(int)$eventid, 'userid'=>$USER->id, 'eventtype'=>'user'))) {
send_file_not_found();
}
// Get the file and serve if successful
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, $eventid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'event_description' and $context->contextlevel == CONTEXT_COURSE) {
// Respect forcelogin and require login unless this is the site.... it probably
// should NEVER be the site
if ($CFG->forcelogin || $course->id != SITEID) {
require_login($course);
}
// Must be able to at least view the course
if (!is_enrolled($context) and !is_viewing($context)) {
//TODO: hmm, do we really want to block guests here?
send_file_not_found();
}
// Get the event id
$eventid = array_shift($args);
// Load the event from the database we need to check whether it is
// a) valid course event
// b) a group event
// Group events use the course context (there is no group context)
if (!$event = $DB->get_record('event', array('id'=>(int)$eventid, 'courseid'=>$course->id))) {
send_file_not_found();
}
// If its a group event require either membership of view all groups capability
if ($event->eventtype === 'group') {
if (!has_capability('moodle/site:accessallgroups', $context) && !groups_is_member($event->groupid, $USER->id)) {
send_file_not_found();
}
} else if ($event->eventtype === 'course') {
//ok
} else {
// some other type
send_file_not_found();
}
// If we get this far we can serve the file
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, $eventid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'user') {
if ($filearea === 'icon' and $context->contextlevel == CONTEXT_USER) {
$redirect = false;
if (count($args) == 1) {
$themename = theme_config::DEFAULT_THEME;
$filename = array_shift($args);
} else {
$themename = array_shift($args);
$filename = array_shift($args);
}
if ((!empty($CFG->forcelogin) and !isloggedin())) {
// protect images if login required and not logged in;
// do not use require_login() because it is expensive and not suitable here anyway
$redirect = true;
}
if (!$redirect and ($filename !== 'f1' and $filename !== 'f2')) {
$filename = 'f1';
$redirect = true;
}
if (!$redirect && !$file = $fs->get_file($context->id, 'user', 'icon', 0, '/', $filename.'/.png')) {
if (!$file = $fs->get_file($context->id, 'user', 'icon', 0, '/', $filename.'/.jpg')) {
$redirect = true;
}
}
if ($redirect) {
$theme = theme_config::load($themename);
redirect($theme->pix_url('u/'.$filename, 'moodle'));
}
send_stored_file($file, 60*60*24); // enable long caching, there are many images on each page
} else if ($filearea === 'private' and $context->contextlevel == CONTEXT_USER) {
require_login();
if (isguestuser()) {
send_file_not_found();
}
if ($USER->id !== $context->instanceid) {
send_file_not_found();
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, true); // must force download - security!
} else if ($filearea === 'profile' and $context->contextlevel == CONTEXT_USER) {
if ($CFG->forcelogin) {
require_login();
}
$userid = $context->instanceid;
if ($USER->id == $userid) {
// always can access own
} else if (!empty($CFG->forceloginforprofiles)) {
require_login();
if (isguestuser()) {
send_file_not_found();
}
// we allow access to site profile of all course contacts (usually teachers)
if (!has_coursecontact_role($userid) && !has_capability('moodle/user:viewdetails', $context)) {
send_file_not_found();
}
$canview = false;
if (has_capability('moodle/user:viewdetails', $context)) {
$canview = true;
} else {
$courses = enrol_get_my_courses();
}
while (!$canview && count($courses) > 0) {
$course = array_shift($courses);
if (has_capability('moodle/user:viewdetails', get_context_instance(CONTEXT_COURSE, $course->id))) {
$canview = true;
}
}
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, $component, $filearea, 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, true); // must force download - security!
} else if ($filearea === 'profile' and $context->contextlevel == CONTEXT_COURSE) {
$userid = (int)array_shift($args);
$usercontext = get_context_instance(CONTEXT_USER, $userid);
if ($CFG->forcelogin) {
require_login();
}
if (!empty($CFG->forceloginforprofiles)) {
require_login();
if (isguestuser()) {
print_error('noguest');
}
//TODO: review this logic of user profile access prevention
if (!has_coursecontact_role($userid) and !has_capability('moodle/user:viewdetails', $usercontext)) {
print_error('usernotavailable');
}
if (!has_capability('moodle/user:viewdetails', $context) && !has_capability('moodle/user:viewdetails', $usercontext)) {
print_error('cannotviewprofile');
}
if (!is_enrolled($context, $userid)) {
print_error('notenrolledprofile');
}
if (groups_get_course_groupmode($course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
print_error('groupnotamember');
}
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($usercontext->id, 'user', 'profile', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, true); // must force download - security!
} else if ($filearea === 'backup' and $context->contextlevel == CONTEXT_USER) {
require_login();
if (isguestuser()) {
send_file_not_found();
}
$userid = $context->instanceid;
if ($USER->id != $userid) {
send_file_not_found();
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'user', 'backup', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, true); // must force download - security!
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'coursecat') {
if ($context->contextlevel != CONTEXT_COURSECAT) {
send_file_not_found();
}
if ($filearea === 'description') {
if ($CFG->forcelogin) {
// no login necessary - unless login forced everywhere
require_login();
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'coursecat', 'description', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'course') {
if ($context->contextlevel != CONTEXT_COURSE) {
send_file_not_found();
}
if ($filearea === 'summary') {
if ($CFG->forcelogin) {
require_login();
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'course', 'summary', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'section') {
if ($CFG->forcelogin) {
require_login($course);
} else if ($course->id != SITEID) {
require_login($course);
}
$sectionid = (int)array_shift($args);
if (!$section = $DB->get_record('course_sections', array('id'=>$sectionid, 'course'=>$course->id))) {
send_file_not_found();
}
if ($course->numsections < $section->section) {
if (!has_capability('moodle/course:update', $context)) {
// block access to unavailable sections if can not edit course
send_file_not_found();
}
}
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'course', 'section', $sectionid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else {
send_file_not_found();
}
} else if ($component === 'group') {
if ($context->contextlevel != CONTEXT_COURSE) {
send_file_not_found();
}
require_course_login($course, true, null, false);
$groupid = (int)array_shift($args);
$group = $DB->get_record('groups', array('id'=>$groupid, 'courseid'=>$course->id), '*', MUST_EXIST);
if (($course->groupmodeforce and $course->groupmode == SEPARATEGROUPS) and !has_capability('moodle/site:accessallgroups', $context) and !groups_is_member($group->id, $USER->id)) {
// do not allow access to separate group info if not member or teacher
send_file_not_found();
}
if ($filearea === 'description') {
require_login($course);
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'group', 'description', $group->id, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'icon') {
$filename = array_pop($args);
if ($filename !== 'f1' and $filename !== 'f2') {
send_file_not_found();
}
if (!$file = $fs->get_file($context->id, 'group', 'icon', $group->id, '/', $filename.'.png')) {
if (!$file = $fs->get_file($context->id, 'group', 'icon', $group->id, '/', $filename.'.jpg')) {
send_file_not_found();
}
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60);
} else {
send_file_not_found();
}
} else if ($component === 'grouping') {
if ($context->contextlevel != CONTEXT_COURSE) {
send_file_not_found();
}
require_login($course);
$groupingid = (int)array_shift($args);
// note: everybody has access to grouping desc images for now
if ($filearea === 'description') {
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'grouping', 'description', $groupingid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'backup') {
if ($filearea === 'course' and $context->contextlevel == CONTEXT_COURSE) {
require_login($course);
require_capability('moodle/backup:downloadfile', $context);
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'backup', 'course', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, $forcedownload);
} else if ($filearea === 'section' and $context->contextlevel == CONTEXT_COURSE) {
require_login($course);
require_capability('moodle/backup:downloadfile', $context);
$sectionid = (int)array_shift($args);
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'backup', 'section', $sectionid, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close();
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'activity' and $context->contextlevel == CONTEXT_MODULE) {
require_login($course, false, $cm);
require_capability('moodle/backup:downloadfile', $context);
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'backup', 'activity', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close();
send_stored_file($file, 60*60, 0, $forcedownload);
} else if ($filearea === 'automated' and $context->contextlevel == CONTEXT_COURSE) {
// Backup files that were generated by the automated backup systems.
require_login($course);
require_capability('moodle/site:config', $context);
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'backup', 'automated', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 0, 0, $forcedownload);
} else {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'question') {
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'question', $filearea, $args, $forcedownload);
send_file_not_found();
// ========================================================================================================================
} else if ($component === 'grading') {
if ($filearea === 'description') {
// files embedded into the form definition description
if ($context->contextlevel == CONTEXT_SYSTEM) {
require_login();
} else if ($context->contextlevel >= CONTEXT_COURSE) {
require_login($course, false, $cm);
} else {
send_file_not_found();
}
$formid = (int)array_shift($args);
$sql = "SELECT ga.id
FROM {grading_areas} ga
JOIN {grading_definitions} gd ON (gd.areaid = ga.id)
WHERE gd.id = ? AND ga.contextid = ?";
$areaid = $DB->get_field_sql($sql, array($formid, $context->id), IGNORE_MISSING);
if (!$areaid) {
send_file_not_found();
}
$fullpath = "/$context->id/$component/$filearea/$formid/".implode('/', $args);
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
session_get_instance()->write_close(); // unlock session during fileserving
send_stored_file($file, 60*60, 0, $forcedownload);
}
// ========================================================================================================================
} else if (strpos($component, 'mod_') === 0) {
$modname = substr($component, 4);
if (!file_exists("$CFG->dirroot/mod/$modname/lib.php")) {
send_file_not_found();
}
require_once("$CFG->dirroot/mod/$modname/lib.php");
if ($context->contextlevel == CONTEXT_MODULE) {
if ($cm->modname !== $modname) {
// somebody tries to gain illegal access, cm type must match the component!
send_file_not_found();
}
}
if ($filearea === 'intro') {
if (!plugin_supports('mod', $modname, FEATURE_MOD_INTRO, true)) {
send_file_not_found();
}
require_course_login($course, true, $cm);
// all users may access it
$filename = array_pop($args);
$filepath = $args ? '/'.implode('/', $args).'/' : '/';
if (!$file = $fs->get_file($context->id, 'mod_'.$modname, 'intro', 0, $filepath, $filename) or $file->is_directory()) {
send_file_not_found();
}
$lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400;
// finally send the file
send_stored_file($file, $lifetime, 0);
}
$filefunction = $component.'_pluginfile';
$filefunctionold = $modname.'_pluginfile';
if (function_exists($filefunction)) {
// if the function exists, it must send the file and terminate. Whatever it returns leads to "not found"
$filefunction($course, $cm, $context, $filearea, $args, $forcedownload);
} else if (function_exists($filefunctionold)) {
// if the function exists, it must send the file and terminate. Whatever it returns leads to "not found"
$filefunctionold($course, $cm, $context, $filearea, $args, $forcedownload);
}
send_file_not_found();
// ========================================================================================================================
} else if (strpos($component, 'block_') === 0) {
$blockname = substr($component, 6);
// note: no more class methods in blocks please, that is ....
if (!file_exists("$CFG->dirroot/blocks/$blockname/lib.php")) {
send_file_not_found();
}
require_once("$CFG->dirroot/blocks/$blockname/lib.php");
if ($context->contextlevel == CONTEXT_BLOCK) {
$birecord = $DB->get_record('block_instances', array('id'=>$context->instanceid), '*',MUST_EXIST);
if ($birecord->blockname !== $blockname) {
// somebody tries to gain illegal access, cm type must match the component!
send_file_not_found();
}
} else {
$birecord = null;
}
$filefunction = $component.'_pluginfile';
if (function_exists($filefunction)) {
// if the function exists, it must send the file and terminate. Whatever it returns leads to "not found"
$filefunction($course, $birecord, $context, $filearea, $args, $forcedownload);
}
send_file_not_found();
} else if (strpos($component, '_') === false) {
// all core subsystems have to be specified above, no more guessing here!
send_file_not_found();
} else {
// try to serve general plugin file in arbitrary context
$dir = get_component_directory($component);
if (!file_exists("$dir/lib.php")) {
send_file_not_found();
}
include_once("$dir/lib.php");
$filefunction = $component.'_pluginfile';
if (function_exists($filefunction)) {
// if the function exists, it must send the file and terminate. Whatever it returns leads to "not found"
$filefunction($course, $cm, $context, $filearea, $args, $forcedownload);
}
send_file_not_found();
}
file_pluginfile($relativepath, $forcedownload);

119
webservice/pluginfile.php Normal file
View File

@ -0,0 +1,119 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* A script to serve files from web service client
*
* @package core
* @subpackage file
* @copyright 2011 Dongsheng Cai <dongsheng@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('NO_MOODLE_COOKIES', true);
require_once(dirname(dirname(__FILE__)) . '/config.php');
require_once($CFG->libdir . '/filelib.php');
$relativepath = get_file_argument();
$token = required_param('token', PARAM_ALPHANUM);
// web service must be enabled to use this script
if (!$CFG->enablewebservices) {
print_error('enablewsdescription', 'webservice');
}
// Obtain token record
if (!$token = $DB->get_record('external_tokens', array('token'=>$token))) {
print_error('invalidtoken', 'webservice');
}
//retrieve web service record
$servicesql = 'SELECT s.*
FROM {external_services} s, {external_tokens} t
WHERE t.externalserviceid = s.id
AND t.token = ? AND t.userid = ? AND s.enabled = 1';
$service = $DB->get_record_sql($servicesql, array($token->token, $token->userid), MUST_EXIST);
$enabledfiledownload = (int)$service->downloadfiles;
if (empty($enabledfiledownload)) {
print_error('enabledirectdownload', 'webservice');
}
$user = $DB->get_record('user', array('id'=>$token->userid, 'deleted'=>0), '*', MUST_EXIST);
//Non admin can not authenticate if maintenance mode
$hassiteconfig = has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM), $user);
if (!empty($CFG->maintenance_enabled) and !$hassiteconfig) {
print_error('sitemaintenance', 'admin');
}
// Validate token date
if ($token->validuntil and $token->validuntil < time()) {
add_to_log(SITEID, 'webservice', get_string('tokenauthlog', 'webservice'), '' , get_string('invalidtimedtoken', 'webservice'), 0);
$DB->delete_records('external_tokens', array('token'=>$token->token));
print_error('invalidtimedtoken', 'webservice');
}
//assumes that if sid is set then there must be a valid associated session no matter the token type
if ($token->sid) {
$session = session_get_instance();
if (!$session->session_exists($token->sid)) {
$DB->delete_records('external_tokens', array('sid'=>$token->sid));
print_error('invalidtokensession', 'webservice');
}
}
// Check ip
if ($token->iprestriction and !address_in_subnet(getremoteaddr(), $token->iprestriction)) {
add_to_log(SITEID, 'webservice', get_string('tokenauthlog', 'webservice'), '' , get_string('failedtolog', 'webservice').": ".getremoteaddr(), 0);
print_error('invalidiptoken', 'webservice');
}
//only confirmed user should be able to call web service
if (empty($user->confirmed)) {
add_to_log(SITEID, 'webservice', 'user unconfirmed', '', $user->username);
print_error('usernotconfirmed', 'moodle', '', $user->username);
}
//check the user is suspended
if (!empty($user->suspended)) {
add_to_log(SITEID, 'webservice', 'user suspended', '', $user->username);
print_error('usersuspended', 'webservice');
}
//check if the auth method is nologin (in this case refuse connection)
if ($user->auth == 'nologin') {
add_to_log(SITEID, 'webservice', 'nologin auth attempt with web service', '', $user->username);
print_error('nologinauth', 'webservice');
}
$auth = get_auth_plugin($user->auth);
if (!empty($auth->config->expiration) and $auth->config->expiration == 1) {
$days2expire = $auth->password_expire($user->username);
if (intval($days2expire) < 0 ) {
add_to_log(SITEID, 'webservice', 'expired password', '', $user->username);
print_error('passwordisexpired', 'webservice');
}
}
// log token access
$DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id));
session_set_user($user);
file_pluginfile($relativepath, 0);

View File

@ -74,7 +74,8 @@ class webservice_test extends UnitTestCase {
'moodle_enrol_get_enrolled_users' => false,
'moodle_group_get_course_groups' => false,
'moodle_group_get_groupmembers' => false,
'moodle_webservice_get_siteinfo' => false
'moodle_webservice_get_siteinfo' => false,
'core_course_get_contents' => false
);
////// WRITE DB tests ////
@ -247,6 +248,29 @@ class webservice_test extends UnitTestCase {
$this->assertEqual(count($users), count($userids));
}
function core_course_get_contents($client) {
global $DB, $CFG;
$dbcourses = $DB->get_records('course');
$function = 'core_course_get_contents';
$coursecontents = array();
foreach ($dbcourses as $dbcourse) {
$params = array('courseid' => $dbcourse->id);
if (file_exists($CFG->dirroot . '/' . '/course/format/' . $dbcourse->format . '/lib.php')) {
$coursecontents = $client->call($function, $params);
}
//TODO: some unit tests to check that generated course content data test match what
// the web service function is returning.
//Realistic TODO: display the content of $coursecontents in your php log and check if you obtain
//what you are expecting
//varlog($coursecontents);
}
}
/**
* This test will:
* 1- create a user (core call)

View File

@ -30,6 +30,12 @@ $filepath = optional_param('filepath', '/', PARAM_PATH);
echo $OUTPUT->header();
//Non admin can not authenticate if maintenance mode
$hassiteconfig = has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM), $user);
if (!empty($CFG->maintenance_enabled) and !$hassiteconfig) {
throw new moodle_exception('sitemaintenance', 'admin');
}
// web service must be enabled to use this script
if (!$CFG->enablewebservices) {
throw new moodle_exception('enablewsdescription', 'webservice');
@ -63,6 +69,35 @@ if ($token->iprestriction and !address_in_subnet(getremoteaddr(), $token->iprest
$user = $DB->get_record('user', array('id'=>$token->userid, 'deleted'=>0), '*', MUST_EXIST);
//check if the auth method is nologin (in this case refuse connection)
if ($auth=='nologin') {
add_to_log(SITEID, 'webservice', 'nologin auth attempt with web service', '', $user->username);
throw new webservice_access_exception(get_string('nologinauth', 'webservice'));
}
//only confirmed user should be able to call web service
if (empty($user->confirmed)) {
add_to_log(SITEID, 'webservice', 'user unconfirmed', '', $user->username);
throw new webservice_access_exception(get_string('usernotconfirmed', 'moodle', $user->username));
}
//check the user is suspended
if (!empty($user->suspended)) {
add_to_log(SITEID, 'webservice', 'user suspended', '', $user->username);
throw new webservice_access_exception(get_string('usersuspended', 'webservice'));
}
// check if credentials have expired
$auth = get_auth_plugin($user->auth);
if (!empty($auth->config->expiration) and $auth->config->expiration == 1) {
$days2expire = $auth->password_expire($user->username);
if (intval($days2expire) < 0 ) {
add_to_log(SITEID, 'webservice', 'expired password', '', $user->username);
throw new webservice_access_exception(get_string('passwordisexpired', 'webservice'));
}
}
// log token access
$DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id));