Merge branch 'MDL-55100-master' of git://github.com/jleyva/moodle

This commit is contained in:
Dan Poltawski 2016-10-04 17:00:53 +01:00
commit 37705564ec
4 changed files with 438 additions and 87 deletions

View File

@ -2167,6 +2167,78 @@ class core_course_external extends external_api {
);
}
/**
* Return the course information that is public (visible by every one)
*
* @param course_in_list $course course in list object
* @param stdClass $coursecontext course context object
* @return array the course information
* @since Moodle 3.2
*/
protected static function get_course_public_information(course_in_list $course, $coursecontext) {
static $categoriescache = array();
// Category information.
if (!array_key_exists($course->category, $categoriescache)) {
$categoriescache[$course->category] = coursecat::get($course->category, IGNORE_MISSING);
}
$category = $categoriescache[$course->category];
// Retrieve course overview used files.
$files = array();
foreach ($course->get_course_overviewfiles() as $file) {
$fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
$file->get_filearea(), null, $file->get_filepath(),
$file->get_filename())->out(false);
$files[] = array(
'filename' => $file->get_filename(),
'fileurl' => $fileurl,
'filesize' => $file->get_filesize(),
'filepath' => $file->get_filepath(),
'mimetype' => $file->get_mimetype(),
'timemodified' => $file->get_timemodified(),
);
}
// Retrieve the course contacts,
// we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services.
$coursecontacts = array();
foreach ($course->get_course_contacts() as $contact) {
$coursecontacts[] = array(
'id' => $contact['user']->id,
'fullname' => $contact['username']
);
}
// Allowed enrolment methods (maybe we can self-enrol).
$enroltypes = array();
$instances = enrol_get_instances($course->id, true);
foreach ($instances as $instance) {
$enroltypes[] = $instance->enrol;
}
// Format summary.
list($summary, $summaryformat) =
external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null);
$displayname = get_course_display_name_for_list($course);
$coursereturns = array();
$coursereturns['id'] = $course->id;
$coursereturns['fullname'] = external_format_string($course->fullname, $coursecontext->id);
$coursereturns['displayname'] = external_format_string($displayname, $coursecontext->id);
$coursereturns['shortname'] = external_format_string($course->shortname, $coursecontext->id);
$coursereturns['categoryid'] = $course->category;
$coursereturns['categoryname'] = $category == null ? '' : $category->name;
$coursereturns['summary'] = $summary;
$coursereturns['summaryformat'] = $summaryformat;
$coursereturns['summaryfiles'] = external_util::get_area_files($coursecontext->id, 'course', 'summary', false, false);
$coursereturns['overviewfiles'] = $files;
$coursereturns['contacts'] = $coursecontacts;
$coursereturns['enrollmentmethods'] = $enroltypes;
return $coursereturns;
}
/**
* Search courses following the specified criteria.
*
@ -2252,63 +2324,7 @@ class core_course_external extends external_api {
$coursecontext = context_course::instance($course->id);
// Category information.
if (!array_key_exists($course->category, $categoriescache)) {
$categoriescache[$course->category] = coursecat::get($course->category, IGNORE_MISSING);
}
$category = $categoriescache[$course->category];
// Retrieve course overfiew used files.
$files = array();
foreach ($course->get_course_overviewfiles() as $file) {
$fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
$file->get_filearea(), null, $file->get_filepath(),
$file->get_filename())->out(false);
$files[] = array(
'filename' => $file->get_filename(),
'fileurl' => $fileurl,
'filesize' => $file->get_filesize(),
'filepath' => $file->get_filepath(),
'mimetype' => $file->get_mimetype(),
'timemodified' => $file->get_timemodified(),
);
}
// Retrieve the course contacts,
// we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services.
$coursecontacts = array();
foreach ($course->get_course_contacts() as $contact) {
$coursecontacts[] = array(
'id' => $contact['user']->id,
'fullname' => $contact['username']
);
}
// Allowed enrolment methods (maybe we can self-enrol).
$enroltypes = array();
$instances = enrol_get_instances($course->id, true);
foreach ($instances as $instance) {
$enroltypes[] = $instance->enrol;
}
// Format summary.
list($summary, $summaryformat) =
external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null);
$displayname = get_course_display_name_for_list($course);
$coursereturns = array();
$coursereturns['id'] = $course->id;
$coursereturns['fullname'] = external_format_string($course->fullname, $coursecontext->id);
$coursereturns['displayname'] = external_format_string($displayname, $coursecontext->id);
$coursereturns['shortname'] = external_format_string($course->shortname, $coursecontext->id);
$coursereturns['categoryid'] = $course->category;
$coursereturns['categoryname'] = $category == null ? '' : $category->name;
$coursereturns['summary'] = $summary;
$coursereturns['summaryformat'] = $summaryformat;
$coursereturns['overviewfiles'] = $files;
$coursereturns['contacts'] = $coursecontacts;
$coursereturns['enrollmentmethods'] = $enroltypes;
$finalcourses[] = $coursereturns;
$finalcourses[] = self::get_course_public_information($course, $coursecontext);
}
return array(
@ -2318,6 +2334,71 @@ class core_course_external extends external_api {
);
}
/**
* Returns a course structure definition
*
* @param boolean $onlypublicdata set to true, to retrieve only fields viewable by anyone when the course is visible
* @return array the course structure
* @since Moodle 3.2
*/
protected static function get_course_structure($onlypublicdata = true) {
$coursestructure = array(
'id' => new external_value(PARAM_INT, 'course id'),
'fullname' => new external_value(PARAM_TEXT, 'course full name'),
'displayname' => new external_value(PARAM_TEXT, 'course display name'),
'shortname' => new external_value(PARAM_TEXT, 'course short name'),
'categoryid' => new external_value(PARAM_INT, 'category id'),
'categoryname' => new external_value(PARAM_TEXT, 'category name'),
'summary' => new external_value(PARAM_RAW, 'summary'),
'summaryformat' => new external_format_value('summary'),
'summaryfiles' => new external_files('summary files in the summary field', VALUE_OPTIONAL),
'overviewfiles' => new external_files('additional overview files attached to this course'),
'contacts' => new external_multiple_structure(
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'contact user id'),
'fullname' => new external_value(PARAM_NOTAGS, 'contact user fullname'),
)
),
'contact users'
),
'enrollmentmethods' => new external_multiple_structure(
new external_value(PARAM_PLUGIN, 'enrollment method'),
'enrollment methods list'
),
);
if (!$onlypublicdata) {
$extra = array(
'idnumber' => new external_value(PARAM_RAW, 'Id number', VALUE_OPTIONAL),
'format' => new external_value(PARAM_PLUGIN, 'Course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
'showgrades' => new external_value(PARAM_INT, '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
'newsitems' => new external_value(PARAM_INT, 'Number of recent items appearing on the course page', VALUE_OPTIONAL),
'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL),
'maxbytes' => new external_value(PARAM_INT, 'Largest size of file that can be uploaded into', VALUE_OPTIONAL),
'showreports' => new external_value(PARAM_INT, 'Are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
'visible' => new external_value(PARAM_INT, '1: available to student, 0:not available', VALUE_OPTIONAL),
'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
'enablecompletion' => new external_value(PARAM_INT, 'Completion enabled? 1: yes 0: no', VALUE_OPTIONAL),
'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
'lang' => new external_value(PARAM_SAFEDIR, 'Forced course language', VALUE_OPTIONAL),
'theme' => new external_value(PARAM_PLUGIN, 'Fame of the forced theme', VALUE_OPTIONAL),
'sortorder' => new external_value(PARAM_INT, 'Sort order in the category', VALUE_OPTIONAL),
'marker' => new external_value(PARAM_INT, 'Current course marker', VALUE_OPTIONAL),
'legacyfiles' => new external_value(PARAM_INT, 'If legacy files are enabled', VALUE_OPTIONAL),
'calendartype' => new external_value(PARAM_PLUGIN, 'Calendar type', VALUE_OPTIONAL),
'timecreated' => new external_value(PARAM_INT, 'Time when the course was created', VALUE_OPTIONAL),
'timemodified' => new external_value(PARAM_INT, 'Last time the course was updated', VALUE_OPTIONAL),
'requested' => new external_value(PARAM_INT, 'If is a requested course', VALUE_OPTIONAL),
'cacherev' => new external_value(PARAM_INT, 'Cache revision number', VALUE_OPTIONAL),
);
$coursestructure = array_merge($coursestructure, $extra);
}
return new external_single_structure($coursestructure);
}
/**
* Returns description of method result value
*
@ -2325,38 +2406,10 @@ class core_course_external extends external_api {
* @since Moodle 3.0
*/
public static function search_courses_returns() {
return new external_single_structure(
array(
'total' => new external_value(PARAM_INT, 'total course count'),
'courses' => new external_multiple_structure(
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'course id'),
'fullname' => new external_value(PARAM_TEXT, 'course full name'),
'displayname' => new external_value(PARAM_TEXT, 'course display name'),
'shortname' => new external_value(PARAM_TEXT, 'course short name'),
'categoryid' => new external_value(PARAM_INT, 'category id'),
'categoryname' => new external_value(PARAM_TEXT, 'category name'),
'summary' => new external_value(PARAM_RAW, 'summary'),
'summaryformat' => new external_format_value('summary'),
'overviewfiles' => new external_files('additional overview files attached to this course'),
'contacts' => new external_multiple_structure(
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'contact user id'),
'fullname' => new external_value(PARAM_NOTAGS, 'contact user fullname'),
)
),
'contact users'
),
'enrollmentmethods' => new external_multiple_structure(
new external_value(PARAM_PLUGIN, 'enrollment method'),
'enrollment methods list'
),
)
), 'course'
),
'courses' => new external_multiple_structure(self::get_course_structure(), 'course'),
'warnings' => new external_warnings()
)
);
@ -2767,4 +2820,135 @@ class core_course_external extends external_api {
public static function get_user_administration_options_returns() {
return self::get_user_navigation_options_returns();
}
/**
* Returns description of method parameters
*
* @return external_function_parameters
* @since Moodle 3.2
*/
public static function get_courses_by_field_parameters() {
return new external_function_parameters(
array(
'field' => new external_value(PARAM_ALPHA, 'The field to search can be left empty for all courses or:
id: course id
ids: comma separated course ids
shortname: course short name
idnumber: course id number
category: category id the course belongs to
', VALUE_DEFAULT, ''),
'value' => new external_value(PARAM_RAW, 'The value to match', VALUE_DEFAULT, '')
)
);
}
/**
* Get courses matching a specific field (id/s, shortname, idnumber, category)
*
* @param string $field field name to search, or empty for all courses
* @param string $value value to search
* @return array list of courses and warnings
* @throws invalid_parameter_exception
* @since Moodle 3.2
*/
public static function get_courses_by_field($field = '', $value = '') {
global $DB, $CFG;
require_once($CFG->libdir . '/coursecatlib.php');
$params = self::validate_parameters(self::get_courses_by_field_parameters(),
array(
'field' => $field,
'value' => $value,
)
);
$warnings = array();
if (empty($params['field'])) {
$courses = $DB->get_records('course', null, 'id ASC');
} else {
switch ($params['field']) {
case 'id':
case 'category':
$value = clean_param($params['value'], PARAM_INT);
break;
case 'ids':
$value = clean_param($params['value'], PARAM_SEQUENCE);
break;
case 'shortname':
$value = clean_param($params['value'], PARAM_TEXT);
break;
case 'idnumber':
$value = clean_param($params['value'], PARAM_RAW);
break;
default:
throw new invalid_parameter_exception('Invalid field name');
}
if ($params['field'] === 'ids') {
$courses = $DB->get_records_list('course', 'id', explode(',', $value), 'id ASC');
} else {
$courses = $DB->get_records('course', array($params['field'] => $value), 'id ASC');
}
}
$coursesdata = array();
foreach ($courses as $course) {
$context = context_course::instance($course->id);
$canupdatecourse = has_capability('moodle/course:update', $context);
$canviewhiddencourses = has_capability('moodle/course:viewhiddencourses', $context);
// Check if the course is visible in the site for the user.
if (!$course->visible and !$canviewhiddencourses and !$canupdatecourse) {
continue;
}
// Get the public course information, even if we are not enrolled.
$courseinlist = new course_in_list($course);
$coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context);
// Now, check if we have access to the course.
try {
self::validate_context($context);
} catch (Exception $e) {
continue;
}
// Return information for any user that can access the course.
$coursefields = array('format', 'showgrades', 'newsitems', 'startdate', 'maxbytes', 'showreports', 'visible',
'groupmode', 'groupmodeforce', 'defaultgroupingid', 'enablecompletion', 'completionnotify', 'lang', 'theme',
'sortorder', 'marker');
// Information for managers only.
if ($canupdatecourse) {
$managerfields = array('idnumber', 'legacyfiles', 'calendartype', 'timecreated', 'timemodified', 'requested',
'cacherev');
$coursefields = array_merge($coursefields, $managerfields);
}
// Populate fields.
foreach ($coursefields as $field) {
$coursesdata[$course->id][$field] = $course->{$field};
}
}
return array(
'courses' => $coursesdata,
'warnings' => $warnings
);
}
/**
* Returns description of method result value
*
* @return external_description
* @since Moodle 3.2
*/
public static function get_courses_by_field_returns() {
// Course structure, including not only public viewable fields.
return new external_single_structure(
array(
'courses' => new external_multiple_structure(self::get_course_structure(false), 'Course'),
'warnings' => new external_warnings()
)
);
}
}

