);
}
+ /**
+ * Describes the parameters for save_attempt.
+ *
+ * @return external_external_function_parameters
+ * @since Moodle 3.1
+ */
+ public static function save_attempt_parameters() {
+ return new external_function_parameters (
+ array(
+ 'attemptid' => new external_value(PARAM_INT, 'attempt id'),
+ 'data' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'name' => new external_value(PARAM_RAW, 'data name'),
+ 'value' => new external_value(PARAM_RAW, 'data value'),
+ )
+ ), 'the data to be saved'
+ ),
+ 'preflightdata' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'name' => new external_value(PARAM_ALPHANUMEXT, 'data name'),
+ 'value' => new external_value(PARAM_RAW, 'data value'),
+ )
+ ), 'Preflight required data (like passwords)', VALUE_DEFAULT, array()
+ )
+ )
+ );
+ }
+
+ /**
+ * Processes save requests during the quiz. This function is intended for the quiz auto-save feature.
+ *
+ * @param int $attemptid attempt id
+ * @param array $data the data to be saved
+ * @param array $preflightdata preflight required data (like passwords)
+ * @return array of warnings and execution result
+ * @since Moodle 3.1
+ */
+ public static function save_attempt($attemptid, $data, $preflightdata = array()) {
+ global $DB;
+
+ $warnings = array();
+
+ $params = array(
+ 'attemptid' => $attemptid,
+ 'data' => $data,
+ 'preflightdata' => $preflightdata,
+ );
+ $params = self::validate_parameters(self::save_attempt_parameters(), $params);
+
+ // Add a page, required by validate_attempt.
+ list($attemptobj, $messages) = self::validate_attempt($params);
+
+ $transaction = $DB->start_delegated_transaction();
+ // Create the $_POST object required by the question engine.
+ $_POST = array();
+ foreach ($data as $element) {
+ $_POST[$element['name']] = $element['value'];
+ }
+ $timenow = time();
+ $attemptobj->process_auto_save($timenow);
+ $transaction->allow_commit();
+
+ $result = array();
+ $result['status'] = true;
+ $result['warnings'] = $warnings;
+ return $result;
+ }
+
+ /**
+ * Describes the save_attempt return value.
+ *
+ * @return external_single_structure
+ * @since Moodle 3.1
+ */
+ public static function save_attempt_returns() {
+ return new external_single_structure(
+ array(
+ 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+ 'warnings' => new external_warnings(),
+ )
+ );
+ }
+
}
'capabilities' => 'mod/quiz:attempt',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
+
+ 'mod_quiz_save_attempt' => array(
+ 'classname' => 'mod_quiz_external',
+ 'methodname' => 'save_attempt',
+ 'description' => 'Processes save requests during the quiz.
+ This function is intended for the quiz auto-save feature.',
+ 'type' => 'write',
+ 'capabilities' => 'mod/quiz:attempt',
+ 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
+ ),
);
// Finish the attempt.
$attemptobj->process_finish(time(), false);
}
- return array($quiz, $context, $quizobj, $attempt, $attemptobj);
+ return array($quiz, $context, $quizobj, $attempt, $attemptobj, $quba);
} else {
return array($quiz, $context, $quizobj);
}
}
+ /**
+ * Test save_attempt
+ */
+ public function test_save_attempt() {
+
+ // Create a new quiz with one attempt started.
+ list($quiz, $context, $quizobj, $attempt, $attemptobj, $quba) = $this->create_quiz_with_questions(true);
+
+ // Response for slot 1.
+ $prefix = $quba->get_field_prefix(1);
+ $data = array(
+ array('name' => 'slots', 'value' => 1),
+ array('name' => $prefix . ':sequencecheck',
+ 'value' => $attemptobj->get_question_attempt(1)->get_sequence_check_count()),
+ array('name' => $prefix . 'answer', 'value' => 1),
+ );
+
+ $this->setUser($this->student);
+
+ $result = mod_quiz_external::save_attempt($attempt->id, $data);
+ $result = external_api::clean_returnvalue(mod_quiz_external::save_attempt_returns(), $result);
+ $this->assertTrue($result['status']);
+
+ // Now, get the summary.
+ $result = mod_quiz_external::get_attempt_summary($attempt->id);
+ $result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_summary_returns(), $result);
+
+ // Check it's marked as completed only the first one.
+ $this->assertEquals('complete', $result['questions'][0]['state']);
+ $this->assertEquals('todo', $result['questions'][1]['state']);
+ $this->assertEquals(1, $result['questions'][0]['number']);
+ $this->assertEquals(2, $result['questions'][1]['number']);
+ $this->assertFalse($result['questions'][0]['flagged']);
+ $this->assertFalse($result['questions'][1]['flagged']);
+ $this->assertEmpty($result['questions'][0]['mark']);
+ $this->assertEmpty($result['questions'][1]['mark']);
+
+ // Now, second slot.
+ $prefix = $quba->get_field_prefix(2);
+ $data = array(
+ array('name' => 'slots', 'value' => 2),
+ array('name' => $prefix . ':sequencecheck',
+ 'value' => $attemptobj->get_question_attempt(1)->get_sequence_check_count()),
+ array('name' => $prefix . 'answer', 'value' => 1),
+ );
+
+ $result = mod_quiz_external::save_attempt($attempt->id, $data);
+ $result = external_api::clean_returnvalue(mod_quiz_external::save_attempt_returns(), $result);
+ $this->assertTrue($result['status']);
+
+ // Now, get the summary.
+ $result = mod_quiz_external::get_attempt_summary($attempt->id);
+ $result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_summary_returns(), $result);
+
+ // Check it's marked as completed only the first one.
+ $this->assertEquals('complete', $result['questions'][0]['state']);
+ $this->assertEquals('complete', $result['questions'][1]['state']);
+
+ }
+
}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2016032101;
+$plugin->version = 2016032102;
$plugin->requires = 2015111000;
$plugin->component = 'mod_quiz';
$plugin->cron = 60;