diff --git a/calendar/classes/local/event/container.php b/calendar/classes/local/event/container.php index 20b39ec5728..9ac8cbf3432 100644 --- a/calendar/classes/local/event/container.php +++ b/calendar/classes/local/event/container.php @@ -71,11 +71,6 @@ class container { */ protected static $eventretrievalstrategy; - /** - * @var array A list of callbacks to use. - */ - protected static $callbacks = array(); - /** * @var \stdClass[] An array of cached courses to use with the event factory. */ @@ -91,16 +86,6 @@ class container { */ private static function init() { if (empty(self::$eventfactory)) { - // When testing the container's components, we need to make sure - // the callback implementations in modules are not executed, since - // we cannot control their output from PHPUnit. To do this we have - // a set of 'testing' callbacks that the factory can use. This way - // we know exactly how the factory behaves when being tested. - $getcallback = function($which) { - return self::$callbacks[PHPUNIT_TEST ? 'testing' : 'production'][$which]; - }; - - self::initcallbacks(); self::$actionfactory = new action_factory(); self::$eventmapper = new event_mapper( // The event mapper we return from here needs to know how to @@ -129,8 +114,8 @@ class container { ); self::$eventfactory = new event_factory( - $getcallback('action'), - $getcallback('visibility'), + [self::class, 'apply_component_provide_event_action'], + [self::class, 'apply_component_is_event_visible'], function ($dbrow) { // At present we only have a bail-out check for events in course modules. if (empty($dbrow->modulename)) { @@ -183,6 +168,19 @@ class container { } } + /** + * Reset all static caches, called between tests. + */ + public static function reset_caches() { + self::$eventfactory = null; + self::$eventmapper = null; + self::$eventvault = null; + self::$actionfactory = null; + self::$eventretrievalstrategy = null; + self::$coursecache = []; + self::$modulecache = []; + } + /** * Gets the event factory. * @@ -214,88 +212,74 @@ class container { } /** - * Initialises the callbacks. + * Calls callback 'core_calendar_provide_event_action' from the component responsible for the event * - * There are two sets here, one is used during PHPUnit runs. - * See the comment at the start of the init method for more - * detail. + * If no callback is present or callback returns null, there is no action on the event + * and it will not be displayed on the dashboard. + * + * @param event_interface $event + * @return action_event|event_interface */ - private static function initcallbacks() { - self::$callbacks = array( - 'testing' => array( - // Always return an action event. - 'action' => function (event_interface $event) { - return new action_event( - $event, - new \core_calendar\local\event\value_objects\action( - 'test', - new \moodle_url('http://example.com'), - 420, - true - )); - }, - // Always be visible. - 'visibility' => function (event_interface $event) { - return true; - } - ), - 'production' => array( - // This function has type event_interface -> event_interface. - // This is enforced by the event_factory. - 'action' => function (event_interface $event) { - // Callbacks will get supplied a "legacy" version - // of the event class. - $mapper = self::$eventmapper; - $action = null; - if ($event->get_course_module()) { - // TODO MDL-58866 Only activity modules currently support this callback. - // Any other event will not be displayed on the dashboard. - $action = component_callback( - 'mod_' . $event->get_course_module()->get('modname'), - 'core_calendar_provide_event_action', - [ - $mapper->from_event_to_legacy_event($event), - self::$actionfactory - ] - ); - } + public static function apply_component_provide_event_action(event_interface $event) { + // Callbacks will get supplied a "legacy" version + // of the event class. + $mapper = self::$eventmapper; + $action = null; + if ($event->get_course_module()) { + // TODO MDL-58866 Only activity modules currently support this callback. + // Any other event will not be displayed on the dashboard. + $action = component_callback( + 'mod_' . $event->get_course_module()->get('modname'), + 'core_calendar_provide_event_action', + [ + $mapper->from_event_to_legacy_event($event), + self::$actionfactory + ] + ); + } - // If we get an action back, return an action event, otherwise - // continue piping through the original event. - // - // If a module does not implement the callback, component_callback - // returns null. - return $action ? new action_event($event, $action) : $event; - }, - // This function has type event_interface -> bool. - // This is enforced by the event_factory. - 'visibility' => function (event_interface $event) { - $mapper = self::$eventmapper; - $eventvisible = null; - if ($event->get_course_module()) { - // TODO MDL-58866 Only activity modules currently support this callback. - $eventvisible = component_callback( - 'mod_' . $event->get_course_module()->get('modname'), - 'core_calendar_is_event_visible', - [ - $mapper->from_event_to_legacy_event($event) - ] - ); - } + // If we get an action back, return an action event, otherwise + // continue piping through the original event. + // + // If a module does not implement the callback, component_callback + // returns null. + return $action ? new action_event($event, $action) : $event; + } - // Do not display the event if there is nothing to action. - if ($event instanceof action_event_interface && $event->get_action()->get_item_count() === 0) { - return false; - } + /** + * Calls callback 'core_calendar_is_event_visible' from the component responsible for the event + * + * The visibility callback is optional, if not present it is assumed as visible. + * If it is an actionable event but the get_item_count() returns 0 the visibility + * is set to false. + * + * @param event_interface $event + * @return bool + */ + public static function apply_component_is_event_visible(event_interface $event) { + $mapper = self::$eventmapper; + $eventvisible = null; + if ($event->get_course_module()) { + // TODO MDL-58866 Only activity modules currently support this callback. + $eventvisible = component_callback( + 'mod_' . $event->get_course_module()->get('modname'), + 'core_calendar_is_event_visible', + [ + $mapper->from_event_to_legacy_event($event) + ] + ); + } - // Module does not implement the callback, event should be visible. - if (is_null($eventvisible)) { - return true; - } + // Do not display the event if there is nothing to action. + if ($event instanceof action_event_interface && $event->get_action()->get_item_count() === 0) { + return false; + } - return $eventvisible ? true : false; - } - ), - ); + // Module does not implement the callback, event should be visible. + if (is_null($eventvisible)) { + return true; + } + + return $eventvisible ? true : false; } } diff --git a/calendar/tests/externallib_test.php b/calendar/tests/externallib_test.php index 9912bcc1381..fc0ecc2effc 100644 --- a/calendar/tests/externallib_test.php +++ b/calendar/tests/externallib_test.php @@ -674,13 +674,15 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase { */ public function test_get_calendar_events_override() { $user = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); $teacher = $this->getDataGenerator()->create_user(); $anotheruser = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign'); $moduleinstance = $generator->create_instance(['course' => $course->id]); - $this->getDataGenerator()->enrol_user($user->id, $course->id); + $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student'); + $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher'); $this->resetAfterTest(true); $this->setAdminUser(); @@ -692,11 +694,12 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase { ]; $now = time(); + // Create two events - one for everybody in the course and one only for the first student. $event1 = $this->create_calendar_event('Base event', 0, 'due', 0, $now + DAYSECS, $params + ['courseid' => $course->id]); $event2 = $this->create_calendar_event('User event', $user->id, 'due', 0, $now + 2*DAYSECS, $params + ['courseid' => 0]); - // Retrieve course events for teacher - only one "Base event" is returned. - $this->setUser($teacher); + // Retrieve course events for the second student - only one "Base event" is returned. + $this->setUser($user2); $paramevents = array('courseids' => array($course->id)); $options = array ('siteevents' => true, 'userevents' => true); $events = core_calendar_external::get_calendar_events($paramevents, $options); @@ -705,7 +708,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase { $this->assertEquals(0, count($events['warnings'])); $this->assertEquals('Base event', $events['events'][0]['name']); - // Retrieve events for user - both events are returned. + // Retrieve events for the first student - both events are returned. $this->setUser($user); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); diff --git a/lib/phpunit/classes/util.php b/lib/phpunit/classes/util.php index a3997c400af..57c67c06d03 100644 --- a/lib/phpunit/classes/util.php +++ b/lib/phpunit/classes/util.php @@ -229,6 +229,11 @@ class phpunit_util extends testing_util { // Reset internal users. core_user::reset_internal_users(); + // Clear static caches in calendar container. + if (class_exists('\core_calendar\local\event\container', false)) { + core_calendar\local\event\container::reset_caches(); + } + //TODO MDL-25290: add more resets here and probably refactor them to new core function // Reset course and module caches.