diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php index 94eba2db16c..261b4a19afc 100644 --- a/mod/quiz/attemptlib.php +++ b/mod/quiz/attemptlib.php @@ -2011,7 +2011,7 @@ class quiz_attempt { // Transition to the appropriate state. switch ($this->quizobj->get_quiz()->overduehandling) { case 'autosubmit': - $this->process_finish($timestamp, false, $studentisonline ? $timestamp : $timeclose); + $this->process_finish($timestamp, false, $studentisonline ? $timestamp : $timeclose, $studentisonline); return; case 'graceperiod': @@ -2183,8 +2183,9 @@ class quiz_attempt { * POST request are stored to be graded, before the attempt is finished. * @param ?int $timefinish if set, use this as the finish time for the attempt. * (otherwise use $timestamp as the finish time as well). + * @param bool $studentisonline is the student currently interacting with Moodle? */ - public function process_finish($timestamp, $processsubmitted, $timefinish = null) { + public function process_finish($timestamp, $processsubmitted, $timefinish = null, $studentisonline = false) { global $DB; $transaction = $DB->start_delegated_transaction(); @@ -2207,7 +2208,7 @@ class quiz_attempt { quiz_save_best_grade($this->get_quiz(), $this->attempt->userid); // Trigger event. - $this->fire_state_transition_event('\mod_quiz\event\attempt_submitted', $timestamp); + $this->fire_state_transition_event('\mod_quiz\event\attempt_submitted', $timestamp, $studentisonline); // Tell any access rules that care that the attempt is over. $this->get_access_manager($timestamp)->current_attempt_finished(); @@ -2246,7 +2247,7 @@ class quiz_attempt { $this->attempt->timecheckstate = $timestamp; $DB->update_record('quiz_attempts', $this->attempt); - $this->fire_state_transition_event('\mod_quiz\event\attempt_becameoverdue', $timestamp); + $this->fire_state_transition_event('\mod_quiz\event\attempt_becameoverdue', $timestamp, $studentisonline); $transaction->allow_commit(); @@ -2268,7 +2269,7 @@ class quiz_attempt { $this->attempt->timecheckstate = null; $DB->update_record('quiz_attempts', $this->attempt); - $this->fire_state_transition_event('\mod_quiz\event\attempt_abandoned', $timestamp); + $this->fire_state_transition_event('\mod_quiz\event\attempt_abandoned', $timestamp, $studentisonline); $transaction->allow_commit(); } @@ -2278,8 +2279,9 @@ class quiz_attempt { * * @param string $eventclass the event class name. * @param int $timestamp the timestamp to include in the event. + * @param bool $studentisonline is the student currently interacting with Moodle? */ - protected function fire_state_transition_event($eventclass, $timestamp) { + protected function fire_state_transition_event($eventclass, $timestamp, $studentisonline) { global $USER; $quizrecord = $this->get_quiz(); $params = array( @@ -2289,10 +2291,10 @@ class quiz_attempt { 'relateduserid' => $this->attempt->userid, 'other' => array( 'submitterid' => CLI_SCRIPT ? null : $USER->id, - 'quizid' => $quizrecord->id + 'quizid' => $quizrecord->id, + 'studentisonline' => $studentisonline ) ); - $event = $eventclass::create($params); $event->add_record_snapshot('quiz', $this->get_quiz()); $event->add_record_snapshot('quiz_attempts', $this->get_attempt()); @@ -2462,7 +2464,7 @@ class quiz_attempt { if ($becomingabandoned) { $this->process_abandon($timenow, true); } else { - $this->process_finish($timenow, !$toolate, $toolate ? $timeclose : $timenow); + $this->process_finish($timenow, !$toolate, $toolate ? $timeclose : $timenow, true); } } catch (question_out_of_sequence_exception $e) { diff --git a/mod/quiz/classes/event/attempt_submitted.php b/mod/quiz/classes/event/attempt_submitted.php index efa5302c007..6d881ce6d36 100644 --- a/mod/quiz/classes/event/attempt_submitted.php +++ b/mod/quiz/classes/event/attempt_submitted.php @@ -32,6 +32,7 @@ defined('MOODLE_INTERNAL') || die(); * * - int submitterid: id of submitter (null when trigged by CLI script). * - int quizid: (optional) the id of the quiz. + * - bool studentisonline: is the student currently interacting with Moodle? * } * * @package mod_quiz diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php index 9ca776ac3dd..e25765faa18 100644 --- a/mod/quiz/lang/en/quiz.php +++ b/mod/quiz/lang/en/quiz.php @@ -311,8 +311,16 @@ Thank you for submitting your answers to \'{$a->quizname}\' in course \'{$a->cou This message confirms that your answers have been saved. +You can access this quiz at {$a->quizurl}.'; +$string['emailconfirmbodyautosubmit'] = 'Hi {$a->username}, + +The time for the quiz \'{$a->quizname}\' in the course \'{$a->coursename}\' expired. Your answers were submitted automatically at {$a->submissiontime}. + +This message confirms that your answers have been saved. + You can access this quiz at {$a->quizurl}.'; $string['emailconfirmsmall'] = 'Thank you for submitting your answers to \'{$a->quizname}\''; +$string['emailconfirmautosubmitsmall'] = 'Thank you for submitting your answers to \'{$a->quizname}\''; $string['emailconfirmsubject'] = 'Submission confirmation: {$a->quizname}'; $string['emailnotifybody'] = 'Hi {$a->username}, diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index 41982de1fc1..34bc48b1df9 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -1584,10 +1584,11 @@ function quiz_get_combined_reviewoptions($quiz, $attempts) { * * @param object $a lots of useful information that can be used in the message * subject and body. + * @param bool $studentisonline is the student currently interacting with Moodle? * * @return int|false as for {@link message_send()}. */ -function quiz_send_confirmation($recipient, $a) { +function quiz_send_confirmation($recipient, $a, $studentisonline) { // Add information about the recipient to $a. // Don't do idnumber. we want idnumber to be the submitter's idnumber. @@ -1604,7 +1605,13 @@ function quiz_send_confirmation($recipient, $a) { $eventdata->userfrom = core_user::get_noreply_user(); $eventdata->userto = $recipient; $eventdata->subject = get_string('emailconfirmsubject', 'quiz', $a); - $eventdata->fullmessage = get_string('emailconfirmbody', 'quiz', $a); + + if ($studentisonline) { + $eventdata->fullmessage = get_string('emailconfirmbody', 'quiz', $a); + } else { + $eventdata->fullmessage = get_string('emailconfirmbodyautosubmit', 'quiz', $a); + } + $eventdata->fullmessageformat = FORMAT_PLAIN; $eventdata->fullmessagehtml = ''; @@ -1676,10 +1683,11 @@ function quiz_send_notification($recipient, $submitter, $a) { * @param object $attempt this attempt just finished * @param object $context the quiz context * @param object $cm the coursemodule for this quiz + * @param bool $studentisonline is the student currently interacting with Moodle? * * @return bool true if all necessary messages were sent successfully, else false. */ -function quiz_send_notification_messages($course, $quiz, $attempt, $context, $cm) { +function quiz_send_notification_messages($course, $quiz, $attempt, $context, $cm, $studentisonline) { global $CFG, $DB; // Do nothing if required objects not present. @@ -1760,7 +1768,7 @@ function quiz_send_notification_messages($course, $quiz, $attempt, $context, $cm // some but not all messages, and then try again later, then teachers may get // duplicate messages, but the student will always get exactly one. if ($sendconfirm) { - $allok = $allok && quiz_send_confirmation($submitter, $a); + $allok = $allok && quiz_send_confirmation($submitter, $a, $studentisonline); } return $allok; @@ -1858,6 +1866,7 @@ function quiz_attempt_submitted_handler($event) { $attempt = $event->get_record_snapshot('quiz_attempts', $event->objectid); $quiz = $event->get_record_snapshot('quiz', $attempt->quiz); $cm = get_coursemodule_from_id('quiz', $event->get_context()->instanceid, $event->courseid); + $eventdata = $event->get_data(); if (!($course && $quiz && $cm && $attempt)) { // Something has been deleted since the event was raised. Therefore, the @@ -1872,7 +1881,7 @@ function quiz_attempt_submitted_handler($event) { $completion->update_state($cm, COMPLETION_COMPLETE, $event->userid); } return quiz_send_notification_messages($course, $quiz, $attempt, - context_module::instance($cm->id), $cm); + context_module::instance($cm->id), $cm, $eventdata['other']['studentisonline']); } /** diff --git a/mod/quiz/tests/locallib_test.php b/mod/quiz/tests/locallib_test.php index c5f5b675338..70876a830f4 100644 --- a/mod/quiz/tests/locallib_test.php +++ b/mod/quiz/tests/locallib_test.php @@ -941,4 +941,57 @@ class mod_quiz_locallib_testcase extends advanced_testcase { $this->assertEquals('Settings overrides exist (Groups: 2, Users: 2)', html_to_text($renderer->quiz_override_summary_links($quiz, $cm), 0, false)); } + + /** + * Test quiz_send_confirmation function. + */ + public function test_quiz_send_confirmation() { + global $CFG, $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + $this->preventResetByRollback(); + + $course = $this->getDataGenerator()->create_course(); + $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); + $quiz = $quizgenerator->create_instance(['course' => $course->id]); + $cm = get_coursemodule_from_instance('quiz', $quiz->id); + + $recipient = $this->getDataGenerator()->create_user(['email' => 'student@example.com']); + + // Allow recipent to receive email confirm submission. + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + assign_capability('mod/quiz:emailconfirmsubmission', CAP_ALLOW, $studentrole->id, + context_course::instance($course->id), true); + $this->getDataGenerator()->enrol_user($recipient->id, $course->id, $studentrole->id, 'manual'); + + $timenow = time(); + $data = new stdClass(); + // Course info. + $data->courseid = $course->id; + $data->coursename = $course->fullname; + // Quiz info. + $data->quizname = $quiz->name; + $data->quizurl = $CFG->wwwroot . '/mod/quiz/view.php?id=' . $cm->id; + $data->quizid = $quiz->id; + $data->quizcmid = $quiz->cmid; + $data->attemptid = 1; + $data->submissiontime = userdate($timenow); + + $sink = $this->redirectEmails(); + quiz_send_confirmation($recipient, $data, true); + $messages = $sink->get_messages(); + $message = reset($messages); + $this->assertStringContainsString("Thank you for submitting your answers" , + quoted_printable_decode($message->body)); + $sink->close(); + + $sink = $this->redirectEmails(); + quiz_send_confirmation($recipient, $data, false); + $messages = $sink->get_messages(); + $message = reset($messages); + $this->assertStringContainsString("Your answers were submitted automatically" , + quoted_printable_decode($message->body)); + $sink->close(); + } }