From fe1ae23939346151e45811554aaeeec3c5df71b9 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Thu, 5 Apr 2018 00:37:56 +1000 Subject: [PATCH] MDL-58768 calendar: Add $requestinguser to the container class The requesting user is not always the same as the current user. The container class now allows a different user to be set as the requesting user. All capability checks are done against the requesting user. Also, the $requestinguser is passed to core_calendar_provide_event_action and core_calendar_is_event_visible callback functions. These callback functions need to be updated in all activity modules to accept a $user parameter. --- calendar/classes/local/event/container.php | 69 +++++++++++++++++++--- calendar/lib.php | 5 ++ calendar/upgrade.txt | 2 + mod/upgrade.txt | 5 ++ 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/calendar/classes/local/event/container.php b/calendar/classes/local/event/container.php index 597eb70889f..0afc5940281 100644 --- a/calendar/classes/local/event/container.php +++ b/calendar/classes/local/event/container.php @@ -81,6 +81,11 @@ class container { */ protected static $modulecache = array(); + /** + * @var int The requesting user. All capability checks are done against this user. + */ + protected static $requestinguserid; + /** * Initialises the dependency graph if it hasn't yet been. */ @@ -117,11 +122,13 @@ class container { [self::class, 'apply_component_provide_event_action'], [self::class, 'apply_component_is_event_visible'], function ($dbrow) { + $requestinguserid = self::get_requesting_user(); + if (!empty($dbrow->categoryid)) { // This is a category event. Check that the category is visible to this user. - $category = \coursecat::get($dbrow->categoryid, IGNORE_MISSING, true); + $category = \coursecat::get($dbrow->categoryid, IGNORE_MISSING, true, $requestinguserid); - if (empty($category) || !$category->is_uservisible()) { + if (empty($category) || !$category->is_uservisible($requestinguserid)) { return true; } } @@ -131,7 +138,7 @@ class container { return false; } - $instances = get_fast_modinfo($dbrow->courseid)->instances; + $instances = get_fast_modinfo($dbrow->courseid, $requestinguserid)->instances; // If modinfo doesn't know about the module, we should ignore it. if (!isset($instances[$dbrow->modulename]) || !isset($instances[$dbrow->modulename][$dbrow->instance])) { @@ -156,11 +163,13 @@ class container { } $coursecontext = \context_course::instance($dbrow->courseid); - if (!$cm->get_course()->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) { + if (!$cm->get_course()->visible && + !has_capability('moodle/course:viewhiddencourses', $coursecontext, $requestinguserid)) { return true; } - if (!has_capability('moodle/course:view', $coursecontext) && !is_enrolled($coursecontext)) { + if (!has_capability('moodle/course:view', $coursecontext, $requestinguserid) && + !is_enrolled($coursecontext, $requestinguserid)) { return true; } @@ -191,6 +200,7 @@ class container { * Reset all static caches, called between tests. */ public static function reset_caches() { + self::$requestinguserid = null; self::$eventfactory = null; self::$eventmapper = null; self::$eventvault = null; @@ -230,6 +240,31 @@ class container { return self::$eventvault; } + /** + * Sets the requesting user so that all capability checks are done against this user. + * Setting the requesting user (hence calling this function) is optional and if you do not so, + * $USER will be used as the requesting user. However, if you wish to set the requesting user yourself, + * you should call this function before any other function of the container class is called. + * + * @param int $userid The user id. + * @throws \coding_exception + */ + public static function set_requesting_user($userid) { + self::$requestinguserid = $userid; + } + + /** + * Returns the requesting user id. + * It usually is the current user unless it has been set explicitly using set_requesting_user. + * + * @return int + */ + public static function get_requesting_user() { + global $USER; + + return empty(self::$requestinguserid) ? $USER->id : self::$requestinguserid; + } + /** * Calls callback 'core_calendar_provide_event_action' from the component responsible for the event * @@ -245,14 +280,23 @@ class container { $mapper = self::$eventmapper; $action = null; if ($event->get_course_module()) { + $requestinguserid = self::get_requesting_user(); + $legacyevent = $mapper->from_event_to_legacy_event($event); + // We know for a fact that the the requesting user might be different from the logged in user, + // but the event mapper is not aware of that. + if (empty($event->user) && !empty($legacyevent->userid)) { + $legacyevent->userid = $requestinguserid; + } + // 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 + $legacyevent, + self::$actionfactory, + $requestinguserid ] ); } @@ -279,12 +323,21 @@ class container { $mapper = self::$eventmapper; $eventvisible = null; if ($event->get_course_module()) { + $requestinguserid = self::get_requesting_user(); + $legacyevent = $mapper->from_event_to_legacy_event($event); + // We know for a fact that the the requesting user might be different from the logged in user, + // but the event mapper is not aware of that. + if (empty($event->user) && !empty($legacyevent->userid)) { + $legacyevent->userid = $requestinguserid; + } + // 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) + $legacyevent, + $requestinguserid ] ); } diff --git a/calendar/lib.php b/calendar/lib.php index 73f7aec821d..eac91efd211 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -3326,6 +3326,11 @@ function calendar_get_legacy_events($tstart, $tend, $users, $groups, $courses, return $param; }, [$users, $groups, $courses, $categories]); + // If a single user is provided, we can use that for capability checks. + // Otherwise current logged in user is used - See MDL-58768. + if (is_array($userparam) && count($userparam) == 1) { + \core_calendar\local\event\container::set_requesting_user($userparam[0]); + } $mapper = \core_calendar\local\event\container::get_event_mapper(); $events = \core_calendar\local\api::get_events( $tstart, diff --git a/calendar/upgrade.txt b/calendar/upgrade.txt index 0c6c3c63f7b..46065bacffc 100644 --- a/calendar/upgrade.txt +++ b/calendar/upgrade.txt @@ -4,6 +4,8 @@ information provided here is intended especially for developers. === 3.5.2 === * calendar_get_default_courses() function now has optional $userid parameter. * calendar_set_filters() function now has optional $user parameter. +* The core_calendar\local\event\container class now provides two new helper methods for getting and setting the requesting user: + set_requesting_user() and get_requesting_user(). === 3.5 === * core_calendar_external::get_calendar_events now returns the categoryid for category events. diff --git a/mod/upgrade.txt b/mod/upgrade.txt index bdd4d745cc3..555a0d6522c 100644 --- a/mod/upgrade.txt +++ b/mod/upgrade.txt @@ -1,6 +1,11 @@ This files describes API changes in /mod/* - activity modules, information provided here is intended especially for developers. +=== 3.5.2 === + +* Now, when mod__core_calendar_is_event_visible or mod__core_calendar_provide_event_action callback functions + are called, the userid of the requesting user is also passed to them. + === 3.5 === * There is a new privacy API that every subsystem and plugin has to implement so that the site can become GDPR