Merge branch 'MDL-41039-master' of git://github.com/FMCorz/moodle
authorSam Hemelryk <sam@moodle.com>
Tue, 1 Oct 2013 23:10:08 +0000 (12:10 +1300)
committerSam Hemelryk <sam@moodle.com>
Tue, 1 Oct 2013 23:10:08 +0000 (12:10 +1300)
mod/quiz/attemptlib.php
mod/quiz/classes/event/attempt_abandoned.php [new file with mode: 0644]
mod/quiz/classes/event/attempt_becameoverdue.php [new file with mode: 0644]
mod/quiz/classes/event/attempt_started.php [new file with mode: 0644]
mod/quiz/classes/event/attempt_submitted.php [new file with mode: 0644]
mod/quiz/db/events.php
mod/quiz/lang/en/quiz.php
mod/quiz/locallib.php
mod/quiz/tests/events_test.php [new file with mode: 0644]

index df659ae..7316434 100644 (file)
@@ -1422,7 +1422,7 @@ class quiz_attempt {
             quiz_save_best_grade($this->get_quiz(), $this->attempt->userid);
 
             // Trigger event.
-            $this->fire_state_transition_event('quiz_attempt_submitted', $timestamp);
+            $this->fire_state_transition_event('\mod_quiz\event\attempt_submitted', $timestamp);
 
             // Tell any access rules that care that the attempt is over.
             $this->get_access_manager($timestamp)->current_attempt_finished();
@@ -1459,7 +1459,7 @@ class quiz_attempt {
         $this->attempt->timecheckstate = $timestamp;
         $DB->update_record('quiz_attempts', $this->attempt);
 
-        $this->fire_state_transition_event('quiz_attempt_overdue', $timestamp);
+        $this->fire_state_transition_event('\mod_quiz\event\attempt_becameoverdue', $timestamp);
 
         $transaction->allow_commit();
     }
@@ -1478,45 +1478,35 @@ class quiz_attempt {
         $this->attempt->timecheckstate = null;
         $DB->update_record('quiz_attempts', $this->attempt);
 
-        $this->fire_state_transition_event('quiz_attempt_abandoned', $timestamp);
+        $this->fire_state_transition_event('\mod_quiz\event\attempt_abandoned', $timestamp);
 
         $transaction->allow_commit();
     }
 
     /**
      * Fire a state transition event.
-     * @param string $event the type of event. Should be listed in db/events.php.
+     * the same event information.
+     * @param string $eventclass the event class name.
      * @param int $timestamp the timestamp to include in the event.
+     * @return void
      */
-    protected function fire_state_transition_event($event, $timestamp) {
+    protected function fire_state_transition_event($eventclass, $timestamp) {
         global $USER;
 
-        // Trigger event.
-        $eventdata = new stdClass();
-        $eventdata->component   = 'mod_quiz';
-        $eventdata->attemptid   = $this->attempt->id;
-        $eventdata->timestamp   = $timestamp;
-        $eventdata->userid      = $this->attempt->userid;
-        $eventdata->quizid      = $this->get_quizid();
-        $eventdata->cmid        = $this->get_cmid();
-        $eventdata->courseid    = $this->get_courseid();
-
-        // I don't think if (CLI_SCRIPT) is really the right logic here. The
-        // question is really 'is $USER currently set to a real user', but I cannot
-        // see standard Moodle function to answer that question. For example,
-        // cron fakes $USER.
-        if (CLI_SCRIPT) {
-            $eventdata->submitterid = null;
-        } else {
-            $eventdata->submitterid = $USER->id;
-        }
-
-        if ($event == 'quiz_attempt_submitted') {
-            // Backwards compatibility for this event type. $eventdata->timestamp is now preferred.
-            $eventdata->timefinish = $timestamp;
-        }
-
-        events_trigger($event, $eventdata);
+        $params = array(
+            'context' => $this->get_quizobj()->get_context(),
+            'courseid' => $this->get_courseid(),
+            'objectid' => $this->attempt->id,
+            'relateduserid' => $this->attempt->userid,
+            'other' => array(
+                'submitterid' => CLI_SCRIPT ? null : $USER->id
+            )
+        );
+
+        $event = $eventclass::create($params);
+        $event->add_record_snapshot('quiz', $this->get_quiz());
+        $event->add_record_snapshot('quiz_attempts', $this->get_attempt());
+        $event->trigger();
     }
 
     /**
diff --git a/mod/quiz/classes/event/attempt_abandoned.php b/mod/quiz/classes/event/attempt_abandoned.php
new file mode 100644 (file)
index 0000000..5b6f439
--- /dev/null
@@ -0,0 +1,114 @@
+<?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/>.
+
+/**
+ * Quiz module event class.
+ *
+ * @package    mod_quiz
+ * @copyright  2013 Adrian Greeve <adrian@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_quiz\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Event for when a quiz attempt is abandoned.
+ *
+ * @package    mod_quiz
+ * @copyright  2013 Adrian Greeve <adrian@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class attempt_abandoned extends \core\event\base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'quiz_attempts';
+        $this->data['crud'] = 'u';
+        $this->data['level'] = self::LEVEL_PARTICIPATING;
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'A quiz with the id of ' . $this->other['quizid'] . ' has been marked as abandoned for the user with the id of ' .
+            $this->relateduserid;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquizattemptabandoned', 'mod_quiz');
+    }
+
+    /**
+     * Does this event replace a legacy event?
+     *
+     * @return string legacy event name
+     */
+    static public function get_legacy_eventname() {
+        return 'quiz_attempt_abandoned';
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/mod/quiz/review.php', array('attempt' => $this->objectid));
+    }
+
+    /**
+     * Legacy event data if get_legacy_eventname() is not empty.
+     *
+     * @return stdClass
+     */
+    protected function get_legacy_eventdata() {
+        $attempt = $this->get_record_snapshot('quiz_attempts', $this->objectid);
+
+        $legacyeventdata = new \stdClass();
+        $legacyeventdata->component = 'mod_quiz';
+        $legacyeventdata->attemptid = $this->objectid;
+        $legacyeventdata->timestamp = $attempt->timemodified;
+        $legacyeventdata->userid = $this->relateduserid;
+        $legacyeventdata->quizid = $attempt->quiz;
+        $legacyeventdata->cmid = $this->context->instanceid;
+        $legacyeventdata->courseid = $this->courseid;
+        $legacyeventdata->submitterid = $this->other['submitterid'];
+
+        return $legacyeventdata;
+
+    }
+
+    /**
+     * Custom validation.
+     */
+    protected function validate_data() {
+        if (!array_key_exists('submitterid', $this->other)) {
+            throw new \coding_exception('Other must contain the key submitterid');
+        } else if (!isset($this->relateduserid)) {
+            throw new \coding_exception('relateduserid must be set');
+        }
+    }
+}
diff --git a/mod/quiz/classes/event/attempt_becameoverdue.php b/mod/quiz/classes/event/attempt_becameoverdue.php
new file mode 100644 (file)
index 0000000..e6ca398
--- /dev/null
@@ -0,0 +1,119 @@
+<?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/>.
+
+/**
+ * Quiz module event class.
+ *
+ * @package    mod_quiz
+ * @copyright  2013 Adrian Greeve <adrian@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_quiz\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Event for when a quiz attempt is overdue.
+ *
+ * Please note that the name of this event is not following the event naming convention.
+ * Its name should not be used as a reference for other events to be created.
+ *
+ * @package    mod_quiz
+ * @copyright  2013 Adrian Greeve <adrian@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class attempt_becameoverdue extends \core\event\base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'quiz_attempts';
+        $this->data['crud'] = 'u';
+        $this->data['level'] = self::LEVEL_PARTICIPATING;
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'A quiz with the id of ' . $this->other['quizid'] . ' has been marked as overdue for the user with ' .
+            'the id of '. $this->relateduserid;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquizattempttimelimitexceeded', 'mod_quiz');
+    }
+
+    /**
+     * Does this event replace a legacy event?
+     *
+     * @return string legacy event name
+     */
+    static public function get_legacy_eventname() {
+        return 'quiz_attempt_overdue';
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/mod/quiz/review.php', array('attempt' => $this->objectid));
+    }
+
+     /**
+     * Legacy event data if get_legacy_eventname() is not empty.
+     *
+     * @return stdClass
+     */
+    protected function get_legacy_eventdata() {
+        $attempt = $this->get_record_snapshot('quiz_attempts', $this->objectid);
+
+        $legacyeventdata = new \stdClass();
+        $legacyeventdata->component = 'mod_quiz';
+        $legacyeventdata->attemptid = $this->objectid;
+        $legacyeventdata->timestamp = $attempt->timemodified;
+        $legacyeventdata->userid = $this->relateduserid;
+        $legacyeventdata->quizid = $attempt->quiz;
+        $legacyeventdata->cmid = $this->context->instanceid;
+        $legacyeventdata->courseid = $this->courseid;
+        $legacyeventdata->submitterid = $this->other['submitterid'];
+
+        return $legacyeventdata;
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        if (!array_key_exists('submitterid', $this->other)) {
+            throw new \coding_exception('Other must contain the key submitterid');
+        } else if (!isset($this->relateduserid)) {
+            throw new \coding_exception('relateduserid must be set');
+        }
+    }
+}
diff --git a/mod/quiz/classes/event/attempt_started.php b/mod/quiz/classes/event/attempt_started.php
new file mode 100644 (file)
index 0000000..abab65e
--- /dev/null
@@ -0,0 +1,114 @@
+<?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/>.
+
+/**
+ * Quiz module event class.
+ *
+ * @package    mod_quiz
+ * @copyright  2013 Adrian Greeve <adrian@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_quiz\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Event for when a quiz attempt is started.
+ *
+ * @package    mod_quiz
+ * @copyright  2013 Adrian Greeve <adrian@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class attempt_started extends \core\event\base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'quiz_attempts';
+        $this->data['crud'] = 'c';
+        $this->data['level'] = self::LEVEL_PARTICIPATING;
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'A quiz with the id of ' . $this->other['quizid'] . ' was started by a user with the id ' .
+            $this->relateduserid . '.';
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquizattemptstarted', 'mod_quiz');
+    }
+
+    /**
+     * Does this event replace a legacy event?
+     *
+     * @return string legacy event name
+     */
+    static public function get_legacy_eventname() {
+        return 'quiz_attempt_started';
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/mod/quiz/review.php', array('attempt' => $this->objectid));
+    }
+
+    /**
+     * Legacy event data if get_legacy_eventname() is not empty.
+     *
+     * @return stdClass
+     */
+    protected function get_legacy_eventdata() {
+        $attempt = $this->get_record_snapshot('quiz_attempts', $this->objectid);
+
+        $legacyeventdata = new \stdClass();
+        $legacyeventdata->component = 'mod_quiz';
+        $legacyeventdata->attemptid = $attempt->id;
+        $legacyeventdata->timestart = $attempt->timestart;
+        $legacyeventdata->timestamp = $attempt->timestart;
+        $legacyeventdata->userid = $this->relateduserid;
+        $legacyeventdata->quizid = $attempt->quiz;
+        $legacyeventdata->cmid = $this->context->instanceid;
+        $legacyeventdata->courseid = $this->courseid;
+
+        return $legacyeventdata;
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        if (!isset($this->relateduserid)) {
+            throw new \coding_exception('relateduserid must be set');
+        }
+    }
+}
diff --git a/mod/quiz/classes/event/attempt_submitted.php b/mod/quiz/classes/event/attempt_submitted.php
new file mode 100644 (file)
index 0000000..83d54f5
--- /dev/null
@@ -0,0 +1,117 @@
+<?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/>.
+
+/**
+ * Quiz module event class.
+ *
+ * @package    mod_quiz
+ * @copyright  2013 Adrian Greeve <adrian@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_quiz\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Event for when a quiz attempt is submitted.
+ *
+ * @package    mod_quiz
+ * @copyright  2013 Adrian Greeve <adrian@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class attempt_submitted extends \core\event\base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'quiz_attempts';
+        $this->data['crud'] = 'u';
+        $this->data['level'] = self::LEVEL_PARTICIPATING;
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'A quiz with the id of ' . $this->other['quizid'] . ' has been marked as submitted for the user with the id of ' .
+            $this->relateduserid;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquizattemptsubmitted', 'mod_quiz');
+    }
+
+    /**
+     * Does this event replace a legacy event?
+     *
+     * @return string legacy event name
+     */
+    static public function get_legacy_eventname() {
+        return 'quiz_attempt_submitted';
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/mod/quiz/review.php', array('attempt' => $this->objectid));
+    }
+
+    /**
+     * Legacy event data if get_legacy_eventname() is not empty.
+     *
+     * @return stdClass
+     */
+    protected function get_legacy_eventdata() {
+        $attempt = $this->get_record_snapshot('quiz_attempts', $this->objectid);
+
+        $legacyeventdata = new \stdClass();
+        $legacyeventdata->component = 'mod_quiz';
+        $legacyeventdata->attemptid = $this->objectid;
+        $legacyeventdata->timestamp = $attempt->timefinish;
+        $legacyeventdata->userid = $this->relateduserid;
+        $legacyeventdata->quizid = $attempt->quiz;
+        $legacyeventdata->cmid = $this->context->instanceid;
+        $legacyeventdata->courseid = $this->courseid;
+        $legacyeventdata->submitterid = $this->other['submitterid'];
+        $legacyeventdata->timefinish = $attempt->timefinish;
+
+        return $legacyeventdata;
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        if (!array_key_exists('submitterid', $this->other)) {
+            throw new \coding_exception('Other must contain the key submitterid');
+        } else if (!isset($this->relateduserid)) {
+            throw new \coding_exception('relateduserid must be set');
+        }
+    }
+}
index 4cab28f..50c12e5 100644 (file)
 
 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',
