Merge branch 'MDL-84327-405' of https://github.com/raortegar/moodle into MOODLE_405_STABLE

This commit is contained in:
Huong Nguyen
2025-02-27 11:10:21 +07:00
5 changed files with 161 additions and 5 deletions
+14 -3
View File
@@ -76,10 +76,13 @@ class notification_helper {
$sql = "SELECT DISTINCT a.id
FROM {assign} a
JOIN {course_modules} cm ON a.id = cm.instance
JOIN {course} c ON a.course = c.id
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 < :futuretime OR ao.duedate < :ao_futuretime)
AND (a.duedate > :timenow OR ao.duedate > :ao_timenow)";
AND (a.duedate > :timenow OR ao.duedate > :ao_timenow)
AND cm.visible = 1
AND c.visible = 1";
$params = [
'timenow' => $timenow,
@@ -113,12 +116,15 @@ class notification_helper {
$sql = "SELECT DISTINCT a.id
FROM {assign} a
JOIN {course_modules} cm ON a.id = cm.instance
JOIN {course} c ON a.course = c.id
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 < :dd_timenow OR ao.duedate < :dd_ao_timenow)
AND (a.duedate > :dd_timewindow OR ao.duedate > :dd_ao_timewindow)
AND ((a.cutoffdate > :co_timenow OR a.cutoffdate = 0) OR
(ao.cutoffdate > :co_ao_timenow OR ao.cutoffdate = 0))";
(ao.cutoffdate > :co_ao_timenow OR ao.cutoffdate = 0))
AND cm.visible = 1
AND c.visible = 1";
$params = [
'dd_timenow' => $timenow,
@@ -147,10 +153,13 @@ class notification_helper {
$sql = "SELECT DISTINCT a.id
FROM {assign} a
JOIN {course_modules} cm ON a.id = cm.instance
JOIN {course} c ON a.course = c.id
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)";
AND (a.duedate >= :startofday OR ao.duedate >= :ao_startofday)
AND cm.visible = 1
AND c.visible = 1";
$params = [
'startofday' => $day['start'],
@@ -190,6 +199,8 @@ class notification_helper {
WHERE (a.duedate <= :endofday OR ao.duedate <= :ao_endofday)
AND (a.duedate >= :startofday OR ao.duedate >= :ao_startofday)
AND ue.userid = :userid
AND cm.visible = 1
AND c.visible = 1
ORDER BY a.duedate ASC";
$params = [
+3
View File
@@ -2346,6 +2346,9 @@ class assign {
}
}
// Exclude suspended users from the list of participants.
$additionalfilters .= " AND u.suspended = 0 AND u.auth <> 'nologin'";
$sql = "SELECT $fields
FROM {user} u
JOIN ($esql UNION $ssql) je ON je.id = u.id
@@ -167,6 +167,14 @@ final class notification_helper_test extends \advanced_testcase {
$user1 = $generator->create_user();
$generator->enrol_user($user1->id, $course->id, 'student');
// Suspended user, should not receive notification.
$user2 = $generator->create_user(['suspended' => 1]);
$generator->enrol_user($user2->id, $course->id, 'student');
// Nologin user, should not receive notification.
$user3 = $generator->create_user(['auth' => 'nologin']);
$generator->enrol_user($user3->id, $course->id, 'student');
/** @var \mod_assign_generator $assignmentgenerator */
$assignmentgenerator = $generator->get_plugin_generator('mod_assign');
@@ -234,6 +242,27 @@ final class notification_helper_test extends \advanced_testcase {
// Clear sink.
$sink->clear();
// Let's update the assignment visibility.
$DB->set_field('course_modules', 'visible', 0, ['id' => $assigncm->id]);
// Update the duedate to force a new notification.
$updatedata = new \stdClass();
$updatedata->id = $assignment->id;
$updatedata->duedate = $duedate + HOURSECS * 3;
$DB->update_record('assign', $updatedata);
// Run the tasks again.
$this->run_due_soon_notification_helper_tasks();
// There should not be a new notification the assignmnet is not visible.
$this->assertEmpty($sink->get_messages_by_component('mod_assign'));
// Update the visibility back to visible before the next assert.
$DB->set_field('course_modules', 'visible', 1, ['id' => $assigncm->id]);
// Clear sink.
$sink->clear();
// Let's modify the 'duedate' one more time.
$updatedata = new \stdClass();
$updatedata->id = $assignment->id;
@@ -410,6 +439,14 @@ final class notification_helper_test extends \advanced_testcase {
$course = $generator->create_course();
$user1 = $generator->create_and_enrol($course, 'student');
// Suspended user, should not receive notification.
$user2 = $generator->create_user(['suspended' => 1]);
$generator->enrol_user($user2->id, $course->id, 'student');
// Nologin user, should not receive notification.
$user3 = $generator->create_user(['auth' => 'nologin']);
$generator->enrol_user($user3->id, $course->id, 'student');
/** @var \mod_assign_generator $assignmentgenerator */
$assignmentgenerator = $generator->get_plugin_generator('mod_assign');
@@ -483,6 +520,28 @@ final class notification_helper_test extends \advanced_testcase {
$expectedsubject = get_string('assignmentoverduesubject', 'mod_assign', ['assignmentname' => $assignment->name]);
$this->assertEquals($expectedsubject, $message->subject);
// Let's update the assignment visibility.
$cm = get_coursemodule_from_instance('assign', $assignment->id, $course->id);
$DB->set_field('course_modules', 'visible', 0, ['id' => $cm->id]);
// Update the duedate to force a new notification.
$updatedata = new \stdClass();
$updatedata->id = $assignment->id;
$updatedata->duedate = $duedate + (MINSECS * 3);
$DB->update_record('assign', $updatedata);
// Clear sink.
$sink->clear();
// Run the tasks again.
$this->run_overdue_notification_helper_tasks();
// There should not be a new notification because the assignment is not visible.
$this->assertEmpty($sink->get_messages_by_component('mod_assign'));
// Update the visibility back to visible before the next assert.
$DB->set_field('course_modules', 'visible', 1, ['id' => $cm->id]);
// Let's modify the 'duedate' one more time.
$updatedata = new \stdClass();
$updatedata->id = $assignment->id;
@@ -610,6 +669,14 @@ final class notification_helper_test extends \advanced_testcase {
$user1 = $generator->create_user();
$generator->enrol_user($user1->id, $course->id, 'student');
// Suspended user, should not receive notification.
$user2 = $generator->create_user(['suspended' => 1]);
$generator->enrol_user($user2->id, $course->id, 'student');
// Nologin user, should not receive notification.
$user3 = $generator->create_user(['auth' => 'nologin']);
$generator->enrol_user($user3->id, $course->id, 'student');
/** @var \mod_assign_generator $assignmentgenerator */
$assignmentgenerator = $generator->get_plugin_generator('mod_assign');
@@ -635,6 +702,15 @@ final class notification_helper_test extends \advanced_testcase {
'submissiondrafts' => 0,
'assignsubmission_onlinetext_enabled' => 1,
]);
// Create an assignment with a visibility restriction.
$duedate4 = $clock->time() + WEEKSECS;
$assignment4 = $assignmentgenerator->create_instance([
'course' => $course->id,
'duedate' => $duedate4,
'visible' => 0,
'submissiondrafts' => 0,
'assignsubmission_onlinetext_enabled' => 1,
]);
$clock->bump(5);
// Run the tasks.
@@ -649,6 +725,7 @@ final class notification_helper_test extends \advanced_testcase {
$this->assertStringContainsString($assignment1->name, $message->fullmessagehtml);
$this->assertStringContainsString($assignment2->name, $message->fullmessagehtml);
$this->assertStringNotContainsString($assignment3->name, $message->fullmessagehtml);
$this->assertStringNotContainsString($assignment4->name, $message->fullmessagehtml);
// Check the message contains the formatted due date.
$formatteddate = userdate($duedate1, get_string('strftimedaydate', 'langconfig'));
@@ -676,6 +753,7 @@ final class notification_helper_test extends \advanced_testcase {
$this->assertStringNotContainsString($assignment1->name, $message->fullmessagehtml);
$this->assertStringContainsString($assignment2->name, $message->fullmessagehtml);
$this->assertStringNotContainsString($assignment3->name, $message->fullmessagehtml);
$this->assertStringNotContainsString($assignment4->name, $message->fullmessagehtml);
// Clear sink.
$sink->clear();
+13 -2
View File
@@ -44,11 +44,14 @@ class notification_helper {
$sql = "SELECT DISTINCT q.id
FROM {quiz} q
JOIN {course} c ON q.course = c.id
JOIN {course_modules} cm ON q.id = cm.instance
JOIN {modules} m ON cm.module = m.id AND m.name = :modulename
LEFT JOIN {quiz_overrides} qo ON q.id = qo.quiz
WHERE (q.timeopen < :futuretime OR qo.timeopen < :qo_futuretime)
AND (q.timeopen > :timenow OR qo.timeopen > :qo_timenow)";
AND (q.timeopen > :timenow OR qo.timeopen > :qo_timenow)
AND cm.visible = 1
AND c.visible = 1";
$params = [
'timenow' => $timenow,
@@ -76,13 +79,21 @@ class notification_helper {
$users = get_enrolled_users(
context: \context_module::instance($quizobj->get_cm()->id),
withcapability: 'mod/quiz:attempt',
userfields: 'u.id, u.firstname',
userfields: 'u.id, u.firstname, u.suspended, u.auth',
);
// Filter a list of users who meet the availability conditions.
$info = new \core_availability\info_module($quizobj->get_cm());
$users = $info->filter_user_list($users);
// Check for any override dates.
$overrides = $quizobj->get_override_manager()->get_all_overrides();
foreach ($users as $key => $user) {
if ($user->suspended || ($user->auth == 'nologin')) {
unset($users[$key]);
continue;
}
// Time open and time close dates can be user specific with an override.
// We begin by assuming it is the same as recorded in the quiz.
$user->timeopen = $quiz->timeopen;
@@ -78,6 +78,7 @@ final class notification_helper_test extends \advanced_testcase {
* Test getting users within a quiz that are within our date range.
*/
public function test_get_users_within_quiz(): void {
global $DB;
$this->resetAfterTest();
$generator = $this->getDataGenerator();
$helper = \core\di::get(notification_helper::class);
@@ -90,11 +91,13 @@ final class notification_helper_test extends \advanced_testcase {
$user3 = $generator->create_user();
$user4 = $generator->create_user();
$user5 = $generator->create_user();
$user6 = $generator->create_user(['suspended' => 1]);
$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, 'teacher');
$generator->enrol_user($user6->id, $course->id, 'student');
/** @var \mod_quiz_generator $quizgenerator */
$quizgenerator = $generator->get_plugin_generator('mod_quiz');
@@ -148,6 +151,35 @@ final class notification_helper_test extends \advanced_testcase {
// User5 should not be in the returned users because they are a teacher.
$this->assertArrayNotHasKey($user5->id, $users);
// User6 should not be in the returned users because it is suspended.
$this->assertArrayNotHasKey($user6->id, $users);
// Let's add some availability conditions.
$availability =
[
'op' => '&',
'showc' => [true],
'c' => [
[
'type' => 'group',
'id' => (int)$group->id,
],
],
];
$cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id);
$DB->set_field('course_modules', 'availability', json_encode($availability), ['id' => $cm->id]);
// Rebuild course cache to apply changes.
rebuild_course_cache($course->id, true);
// Get the users after availability conditions of the given quiz.
$users = notification_helper::get_users_within_quiz($quiz->id);
// Returns only users matching availability conditions who are in the specified group.
$this->assertCount(2, $users);
ksort($users);
$this->assertEquals([$user2->id, $user3->id], array_keys($users));
}
/**
@@ -220,6 +252,27 @@ final class notification_helper_test extends \advanced_testcase {
// Clear sink.
$sink->clear();
// Let's modify the 'timeopen' one more time and change the visibility.
$cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id);
$DB->set_field('course_modules', 'visible', 0, ['id' => $cm->id]);
$updatedata = new \stdClass();
$updatedata->id = $quiz->id;
$updatedata->timeopen = $timeopen + DAYSECS;
$DB->update_record('quiz', $updatedata);
// Run the tasks again.
$this->run_notification_helper_tasks();
// There should not be a new notification because the quiz is not visible.
$this->assertCount(0, $sink->get_messages_by_component('mod_quiz'));
// Set back the visibility.
$DB->set_field('course_modules', 'visible', 1, ['id' => $cm->id]);
// Clear sink.
$sink->clear();
// Let's modify the 'timeopen' one more time.
$updatedata = new \stdClass();
$updatedata->id = $quiz->id;