Merge branch 'wip-MDL-33950-master' of git://github.com/marinaglancy/moodle

This commit is contained in:
Dan Poltawski
2012-07-10 14:56:46 +08:00
4 changed files with 155 additions and 202 deletions
+66 -46
View File
@@ -59,6 +59,7 @@ $search_text = optional_param('s', '', PARAM_CLEANHTML);
$maxfiles = optional_param('maxfiles', -1, PARAM_INT); // maxfiles
$maxbytes = optional_param('maxbytes', 0, PARAM_INT); // maxbytes
$subdirs = optional_param('subdirs', 0, PARAM_INT); // maxbytes
$accepted_types = optional_param_array('accepted_types', '*', PARAM_RAW);
// the path to save files
$savepath = optional_param('savepath', '/', PARAM_PATH);
@@ -75,22 +76,16 @@ if (!$course = $DB->get_record('course', array('id'=>$courseid))) {
}
$PAGE->set_course($course);
// init repository plugin
$sql = 'SELECT i.name, i.typeid, r.type FROM {repository} r, {repository_instances} i '.
'WHERE i.id=? AND i.typeid=r.id';
if ($repository = $DB->get_record_sql($sql, array($repo_id))) {
$type = $repository->type;
if (file_exists($CFG->dirroot.'/repository/'.$type.'/lib.php')) {
require_once($CFG->dirroot.'/repository/'.$type.'/lib.php');
$classname = 'repository_' . $type;
try {
$repo = new $classname($repo_id, $contextid, array('ajax'=>false, 'name'=>$repository->name, 'type'=>$type));
} catch (repository_exception $e){
print_error('pluginerror', 'repository');
}
} else {
print_error('invalidplugin', 'repository');
}
if ($repo_id) {
// Get repository instance information
$repooptions = array(
'ajax' => false,
'mimetypes' => $accepted_types
);
$repo = repository::get_repository_by_id($repo_id, $contextid, $repooptions);
// Check permissions
$repo->check_capability();
}
$context = context::instance_by_id($contextid);
@@ -285,40 +280,65 @@ case 'sign':
break;
case 'download':
$thefile = $repo->get_file($fileurl, $filename);
$filesize = filesize($thefile['path']);
if (($maxbytes!=-1) && ($filesize>$maxbytes)) {
print_error('maxbytes');
// Check that user has permission to access this file
if (!$repo->file_is_accessible($fileurl)) {
print_error('storedfilecannotread');
}
if (!empty($thefile)) {
$record = new stdClass();
$record->filepath = $savepath;
$record->filename = $filename;
$record->component = 'user';
$record->filearea = 'draft';
$record->itemid = $itemid;
$record->license = '';
$record->author = '';
$now = time();
$record->timecreated = $now;
$record->timemodified = $now;
$record->userid = $USER->id;
$record->contextid = $user_context->id;
$sourcefield = $repo->get_file_source_info($thefile['url']);
$record->source = repository::build_source_field($sourcefield);
try {
$info = repository::move_to_filepool($thefile['path'], $record);
redirect($home_url, get_string('downloadsucc', 'repository'));
} catch (moodle_exception $e) {
// inject target URL
$e->link = $PAGE->url->out();
echo $OUTPUT->header(); // hack: we need the embedded header here, standard error printing would not use it
throw $e;
// If file is already a reference, set $fileurl = file source, $repo = file repository
// note that in this case user may not have permission to access the source file directly
// so no file_browser/file_info can be used below
if ($repo->has_moodle_files()) {
$file = repository::get_moodle_file($fileurl);
if ($file && $file->is_external_file()) {
$fileurl = $file->get_reference();
$repo_id = $file->get_repository_id();
$repo = repository::get_repository_by_id($repo_id, $contextid, $repooptions);
}
}
$record = new stdClass();
$record->filepath = $savepath;
$record->filename = $filename;
$record->component = 'user';
$record->filearea = 'draft';
$record->itemid = $itemid;
$record->license = '';
$record->author = '';
$now = time();
$record->timecreated = $now;
$record->timemodified = $now;
$record->userid = $USER->id;
$record->contextid = $user_context->id;
$record->sortorder = 0;
$sourcefield = $repo->get_file_source_info($fileurl);
$record->source = repository::build_source_field($sourcefield);
if ($repo->has_moodle_files()) {
$fileinfo = $repo->copy_to_area($fileurl, $record, $maxbytes);
redirect($home_url, get_string('downloadsucc', 'repository'));
} else {
print_error('cannotdownload', 'repository');
$thefile = $repo->get_file($fileurl, $filename);
if (!empty($thefile['path'])) {
$filesize = filesize($thefile['path']);
if ($maxbytes != -1 && $filesize>$maxbytes) {
unlink($thefile['path']);
print_error('maxbytes');
}
try {
$info = repository::move_to_filepool($thefile['path'], $record);
redirect($home_url, get_string('downloadsucc', 'repository'));
} catch (moodle_exception $e) {
// inject target URL
$e->link = $PAGE->url->out();
echo $OUTPUT->header(); // hack: we need the embedded header here, standard error printing would not use it
throw $e;
}
} else {
print_error('cannotdownload', 'repository');
}
}
break;
+62 -49
View File
@@ -521,9 +521,10 @@ abstract class repository {
*
* @param int $repositoryid repository ID
* @param stdClass|int $context context instance or context ID
* @param array $options additional repository options
* @return repository
*/
public static function get_repository_by_id($repositoryid, $context) {
public static function get_repository_by_id($repositoryid, $context, $options = array()) {
global $CFG, $DB;
$sql = 'SELECT i.name, i.typeid, r.type FROM {repository} r, {repository_instances} i WHERE i.id=? AND i.typeid=r.id';
@@ -539,10 +540,15 @@ abstract class repository {
if (is_object($context)) {
$contextid = $context->id;
}
$repository = new $classname($repositoryid, $contextid, array('type'=>$type));
$options['type'] = $type;
$options['typeid'] = $record->typeid;
if (empty($options['name'])) {
$options['name'] = $record->name;
}
$repository = new $classname($repositoryid, $contextid, $options);
return $repository;
} else {
throw new moodle_exception('error');
throw new repository_exception('invalidplugin', 'repository');
}
}
}
@@ -609,16 +615,16 @@ abstract class repository {
}
/**
* To check if the context id is valid
* Checks if user has a capability to view the current repository in current context
*
* @static
* @param int $contextid
* @param stdClass $instance
* @return bool
*/
public static function check_capability($contextid, $instance) {
$context = get_context_instance_by_id($contextid);
$capability = has_capability('repository/'.$instance->type.':view', $context);
public final function check_capability() {
$capability = false;
if (preg_match("/^repository_(.*)$/", get_class($this), $matches)) {
$type = $matches[1];
$capability = has_capability('repository/'.$type.':view', $this->context);
}
if (!$capability) {
throw new repository_exception('nopermissiontoaccess', 'repository');
}
@@ -651,45 +657,60 @@ abstract class repository {
* @return stored_file|null
*/
public static function get_moodle_file($source) {
$params = unserialize(base64_decode($source));
if (empty($params) || !is_array($params)) {
return null;
}
foreach (array('contextid', 'itemid', 'filename', 'filepath', 'component') as $key) {
if (!array_key_exists($key, $params)) {
return null;
}
}
$contextid = clean_param($params['contextid'], PARAM_INT);
$component = clean_param($params['component'], PARAM_COMPONENT);
$filearea = clean_param($params['filearea'], PARAM_AREA);
$itemid = clean_param($params['itemid'], PARAM_INT);
$filepath = clean_param($params['filepath'], PARAM_PATH);
$filename = clean_param($params['filename'], PARAM_FILE);
$params = file_storage::unpack_reference($source, true);
$fs = get_file_storage();
return $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename);
return $fs->get_file($params['contextid'], $params['component'], $params['filearea'],
$params['itemid'], $params['filepath'], $params['filename']);
}
/**
* This function is used to copy a moodle file to draft area
* Repository method to make sure that user can access particular file.
*
* @param string $encoded The metainfo of file, it is base64 encoded php serialized data
* This is checked when user tries to pick the file from repository to deal with
* potential parameter substitutions is request
*
* @param string $source
* @return bool whether the file is accessible by current user
*/
public function file_is_accessible($source) {
if ($this->has_moodle_files()) {
try {
$params = file_storage::unpack_reference($source, true);
} catch (file_reference_exception $e) {
return false;
}
$browser = get_file_browser();
$context = context::instance_by_id($params['contextid']);
$file_info = $browser->get_file_info($context, $params['component'], $params['filearea'],
$params['itemid'], $params['filepath'], $params['filename']);
return !empty($file_info);
}
return true;
}
/**
* This function is used to copy a moodle file to draft area.
*
* It DOES NOT check if the user is allowed to access this file because the actual file
* can be located in the area where user does not have access to but there is an alias
* to this file in the area where user CAN access it.
* {@link file_is_accessible} should be called for alias location before calling this function.
*
* @param string $source The metainfo of file, it is base64 encoded php serialized data
* @param stdClass|array $filerecord contains itemid, filepath, filename and optionally other
* attributes of the new file
* @param int $maxbytes maximum allowed size of file, -1 if unlimited. If size of file exceeds
* the limit, the file_exception is thrown.
* @return array The information of file
* @return array The information about the created file
*/
public function copy_to_area($encoded, $filerecord, $maxbytes = -1) {
public function copy_to_area($source, $filerecord, $maxbytes = -1) {
global $USER;
$fs = get_file_storage();
$browser = get_file_browser();
if ($this->has_moodle_files() == false) {
throw new coding_exception('Only repository used to browse moodle files can use repository::copy_to_area()');
}
$params = unserialize(base64_decode($encoded));
$user_context = context_user::instance($USER->id);
$filerecord = (array)$filerecord;
@@ -701,17 +722,9 @@ abstract class repository {
$new_filepath = $filerecord['filepath'];
$new_filename = $filerecord['filename'];
$contextid = clean_param($params['contextid'], PARAM_INT);
$fileitemid = clean_param($params['itemid'], PARAM_INT);
$filename = clean_param($params['filename'], PARAM_FILE);
$filepath = clean_param($params['filepath'], PARAM_PATH);;
$filearea = clean_param($params['filearea'], PARAM_AREA);
$component = clean_param($params['component'], PARAM_COMPONENT);
$context = get_context_instance_by_id($contextid);
// the file needs to copied to draft area
$file_info = $browser->get_file_info($context, $component, $filearea, $fileitemid, $filepath, $filename);
if ($maxbytes !== -1 && $file_info->get_filesize() > $maxbytes) {
$stored_file = self::get_moodle_file($source);
if ($maxbytes != -1 && $stored_file->get_filesize() > $maxbytes) {
throw new file_exception('maxbytes');
}
@@ -719,7 +732,7 @@ abstract class repository {
// create new file
$unused_filename = repository::get_unused_filename($draftitemid, $new_filepath, $new_filename);
$filerecord['filename'] = $unused_filename;
$file_info->copy_to_storage($filerecord);
$fs->create_file_from_storedfile($filerecord, $stored_file);
$event = array();
$event['event'] = 'fileexists';
$event['newfile'] = new stdClass;
@@ -729,17 +742,17 @@ abstract class repository {
$event['existingfile'] = new stdClass;
$event['existingfile']->filepath = $new_filepath;
$event['existingfile']->filename = $new_filename;
$event['existingfile']->url = moodle_url::make_draftfile_url($draftitemid, $new_filepath, $new_filename)->out();;
$event['existingfile']->url = moodle_url::make_draftfile_url($draftitemid, $new_filepath, $new_filename)->out();
return $event;
} else {
$file_info->copy_to_storage($filerecord);
$fs->create_file_from_storedfile($filerecord, $stored_file);
$info = array();
$info['itemid'] = $draftitemid;
$info['file'] = $new_filename;
$info['title'] = $new_filename;
$info['file'] = $new_filename;
$info['title'] = $new_filename;
$info['contextid'] = $user_context->id;
$info['url'] = moodle_url::make_draftfile_url($draftitemid, $new_filepath, $new_filename)->out();;
$info['filesize'] = $file_info->get_filesize();
$info['url'] = moodle_url::make_draftfile_url($draftitemid, $new_filepath, $new_filename)->out();
$info['filesize'] = $stored_file->get_filesize();
return $info;
}
}
+12 -81
View File
@@ -177,91 +177,22 @@ class repository_recent extends repository {
public function supported_returntypes() {
return FILE_INTERNAL;
}
/**
* This function overwrite the default implement to copying file using file_storage
* Repository method to make sure that user can access particular file.
*
* @param string $encoded The information of file, it is base64 encoded php serialized data
* @param stdClass|array $filerecord contains itemid, filepath, filename and optionally other
* attributes of the new file
* @param int $maxbytes maximum allowed size of file, -1 if unlimited. If size of file exceeds
* the limit, the file_exception is thrown.
* @return array The information of file
* This is checked when user tries to pick the file from repository to deal with
* potential parameter substitutions is request
*
* @todo MDL-33805 remove this function when recent files are managed correctly
*
* @param string $source
* @return bool whether the file is accessible by current user
*/
public function copy_to_area($encoded, $filerecord, $maxbytes = -1) {
public function file_is_accessible($source) {
global $USER;
$user_context = get_context_instance(CONTEXT_USER, $USER->id);
$filerecord = (array)$filerecord;
// make sure the new file will be created in user draft area
$filerecord['component'] = 'user'; // make sure
$filerecord['filearea'] = 'draft'; // make sure
$filerecord['contextid'] = $user_context->id;
$filerecord['sortorder'] = 0;
$draftitemid = $filerecord['itemid'];
$new_filepath = $filerecord['filepath'];
$new_filename = $filerecord['filename'];
$fs = get_file_storage();
$params = unserialize(base64_decode($encoded));
$contextid = clean_param($params['contextid'], PARAM_INT);
$fileitemid = clean_param($params['itemid'], PARAM_INT);
$filename = clean_param($params['filename'], PARAM_FILE);
$filepath = clean_param($params['filepath'], PARAM_PATH);;
$filearea = clean_param($params['filearea'], PARAM_AREA);
$component = clean_param($params['component'], PARAM_COMPONENT);
// XXX:
// When user try to pick a file from other filearea, normally file api will use file browse to
// operate the files with capability check, but in some areas, users don't have permission to
// browse the files (for example, forum_attachment area).
//
// To get 'recent' plugin working, we need to use lower level file_stoarge class to bypass the
// capability check, we will use a better workaround to improve it.
// TODO MDL-33297 apply here
if ($stored_file = $fs->get_file($contextid, $component, $filearea, $fileitemid, $filepath, $filename)) {
// verify user id
if ($USER->id != $stored_file->get_userid()) {
throw new moodle_exception('errornotyourfile', 'repository');
}
if ($maxbytes !== -1 && $stored_file->get_filesize() > $maxbytes) {
throw new file_exception('maxbytes');
}
// test if file already exists
if (repository::draftfile_exists($draftitemid, $new_filepath, $new_filename)) {
// create new file
$unused_filename = repository::get_unused_filename($draftitemid, $new_filepath, $new_filename);
$filerecord['filename'] = $unused_filename;
// create a tmp file
$fs->create_file_from_storedfile($filerecord, $stored_file);
$event = array();
$event['event'] = 'fileexists';
$event['newfile'] = new stdClass;
$event['newfile']->filepath = $new_filepath;
$event['newfile']->filename = $unused_filename;
$event['newfile']->url = moodle_url::make_draftfile_url($draftitemid, $new_filepath, $unused_filename)->out();
$event['existingfile'] = new stdClass;
$event['existingfile']->filepath = $new_filepath;
$event['existingfile']->filename = $new_filename;
$event['existingfile']->url = moodle_url::make_draftfile_url($draftitemid, $new_filepath, $new_filename)->out();;
return $event;
} else {
$fs->create_file_from_storedfile($filerecord, $stored_file);
$info = array();
$info['title'] = $new_filename;
$info['file'] = $new_filename;
$info['itemid'] = $draftitemid;
$info['filesize'] = $stored_file->get_filesize();
$info['url'] = moodle_url::make_draftfile_url($draftitemid, $new_filepath, $new_filename)->out();;
$info['contextid'] = $user_context->id;
return $info;
}
}
return false;
$file = self::get_moodle_file($source);
return (!empty($file) && $file->get_userid() == $USER->id);
}
/**
+15 -26
View File
@@ -70,17 +70,14 @@ if (!confirm_sesskey()) {
}
// Get repository instance information
$sql = 'SELECT i.name, i.typeid, r.type FROM {repository} r, {repository_instances} i WHERE i.id=? AND i.typeid=r.id';
if (!$repository = $DB->get_record_sql($sql, array($repo_id))) {
$err->error = get_string('invalidrepositoryid', 'repository');
die(json_encode($err));
} else {
$type = $repository->type;
}
$repooptions = array(
'ajax' => true,
'mimetypes' => $accepted_types
);
$repo = repository::get_repository_by_id($repo_id, $contextid, $repooptions);
// Check permissions
repository::check_capability($contextid, $repository);
$repo->check_capability();
$moodle_maxbytes = get_user_max_upload_file_size($context);
// to prevent maxbytes greater than moodle maxbytes setting
@@ -121,21 +118,6 @@ switch ($action) {
break;
}
if (file_exists($CFG->dirroot.'/repository/'.$type.'/lib.php')) {
require_once($CFG->dirroot.'/repository/'.$type.'/lib.php');
$classname = 'repository_' . $type;
$repooptions = array(
'ajax' => true,
'name' => $repository->name,
'type' => $type,
'mimetypes' => $accepted_types
);
$repo = new $classname($repo_id, $contextid, $repooptions);
} else {
$err->error = get_string('invalidplugin', 'repository', $type);
die(json_encode($err));
}
// These actions all occur on the currently active repository instance
switch ($action) {
case 'sign':
@@ -232,7 +214,14 @@ switch ($action) {
$record->userid = $USER->id;
$record->sortorder = 0;
// Check that user has permission to access this file
if (!$repo->file_is_accessible($source)) {
throw new file_exception('storedfilecannotread');
}
// If file is already a reference, set $source = file source, $repo = file repository
// note that in this case user may not have permission to access the source file directly
// so no file_browser/file_info can be used below
if ($repo->has_moodle_files()) {
$file = repository::get_moodle_file($source);
if ($file && $file->is_external_file()) {
@@ -298,13 +287,13 @@ switch ($action) {
} else {
// Download file to moodle.
$downloadedfile = $repo->get_file($source, $saveas_filename);
if ($downloadedfile['path'] === false) {
if (empty($downloadedfile['path'])) {
$err->error = get_string('cannotdownload', 'repository');
die(json_encode($err));
}
// Check if exceed maxbytes.
if (($maxbytes!==-1) && (filesize($downloadedfile['path']) > $maxbytes)) {
if ($maxbytes != -1 && filesize($downloadedfile['path']) > $maxbytes) {
throw new file_exception('maxbytes');
}