-    ),
-
-    // 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',
-    ),
-
-);
-
 $observers = array(
 
     // Handle group events, so that open quiz attempts with group overrides get updated check times.
@@ -70,49 +50,21 @@ $observers = array(
         'callback' => '\mod_quiz\group_observers::group_member_removed',
     ),
 
-);
-
-/* 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.
-    ->timestamp   = // 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.
-    ->timestamp   = // 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.
-
-quiz_attempt_overdue
-    ->component   = 'mod_quiz';
-    ->attemptid   = // The id of the quiz attempt that has become overdue.
-    ->timestamp   = // The timestamp of when the attempt become overdue.
-    ->userid      = // The user id that the attempt belongs to.
-    ->submitterid = // The user id of the user who triggered this transition (may be null, e.g. on cron.).
-    ->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_abandoned
-    ->component   = 'mod_quiz';
-    ->attemptid   = // The id of the quiz attempt that was submitted.
-    ->timestamp   = // 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 triggered this transition (may be null, e.g. on cron.).
-    ->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.
+    // Handle our own \mod_quiz\event\attempt_becameoverdue event, to email
+    // the student to let them know they forgot to submit, and that they have another chance.
+    array(
+        'eventname' => '\mod_quiz\event\attempt_becameoverdue',
+        'includefile' => '/mod/quiz/locallib.php',
+        'callback' => 'quiz_attempt_overdue_handler',
+        'internal' => false,
+    ),
 
-*/
+    // Handle our own \mod_quiz\event\attempt_submitted event, as a way to
+    // send confirmation messages asynchronously.
+    array(
+        'eventname' => '\mod_quiz\event\attempt_submitted',
+        'includefile'     => '/mod/quiz/locallib.php',
+        'callback' => 'quiz_attempt_submitted_handler',
+        'internal' => false
+    ),
+);
index c6a4f8b..432fb24 100644 (file)
@@ -299,6 +299,10 @@ $string['errornotnumbers'] = 'Error - answers must be numeric';
 $string['errorunexpectedevent'] = 'Unexpected event code {$a->event} found for question {$a->questionid} in attempt {$a->attemptid}.';
 $string['essay'] = 'Essay';
 $string['essayquestions'] = 'Questions';
