Merge branch 'MDL-28166' of git://github.com/timhunt/moodle

This commit is contained in:
Eloy Lafuente (stronk7) 2011-07-06 19:03:59 +02:00
commit cb600c4606
12 changed files with 201 additions and 150 deletions

View File

@ -490,7 +490,7 @@ abstract class quiz_access_rule_base {
}
/**
* If, becuase of this rule, the user has to finish their attempt by a certain time,
* If, because of this rule, the user has to finish their attempt by a certain time,
* you should override this method to return the amount of time left in seconds.
* @param object $attempt the current attempt
* @param int $timenow the time now. We don't use $this->_timenow, so we can

View File

@ -995,14 +995,6 @@ class quiz_attempt {
$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 @@ class quiz_attempt {
}
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 @@ class quiz_attempt {
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();
}
}

View File

@ -122,7 +122,7 @@ class restore_quiz_activity_task extends restore_activity_task {
// All the ones calling to review.php have two rules to handle both old and new urls
// in any case they are always converted to new urls on restore
// TODO: In Moodle 2.x (x >= 5) kill the old rules
// Note we are using the 'quiz_attempt_id' mapping becuase that is the
// Note we are using the 'quiz_attempt_id' mapping because that is the
// one containing the quiz_attempt->ids old an new for quiz-attempt
$rules[] = new restore_log_rule('quiz', 'attempt',
'review.php?id={course_module}&attempt={quiz_attempt}', '{quiz}',

View File

@ -149,14 +149,14 @@ $capabilities = array(
'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,

61
mod/quiz/db/events.php Executable file
View File

@ -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/>.
/**
* 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.
*/

View File

@ -256,7 +256,7 @@ Thank you for submitting your answers to
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['quizclosed'] = 'This quiz closed on {$a}';
$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';

View File

@ -622,7 +622,7 @@ function quiz_update_all_final_grades($quiz) {
switch ($quiz->grademethod) {
case QUIZ_ATTEMPTFIRST:
// Becuase of the where clause, there will only be one row, but we
// Because of the where clause, there will only be one row, but we
// must still use an aggregate function.
$select = 'MAX(quiza.sumgrades)';
$join = $firstlastattemptjoin;
@ -630,7 +630,7 @@ function quiz_update_all_final_grades($quiz) {
break;
case QUIZ_ATTEMPTLAST:
// Becuase of the where clause, there will only be one row, but we
// Because of the where clause, there will only be one row, but we
// must still use an aggregate function.
$select = 'MAX(quiza.sumgrades)';
$join = $firstlastattemptjoin;
@ -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) {
function quiz_send_confirmation($recipient, $a) {
global $USER;
// 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;
// recipient is self
$a->useridnumber = $USER->idnumber;
$a->username = fullname($USER);
$a->userusername = $USER->username;
// fetch the subject and body from strings
$subject = get_string('emailconfirmsubject', 'quiz', $a);
$body = get_string('emailconfirmbody', 'quiz', $a);
// 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 = '<a href="' . $a->quizreporturl . '">' .
format_string($quiz->name) . ' report</a>';
$a->quizreviewurl = $CFG->wwwroot . '/mod/quiz/review.php?attempt=' . $attempt->id;
$a->quizreviewlink = '<a href="' . $a->quizreviewurl . '">' .
format_string($quiz->name) . ' review</a>';
$a->quizurl = $CFG->wwwroot . '/mod/quiz/view.php?id=' . $cm->id;
$a->quizlink = '<a href="' . $a->quizurl . '">' . format_string($quiz->name) . '</a>';
// 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;
}
}
$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 = '<a href="' . $a->quizreporturl . '">' .
format_string($quiz->name) . ' report</a>';
$a->quizreviewurl = $CFG->wwwroot . '/mod/quiz/review.php?attempt=' . $attempt->id;
$a->quizreviewlink = '<a href="' . $a->quizreviewurl . '">' .
format_string($quiz->name) . ' review</a>';
$a->quizurl = $CFG->wwwroot . '/mod/quiz/view.php?id=' . $cm->id;
$a->quizlink = '<a href="' . $a->quizurl . '">' . format_string($quiz->name) . '</a>';
// 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;
// send notifications if required
$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 the number of successfully sent emails
return $emailresult['good'];
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 quiz_send_notification_messages($course, $quiz, $attempt,
get_context_instance(CONTEXT_MODULE, $cm->id), $cm);
}
/**

View File

@ -338,7 +338,7 @@ class mod_quiz_mod_form extends moodleform_mod {
'boundary_add_fields');
// Add the disabledif rules. We cannot do this using the $repeatoptions parameter to
// repeat_elements becuase we don't want to dissable the first feedbacktext.
// repeat_elements because we don't want to dissable the first feedbacktext.
for ($i = 0; $i < $nextel; $i++) {
$mform->disabledIf('feedbackboundaries[' . $i . ']', 'grade', 'eq', 0);
$mform->disabledIf('feedbacktext[' . ($i + 1) . ']', 'grade', 'eq', 0);

View File

@ -112,20 +112,6 @@ add_to_log($attemptobj->get_courseid(), 'quiz', 'close attempt',
// 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());

View File

@ -52,7 +52,7 @@ class mod_quiz_admin_review_setting extends admin_setting {
/**
* This should match {@link mod_quiz_mod_form::$reviewfields} but copied
* here becuase generating the admin tree needs to be fast.
* here because generating the admin tree needs to be fast.
* @return array
*/
public static function fields() {

View File

@ -210,12 +210,13 @@ if ($attempt->preview) {
// 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();

View File

@ -25,6 +25,6 @@
defined('MOODLE_INTERNAL') || die();
$module->version = 2011051250;
$module->version = 2011070100;
$module->requires = 2011060313;
$module->cron = 0;