This commit is contained in:
Jun Pataleta
2025-03-24 11:45:28 +08:00
8 changed files with 245 additions and 103 deletions
@@ -0,0 +1,18 @@
issueNumber: MDL-84733
notes:
mod_assign:
- message: >-
When the assignment activity sends notifications, it now sets more
information in the $a object passed to the languages strings for the
message subject and body. This is to give more flexibility to people
using Language Customisation. The avaiable information is the same as
the template context for the two templates in the next paragraph, but
without the messagetext/html.
Also, the overall structure of these messages is now rendered using
templates mod_assign/messages/notification_html and
mod_assign/messages/notification_html, so they can be overridden by themes.
As a result of this, the methods format_notification_message_text and
format_notification_message_html (which should have been private to
mod_assign and not used anywhere else) have been removed.
type: improved
@@ -143,14 +143,14 @@ Feature: Manage notification preferences - Email
# Open the popover.
And I open the notification popover
# Confirm the submission notification is NOT visible.
And I should not see "You have submitted your assignment submission for Test assignment name" in the "#nav-notification-popover-container" "css_element"
And I should not see "Assignment Submission Confirmation" in the "#nav-notification-popover-container" "css_element"
And I log in as "student2"
# Confirm the popover is showing the unread notifications.
Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element"
# Open the popover.
And I open the notification popover
# Confirm the submission notification is visible.
And I should see "Assignment Submission Confirmation - Test assignment name" in the "#nav-notification-popover-container" "css_element"
And I should see "Assignment Submission Confirmation" in the "#nav-notification-popover-container" "css_element"
Scenario: User cannot disable forced notification preferences
Given the following "courses" exist:
@@ -182,7 +182,7 @@ Feature: Manage notification preferences - Email
# Open the popover.
And I open the notification popover
# Confirm the submission notification is visible.
And I should see "Assignment Submission Confirmation - Test assignment name" in the "#nav-notification-popover-container" "css_element"
And I should see "Assignment Submission Confirmation" in the "#nav-notification-popover-container" "css_element"
Scenario: User cannot disable disallowed notification preferences
Given the following "courses" exist:
@@ -214,7 +214,7 @@ Feature: Manage notification preferences - Email
# Open the popover.
And I open the notification popover
# Confirm the submission notification is NOT visible.
And I should not see "Assignment Submission Confirmation - Test assignment name" in the "#nav-notification-popover-container" "css_element"
And I should not see "Assignment Submission Confirmation" in the "#nav-notification-popover-container" "css_element"
Scenario: Toggle notification preferences hides/displays options
Given I log in as "admin"
+6 -12
View File
@@ -92,8 +92,7 @@ assignment submission for \'{$a->assignment}\'
You can see it appended to your assignment submission:
{$a->url}';
$string['assignmentmailhtml'] = '<p>{$a->grader} has posted some feedback on your
assignment submission for \'<i>{$a->assignment}</i>\'.</p>
$string['assignmentmailhtml'] = '<p>{$a->grader} has posted some feedback on your assignment submission for \'<i>{$a->assignment}</i>\'.</p>
<p>You can see it appended to your <a href="{$a->url}">assignment submission</a>.</p>';
$string['assignmentmailsmall'] = '{$a->grader} has posted some feedback on your
assignment submission for \'{$a->assignment}\' You can see it appended to your submission';
@@ -268,8 +267,7 @@ for \'{$a->assignment}\' at {$a->timeupdated}
It is available here:
{$a->url}';
$string['gradersubmissionupdatedhtml'] = '{$a->username} has updated their assignment submission
for <i>\'{$a->assignment}\' at {$a->timeupdated}</i><br /><br />
$string['gradersubmissionupdatedhtml'] = '{$a->username} has updated their assignment submission for <i>\'{$a->assignment}\' at {$a->timeupdated}</i><br /><br />
It is <a href="{$a->url}">available on the web site</a>.';
$string['gradersubmissionupdatedsmall'] = '{$a->username} has updated their submission for assignment {$a->assignment}.';
$string['gradeuser'] = 'Grade {$a}';
@@ -286,8 +284,7 @@ assignment submission for \'{$a->assignment}\'
You can see it appended to your assignment submission:
{$a->url}';
$string['feedbackavailablehtml'] = '{$a->username} has posted some feedback on your
assignment submission for \'<i>{$a->assignment}</i>\'<br /><br />
$string['feedbackavailablehtml'] = '{$a->username} has posted some feedback on your assignment submission for \'<i>{$a->assignment}</i>\'<br /><br />
You can see it appended to your <a href="{$a->url}">assignment submission</a>.';
$string['feedbackavailablesmall'] = '{$a->username} has given feedback for assignment {$a->assignment}';
$string['feedbackavailableanontext'] = 'You have new feedback on your
@@ -296,8 +293,7 @@ assignment submission for \'{$a->assignment}\'
You can see it appended to your assignment submission:
{$a->url}';
$string['feedbackavailableanonhtml'] = 'You have new feedback on your
assignment submission for \'<i>{$a->assignment}</i>\'<br /><br />
$string['feedbackavailableanonhtml'] = 'You have new feedback on your assignment submission for \'<i>{$a->assignment}</i>\'<br /><br />
You can see it appended to your <a href="{$a->url}">assignment submission</a>.';
$string['feedbackavailableanonsmall'] = 'New feedback for assignment {$a->assignment}';
$string['feedbackplugins'] = 'Feedback plugins';
@@ -580,8 +576,7 @@ $string['submissioncopiedtext'] = 'You have made a copy of your previous assignm
You can see the status of your assignment submission:
{$a->url}';
$string['submissioncopiedhtml'] = '<p>You have made a copy of your previous
assignment submission for \'<i>{$a->assignment}</i>\'.</p>
$string['submissioncopiedhtml'] = '<p>You have made a copy of your previous assignment submission for \'<i>{$a->assignment}</i>\'.</p>
<p>You can see the status of your <a href="{$a->url}">assignment submission</a>.</p>';
$string['submissioncopiedsmall'] = 'You have copied your previous assignment submission for {$a->assignment}';
$string['submissiondrafts'] = 'Require students to click the submit button';
@@ -603,8 +598,7 @@ $string['submissionreceiptothertext'] = 'Your assignment submission for \'{$a->a
You can see the status of your assignment submission:
{$a->url}';
$string['submissionreceiptotherhtml'] = 'Your assignment submission for
\'<i>{$a->assignment}</i>\' has been submitted.<br /><br />
$string['submissionreceiptotherhtml'] = 'Your assignment submission for \'<i>{$a->assignment}</i>\' has been submitted.<br /><br />
You can see the status of your <a href="{$a->url}">assignment submission</a>.';
$string['submissionreceiptothersmall'] = 'Your assignment submission for {$a->assignment} has been submitted.';
$string['submissionreceipts'] = 'Send submission receipts';
+52 -83
View File
@@ -6433,70 +6433,6 @@ class assign {
return $notifiableusers;
}
/**
* Format a notification for plain text.
*
* @param string $messagetype
* @param stdClass $info
* @param stdClass $course
* @param stdClass $context
* @param string $modulename
* @param string $assignmentname
*/
protected static function format_notification_message_text($messagetype,
$info,
$course,
$context,
$modulename,
$assignmentname) {
$formatparams = array('context' => $context->get_course_context());
$posttext = format_string($course->shortname, true, $formatparams) .
' -> ' .
$modulename .
' -> ' .
format_string($assignmentname, true, $formatparams) . "\n";
$posttext .= '---------------------------------------------------------------------' . "\n";
$posttext .= get_string($messagetype . 'text', 'assign', $info)."\n";
$posttext .= "\n---------------------------------------------------------------------\n";
return $posttext;
}
/**
* Format a notification for HTML.
*
* @param string $messagetype
* @param stdClass $info
* @param stdClass $course
* @param stdClass $context
* @param string $modulename
* @param stdClass $coursemodule
* @param string $assignmentname
*/
protected static function format_notification_message_html($messagetype,
$info,
$course,
$context,
$modulename,
$coursemodule,
$assignmentname) {
global $CFG;
$formatparams = array('context' => $context->get_course_context());
$posthtml = '<p><font face="sans-serif">' .
'<a href="' . $CFG->wwwroot . '/course/view.php?id=' . $course->id . '">' .
format_string($course->shortname, true, $formatparams) .
'</a> ->' .
'<a href="' . $CFG->wwwroot . '/mod/assign/index.php?id=' . $course->id . '">' .
$modulename .
'</a> ->' .
'<a href="' . $CFG->wwwroot . '/mod/assign/view.php?id=' . $coursemodule->id . '">' .
format_string($assignmentname, true, $formatparams) .
'</a></font></p>';
$posthtml .= '<hr /><font face="sans-serif">';
$posthtml .= '<p>' . get_string($messagetype . 'html', 'assign', $info) . '</p>';
$posthtml .= '</font><hr />';
return $posthtml;
}
/**
* Message someone about something (static so it can be called from cron).
*
@@ -6506,9 +6442,9 @@ class assign {
* @param string $eventtype
* @param int $updatetime
* @param stdClass $coursemodule
* @param stdClass $context
* @param context $context
* @param stdClass $course
* @param string $modulename
* @param string $modulename - no longer used.
* @param string $assignmentname
* @param bool $blindmarking
* @param int $uniqueidforuser
@@ -6530,6 +6466,11 @@ class assign {
$extrainfo = []) {
global $CFG, $PAGE;
// We put more data into info that is used by the default language strings,
// so that there is more flexibility for organisations that want to use
// Language Customisation to customise the messages.
// Information about the sender - normally the user who performed the action.
$info = new stdClass();
if ($blindmarking) {
$userfrom = clone($userfrom);
@@ -6538,31 +6479,59 @@ class assign {
$userfrom->lastname = $uniqueidforuser;
$userfrom->email = $CFG->noreplyaddress;
} else {
$info->username = fullname($userfrom, true);
$info->username = core_user::get_fullname($userfrom, $context, ['override' => true]);
}
$info->assignment = format_string($assignmentname, true, array('context'=>$context));
$info->url = $CFG->wwwroot.'/mod/assign/view.php?id='.$coursemodule->id;
// Information about the recipient (for greeting, etc.).
$info->recipentname = core_user::get_fullname($userto, $context, ['override' => true]);
// Information about the assignment.
$info->assignment = format_string($assignmentname, true, ['context' => $context]);
$info->url = $CFG->wwwroot . '/mod/assign/view.php?id=' . $coursemodule->id;
// Note: URLs here avoid the & character to avoid escaping issues between text and HTML messages.
$info->assignmentlink = '<a href="' . $info->url . '">' . s($info->assignment) . ' report</a>';
$info->assigncmid = $coursemodule->id;
// Information about the course.
$info->courseshortname = format_string($course->shortname, true, ['context' => $context]);
$info->coursefullname = format_string($course->fullname, true, ['context' => $context]);
// Note: URLs here avoid the & character to avoid escaping issues between text and HTML messages.
$info->courseurl = $CFG->wwwroot . '/course/view.php?id=' . $course->id;
$info->courseassignsurl = $CFG->wwwroot . '/mod/assign/index.php?id=' . $course->id;
// Time of the action.
$info->timeupdated = userdate($updatetime, get_string('strftimerecentfull'));
// Other data passed in.
$info = (object) array_merge((array) $info, $extrainfo);
$postsubject = get_string($messagetype . 'small', 'assign', $info);
$posttext = self::format_notification_message_text($messagetype,
$info,
$course,
$context,
$modulename,
$assignmentname);
// Since format_string returns text with HTML characters escaped,
// we need to un-escape before including in the plain text email and subject line.
// (Test with a course or assignment called "Escaping & unescaping" to understand).
$plaintextinfo = clone $info;
$plaintextinfo->assignment = html_entity_decode($info->assignment);
$plaintextinfo->courseshortname = html_entity_decode($info->courseshortname);
$plaintextinfo->coursefullname = html_entity_decode($info->coursefullname);
// Prepare the message subject and bodies.
$postsubject = get_string($messagetype . 'small', 'assign', $plaintextinfo);
$renderer = $PAGE->get_renderer('mod_assign');
$context = clone $plaintextinfo;
$context->messagetext = get_string($messagetype . 'text', 'assign', $plaintextinfo);
// Mustache strips off all training whitespace, but we want a newline at the end.
$posttext = $renderer->render_from_template(
'mod_assign/messages/notification_text', $context) . "\n";
$posthtml = '';
if ($userto->mailformat == 1) {
$posthtml = self::format_notification_message_html($messagetype,
$info,
$course,
$context,
$modulename,
$coursemodule,
$assignmentname);
$context = clone $info;
$context->messagehtml = get_string($messagetype . 'html', 'assign', $info);
$posthtml = $renderer->render_from_template(
'mod_assign/messages/notification_html', $context);
}
// Build the message object.
$eventdata = new \core\message\message();
$eventdata->courseid = $course->id;
$eventdata->modulename = 'assign';
@@ -0,0 +1,63 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/messages/notification_html
This template is used for the HTML version of messages sent from the assignment module.
Note: much more is passed to this template than might be needed, but this is to give
flexibility to themes.
Classes required for JS:
* none
Context variables required for this template:
* messagehtml - the main content of the message. Normally comes from a lang string.
* username - full name of the user who performed the action leading to the message, but will be changed to 'Participant 123' if blink marking is in use.
* recipentname - full name of the user the email is being sent to. (Never anonymised.)
* assignment - The name of this assignment.
* url - The URL of this assignment activity.
* assigncmid - Course-module id of this assignment.
* courseshortname - short name of the course this assignment is in.
* coursefullname - full name of the course this assignment is in.
* courseurl - URL of the course page.
* courseassignsurl - URL of the paeg listing all assignments in the course.
* timeupdated - Time the action that triggered this message was performed, formatted for display.
Example context (json):
{
"messagehtml": "<p>Your assignment for <strong>Assignment 1</strong> has been submitted. You can check its status on the <a href='https://qa.moodledemo.net/mod/assign/view.php?id=5'>assignment page</a>.</p>",
"username": "Trusty Teacher",
"recipientname": "Smart Student",
"assignment": "Assignment 1",
"url": "https://qa.moodledemo.net/mod/assign/view.php?id=5",
"assigncmid": 5,
"courseshortname": "A100",
"coursefullname": "Assessment for beginners",
"courseurl": "https://qa.moodledemo.net/course/view.php?id=2",
"courseassignsurl": "https://qa.moodledemo.net/mod/assign/index.php?id=2",
"timeupdated": "Fri, 14 Mar 2025, 11:08 PM"
}
}}
<p><font face="sans-serif">
<a href="{{courseurl}}">{{{courseshortname}}}</a> ->
<a href="{{courseassignsurl}}">{{#str}}modulename, mod_assign{{/str}}</a> ->
<a href="{{url}}">{{{assignment}}}</a>
</font></p>
<hr>
<font face="sans-serif"><p>{{{messagehtml}}}</p></font>
<hr>
@@ -0,0 +1,60 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_assign/messages/notification_text
This template is used for the plain text version of messages sent from the assignment module.
Note: much more is passed to this template than might be needed, but this is to give
flexibility to themes.
Classes required for JS:
* none
Context variables required for this template:
* messagetext - the main content of the message. Normally comes from a lang string.
* username - full name of the user who performed the action leading to the message, but will be changed to 'Participant 123' if blink marking is in use.
* recipentname - full name of the user the email is being sent to. (Never anonymised.)
* assignment - The name of this assignment.
* url - The URL of this assignment activity.
* assigncmid - Course-module id of this assignment.
* courseshortname - short name of the course this assignment is in.
* coursefullname - full name of the course this assignment is in.
* courseurl - URL of the course page.
* courseassignsurl - URL of the paeg listing all assignments in the course.
* timeupdated - Time the action that triggered this message was performed, formatted for display.
Example context (json):
{
"messagehtml": "Your assignment for Assignment 1 has been submitted. You can check its status at https://qa.moodledemo.net/mod/assign/view.php?id=5.",
"username": "Trusty Teacher",
"recipientname": "Smart Student",
"assignment": "Assignment 1",
"url": "https://qa.moodledemo.net/mod/assign/view.php?id=5",
"assigncmid": 5,
"courseshortname": "A100",
"coursefullname": "Assessment for beginners",
"courseurl": "https://qa.moodledemo.net/course/view.php?id=2",
"courseassignsurl": "https://qa.moodledemo.net/mod/assign/index.php?id=2",
"timeupdated": "Fri, 14 Mar 2025, 11:08 PM"
}
}}
{{{courseshortname}}} -> {{#str}}modulename, mod_assign{{/str}} -> {{{assignment}}}
---------------------------------------------------------------------
{{{messagetext}}}
---------------------------------------------------------------------
+1 -1
View File
@@ -41,7 +41,7 @@ trait mod_assign_test_generator {
* Convenience function to create a testable instance of an assignment.
*
* @param array $params Array of parameters to pass to the generator
* @return testable_assign Testable wrapper around the assign class.
* @return mod_assign_testable_assign Testable wrapper around the assign class.
*/
protected function create_instance($course, $params = [], $options = []) {
$params['course'] = $course->id;
+41 -3
View File
@@ -1724,13 +1724,14 @@ final class locallib_test extends \advanced_testcase {
\core\cron::setup_user();
\assign::cron();
$course = $this->getDataGenerator()->create_course();
$course = $this->getDataGenerator()->create_course(['shortname' => 'E&U']);
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
// Now create an assignment and add some feedback.
$this->setUser($teacher);
$assign = $this->create_instance($course, [
'name' => 'Escaping & Unescaping',
'sendstudentnotifications' => 1,
'markingworkflow' => 1,
]);
@@ -1765,8 +1766,45 @@ final class locallib_test extends \advanced_testcase {
$messages = $sink->get_messages();
$this->assertEquals(1, count($messages));
$this->assertEquals(1, $messages[0]->notification);
$this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
// Get some bits we will need to verify the content of the message.
$assignname = $assign->get_instance()->name;
$assignurl = (new \moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]))->out();
$teachername = fullname($teacher);
$assignsurl = (new \moodle_url('/mod/assign/index.php', ['id' => $course->id]))->out();
$courseurl = (new \moodle_url('/course/view.php', ['id' => $course->id]))->out();
$message = $messages[0];
$this->assertEquals(1, $message->notification);
$this->assertEquals(format_string($assign->get_instance()->name), $message->contexturlname);
$this->assertEquals("$teachername has given feedback for assignment $assignname", $message->subject, );
$this->assertEquals("E&U -> Assignment -> Escaping & Unescaping
---------------------------------------------------------------------
$teachername has posted some feedback on your
assignment submission for 'Escaping & Unescaping'
You can see it appended to your assignment submission:
$assignurl
---------------------------------------------------------------------
",
$message->fullmessage,
);
$this->assertEquals(
'<p><font face="sans-serif">
<a href="' . $courseurl . '">E&amp;U</a> ->
<a href="' . $assignsurl . '">Assignment</a> ->
<a href="' . $assignurl . '">Escaping &amp; Unescaping</a>
</font></p>
<hr>
<font face="sans-serif"><p>' . $teachername . ' has posted some feedback on your ' .
'assignment submission for \'<i>Escaping &amp; Unescaping</i>\'<br /><br />
You can see it appended to your <a href="' . $assignurl .
'">assignment submission</a>.</p></font>
<hr>',
$message->fullmessagehtml
);
}
public function test_cron_message_includes_courseid(): void {