+$string['eventquizattemptabandoned'] = 'Quiz attempt abandoned';
+$string['eventquizattempttimelimitexceeded'] = 'Quiz attempt time limit exceeded';
+$string['eventquizattemptstarted'] = 'Quiz attempt started';
+$string['eventquizattemptsubmitted'] = 'Quiz attempt submitted';
 $string['everynquestions'] = 'Every {$a} questions';
 $string['everyquestion'] = 'Every question';
 $string['everythingon'] = 'Everything on';
index a11c611..71ded19 100644 (file)
@@ -290,16 +290,15 @@ function quiz_attempt_save_started($quizobj, $quba, $attempt) {
  */
 function quiz_fire_attempt_started_event($attempt, $quizobj) {
     // Trigger event.
-    $eventdata = new stdClass();
-    $eventdata->component = 'mod_quiz';
-    $eventdata->attemptid = $attempt->id;
-    $eventdata->timestart = $attempt->timestart;
-    $eventdata->timestamp = $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);
+    $eventdata = array();
+    $eventdata['context'] = $quizobj->get_context();
+    $eventdata['courseid'] = $quizobj->get_courseid();
+    $eventdata['relateduserid'] = $attempt->userid;
+    $eventdata['objectid'] = $attempt->id;
+    $event = \mod_quiz\event\attempt_started::create($eventdata);
+    $event->add_record_snapshot('quiz', $quizobj->get_quiz());
+    $event->add_record_snapshot('quiz_attempts', $attempt);
+    $event->trigger();
 }
 
 /**
@@ -1744,9 +1743,9 @@ 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));
+    $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);
 
     if (!($course && $quiz && $cm && $attempt)) {
         // Something has been deleted since the event was raised. Therefore, the
@@ -1770,9 +1769,9 @@ 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));
+    $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);
 
     if (!($course && $quiz && $cm && $attempt)) {
         // Something has been deleted since the event was raised. Therefore, the
diff --git a/mod/quiz/tests/events_test.php b/mod/quiz/tests/events_test.php
new file mode 100644 (file)
index 0000000..1743af0
--- /dev/null
@@ -0,0 +1,222 @@
+<?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/>.
+
+/**
+ * Quiz events tests.
+ *
+ * @package    mod_quiz
+ * @category   phpunit
+ * @copyright  2013 Adrian Greeve
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
+require_once($CFG->dirroot . '/mod/quiz/editlib.php');
+
+/**
+ * Unit tests for quiz events.
+ *
+ * @package    mod_quiz
+ * @category   phpunit
+ * @copyright  2013 Adrian Greeve
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class mod_quiz_events_testcase extends advanced_testcase {
+
+    protected function prepare_quiz_data() {
+
+        $this->resetAfterTest(true);
+
+        // Create a course
+        $course = $this->getDataGenerator()->create_course();
+
+        // Make a quiz.
+        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
+
+        $quiz = $quizgenerator->create_instance(array('course'=>$course->id, 'questionsperpage' => 0,
+            'grade' => 100.0, 'sumgrades' => 2));
+
+        $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id);
+
+        // Create a couple of questions.
+        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
+
+        $cat = $questiongenerator->create_question_category();
+        $saq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
+        $numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
+
+        // Add them to the quiz.
+        quiz_add_quiz_question($saq->id, $quiz);
+        quiz_add_quiz_question($numq->id, $quiz);
+
+        // Make a user to do the quiz.
+        $user1 = $this->getDataGenerator()->create_user();
+        $this->setUser($user1);
+
+        $quizobj = quiz::create($quiz->id, $user1->id);
+
+        // Start the attempt.
+        $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
+        $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
+
+        $timenow = time();
+        $attempt = quiz_create_attempt($quizobj, 1, false, $timenow);
+        quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
+        quiz_attempt_save_started($quizobj, $quba, $attempt);
+
+        return array($quizobj, $quba, $attempt);
+    }
+
+    public function test_attempt_submitted() {
+
+        list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
+        $attemptobj = quiz_attempt::create($attempt->id);
+
+        // Catch the event.
+        $sink = $this->redirectEvents();
+
+        $timefinish = time();
+        $attemptobj->process_finish($timefinish, false);
+        $events = $sink->get_events();
+        $sink->close();
+
+        // Validate the event.
+        $this->assertCount(1, $events);
+        $event = $events[0];
+        $this->assertInstanceOf('\mod_quiz\event\attempt_submitted', $event);
+        $this->assertEquals('quiz_attempts', $event->objecttable);
+        $this->assertEquals($quizobj->get_context(), $event->get_context());
+        $this->assertEquals($attempt->userid, $event->relateduserid);
+        $this->assertEquals(null, $event->other['submitterid']); // Should be the user, but PHP Unit complains...
+        $this->assertEquals('quiz_attempt_submitted', $event->get_legacy_eventname());
+        $legacydata = new stdClass();
+        $legacydata->component = 'mod_quiz';
+        $legacydata->attemptid = (string) $attempt->id;
+        $legacydata->timestamp = $timefinish;
+        $legacydata->userid = $attempt->userid;
+        $legacydata->cmid = $quizobj->get_cmid();
+        $legacydata->courseid = $quizobj->get_courseid();
+        $legacydata->quizid = $quizobj->get_quizid();
+        // Submitterid should be the user, but as we are in PHP Unit, CLI_SCRIPT is set to true which sets null in submitterid.
+        $legacydata->submitterid = null;
+        $legacydata->timefinish = $timefinish;
+        $this->assertEventLegacyData($legacydata, $event);
+    }
+
+    public function test_attempt_becameoverdue() {
+
+        list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
+        $attemptobj = quiz_attempt::create($attempt->id);
+
+        // Catch the event.
+        $sink = $this->redirectEvents();
+        $timefinish = time();
+        $attemptobj->process_going_overdue($timefinish, false);
+        $events = $sink->get_events();
+        $sink->close();
+
+        $this->assertCount(1, $events);
+        $event = $events[0];
+        $this->assertInstanceOf('\mod_quiz\event\attempt_becameoverdue', $event);
+        $this->assertEquals('quiz_attempts', $event->objecttable);
+        $this->assertEquals($quizobj->get_context(), $event->get_context());
+        $this->assertEquals($attempt->userid, $event->relateduserid);
+        // Submitterid should be the user, but as we are in PHP Unit, CLI_SCRIPT is set to true which sets null in submitterid.
+        $this->assertEquals(null, $event->other['submitterid']);
+        $this->assertEquals('quiz_attempt_overdue', $event->get_legacy_eventname());
+        $legacydata = new stdClass();
+        $legacydata->component = 'mod_quiz';
+        $legacydata->attemptid = (string) $attempt->id;
+        $legacydata->timestamp = $timefinish;
+        $legacydata->userid = $attempt->userid;
+        $legacydata->cmid = $quizobj->get_cmid();
+        $legacydata->courseid = $quizobj->get_courseid();
+        $legacydata->quizid = $quizobj->get_quizid();
+        $legacydata->submitterid = null; // Should be the user, but PHP Unit complains...
+        $this->assertEventLegacyData($legacydata, $event);
+    }
+
+    public function test_attempt_abandoned() {
+
+        list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
+        $attemptobj = quiz_attempt::create($attempt->id);
+
+        // Catch the event.
+        $sink = $this->redirectEvents();
+        $timefinish = time();
+        $attemptobj->process_abandon($timefinish, false);
+        $events = $sink->get_events();
+        $sink->close();
+
+        $this->assertCount(1, $events);
+        $event = $events[0];
+        $this->assertInstanceOf('\mod_quiz\event\attempt_abandoned', $event);
+        $this->assertEquals('quiz_attempts', $event->objecttable);
+        $this->assertEquals($quizobj->get_context(), $event->get_context());
+        $this->assertEquals($attempt->userid, $event->relateduserid);
+        // Submitterid should be the user, but as we are in PHP Unit, CLI_SCRIPT is set to true which sets null in submitterid.
+        $this->assertEquals(null, $event->other['submitterid']);
+        $this->assertEquals('quiz_attempt_abandoned', $event->get_legacy_eventname());
+        $legacydata = new stdClass();
+        $legacydata->component = 'mod_quiz';
+        $legacydata->attemptid = (string) $attempt->id;
+        $legacydata->timestamp = $timefinish;
+        $legacydata->userid = $attempt->userid;
+        $legacydata->cmid = $quizobj->get_cmid();
+        $legacydata->courseid = $quizobj->get_courseid();
+        $legacydata->quizid = $quizobj->get_quizid();
+        $legacydata->submitterid = null; // Should be the user, but PHP Unit complains...
+        $this->assertEventLegacyData($legacydata, $event);
+    }
+
+    public function test_attempt_started() {
+        global $USER;
+
+        list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
+        $attemptobj = quiz_attempt::create($attempt->id);
+
+        // Catch the event.
+        $sink = $this->redirectEvents();
+        quiz_fire_attempt_started_event($attempt, $quizobj);
+        $events = $sink->get_events();
+        $sink->close();
+
+        // Legacy event data.
+        $legacydata = new stdClass();
+        $legacydata->component = 'mod_quiz';
+        $legacydata->attemptid = $attempt->id;
+        $legacydata->timestart = $attempt->timestart;
+        $legacydata->timestamp = $attempt->timestart;
+        $legacydata->userid = $attempt->userid;
+        $legacydata->quizid = $quizobj->get_quizid();
+        $legacydata->cmid = $quizobj->get_cmid();
+        $legacydata->courseid = $quizobj->get_courseid();
+
+        // Validate the event.
+        $this->assertCount(1, $events);
+        $event = $events[0];
+        $this->assertInstanceOf('\mod_quiz\event\attempt_started', $event);
+        $this->assertEquals('quiz_attempts', $event->objecttable);
+        $this->assertEquals($attempt->id, $event->objectid);
+        $this->assertEquals($attempt->userid, $event->relateduserid);
+        $this->assertEquals($quizobj->get_context(), $event->get_context());
+        $this->assertEquals('quiz_attempt_started', $event->get_legacy_eventname());
+        $this->assertEventLegacyData($legacydata, $event);
+    }
+}