mirror of
https://github.com/moodle/moodle.git
synced 2025-08-09 19:06:41 +02:00
Merge branch 'MDL-79734-main' of https://github.com/stevandoMoodle/moodle
This commit is contained in:
commit
bef9054099
8 changed files with 511 additions and 4 deletions
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
namespace mod_assign;
|
namespace mod_assign;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use core\output\html_writer;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||||
|
@ -39,6 +42,11 @@ class notification_helper {
|
||||||
*/
|
*/
|
||||||
private const INTERVAL_OVERDUE = (HOURSECS * 2);
|
private const INTERVAL_OVERDUE = (HOURSECS * 2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int Due digest time interval of 7 days.
|
||||||
|
*/
|
||||||
|
private const INTERVAL_DUE_DIGEST = WEEKSECS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string Due soon notification type.
|
* @var string Due soon notification type.
|
||||||
*/
|
*/
|
||||||
|
@ -49,6 +57,11 @@ class notification_helper {
|
||||||
*/
|
*/
|
||||||
public const TYPE_OVERDUE = 'assign_overdue';
|
public const TYPE_OVERDUE = 'assign_overdue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string Due digest notification type.
|
||||||
|
*/
|
||||||
|
public const TYPE_DUE_DIGEST = 'assign_due_digest';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all assignments that have an approaching due date (includes users and groups with due date overrides).
|
* Get all assignments that have an approaching due date (includes users and groups with due date overrides).
|
||||||
*
|
*
|
||||||
|
@ -120,6 +133,77 @@ class notification_helper {
|
||||||
return $DB->get_recordset_sql($sql, $params);
|
return $DB->get_recordset_sql($sql, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all assignments that are due in 7 days (includes users and groups with due date overrides).
|
||||||
|
*
|
||||||
|
* @return \moodle_recordset Returns the matching assignment records.
|
||||||
|
*/
|
||||||
|
public static function get_due_digest_assignments(): \moodle_recordset {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$futuretime = self::get_future_time(self::INTERVAL_DUE_DIGEST);
|
||||||
|
$day = self::get_day_start_and_end($futuretime);
|
||||||
|
|
||||||
|
$sql = "SELECT DISTINCT a.id
|
||||||
|
FROM {assign} a
|
||||||
|
JOIN {course_modules} cm ON a.id = cm.instance
|
||||||
|
JOIN {modules} m ON cm.module = m.id AND m.name = :modulename
|
||||||
|
LEFT JOIN {assign_overrides} ao ON a.id = ao.assignid
|
||||||
|
WHERE (a.duedate <= :endofday OR ao.duedate <= :ao_endofday)
|
||||||
|
AND (a.duedate >= :startofday OR ao.duedate >= :ao_startofday)";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
'startofday' => $day['start'],
|
||||||
|
'endofday' => $day['end'],
|
||||||
|
'ao_startofday' => $day['start'],
|
||||||
|
'ao_endofday' => $day['end'],
|
||||||
|
'modulename' => 'assign',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $DB->get_recordset_sql($sql, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all assignments for a user that are due in 7 days (includes users and groups with due date overrides).
|
||||||
|
*
|
||||||
|
* @param int $userid The user id.
|
||||||
|
* @return \moodle_recordset Returns the matching assignment records.
|
||||||
|
*/
|
||||||
|
public static function get_due_digest_assignments_for_user(int $userid): \moodle_recordset {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$futuretime = self::get_future_time(self::INTERVAL_DUE_DIGEST);
|
||||||
|
$day = self::get_day_start_and_end($futuretime);
|
||||||
|
|
||||||
|
$sql = "SELECT DISTINCT a.id,
|
||||||
|
a.duedate,
|
||||||
|
a.name AS assignmentname,
|
||||||
|
c.fullname AS coursename,
|
||||||
|
cm.id AS cmid
|
||||||
|
FROM {assign} a
|
||||||
|
JOIN {course} c ON a.course = c.id
|
||||||
|
JOIN {course_modules} cm ON a.id = cm.instance
|
||||||
|
JOIN {modules} m ON cm.module = m.id AND m.name = :modulename
|
||||||
|
JOIN {enrol} e ON c.id = e.courseid
|
||||||
|
JOIN {user_enrolments} ue ON e.id = ue.enrolid
|
||||||
|
LEFT JOIN {assign_overrides} ao ON a.id = ao.assignid
|
||||||
|
WHERE (a.duedate <= :endofday OR ao.duedate <= :ao_endofday)
|
||||||
|
AND (a.duedate >= :startofday OR ao.duedate >= :ao_startofday)
|
||||||
|
AND ue.userid = :userid
|
||||||
|
ORDER BY a.duedate ASC";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
'startofday' => $day['start'],
|
||||||
|
'endofday' => $day['end'],
|
||||||
|
'ao_startofday' => $day['start'],
|
||||||
|
'ao_endofday' => $day['end'],
|
||||||
|
'modulename' => 'assign',
|
||||||
|
'userid' => $userid,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $DB->get_recordset_sql($sql, $params);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all assignment users that we should send the notification to.
|
* Get all assignment users that we should send the notification to.
|
||||||
*
|
*
|
||||||
|
@ -153,6 +237,7 @@ class notification_helper {
|
||||||
|
|
||||||
// Perform some checks depending on the notification type.
|
// Perform some checks depending on the notification type.
|
||||||
$match = [];
|
$match = [];
|
||||||
|
$checksent = true;
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case self::TYPE_DUE_SOON:
|
case self::TYPE_DUE_SOON:
|
||||||
$range = [
|
$range = [
|
||||||
|
@ -186,12 +271,26 @@ class notification_helper {
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case self::TYPE_DUE_DIGEST:
|
||||||
|
$checksent = false;
|
||||||
|
$futuretime = self::get_future_time(self::INTERVAL_DUE_DIGEST);
|
||||||
|
$day = self::get_day_start_and_end($futuretime);
|
||||||
|
$range = [
|
||||||
|
'lower' => $day['start'],
|
||||||
|
'upper' => $day['end'],
|
||||||
|
];
|
||||||
|
if (!self::is_time_within_range($duedate, $range)) {
|
||||||
|
unset($users[$key]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user has already received this notification.
|
// Check if the user has already received this notification.
|
||||||
if (self::has_user_been_sent_a_notification_already($user->id, json_encode($match), $type)) {
|
if ($checksent && self::has_user_been_sent_a_notification_already($user->id, json_encode($match), $type)) {
|
||||||
unset($users[$key]);
|
unset($users[$key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -358,6 +457,95 @@ class notification_helper {
|
||||||
message_send($message);
|
message_send($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the assignments and send the due digest notification to the user.
|
||||||
|
*
|
||||||
|
* @param int $userid The user id.
|
||||||
|
*/
|
||||||
|
public static function send_due_digest_notification_to_user(int $userid): void {
|
||||||
|
// Get all the user's assignments due in 7 days.
|
||||||
|
$assignments = self::get_due_digest_assignments_for_user($userid);
|
||||||
|
$assignmentsfordigest = [];
|
||||||
|
|
||||||
|
foreach ($assignments as $assignment) {
|
||||||
|
$assignmentobj = self::get_assignment_data($assignment->id);
|
||||||
|
|
||||||
|
// Check if the user has submitted already.
|
||||||
|
if ($assignmentobj->get_user_submission($userid, false)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the due date is still within range.
|
||||||
|
$assignmentobj->update_effective_access($userid);
|
||||||
|
$duedate = $assignmentobj->get_instance($userid)->duedate;
|
||||||
|
$futuretime = self::get_future_time(self::INTERVAL_DUE_DIGEST);
|
||||||
|
$day = self::get_day_start_and_end($futuretime);
|
||||||
|
$range = [
|
||||||
|
'lower' => $day['start'],
|
||||||
|
'upper' => $day['end'],
|
||||||
|
];
|
||||||
|
if (!self::is_time_within_range($duedate, $range)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record the assignment data to help us build the digest.
|
||||||
|
$urlparams = [
|
||||||
|
'id' => $assignmentobj->get_course_module()->id,
|
||||||
|
'action' => 'view',
|
||||||
|
];
|
||||||
|
$assignmentsfordigest[$assignment->id] = [
|
||||||
|
'assignmentname' => $assignmentobj->get_instance()->name,
|
||||||
|
'coursename' => $assignmentobj->get_course()->fullname,
|
||||||
|
'duetime' => userdate($duedate, get_string('strftimetime12', 'langconfig')),
|
||||||
|
'url' => new \moodle_url('/mod/assign/view.php', $urlparams),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$assignments->close();
|
||||||
|
|
||||||
|
// If there are no assignments in the digest, don't send anything.
|
||||||
|
if (empty($assignmentsfordigest)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the digest.
|
||||||
|
$digestarray = [];
|
||||||
|
foreach ($assignmentsfordigest as $digestitem) {
|
||||||
|
$digestarray[] = get_string('assignmentduedigestitem', 'mod_assign', $digestitem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the digest into list.
|
||||||
|
$digest = html_writer::alist($digestarray);
|
||||||
|
|
||||||
|
// Get user's object.
|
||||||
|
$userobject = \core_user::get_user($userid);
|
||||||
|
|
||||||
|
$stringparams = [
|
||||||
|
'firstname' => $userobject->firstname,
|
||||||
|
'duedate' => userdate(self::get_future_time(self::INTERVAL_DUE_DIGEST), get_string('strftimedaydate', 'langconfig')),
|
||||||
|
'digest' => $digest,
|
||||||
|
];
|
||||||
|
|
||||||
|
$messagedata = [
|
||||||
|
'user' => $userobject,
|
||||||
|
'subject' => get_string('assignmentduedigestsubject', 'mod_assign'),
|
||||||
|
'html' => get_string('assignmentduedigesthtml', 'mod_assign', $stringparams),
|
||||||
|
];
|
||||||
|
|
||||||
|
$message = new \core\message\message();
|
||||||
|
$message->component = 'mod_assign';
|
||||||
|
$message->name = self::TYPE_DUE_DIGEST;
|
||||||
|
$message->userfrom = \core_user::get_noreply_user();
|
||||||
|
$message->userto = $messagedata['user'];
|
||||||
|
$message->subject = $messagedata['subject'];
|
||||||
|
$message->fullmessageformat = FORMAT_HTML;
|
||||||
|
$message->fullmessage = html_to_text($messagedata['html']);
|
||||||
|
$message->fullmessagehtml = $messagedata['html'];
|
||||||
|
$message->smallmessage = $messagedata['subject'];
|
||||||
|
$message->notification = 1;
|
||||||
|
|
||||||
|
message_send($message);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the time now.
|
* Get the time now.
|
||||||
*
|
*
|
||||||
|
@ -378,14 +566,33 @@ class notification_helper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a time is within the current time now and the future time values.
|
* Get the timestamps for the start (00:00:00) and end (23:59:59) of the provided day.
|
||||||
|
*
|
||||||
|
* @param int $timestamp The timestamp to base the calculation on.
|
||||||
|
* @return array Day start and end timestamps.
|
||||||
|
*/
|
||||||
|
protected static function get_day_start_and_end(int $timestamp): array {
|
||||||
|
$day = [];
|
||||||
|
|
||||||
|
$date = new DateTime();
|
||||||
|
$date->setTimestamp($timestamp);
|
||||||
|
$date->setTime(0, 0, 0);
|
||||||
|
$day['start'] = $date->getTimestamp();
|
||||||
|
$date->setTime(23, 59, 59);
|
||||||
|
$day['end'] = $date->getTimestamp();
|
||||||
|
|
||||||
|
return $day;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a time is within the current time now and the future time values (inclusive).
|
||||||
*
|
*
|
||||||
* @param int $time The timestamp to check.
|
* @param int $time The timestamp to check.
|
||||||
* @param array $range Lower and upper times to check.
|
* @param array $range Lower and upper times to check.
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
protected static function is_time_within_range(int $time, array $range): bool {
|
protected static function is_time_within_range(int $time, array $range): bool {
|
||||||
return ($time > $range['lower'] && $time < $range['upper']);
|
return ($time >= $range['lower'] && $time <= $range['upper']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
namespace mod_assign\task;
|
||||||
|
|
||||||
|
use core\task\scheduled_task;
|
||||||
|
use mod_assign\notification_helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scheduled task to queue tasks for notifying about assignments that are due in 7 days.
|
||||||
|
*
|
||||||
|
* @package mod_assign
|
||||||
|
* @copyright 2024 David Woloszyn <david.woloszyn@moodle.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class queue_all_assignment_due_digest_notification_tasks extends scheduled_task {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the task name.
|
||||||
|
*
|
||||||
|
* @return string The name of the task.
|
||||||
|
*/
|
||||||
|
public function get_name(): string {
|
||||||
|
return get_string('sendnotificationduedigest', 'mod_assign');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the task.
|
||||||
|
*/
|
||||||
|
public function execute(): void {
|
||||||
|
// Get all our assignments and the users within them.
|
||||||
|
$assignments = notification_helper::get_due_digest_assignments();
|
||||||
|
$type = notification_helper::TYPE_DUE_DIGEST;
|
||||||
|
$users = [];
|
||||||
|
foreach ($assignments as $assignment) {
|
||||||
|
$newusers = notification_helper::get_users_within_assignment($assignment->id, $type);
|
||||||
|
$users = array_replace($users, $newusers);
|
||||||
|
}
|
||||||
|
$assignments->close();
|
||||||
|
|
||||||
|
// Queue a task for each user.
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$task = new send_assignment_due_digest_notification_to_user();
|
||||||
|
$task->set_userid($user->id);
|
||||||
|
\core\task\manager::queue_adhoc_task($task, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
// 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/>.
|
||||||
|
|
||||||
|
namespace mod_assign\task;
|
||||||
|
|
||||||
|
use core\task\adhoc_task;
|
||||||
|
use mod_assign\notification_helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ad-hoc task to send a notification to a user about assignments that are due in 7 days.
|
||||||
|
*
|
||||||
|
* @package mod_assign
|
||||||
|
* @copyright 2024 David Woloszyn <david.woloszyn@moodle.com>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class send_assignment_due_digest_notification_to_user extends adhoc_task {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the task.
|
||||||
|
*/
|
||||||
|
public function execute(): void {
|
||||||
|
notification_helper::send_due_digest_notification_to_user($this->get_userid());
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,4 +47,12 @@ $messageproviders = array (
|
||||||
'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
|
'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
// Assignments that are due in 7 days.
|
||||||
|
'assign_due_digest' => [
|
||||||
|
'defaults' => [
|
||||||
|
'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
|
||||||
|
'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
|
||||||
|
'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
|
||||||
|
],
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -49,4 +49,13 @@ $tasks = array(
|
||||||
'month' => '*',
|
'month' => '*',
|
||||||
'dayofweek' => '*',
|
'dayofweek' => '*',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'classname' => '\mod_assign\task\queue_all_assignment_due_digest_notification_tasks',
|
||||||
|
'blocking' => 0,
|
||||||
|
'minute' => 'R',
|
||||||
|
'hour' => '1',
|
||||||
|
'day' => '*',
|
||||||
|
'month' => '*',
|
||||||
|
'dayofweek' => '*',
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -67,6 +67,13 @@ $string['assign:view'] = 'View assignment';
|
||||||
$string['assign:viewownsubmissionsummary'] = 'View own submission summary';
|
$string['assign:viewownsubmissionsummary'] = 'View own submission summary';
|
||||||
$string['assignfeedback'] = 'Feedback plugin';
|
$string['assignfeedback'] = 'Feedback plugin';
|
||||||
$string['assignfeedbackpluginname'] = 'Feedback plugin';
|
$string['assignfeedbackpluginname'] = 'Feedback plugin';
|
||||||
|
$string['assignmentduedigesthtml'] = '<p>Hi {$a->firstname},</p>
|
||||||
|
<p>The following assignments are due on <strong>{$a->duedate}</strong>.</p>
|
||||||
|
{$a->digest}';
|
||||||
|
$string['assignmentduedigestitem'] = '<strong>{$a->assignmentname}</strong> in course {$a->coursename}<br/>
|
||||||
|
<strong>Due: {$a->duetime}</strong><br/>
|
||||||
|
<a href="{$a->url}" aria-label="Go to {$a->assignmentname}">Go to activity</a>';
|
||||||
|
$string['assignmentduedigestsubject'] = 'You have assignments due in 7 days';
|
||||||
$string['assignmentduesoonhtml'] = '<p>Hi {$a->firstname},</p>
|
$string['assignmentduesoonhtml'] = '<p>Hi {$a->firstname},</p>
|
||||||
<p>The assignment <strong>{$a->assignmentname}</strong> in course {$a->coursename} is due soon.</p>
|
<p>The assignment <strong>{$a->assignmentname}</strong> in course {$a->coursename} is due soon.</p>
|
||||||
<p><strong>Due: {$a->duedate}</strong></p>
|
<p><strong>Due: {$a->duedate}</strong></p>
|
||||||
|
@ -389,6 +396,7 @@ $string['maxgrade'] = 'Maximum grade';
|
||||||
$string['maxgrade'] = 'Maximum Grade';
|
$string['maxgrade'] = 'Maximum Grade';
|
||||||
$string['maxperpage'] = 'Maximum assignments per page';
|
$string['maxperpage'] = 'Maximum assignments per page';
|
||||||
$string['maxperpage_help'] = 'The maximum number of assignments a grader can show in the assignment grading page. This setting is useful in preventing timeouts for courses with a large number of participants.';
|
$string['maxperpage_help'] = 'The maximum number of assignments a grader can show in the assignment grading page. This setting is useful in preventing timeouts for courses with a large number of participants.';
|
||||||
|
$string['messageprovider:assign_due_digest'] = 'Assignments due in 7 days notification';
|
||||||
$string['messageprovider:assign_due_soon'] = 'Assignment due soon notification';
|
$string['messageprovider:assign_due_soon'] = 'Assignment due soon notification';
|
||||||
$string['messageprovider:assign_overdue'] = 'Assignment overdue notification';
|
$string['messageprovider:assign_overdue'] = 'Assignment overdue notification';
|
||||||
$string['messageprovider:assign_notification'] = 'Assignment notifications';
|
$string['messageprovider:assign_notification'] = 'Assignment notifications';
|
||||||
|
@ -539,6 +547,7 @@ $string['sendlatenotifications'] = 'Notify graders about late submissions';
|
||||||
$string['sendlatenotifications_help'] = 'If enabled, graders (usually teachers) receive a message whenever a student submits an assignment late. Message methods are configurable.';
|
$string['sendlatenotifications_help'] = 'If enabled, graders (usually teachers) receive a message whenever a student submits an assignment late. Message methods are configurable.';
|
||||||
$string['sendnotificationduedatesoon'] = 'Notify user of an approaching assignment due date';
|
$string['sendnotificationduedatesoon'] = 'Notify user of an approaching assignment due date';
|
||||||
$string['sendnotificationoverdue'] = 'Notify user of an assignment that is overdue';
|
$string['sendnotificationoverdue'] = 'Notify user of an assignment that is overdue';
|
||||||
|
$string['sendnotificationduedigest'] = 'Notify user of assignments due in 7 days';
|
||||||
$string['sendsubmissionreceipts'] = 'Send submission receipt to students';
|
$string['sendsubmissionreceipts'] = 'Send submission receipt to students';
|
||||||
$string['sendsubmissionreceipts_help'] = 'This switch enables submission receipts for students. Students will receive a notification every time they successfully submit an assignment.';
|
$string['sendsubmissionreceipts_help'] = 'This switch enables submission receipts for students. Students will receive a notification every time they successfully submit an assignment.';
|
||||||
$string['setmarkingallocation'] = 'Set allocated marker';
|
$string['setmarkingallocation'] = 'Set allocated marker';
|
||||||
|
|
|
@ -475,4 +475,180 @@ final class notification_helper_test extends \advanced_testcase {
|
||||||
// No new notification should have been sent.
|
// No new notification should have been sent.
|
||||||
$this->assertEmpty($sink->get_messages_by_component('mod_assign'));
|
$this->assertEmpty($sink->get_messages_by_component('mod_assign'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run all the tasks related to the due digest notifications.
|
||||||
|
*/
|
||||||
|
protected function run_due_digest_notification_helper_tasks(): void {
|
||||||
|
$task = \core\task\manager::get_scheduled_task(\mod_assign\task\queue_all_assignment_due_digest_notification_tasks::class);
|
||||||
|
$task->execute();
|
||||||
|
$clock = \core\di::get(\core\clock::class);
|
||||||
|
|
||||||
|
$adhoctask = \core\task\manager::get_next_adhoc_task($clock->time());
|
||||||
|
if ($adhoctask) {
|
||||||
|
$this->assertInstanceOf(\mod_assign\task\send_assignment_due_digest_notification_to_user::class, $adhoctask);
|
||||||
|
$adhoctask->execute();
|
||||||
|
\core\task\manager::adhoc_task_complete($adhoctask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getting users for the due digest.
|
||||||
|
*/
|
||||||
|
public function test_get_users_for_due_digest(): void {
|
||||||
|
$this->resetAfterTest();
|
||||||
|
$generator = $this->getDataGenerator();
|
||||||
|
$helper = \core\di::get(notification_helper::class);
|
||||||
|
$clock = $this->mock_clock_with_frozen();
|
||||||
|
|
||||||
|
// Create a course and enrol some users.
|
||||||
|
$course = $generator->create_course();
|
||||||
|
$user1 = $generator->create_user();
|
||||||
|
$user2 = $generator->create_user();
|
||||||
|
$user3 = $generator->create_user();
|
||||||
|
$user4 = $generator->create_user();
|
||||||
|
$user5 = $generator->create_user();
|
||||||
|
$user6 = $generator->create_user();
|
||||||
|
$generator->enrol_user($user1->id, $course->id, 'student');
|
||||||
|
$generator->enrol_user($user2->id, $course->id, 'student');
|
||||||
|
$generator->enrol_user($user3->id, $course->id, 'student');
|
||||||
|
$generator->enrol_user($user4->id, $course->id, 'student');
|
||||||
|
$generator->enrol_user($user5->id, $course->id, 'student');
|
||||||
|
$generator->enrol_user($user6->id, $course->id, 'teacher');
|
||||||
|
|
||||||
|
/** @var \mod_assign_generator $assignmentgenerator */
|
||||||
|
$assignmentgenerator = $generator->get_plugin_generator('mod_assign');
|
||||||
|
|
||||||
|
// Create an assignment with a due date 7 days from now (the due digest range).
|
||||||
|
$duedate = $clock->time() + WEEKSECS;
|
||||||
|
$assignment = $assignmentgenerator->create_instance([
|
||||||
|
'course' => $course->id,
|
||||||
|
'duedate' => $duedate,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// User1 will have a user override, giving them an extra 1 day for 'duedate', excluding them from the results.
|
||||||
|
$userduedate = $duedate + DAYSECS;
|
||||||
|
$assignmentgenerator->create_override([
|
||||||
|
'assignid' => $assignment->id,
|
||||||
|
'userid' => $user1->id,
|
||||||
|
'duedate' => $userduedate,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// User2 and user3 will have a group override, giving them an extra 2 days for 'duedate', excluding them from the results.
|
||||||
|
$groupduedate = $duedate + (DAYSECS * 2);
|
||||||
|
$group = $generator->create_group(['courseid' => $course->id]);
|
||||||
|
$generator->create_group_member(['groupid' => $group->id, 'userid' => $user2->id]);
|
||||||
|
$generator->create_group_member(['groupid' => $group->id, 'userid' => $user3->id]);
|
||||||
|
$assignmentgenerator->create_override([
|
||||||
|
'assignid' => $assignment->id,
|
||||||
|
'groupid' => $group->id,
|
||||||
|
'duedate' => $groupduedate,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// User4 will submit the assignment, excluding them from the results.
|
||||||
|
$assignmentgenerator->create_submission([
|
||||||
|
'userid' => $user4->id,
|
||||||
|
'assignid' => $assignment->cmid,
|
||||||
|
'status' => 'submitted',
|
||||||
|
'timemodified' => $clock->time(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// There should be 1 user with the teacher excluded.
|
||||||
|
$users = $helper::get_users_within_assignment($assignment->id, $helper::TYPE_DUE_DIGEST);
|
||||||
|
$this->assertCount(1, $users);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test sending the assignment due digest notification to a user.
|
||||||
|
*/
|
||||||
|
public function test_send_due_digest_notification_to_user(): void {
|
||||||
|
global $DB;
|
||||||
|
$this->resetAfterTest();
|
||||||
|
$generator = $this->getDataGenerator();
|
||||||
|
$clock = $this->mock_clock_with_frozen();
|
||||||
|
$sink = $this->redirectMessages();
|
||||||
|
|
||||||
|
// Create a course and enrol a user.
|
||||||
|
$course = $generator->create_course();
|
||||||
|
$user1 = $generator->create_user();
|
||||||
|
$generator->enrol_user($user1->id, $course->id, 'student');
|
||||||
|
|
||||||
|
/** @var \mod_assign_generator $assignmentgenerator */
|
||||||
|
$assignmentgenerator = $generator->get_plugin_generator('mod_assign');
|
||||||
|
|
||||||
|
// Create a few assignments with different due dates.
|
||||||
|
$duedate1 = $clock->time() + WEEKSECS;
|
||||||
|
$assignment1 = $assignmentgenerator->create_instance([
|
||||||
|
'course' => $course->id,
|
||||||
|
'duedate' => $duedate1,
|
||||||
|
]);
|
||||||
|
$duedate2 = $clock->time() + WEEKSECS;
|
||||||
|
$assignment2 = $assignmentgenerator->create_instance([
|
||||||
|
'course' => $course->id,
|
||||||
|
'duedate' => $duedate2,
|
||||||
|
]);
|
||||||
|
$duedate3 = $clock->time() + WEEKSECS + DAYSECS;
|
||||||
|
$assignment3 = $assignmentgenerator->create_instance([
|
||||||
|
'course' => $course->id,
|
||||||
|
'duedate' => $duedate3,
|
||||||
|
]);
|
||||||
|
$clock->bump(5);
|
||||||
|
|
||||||
|
// Run the tasks.
|
||||||
|
$this->run_due_digest_notification_helper_tasks();
|
||||||
|
|
||||||
|
// Get the notifications that should have been created during the adhoc task.
|
||||||
|
$this->assertCount(1, $sink->get_messages_by_component('mod_assign'));
|
||||||
|
|
||||||
|
// Check the message for the expected assignments.
|
||||||
|
$messages = $sink->get_messages_by_component('mod_assign');
|
||||||
|
$message = reset($messages);
|
||||||
|
$this->assertStringContainsString($assignment1->name, $message->fullmessagehtml);
|
||||||
|
$this->assertStringContainsString($assignment2->name, $message->fullmessagehtml);
|
||||||
|
$this->assertStringNotContainsString($assignment3->name, $message->fullmessagehtml);
|
||||||
|
|
||||||
|
// Check the message contains the formatted due date.
|
||||||
|
$formatteddate = userdate($duedate1, get_string('strftimedaydate', 'langconfig'));
|
||||||
|
$this->assertStringContainsString($formatteddate, $message->fullmessagehtml);
|
||||||
|
|
||||||
|
// Clear sink.
|
||||||
|
$sink->clear();
|
||||||
|
|
||||||
|
// Let's modify the due date for one of the assignment.
|
||||||
|
$updatedata = new \stdClass();
|
||||||
|
$updatedata->id = $assignment1->id;
|
||||||
|
$updatedata->duedate = $duedate1 + DAYSECS;
|
||||||
|
$DB->update_record('assign', $updatedata);
|
||||||
|
|
||||||
|
// Run the tasks again.
|
||||||
|
$this->run_due_digest_notification_helper_tasks();
|
||||||
|
|
||||||
|
// Check the message for the expected assignments.
|
||||||
|
$messages = $sink->get_messages_by_component('mod_assign');
|
||||||
|
$message = reset($messages);
|
||||||
|
$this->assertStringNotContainsString($assignment1->name, $message->fullmessagehtml);
|
||||||
|
$this->assertStringContainsString($assignment2->name, $message->fullmessagehtml);
|
||||||
|
$this->assertStringNotContainsString($assignment3->name, $message->fullmessagehtml);
|
||||||
|
|
||||||
|
// Clear sink.
|
||||||
|
$sink->clear();
|
||||||
|
|
||||||
|
// This time, the user will submit an assignment.
|
||||||
|
$assignmentgenerator->create_submission([
|
||||||
|
'userid' => $user1->id,
|
||||||
|
'assignid' => $assignment2->cmid,
|
||||||
|
'status' => 'submitted',
|
||||||
|
'timemodified' => $clock->time(),
|
||||||
|
]);
|
||||||
|
$clock->bump(5);
|
||||||
|
|
||||||
|
// Run the tasks again.
|
||||||
|
$this->run_due_digest_notification_helper_tasks();
|
||||||
|
|
||||||
|
// There are no assignments left to report, so no notification should have been sent.
|
||||||
|
$this->assertEmpty($sink->get_messages_by_component('mod_assign'));
|
||||||
|
|
||||||
|
// Clear sink.
|
||||||
|
$sink->clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,5 +25,5 @@
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
$plugin->component = 'mod_assign'; // Full name of the plugin (used for diagnostics).
|
$plugin->component = 'mod_assign'; // Full name of the plugin (used for diagnostics).
|
||||||
$plugin->version = 2024070800; // The current module version (Date: YYYYMMDDXX).
|
$plugin->version = 2024082100; // The current module version (Date: YYYYMMDDXX).
|
||||||
$plugin->requires = 2024041600; // Requires this Moodle version.
|
$plugin->requires = 2024041600; // Requires this Moodle version.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue