Merge branch 'MDL-52888-master' of git://github.com/jleyva/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 21 Mar 2016 21:40:43 +0000 (22:40 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 21 Mar 2016 21:40:43 +0000 (22:40 +0100)
mod/quiz/attempt.php
mod/quiz/attemptlib.php
mod/quiz/classes/external.php
mod/quiz/db/services.php
mod/quiz/review.php
mod/quiz/summary.php
mod/quiz/tests/external_test.php
mod/quiz/version.php

index 181c9bc..f9208e7 100644 (file)
@@ -95,18 +95,7 @@ if ($autosaveperiod) {
 }
 
 // Log this page view.
-$params = array(
-    'objectid' => $attemptid,
-    'relateduserid' => $attemptobj->get_userid(),
-    'courseid' => $attemptobj->get_courseid(),
-    'context' => context_module::instance($attemptobj->get_cmid()),
-    'other' => array(
-        'quizid' => $attemptobj->get_quizid()
-    )
-);
-$event = \mod_quiz\event\attempt_viewed::create($params);
-$event->add_record_snapshot('quiz_attempts', $attemptobj->get_attempt());
-$event->trigger();
+$attemptobj->fire_attempt_viewed_event();
 
 // Get the list of questions needed by this page.
 $slots = $attemptobj->get_slots($page);
@@ -116,13 +105,9 @@ if (empty($slots)) {
     throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'noquestionsfound');
 }
 
