From f53b0b84d233768b2f9fe6b6fe22b5d4a5d92150 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Mon, 15 Jan 2024 15:14:50 +0700 Subject: [PATCH 01/11] MDL-4188 core_enrol: Added enrol_plugin::get_welcome_message_contact() Including in this commit - enrol_self_plugin::get_welcome_email_contact() has been deprecated --- enrol/self/lib.php | 49 ++++++--------------------- enrol/self/tests/self_test.php | 24 ++++++++----- enrol/upgrade.txt | 2 ++ lib/enrollib.php | 61 ++++++++++++++++++++++++++++++++++ lib/upgrade.txt | 1 + 5 files changed, 90 insertions(+), 47 deletions(-) diff --git a/enrol/self/lib.php b/enrol/self/lib.php index 5ecfb19aee1..a4f8390695b 100644 --- a/enrol/self/lib.php +++ b/enrol/self/lib.php @@ -425,7 +425,7 @@ class enrol_self_plugin extends enrol_plugin { $subject = get_string('welcometocourse', 'enrol_self', format_string($course->fullname, true, array('context'=>$context))); $sendoption = $instance->customint4; - $contact = $this->get_welcome_email_contact($sendoption, $context); + $contact = $this->get_welcome_message_contact($sendoption, $context); // Directly emailing welcome message rather than using messaging. email_to_user($user, $contact, $subject, $messagetext, $messagehtml); @@ -1154,46 +1154,17 @@ class enrol_self_plugin extends enrol_plugin { * @param int $sendoption send email from constant ENROL_SEND_EMAIL_FROM_* * @param $context context where the user will be fetched * @return mixed|stdClass the contact user object. + * @deprecated since Moodle 4.4 + * @see \enrol_plugin::get_welcome_message_contact() + * @todo MDL-81185 Final deprecation in Moodle 4.8. */ + #[\core\attribute\deprecated('enrol_plugin::get_welcome_message_contact', since: '4.4', mdl: 'MDL-4188')] public function get_welcome_email_contact($sendoption, $context) { - global $CFG; - - $contact = null; - // Send as the first user assigned as the course contact. - if ($sendoption == ENROL_SEND_EMAIL_FROM_COURSE_CONTACT) { - $rusers = array(); - if (!empty($CFG->coursecontact)) { - $croles = explode(',', $CFG->coursecontact); - list($sort, $sortparams) = users_order_by_sql('u'); - // We only use the first user. - $i = 0; - do { - $userfieldsapi = \core_user\fields::for_name(); - $allnames = $userfieldsapi->get_sql('u', false, '', '', false)->selects; - $rusers = get_role_users($croles[$i], $context, true, 'u.id, u.confirmed, u.username, '. $allnames . ', - u.email, r.sortorder, ra.id', 'r.sortorder, ra.id ASC, ' . $sort, null, '', '', '', '', $sortparams); - $i++; - } while (empty($rusers) && !empty($croles[$i])); - } - if ($rusers) { - $contact = array_values($rusers)[0]; - } - } else if ($sendoption == ENROL_SEND_EMAIL_FROM_KEY_HOLDER) { - // Send as the first user with enrol/self:holdkey capability assigned in the course. - list($sort) = users_order_by_sql('u'); - $keyholders = get_users_by_capability($context, 'enrol/self:holdkey', 'u.*', $sort); - if (!empty($keyholders)) { - $contact = array_values($keyholders)[0]; - } - } - - // If send welcome email option is set to no reply or if none of the previous options have - // returned a contact send welcome message as noreplyuser. - if ($sendoption == ENROL_SEND_EMAIL_FROM_NOREPLY || empty($contact)) { - $contact = core_user::get_noreply_user(); - } - - return $contact; + \core\deprecation::emit_deprecation_if_present(__FUNCTION__); + return $this->get_welcome_message_contact( + sendoption: $sendoption, + context: $context, + ); } /** diff --git a/enrol/self/tests/self_test.php b/enrol/self/tests/self_test.php index 66e37d5e7d4..722fea9b965 100644 --- a/enrol/self/tests/self_test.php +++ b/enrol/self/tests/self_test.php @@ -1014,22 +1014,26 @@ class self_test extends \advanced_testcase { $DB->update_record('enrol', $instance1); $selfplugin->update_status($instance1, ENROL_INSTANCE_ENABLED); - // We do not have a teacher enrolled at this point, so it should send as no reply user. - $contact = $selfplugin->get_welcome_email_contact(ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, $context); - $this->assertEquals($noreplyuser, $contact); + // This should return null. + $contact = $selfplugin->get_welcome_message_contact(ENROL_DO_NOT_SEND_EMAIL, $context); + $this->assertNull($contact); + + // We do not have a teacher enrolled at this point, so it should return null. + $contact = $selfplugin->get_welcome_message_contact(ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, $context); + $this->assertNull($contact); // By default, course contact is assigned to teacher role. // Enrol a teacher, now it should send emails from teacher email's address. $selfplugin->enrol_user($instance1, $user1->id, $editingteacherrole->id); // We should get the teacher email. - $contact = $selfplugin->get_welcome_email_contact(ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, $context); + $contact = $selfplugin->get_welcome_message_contact(ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, $context); $this->assertEquals($user1->username, $contact->username); $this->assertEquals($user1->email, $contact->email); // Now let's enrol another teacher. $selfplugin->enrol_user($instance1, $user2->id, $editingteacherrole->id); - $contact = $selfplugin->get_welcome_email_contact(ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, $context); + $contact = $selfplugin->get_welcome_message_contact(ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, $context); $this->assertEquals($user1->username, $contact->username); $this->assertEquals($user1->email, $contact->email); @@ -1044,21 +1048,25 @@ class self_test extends \advanced_testcase { assign_capability('enrol/self:holdkey', CAP_ALLOW, $managerrole->id, $context); // We should get the manager email contact. - $contact = $selfplugin->get_welcome_email_contact(ENROL_SEND_EMAIL_FROM_KEY_HOLDER, $context); + $contact = $selfplugin->get_welcome_message_contact(ENROL_SEND_EMAIL_FROM_KEY_HOLDER, $context); $this->assertEquals($user3->username, $contact->username); $this->assertEquals($user3->email, $contact->email); // Now let's enrol another manager. $selfplugin->enrol_user($instance1, $user4->id, $managerrole->id); - $contact = $selfplugin->get_welcome_email_contact(ENROL_SEND_EMAIL_FROM_KEY_HOLDER, $context); + $contact = $selfplugin->get_welcome_message_contact(ENROL_SEND_EMAIL_FROM_KEY_HOLDER, $context); $this->assertEquals($user3->username, $contact->username); $this->assertEquals($user3->email, $contact->email); $instance1->customint4 = ENROL_SEND_EMAIL_FROM_NOREPLY; $DB->update_record('enrol', $instance1); - $contact = $selfplugin->get_welcome_email_contact(ENROL_SEND_EMAIL_FROM_NOREPLY, $context); + $contact = $selfplugin->get_welcome_message_contact(ENROL_SEND_EMAIL_FROM_NOREPLY, $context); $this->assertEquals($noreplyuser, $contact); + + $this->expectException(\moodle_exception::class); + $this->expectExceptionMessage('Invalid send option'); + $contact = $selfplugin->get_welcome_message_contact(10, $context); } /** diff --git a/enrol/upgrade.txt b/enrol/upgrade.txt index b369e119886..add63582cce 100644 --- a/enrol/upgrade.txt +++ b/enrol/upgrade.txt @@ -13,6 +13,8 @@ information provided here is intended especially for developers. plugin since it is already supported in CSV course upload. For self enrolment plugin, find_instance() returns first available instance in the course. * A sesskey is no longer passed to the enrol/test_settings.php page so it can no longer be required in test_settings(). +* enrol_self_plugin::get_welcome_email_contact() has been deprecated. + Please use enrol_plugin::get_welcome_message_contact() instead. === 4.3 === diff --git a/lib/enrollib.php b/lib/enrollib.php index e8e74811637..61e0f5119d2 100644 --- a/lib/enrollib.php +++ b/lib/enrollib.php @@ -3571,4 +3571,65 @@ abstract class enrol_plugin { // Plugins can override this if they can uniquely identify an instance. return null; } + + /** + * Get the "from" contact which the message will be sent from. + * + * @param int $sendoption send email from constant ENROL_SEND_EMAIL_FROM_* + * @param context $context where the user will be fetched from. + * @return null|stdClass the contact user object. + */ + public function get_welcome_message_contact( + int $sendoption, + context $context, + ): ?stdClass { + global $CFG; + + $acceptedsendoptions = [ + ENROL_DO_NOT_SEND_EMAIL, + ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, + ENROL_SEND_EMAIL_FROM_KEY_HOLDER, + ENROL_SEND_EMAIL_FROM_NOREPLY, + ]; + if (!in_array($sendoption, $acceptedsendoptions)) { + throw new coding_exception('Invalid send option'); + } + if ($sendoption === ENROL_DO_NOT_SEND_EMAIL) { + return null; + } + $contact = null; + // Send as the first user assigned as the course contact. + if ($sendoption === ENROL_SEND_EMAIL_FROM_COURSE_CONTACT) { + $rusers = []; + if (!empty($CFG->coursecontact)) { + $croles = explode(',', $CFG->coursecontact); + [$sort, $sortparams] = users_order_by_sql('u'); + // We only use the first user. + $i = 0; + do { + $userfieldsapi = \core_user\fields::for_name(); + $allnames = $userfieldsapi->get_sql('u', false, '', '', false)->selects; + $rusers = get_role_users($croles[$i], $context, true, 'u.id, u.confirmed, u.username, '. $allnames . ', + u.email, r.sortorder, ra.id', 'r.sortorder, ra.id ASC, ' . $sort, null, '', '', '', '', $sortparams); + $i++; + } while (empty($rusers) && !empty($croles[$i])); + } + if ($rusers) { + $contact = array_values($rusers)[0]; + } + } else if ($sendoption === ENROL_SEND_EMAIL_FROM_KEY_HOLDER) { + // Send as the first user with enrol/self:holdkey capability assigned in the course. + [$sort] = users_order_by_sql('u'); + $keyholders = get_users_by_capability($context, 'enrol/self:holdkey', 'u.*', $sort); + if (!empty($keyholders)) { + $contact = array_values($keyholders)[0]; + } + } + + if ($sendoption === ENROL_SEND_EMAIL_FROM_NOREPLY) { + $contact = core_user::get_noreply_user(); + } + + return $contact; + } } diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 163bde8bb81..68adac414c7 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -137,6 +137,7 @@ information provided here is intended especially for developers. because there aren't cases in core. - Deprecation: Cannot use the "Test" suffix on abstract test case classes. Proceed to rename them to end with "TestCase" instead. +* There is a new method called enrol_plugin::get_welcome_message_contact() that returns the contact details for the course welcome message. === 4.3 === From 1593f02492e68522ddaa8c9448cd5e113d6eeb40 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Mon, 15 Jan 2024 15:18:06 +0700 Subject: [PATCH 02/11] MDL-4188 core: Added course welcome message notification --- lang/en/moodle.php | 1 + lib/db/messages.php | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/lang/en/moodle.php b/lang/en/moodle.php index ceb8ba9be58..e2fd3c4f54d 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1308,6 +1308,7 @@ $string['messageprovider:coursecontentupdated'] = 'Course content changes'; $string['messageprovider:courserequestapproved'] = 'Course creation request approval notification'; $string['messageprovider:courserequested'] = 'Course creation request notification'; $string['messageprovider:courserequestrejected'] = 'Course creation request rejection notification'; +$string['messageprovider:enrolcoursewelcomemessage'] = 'Welcome message for new course enrolments'; $string['messageprovider:errors'] = 'Important errors with the site'; $string['messageprovider:errors_help'] = 'These are important errors that an administrator should know about.'; $string['messageprovider:gradenotifications'] = 'Grade notifications'; diff --git a/lib/db/messages.php b/lib/db/messages.php index 56b67ecd630..ab5bd27c06b 100644 --- a/lib/db/messages.php +++ b/lib/db/messages.php @@ -216,4 +216,13 @@ $messageproviders = array ( 'email' => MESSAGE_FORCED, ], ], + + // Course welcome message. + 'enrolcoursewelcomemessage' => [ + 'defaults' => [ + 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, + 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, + 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, + ], + ], ); From b6af21bec08b355aa8b4277977e951d048574ff4 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Tue, 16 Jan 2024 09:14:44 +0700 Subject: [PATCH 03/11] MDL-4188 enrol_manual: Send course welcome message on enrolment AMOS BEGIN CPY [customwelcomemessage,enrol_self],[customwelcomemessage,core_enrol] CPY [customwelcomemessage_help,enrol_self],[customwelcomemessage_help,core_enrol] AMOS END --- enrol/classes/hook/after_user_enrolled.php | 9 ++ .../classes/user_enrolment_callbacks.php | 46 ++++++ enrol/manual/db/hooks.php | 32 ++++ enrol/manual/lib.php | 67 ++++++++ enrol/manual/settings.php | 16 ++ .../manual/tests/behat/welcomemessage.feature | 123 ++++++++++++++ enrol/manual/tests/lib_test.php | 152 ++++++++++++++++++ enrol/manual/version.php | 2 +- enrol/self/lang/en/enrol_self.php | 17 +- lang/en/enrol.php | 13 ++ lang/en/moodle.php | 2 +- lib/enrollib.php | 99 +++++++++++- lib/upgrade.txt | 1 + 13 files changed, 568 insertions(+), 11 deletions(-) create mode 100644 enrol/manual/classes/user_enrolment_callbacks.php create mode 100644 enrol/manual/db/hooks.php create mode 100644 enrol/manual/tests/behat/welcomemessage.feature diff --git a/enrol/classes/hook/after_user_enrolled.php b/enrol/classes/hook/after_user_enrolled.php index 3d00294d310..9336f7793a9 100644 --- a/enrol/classes/hook/after_user_enrolled.php +++ b/enrol/classes/hook/after_user_enrolled.php @@ -49,4 +49,13 @@ class after_user_enrolled { public function get_userid(): int { return $this->userenrolmentinstance->userid; } + + /** + * Get the enrol instance. + * + * @return stdClass The enrol instance. + */ + public function get_enrolinstance(): stdClass { + return $this->enrolinstance; + } } diff --git a/enrol/manual/classes/user_enrolment_callbacks.php b/enrol/manual/classes/user_enrolment_callbacks.php new file mode 100644 index 00000000000..ac46dbb3388 --- /dev/null +++ b/enrol/manual/classes/user_enrolment_callbacks.php @@ -0,0 +1,46 @@ +. + +namespace enrol_manual; + +/** + * Hook callbacks to get the enrolment information. + * + * @package enrol_manual + * @copyright 2024 Huong Nguyen + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class user_enrolment_callbacks { + + /** + * Callback for the user_enrolment hook. + * + * @param \core_enrol\hook\after_user_enrolled $hook + */ + public static function send_course_welcome_message(\core_enrol\hook\after_user_enrolled $hook): void { + $instance = $hook->get_enrolinstance(); + // Send welcome message. + if ($instance->enrol == 'manual' && $instance->customint1 && $instance->customint1 !== ENROL_DO_NOT_SEND_EMAIL) { + $plugin = enrol_get_plugin($instance->enrol); + $plugin->send_course_welcome_message_to_user( + instance: $instance, + userid: $hook->get_userid(), + sendoption: $instance->customint1, + message: $instance->customtext1, + ); + } + } +} diff --git a/enrol/manual/db/hooks.php b/enrol/manual/db/hooks.php new file mode 100644 index 00000000000..d1236526f0a --- /dev/null +++ b/enrol/manual/db/hooks.php @@ -0,0 +1,32 @@ +. + +/** + * Hook callbacks for enrol_manual + * + * @package enrol_manual + * @copyright 2024 Huong Nguyen + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$callbacks = [ + [ + 'hook' => core_enrol\hook\after_user_enrolled::class, + 'callback' => 'enrol_manual\user_enrolment_callbacks::send_course_welcome_message', + ], +]; diff --git a/enrol/manual/lib.php b/enrol/manual/lib.php index 89e935c366b..c41d5d970d0 100644 --- a/enrol/manual/lib.php +++ b/enrol/manual/lib.php @@ -135,6 +135,7 @@ class enrol_manual_plugin extends enrol_plugin { 'expirynotify' => $expirynotify, 'notifyall' => $expirynotify == 2 ? 1 : 0, 'expirythreshold' => $this->get_config('expirythreshold', 86400), + 'customint1' => $this->get_config('sendcoursewelcomemessage'), ); return $this->add_instance($course, $fields); } @@ -604,6 +605,62 @@ class enrol_manual_plugin extends enrol_plugin { $mform->addHelpButton('expirythreshold', 'expirythreshold', 'core_enrol'); $mform->disabledIf('expirythreshold', 'expirynotify', 'eq', 0); + // Course welcome message. + $mform->addElement( + 'select', + 'customint1', + get_string( + identifier: 'sendcoursewelcomemessage', + component: 'core_enrol', + ), + enrol_send_welcome_email_options(), + ); + $mform->addHelpButton( + elementname: 'customint1', + identifier: 'sendcoursewelcomemessage', + component: 'core_enrol', + ); + + $options = [ + 'cols' => '60', + 'rows' => '8', + ]; + $mform->addElement( + 'textarea', + 'customtext1', + get_string( + identifier: 'customwelcomemessage', + component: 'core_enrol', + ), + $options, + ); + $mform->setDefault('customtext1', get_string('customwelcomemessageplaceholder', 'core_enrol')); + $mform->hideIf( + elementname: 'customtext1', + dependenton: 'customint1', + condition: 'eq', + value: ENROL_DO_NOT_SEND_EMAIL, + ); + + // Static form elements cannot be hidden by hideIf() so we need to add a dummy group. + // See: https://tracker.moodle.org/browse/MDL-66251. + $group[] = $mform->createElement( + 'static', + 'customwelcomemessage_extra_help', + null, + get_string( + identifier: 'customwelcomemessage_help', + component: 'core_enrol', + ), + ); + $mform->addGroup($group, 'group_customwelcomemessage_extra_help', '', ' ', false); + $mform->hideIf( + elementname: 'group_customwelcomemessage_extra_help', + dependenton: 'customint1', + condition: 'eq', + value: ENROL_DO_NOT_SEND_EMAIL, + ); + if (enrol_accessing_via_instance($instance)) { $warntext = get_string('instanceeditselfwarningtext', 'core_enrol'); $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), $warntext); @@ -689,6 +746,16 @@ class enrol_manual_plugin extends enrol_plugin { 'expirythreshold' => 0, ]; } + + /** + * Returns defaults for new instances. + * + * @return array + */ + public function get_instance_defaults(): array { + $fields['customint1'] = $this->get_config('sendcoursewelcomemessage'); + return $fields; + } } /** diff --git a/enrol/manual/settings.php b/enrol/manual/settings.php index 8dcabcc5951..9a79378c9bd 100644 --- a/enrol/manual/settings.php +++ b/enrol/manual/settings.php @@ -81,4 +81,20 @@ if ($ADMIN->fulltree) { $settings->add(new admin_setting_configduration('enrol_manual/expirythreshold', get_string('expirythreshold', 'core_enrol'), get_string('expirythreshold_help', 'core_enrol'), 86400, 86400)); + // Course welcome message. + $settings->add( + new admin_setting_configselect( + name: 'enrol_manual/sendcoursewelcomemessage', + visiblename: get_string( + identifier: 'sendcoursewelcomemessage', + component: 'core_enrol', + ), + description: get_string( + identifier: 'sendcoursewelcomemessage_help', + component: 'core_enrol', + ), + defaultsetting: ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, + choices: enrol_send_welcome_email_options(), + ), + ); } diff --git a/enrol/manual/tests/behat/welcomemessage.feature b/enrol/manual/tests/behat/welcomemessage.feature new file mode 100644 index 00000000000..17d5377d5c6 --- /dev/null +++ b/enrol/manual/tests/behat/welcomemessage.feature @@ -0,0 +1,123 @@ +@enrol @enrol_manual +Feature: A course welcome message will be sent to the user when they are enrolled in a course + In order to let the user know they have been enrolled in a course + As a teacher + I want the user to receive a welcome message when they are enrolled in a course + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | manager | Manager | User | manager@example.com | + | teacher | Teacher | User | teacher@example.com | + | user1 | First | User | first@example.com | + | user2 | Second | User | second@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + | Course 2 | C2 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | manager | C1 | manager | + | teacher | C1 | editingteacher | + | teacher | C2 | editingteacher | + + @javascript + Scenario: Manager should see the new settings for course welcome message + Given I am on the "C1" "Enrolled users" page logged in as manager + And I set the field "Participants tertiary navigation" to "Enrolment methods" + When I click on "Edit" "link" in the "Manual enrolments" "table_row" + Then I should see "Send course welcome message" + And the field "Send course welcome message" matches value "From the course contact" + And I should see "Custom welcome message" + And the field "Custom welcome message" matches value "Dear {$a->fullname}, you have successfully been enrolled to course {$a->coursename}" + And I should see "Accepted formats: Plain text or Moodle-auto format. HTML tags and multi-lang tags are also accepted, as well as the following placeholders:" + And I set the field "Send course welcome message" to "No" + And I should not see "Custom welcome message" + And I should not see "Accepted formats: Plain text or Moodle-auto format. HTML tags and multi-lang tags are also accepted, as well as the following placeholders:" + + @javascript + Scenario: Student should not receive a welcome message if the setting is disabled + Given I am on the "C1" "Enrolled users" page logged in as manager + And I set the field "Participants tertiary navigation" to "Enrolment methods" + And I click on "Edit" "link" in the "Manual enrolments" "table_row" + And I set the field "Send course welcome message" to "No" + And I press "Save changes" + And I am on the "C1" "Enrolled users" page logged in as teacher + And I press "Enrol users" + And I set the field "Select users" to "First User" + And I should see "First User" + And I click on "Enrol users" "button" in the "Enrol users" "dialogue" + And I should see "Active" in the "First User" "table_row" + When I am on the "C1" "course" page logged in as user1 + Then I should not see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" + + @javascript + Scenario: Students should receive a welcome message if the setting is enabled - Default message + Given I am on the "C1" "Enrolled users" page logged in as teacher + # Enrol first user to Course 1. + And I press "Enrol users" + And I set the field "Select users" to "First User" + And I should see "First User" + And I click on "Enrol users" "button" in the "Enrol users" "dialogue" + # Enrol second user to Course 2. + And I am on the "C2" "Enrolled users" page + And I press "Enrol users" + And I set the field "Select users" to "Second User" + And I should see "Second User" + And I click on "Enrol users" "button" in the "Enrol users" "dialogue" + # Login as first user and check the notification. + When I am on the "C1" "course" page logged in as user1 + Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" + And I open the notification popover + And I should see "Welcome to Course 1" + And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" + And I should see "Dear First User, you have successfully been enrolled to course Course 1" + # Login as second user and check the notification. + And I am on the "C1" "course" page logged in as user2 + And I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" + And I open the notification popover + And I should see "Welcome to Course 2" + And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" + And I should see "Dear Second User, you have successfully been enrolled to course Course 2" + + @javascript + Scenario: Students should receive a welcome message if the setting is enabled - Custom message + Given I am on the "C1" "Enrolled users" page logged in as manager + And I set the field "Participants tertiary navigation" to "Enrolment methods" + And I click on "Edit" "link" in the "Manual enrolments" "table_row" + And I set the field "Custom welcome message" to multiline: + """ + Dear {$a->fullname}, you have successfully been enrolled to course {$a->coursename}. + Your email address: {$a->email} + Your first name: {$a->firstname} + Your last name: {$a->lastname} + Your course role: {$a->courserole} + """ + And I press "Save changes" + # Enrol first user and second user to Course 1. + And the following "course enrolments" exist: + | user | course | role | + | user1 | C1 | student | + | user2 | C1 | student | + # Login as first user and check the notification. + When I am on the "C1" "course" page logged in as user1 + Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" + And I open the notification popover + And I should see "Welcome to Course 1" + And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" + And I should see "Dear First User, you have successfully been enrolled to course Course 1" + And I should see "Your email address: first@example.com" + And I should see "Your first name: First" + And I should see "Your last name: User" + And I should see "Your course role: student" + # Login as second user and check the notification. + When I am on the "C1" "course" page logged in as user2 + Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" + And I open the notification popover + And I should see "Welcome to Course 1" + And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" + And I should see "Dear Second User, you have successfully been enrolled to course Course 1" + And I should see "Your email address: second@example.com" + And I should see "Your first name: Second" + And I should see "Your last name: User" + And I should see "Your course role: student" diff --git a/enrol/manual/tests/lib_test.php b/enrol/manual/tests/lib_test.php index ce51f186a7d..28258e8d7f8 100644 --- a/enrol/manual/tests/lib_test.php +++ b/enrol/manual/tests/lib_test.php @@ -789,4 +789,156 @@ class lib_test extends \advanced_testcase { $this->assertEquals($expected->id, $actual->id); } + /** + * Test send_course_welcome_message_to_user() method. + * + * @covers \enrol_plugin::send_course_welcome_message_to_user + */ + public function test_send_course_welcome_message(): void { + global $DB; + $this->resetAfterTest(); + + // Create course. + $course = $this->getDataGenerator()->create_course([ + 'fullname' => 'Course 1', + 'shortname' => 'C1', + ]); + // Create users. + $student = $this->getDataGenerator()->create_user(); + $teacher1 = $this->getDataGenerator()->create_user(); + $teacher2 = $this->getDataGenerator()->create_user(); + $noreplyuser = \core_user::get_noreply_user(); + // Enrol users. + $this->getDataGenerator()->enrol_user($teacher1->id, $course->id, 'editingteacher'); + $this->getDataGenerator()->enrol_user($teacher2->id, $course->id, 'editingteacher'); + // Get manual plugin. + $manualplugin = enrol_get_plugin('manual'); + $maninstance = $DB->get_record( + 'enrol', + ['courseid' => $course->id, 'enrol' => 'manual'], + '*', + MUST_EXIST, + ); + + // Test 1: Send welcome message to user from course contact with default message. + // Redirect messages. + $messagesink = $this->redirectMessages(); + $manualplugin->send_course_welcome_message_to_user( + instance: $maninstance, + userid: $student->id, + sendoption: ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, + message: '', + ); + $messages = $messagesink->get_messages_by_component_and_type( + 'moodle', + 'enrolcoursewelcomemessage', + ); + $this->assertNotEmpty($messages); + $message = reset($messages); + + // The message should be sent from the first teacher. + $this->assertEquals($teacher1->id, $message->useridfrom); + $this->assertStringContainsString($course->fullname, $message->subject); + $this->assertEquals( + get_string( + 'customwelcomemessageplaceholder', + 'core_enrol', + ['fullname' => fullname($student), 'coursename' => $course->fullname], + ), + $message->fullmessage, + ); + + // Clear sink. + $messagesink->clear(); + + // Test 2: Send welcome message to user from course contact with a custom message. + // Unenrol the first teacher from course. + $manualplugin->unenrol_user($maninstance, $teacher1->id); + // Redirect messages. + $messagesink = $this->redirectMessages(); + $manualplugin->send_course_welcome_message_to_user( + instance: $maninstance, + userid: $student->id, + sendoption: ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, + message: 'Your email address: {$a->email}, your first name: {$a->firstname}, your last name: {$a->lastname}', + ); + $messages = $messagesink->get_messages_by_component_and_type( + 'moodle', + 'enrolcoursewelcomemessage', + ); + $this->assertNotEmpty($messages); + $message = reset($messages); + + // The message should be sent from the second teacher. + $this->assertEquals($teacher2->id, $message->useridfrom); + $this->assertStringContainsString($course->fullname, $message->subject); + $this->assertEquals( + 'Your email address: ' . $student->email . ', your first name: ' . $student->firstname . ', your last name: ' . + $student->lastname, + $message->fullmessage, + ); + // Clear sink. + $messagesink->clear(); + + // Test 3: Send welcome message to user from no-reply user with a custom message. + // Redirect messages. + $messagesink = $this->redirectMessages(); + $manualplugin->send_course_welcome_message_to_user( + instance: $maninstance, + userid: $student->id, + sendoption: ENROL_SEND_EMAIL_FROM_NOREPLY, + message: 'Your email address: {$a->email}, your first name: {$a->firstname}, your last name: {$a->lastname}', + ); + $messages = $messagesink->get_messages_by_component_and_type( + 'moodle', + 'enrolcoursewelcomemessage', + ); + $this->assertNotEmpty($messages); + $message = reset($messages); + + // The message should be sent from the noreply user. + $this->assertEquals($noreplyuser->id, $message->useridfrom); + $this->assertStringContainsString($course->fullname, $message->subject); + $this->assertEquals( + 'Your email address: ' . $student->email . ', your first name: ' . $student->firstname . ', your last name: ' . + $student->lastname, + $message->fullmessage, + ); + // Clear sink. + $messagesink->clear(); + + } + + /** + * Test send_course_welcome_message_to_user() method via hook. + * + * @covers \enrol_plugin::send_course_welcome_message_to_user + */ + public function test_send_course_welcome_message_via_hook(): void { + global $DB; + $this->resetAfterTest(); + $messagesink = $this->redirectMessages(); + $course = $this->getDataGenerator()->create_course([ + 'fullname' => 'Course 1', + 'shortname' => 'C1', + ]); + $maninstance = $DB->get_record( + 'enrol', + ['courseid' => $course->id, 'enrol' => 'manual'], + '*', + MUST_EXIST, + ); + $maninstance->customint1 = ENROL_SEND_EMAIL_FROM_NOREPLY; + $DB->update_record('enrol', $maninstance); + $student = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($student->id, $course->id); + $messages = $messagesink->get_messages_by_component_and_type( + 'moodle', + 'enrolcoursewelcomemessage', + ); + $this->assertNotEmpty($messages); + $message = reset($messages); + $this->assertStringContainsString($course->fullname, $message->subject); + } + } diff --git a/enrol/manual/version.php b/enrol/manual/version.php index 5516803a188..fec37be7983 100644 --- a/enrol/manual/version.php +++ b/enrol/manual/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2023100900; // The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2023100901; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2023100400; // Requires this Moodle version. $plugin->component = 'enrol_manual'; // Full name of the plugin (used for diagnostics) diff --git a/enrol/self/lang/en/enrol_self.php b/enrol/self/lang/en/enrol_self.php index 39128095ec7..c5c0afa10f6 100644 --- a/enrol/self/lang/en/enrol_self.php +++ b/enrol/self/lang/en/enrol_self.php @@ -30,14 +30,15 @@ $string['cohortonly'] = 'Only cohort members'; $string['cohortonly_help'] = 'Self enrolment may be restricted to members of a specified cohort only. Note that changing this setting has no effect on existing enrolments.'; $string['confirmbulkdeleteenrolment'] = 'Are you sure you want to delete these user enrolments?'; $string['customwelcomemessage'] = 'Custom welcome message'; -$string['customwelcomemessage_help'] = 'A custom welcome message may be added as plain text or Moodle-auto format, including HTML tags and multi-lang tags. - -The following placeholders may be included in the message: - -* Course name {$a->coursename} -* Link to user\'s profile page {$a->profileurl} -* User email {$a->email} -* User fullname {$a->fullname}'; +$string['customwelcomemessage_help'] = 'Accepted formats: Plain text or Moodle-auto format. HTML tags and multi-lang tags are also accepted, as well as the following placeholders: +
+* Course name {$a->coursename}
+* Link to user\'s profile page {$a->profileurl}
+* User email {$a->email}
+* User fullname {$a->fullname}
+* User first name {$a->firstname}
+* User last name {$a->lastname}
+* User course role {$a->courserole}
'; $string['defaultrole'] = 'Default role assignment'; $string['defaultrole_desc'] = 'Select role which should be assigned to users during self enrolment'; $string['deleteselectedusers'] = 'Delete selected user enrolments'; diff --git a/lang/en/enrol.php b/lang/en/enrol.php index 014e59d32c4..7d6ad4b494c 100644 --- a/lang/en/enrol.php +++ b/lang/en/enrol.php @@ -34,6 +34,17 @@ $string['assignnotpermitted'] = 'You do not have permission or can not assign ro $string['bulkuseroperation'] = 'Bulk user operation'; $string['configenrolplugins'] = 'Please select all required plugins and arrange then in appropriate order.'; $string['custominstancename'] = 'Custom instance name'; +$string['customwelcomemessage'] = 'Custom welcome message'; +$string['customwelcomemessage_help'] = 'Accepted formats: Plain text or Moodle-auto format. HTML tags and multi-lang tags are also accepted, as well as the following placeholders: +
+* Course name {$a->coursename}
+* Link to user\'s profile page {$a->profileurl}
+* User email {$a->email}
+* User fullname {$a->fullname}
+* User first name {$a->firstname}
+* User last name {$a->lastname}
+* User course role {$a->courserole}
'; +$string['customwelcomemessageplaceholder'] = 'Dear {$a->fullname}, you have successfully been enrolled to course {$a->coursename}'; $string['defaultenrol'] = 'Add instance to new courses'; $string['defaultenrol_desc'] = 'It is possible to add this plugin to all new courses by default.'; $string['deleteinstanceconfirm'] = 'You are about to delete the enrolment method "{$a->name}". All {$a->users} users currently enrolled using this method will be unenrolled and any course-related data such as users\' grades, group membership or forum subscriptions will be deleted. @@ -129,6 +140,8 @@ $string['rolefromsystem'] = '{$a->role} (Assigned at site level)'; $string['sendfromcoursecontact'] = 'From the course contact'; $string['sendfromkeyholder'] = 'From the key holder'; $string['sendfromnoreply'] = 'From the no-reply address'; +$string['sendcoursewelcomemessage'] = 'Send course welcome message'; +$string['sendcoursewelcomemessage_help'] = 'When enrolling a user or cohort in the course, they may be sent a welcome message email. If sent from the course contact (by default the teacher), and more than one user has this role, the email is sent from the first user to be assigned the role.'; $string['startdatetoday'] = 'Today'; $string['synced'] = 'Synced'; $string['testsettings'] = 'Test settings'; diff --git a/lang/en/moodle.php b/lang/en/moodle.php index e2fd3c4f54d..8b89877de87 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -2404,7 +2404,7 @@ $string['welcometocoursetext'] = 'Welcome to {$a->coursename}! If you have not done so already, you should edit your profile page so that we can learn more about you: - {$a->profileurl}'; +{$a->profileurl}'; $string['whatforlink'] = 'What do you want to do with the link?'; $string['whatforpage'] = 'What do you want to do with the text?'; $string['whatisyourage'] = 'What is your age?'; diff --git a/lib/enrollib.php b/lib/enrollib.php index 61e0f5119d2..cd2c2f77c54 100644 --- a/lib/enrollib.php +++ b/lib/enrollib.php @@ -3610,7 +3610,7 @@ abstract class enrol_plugin { $userfieldsapi = \core_user\fields::for_name(); $allnames = $userfieldsapi->get_sql('u', false, '', '', false)->selects; $rusers = get_role_users($croles[$i], $context, true, 'u.id, u.confirmed, u.username, '. $allnames . ', - u.email, r.sortorder, ra.id', 'r.sortorder, ra.id ASC, ' . $sort, null, '', '', '', '', $sortparams); + u.email, r.sortorder, ra.id AS raid', 'r.sortorder, ra.id ASC, ' . $sort, null, '', '', '', '', $sortparams); $i++; } while (empty($rusers) && !empty($croles[$i])); } @@ -3632,4 +3632,101 @@ abstract class enrol_plugin { return $contact; } + + /** + * Send course welcome message to user. + * + * @param stdClass $instance Enrol instance. + * @param int $userid User ID. + * @param int $sendoption Send email from constant ENROL_SEND_EMAIL_FROM_* + * @param null|string $message Message to send to the user. + */ + public function send_course_welcome_message_to_user( + stdClass $instance, + int $userid, + int $sendoption, + ?string $message = '', + ): void { + global $DB; + $context = context_course::instance($instance->courseid); + $user = core_user::get_user($userid); + $course = get_course($instance->courseid); + $courserole = $DB->get_field( + table: 'role', + return: 'shortname', + conditions: ['id' => $instance->roleid], + ); + + $a = new stdClass(); + $a->coursename = format_string($course->fullname, true, ['context' => $context]); + $a->profileurl = (new moodle_url( + url: '/user/view.php', + params: [ + 'id' => $user->id, + 'course' => $instance->courseid, + ], + ))->out(); + $a->fullname = fullname($user); + + if ($message && trim($message) !== '') { + $placeholders = [ + '{$a->coursename}', + '{$a->profileurl}', + '{$a->fullname}', + '{$a->email}', + '{$a->firstname}', + '{$a->lastname}', + '{$a->courserole}', + ]; + $values = [ + $a->coursename, + $a->profileurl, + fullname($user), + $user->email, + $user->firstname, + $user->lastname, + $courserole, + ]; + $message = str_replace($placeholders, $values, $message); + if (strpos($message, '<') === false) { + // Plain text only. + $messagetext = $message; + $messagehtml = text_to_html($messagetext, null, false, true); + } else { + // This is most probably the tag/newline soup known as FORMAT_MOODLE. + $messagehtml = format_text($message, FORMAT_MOODLE, + ['context' => $context, 'para' => false, 'newlines' => true, 'filter' => true]); + $messagetext = html_to_text($messagehtml); + } + } else { + $messagetext = get_string('customwelcomemessageplaceholder', 'core_enrol', $a); + $messagehtml = text_to_html($messagetext, null, false, true); + } + + $subject = get_string('welcometocourse', 'moodle', format_string($course->fullname, true, ['context' => $context])); + $contact = $this->get_welcome_message_contact( + sendoption: $sendoption, + context: $context, + ); + if (!$contact) { + // Cannot find the contact to send the message from. + return; + } + + $message = new \core\message\message(); + $message->courseid = $instance->courseid; + $message->component = 'moodle'; + $message->name = 'enrolcoursewelcomemessage'; + $message->userfrom = $contact; + $message->userto = $user; + $message->subject = $subject; + $message->fullmessage = $messagetext; + $message->fullmessageformat = FORMAT_MARKDOWN; + $message->fullmessagehtml = $messagehtml; + $message->notification = 1; + $message->contexturl = $a->profileurl; + $message->contexturlname = $course->fullname; + + message_send($message); + } } diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 68adac414c7..0691cbe6d95 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -138,6 +138,7 @@ information provided here is intended especially for developers. - Deprecation: Cannot use the "Test" suffix on abstract test case classes. Proceed to rename them to end with "TestCase" instead. * There is a new method called enrol_plugin::get_welcome_message_contact() that returns the contact details for the course welcome message. +* There is a new method called enrol_plugin::send_course_welcome_message_to_user() that sends the course welcome message to a user. === 4.3 === From 097a6d46e0976fea7f1db4fcb868d6257bb41c45 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Thu, 4 Apr 2024 16:07:25 +0700 Subject: [PATCH 04/11] MDL-4188 enrol_self: Send course welcome message on enrolment Including in this commit: - Use language strings from core_enrol to match with enrol_manual - Minor update for UI so Custom welcome message text area will not be shown if the Send course welcome message is set to No - enrol_self now using Hook API to send the welcome message - enrol_self_plugin::email_welcome_message() has been deprecated - Added Behat test to test the welcome message --- .../self/classes/user_enrolment_callbacks.php | 46 +++++++ enrol/self/db/hooks.php | 32 +++++ enrol/self/lang/en/deprecated.txt | 4 + enrol/self/lang/en/enrol_self.php | 24 ++-- enrol/self/lib.php | 79 ++++++------ enrol/self/tests/behat/welcomemessage.feature | 115 ++++++++++++++++++ enrol/upgrade.txt | 2 + 7 files changed, 250 insertions(+), 52 deletions(-) create mode 100644 enrol/self/classes/user_enrolment_callbacks.php create mode 100644 enrol/self/db/hooks.php create mode 100644 enrol/self/lang/en/deprecated.txt create mode 100644 enrol/self/tests/behat/welcomemessage.feature diff --git a/enrol/self/classes/user_enrolment_callbacks.php b/enrol/self/classes/user_enrolment_callbacks.php new file mode 100644 index 00000000000..61177fdf0eb --- /dev/null +++ b/enrol/self/classes/user_enrolment_callbacks.php @@ -0,0 +1,46 @@ +. + +namespace enrol_self; + +/** + * Hook callbacks to get the enrolment information. + * + * @package enrol_self + * @copyright 2024 Huong Nguyen + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class user_enrolment_callbacks { + + /** + * Callback for the user_enrolment hook. + * + * @param \core_enrol\hook\after_user_enrolled $hook + */ + public static function send_course_welcome_message(\core_enrol\hook\after_user_enrolled $hook): void { + $instance = $hook->get_enrolinstance(); + // Send welcome message. + if ($instance->enrol == 'self' && $instance->customint4 && $instance->customint4 !== ENROL_DO_NOT_SEND_EMAIL) { + $plugin = enrol_get_plugin($instance->enrol); + $plugin->send_course_welcome_message_to_user( + instance: $instance, + userid: $hook->get_userid(), + sendoption: $instance->customint4, + message: $instance->customtext1, + ); + } + } +} diff --git a/enrol/self/db/hooks.php b/enrol/self/db/hooks.php new file mode 100644 index 00000000000..8625dab8978 --- /dev/null +++ b/enrol/self/db/hooks.php @@ -0,0 +1,32 @@ +. + +/** + * Hook callbacks for enrol_self + * + * @package enrol_self + * @copyright 2024 Huong Nguyen + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$callbacks = [ + [ + 'hook' => core_enrol\hook\after_user_enrolled::class, + 'callback' => 'enrol_self\user_enrolment_callbacks::send_course_welcome_message', + ], +]; diff --git a/enrol/self/lang/en/deprecated.txt b/enrol/self/lang/en/deprecated.txt new file mode 100644 index 00000000000..3865b9499d5 --- /dev/null +++ b/enrol/self/lang/en/deprecated.txt @@ -0,0 +1,4 @@ +customwelcomemessage,enrol_self +customwelcomemessage_help,enrol_self +welcometocourse,enrol_self +welcometocoursetext,enrol_self diff --git a/enrol/self/lang/en/enrol_self.php b/enrol/self/lang/en/enrol_self.php index c5c0afa10f6..3cd6068521c 100644 --- a/enrol/self/lang/en/enrol_self.php +++ b/enrol/self/lang/en/enrol_self.php @@ -29,16 +29,6 @@ $string['cohortnonmemberinfo'] = 'Only members of cohort \'{$a}\' can self-enrol $string['cohortonly'] = 'Only cohort members'; $string['cohortonly_help'] = 'Self enrolment may be restricted to members of a specified cohort only. Note that changing this setting has no effect on existing enrolments.'; $string['confirmbulkdeleteenrolment'] = 'Are you sure you want to delete these user enrolments?'; -$string['customwelcomemessage'] = 'Custom welcome message'; -$string['customwelcomemessage_help'] = 'Accepted formats: Plain text or Moodle-auto format. HTML tags and multi-lang tags are also accepted, as well as the following placeholders: -
-* Course name {$a->coursename}
-* Link to user\'s profile page {$a->profileurl}
-* User email {$a->email}
-* User fullname {$a->fullname}
-* User first name {$a->firstname}
-* User last name {$a->lastname}
-* User course role {$a->courserole}
'; $string['defaultrole'] = 'Default role assignment'; $string['defaultrole_desc'] = 'Select role which should be assigned to users during self enrolment'; $string['deleteselectedusers'] = 'Delete selected user enrolments'; @@ -126,10 +116,22 @@ $string['unenroluser'] = 'Do you really want to unenrol "{$a->user}" from course $string['unenrolusers'] = 'Unenrol users'; $string['usepasswordpolicy'] = 'Use password policy'; $string['usepasswordpolicy_desc'] = 'Use standard password policy for enrolment keys.'; +$string['privacy:metadata'] = 'The Self enrolment plugin does not store any personal data.'; + +// Deprecated since Moodle 4.4. +$string['customwelcomemessage'] = 'Custom welcome message'; +$string['customwelcomemessage_help'] = 'Accepted formats: Plain text or Moodle-auto format. HTML tags and multi-lang tags are also accepted, as well as the following placeholders: +
+* Course name {$a->coursename}
+* Link to user\'s profile page {$a->profileurl}
+* User email {$a->email}
+* User fullname {$a->fullname}
+* User first name {$a->firstname}
+* User last name {$a->lastname}
+* User course role {$a->courserole}
'; $string['welcometocourse'] = 'Welcome to {$a}'; $string['welcometocoursetext'] = 'Welcome to {$a->coursename}! If you have not done so already, you should edit your profile page so that we can learn more about you: {$a->profileurl}'; -$string['privacy:metadata'] = 'The Self enrolment plugin does not store any personal data.'; diff --git a/enrol/self/lib.php b/enrol/self/lib.php index a4f8390695b..2001f77e97d 100644 --- a/enrol/self/lib.php +++ b/enrol/self/lib.php @@ -174,10 +174,6 @@ class enrol_self_plugin extends enrol_plugin { } } } - // Send welcome message. - if ($instance->customint4 != ENROL_DO_NOT_SEND_EMAIL) { - $this->email_welcome_message($instance, $USER); - } } /** @@ -392,43 +388,19 @@ class enrol_self_plugin extends enrol_plugin { * @param stdClass $instance * @param stdClass $user user record * @return void + * @deprecated since Moodle 4.4 + * @see \enrol_plugin::send_course_welcome_message_to_user() + * @todo MDL-81185 Final deprecation in Moodle 4.8. */ + #[\core\attribute\deprecated('enrol_plugin::send_course_welcome_message_to_user', since: '4.4', mdl: 'MDL-4188')] protected function email_welcome_message($instance, $user) { - global $CFG, $DB; - - $course = $DB->get_record('course', array('id'=>$instance->courseid), '*', MUST_EXIST); - $context = context_course::instance($course->id); - - $a = new stdClass(); - $a->coursename = format_string($course->fullname, true, array('context'=>$context)); - $a->profileurl = "$CFG->wwwroot/user/view.php?id=$user->id&course=$course->id"; - - if (!is_null($instance->customtext1) && trim($instance->customtext1) !== '') { - $message = $instance->customtext1; - $key = array('{$a->coursename}', '{$a->profileurl}', '{$a->fullname}', '{$a->email}'); - $value = array($a->coursename, $a->profileurl, fullname($user), $user->email); - $message = str_replace($key, $value, $message); - if (strpos($message, '<') === false) { - // Plain text only. - $messagetext = $message; - $messagehtml = text_to_html($messagetext, null, false, true); - } else { - // This is most probably the tag/newline soup known as FORMAT_MOODLE. - $messagehtml = format_text($message, FORMAT_MOODLE, array('context'=>$context, 'para'=>false, 'newlines'=>true, 'filter'=>true)); - $messagetext = html_to_text($messagehtml); - } - } else { - $messagetext = get_string('welcometocoursetext', 'enrol_self', $a); - $messagehtml = text_to_html($messagetext, null, false, true); - } - - $subject = get_string('welcometocourse', 'enrol_self', format_string($course->fullname, true, array('context'=>$context))); - - $sendoption = $instance->customint4; - $contact = $this->get_welcome_message_contact($sendoption, $context); - - // Directly emailing welcome message rather than using messaging. - email_to_user($user, $contact, $subject, $messagetext, $messagehtml); + \core\deprecation::emit_deprecation_if_present(__FUNCTION__); + $this->send_course_welcome_message_to_user( + instance: $instance, + userid: $user->id, + sendoption: $instance->customint4, + message: $instance->customtext1, + ); } /** @@ -960,8 +932,33 @@ class enrol_self_plugin extends enrol_plugin { $mform->addHelpButton('customint4', 'sendcoursewelcomemessage', 'enrol_self'); $options = array('cols' => '60', 'rows' => '8'); - $mform->addElement('textarea', 'customtext1', get_string('customwelcomemessage', 'enrol_self'), $options); - $mform->addHelpButton('customtext1', 'customwelcomemessage', 'enrol_self'); + $mform->addElement('textarea', 'customtext1', get_string('customwelcomemessage', 'core_enrol'), $options); + $mform->setDefault('customtext1', get_string('customwelcomemessageplaceholder', 'core_enrol')); + $mform->hideIf( + elementname: 'customtext1', + dependenton: 'customint4', + condition: 'eq', + value: ENROL_DO_NOT_SEND_EMAIL, + ); + + // Static form elements cannot be hidden by hideIf() so we need to add a dummy group. + // See: https://tracker.moodle.org/browse/MDL-66251. + $group[] = $mform->createElement( + 'static', + 'customwelcomemessage_extra_help', + null, + get_string( + identifier: 'customwelcomemessage_help', + component: 'core_enrol', + ), + ); + $mform->addGroup($group, 'group_customwelcomemessage_extra_help', '', ' ', false); + $mform->hideIf( + elementname: 'group_customwelcomemessage_extra_help', + dependenton: 'customint4', + condition: 'eq', + value: ENROL_DO_NOT_SEND_EMAIL, + ); if (enrol_accessing_via_instance($instance)) { $warntext = get_string('instanceeditselfwarningtext', 'core_enrol'); diff --git a/enrol/self/tests/behat/welcomemessage.feature b/enrol/self/tests/behat/welcomemessage.feature new file mode 100644 index 00000000000..3cc8e7df22f --- /dev/null +++ b/enrol/self/tests/behat/welcomemessage.feature @@ -0,0 +1,115 @@ +@enrol @enrol_self +Feature: A course welcome message will be sent to the user when they auto-enrol themself in a course + In order to let the user know they have been auto-enrol themself in a course successfully + As a teacher + I want the user to receive a welcome message when they auto-enrol themself in a course + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | manager | Manager | User | manager@example.com | + | teacher | Teacher | User | teacher@example.com | + | user1 | First | User | first@example.com | + | user2 | Second | User | second@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + | Course 2 | C2 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | manager | C1 | manager | + | teacher | C1 | editingteacher | + | teacher | C2 | editingteacher | + And I log in as "admin" + And I add "Self enrolment" enrolment method in "Course 1" with: + | Custom instance name | Test student enrolment | + And I add "Self enrolment" enrolment method in "Course 2" with: + | Custom instance name | Test student enrolment | + + @javascript + Scenario: Manager should see the new settings for course welcome message + Given I am on the "C1" "Enrolled users" page logged in as manager + And I set the field "Participants tertiary navigation" to "Enrolment methods" + When I click on "Edit" "link" in the "Test student enrolment" "table_row" + Then I should see "Send course welcome message" + And the field "Send course welcome message" matches value "From the course contact" + And I should see "Custom welcome message" + And the field "Custom welcome message" matches value "Dear {$a->fullname}, you have successfully been enrolled to course {$a->coursename}" + And I should see "Accepted formats: Plain text or Moodle-auto format. HTML tags and multi-lang tags are also accepted, as well as the following placeholders:" + And I set the field "Send course welcome message" to "No" + And I should not see "Custom welcome message" + And I should not see "Accepted formats: Plain text or Moodle-auto format. HTML tags and multi-lang tags are also accepted, as well as the following placeholders:" + + @javascript + Scenario: Student should not receive a welcome message if the setting is disabled + Given I am on the "C1" "Enrolled users" page logged in as manager + And I set the field "Participants tertiary navigation" to "Enrolment methods" + And I click on "Edit" "link" in the "Test student enrolment" "table_row" + And I set the field "Send course welcome message" to "No" + And I press "Save changes" + And I log in as "user1" + And I am on "Course 1" course homepage + When I press "Enrol me" + Then I should not see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" + + @javascript + Scenario: Students should receive a welcome message if the setting is enabled - Default message + # Login as first user and check the notification. + Given I log in as "user1" + And I am on "Course 1" course homepage + When I press "Enrol me" + Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" + And I open the notification popover + And I should see "Welcome to Course 1" + And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" + And I should see "Dear First User, you have successfully been enrolled to course Course 1" + # Login as second user and check the notification. + And I log in as "user2" + And I am on "Course 2" course homepage + And I press "Enrol me" + And I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" + And I open the notification popover + And I should see "Welcome to Course 2" + And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" + And I should see "Dear Second User, you have successfully been enrolled to course Course 2" + + @javascript + Scenario: Students should receive a welcome message if the setting is enabled - Custom message + Given I am on the "C1" "Enrolled users" page logged in as manager + And I set the field "Participants tertiary navigation" to "Enrolment methods" + And I click on "Edit" "link" in the "Test student enrolment" "table_row" + And I set the field "Custom welcome message" to multiline: + """ + Dear {$a->fullname}, you have successfully been enrolled to course {$a->coursename}. + Your email address: {$a->email} + Your first name: {$a->firstname} + Your last name: {$a->lastname} + Your course role: {$a->courserole} + """ + And I press "Save changes" + # Login as first user and check the notification. + And I log in as "user1" + And I am on "Course 1" course homepage + When I press "Enrol me" + Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" + And I open the notification popover + And I should see "Welcome to Course 1" + And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" + And I should see "Dear First User, you have successfully been enrolled to course Course 1" + And I should see "Your email address: first@example.com" + And I should see "Your first name: First" + And I should see "Your last name: User" + And I should see "Your course role: student" + # Login as second user and check the notification. + And I log in as "user2" + And I am on "Course 1" course homepage + And I press "Enrol me" + And I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" + And I open the notification popover + And I should see "Welcome to Course 1" + And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" + And I should see "Dear Second User, you have successfully been enrolled to course Course 1" + And I should see "Your email address: second@example.com" + And I should see "Your first name: Second" + And I should see "Your last name: User" + And I should see "Your course role: student" diff --git a/enrol/upgrade.txt b/enrol/upgrade.txt index add63582cce..6b1d2450f7b 100644 --- a/enrol/upgrade.txt +++ b/enrol/upgrade.txt @@ -15,6 +15,8 @@ information provided here is intended especially for developers. * A sesskey is no longer passed to the enrol/test_settings.php page so it can no longer be required in test_settings(). * enrol_self_plugin::get_welcome_email_contact() has been deprecated. Please use enrol_plugin::get_welcome_message_contact() instead. +* enrol_self_plugin::email_welcome_message() has been deprecated. + Please use enrol_plugin::send_course_welcome_message_to_user() instead. === 4.3 === From b68a89a4da1c59d0600261febf87bb5251fc2a22 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Tue, 30 Jan 2024 10:56:37 +0700 Subject: [PATCH 05/11] MDL-4188 mod_forum: Modify PHPUnit to use the new sink method --- mod/forum/tests/mail_test.php | 7 ++++--- mod/forum/tests/maildigest_test.php | 10 +++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/mod/forum/tests/mail_test.php b/mod/forum/tests/mail_test.php index e3e2ec820c9..ed7af4e5e5f 100644 --- a/mod/forum/tests/mail_test.php +++ b/mod/forum/tests/mail_test.php @@ -927,7 +927,7 @@ class mail_test extends \advanced_testcase { $this->queue_tasks_and_assert($expect); $this->send_notifications_and_assert($author, [$post]); - $messages = $this->messagesink->get_messages(); + $messages = $this->messagesink->get_messages_by_component('mod_forum'); $message = reset($messages); $this->assertEquals($author->id, $message->useridfrom); $this->assertEquals($expectedsubject, $message->subject); @@ -1600,8 +1600,9 @@ class mail_test extends \advanced_testcase { $this->send_notifications_and_assert($author, [$post]); $this->send_notifications_and_assert($commenter, [$post]); - $messages = $this->messagesink->get_messages(); - $customdata = json_decode($messages[0]->customdata); + $messages = $this->messagesink->get_messages_by_component('mod_forum'); + $messages = reset($messages); + $customdata = json_decode($messages->customdata); $this->assertEquals($forum->id, $customdata->instance); $this->assertEquals($forum->cmid, $customdata->cmid); $this->assertEquals($post->id, $customdata->postid); diff --git a/mod/forum/tests/maildigest_test.php b/mod/forum/tests/maildigest_test.php index 27e026feeac..d4981370e78 100644 --- a/mod/forum/tests/maildigest_test.php +++ b/mod/forum/tests/maildigest_test.php @@ -62,7 +62,7 @@ class maildigest_test extends \advanced_testcase { $this->mailsink = $this->redirectEmails(); // Confirm that we have an empty message sink so far. - $messages = $this->messagesink->get_messages(); + $messages = $this->messagesink->get_messages_by_component('mod_forum'); $this->assertEquals(0, count($messages)); $messages = $this->mailsink->get_messages(); @@ -405,7 +405,9 @@ class maildigest_test extends \advanced_testcase { $this->send_digests_and_assert($user, $posts); // The user does not, by default, have permission to view the fullname. - $messagecontent = $this->messagesink->get_messages()[0]->fullmessage; + $messages = $this->messagesink->get_messages_by_component('mod_forum'); + $messages = reset($messages); + $messagecontent = $messages->fullmessage; // Assert that the expected name is present (lastname only). $this->assertStringContainsString(fullname($user, false), $messagecontent); @@ -456,7 +458,9 @@ class maildigest_test extends \advanced_testcase { // The user does not, by default, have permission to view the fullname. // However we have given the user that capability so we expect to see both firstname and lastname. - $messagecontent = $this->messagesink->get_messages()[0]->fullmessage; + $messages = $this->messagesink->get_messages_by_component('mod_forum'); + $messages = reset($messages); + $messagecontent = $messages->fullmessage; // Assert that the expected name is present (lastname only). $this->assertStringContainsString(fullname($user, false), $messagecontent); From 70f85b92e3d45a28635c4205088cf28e1204b499 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Tue, 30 Jan 2024 11:06:05 +0700 Subject: [PATCH 06/11] MDL-4188 mod_bigbluebuttonbn: Modify PHPUnit to use the new sink method --- .../tests/local/helpers/mod_helper_trait_test.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mod/bigbluebuttonbn/tests/local/helpers/mod_helper_trait_test.php b/mod/bigbluebuttonbn/tests/local/helpers/mod_helper_trait_test.php index 6656a0897b0..fe7c63466b6 100644 --- a/mod/bigbluebuttonbn/tests/local/helpers/mod_helper_trait_test.php +++ b/mod/bigbluebuttonbn/tests/local/helpers/mod_helper_trait_test.php @@ -157,8 +157,12 @@ class mod_helper_trait_test extends \advanced_testcase { ob_start(); $this->runAdhocTasks(); ob_get_clean(); // Suppress output as it can fail the test. - $this->assertEquals(1, $messagesink->count()); - $firstmessage = $messagesink->get_messages()[0]; + $messages = $messagesink->get_messages_by_component_and_type( + component: 'core', + type: 'coursecontentupdated', + ); + $this->assertEquals(1, count($messages)); + $firstmessage = reset($messages); $this->assertStringContainsString('is new in', $firstmessage->smallmessage); } From c4e07686ea34eec7f869ce5744abd77c6dbb76d5 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Tue, 30 Jan 2024 11:23:09 +0700 Subject: [PATCH 07/11] MDL-4188 core_message: Modify PHPUnit to turn off course welcome message --- message/tests/privacy/provider_test.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/message/tests/privacy/provider_test.php b/message/tests/privacy/provider_test.php index 9df8c83204f..5f5a59181a4 100644 --- a/message/tests/privacy/provider_test.php +++ b/message/tests/privacy/provider_test.php @@ -41,10 +41,20 @@ defined('MOODLE_INTERNAL') || die(); */ class provider_test extends \core_privacy\tests\provider_testcase { + /** + * Setup. + */ + protected function setUp(): void { + parent::setUp(); + // Turn off the course welcome message, so we can easily test other messages. + set_config('sendcoursewelcomemessage', 0, 'enrol_manual'); + } + /** * Test for provider::get_metadata(). */ public function test_get_metadata() { + $this->resetAfterTest(); $collection = new collection('core_message'); $newcollection = provider::get_metadata($collection); $itemcollection = $newcollection->get_collection(); From ad4f35f5a38d0c582d61ede7813741da6c600caa Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Tue, 30 Jan 2024 11:37:18 +0700 Subject: [PATCH 08/11] MDL-4188 core_course: Modify PHPUnit to use the new sink method --- course/tests/courserequest_test.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/course/tests/courserequest_test.php b/course/tests/courserequest_test.php index e47df520e4b..6a8086b1bb3 100644 --- a/course/tests/courserequest_test.php +++ b/course/tests/courserequest_test.php @@ -113,7 +113,7 @@ class courserequest_test extends \advanced_testcase { $this->setAdminUser(); $sink = $this->redirectMessages(); $id = $cr->approve(); - $this->assertCount(1, $sink->get_messages()); + $this->assertCount(1, $sink->get_messages_by_component_and_type('core', 'courserequestapproved')); $sink->close(); $course = $DB->get_record('course', array('id' => $id)); $this->assertEquals($data->fullname, $course->fullname); @@ -133,7 +133,7 @@ class courserequest_test extends \advanced_testcase { $this->setAdminUser(); $sink = $this->redirectMessages(); $id = $cr->approve(); - $this->assertCount(1, $sink->get_messages()); + $this->assertCount(1, $sink->get_messages_by_component_and_type('core', 'courserequestapproved')); $sink->close(); $course = $DB->get_record('course', array('id' => $id)); $this->assertEquals($data->category, $course->category); From 66e8bf76f991d34c3696958fadf67a3e22c2bdd9 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Tue, 30 Jan 2024 12:32:21 +0700 Subject: [PATCH 09/11] MDL-4188 core: Modify PHPUnit to use the new sink method --- lib/tests/accesslib_test.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/tests/accesslib_test.php b/lib/tests/accesslib_test.php index cb38c113e70..60aa99c2063 100644 --- a/lib/tests/accesslib_test.php +++ b/lib/tests/accesslib_test.php @@ -31,6 +31,17 @@ defined('MOODLE_INTERNAL') || die(); * Note: execution may take many minutes especially on slower servers. */ class accesslib_test extends advanced_testcase { + + /** + * Setup. + */ + protected function setUp(): void { + parent::setUp(); + $this->resetAfterTest(); + // Turn off the course welcome message, so we can easily test other messages. + set_config('sendcoursewelcomemessage', 0, 'enrol_manual'); + } + /** * Verify comparison of context instances in phpunit asserts. */ From f64fda43d540f4a8c41f5bb17c879b64a1a79674 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Wed, 27 Mar 2024 10:00:21 +0700 Subject: [PATCH 10/11] MDL-4188 behat: Turn off course welcome message Turn off the course welcome message, so we can easily test other messages. --- analytics/tests/behat/manage_models.feature | 4 +++- .../behat/message_manage_notification_preferences.feature | 4 +++- mod/assign/tests/behat/submission_notifications.feature | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/analytics/tests/behat/manage_models.feature b/analytics/tests/behat/manage_models.feature index 0c7daa0a1de..13e721e3275 100644 --- a/analytics/tests/behat/manage_models.feature +++ b/analytics/tests/behat/manage_models.feature @@ -5,8 +5,10 @@ Feature: Manage analytics models I need to create and use a model Background: + # Turn off the course welcome message, so we can easily test other messages. Given the following config values are set as admin: - | onlycli | 0 | analytics | + | onlycli | 0 | analytics | + | sendcoursewelcomemessage | 0 | enrol_manual | And the following "users" exist: | username | firstname | lastname | email | | teacher1 | Teacher | 1 | teacher1@example.com | diff --git a/message/tests/behat/message_manage_notification_preferences.feature b/message/tests/behat/message_manage_notification_preferences.feature index 4c562f8e9cd..c60d12a4998 100644 --- a/message/tests/behat/message_manage_notification_preferences.feature +++ b/message/tests/behat/message_manage_notification_preferences.feature @@ -9,8 +9,10 @@ Feature: Manage notification preferences - Email | username | firstname | lastname | email | | student1 | Student | 1 | student1@example.com | | student2 | Student | 2 | student2@example.com | + # Turn off the course welcome message, so we can easily test other messages. And the following config values are set as admin: - | messaging | 1 | + | messaging | 1 | core | + | sendcoursewelcomemessage | 0 | enrol_manual | Scenario: Disable email notifications for everybody Given I log in as "admin" diff --git a/mod/assign/tests/behat/submission_notifications.feature b/mod/assign/tests/behat/submission_notifications.feature index 96ebdf088d7..7c3a0a9da5d 100644 --- a/mod/assign/tests/behat/submission_notifications.feature +++ b/mod/assign/tests/behat/submission_notifications.feature @@ -5,7 +5,10 @@ Feature: Manage assignment submission web notifications I need to be able to turn on web notifications for assignment submission Background: - Given the following "users" exist: + # Turn off the course welcome message, so we can easily test other messages. + Given the following config values are set as admin: + | sendcoursewelcomemessage | 0 | enrol_manual | + And the following "users" exist: | username | firstname | lastname | email | | student1 | Student | 1 | student1@example.com | | teacher1 | Teacher | 1 | teacher1@example.com | From 41f76c86ea8d70363a65eec8dbf031e5f74ec834 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Thu, 4 Apr 2024 22:19:56 +0700 Subject: [PATCH 11/11] MDL-4188 core: Version bump --- version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.php b/version.php index 0d2a6f22720..91aba1a526d 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2024040200.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2024040200.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.4dev+ (Build: 20240402)'; // Human-friendly version name