public function test_group_members_only() { global $CFG; $this->setAdminUser(); $this->create_extra_users(); $CFG->enableavailability = true; $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id)); groups_assign_grouping($grouping->id, $this->groups[0]->id); // Force create an assignment with SEPARATEGROUPS. $instance = $this->getDataGenerator()->create_module('assign', array('course' => $this->course->id), array('availability' => json_encode(\core_availability\tree::get_root_json(array(\availability_grouping\condition::get_json()))), 'groupingid' => $grouping->id)); $cm = get_coursemodule_from_instance('assign', $instance->id); $context = context_module::instance($cm->id); $assign = new testable_assign($context, $cm, $this->course); $this->setUser($this->teachers[0]); get_fast_modinfo($this->course, 0, true); $this->assertCount(5, $assign->list_participants(0, true)); }
/** * Tests user restrictions, as they affect lists of users returned by * core API functions. * * This includes the groupingid option (when group mode is in use), and * standard activity restrictions using the availability API. */ public function test_user_restrictions() { global $DB, $CFG; $this->resetAfterTest(); // Use existing sample course from setUp. $courseid = $this->workshop->course->id; // Make a test grouping and two groups. $generator = $this->getDataGenerator(); $grouping = $generator->create_grouping(array('courseid' => $courseid)); $group1 = $generator->create_group(array('courseid' => $courseid)); groups_assign_grouping($grouping->id, $group1->id); $group2 = $generator->create_group(array('courseid' => $courseid)); groups_assign_grouping($grouping->id, $group2->id); // Group 3 is not in the grouping. $group3 = $generator->create_group(array('courseid' => $courseid)); // Enrol some students. $roleids = $DB->get_records_menu('role', null, '', 'shortname, id'); $student1 = $generator->create_user(); $student2 = $generator->create_user(); $student3 = $generator->create_user(); $generator->enrol_user($student1->id, $courseid, $roleids['student']); $generator->enrol_user($student2->id, $courseid, $roleids['student']); $generator->enrol_user($student3->id, $courseid, $roleids['student']); // Place students in groups (except student 3). groups_add_member($group1, $student1); groups_add_member($group2, $student2); groups_add_member($group3, $student3); // The existing workshop doesn't have any restrictions, so user lists // should include all three users. $allusers = get_enrolled_users(context_course::instance($courseid)); $result = $this->workshop->get_grouped($allusers); $this->assertCount(4, $result); $users = array_keys($result[0]); sort($users); $this->assertEquals(array($student1->id, $student2->id, $student3->id), $users); $this->assertEquals(array($student1->id), array_keys($result[$group1->id])); $this->assertEquals(array($student2->id), array_keys($result[$group2->id])); $this->assertEquals(array($student3->id), array_keys($result[$group3->id])); // Test get_users_with_capability_sql (via get_potential_authors). $users = $this->workshop->get_potential_authors(false); $this->assertCount(3, $users); $users = $this->workshop->get_potential_authors(false, $group2->id); $this->assertEquals(array($student2->id), array_keys($users)); // Create another test workshop with grouping set. $workshopitem = $this->getDataGenerator()->create_module('workshop', array('course' => $courseid, 'groupmode' => SEPARATEGROUPS, 'groupingid' => $grouping->id)); $cm = get_coursemodule_from_instance('workshop', $workshopitem->id, $courseid, false, MUST_EXIST); $workshopgrouping = new testable_workshop($workshopitem, $cm, $this->workshop->course); // This time the result should only include users and groups in the // selected grouping. $result = $workshopgrouping->get_grouped($allusers); $this->assertCount(3, $result); $users = array_keys($result[0]); sort($users); $this->assertEquals(array($student1->id, $student2->id), $users); $this->assertEquals(array($student1->id), array_keys($result[$group1->id])); $this->assertEquals(array($student2->id), array_keys($result[$group2->id])); // Test get_users_with_capability_sql (via get_potential_authors). $users = $workshopgrouping->get_potential_authors(false); $userids = array_keys($users); sort($userids); $this->assertEquals(array($student1->id, $student2->id), $userids); $users = $workshopgrouping->get_potential_authors(false, $group2->id); $this->assertEquals(array($student2->id), array_keys($users)); // Enable the availability system and create another test workshop with // availability restriction on grouping. $CFG->enableavailability = true; $workshopitem = $this->getDataGenerator()->create_module('workshop', array('course' => $courseid, 'availability' => json_encode(\core_availability\tree::get_root_json(array(\availability_grouping\condition::get_json($grouping->id)), \core_availability\tree::OP_AND, false)))); $cm = get_coursemodule_from_instance('workshop', $workshopitem->id, $courseid, false, MUST_EXIST); $workshoprestricted = new testable_workshop($workshopitem, $cm, $this->workshop->course); // The get_grouped function isn't intended to apply this restriction, // so it should be the same as the base workshop. (Note: in reality, // get_grouped is always run with the parameter being the result of // one of the get_potential_xxx functions, so it works.) $result = $workshoprestricted->get_grouped($allusers); $this->assertCount(4, $result); $this->assertCount(3, $result[0]); // The get_users_with_capability_sql-based functions should apply it. $users = $workshoprestricted->get_potential_authors(false); $userids = array_keys($users); sort($userids); $this->assertEquals(array($student1->id, $student2->id), $userids); $users = $workshoprestricted->get_potential_authors(false, $group2->id); $this->assertEquals(array($student2->id), array_keys($users)); }
/** * Sets the value(s) of an availability element. * * At present this only supports the following value 'Grouping: xxx' where * xxx is the name of a grouping. Additional value types can be added as * necessary. * * @param string $value Value code * @return void */ public function set_value($value) { global $DB; $driver = $this->session->getDriver(); // Check the availability condition is currently unset - we don't yet // support changing an existing one. $existing = $this->get_value(); if ($existing && $existing !== '{"op":"&","c":[],"showc":[]}') { throw new Exception('Cannot automatically set availability when ' . 'there is existing setting - must clear manually'); } // Check the value matches a supported format. $matches = array(); if (!preg_match('~^\\s*([^:]*):\\s*(.*?)\\s*$~', $value, $matches)) { throw new Exception('Value for availability field does not match correct ' . 'format. Example: "Grouping: G1"'); } $type = $matches[1]; $param = $matches[2]; if ($this->running_javascript()) { switch (strtolower($type)) { case 'grouping': // Set a grouping condition. $driver->click('//div[@class="availability-button"]/button'); $driver->click('//button[@id="availability_addrestriction_grouping"]'); $escparam = $this->session->getSelectorsHandler()->xpathLiteral($param); $nodes = $driver->find('//span[contains(concat(" " , @class, " "), " availability_grouping ")]//' . 'option[normalize-space(.) = ' . $escparam . ']'); if (count($nodes) != 1) { throw new Exception('Cannot find grouping in dropdown' . count($nodes)); } $node = reset($nodes); $value = $node->getValue(); $driver->selectOption('//span[contains(concat(" " , @class, " "), " availability_grouping ")]//' . 'select', $value); break; default: // We don't support other types yet. The test author must write // manual 'click on that button, etc' commands. throw new Exception('The availability type "' . $type . '" is currently not supported - must set manually'); } } else { $courseid = $driver->getValue('//input[@name="course"]'); switch (strtolower($type)) { case 'grouping': // Define result with one grouping condition. $groupingid = $DB->get_field('groupings', 'id', array('courseid' => $courseid, 'name' => $param)); $json = \core_availability\tree::get_root_json(array(\availability_grouping\condition::get_json($groupingid))); break; default: // We don't support other types yet. throw new Exception('The availability type "' . $type . '" is currently not supported - must set with JavaScript'); } $driver->setValue('//textarea[@name="availabilityconditionsjson"]', json_encode($json)); } }
/** * Tests the filter_users (bulk checking) function. */ public function test_filter_users() { global $DB, $CFG; $this->resetAfterTest(); $CFG->enableavailability = true; // Erase static cache before test. condition::wipe_static_cache(); // Make a test course and some users. $generator = $this->getDataGenerator(); $course = $generator->create_course(); $roleids = $DB->get_records_menu('role', null, '', 'shortname, id'); $teacher = $generator->create_user(); $generator->enrol_user($teacher->id, $course->id, $roleids['teacher']); $allusers = array($teacher->id => $teacher); $students = array(); for ($i = 0; $i < 3; $i++) { $student = $generator->create_user(); $students[$i] = $student; $generator->enrol_user($student->id, $course->id, $roleids['student']); $allusers[$student->id] = $student; } $info = new \core_availability\mock_info($course); $checker = new \core_availability\capability_checker($info->get_context()); // Make test groups. $group1 = $generator->create_group(array('courseid' => $course->id)); $group2 = $generator->create_group(array('courseid' => $course->id)); $grouping1 = $generator->create_grouping(array('courseid' => $course->id)); $grouping2 = $generator->create_grouping(array('courseid' => $course->id)); groups_assign_grouping($grouping1->id, $group1->id); groups_assign_grouping($grouping2->id, $group2->id); // Make page in grouping 2. $pagegen = $generator->get_plugin_generator('mod_page'); $page = $pagegen->create_instance(array('course' => $course->id, 'groupingid' => $grouping2->id, 'availability' => '{"op":"|","show":true,"c":[{"type":"grouping","activity":true}]}')); // Assign students to groups as follows (teacher is not in a group): // 0: no groups. // 1: in group 1/grouping 1. // 2: in group 2/grouping 2. groups_add_member($group1, $students[1]); groups_add_member($group2, $students[2]); // Test specific grouping. $cond = new condition((object) array('id' => (int) $grouping1->id)); $result = array_keys($cond->filter_user_list($allusers, false, $info, $checker)); ksort($result); $this->assertEquals(array($teacher->id, $students[1]->id), $result); $result = array_keys($cond->filter_user_list($allusers, true, $info, $checker)); ksort($result); $this->assertEquals(array($teacher->id, $students[0]->id, $students[2]->id), $result); // Test course-module grouping. $modinfo = get_fast_modinfo($course); $cm = $modinfo->get_cm($page->cmid); $info = new \core_availability\info_module($cm); $result = array_keys($info->filter_user_list($allusers, $course)); $this->assertEquals(array($teacher->id, $students[2]->id), $result); }
/** * Tests the behaviour of the counter in unique_sql_parameter(). * * There was a problem with static counters used to implement a sequence of * parameter placeholders (MDL-53481). As always with static variables, it * is a bit tricky to unit test the behaviour reliably as it depends on the * actual tests executed and also their order. * * To minimise risk of false expected behaviour, this test method should be * first one where {@link core_availability\tree::get_user_list_sql()} is * used. We also use higher number of condition instances to increase the * risk of the counter collision, should there remain a problem. */ public function test_unique_sql_parameter_behaviour() { global $DB; $this->resetAfterTest(); $generator = $this->getDataGenerator(); // Create a test course with multiple groupings and groups and a student in each of them. $course = $generator->create_course(); $user = $generator->create_user(); $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student')); $generator->enrol_user($user->id, $course->id, $studentroleid); // The total number of groupings and groups must not be greater than 61. // There is a limit in MySQL on the max number of joined tables. $groups = []; for ($i = 0; $i < 25; $i++) { $group = $generator->create_group(array('courseid' => $course->id)); groups_add_member($group, $user); $groups[] = $group; } $groupings = []; for ($i = 0; $i < 25; $i++) { $groupings[] = $generator->create_grouping(array('courseid' => $course->id)); } foreach ($groupings as $grouping) { foreach ($groups as $group) { groups_assign_grouping($grouping->id, $group->id); } } $info = new \core_availability\mock_info($course); // Make a huge tree with 'AND' of all groups and groupings conditions. $conditions = []; foreach ($groups as $group) { $conditions[] = \availability_group\condition::get_json($group->id); } foreach ($groupings as $groupingid) { $conditions[] = \availability_grouping\condition::get_json($grouping->id); } shuffle($conditions); $tree = new tree(tree::get_root_json($conditions)); list($sql, $params) = $tree->get_user_list_sql(false, $info, false); // This must not throw exception. $DB->fix_sql_params($sql, $params); }