From 31496ddeba6fb3677abb4988e6756e6512c2c4f6 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Fri, 16 Jun 2017 12:25:34 +0100 Subject: [PATCH 1/2] MDL-59238 mod_workshop: Move event and completion to Workshop class The completion set_module_viewed and the course_module_viewed event code has been moved to the workshop class. This change is consistent with other modules (like lesson, feedback or quiz) and it is necessary to be able to use the new method via WS. --- mod/workshop/locallib.php | 31 ++++++++++++++++++++++++++++++- mod/workshop/view.php | 17 +++-------------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index af0dca3c231..dcdedc21fd1 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -57,6 +57,9 @@ class workshop { const EXAMPLES_BEFORE_SUBMISSION = 1; const EXAMPLES_BEFORE_ASSESSMENT = 2; + /** @var stdclass workshop record from database */ + public $dbrecord; + /** @var cm_info course module record */ public $cm; @@ -195,7 +198,8 @@ class workshop { * @param stdClass $context The context of the workshop instance */ public function __construct(stdclass $dbrecord, $cm, $course, stdclass $context=null) { - foreach ($dbrecord as $field => $value) { + $this->dbrecord = $dbrecord; + foreach ($this->dbrecord as $field => $value) { if (property_exists('workshop', $field)) { $this->{$field} = $value; } @@ -2671,6 +2675,31 @@ class workshop { } } + /** + * Trigger module viewed event and set the module viewed for completion. + * + * @since Moodle 3.4 + */ + public function set_module_viewed() { + global $CFG; + require_once($CFG->libdir . '/completionlib.php'); + + // Mark viewed. + $completion = new completion_info($this->course); + $completion->set_module_viewed($this->cm); + + $eventdata = array(); + $eventdata['objectid'] = $this->id; + $eventdata['context'] = $this->context; + + // Trigger module viewed event. + $event = \mod_workshop\event\course_module_viewed::create($eventdata); + $event->add_record_snapshot('course', $this->course); + $event->add_record_snapshot('workshop', $this->dbrecord); + $event->add_record_snapshot('course_modules', $this->cm); + $event->trigger(); + } + //////////////////////////////////////////////////////////////////////////////// // Internal methods (implementation details) // //////////////////////////////////////////////////////////////////////////////// diff --git a/mod/workshop/view.php b/mod/workshop/view.php index 09dc0aef791..19e07693041 100644 --- a/mod/workshop/view.php +++ b/mod/workshop/view.php @@ -28,7 +28,6 @@ require(__DIR__.'/../../config.php'); require_once(__DIR__.'/locallib.php'); -require_once($CFG->libdir.'/completionlib.php'); $id = optional_param('id', 0, PARAM_INT); // course_module ID, or $w = optional_param('w', 0, PARAM_INT); // workshop instance ID @@ -54,20 +53,10 @@ require_capability('mod/workshop:view', $PAGE->context); $workshop = new workshop($workshoprecord, $cm, $course); -// Mark viewed -$completion = new completion_info($course); -$completion->set_module_viewed($cm); - -$eventdata = array(); -$eventdata['objectid'] = $workshop->id; -$eventdata['context'] = $workshop->context; - $PAGE->set_url($workshop->view_url()); -$event = \mod_workshop\event\course_module_viewed::create($eventdata); -$event->add_record_snapshot('course', $course); -$event->add_record_snapshot('workshop', $workshoprecord); -$event->add_record_snapshot('course_modules', $cm); -$event->trigger(); + +// Mark viewed. +$workshop->set_module_viewed(); // If the phase is to be switched, do it asap. This just has to happen after triggering // the event so that the scheduled allocator had a chance to allocate submissions. From 291645f7605668e5257b6a37e1ebf63c9ba6d8af Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Fri, 16 Jun 2017 12:32:34 +0100 Subject: [PATCH 2/2] MDL-59238 mod_workshop: New WS mod_workshop_view_workshop --- mod/workshop/classes/external.php | 54 ++++++++++++++++++++++++ mod/workshop/db/services.php | 8 ++++ mod/workshop/tests/external_test.php | 62 ++++++++++++++++++++++++++++ mod/workshop/version.php | 2 +- 4 files changed, 125 insertions(+), 1 deletion(-) diff --git a/mod/workshop/classes/external.php b/mod/workshop/classes/external.php index 788fb37ff3f..2e5d5540ef2 100644 --- a/mod/workshop/classes/external.php +++ b/mod/workshop/classes/external.php @@ -365,4 +365,58 @@ class mod_workshop_external extends external_api { ) ); } + + /** + * Describes the parameters for view_workshop. + * + * @return external_function_parameters + * @since Moodle 3.4 + */ + public static function view_workshop_parameters() { + return new external_function_parameters ( + array( + 'workshopid' => new external_value(PARAM_INT, 'Workshop instance id'), + ) + ); + } + + /** + * Trigger the course module viewed event and update the module completion status. + * + * @param int $workshopid workshop instance id + * @return array of warnings and status result + * @since Moodle 3.4 + * @throws moodle_exception + */ + public static function view_workshop($workshopid) { + + $params = array('workshopid' => $workshopid); + $params = self::validate_parameters(self::view_workshop_parameters(), $params); + $warnings = array(); + + list($workshop, $course, $cm, $context) = self::validate_workshop($params['workshopid']); + + $workshop->set_module_viewed(); + + $result = array( + 'status' => true, + 'warnings' => $warnings, + ); + return $result; + } + + /** + * Describes the view_workshop return value. + * + * @return external_single_structure + * @since Moodle 3.4 + */ + public static function view_workshop_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/workshop/db/services.php b/mod/workshop/db/services.php index f86b53fbe51..8e362d8a77b 100644 --- a/mod/workshop/db/services.php +++ b/mod/workshop/db/services.php @@ -53,4 +53,12 @@ $functions = array( 'capabilities' => 'mod/workshop:view', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), + 'mod_workshop_view_workshop' => array( + 'classname' => 'mod_workshop_external', + 'methodname' => 'view_workshop', + 'description' => 'Trigger the course module viewed event and update the module completion status.', + 'type' => 'write', + 'capabilities' => 'mod/workshop:view', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + ), ); diff --git a/mod/workshop/tests/external_test.php b/mod/workshop/tests/external_test.php index 8d7f4071b9a..c6974b8be77 100644 --- a/mod/workshop/tests/external_test.php +++ b/mod/workshop/tests/external_test.php @@ -350,4 +350,66 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase { $this->assertEquals(workshop::PHASE_SUBMISSION, $result['userplan']['phases'][1]['code']); $this->assertTrue($result['userplan']['phases'][1]['active']); // We are now in submission phase. } + + /** + * Test test_view_workshop invalid id. + */ + public function test_view_workshop_invalid_id() { + $this->expectException('moodle_exception'); + mod_workshop_external::view_workshop(0); + } + + /** + * Test test_view_workshop user not enrolled. + */ + public function test_view_workshop_user_not_enrolled() { + // Test not-enrolled user. + $usernotenrolled = self::getDataGenerator()->create_user(); + $this->setUser($usernotenrolled); + $this->expectException('moodle_exception'); + mod_workshop_external::view_workshop($this->workshop->id); + } + + /** + * Test test_view_workshop user student. + */ + public function test_view_workshop_user_student() { + // Test user with full capabilities. + $this->setUser($this->student); + + // Trigger and capture the event. + $sink = $this->redirectEvents(); + + $result = mod_workshop_external::view_workshop($this->workshop->id); + $result = external_api::clean_returnvalue(mod_workshop_external::view_workshop_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_workshop\event\course_module_viewed', $event); + $this->assertEquals($this->context, $event->get_context()); + $moodleworkshop = new \moodle_url('/mod/workshop/view.php', array('id' => $this->cm->id)); + $this->assertEquals($moodleworkshop, $event->get_url()); + $this->assertEventContextNotUsed($event); + $this->assertNotEmpty($event->get_name()); + } + + /** + * Test test_view_workshop user missing capabilities. + */ + public function test_view_workshop_user_missing_capabilities() { + // 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/workshop: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(); + + $this->setUser($this->student); + $this->expectException('moodle_exception'); + mod_workshop_external::view_workshop($this->workshop->id); + } } diff --git a/mod/workshop/version.php b/mod/workshop/version.php index 68fff480ef7..fadef548fc9 100644 --- a/mod/workshop/version.php +++ b/mod/workshop/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2017051503; // The current module version (YYYYMMDDXX) +$plugin->version = 2017051504; // The current module version (YYYYMMDDXX) $plugin->requires = 2017050500; // Requires this Moodle version. $plugin->component = 'mod_workshop'; $plugin->cron = 60; // Give as a chance every minute.