From 158f795c0df5b95c9eddd51d4247e112370bc023 Mon Sep 17 00:00:00 2001 From: Renaat Debleu Date: Mon, 7 Oct 2024 16:21:58 +0000 Subject: [PATCH] unenrol --- CHANGES.md | 6 +++ README.md | 3 +- classes/hook_listener.php | 21 ++++++++- classes/plugin.php | 31 +++++++------ lang/en/enrol_coursecompleted.php | 3 ++ settings.php | 50 ++++++++++++--------- tests/behat/enrol_groups.feature | 11 ++++- tests/enrol_test.php | 5 ++- tests/hook_test.php | 72 ++++++++++++++++++++++++++++++- version.php | 2 +- 10 files changed, 165 insertions(+), 39 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2b33439..a81de76 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,9 @@ +## v4.4.4 (2024-10-07) + +* version update +* unenrol when plugin is suppporting self unenrolment +* readme + ## v4.4.4 (2024-10-06) * version update diff --git a/README.md b/README.md index 141eb4e..3caddf0 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Course completed enrolment handles the enrolment of users upon completion of a c * Administrators can also enrol users who completed past courses. * Administrators can bulk modify and delete users (works only when 1 instance is installed [MDL-66652](https://tracker.moodle.org/browse/MDL-66652)). * When a user is part of a group in the first course, the user will also be part of the group with the same name in the second course. +* When a user can use self unenrolment in the course that has to be completed, the enrolment method will try to unenroll this user when configured. * When you want to enrol all users only after a particular date, configure this date as part of the course completion. * When you want that this plugin only works for a limited period, configure enrolment start and end date. Before and after this date, this plugin will do nothing. * Now you can also enrol a user in the future (the welcome message will also be sent only the moment the user is enrolled). @@ -83,7 +84,7 @@ Stable [![Build Status](https://github.com/ewallah/moodle-enrol_coursecompleted/workflows/Tests/badge.svg)](https://github.com/ewallah/moodle-enrol_coursecompleted/actions) [![Coverage Status](https://coveralls.io/repos/github/ewallah/moodle-enrol_coursecompleted/badge.svg?branch=MOODLE_404_STABLE)](https://coveralls.io/github/ewallah/moodle-enrol_coursecompleted?branch=MOODLE_404_STABLE) -![Mutation score](https://badgen.net/badge/Mutation%20Score%20Indicator/87) +![Mutation score](https://badgen.net/badge/Mutation%20Score%20Indicator/88) ## Copyright diff --git a/classes/hook_listener.php b/classes/hook_listener.php index 5c0b76b..1a47b87 100644 --- a/classes/hook_listener.php +++ b/classes/hook_listener.php @@ -21,6 +21,7 @@ use core_enrol\hook\after_enrol_instance_status_updated; use core_enrol\hook\after_user_enrolled; use core_user; +use moodle_page; use stdClass; /** @@ -39,7 +40,7 @@ class hook_listener { * @param \core_enrol\hook\after_user_enrolled $hook */ public static function send_course_welcome_message(after_user_enrolled $hook): void { - global $CFG; + global $CFG, $DB; $instance = $hook->get_enrolinstance(); if ($instance->enrol == 'coursecompleted') { if (enrol_is_enabled('coursecompleted')) { @@ -81,6 +82,24 @@ public static function send_course_welcome_message(after_user_enrolled $hook): v } } } + // Try unenrol the user from the completed course. + if ($instance->customint5 > 0) { + if ($context = context_course::instance($instance->customint1, IGNORE_MISSING)) { + $page = new moodle_page(); + require_once($CFG->dirroot . '/enrol/locallib.php'); + $course = get_course($instance->customint1); + $cem = new \course_enrolment_manager($page, $course); + $enrols = $cem->get_user_enrolments($hook->get_userid()); + foreach ($enrols as $enrol) { + $plugin = enrol_get_plugin($enrol->enrolmentinstance->enrol); + if ($instance = $DB->get_record('enrol', ['id' => $enrol->enrolid], '*', MUST_EXIST)) { + if ($plugin->allow_unenrol_user($instance, $enrol)) { + $plugin->unenrol_user($instance, $hook->get_userid()); + } + } + } + } + } } } } diff --git a/classes/plugin.php b/classes/plugin.php index dbd399d..2cd19ed 100644 --- a/classes/plugin.php +++ b/classes/plugin.php @@ -352,6 +352,7 @@ public function use_standard_editing_ui(): bool { * @return bool */ public function edit_instance_form($instance, \MoodleQuickForm $mform, $context) { + $plugin = 'enrol_coursecompleted'; $options = [ENROL_INSTANCE_ENABLED => get_string('yes'), ENROL_INSTANCE_DISABLED => get_string('no')]; $mform->addElement('select', 'status', get_string('enabled', 'admin'), $options); $mform->setDefault('status', $this->get_config('status')); @@ -371,40 +372,44 @@ public function edit_instance_form($instance, \MoodleQuickForm $mform, $context) $mform->setDefault('roleid', $this->get_config('roleid')); $arr = ['optional' => true, 'defaulttime' => $start]; - $mform->addElement('date_time_selector', 'customint4', get_string('enroldate', 'enrol_coursecompleted'), $arr); - $mform->addHelpButton('customint4', 'enroldate', 'enrol_coursecompleted'); + $mform->addElement('date_time_selector', 'customint4', get_string('enroldate', $plugin), $arr); + $mform->addHelpButton('customint4', 'enroldate', $plugin); $arr = ['optional' => true, 'defaultunit' => 86400]; - $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_coursecompleted'), $arr); + $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', $plugin), $arr); $mform->setDefault('enrolperiod', $this->get_config('enrolperiod')); - $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_coursecompleted'); + $mform->addHelpButton('enrolperiod', 'enrolperiod', $plugin); $conditions = ['onlywithcompletion' => true, 'multiple' => false, 'includefrontpage' => false]; $mform->addElement('course', 'customint1', get_string('course'), $conditions); $mform->addRule('customint1', get_string('required'), 'required', null, 'client'); - $mform->addHelpButton('customint1', 'compcourse', 'enrol_coursecompleted'); + $mform->addHelpButton('customint1', 'compcourse', $plugin); - $mform->addElement('advcheckbox', 'customint3', get_string('groups'), get_string('group', 'enrol_coursecompleted')); - $mform->addHelpButton('customint3', 'group', 'enrol_coursecompleted'); + $mform->addElement('advcheckbox', 'customint5', get_string('unenrol', 'enrol'), get_string('tryunenrol', $plugin)); + $mform->addHelpButton('customint5', 'tryunenrol', $plugin); + $mform->setDefault('customint5', $this->get_config('tryunenrol')); + + $mform->addElement('advcheckbox', 'customint3', get_string('groups'), get_string('group', $plugin)); + $mform->addHelpButton('customint3', 'group', $plugin); $mform->setDefault('customint3', $this->get_config('keepgroup')); $options = self::email_options(); $mform->addElement('select', 'customint2', get_string('categoryemail', 'admin'), $options); - $mform->addHelpButton('customint2', 'welcome', 'enrol_coursecompleted'); + $mform->addHelpButton('customint2', 'welcome', $plugin); $mform->setDefault('customint2', $this->get_config('welcome')); $arr = ['cols' => '60', 'rows' => '8']; - $mform->addElement('textarea', 'customtext1', get_string('customwelcome', 'enrol_coursecompleted'), $arr); - $mform->addHelpButton('customtext1', 'customwelcome', 'enrol_coursecompleted'); + $mform->addElement('textarea', 'customtext1', get_string('customwelcome', $plugin), $arr); + $mform->addHelpButton('customtext1', 'customwelcome', $plugin); $mform->disabledIf('customtext1', 'customint2', 'notchecked'); $arr = ['optional' => true, 'defaulttime' => $start]; - $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_coursecompleted'), $arr); - $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_coursecompleted'); + $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', $plugin), $arr); + $mform->addHelpButton('enrolstartdate', 'enrolstartdate', $plugin); $arr['defaulttime'] = $start + get_config('moodlecourse', 'courseduration'); - $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_coursecompleted'), $arr); + $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', $plugin), $arr); $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_coursecompleted'); } diff --git a/lang/en/enrol_coursecompleted.php b/lang/en/enrol_coursecompleted.php index c808039..de07810 100644 --- a/lang/en/enrol_coursecompleted.php +++ b/lang/en/enrol_coursecompleted.php @@ -72,6 +72,9 @@ $string['status_link'] = 'enrol/coursecompleted'; $string['svglearnpath'] = 'Display learning path'; $string['svglearnpath_help'] = 'Display (possible) learning path using svg icons.'; +$string['tryunenrol'] = 'Unenrol user from completed course.'; +$string['tryunenrol_help'] = 'Try to self unenrol user from completed course. +If the user was enrolled with a method that allows self unenrolment, then this plugin will try to self unenrol the user.'; $string['unenrolusers'] = 'Unenrol users'; $string['uponcompleting'] = 'Upon completing course {$a}'; $string['usersenrolled'] = '{$a} Users enrolled'; diff --git a/settings.php b/settings.php index fd2b378..1d2b1e8 100644 --- a/settings.php +++ b/settings.php @@ -31,15 +31,15 @@ $settings->add( new admin_setting_heading( - 'enrol_coursecompleted_settings', + $plugin . '_settings', '', - get_string('pluginname_desc', $plugin), + get_string('pluginname_desc', $plugin) ) ); $settings->add( new admin_setting_heading( - 'enrol_coursecompleted_defaults', + $plugin . '_defaults', get_string('enrolinstancedefaults', 'admin'), get_string('enrolinstancedefaults_desc', 'admin'), ) @@ -47,7 +47,7 @@ $settings->add( new admin_setting_configcheckbox( - 'enrol_coursecompleted/defaultenrol', + "$plugin/defaultenrol", get_string('defaultenrol', 'enrol'), get_string('defaultenrol_desc', 'enrol'), 0 @@ -67,64 +67,74 @@ $settings->add( new admin_setting_configselect( - name: 'enrol_coursecompleted/expiredaction', + name: "$plugin/expiredaction", visiblename: get_string( identifier: 'expiredaction', - component: 'enrol_fee', + component: 'enrol_fee' ), description: get_string( identifier: 'expiredaction_help', - component: 'enrol_fee', + component: 'enrol_fee' ), defaultsetting: ENROL_EXT_REMOVED_SUSPENDNOROLES, - choices: $roptions, + choices: $roptions ) ); $settings->add( new admin_setting_configselect( - name: 'enrol_coursecompleted/roleid', + name: "$plugin/roleid", visiblename: get_string( identifier: 'defaultrole', - component: $plugin, + component: $plugin ), description: get_string( identifier: 'defaultrole_desc', - component: $plugin, + component: $plugin ), defaultsetting: $student->id, - choices: $options, + choices: $options ) ); } $settings->add( new admin_setting_configduration( - 'enrol_coursecompleted/enrolperiod', + "$plugin/enrolperiod", get_string('enrolperiod', 'enrol_fee'), get_string('enrolperiod_desc', 'enrol_fee'), - 0, + 0 + ) + ); + + $settings->add( + new admin_setting_configcheckbox( + "$plugin/tryunenrol", + get_string('tryunenrol', $plugin), + get_string('tryunenrol_help', $plugin), + 0 ) ); + $settings->add( new admin_setting_configselect( - name: 'enrol_coursecompleted/welcome', + name: "$plugin/welcome", visiblename: get_string( identifier: 'welcome', - component: $plugin, + component: $plugin ), description: get_string( identifier: 'welcome_help', - component: $plugin, + component: $plugin ), defaultsetting: ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, - choices: enrol_coursecompleted_plugin::email_options(), + choices: enrol_coursecompleted_plugin::email_options() ) ); $settings->add( new admin_setting_configcheckbox( - 'enrol_coursecompleted/svglearnpath', + "$plugin/svglearnpath", get_string('svglearnpath', $plugin), get_string('svglearnpath_help', $plugin), 1 @@ -133,7 +143,7 @@ $settings->add( new admin_setting_configcheckbox( - 'enrol_coursecompleted/keepgroup', + "$plugin/keepgroup", get_string('keepgroup', $plugin), get_string('keepgroup_help', $plugin), 1 diff --git a/tests/behat/enrol_groups.feature b/tests/behat/enrol_groups.feature index 4869891..3721bdb 100644 --- a/tests/behat/enrol_groups.feature +++ b/tests/behat/enrol_groups.feature @@ -27,6 +27,9 @@ Feature: Groups kept during enrolment on course completion Scenario: User stays in same group after completing one or several courses Given I log in as "admin" And I am on "Course 1" course homepage + And I navigate to course participants + And I should see "2 participants found" + And I navigate to "Course completion" in current page administration And I expand all fieldsets And I set the field "Teacher" to "1" @@ -35,7 +38,8 @@ Feature: Groups kept during enrolment on course completion When I am on the "C2" "Course" page logged in as "teacher1" And I add "Course completed enrolment" enrolment method in "Course 2" with: - | Course | Course 1 | + | Course | Course 1 | + | Unenrol | 1 | And I am on "Course 1" course homepage And I navigate to "Reports" in current page administration And I click on "Course completion" "link" in the "region-main" "region" @@ -51,3 +55,8 @@ Feature: Groups kept during enrolment on course completion And I set the field "Type or select..." in the "Filter 1" "fieldset" to "Group 1" And I click on "Apply filters" "button" And I should see "1 participants found" + + # Student user should be unenrolled. + And I am on "Course 1" course homepage + And I navigate to course participants + And I should see "1 participants found" diff --git a/tests/enrol_test.php b/tests/enrol_test.php index 4777c2f..7350531 100644 --- a/tests/enrol_test.php +++ b/tests/enrol_test.php @@ -100,6 +100,7 @@ protected function setUp(): void { 'status' => ENROL_INSTANCE_ENABLED, 'customint1' => $this->course1->id, 'customint2' => ENROL_SEND_EMAIL_FROM_NOREPLY, + 'customint4' => 100, 'roleid' => $studentrole, ] ); @@ -361,7 +362,7 @@ public function test_library_functions(): void { public function test_library_other_functionality(): void { global $DB; $studentrole = $DB->get_field('role', 'id', ['shortname' => 'student']); - $arr = ['status' => 0, 'customint4' => 666, 'enrolenddate' => time(), 'enrolstartdate' => time() + 10000]; + $arr = ['status' => 0, 'customint1' => 666, 'enrolenddate' => time(), 'enrolstartdate' => time() + 10000]; $tmp = $this->plugin->edit_instance_validation($arr, null, $this->instance, null); $this->assertEquals('The specified course does not exist', $tmp['customint1']); $this->assertEquals('The enrolment end date cannot be earlier than the start date.', $tmp['enrolenddate']); @@ -437,6 +438,8 @@ public function test_form(): void { $this->assertStringContainsString('d="id_enrolstartdate_enabled"value="1">Enable', $cleaned); $this->assertStringContainsString('cols="60"rows="8"', $cleaned); $this->assertStringContainsString('name="customint3"class="form-check-input"value="1"id="id_customint3"', $cleaned); + $this->assertStringContainsString('fieldsetdata-fieldtype="date_time"class="m-0p-0border-0"id="id_customint4"', $cleaned); + $this->assertStringContainsString('name="customint5"class="form-check-input"value="1"id="id_customint5"', $cleaned); $this->assertStringContainsString( 'Yes', $cleaned diff --git a/tests/hook_test.php b/tests/hook_test.php index ef20720..00104b4 100644 --- a/tests/hook_test.php +++ b/tests/hook_test.php @@ -109,6 +109,7 @@ public function test_disabled(): void { 'customint1' => $this->course2->id, 'customint2' => ENROL_DO_NOT_SEND_EMAIL, 'customint4' => time() + 20, + 'customint5' => 1, ] ); $this->event->trigger(); @@ -204,7 +205,7 @@ public function test_later_messages(): void { 'roleid' => 5, 'customint1' => $this->course2->id, 'customint2' => ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, - 'customint4' => time() + 66666666, + 'customint4' => time() + 666666, ] ); $this->event->trigger(); @@ -234,6 +235,55 @@ public function test_role(): void { $this->assertTrue(user_has_role_assignment($this->student->id, 6, $context->id)); } + + /** + * Test unenrol. + * @covers \enrol_coursecompleted\hook_listener + */ + public function test_unenrol(): void { + $context = \context_course::instance($this->course2->id); + $this->assertTrue(user_has_role_assignment($this->student->id, 5, $context->id)); + + $this->plugin->add_instance( + $this->course1, + [ + 'status' => ENROL_INSTANCE_ENABLED, + 'roleid' => 5, + 'customint1' => $this->course2->id, + 'customint5' => 1, + ] + ); + $this->event->trigger(); + $context = \context_course::instance($this->course1->id); + $this->assertTrue(user_has_role_assignment($this->student->id, 5, $context->id)); + $context = \context_course::instance($this->course2->id); + $this->assertFalse(user_has_role_assignment($this->student->id, 5, $context->id)); + } + + /** + * Test not unenrol. + * @covers \enrol_coursecompleted\hook_listener + */ + public function test_not_unenrol(): void { + $context = \context_course::instance($this->course2->id); + $this->assertTrue(user_has_role_assignment($this->student->id, 5, $context->id)); + + $this->plugin->add_instance( + $this->course1, + [ + 'status' => ENROL_INSTANCE_ENABLED, + 'roleid' => 5, + 'customint1' => $this->course2->id, + 'customint5' => 0, + ] + ); + $this->event->trigger(); + $context = \context_course::instance($this->course1->id); + $this->assertTrue(user_has_role_assignment($this->student->id, 5, $context->id)); + $context = \context_course::instance($this->course2->id); + $this->assertTrue(user_has_role_assignment($this->student->id, 5, $context->id)); + } + /** * Test group. * @covers \enrol_coursecompleted\hook_listener @@ -254,6 +304,26 @@ public function test_group(): void { $this->assertTrue(groups_is_member($groupid2, $this->student->id)); } + /** + * Test not group. + * @covers \enrol_coursecompleted\hook_listener + */ + public function test_not_group(): void { + [$groupid1, $groupid2] = $this->create_groups(); + $this->plugin->add_instance( + $this->course1, + [ + 'status' => ENROL_INSTANCE_ENABLED, + 'roleid' => 5, + 'customint1' => $this->course2->id, + 'customint3' => false, + ] + ); + $this->event->trigger(); + $this->assertFalse(groups_is_member($groupid1, $this->student->id)); + $this->assertTrue(groups_is_member($groupid2, $this->student->id)); + } + /** * Create groups. * @return array diff --git a/version.php b/version.php index ec6aa3a..7f545ed 100644 --- a/version.php +++ b/version.php @@ -30,4 +30,4 @@ $plugin->maturity = MATURITY_STABLE; $plugin->supported = [404, 405]; $plugin->release = 'v4.4.4'; -$plugin->version = 2024091400; +$plugin->version = 2024100600;