View File

@ -1870,4 +1870,163 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
}
}
}
/**
* Test get_courses_by_fields
*/
public function test_get_courses_by_field() {
global $DB;
$this->resetAfterTest(true);
$category1 = self::getDataGenerator()->create_category();
$category2 = self::getDataGenerator()->create_category(array('parent' => $category1->id));
$course1 = self::getDataGenerator()->create_course(array('category' => $category1->id, 'shortname' => 'c1'));
$course2 = self::getDataGenerator()->create_course(array('visible' => 0, 'category' => $category2->id, 'idnumber' => 'i2'));
$student1 = self::getDataGenerator()->create_user();
$user1 = self::getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
self::getDataGenerator()->enrol_user($student1->id, $course1->id, $studentrole->id);
self::getDataGenerator()->enrol_user($student1->id, $course2->id, $studentrole->id);
self::setAdminUser();
// As admins, we should be able to retrieve everything.
$result = core_course_external::get_courses_by_field();
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(3, $result['courses']);
// Expect to receive all the fields.
$this->assertCount(35, $result['courses'][0]);
$this->assertCount(35, $result['courses'][1]);
$this->assertCount(35, $result['courses'][2]);
$result = core_course_external::get_courses_by_field('id', $course1->id);
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course1->id, $result['courses'][0]['id']);
// Expect to receive all the fields.
$this->assertCount(35, $result['courses'][0]);
$result = core_course_external::get_courses_by_field('id', $course2->id);
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course2->id, $result['courses'][0]['id']);
$result = core_course_external::get_courses_by_field('ids', "$course1->id,$course2->id");
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(2, $result['courses']);
$result = core_course_external::get_courses_by_field('category', $category1->id);
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course1->id, $result['courses'][0]['id']);
$result = core_course_external::get_courses_by_field('shortname', 'c1');
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course1->id, $result['courses'][0]['id']);
$result = core_course_external::get_courses_by_field('idnumber', 'i2');
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course2->id, $result['courses'][0]['id']);
$result = core_course_external::get_courses_by_field('idnumber', 'x');
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(0, $result['courses']);
self::setUser($student1);
// All visible courses (including front page) for normal student.
$result = core_course_external::get_courses_by_field();
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(2, $result['courses']);
$this->assertCount(28, $result['courses'][0]);
$this->assertCount(28, $result['courses'][1]);
$result = core_course_external::get_courses_by_field('id', $course1->id);
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course1->id, $result['courses'][0]['id']);
// Expect to receive all the files that a student can see.
$this->assertCount(28, $result['courses'][0]);
// Course 2 is not visible.
$result = core_course_external::get_courses_by_field('id', $course2->id);
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(0, $result['courses']);
$result = core_course_external::get_courses_by_field('ids', "$course1->id,$course2->id");
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$result = core_course_external::get_courses_by_field('category', $category1->id);
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course1->id, $result['courses'][0]['id']);
$result = core_course_external::get_courses_by_field('shortname', 'c1');
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course1->id, $result['courses'][0]['id']);
$result = core_course_external::get_courses_by_field('idnumber', 'i2');
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(0, $result['courses']);
$result = core_course_external::get_courses_by_field('idnumber', 'x');
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(0, $result['courses']);
self::setUser($user1);
// All visible courses (including front page) for authenticated user.
$result = core_course_external::get_courses_by_field();
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(2, $result['courses']);
$this->assertCount(28, $result['courses'][0]); // Site course.
$this->assertCount(12, $result['courses'][1]); // Only public information, not enrolled.
$result = core_course_external::get_courses_by_field('id', $course1->id);
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course1->id, $result['courses'][0]['id']);
// Expect to receive all the files that a authenticated can see.
$this->assertCount(12, $result['courses'][0]);
// Course 2 is not visible.
$result = core_course_external::get_courses_by_field('id', $course2->id);
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(0, $result['courses']);
$result = core_course_external::get_courses_by_field('ids', "$course1->id,$course2->id");
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$result = core_course_external::get_courses_by_field('category', $category1->id);
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course1->id, $result['courses'][0]['id']);
$result = core_course_external::get_courses_by_field('shortname', 'c1');
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(1, $result['courses']);
$this->assertEquals($course1->id, $result['courses'][0]['id']);
$result = core_course_external::get_courses_by_field('idnumber', 'i2');
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(0, $result['courses']);
$result = core_course_external::get_courses_by_field('idnumber', 'x');
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(0, $result['courses']);
}
public function test_get_courses_by_field_invalid_field() {
$this->expectException('invalid_parameter_exception');
$result = core_course_external::get_courses_by_field('zyx', 'x');
}
public function test_get_courses_by_field_invalid_courses() {
$result = core_course_external::get_courses_by_field('id', '-1');
$result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
$this->assertCount(0, $result['courses']);
}
}

View File

@ -319,6 +319,14 @@ $functions = array(
'type' => 'read',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_course_get_courses_by_field' => array(
'classname' => 'core_course_external',
'methodname' => 'get_courses_by_field',
'classpath' => 'course/externallib.php',
'description' => 'Get courses matching a specific field (id/s, shortname, idnumber, category)',
'type' => 'read',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_enrol_get_course_enrolment_methods' => array(
'classname' => 'core_enrol_external',
'methodname' => 'get_course_enrolment_methods',

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2016100402.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2016100403.00; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.