diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php
index adf1182387591..aedd15c973d43 100644
--- a/mod/quiz/attemptlib.php
+++ b/mod/quiz/attemptlib.php
@@ -995,14 +995,6 @@ public function check_file_access($slot, $reviewing, $contextid, $component,
$component, $filearea, $args, $forcedownload);
}
- /**
- * Triggers the sending of the notification emails at the end of this attempt.
- */
- public function quiz_send_notification_emails() {
- quiz_send_notification_emails($this->get_course(), $this->get_quiz(), $this->attempt,
- $this->quizobj->get_context(), $this->get_cm());
- }
-
/**
* Get the navigation panel object for this attempt.
*
@@ -1080,7 +1072,7 @@ public function save_question_flags() {
}
public function finish_attempt($timestamp) {
- global $DB;
+ global $DB, $USER;
$this->quba->process_all_actions($timestamp);
$this->quba->finish_all_questions($timestamp);
@@ -1093,7 +1085,21 @@ public function finish_attempt($timestamp) {
if (!$this->is_preview()) {
quiz_save_best_grade($this->get_quiz());
- $this->quiz_send_notification_emails();
+
+ // Trigger event
+ $eventdata = new stdClass();
+ $eventdata->component = 'mod_quiz';
+ $eventdata->attemptid = $this->attempt->id;
+ $eventdata->timefinish = $this->attempt->timefinish;
+ $eventdata->userid = $this->attempt->userid;
+ $eventdata->submitterid = $USER->id;
+ $eventdata->quizid = $this->get_quizid();
+ $eventdata->cmid = $this->get_cmid();
+ $eventdata->courseid = $this->get_courseid();
+ events_trigger('quiz_attempt_submitted', $eventdata);
+
+ // Clear the password check flag in the session.
+ $this->get_access_manager($timestamp)->clear_password_access();
}
}
diff --git a/mod/quiz/db/access.php b/mod/quiz/db/access.php
index a792b6ac0aa8d..485cc246a44e3 100644
--- a/mod/quiz/db/access.php
+++ b/mod/quiz/db/access.php
@@ -149,14 +149,14 @@
'archetypes' => array()
),
- // Receive email confirmation of own quiz submission
+ // Receive a confirmation message of own quiz submission.
'mod/quiz:emailconfirmsubmission' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array()
),
- // Receive email notification of other peoples quiz submissions
+ // Receive a notification message of other peoples' quiz submissions.
'mod/quiz:emailnotifysubmission' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
diff --git a/mod/quiz/db/events.php b/mod/quiz/db/events.php
new file mode 100755
index 0000000000000..bfb7b5c3527b1
--- /dev/null
+++ b/mod/quiz/db/events.php
@@ -0,0 +1,61 @@
+.
+
+/**
+ * Post-install code for the quiz module.
+ *
+ * @package mod
+ * @subpackage quiz
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+
+$handlers = array(
+ // Handle our own quiz_attempt_submitted event, as a way to send confirmation
+ // messages asynchronously.
+ 'quiz_attempt_submitted' => array (
+ 'handlerfile' => '/mod/quiz/locallib.php',
+ 'handlerfunction' => 'quiz_attempt_submitted_handler',
+ 'schedule' => 'cron',
+ ),
+);
+
+/* List of events generated by the quiz module, with the fields on the event object.
+
+quiz_attempt_started
+ ->component = 'mod_quiz';
+ ->attemptid = // The id of the new quiz attempt.
+ ->timestart = // The timestamp of when the attempt was started.
+ ->userid = // The user id that the attempt belongs to.
+ ->quizid = // The quiz id of the quiz the attempt belongs to.
+ ->cmid = // The course_module id of the quiz the attempt belongs to.
+ ->courseid = // The course id of the course the quiz belongs to.
+
+quiz_attempt_submitted
+ ->component = 'mod_quiz';
+ ->attemptid = // The id of the quiz attempt that was submitted.
+ ->timefinish = // The timestamp of when the attempt was submitted.
+ ->userid = // The user id that the attempt belongs to.
+ ->submitterid = // The user id of the user who sumitted the attempt.
+ ->quizid = // The quiz id of the quiz the attempt belongs to.
+ ->cmid = // The course_module id of the quiz the attempt belongs to.
+ ->courseid = // The course id of the course the quiz belongs to.
+
+*/
diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php
index f788ef5daeb94..deaf6a7ac0331 100644
--- a/mod/quiz/lang/en/quiz.php
+++ b/mod/quiz/lang/en/quiz.php
@@ -256,7 +256,7 @@
in course \'{$a->coursename}\'
at {$a->submissiontime}.
-This email confirms that we have safely received your answers.
+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}\'';
@@ -550,8 +550,8 @@
$string['quizcloses'] = 'Quiz closes';
$string['quizcloseson'] = 'This quiz will close at {$a}';
$string['quiz:deleteattempts'] = 'Delete quiz attempts';
-$string['quiz:emailconfirmsubmission'] = 'Get email confirmation when submitting';
-$string['quiz:emailnotifysubmission'] = 'Get email notification of submissions';
+$string['quiz:emailconfirmsubmission'] = 'Get a confirmation message when submitting';
+$string['quiz:emailnotifysubmission'] = 'Get a notification message when an attempt is submitted';
$string['quiz:grade'] = 'Grade quizzes manually';
$string['quiz:ignoretimelimits'] = 'Ignores time limit on quizzes';
$string['quizisclosed'] = 'This quiz is closed';
diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php
index 0e5718215db21..8cad96ad9cda9 100644
--- a/mod/quiz/locallib.php
+++ b/mod/quiz/locallib.php
@@ -1092,38 +1092,33 @@ function quiz_get_slot_for_question($quiz, $questionid) {
return null;
}
-/// FUNCTIONS FOR SENDING NOTIFICATION EMAILS ///////////////////////////////
+/// FUNCTIONS FOR SENDING NOTIFICATION MESSAGES ///////////////////////////////
/**
- * Sends confirmation email to the student taking the course
+ * Sends a confirmation message to the student confirming that the attempt was processed.
*
- * @param object $a associative array of replaceable fields for the templates
+ * @param object $a lots of useful information that can be used in the message
+ * subject and body.
*
- * @return bool
+ * @return int|false as for {@link message_send()}.
*/
-function quiz_send_confirmation($a) {
-
- global $USER;
-
- // recipient is self
- $a->useridnumber = $USER->idnumber;
- $a->username = fullname($USER);
- $a->userusername = $USER->username;
+function quiz_send_confirmation($recipient, $a) {
- // fetch the subject and body from strings
- $subject = get_string('emailconfirmsubject', 'quiz', $a);
- $body = get_string('emailconfirmbody', 'quiz', $a);
+ // Add information about the recipient to $a
+ // Don't do idnumber. we want idnumber to be the submitter's idnumber.
+ $a->username = fullname($recipient);
+ $a->userusername = $recipient->username;
- // send email and analyse result
+ // Prepare message
$eventdata = new stdClass();
- $eventdata->component = 'mod_quiz';
- $eventdata->name = 'confirmation';
+ $eventdata->component = 'mod_quiz';
+ $eventdata->name = 'confirmation';
$eventdata->notification = 1;
$eventdata->userfrom = get_admin();
- $eventdata->userto = $USER;
- $eventdata->subject = $subject;
- $eventdata->fullmessage = $body;
+ $eventdata->userto = $recipient;
+ $eventdata->subject = get_string('emailconfirmsubject', 'quiz', $a);
+ $eventdata->fullmessage = get_string('emailconfirmbody', 'quiz', $a);
$eventdata->fullmessageformat = FORMAT_PLAIN;
$eventdata->fullmessagehtml = '';
@@ -1131,7 +1126,8 @@ function quiz_send_confirmation($a) {
$eventdata->contexturl = $a->quizurl;
$eventdata->contexturlname = $a->quizname;
- return (bool)message_send($eventdata); // returns message id or false
+ // ... and send it.
+ return message_send($eventdata);
}
/**
@@ -1140,30 +1136,27 @@ function quiz_send_confirmation($a) {
* @param object $recipient user object of the intended recipient
* @param object $a associative array of replaceable fields for the templates
*
- * @return bool
+ * @return int|false as for {@link message_send()}.
*/
-function quiz_send_notification($recipient, $a) {
+function quiz_send_notification($recipient, $submitter, $a) {
global $USER;
- // recipient info for template
- $a->username = fullname($recipient);
+ // Recipient info for template
+ $a->useridnumber = $recipient->idnumber;
+ $a->username = fullname($recipient);
$a->userusername = $recipient->username;
- // fetch the subject and body from strings
- $subject = get_string('emailnotifysubject', 'quiz', $a);
- $body = get_string('emailnotifybody', 'quiz', $a);
-
- // send email and analyse result
+ // Prepare message
$eventdata = new stdClass();
- $eventdata->component = 'mod_quiz';
- $eventdata->name = 'submission';
+ $eventdata->component = 'mod_quiz';
+ $eventdata->name = 'submission';
$eventdata->notification = 1;
- $eventdata->userfrom = $USER;
+ $eventdata->userfrom = $submitter;
$eventdata->userto = $recipient;
- $eventdata->subject = $subject;
- $eventdata->fullmessage = $body;
+ $eventdata->subject = get_string('emailnotifysubject', 'quiz', $a);
+ $eventdata->fullmessage = get_string('emailnotifybody', 'quiz', $a);
$eventdata->fullmessageformat = FORMAT_PLAIN;
$eventdata->fullmessagehtml = '';
@@ -1171,12 +1164,12 @@ function quiz_send_notification($recipient, $a) {
$eventdata->contexturl = $a->quizreviewurl;
$eventdata->contexturlname = $a->quizname;
- return (bool)message_send($eventdata);
+ // ... and send it.
+ return message_send($eventdata);
}
/**
- * Takes a bunch of information to format into an email and send
- * to the specified recipient.
+ * Send all the requried messages when a quiz attempt is submitted.
*
* @param object $course the course
* @param object $quiz the quiz
@@ -1184,39 +1177,35 @@ function quiz_send_notification($recipient, $a) {
* @param object $context the quiz context
* @param object $cm the coursemodule for this quiz
*
- * @return int number of emails sent
+ * @return bool true if all necessary messages were sent successfully, else false.
*/
-function quiz_send_notification_emails($course, $quiz, $attempt, $context, $cm) {
- global $CFG, $USER;
- // we will count goods and bads for error logging
- $emailresult = array('good' => 0, 'fail' => 0);
+function quiz_send_notification_messages($course, $quiz, $attempt, $context, $cm) {
+ global $CFG, $DB;
- // do nothing if required objects not present
+ // Do nothing if required objects not present
if (empty($course) or empty($quiz) or empty($attempt) or empty($context)) {
- debugging('quiz_send_notification_emails: Email(s) not sent due to program error.',
- DEBUG_DEVELOPER);
- return $emailresult['fail'];
+ throw new coding_exception('$course, $quiz, $attempt, $context and $cm must all be set.');
}
- // check for confirmation required
+ $submitter = $DB->get_record('user', array('id' => $attempt->userid), '*', MUST_EXIST);
+
+ // Check for confirmation required
$sendconfirm = false;
$notifyexcludeusers = '';
- if (has_capability('mod/quiz:emailconfirmsubmission', $context, null, false)) {
- // exclude from notify emails later
- $notifyexcludeusers = $USER->id;
- // send the email
+ if (has_capability('mod/quiz:emailconfirmsubmission', $context, $submitter, false)) {
+ $notifyexcludeusers = $submitter->id;
$sendconfirm = true;
}
// check for notifications required
- $notifyfields = 'u.id, u.username, u.firstname, u.lastname, u.email, u.lang, ' .
- 'u.timezone, u.mailformat, u.maildisplay';
- $groups = groups_get_all_groups($course->id, $USER->id);
+ $notifyfields = 'u.id, u.username, u.firstname, u.lastname, u.idnumber, u.email, ' .
+ 'u.lang, u.timezone, u.mailformat, u.maildisplay';
+ $groups = groups_get_all_groups($course->id, $submitter->id);
if (is_array($groups) && count($groups) > 0) {
$groups = array_keys($groups);
} else if (groups_get_activity_groupmode($cm, $course) != NOGROUPS) {
// If the user is not in a group, and the quiz is set to group mode,
- // then set $gropus to a non-existant id so that only users with
+ // then set $groups to a non-existant id so that only users with
// 'moodle/site:accessallgroups' get notified.
$groups = -1;
} else {
@@ -1225,67 +1214,75 @@ function quiz_send_notification_emails($course, $quiz, $attempt, $context, $cm)
$userstonotify = get_users_by_capability($context, 'mod/quiz:emailnotifysubmission',
$notifyfields, '', '', '', $groups, $notifyexcludeusers, false, false, true);
- // if something to send, then build $a
- if (! empty($userstonotify) or $sendconfirm) {
- $a = new stdClass();
- // course info
- $a->coursename = $course->fullname;
- $a->courseshortname = $course->shortname;
- // quiz info
- $a->quizname = $quiz->name;
- $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);
- // student who sat the quiz info
- $a->studentidnumber = $USER->idnumber;
- $a->studentname = fullname($USER);
- $a->studentusername = $USER->username;
+ if (empty($userstonotify) && !$sendconfirm) {
+ return true; // Nothing to do.
}
- // send confirmation if required
- if ($sendconfirm) {
- // send the email and update stats
- switch (quiz_send_confirmation($a)) {
- case true:
- $emailresult['good']++;
- break;
- case false:
- $emailresult['fail']++;
- break;
- }
- }
-
- // send notifications if required
+ $a = new stdClass();
+ // Course info
+ $a->coursename = $course->fullname;
+ $a->courseshortname = $course->shortname;
+ // Quiz info
+ $a->quizname = $quiz->name;
+ $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);
+ // Student who sat the quiz info
+ $a->studentidnumber = $submitter->idnumber;
+ $a->studentname = fullname($submitter);
+ $a->studentusername = $submitter->username;
+
+ $allok = true;
+
+ // Send notifications if required
if (!empty($userstonotify)) {
- // loop through recipients and send an email to each and update stats
foreach ($userstonotify as $recipient) {
- switch (quiz_send_notification($recipient, $a)) {
- case true:
- $emailresult['good']++;
- break;
- case false:
- $emailresult['fail']++;
- break;
- }
+ $allok = $allok && quiz_send_notification($recipient, $submitter, $a);
}
}
- // log errors sending emails if any
- if (! empty($emailresult['fail'])) {
- debugging('quiz_send_notification_emails:: ' . $emailresult['fail'] .
- ' email(s) failed to be sent.', DEBUG_DEVELOPER);
+ // Send confirmation if required. We send the student confirmation last, so
+ // that if message sending is being intermittently buggy, which means we send
+ // 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);
+ }
+
+ return $allok;
+}
+
+/**
+ * Handle the quiz_attempt_submitted event.
+ *
+ * This sends the confirmation and notification messages, if required.
+ *
+ * @param object $event the event object.
+ */
+function quiz_attempt_submitted_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 the number of successfully sent emails
- return $emailresult['good'];
+ return quiz_send_notification_messages($course, $quiz, $attempt,
+ get_context_instance(CONTEXT_MODULE, $cm->id), $cm);
}
/**
diff --git a/mod/quiz/processattempt.php b/mod/quiz/processattempt.php
index 7d74e19432ca8..79da301051279 100644
--- a/mod/quiz/processattempt.php
+++ b/mod/quiz/processattempt.php
@@ -112,20 +112,6 @@
// Update the quiz attempt record.
$attemptobj->finish_attempt($timenow);
-// Trigger event
-$eventdata = new stdClass();
-$eventdata->component = 'mod_quiz';
-$eventdata->course = $attemptobj->get_courseid();
-$eventdata->quiz = $attemptobj->get_quizid();
-$eventdata->cm = $attemptobj->get_cmid();
-$eventdata->user = $USER;
-$eventdata->attempt = $attemptobj->get_attemptid();
-events_trigger('quiz_attempt_processed', $eventdata);
-
-// Clear the password check flag in the session.
-$accessmanager = $attemptobj->get_access_manager($timenow);
-$accessmanager->clear_password_access();
-
// Send the user to the review page.
$transaction->allow_commit();
redirect($attemptobj->review_url());
diff --git a/mod/quiz/startattempt.php b/mod/quiz/startattempt.php
index f44ecc54443de..4f85d373ca311 100644
--- a/mod/quiz/startattempt.php
+++ b/mod/quiz/startattempt.php
@@ -210,12 +210,13 @@
// Trigger event
$eventdata = new stdClass();
-$eventdata->component = 'mod_quiz';
-$eventdata->course = $quizobj->get_courseid();
-$eventdata->quiz = $quizobj->get_quizid();
-$eventdata->cm = $quizobj->get_cmid();
-$eventdata->user = $USER;
-$eventdata->attempt = $attempt->id;
+$eventdata->component = 'mod_quiz';
+$eventdata->attemptid = $attempt->id;
+$eventdata->timestart = $attempt->timestart;
+$eventdata->userid = $attempt->userid;
+$eventdata->quizid = $quizobj->get_quizid();
+$eventdata->cmid = $quizobj->get_cmid();
+$eventdata->courseid = $quizobj->get_courseid();
events_trigger('quiz_attempt_started', $eventdata);
$transaction->allow_commit();
diff --git a/mod/quiz/version.php b/mod/quiz/version.php
index 9b7fd7edc314a..1381c23225b34 100644
--- a/mod/quiz/version.php
+++ b/mod/quiz/version.php
@@ -25,6 +25,6 @@
defined('MOODLE_INTERNAL') || die();
-$module->version = 2011051250;
+$module->version = 2011070100;
$module->requires = 2011060313;
$module->cron = 0;