From 33f6554fef6c1bd7ac36cb036b2aaf4933d38c7c Mon Sep 17 00:00:00 2001 From: Dmitrii Metelkin Date: Fri, 10 May 2024 16:24:33 +1000 Subject: [PATCH] issue #42: add user enrolment condition --- README.md | 5 +- .../condition/user_enrolment.php | 339 ++++++++++ lang/en/tool_dynamic_cohorts.php | 6 + .../condition/user_enroment_test.php | 592 ++++++++++++++++++ version.php | 4 +- 5 files changed, 942 insertions(+), 4 deletions(-) create mode 100644 classes/local/tool_dynamic_cohorts/condition/user_enrolment.php create mode 100644 tests/local/tool_dynamic_cohorts/condition/user_enroment_test.php diff --git a/README.md b/README.md index ac9013a..e2abc9b 100644 --- a/README.md +++ b/README.md @@ -59,11 +59,12 @@ Conditions are simple predicates which assert something about a user in the syst * Cohort membership (if a user is a member of cohort(s)). * Course completed (if a user has completed a course). * Course not completed (if a user has not completed a course). -* User last login (time since a user last logged in). * User created time (time since a user was created). -* User standard profile fields (e.g. first name, last name, username, auth method and etc). * User custom profile fields (text and menu types are supported). +* User enrolment (if a user is enrolled into a course). +* User last login (time since a user last logged in). * User role (if a user has a role in a given context) +* User standard profile fields (e.g. first name, last name, username, auth method and etc). ## Rules diff --git a/classes/local/tool_dynamic_cohorts/condition/user_enrolment.php b/classes/local/tool_dynamic_cohorts/condition/user_enrolment.php new file mode 100644 index 0000000..0608e1a --- /dev/null +++ b/classes/local/tool_dynamic_cohorts/condition/user_enrolment.php @@ -0,0 +1,339 @@ +. + +namespace tool_dynamic_cohorts\local\tool_dynamic_cohorts\condition; + +use tool_dynamic_cohorts\condition_base; +use tool_dynamic_cohorts\condition_sql; +use context_course; + +/** + * Condition based on user's enrolment. + * + * @package tool_dynamic_cohorts + * @copyright 2024 Catalyst IT + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class user_enrolment extends condition_base { + + /** + * Operator for not enrolled users. + */ + public const OPERATOR_NOT_ENROLLED = 0; + + /** + * Operator for enrolled users. + */ + public const OPERATOR_ENROLLED = 1; + + /** + * Condition name. + * + * @return string + */ + public function get_name(): string { + return get_string('condition:user_enrolment', 'tool_dynamic_cohorts'); + } + + /** + * Gets a list of all roles. + * + * @return array + */ + protected function get_roles(): array { + $roles = [ + 0 => get_string('any'), + ]; + foreach (role_get_names() as $role) { + if ($role->archetype === 'guest') { + continue; + } + $roles[$role->id] = $role->localname; + } + + return $roles; + } + + /** + * Returns a list of enrolment methods. + * + * @return array + */ + protected function get_enrolment_methods(): array { + $enrolmentmethods = ['' => get_string('any')]; + foreach (enrol_get_plugins(false) as $method) { + $name = $method->get_name(); + $enrolmentmethods[$name] = get_string('pluginname', 'enrol_' . $name); + } + + return $enrolmentmethods; + } + + /** + * Gets a list of operators. + * + * @return array A list of operators. + */ + protected function get_operators(): array { + return [ + self::OPERATOR_ENROLLED => get_string('enrolled', 'tool_dynamic_cohorts'), + self::OPERATOR_NOT_ENROLLED => get_string('notenrolled', 'tool_dynamic_cohorts'), + ]; + } + + /** + * Add config form elements. + * + * @param \MoodleQuickForm $mform + */ + public function config_form_add(\MoodleQuickForm $mform): void { + // Operator. + $mform->addElement( + 'select', + 'operator', + get_string('operator', 'tool_dynamic_cohorts'), + $this->get_operators() + ); + + // Course. + $mform->addElement('course', 'courseid', get_string('course')); + $mform->setType('courseid', PARAM_INT); + $mform->hideIf('courseid', 'contextlevel', 'in', [CONTEXT_SYSTEM, CONTEXT_COURSECAT]); + + // Enrolment method. + $mform->addElement( + 'select', + 'enrolmethod', + get_string('enrolmethod', 'tool_dynamic_cohorts'), + $this->get_enrolment_methods() + ); + $mform->setType('enrolmethod', PARAM_COMPONENT); + + // Role. + $mform->addElement('select', 'roleid', get_string('role'), $this->get_roles()); + $mform->setType('roleid', PARAM_INT); + } + + /** + * Validate config form elements. + * + * @param array $data Data to validate. + * @return array + */ + public function config_form_validate(array $data): array { + $errors = []; + + if (empty($data['courseid'])) { + $errors['courseid'] = get_string('required'); + } + + return $errors; + } + + /** + * Gets operator. + * + * @return int + */ + protected function get_operator_value(): int { + return $this->get_config_data()['operator'] ?? self::OPERATOR_ENROLLED; + } + + /** + * Gets configured role ID. + * + * @return int + */ + protected function get_roleid_value(): int { + return $this->get_config_data()['roleid'] ?? 0; + } + + /** + * Gets configured enrolment method. + * + * @return string + */ + protected function get_enrolment_method_value(): string { + return $this->get_config_data()['enrolmethod'] ?? ''; + } + + /** + * Gets configured course ID. + * + * @return int + */ + protected function get_courseid_value(): int { + return $this->get_config_data()['courseid'] ?? 0; + } + + /** + * Human-readable description of the configured condition. + * + * @return string + */ + public function get_config_description(): string { + global $DB; + + $coursename = $DB->get_field('course', 'fullname', ['id' => $this->get_courseid_value()]); + $coursename = format_string($coursename, true, ['context' => \context_system::instance(), 'escape' => false]); + + return get_string('condition:user_enrolment_description', 'tool_dynamic_cohorts', (object) [ + 'operator' => strtolower($this->get_operators()[$this->get_operator_value()]), + 'role' => $this->get_roles()[$this->get_roleid_value()], + 'coursename' => $coursename, + 'courseid' => $this->get_courseid_value(), + 'enrolmethod' => $this->get_enrolment_methods()[$this->get_enrolment_method_value()], + ]); + } + + /** + * Human-readable description of the broken condition. + * + * @return string + */ + public function get_broken_description(): string { + global $DB; + + // Missing role. + if (!array_key_exists($this->get_roleid_value(), $this->get_roles())) { + return get_string('missingrole', 'tool_dynamic_cohorts'); + } + + // Missing course. + if (!$DB->get_record('course', ['id' => $this->get_courseid_value()])) { + return get_string('missingcourse', 'tool_dynamic_cohorts'); + } + + // Missing enrolment method. + if (!array_key_exists($this->get_enrolment_method_value(), $this->get_enrolment_methods())) { + return get_string('missingenrolmentmethod', 'tool_dynamic_cohorts', $this->get_enrolment_method_value()); + } + + return parent::get_broken_description(); + } + + /** + * Gets SQL data for building SQL. + * + * @return condition_sql + */ + public function get_sql(): condition_sql { + $sql = new condition_sql('', '1=0', []); + + if (!$this->is_broken()) { + $join = ''; + $params = []; + + // Enrolment tables. + $uetable = condition_sql::generate_table_alias(); + $enroltable = condition_sql::generate_table_alias(); + + // Course parameter. + $courseidparam = condition_sql::generate_param_alias(); + $params[$courseidparam] = $this->get_courseid_value(); + + // In case we are filtering by enrolment method. + $enrolmethodwhere = ''; + $enrolmethod = $this->get_enrolment_method_value(); + if (!empty($enrolmethod)) { + $enrolmethodparam = condition_sql::generate_param_alias(); + $params[$enrolmethodparam] = $enrolmethod; + $enrolmethodwhere = "AND $enroltable.enrol = :{$enrolmethodparam}"; + } + + // In case we are filtering by role. + $rolesql = ''; + $rolewhere = ''; + $roleid = $this->get_roleid_value(); + if (!empty($roleid)) { + $outertable = condition_sql::generate_table_alias(); + $ratable = condition_sql::generate_table_alias(); + $roleidparam = condition_sql::generate_param_alias(); + $params[$roleidparam] = $roleid; + + $context = context_course::instance($this->get_courseid_value()); + $contexidtparam = condition_sql::generate_param_alias(); + $params[$contexidtparam] = $context->id; + + $rolesql = "LEFT JOIN (SELECT $ratable.userid + FROM {role_assignments} $ratable + WHERE $ratable.roleid = :{$roleidparam} + AND $ratable.contextid = :{$contexidtparam} + ) {$outertable} ON {$uetable}.userid = {$outertable}.userid "; + + $rolewhere = "AND {$outertable}.userid is NOT NULL"; + } + + $operator = $this->get_operator_value() == self::OPERATOR_ENROLLED ? 'EXISTS' : 'NOT EXISTS'; + + $where = "{$operator} (SELECT 1 FROM {user_enrolments} {$uetable} + JOIN {enrol} {$enroltable} ON ({$enroltable}.id = {$uetable}.enrolid AND {$enroltable}.status = 0) + $rolesql + WHERE {$uetable}.userid = u.id + AND $enroltable.courseid = :{$courseidparam} + AND {$uetable}.status = 0 + $enrolmethodwhere + $rolewhere)"; + + $sql = new condition_sql($join, $where, $params); + } + + return $sql; + } + + /** + * Is condition broken. + * + * @return bool + */ + public function is_broken(): bool { + global $DB; + + if ($this->get_config_data()) { + // Check role exists. + if (!array_key_exists($this->get_roleid_value(), $this->get_roles())) { + return true; + } + + // Check course exists. + if (!$DB->get_record('course', ['id' => $this->get_courseid_value()])) { + return true; + } + + // Check enrolment method exists. + if (!array_key_exists($this->get_enrolment_method_value(), $this->get_enrolment_methods())) { + return true; + } + } + + return false; + } + + /** + * Gets a list of event classes the condition will be triggered on. + * + * @return string[] + */ + public function get_events(): array { + return [ + 'core\event\role_assigned', + 'core\event\role_unassigned', + 'core\event\user_enrolment_created', + 'core\event\user_enrolment_updated', + 'core\event\user_enrolment_deleted', + ]; + } +} diff --git a/lang/en/tool_dynamic_cohorts.php b/lang/en/tool_dynamic_cohorts.php index 795e4ec..c5267ae 100644 --- a/lang/en/tool_dynamic_cohorts.php +++ b/lang/en/tool_dynamic_cohorts.php @@ -60,6 +60,8 @@ $string['condition:course_not_completed'] = 'Course not completed'; $string['condition:course_not_completed_description'] = 'Users who have not completed course "{$a->course}"'; $string['condition:profile_field_description'] = 'Users with {$a->field} {$a->fieldoperator} {$a->fieldvalue}'; +$string['condition:user_enrolment'] = 'User enrolment'; +$string['condition:user_enrolment_description'] = 'Users who are {$a->operator} into course "{$a->coursename}" (id {$a->courseid}) with "{$a->role}" role using "{$a->enrolmethod}" enrolment method'; $string['condition:user_last_login'] = 'User last login'; $string['condition:user_created'] = 'User created time'; $string['condition:user_profile'] = 'User standard profile field'; @@ -82,6 +84,8 @@ $string['edit_rule'] = 'Edit rule'; $string['enabled'] = 'Enabled'; $string['enable_confirm'] = 'Are you sure you want to enable rule {$a}?'; +$string['enrolmethod'] = 'Enrolment method'; +$string['enrolled'] = 'Enrolled'; $string['ever'] = 'Ever'; $string['everloggedin'] = 'Users who have logged in at least once'; $string['event:conditioncreated'] = 'Condition created'; @@ -112,11 +116,13 @@ $string['matchingusers'] = 'Matching users'; $string['missingcourse'] = 'Missing course'; $string['missingcoursecat'] = 'Missing course category'; +$string['missingenrolmentmethod'] = 'Missing enrolment method {$a}'; $string['missingrole'] = 'Missing role'; $string['name'] = 'Rule name'; $string['name_help'] = 'A human readable name of this rule.'; $string['never'] = 'Never'; $string['neverloggedin'] = 'Users who have never logged in'; +$string['notenrolled'] = 'Not enrolled'; $string['operator'] = 'Operator'; $string['or'] = 'OR'; $string['pleaseselectcohort'] = 'Please select a cohort'; diff --git a/tests/local/tool_dynamic_cohorts/condition/user_enroment_test.php b/tests/local/tool_dynamic_cohorts/condition/user_enroment_test.php new file mode 100644 index 0000000..f36d68b --- /dev/null +++ b/tests/local/tool_dynamic_cohorts/condition/user_enroment_test.php @@ -0,0 +1,592 @@ +. + +namespace tool_dynamic_cohorts\local\tool_dynamic_cohorts\condition; + +use context_system; +use context_coursecat; +use tool_dynamic_cohorts\condition_base; + +/** + * Unit tests for user_enrolment condition class. + * + * @package tool_dynamic_cohorts + * @copyright 2024 Catalyst IT + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * + * @covers \tool_dynamic_cohorts\local\tool_dynamic_cohorts\condition\user_enrolment + */ +class user_enroment_test extends \advanced_testcase { + + /** + * Get condition instance for testing. + * + * @param array $configdata Config data to be set. + * @return condition_base + */ + protected function get_condition(array $configdata = []): condition_base { + $condition = condition_base::get_instance(0, (object)[ + 'classname' => '\tool_dynamic_cohorts\local\tool_dynamic_cohorts\condition\user_enrolment', + ]); + $condition->set_config_data($configdata); + + return $condition; + } + + /** + * Test retrieving of config data. + */ + public function test_retrieving_configdata() { + $formdata = (object)[ + 'operator' => 1, + 'roleid' => 1, + 'courseid' => 1, + 'enrolmethod' => 'manual', + 'sortorder' => 0, + ]; + + $actual = $this->get_condition()::retrieve_config_data($formdata); + $expected = [ + 'operator' => 1, + 'roleid' => 1, + 'courseid' => 1, + 'enrolmethod' => 'manual', + ]; + $this->assertEquals($expected, $actual); + } + + /** + * Test setting and getting config data. + */ + public function test_set_and_get_configdata() { + $condition = $this->get_condition([ + 'operator' => 1, + 'roleid' => 1, + 'courseid' => 1, + 'enrolmethod' => 'manual', + ]); + + $this->assertEquals( + [ + 'operator' => 1, + 'roleid' => 1, + 'courseid' => 1, + 'enrolmethod' => 'manual', + ], + $condition->get_config_data() + ); + } + + /** + * Test getting config description. + */ + public function test_config_description() { + $this->resetAfterTest(); + + $roleid = $this->getDataGenerator()->create_role(); + $roles = get_all_roles(); + $course = $this->getDataGenerator()->create_course(); + + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course->id, + ]); + + $this->assertSame( + 'Users who are enrolled into course "' . $course->fullname . '" (id ' . $course->id .')' + . ' with "Any" role using "Any" enrolment method', + $condition->get_config_description(), + ); + + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course->id, + ]); + + $this->assertSame( + 'Users who are not enrolled into course "' . $course->fullname . '" (id ' . $course->id .')' + . ' with "Any" role using "Any" enrolment method', + $condition->get_config_description(), + ); + + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course->id, + 'enrolmethod' => 'manual', + ]); + + $this->assertSame( + 'Users who are enrolled into course "' . $course->fullname . '" (id ' . $course->id .')' + . ' with "Any" role using "Manual enrolments" enrolment method', + $condition->get_config_description(), + ); + + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course->id, + 'enrolmethod' => 'manual', + ]); + + $this->assertSame( + 'Users who are not enrolled into course "' . $course->fullname . '" (id ' . $course->id .')' + . ' with "Any" role using "Manual enrolments" enrolment method', + $condition->get_config_description(), + ); + + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course->id, + 'enrolmethod' => 'manual', + 'roleid' => $roleid, + + ]); + + $this->assertSame( + 'Users who are enrolled into course "' . $course->fullname . '" (id ' . $course->id .')' + . ' with "' . $roles[$roleid]->name . '" role using "Manual enrolments" enrolment method', + $condition->get_config_description(), + ); + + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course->id, + 'enrolmethod' => 'manual', + 'roleid' => $roleid, + ]); + + $this->assertSame( + 'Users who are not enrolled into course "' . $course->fullname . '" (id ' . $course->id .')' + . ' with "' . $roles[$roleid]->name . '" role using "Manual enrolments" enrolment method', + $condition->get_config_description(), + ); + } + + /** + * Test is broken and description. + */ + public function test_is_broken_and_broken_description() { + $this->resetAfterTest(); + + $roleid = $this->getDataGenerator()->create_role(); + $course = $this->getDataGenerator()->create_course(); + + $condition = condition_base::get_instance(0, (object)[ + 'classname' => '\tool_dynamic_cohorts\local\tool_dynamic_cohorts\condition\user_enrolment', + ]); + + $this->assertFalse($condition->is_broken()); + + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course->id, + ]); + $this->assertFalse($condition->is_broken()); + + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course->id, + 'roleid' => $roleid, + ]); + $this->assertFalse($condition->is_broken()); + + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course->id, + 'roleid' => $roleid, + 'enrolmethod' => 'manual', + ]); + $this->assertFalse($condition->is_broken()); + + // Invalid course. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => 7777, + 'roleid' => $roleid, + 'enrolmethod' => 'manual', + ]); + $this->assertTrue($condition->is_broken()); + $this->assertSame('Missing course', $condition->get_broken_description()); + + // Invalid role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course->id, + 'roleid' => 77777, + 'enrolmethod' => 'manual', + ]); + $this->assertTrue($condition->is_broken()); + $this->assertSame('Missing role', $condition->get_broken_description()); + + // Invalid enrole method. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course->id, + 'roleid' => $roleid, + 'enrolmethod' => 'broken', + ]); + $this->assertTrue($condition->is_broken()); + $this->assertSame('Missing enrolment method broken', $condition->get_broken_description()); + } + + /** + * Test getting correct SQL when operator "enrolled". + */ + public function test_get_sql_data_operator_enrolled() { + global $DB; + + $this->resetAfterTest(); + + $category = $this->getDataGenerator()->create_category(); + $course1 = $this->getDataGenerator()->create_course(['category' => $category->id]); + $course2 = $this->getDataGenerator()->create_course(['category' => $category->id]); + + $managerrole = $DB->get_record('role', ['shortname' => 'manager']); + $studentrole = $DB->get_record('role', ['shortname' => 'student']); + $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']); + + $manager = $this->getDataGenerator()->create_user(); + $catteacher = $this->getDataGenerator()->create_user(); + $courseteacher = $this->getDataGenerator()->create_user(); + $student1 = $this->getDataGenerator()->create_user(); + $student2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->role_assign($managerrole->id, $manager->id, context_system::instance()->id); + $this->getDataGenerator()->role_assign($teacherrole->id, $catteacher->id, context_coursecat::instance($category->id)->id); + $this->getDataGenerator()->enrol_user($courseteacher->id, $course1->id, $teacherrole->id); + $this->getDataGenerator()->enrol_user($student1->id, $course1->id, $studentrole->id); + $this->getDataGenerator()->enrol_user($student2->id, $course1->id, $studentrole->id); + + // Anyone enrolled in course 1. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course1->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(3, $actual); + $this->assertArrayHasKey($courseteacher->id, $actual); + $this->assertArrayHasKey($student1->id, $actual); + $this->assertArrayHasKey($student2->id, $actual); + + // Anyone enrolled in course 1 with student role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course1->id, + 'roleid' => $studentrole->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(2, $actual); + $this->assertArrayHasKey($student1->id, $actual); + $this->assertArrayHasKey($student2->id, $actual); + + // Anyone enrolled in course 1 with teacher role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course1->id, + 'roleid' => $teacherrole->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(1, $actual); + $this->assertArrayHasKey($courseteacher->id, $actual); + + // Anyone enrolled in course 1 with manager role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course1->id, + 'roleid' => $managerrole->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(0, $actual); + + // Anyone enrolled in course 1 with manual enrol. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course1->id, + 'enrolmethod' => 'manual', + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(3, $actual); + $this->assertArrayHasKey($courseteacher->id, $actual); + $this->assertArrayHasKey($student1->id, $actual); + $this->assertArrayHasKey($student2->id, $actual); + + // Anyone enrolled in course 1 with manual enrol and student role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course1->id, + 'enrolmethod' => 'manual', + 'roleid' => $studentrole->id, + ]); + + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(2, $actual); + $this->assertArrayHasKey($student1->id, $actual); + $this->assertArrayHasKey($student2->id, $actual); + + // Anyone enrolled in course 1 with self enrol. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course1->id, + 'enrolmethod' => 'self', + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(0, $actual); + + // Anyone enrolled in course 2. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course2->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(0, $actual); + + // Anyone enrolled in course 2 with student role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course2->id, + 'roleid' => $studentrole->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(0, $actual); + + // Anyone enrolled in course 2 with manual method. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course2->id, + 'enrolmethod' => 'manual', + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(0, $actual); + + // Anyone enrolled in course 2 with self method. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course2->id, + 'enrolmethod' => 'self', + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(0, $actual); + + // Anyone enrolled in course 2 with manual method and student role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_ENROLLED, + 'courseid' => $course2->id, + 'roleid' => $studentrole->id, + 'enrolmethod' => 'manual', + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount(0, $actual); + } + /** + * Test getting correct SQL when operator "not enrolled". + */ + public function test_get_sql_data_operator_not_enrolled() { + global $DB; + + $this->resetAfterTest(); + + $category = $this->getDataGenerator()->create_category(); + $course1 = $this->getDataGenerator()->create_course(['category' => $category->id]); + $course2 = $this->getDataGenerator()->create_course(['category' => $category->id]); + + $managerrole = $DB->get_record('role', ['shortname' => 'manager']); + $studentrole = $DB->get_record('role', ['shortname' => 'student']); + $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']); + + $manager = $this->getDataGenerator()->create_user(); + $catteacher = $this->getDataGenerator()->create_user(); + $courseteacher = $this->getDataGenerator()->create_user(); + $student1 = $this->getDataGenerator()->create_user(); + $student2 = $this->getDataGenerator()->create_user(); + + $totalusers = $DB->count_records('user'); + + $this->getDataGenerator()->role_assign($managerrole->id, $manager->id, context_system::instance()->id); + $this->getDataGenerator()->role_assign($teacherrole->id, $catteacher->id, context_coursecat::instance($category->id)->id); + $this->getDataGenerator()->enrol_user($courseteacher->id, $course1->id, $teacherrole->id); + $this->getDataGenerator()->enrol_user($student1->id, $course1->id, $studentrole->id); + $this->getDataGenerator()->enrol_user($student2->id, $course1->id, $studentrole->id); + + // Anyone not enrolled in course 1. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course1->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers - 3, $actual); + $this->assertArrayNotHasKey($courseteacher->id, $actual); + $this->assertArrayNotHasKey($student1->id, $actual); + $this->assertArrayNotHasKey($student2->id, $actual); + + // Anyone enrolled in course 1 with student role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course1->id, + 'roleid' => $studentrole->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers - 2, $actual); + $this->assertArrayNotHasKey($student1->id, $actual); + $this->assertArrayNotHasKey($student2->id, $actual); + + // Anyone enrolled in course 1 with teacher role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course1->id, + 'roleid' => $teacherrole->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers - 1, $actual); + $this->assertArrayNotHasKey($courseteacher->id, $actual); + + // Anyone enrolled in course 1 with manager role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course1->id, + 'roleid' => $managerrole->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers, $actual); + + // Anyone enrolled in course 1 with manual enrol. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course1->id, + 'enrolmethod' => 'manual', + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers - 3, $actual); + $this->assertArrayNotHasKey($courseteacher->id, $actual); + $this->assertArrayNotHasKey($student1->id, $actual); + $this->assertArrayNotHasKey($student2->id, $actual); + + // Anyone enrolled in course 1 with manual enrol and student role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course1->id, + 'enrolmethod' => 'manual', + 'roleid' => $studentrole->id, + ]); + + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers - 2, $actual); + $this->assertArrayNotHasKey($student1->id, $actual); + $this->assertArrayNotHasKey($student2->id, $actual); + + // Anyone enrolled in course 1 with self enrol. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course1->id, + 'enrolmethod' => 'self', + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers, $actual); + + // Anyone enrolled in course 2. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course2->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers, $actual); + + // Anyone enrolled in course 2 with student role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course2->id, + 'roleid' => $studentrole->id, + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers, $actual); + + // Anyone enrolled in course 2 with manual method. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course2->id, + 'enrolmethod' => 'manual', + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers, $actual); + + // Anyone enrolled in course 2 with manual method and student role. + $condition = $this->get_condition([ + 'operator' => user_enrolment::OPERATOR_NOT_ENROLLED, + 'courseid' => $course2->id, + 'roleid' => $studentrole->id, + 'enrolmethod' => 'manual', + ]); + $result = $condition->get_sql(); + $sql = "SELECT u.id FROM {user} u {$result->get_join()} WHERE {$result->get_where()}"; + $actual = $DB->get_records_sql($sql, $result->get_params()); + $this->assertCount($totalusers, $actual); + } + + /** + * Test events that the condition is listening to. + */ + public function test_get_events() { + $this->assertEquals([ + 'core\event\role_assigned', + 'core\event\role_unassigned', + 'core\event\user_enrolment_created', + 'core\event\user_enrolment_updated', + 'core\event\user_enrolment_deleted', + ], $this->get_condition()->get_events()); + } +} diff --git a/version.php b/version.php index 3f290a7..b765a46 100644 --- a/version.php +++ b/version.php @@ -25,8 +25,8 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'tool_dynamic_cohorts'; -$plugin->release = 2024042001; -$plugin->version = 2024042001; +$plugin->release = 2024051000; +$plugin->version = 2024051000; $plugin->requires = 2022112800; $plugin->supported = [401, 403]; $plugin->maturity = MATURITY_STABLE;