diff --git a/enrol/externallib.php b/enrol/externallib.php index 0b11256f06c..9e2338d7e3a 100644 --- a/enrol/externallib.php +++ b/enrol/externallib.php @@ -586,7 +586,10 @@ class core_role_external extends external_api { array( 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'), 'userid' => new external_value(PARAM_INT, 'The user that is going to be assigned'), - 'contextid' => new external_value(PARAM_INT, 'The context to assign the user role in'), + 'contextid' => new external_value(PARAM_INT, 'The context to assign the user role in', VALUE_OPTIONAL), + 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level to assign the user role in + (block, course, coursecat, system, user, module)', VALUE_OPTIONAL), + 'instanceid' => new external_value(PARAM_INT, 'The Instance id of item where the role needs to be assigned', VALUE_OPTIONAL), ) ) ) @@ -609,8 +612,10 @@ class core_role_external extends external_api { $transaction = $DB->start_delegated_transaction(); foreach ($params['assignments'] as $assignment) { - // Ensure the current user is allowed to run this function in the enrolment context - $context = context::instance_by_id($assignment['contextid'], IGNORE_MISSING); + // Ensure correct context level with a instance id or contextid is passed. + $context = self::get_context_from_params($assignment); + + // Ensure the current user is allowed to run this function in the enrolment context. self::validate_context($context); require_capability('moodle/role:assign', $context); @@ -621,7 +626,7 @@ class core_role_external extends external_api { throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']); } - role_assign($assignment['roleid'], $assignment['userid'], $assignment['contextid']); + role_assign($assignment['roleid'], $assignment['userid'], $context->id); } $transaction->allow_commit(); diff --git a/enrol/tests/externallib_role_test.php b/enrol/tests/externallib_role_test.php new file mode 100644 index 00000000000..a3ed01e3307 --- /dev/null +++ b/enrol/tests/externallib_role_test.php @@ -0,0 +1,132 @@ +. + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); +require_once($CFG->dirroot . '/enrol/externallib.php'); + +/** + * Role external PHPunit tests + * + * @package core_enrol + * @category external + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 2.4 + */ +class core_role_external_testcase extends externallib_advanced_testcase { + + /** + * Tests set up + */ + protected function setUp() { + global $CFG; + require_once($CFG->dirroot . '/enrol/externallib.php'); + } + + /** + * Test assign_roles + */ + public function test_assign_roles() { + global $USER; + + $this->resetAfterTest(true); + + $course = self::getDataGenerator()->create_course(); + + // Set the required capabilities by the external function. + $context = context_course::instance($course->id); + $roleid = $this->assignUserCapability('moodle/role:assign', $context->id); + $this->assignUserCapability('moodle/course:view', $context->id, $roleid); + + // Add manager role to $USER. + // So $USER is allowed to assign 'manager', 'editingteacher', 'teacher' and 'student'. + role_assign(1, $USER->id, context_system::instance()->id); + + // Check the teacher role has not been assigned to $USER. + $users = get_role_users(3, $context); + $this->assertEquals(count($users), 0); + + // Call the external function. Assign teacher role to $USER with contextid. + core_role_external::assign_roles(array( + array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id))); + + // Check the role has been assigned. + $users = get_role_users(3, $context); + $this->assertEquals(count($users), 1); + + // Unassign role. + role_unassign(3, $USER->id, $context->id); + $users = get_role_users(3, $context); + $this->assertEquals(count($users), 0); + + // Call the external function. Assign teacher role to $USER. + core_role_external::assign_roles(array( + array('roleid' => 3, 'userid' => $USER->id, 'contextlevel' => "course", 'instanceid' => $course->id))); + $users = get_role_users(3, $context); + $this->assertEquals(count($users), 1); + + // Call without required capability. + $this->unassignUserCapability('moodle/role:assign', $context->id, $roleid); + $this->setExpectedException('moodle_exception'); + $categories = core_role_external::assign_roles( + array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id)); + } + + /** + * Test unassign_roles + */ + public function test_unassign_roles() { + global $USER; + + $this->resetAfterTest(true); + + $course = self::getDataGenerator()->create_course(); + + // Set the required capabilities by the external function. + $context = context_course::instance($course->id); + $roleid = $this->assignUserCapability('moodle/role:assign', $context->id); + $this->assignUserCapability('moodle/course:view', $context->id, $roleid); + + // Add manager role to $USER. + // So $USER is allowed to assign 'manager', 'editingteacher', 'teacher' and 'student'. + role_assign(1, $USER->id, context_system::instance()->id); + + // Add teacher role to $USER on course context. + role_assign(3, $USER->id, $context->id); + + // Check the teacher role has been assigned to $USER on course context. + $users = get_role_users(3, $context); + $this->assertEquals(count($users), 1); + + // Call the external function. Assign teacher role to $USER. + core_role_external::unassign_roles(array( + array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id))); + + // Check the role has been unassigned on course context. + $users = get_role_users(3, $context); + $this->assertEquals(count($users), 0); + + // Call without required capability. + $this->unassignUserCapability('moodle/role:assign', $context->id, $roleid); + $this->setExpectedException('moodle_exception'); + $categories = core_role_external::unassign_roles( + array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id)); + } +} \ No newline at end of file diff --git a/enrol/tests/externallib_test.php b/enrol/tests/externallib_test.php index 0f053ebf5e1..c93ebcb17ef 100644 --- a/enrol/tests/externallib_test.php +++ b/enrol/tests/externallib_test.php @@ -156,102 +156,3 @@ class core_enrol_external_testcase extends externallib_advanced_testcase { } } - -/** - * Role external PHPunit tests - * - * @package core_enrol - * @category external - * @copyright 2012 Jerome Mouneyrac - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @since Moodle 2.4 - */ -class core_role_external_testcase extends externallib_advanced_testcase { - - /** - * Tests set up - */ - protected function setUp() { - global $CFG; - require_once($CFG->dirroot . '/enrol/externallib.php'); - } - - /** - * Test assign_roles - */ - public function test_assign_roles() { - global $USER; - - $this->resetAfterTest(true); - - $course = self::getDataGenerator()->create_course(); - - // Set the required capabilities by the external function. - $context = context_course::instance($course->id); - $roleid = $this->assignUserCapability('moodle/role:assign', $context->id); - $this->assignUserCapability('moodle/course:view', $context->id, $roleid); - - // Add manager role to $USER. - // So $USER is allowed to assign 'manager', 'editingteacher', 'teacher' and 'student'. - role_assign(1, $USER->id, context_system::instance()->id); - - // Check the teacher role has not been assigned to $USER. - $users = get_role_users(3, $context); - $this->assertEquals(count($users), 0); - - // Call the external function. Assign teacher role to $USER. - core_role_external::assign_roles(array( - array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id))); - - // Check the role has been assigned. - $users = get_role_users(3, $context); - $this->assertEquals(count($users), 1); - - // Call without required capability. - $this->unassignUserCapability('moodle/role:assign', $context->id, $roleid); - $this->setExpectedException('moodle_exception'); - $categories = core_role_external::assign_roles( - array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id)); - } - - /** - * Test unassign_roles - */ - public function test_unassign_roles() { - global $USER; - - $this->resetAfterTest(true); - - $course = self::getDataGenerator()->create_course(); - - // Set the required capabilities by the external function. - $context = context_course::instance($course->id); - $roleid = $this->assignUserCapability('moodle/role:assign', $context->id); - $this->assignUserCapability('moodle/course:view', $context->id, $roleid); - - // Add manager role to $USER. - // So $USER is allowed to assign 'manager', 'editingteacher', 'teacher' and 'student'. - role_assign(1, $USER->id, context_system::instance()->id); - - // Add teacher role to $USER on course context. - role_assign(3, $USER->id, $context->id); - - // Check the teacher role has been assigned to $USER on course context. - $users = get_role_users(3, $context); - $this->assertEquals(count($users), 1); - - // Call the external function. Assign teacher role to $USER. - core_role_external::unassign_roles(array( - array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id))); - - // Check the role has been unassigned on course context. - $users = get_role_users(3, $context); - $this->assertEquals(count($users), 0); - - // Call without required capability. - $this->unassignUserCapability('moodle/role:assign', $context->id, $roleid); - $this->setExpectedException('moodle_exception'); - $categories = core_role_external::unassign_roles( - array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id)); - } -} diff --git a/lib/externallib.php b/lib/externallib.php index 578ed48097a..9b92c5ec660 100644 --- a/lib/externallib.php +++ b/lib/externallib.php @@ -356,6 +356,34 @@ class external_api { require_login($course, false, $cm, false, true); } } + + /** + * Get context from passed parameters. + * The passed array must either contain a contextid or a combination of context level and instance id to fetch the context. + * For example, the context level can be "course" and instanceid can be courseid. + * + * See context_helper::get_all_levels() for a list of valid context levels. + * + * @param array $param + * @since Moodle 2.6 + * @throws invalid_parameter_exception + * @return context + */ + protected static function get_context_from_params($param) { + $levels = context_helper::get_all_levels(); + if (isset($param['contextid'])) { + return context::instance_by_id($param['contextid'], IGNORE_MISSING); + } else if (isset($param['contextlevel']) && isset($param['instanceid'])) { + $contextlevel = "context_".$param['contextlevel']; + if (!array_search($contextlevel, $levels)) { + throw new invalid_parameter_exception('Invalid context level = '.$param['contextlevel']); + } + return $contextlevel::instance($param['instanceid'], IGNORE_MISSING); + } else { + // No valid context info was found. + throw new invalid_parameter_exception('Missing parameters, please provide either context level with instance id or contextid'); + } + } } /** diff --git a/lib/tests/externallib_test.php b/lib/tests/externallib_test.php index 5349ca42aac..318dfb7f96d 100644 --- a/lib/tests/externallib_test.php +++ b/lib/tests/externallib_test.php @@ -29,7 +29,7 @@ global $CFG; require_once($CFG->libdir . '/externallib.php'); -class externallib_testcase extends basic_testcase { +class externallib_testcase extends advanced_testcase { public function test_validate_params() { $params = array('text'=>'aaa', 'someid'=>'6',); $description = new external_function_parameters(array('someid' => new external_value(PARAM_INT, 'Some int value'), @@ -135,4 +135,71 @@ class externallib_testcase extends basic_testcase { $this->setExpectedException('invalid_response_exception'); $cleanedvalue = external_api::clean_returnvalue($returndesc, $testdata); } + /* + * Test external_api::get_context_from_params() + */ + public function test_get_context_from_params() { + global $USER; + + $this->resetAfterTest(true); + $course = $this->getDataGenerator()->create_course(); + $realcontext = context_course::instance($course->id); + + // Use context id. + $fetchedcontext = test_exernal_api::get_context_wrapper(array("contextid" => $realcontext->id)); + $this->assertSame($realcontext, $fetchedcontext); + + // Use context level and instance id. + $fetchedcontext = test_exernal_api::get_context_wrapper(array("contextlevel" => "course", "instanceid" => $course->id)); + $this->assertSame($realcontext, $fetchedcontext); + + // Passing wrong level + $this->setExpectedException('invalid_parameter_exception'); + $fetchedcontext = test_exernal_api::get_context_wrapper(array("contextlevel" => "random", "instanceid" => $course->id)); + } + + /* + * Test external_api::get_context()_from_params parameter validation + */ + public function test_get_context_params() { + global $USER; + + // Call without correct context details. + $this->setExpectedException('invalid_parameter_exception'); + test_exernal_api::get_context_wrapper(array('roleid' => 3, 'userid' => $USER->id)); + } + + /* + * Test external_api::get_context()_from_params parameter validation + */ + public function test_get_context_params2() { + global $USER; + + // Call without correct context details. + $this->setExpectedException('invalid_parameter_exception'); + test_exernal_api::get_context_wrapper(array('roleid' => 3, 'userid' => $USER->id, 'contextlevel' => "course")); + } + + /* + * Test external_api::get_context()_from_params parameter validation + */ + public function test_get_context_params3() { + global $USER; + + // Call without correct context details. + $this->resetAfterTest(true); + $course = self::getDataGenerator()->create_course(); + $this->setExpectedException('invalid_parameter_exception'); + test_exernal_api::get_context_wrapper(array('roleid' => 3, 'userid' => $USER->id, 'instanceid' => $course->id)); + } +} + +/* + * Just a wrapper to access protected apis for testing + */ +class test_exernal_api extends external_api { + + public static function get_context_wrapper($params) { + return self::get_context_from_params($params); + } }