diff --git a/lib/db/services.php b/lib/db/services.php index ed2c26a9864..fccdf1cf4b1 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1300,6 +1300,7 @@ $services = array( 'mod_imscp_view_imscp', 'mod_imscp_get_imscps_by_courses', 'mod_quiz_get_quizzes_by_courses', + 'mod_quiz_view_quiz', 'mod_glossary_get_glossaries_by_courses', 'mod_wiki_get_wikis_by_courses', 'mod_wiki_view_wiki', diff --git a/mod/quiz/classes/external.php b/mod/quiz/classes/external.php index c526e42d8bf..419ca2c2dc2 100644 --- a/mod/quiz/classes/external.php +++ b/mod/quiz/classes/external.php @@ -272,4 +272,63 @@ class mod_quiz_external extends external_api { ); } + /** + * Describes the parameters for view_quiz. + * + * @return external_external_function_parameters + * @since Moodle 3.1 + */ + public static function view_quiz_parameters() { + return new external_function_parameters ( + array( + 'quizid' => new external_value(PARAM_INT, 'quiz instance id'), + ) + ); + } + + /** + * Trigger the course module viewed event and update the module completion status. + * + * @param int $quizid quiz instance id + * @return array of warnings and status result + * @since Moodle 3.1 + * @throws moodle_exception + */ + public static function view_quiz($quizid) { + global $DB; + + $params = self::validate_parameters(self::view_quiz_parameters(), array('quizid' => $quizid)); + $warnings = array(); + + // Request and permission validation. + $quiz = $DB->get_record('quiz', array('id' => $params['quizid']), '*', MUST_EXIST); + list($course, $cm) = get_course_and_cm_from_instance($quiz, 'quiz'); + + $context = context_module::instance($cm->id); + self::validate_context($context); + + // Trigger course_module_viewed event and completion. + quiz_view($quiz, $course, $cm, $context); + + $result = array(); + $result['status'] = true; + $result['warnings'] = $warnings; + return $result; + } + + /** + * Describes the view_quiz return value. + * + * @return external_single_structure + * @since Moodle 3.1 + */ + public static function view_quiz_returns() { + return new external_single_structure( + array( + 'status' => new external_value(PARAM_BOOL, 'status: true if success'), + 'warnings' => new external_warnings(), + ) + ); + } + } diff --git a/mod/quiz/db/services.php b/mod/quiz/db/services.php index 0dab1f9bac4..cf5adbd3aa5 100644 --- a/mod/quiz/db/services.php +++ b/mod/quiz/db/services.php @@ -36,4 +36,12 @@ $functions = array( 'type' => 'read', 'capabilities' => 'mod/quiz:view' ), + + 'mod_quiz_view_quiz' => array( + 'classname' => 'mod_quiz_external', + 'methodname' => 'view_quiz', + 'description' => 'Trigger the course module viewed event and update the module completion status.', + 'type' => 'write', + 'capabilities' => 'mod/quiz:view' + ), ); diff --git a/mod/quiz/tests/external_test.php b/mod/quiz/tests/external_test.php index 5de4726c3ab..5cd2645b4d0 100644 --- a/mod/quiz/tests/external_test.php +++ b/mod/quiz/tests/external_test.php @@ -206,4 +206,66 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase { } + /** + * Test test_view_quiz + */ + public function test_view_quiz() { + global $DB; + + // Test invalid instance id. + try { + mod_quiz_external::view_quiz(0); + $this->fail('Exception expected due to invalid mod_quiz instance id.'); + } catch (moodle_exception $e) { + $this->assertEquals('invalidrecord', $e->errorcode); + } + + // Test not-enrolled user. + $usernotenrolled = self::getDataGenerator()->create_user(); + $this->setUser($usernotenrolled); + try { + mod_quiz_external::view_quiz($this->quiz->id); + $this->fail('Exception expected due to not enrolled user.'); + } catch (moodle_exception $e) { + $this->assertEquals('requireloginerror', $e->errorcode); + } + + // Test user with full capabilities. + $this->setUser($this->student); + + // Trigger and capture the event. + $sink = $this->redirectEvents(); + + $result = mod_quiz_external::view_quiz($this->quiz->id); + $result = external_api::clean_returnvalue(mod_quiz_external::view_quiz_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\course_module_viewed', $event); + $this->assertEquals($this->context, $event->get_context()); + $moodlequiz = new \moodle_url('/mod/quiz/view.php', array('id' => $this->cm->id)); + $this->assertEquals($moodlequiz, $event->get_url()); + $this->assertEventContextNotUsed($event); + $this->assertNotEmpty($event->get_name()); + + // Test user with no capabilities. + // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles. + assign_capability('mod/quiz:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id); + // Empty all the caches that may be affected by this change. + accesslib_clear_all_caches_for_unit_testing(); + course_modinfo::clear_instance_cache(); + + try { + mod_quiz_external::view_quiz($this->quiz->id); + $this->fail('Exception expected due to missing capability.'); + } catch (moodle_exception $e) { + $this->assertEquals('requireloginerror', $e->errorcode); + } + + } + } diff --git a/mod/quiz/version.php b/mod/quiz/version.php index 6e4f8e8e6cd..70a0e4b90c2 100644 --- a/mod/quiz/version.php +++ b/mod/quiz/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2015111601; +$plugin->version = 2015111602; $plugin->requires = 2015111000; $plugin->component = 'mod_quiz'; $plugin->cron = 60;