-// Update attempt page.
-if ($attemptobj->get_currentpage() != $page) {
-    if ($attemptobj->get_navigation_method() == QUIZ_NAVMETHOD_SEQ && $attemptobj->get_currentpage() > $page) {
-        // Prevent out of sequence access.
-        redirect($attemptobj->start_attempt_url(null, $attemptobj->get_currentpage()));
-    }
-    $DB->set_field('quiz_attempts', 'currentpage', $page, array('id' => $attemptid));
+// Update attempt page, redirecting the user if $page is not valid.
+if (!$attemptobj->set_currentpage($page)) {
+    redirect($attemptobj->start_attempt_url(null, $attemptobj->get_currentpage()));
 }
 
 // Initialise the JavaScript.
index 39f093e..c1fbf70 100644 (file)
@@ -2170,6 +2170,103 @@ class quiz_attempt {
         return $becomingabandoned ? self::ABANDONED : self::FINISHED;
     }
 
+    /**
+     * Check a page access to see if is an out of sequence access.
+     *
+     * @param  int $page page number
+     * @return boolean false is is an out of sequence access, true otherwise.
+     * @since Moodle 3.1
+     */
+    public function check_page_access($page) {
+        global $DB;
+
+        if ($this->get_currentpage() != $page) {
+            if ($this->get_navigation_method() == QUIZ_NAVMETHOD_SEQ && $this->get_currentpage() > $page) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Update attempt page.
+     *
+     * @param  int $page page number
+     * @return boolean true if everything was ok, false otherwise (out of sequence access).
+     * @since Moodle 3.1
+     */
+    public function set_currentpage($page) {
+        global $DB;
+
+        if ($this->check_page_access($page)) {
+            $DB->set_field('quiz_attempts', 'currentpage', $page, array('id' => $this->get_attemptid()));
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Trigger the attempt_viewed event.
+     *
+     * @since Moodle 3.1
+     */
+    public function fire_attempt_viewed_event() {
+        $params = array(
+            'objectid' => $this->get_attemptid(),
+            'relateduserid' => $this->get_userid(),
+            'courseid' => $this->get_courseid(),
+            'context' => context_module::instance($this->get_cmid()),
+            'other' => array(
+                'quizid' => $this->get_quizid()
+            )
+        );
+        $event = \mod_quiz\event\attempt_viewed::create($params);
+        $event->add_record_snapshot('quiz_attempts', $this->get_attempt());
+        $event->trigger();
+    }
+
+    /**
+     * Trigger the attempt_summary_viewed event.
+     *
+     * @since Moodle 3.1
+     */
+    public function fire_attempt_summary_viewed_event() {
+
+        $params = array(
+            'objectid' => $this->get_attemptid(),
+            'relateduserid' => $this->get_userid(),
+            'courseid' => $this->get_courseid(),
+            'context' => context_module::instance($this->get_cmid()),
+            'other' => array(
+                'quizid' => $this->get_quizid()
+            )
+        );
+        $event = \mod_quiz\event\attempt_summary_viewed::create($params);
+        $event->add_record_snapshot('quiz_attempts', $this->get_attempt());
+        $event->trigger();
+    }
+
+    /**
+     * Trigger the attempt_reviewed event.
+     *
+     * @since Moodle 3.1
+     */
+    public function fire_attempt_reviewed_event() {
+
+        $params = array(
+            'objectid' => $this->get_attemptid(),
+            'relateduserid' => $this->get_userid(),
+            'courseid' => $this->get_courseid(),
+            'context' => context_module::instance($this->get_cmid()),
+            'other' => array(
+                'quizid' => $this->get_quizid()
+            )
+        );
+        $event = \mod_quiz\event\attempt_reviewed::create($params);
+        $event->add_record_snapshot('quiz_attempts', $this->get_attempt());
+        $event->trigger();
+    }
+
 }
 
 
index 21a23e7..dd487ba 100644 (file)
@@ -845,12 +845,8 @@ class mod_quiz_external extends external_api {
             }
 
             // Prevent out of sequence access.
-            if ($attemptobj->get_currentpage() != $params['page']) {
-                if ($attemptobj->get_navigation_method() == QUIZ_NAVMETHOD_SEQ &&
-                        $attemptobj->get_currentpage() > $params['page']) {
-
-                    throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'Out of sequence access');
-                }
+            if (!$attemptobj->check_page_access($params['page'])) {
+                throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'Out of sequence access');
             }
 
             // Check slots.
@@ -1386,4 +1382,177 @@ class mod_quiz_external extends external_api {
         );
     }
 
+    /**
+     * Describes the parameters for view_attempt.
+     *
+     * @return external_external_function_parameters
+     * @since Moodle 3.1
+     */
+    public static function view_attempt_parameters() {
+        return new external_function_parameters (
+            array(
+                'attemptid' => new external_value(PARAM_INT, 'attempt id'),
+                'page' => new external_value(PARAM_INT, 'page number'),
+            )
+        );
+    }
+
+    /**
+     * Trigger the attempt viewed event.
+     *
+     * @param int $attemptid attempt id
+     * @param int $page page number
+     * @return array of warnings and status result
+     * @since Moodle 3.1
+     */
+    public static function view_attempt($attemptid, $page) {
+
+        $warnings = array();
+
+        $params = array(
+            'attemptid' => $attemptid,
+            'page' => $page,
+        );
+        $params = self::validate_parameters(self::view_attempt_parameters(), $params);
+        list($attemptobj, $messages) = self::validate_attempt($params);
+
+        // Log action.
+        $attemptobj->fire_attempt_viewed_event();
+
+        // Update attempt page, throwing an exception if $page is not valid.
+        if (!$attemptobj->set_currentpage($params['page'])) {
+            throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'Out of sequence access');
+        }
+
+        $result = array();
+        $result['status'] = true;
+        $result['warnings'] = $warnings;
+        return $result;
+    }
+
+    /**
+     * Describes the view_attempt return value.
+     *
+     * @return external_single_structure
+     * @since Moodle 3.1
+     */
+    public static function view_attempt_returns() {
+        return new external_single_structure(
+            array(
+                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+                'warnings' => new external_warnings(),
+            )
+        );
+    }
+
+    /**
+     * Describes the parameters for view_attempt_summary.
+     *
+     * @return external_external_function_parameters
+     * @since Moodle 3.1
+     */
+    public static function view_attempt_summary_parameters() {
+        return new external_function_parameters (
+            array(
+                'attemptid' => new external_value(PARAM_INT, 'attempt id'),
+            )
+        );
+    }
+
+    /**
+     * Trigger the attempt summary viewed event.
+     *
+     * @param int $attemptid attempt id
+     * @return array of warnings and status result
+     * @since Moodle 3.1
+     */
+    public static function view_attempt_summary($attemptid) {
+
+        $warnings = array();
+
+        $params = array(
+            'attemptid' => $attemptid,
+        );
+        $params = self::validate_parameters(self::view_attempt_summary_parameters(), $params);
+        list($attemptobj, $messages) = self::validate_attempt($params);
+
+        // Log action.
+        $attemptobj->fire_attempt_summary_viewed_event();
+
+        $result = array();
+        $result['status'] = true;
+        $result['warnings'] = $warnings;
+        return $result;
+    }
+
+    /**
+     * Describes the view_attempt_summary return value.
+     *
+     * @return external_single_structure
+     * @since Moodle 3.1
+     */
+    public static function view_attempt_summary_returns() {
+        return new external_single_structure(
+            array(
+                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+                'warnings' => new external_warnings(),
+            )
+        );
+    }
+
+    /**
+     * Describes the parameters for view_attempt_review.
+     *
+     * @return external_external_function_parameters
+     * @since Moodle 3.1
+     */
+    public static function view_attempt_review_parameters() {
+        return new external_function_parameters (
+            array(
+                'attemptid' => new external_value(PARAM_INT, 'attempt id'),
+            )
+        );
+    }
+
+    /**
+     * Trigger the attempt reviewed event.
+     *
+     * @param int $attemptid attempt id
+     * @return array of warnings and status result
+     * @since Moodle 3.1
+     */
+    public static function view_attempt_review($attemptid) {
+
+        $warnings = array();
+
+        $params = array(
+            'attemptid' => $attemptid,
+        );
+        $params = self::validate_parameters(self::view_attempt_review_parameters(), $params);
+        list($attemptobj, $displayoptions) = self::validate_attempt_review($params);
+
+        // Log action.
+        $attemptobj->fire_attempt_reviewed_event();
+
+        $result = array();
+        $result['status'] = true;
+        $result['warnings'] = $warnings;
+        return $result;
+    }
+
+    /**
+     * Describes the view_attempt_review return value.
+     *
+     * @return external_single_structure
+     * @since Moodle 3.1
+     */
+    public static function view_attempt_review_returns() {
+        return new external_single_structure(
+            array(
+                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+                'warnings' => new external_warnings(),
+            )
+        );
+    }
+
 }
