Merge branch 'MDL-46706-master' of https://github.com/sammarshallou/moodle

Conflicts:
	lib/tests/modinfolib_test.php
This commit is contained in:
Damyon Wiese 2014-08-19 17:13:05 +08:00
commit 52ed065131
3 changed files with 421 additions and 0 deletions

View File

@ -1747,6 +1747,31 @@ class cm_info implements IteratorAggregate {
: null;
}
/**
* Creates a cm_info object from a database record (also accepts cm_info
* in which case it is just returned unchanged).
*
* @param stdClass|cm_info|null $cm Stdclass or cm_info (or null)
* @param int $userid Optional userid (default to current)
* @return cm_info|null Object as cm_info, or null if input was null
*/
public static function create($cm, $userid = 0) {
// Nulls get passed through.
if (is_null($cm)) {
return null;
}
// If it is already a cm_info object, just return it.
if ($cm instanceof cm_info) {
return $cm;
}
// Otherwise load modinfo.
if (empty($cm->id) || empty($cm->course)) {
throw new coding_exception('$cm must contain ->id and ->course');
}
$modinfo = get_fast_modinfo($cm->course, $userid);
return $modinfo->get_cm($cm->id);
}
/**
* If dynamic data for this course-module is not yet available, gets it.
*
@ -2062,6 +2087,165 @@ function get_fast_modinfo($courseorid, $userid = 0, $resetonly = false) {
return course_modinfo::instance($courseorid, $userid);
}
/**
* Efficiently retrieves the $course (stdclass) and $cm (cm_info) objects, given
* a cmid. If module name is also provided, it will ensure the cm is of that type.
*
* Usage:
* list($course, $cm) = get_course_and_cm_from_cmid($cmid, 'forum');
*
* Using this method has a performance advantage because it works by loading
* modinfo for the course - which will then be cached and it is needed later
* in most requests. It also guarantees that the $cm object is a cm_info and
* not a stdclass.
*
* The $course object can be supplied if already known and will speed
* up this function - although it is more efficient to use this function to
* get the course if you are starting from a cmid.
*
* To avoid security problems and obscure bugs, you should always specify
* $modulename if the cmid value came from user input.
*
* By default this obtains information (for example, whether user can access
* the activity) for current user, but you can specify a userid if required.
*
* @param stdClass|int $cmorid Id of course-module, or database object
* @param string $modulename Optional modulename (improves security)
* @param stdClass|int $courseorid Optional course object if already loaded
* @param int $userid Optional userid (default = current)
* @return array Array with 2 elements $course and $cm
* @throws moodle_exception If the item doesn't exist or is of wrong module name
*/
function get_course_and_cm_from_cmid($cmorid, $modulename = '', $courseorid = 0, $userid = 0) {
global $DB;
if (is_object($cmorid)) {
$cmid = $cmorid->id;
if (isset($cmorid->course)) {
$courseid = (int)$cmorid->course;
} else {
$courseid = 0;
}
} else {
$cmid = (int)$cmorid;
$courseid = 0;
}
// Validate module name if supplied.
if ($modulename && !core_component::is_valid_plugin_name('mod', $modulename)) {
throw new coding_exception('Invalid modulename parameter');
}
// Get course from last parameter if supplied.
$course = null;
if (is_object($courseorid)) {
$course = $courseorid;
} else if ($courseorid) {
$courseid = (int)$courseorid;
}
if (!$course) {
if ($courseid) {
// If course ID is known, get it using normal function.
$course = get_course($courseid);
} else {
// Get course record in a single query based on cmid.
$course = $DB->get_record_sql("
SELECT c.*
FROM {course_modules} cm
JOIN {course} c ON c.id = cm.course
WHERE cm.id = ?", array($cmid), MUST_EXIST);
}
}
// Get cm from get_fast_modinfo.
$modinfo = get_fast_modinfo($course, $userid);
$cm = $modinfo->get_cm($cmid);
if ($modulename && $cm->modname !== $modulename) {
throw new moodle_exception('invalidcoursemodule', 'error');
}
return array($course, $cm);
}
/**
* Efficiently retrieves the $course (stdclass) and $cm (cm_info) objects, given
* an instance id or record and module name.
*
* Usage:
* list($course, $cm) = get_course_and_cm_from_instance($forum, 'forum');
*
* Using this method has a performance advantage because it works by loading
* modinfo for the course - which will then be cached and it is needed later
* in most requests. It also guarantees that the $cm object is a cm_info and
* not a stdclass.
*
* The $course object can be supplied if already known and will speed
* up this function - although it is more efficient to use this function to
* get the course if you are starting from an instance id.
*
* By default this obtains information (for example, whether user can access
* the activity) for current user, but you can specify a userid if required.
*
* @param stdclass|int $instanceorid Id of module instance, or database object
* @param string $modulename Modulename (required)
* @param stdClass|int $courseorid Optional course object if already loaded
* @param int $userid Optional userid (default = current)
* @return array Array with 2 elements $course and $cm
* @throws moodle_exception If the item doesn't exist or is of wrong module name
*/
function get_course_and_cm_from_instance($instanceorid, $modulename, $courseorid = 0, $userid = 0) {
global $DB;
// Get data from parameter.
if (is_object($instanceorid)) {
$instanceid = $instanceorid->id;
if (isset($instanceorid->course)) {
$courseid = (int)$instanceorid->course;
} else {
$courseid = 0;
}
} else {
$instanceid = (int)$instanceorid;
$courseid = 0;
}
// Get course from last parameter if supplied.
$course = null;
if (is_object($courseorid)) {
$course = $courseorid;
} else if ($courseorid) {
$courseid = (int)$courseorid;
}
// Validate module name if supplied.
if (!core_component::is_valid_plugin_name('mod', $modulename)) {
throw new coding_exception('Invalid modulename parameter');
}
if (!$course) {
if ($courseid) {
// If course ID is known, get it using normal function.
$course = get_course($courseid);
} else {
// Get course record in a single query based on instance id.
$pagetable = '{' . $modulename . '}';
$course = $DB->get_record_sql("
SELECT c.*
FROM $pagetable instance
JOIN {course} c ON c.id = instance.course
WHERE instance.id = ?", array($instanceid), MUST_EXIST);
}
}
// Get cm from get_fast_modinfo.
$modinfo = get_fast_modinfo($course, $userid);
$instances = $modinfo->get_instances_of($modulename);
if (!array_key_exists($instanceid, $instances)) {
throw new moodle_exception('invalidmoduleid', 'error', $instanceid);
}
return array($course, $instances[$instanceid]);
}
/**
* Rebuilds or resets the cached list of course activities stored in MUC.
*

View File

@ -965,4 +965,236 @@ class core_modinfolib_testcase extends advanced_testcase {
$this->assertCount(0, $groups);
$this->assertArrayNotHasKey($group1->id, $groups);
}
/**
* Tests the function for constructing a cm_info from mixed data.
*/
public function test_create() {
global $CFG, $DB;
$this->resetAfterTest();
// Create a course and an activity.
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$page = $generator->create_module('page', array('course' => $course->id,
'name' => 'Annie'));
// Null is passed through.
$this->assertNull(cm_info::create(null));
// Stdclass object turns into cm_info.
$cm = cm_info::create(
(object)array('id' => $page->cmid, 'course' => $course->id));
$this->assertInstanceOf('cm_info', $cm);
$this->assertEquals('Annie', $cm->name);
// A cm_info object stays as cm_info.
$this->assertSame($cm, cm_info::create($cm));
// Invalid object (missing fields) causes error.
try {
cm_info::create((object)array('id' => $page->cmid));
$this->fail();
} catch (Exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
// Create a second hidden activity.
$hiddenpage = $generator->create_module('page', array('course' => $course->id,
'name' => 'Annie', 'visible' => 0));
// Create 2 user accounts, one is a manager who can see everything.
$user = $generator->create_user();
$generator->enrol_user($user->id, $course->id);
$manager = $generator->create_user();
$generator->enrol_user($manager->id, $course->id,
$DB->get_field('role', 'id', array('shortname' => 'manager'), MUST_EXIST));
// User can see the normal page but not the hidden one.
$cm = cm_info::create((object)array('id' => $page->cmid, 'course' => $course->id),
$user->id);
$this->assertTrue($cm->uservisible);
$cm = cm_info::create((object)array('id' => $hiddenpage->cmid, 'course' => $course->id),
$user->id);
$this->assertFalse($cm->uservisible);
// Manager can see the hidden one too.
$cm = cm_info::create((object)array('id' => $hiddenpage->cmid, 'course' => $course->id),
$manager->id);
$this->assertTrue($cm->uservisible);
}
/**
* Tests function for getting $course and $cm at once quickly from modinfo
* based on cmid or cm record.
*/
public function test_get_course_and_cm_from_cmid() {
global $CFG, $DB;
$this->resetAfterTest();
// Create a course and an activity.
$generator = $this->getDataGenerator();
$course = $generator->create_course(array('shortname' => 'Halls'));
$page = $generator->create_module('page', array('course' => $course->id,
'name' => 'Annie'));
// Successful usage.
list($course, $cm) = get_course_and_cm_from_cmid($page->cmid);
$this->assertEquals('Halls', $course->shortname);
$this->assertInstanceOf('cm_info', $cm);
$this->assertEquals('Annie', $cm->name);
// Specified module type.
list($course, $cm) = get_course_and_cm_from_cmid($page->cmid, 'page');
$this->assertEquals('Annie', $cm->name);
// With id in object.
$fakecm = (object)array('id' => $page->cmid);
list($course, $cm) = get_course_and_cm_from_cmid($fakecm);
$this->assertEquals('Halls', $course->shortname);
$this->assertEquals('Annie', $cm->name);
// With both id and course in object.
$fakecm->course = $course->id;
list($course, $cm) = get_course_and_cm_from_cmid($fakecm);
$this->assertEquals('Halls', $course->shortname);
$this->assertEquals('Annie', $cm->name);
// With supplied course id.
list($course, $cm) = get_course_and_cm_from_cmid($page->cmid, 'page', $course->id);
$this->assertEquals('Annie', $cm->name);
// With supplied course object (modified just so we can check it is
// indeed reusing the supplied object).
$course->silly = true;
list($course, $cm) = get_course_and_cm_from_cmid($page->cmid, 'page', $course);
$this->assertEquals('Annie', $cm->name);
$this->assertTrue($course->silly);
// Incorrect module type.
try {
get_course_and_cm_from_cmid($page->cmid, 'forum');
$this->fail();
} catch (moodle_exception $e) {
$this->assertEquals('invalidcoursemodule', $e->errorcode);
}
// Invalid module name.
try {
get_course_and_cm_from_cmid($page->cmid, 'pigs can fly');
$this->fail();
} catch (coding_exception $e) {
$this->assertContains('Invalid modulename parameter', $e->getMessage());
}
// Doesn't exist.
try {
get_course_and_cm_from_cmid($page->cmid + 1);
$this->fail();
} catch (moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
// Create a second hidden activity.
$hiddenpage = $generator->create_module('page', array('course' => $course->id,
'name' => 'Annie', 'visible' => 0));
// Create 2 user accounts, one is a manager who can see everything.
$user = $generator->create_user();
$generator->enrol_user($user->id, $course->id);
$manager = $generator->create_user();
$generator->enrol_user($manager->id, $course->id,
$DB->get_field('role', 'id', array('shortname' => 'manager'), MUST_EXIST));
// User can see the normal page but not the hidden one.
list($course, $cm) = get_course_and_cm_from_cmid($page->cmid, 'page', 0, $user->id);
$this->assertTrue($cm->uservisible);
list($course, $cm) = get_course_and_cm_from_cmid($hiddenpage->cmid, 'page', 0, $user->id);
$this->assertFalse($cm->uservisible);
// Manager can see the hidden one too.
list($course, $cm) = get_course_and_cm_from_cmid($hiddenpage->cmid, 'page', 0, $manager->id);
$this->assertTrue($cm->uservisible);
}
/**
* Tests function for getting $course and $cm at once quickly from modinfo
* based on instance id or record.
*/
public function test_get_course_and_cm_from_instance() {
global $CFG, $DB;
$this->resetAfterTest();
// Create a course and an activity.
$generator = $this->getDataGenerator();
$course = $generator->create_course(array('shortname' => 'Halls'));
$page = $generator->create_module('page', array('course' => $course->id,
'name' => 'Annie'));
// Successful usage.
list($course, $cm) = get_course_and_cm_from_instance($page->id, 'page');
$this->assertEquals('Halls', $course->shortname);
$this->assertInstanceOf('cm_info', $cm);
$this->assertEquals('Annie', $cm->name);
// With id in object.
$fakeinstance = (object)array('id' => $page->id);
list($course, $cm) = get_course_and_cm_from_instance($fakeinstance, 'page');
$this->assertEquals('Halls', $course->shortname);
$this->assertEquals('Annie', $cm->name);
// With both id and course in object.
$fakeinstance->course = $course->id;
list($course, $cm) = get_course_and_cm_from_instance($fakeinstance, 'page');
$this->assertEquals('Halls', $course->shortname);
$this->assertEquals('Annie', $cm->name);
// With supplied course id.
list($course, $cm) = get_course_and_cm_from_instance($page->id, 'page', $course->id);
$this->assertEquals('Annie', $cm->name);
// With supplied course object (modified just so we can check it is
// indeed reusing the supplied object).
$course->silly = true;
list($course, $cm) = get_course_and_cm_from_instance($page->id, 'page', $course);
$this->assertEquals('Annie', $cm->name);
$this->assertTrue($course->silly);
// Doesn't exist (or is wrong type).
try {
get_course_and_cm_from_instance($page->id, 'forum');
$this->fail();
} catch (moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
// Invalid module name.
try {
get_course_and_cm_from_cmid($page->cmid, '1337 h4x0ring');
$this->fail();
} catch (coding_exception $e) {
$this->assertContains('Invalid modulename parameter', $e->getMessage());
}
// Create a second hidden activity.
$hiddenpage = $generator->create_module('page', array('course' => $course->id,
'name' => 'Annie', 'visible' => 0));
// Create 2 user accounts, one is a manager who can see everything.
$user = $generator->create_user();
$generator->enrol_user($user->id, $course->id);
$manager = $generator->create_user();
$generator->enrol_user($manager->id, $course->id,
$DB->get_field('role', 'id', array('shortname' => 'manager'), MUST_EXIST));
// User can see the normal page but not the hidden one.
list($course, $cm) = get_course_and_cm_from_cmid($page->cmid, 'page', 0, $user->id);
$this->assertTrue($cm->uservisible);
list($course, $cm) = get_course_and_cm_from_cmid($hiddenpage->cmid, 'page', 0, $user->id);
$this->assertFalse($cm->uservisible);
// Manager can see the hidden one too.
list($course, $cm) = get_course_and_cm_from_cmid($hiddenpage->cmid, 'page', 0, $manager->id);
$this->assertTrue($cm->uservisible);
}
}

View File

@ -7,6 +7,11 @@ information provided here is intended especially for developers.
* renderers: We now remove the suffix _renderable when looking for a render method for a renderable.
If you have a renderable class named like "blah_renderable" and have a method on a renderer named "render_blah_renderable"
you will need to change the name of your render method to "render_blah" instead, as renderable at the end is no longer accepted.
* New functions get_course_and_cm_from_cmid($cmorid, $modulename) and
get_course_and_cm_from_instance($instanceorid, $modulename) can be used to
more efficiently load these basic data objects at the start of a script.
* New function cm_info::create($cm) can be used when you need a cm_info
object, but have a $cm which might only be a standard database record.
DEPRECATIONS:
* completion_info->get_incomplete_criteria() is deprecated and will be removed in Moodle 3.0.