diff --git a/mod/quiz/db/events.php b/mod/quiz/db/events.php
index 84b4b1422b9..0ff77f52866 100644
--- a/mod/quiz/db/events.php
+++ b/mod/quiz/db/events.php
@@ -35,6 +35,14 @@ $handlers = array(
'handlerfunction' => 'quiz_attempt_submitted_handler',
'schedule' => 'cron',
),
+
+ // Handle our own quiz_attempt_overdue event, to email the student to let them
+ // know they forgot to submit, and that they have another chance.
+ 'quiz_attempt_overdue' => array (
+ 'handlerfile' => '/mod/quiz/locallib.php',
+ 'handlerfunction' => 'quiz_attempt_overdue_handler',
+ 'schedule' => 'cron',
+ ),
);
/* List of events generated by the quiz module, with the fields on the event object.
diff --git a/mod/quiz/db/messages.php b/mod/quiz/db/messages.php
index e8094070f62..c6ab3905d17 100644
--- a/mod/quiz/db/messages.php
+++ b/mod/quiz/db/messages.php
@@ -25,14 +25,20 @@
defined('MOODLE_INTERNAL') || die();
-$messageproviders = array (
- // Notify teacher that a student has submitted a quiz attempt
- 'submission' => array (
- 'capability' => 'mod/quiz:emailnotifysubmission'
+$messageproviders = array(
+ // Notify teacher that a student has submitted a quiz attempt.
+ 'submission' => array(
+ 'capability' => 'mod/quiz:emailnotifysubmission'
),
- // Confirm a student's quiz attempt
- 'confirmation' => array (
- 'capability' => 'mod/quiz:emailconfirmsubmission'
- )
+ // Confirm a student's quiz attempt.
+ 'confirmation' => array(
+ 'capability' => 'mod/quiz:emailconfirmsubmission'
+ ),
+
+ // Warning to the student that their quiz attempt is now overdue, if the quiz
+ // has a grace period.
+ 'attempt_overdue' => array(
+ 'capability' => 'mod/quiz:emailwarnoverdue'
+ ),
);
diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php
index d207e9e4b6f..340e678612a 100644
--- a/mod/quiz/lang/en/quiz.php
+++ b/mod/quiz/lang/en/quiz.php
@@ -262,16 +262,27 @@ This message confirms that we have safely received your answers.
You can access this quiz at {$a->quizurl}.';
$string['emailconfirmsmall'] = 'Thank you for submitting your answers to \'{$a->quizname}\'';
-$string['emailconfirmsubject'] = 'Quiz submission confirmation: {$a->quizname}';
+$string['emailconfirmsubject'] = 'Submission confirmation: {$a->quizname}';
$string['emailnotifybody'] = 'Dear {$a->username},
-{$a->studentname} has completed the quiz
+{$a->studentname} has completed
\'{$a->quizname}\' ({$a->quizurl})
in course \'{$a->coursename}\'
You can review this attempt at {$a->quizreviewurl}.';
-$string['emailnotifysmall'] = '{$a->studentname} has completed {$a->quizname}';
-$string['emailnotifysubject'] = '{$a->studentname} has completed quiz {$a->quizname}';
+$string['emailnotifysmall'] = '{$a->studentname} has completed {$a->quizname}. See {$a->quizreviewurl}';
+$string['emailnotifysubject'] = '{$a->studentname} has completed {$a->quizname}';
+$string['emailoverduebody'] = 'Dear {$a->studentname},
+
+You started an attempt at \'{$a->quizname}\'
+in course \'{$a->coursename}\', but you never submitted it.
+
+If you would would still like to submit this attempt, please go to
+{$a->attemptsummaryurl} and click the submit button.
+You must do this before {$a->attemptgraceend}
+otherwise you attempt will not be counted.';
+$string['emailoverduesmall'] = 'You did not submit your attempt at {$a->quizname}. Please go to {$a->attemptsummaryurl} before {$a->attemptgraceend} if you would still like to submit.';
+$string['emailoverduesubject'] = 'Attempt now overdue: {$a->quizname}';
$string['empty'] = 'Empty';
$string['enabled'] = 'Enabled';
$string['endtest'] = 'Finish attempt ...';
diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php
index 82797a68ee8..0e8fd9092c3 100644
--- a/mod/quiz/locallib.php
+++ b/mod/quiz/locallib.php
@@ -1255,14 +1255,14 @@ function quiz_send_notification_messages($course, $quiz, $attempt, $context, $cm
$a->quizreporturl = $CFG->wwwroot . '/mod/quiz/report.php?id=' . $cm->id;
$a->quizreportlink = '' .
format_string($quiz->name) . ' report';
- $a->quizreviewurl = $CFG->wwwroot . '/mod/quiz/review.php?attempt=' . $attempt->id;
- $a->quizreviewlink = '' .
- format_string($quiz->name) . ' review';
$a->quizurl = $CFG->wwwroot . '/mod/quiz/view.php?id=' . $cm->id;
$a->quizlink = '' . format_string($quiz->name) . '';
// Attempt info
$a->submissiontime = userdate($attempt->timefinish);
$a->timetaken = format_time($attempt->timefinish - $attempt->timestart);
+ $a->quizreviewurl = $CFG->wwwroot . '/mod/quiz/review.php?attempt=' . $attempt->id;
+ $a->quizreviewlink = '' .
+ format_string($quiz->name) . ' review';
// Student who sat the quiz info
$a->studentidnumber = $submitter->idnumber;
$a->studentname = fullname($submitter);
@@ -1288,6 +1288,80 @@ function quiz_send_notification_messages($course, $quiz, $attempt, $context, $cm
return $allok;
}
+/**
+ * Send the notification message when a quiz attempt becomes overdue.
+ *
+ * @param object $course the course
+ * @param object $quiz the quiz
+ * @param object $attempt this attempt just finished
+ * @param object $context the quiz context
+ * @param object $cm the coursemodule for this quiz
+ */
+function quiz_send_overdue_message($course, $quiz, $attempt, $context, $cm) {
+ global $CFG, $DB;
+
+ // Do nothing if required objects not present
+ if (empty($course) or empty($quiz) or empty($attempt) or empty($context)) {
+ throw new coding_exception('$course, $quiz, $attempt, $context and $cm must all be set.');
+ }
+
+ $submitter = $DB->get_record('user', array('id' => $attempt->userid), '*', MUST_EXIST);
+
+ if (!has_capability('mod/quiz:emailwarnoverdue', $context, $submitter, false)) {
+ return; // Message not required.
+ }
+
+ // Prepare lots of useful information that admins might want to include in
+ // the email message.
+ $quizname = format_string($quiz->name);
+
+ $deadlines = array();
+ if ($quiz->timelimit) {
+ $deadlines[] = $attempt->timestart + $quiz->timelimit;
+ }
+ if ($quiz->timeclose) {
+ $deadlines[] = $quiz->timeclose;
+ }
+ $duedate = min($deadlines) + $quiz->graceperiod;
+
+ $a = new stdClass();
+ // Course info.
+ $a->coursename = $course->fullname;
+ $a->courseshortname = $course->shortname;
+ // Quiz info.
+ $a->quizname = $quizname;
+ $a->quizurl = $CFG->wwwroot . '/mod/quiz/view.php?id=' . $cm->id;
+ $a->quizlink = '' . $quizname . '';
+ // Attempt info.
+ $a->attemptgraceend = format_time($duedate);
+ $a->attemptsummaryurl = $CFG->wwwroot . '/mod/quiz/summary.php?attempt=' . $attempt->id;
+ $a->attemptsummarylink = '' . $quizname . ' review';
+ // Student's info.
+ $a->studentidnumber = $submitter->idnumber;
+ $a->studentname = fullname($submitter);
+ $a->studentusername = $submitter->username;
+
+ // Prepare the message.
+ $eventdata = new stdClass();
+ $eventdata->component = 'mod_quiz';
+ $eventdata->name = 'attempt_overdue';
+ $eventdata->notification = 1;
+
+ $eventdata->userfrom = get_admin();
+ $eventdata->userto = $submitter;
+ $eventdata->subject = get_string('emailoverduesubject', 'quiz', $a);
+ $eventdata->fullmessage = get_string('emailoverduebody', 'quiz', $a);
+ $eventdata->fullmessageformat = FORMAT_PLAIN;
+ $eventdata->fullmessagehtml = '';
+
+ $eventdata->smallmessage = get_string('emailoverduesmall', 'quiz', $a);
+ $eventdata->contexturl = $a->quizurl;
+ $eventdata->contexturlname = $a->quizname;
+
+ // Send the message.
+ return message_send($eventdata);
+}
+
/**
* Handle the quiz_attempt_submitted event.
*
@@ -1313,6 +1387,36 @@ function quiz_attempt_submitted_handler($event) {
get_context_instance(CONTEXT_MODULE, $cm->id), $cm);
}
+/**
+ * Handle the quiz_attempt_overdue event.
+ *
+ * For quizzes with applicable settings, this sends a message to the user, reminding
+ * them that they forgot to submit, and that they have another chance to do so.
+ *
+ * @param object $event the event object.
+ */
+function quiz_attempt_overdue_handler($event) {
+ global $DB;
+
+ $course = $DB->get_record('course', array('id' => $event->courseid));
+ $quiz = $DB->get_record('quiz', array('id' => $event->quizid));
+ $cm = get_coursemodule_from_id('quiz', $event->cmid, $event->courseid);
+ $attempt = $DB->get_record('quiz_attempts', array('id' => $event->attemptid));
+
+ if (!($course && $quiz && $cm && $attempt)) {
+ // Something has been deleted since the event was raised. Therefore, the
+ // event is no longer relevant.
+ return true;
+ }
+
+ return quiz_send_overdue_message($course, $quiz, $attempt,
+ get_context_instance(CONTEXT_MODULE, $cm->id), $cm);
+}
+
+/**
+ * Get the information about the standard quiz JavaScript module.
+ * @return array a standard jsmodule structure.
+ */
function quiz_get_js_module() {
global $PAGE;