index 0abd1dc..af51f0e 100644 (file)
@@ -128,4 +128,31 @@ $functions = array(
         'capabilities'  => 'mod/quiz:reviewmyattempts',
         'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
     ),
+
+    'mod_quiz_view_attempt' => array(
+        'classname'     => 'mod_quiz_external',
+        'methodname'    => 'view_attempt',
+        'description'   => 'Trigger the attempt viewed event.',
+        'type'          => 'write',
+        'capabilities'  => 'mod/quiz:attempt',
+        'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
+    ),
+
+    'mod_quiz_view_attempt_summary' => array(
+        'classname'     => 'mod_quiz_external',
+        'methodname'    => 'view_attempt_summary',
+        'description'   => 'Trigger the attempt summary viewed event.',
+        'type'          => 'write',
+        'capabilities'  => 'mod/quiz:attempt',
+        'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
+    ),
+
+    'mod_quiz_view_attempt_review' => array(
+        'classname'     => 'mod_quiz_external',
+        'methodname'    => 'view_attempt_review',
+        'description'   => 'Trigger the attempt reviewed event.',
+        'type'          => 'write',
+        'capabilities'  => 'mod/quiz:reviewmyattempts',
+        'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
+    ),
 );
index be91bd6..6e78ef3 100644 (file)
@@ -259,15 +259,4 @@ $PAGE->blocks->add_fake_block($navbc, reset($regions));
 echo $output->review_page($attemptobj, $slots, $page, $showall, $lastpage, $options, $summarydata);
 
 // Trigger an event for this review.
-$params = array(
-    'objectid' => $attemptobj->get_attemptid(),
-    'relateduserid' => $attemptobj->get_userid(),
-    'courseid' => $attemptobj->get_courseid(),
-    'context' => context_module::instance($attemptobj->get_cmid()),
-    'other' => array(
-        'quizid' => $attemptobj->get_quizid()
-    )
-);
-$event = \mod_quiz\event\attempt_reviewed::create($params);
-$event->add_record_snapshot('quiz_attempts', $attemptobj->get_attempt());
-$event->trigger();
+$attemptobj->fire_attempt_reviewed_event();
index b1ac720..4ccec15 100644 (file)
@@ -93,15 +93,4 @@ $PAGE->set_heading($attemptobj->get_course()->fullname);
 echo $output->summary_page($attemptobj, $displayoptions);
 
 // Log this page view.
-$params = array(
-    'objectid' => $attemptobj->get_attemptid(),
-    'relateduserid' => $attemptobj->get_userid(),
-    'courseid' => $attemptobj->get_courseid(),
-    'context' => context_module::instance($attemptobj->get_cmid()),
-    'other' => array(
-        'quizid' => $attemptobj->get_quizid()
-    )
-);
-$event = \mod_quiz\event\attempt_summary_viewed::create($params);
-$event->add_record_snapshot('quiz_attempts', $attemptobj->get_attempt());
-$event->trigger();
+$attemptobj->fire_attempt_summary_viewed_event();
index df10871..3361d7b 100644 (file)
@@ -1247,4 +1247,119 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
 
     }
 
+    /**
+     * Test test_view_attempt
+     */
+    public function test_view_attempt() {
+        global $DB;
+
+        // Create a new quiz with two questions and one attempt started.
+        list($quiz, $context, $quizobj, $attempt, $attemptobj, $quba) = $this->create_quiz_with_questions(true, false);
+
+        // Test user with full capabilities.
+        $this->setUser($this->student);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        $result = mod_quiz_external::view_attempt($attempt->id, 0);
+        $result = external_api::clean_returnvalue(mod_quiz_external::view_attempt_returns(), $result);
+        $this->assertTrue($result['status']);
+
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_quiz\event\attempt_viewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+
+        // Now, force the quiz with QUIZ_NAVMETHOD_SEQ (sequencial) navigation method.
+        $DB->set_field('quiz', 'navmethod', QUIZ_NAVMETHOD_SEQ, array('id' => $quiz->id));
+        // See next page.
+        $result = mod_quiz_external::view_attempt($attempt->id, 1);
+        $result = external_api::clean_returnvalue(mod_quiz_external::view_attempt_returns(), $result);
+        $this->assertTrue($result['status']);
+
+        $events = $sink->get_events();
+        $this->assertCount(2, $events);
+
+        // Try to go to previous page.
+        try {
+            mod_quiz_external::view_attempt($attempt->id, 0);
+            $this->fail('Exception expected due to try to see a previous page.');
+        } catch (moodle_quiz_exception $e) {
+            $this->assertEquals('Out of sequence access', $e->errorcode);
+        }
+
+    }
+
+    /**
+     * Test test_view_attempt_summary
+     */
+    public function test_view_attempt_summary() {
+        global $DB;
+
+        // Create a new quiz with two questions and one attempt started.
+        list($quiz, $context, $quizobj, $attempt, $attemptobj, $quba) = $this->create_quiz_with_questions(true, false);
+
+        // Test user with full capabilities.
+        $this->setUser($this->student);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        $result = mod_quiz_external::view_attempt_summary($attempt->id, 0);
+        $result = external_api::clean_returnvalue(mod_quiz_external::view_attempt_summary_returns(), $result);
+        $this->assertTrue($result['status']);
+
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_quiz\event\attempt_summary_viewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $moodlequiz = new \moodle_url('/mod/quiz/summary.php', array('attempt' => $attempt->id));
+        $this->assertEquals($moodlequiz, $event->get_url());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+
+    }
+
+    /**
+     * Test test_view_attempt_summary
+     */
+    public function test_view_attempt_review() {
+        global $DB;
+
+        // Create a new quiz with two questions and one attempt finished.
+        list($quiz, $context, $quizobj, $attempt, $attemptobj, $quba) = $this->create_quiz_with_questions(true, true);
+
+        // Test user with full capabilities.
+        $this->setUser($this->student);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        $result = mod_quiz_external::view_attempt_review($attempt->id, 0);
+        $result = external_api::clean_returnvalue(mod_quiz_external::view_attempt_review_returns(), $result);
+        $this->assertTrue($result['status']);
+
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_quiz\event\attempt_reviewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $moodlequiz = new \moodle_url('/mod/quiz/review.php', array('attempt' => $attempt->id));
+        $this->assertEquals($moodlequiz, $event->get_url());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+
+    }
+
 }
index 96623a8..b37de0d 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2016032104;
+$plugin->version   = 2016032105;
 $plugin->requires  = 2015111000;
 $plugin->component = 'mod_quiz';
 $plugin->cron      = 60;