diff --git a/calendar/externallib.php b/calendar/externallib.php index 0ccbf31ae3b..213930f7ffc 100644 --- a/calendar/externallib.php +++ b/calendar/externallib.php @@ -138,7 +138,12 @@ class core_calendar_external extends external_api { new external_value(PARAM_INT, 'group ids') , 'List of group ids for which events should be returned', VALUE_DEFAULT, array(), NULL_ALLOWED - ) + ), + 'categoryids' => new external_multiple_structure( + new external_value(PARAM_INT, 'Category ids'), + 'List of category ids for which events will be returned', + VALUE_DEFAULT, array() + ), ), 'Event details', VALUE_DEFAULT, array()), 'options' => new external_single_structure( array( @@ -177,7 +182,7 @@ class core_calendar_external extends external_api { // Parameter validation. $params = self::validate_parameters(self::get_calendar_events_parameters(), array('events' => $events, 'options' => $options)); - $funcparam = array('courses' => array(), 'groups' => array()); + $funcparam = array('courses' => array(), 'groups' => array(), 'categories' => array()); $hassystemcap = has_capability('moodle/calendar:manageentries', context_system::instance()); $warnings = array(); @@ -203,8 +208,6 @@ class core_calendar_external extends external_api { $courses = $params['events']['courseids']; $funcparam['courses'] = $courses; } - // Now get categories we can get events from. - $categories = \coursecat::get_all(); // Let us findout groups that we can return events from. if (!$hassystemcap) { @@ -222,6 +225,62 @@ class core_calendar_external extends external_api { $funcparam['groups'] = $groups; } + $categories = array(); + if (empty($params['events']['categoryids']) && !empty($courses)) { + list($wheresql, $sqlparams) = $DB->get_in_or_equal($courses); + $wheresql = "id $wheresql"; + $courseswithcategory = $DB->get_records_select('course', $wheresql, $sqlparams); + + // Grab the list of course categories for the requested course list. + $coursecategories = array(); + foreach ($courseswithcategory as $course) { + if (empty($course->visible)) { + if (!has_capability('moodle/course:viewhidden', context_course::instance($course->id))) { + continue; + } + } + $category = \coursecat::get($course->category); + $coursecategories[] = $category; + } + + foreach (\coursecat::get_all() as $category) { + if (has_capability('moodle/category:manage', $category->get_context(), $USER, false)) { + // If a user can manage a category, then they can see all child categories. as well as all parent categories. + $categories[] = $category->id; + + foreach (\coursecat::get_all() as $cat) { + if (array_search($category->id, $cat->get_parents()) !== false) { + $categories[] = $cat->id; + } + } + $categories = array_merge($categories, $category->get_parents()); + } else if (isset($coursecategories[$category->id])) { + // The user has access to a course in this category. + // Fetch all of the parents too. + $categories = array_merge($categories, [$category->id], $category->get_parents()); + $categories[] = $category->id; + } + } + } else { + // Build the category list. + // This includes the current category. + foreach ($params['events']['categoryids'] as $categoryid) { + $category = \coursecat::get($categoryid); + $categories = [$category->id]; + // All of its descendants. + foreach (\coursecat::get_all() as $cat) { + if (array_search($categoryid, $cat->get_parents()) !== false) { + $categories[] = $cat->id; + } + } + + // And all of its parents. + $categories = array_merge($categories, $category->get_parents()); + } + } + + $funcparam['categories'] = array_unique($categories); + // Do we need user events? if (!empty($params['options']['userevents'])) { $funcparam['users'] = array($USER->id); @@ -241,7 +300,8 @@ class core_calendar_external extends external_api { // Event list does not check visibility and permissions, we'll check that later. $eventlist = calendar_get_legacy_events($params['options']['timestart'], $params['options']['timeend'], - $funcparam['users'], $funcparam['groups'], $funcparam['courses'], true, $params['options']['ignorehidden']); + $funcparam['users'], $funcparam['groups'], $funcparam['courses'], true, + $params['options']['ignorehidden'], $funcparam['categories']); // WS expects arrays. $events = array(); @@ -250,7 +310,6 @@ class core_calendar_external extends external_api { if ($eventsbyid = calendar_get_events_by_id($params['events']['eventids'])) { $eventlist += $eventsbyid; } - foreach ($eventlist as $eventid => $eventobj) { $event = (array) $eventobj; // Description formatting. diff --git a/calendar/lib.php b/calendar/lib.php index 8e76d8994cd..4790d705e8a 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -3289,6 +3289,7 @@ function core_calendar_user_preferences() { * @param boolean $withduration whether only events starting within time range selected * or events in progress/already started selected as well * @param boolean $ignorehidden whether to select only visible events or all events + * @param array $categories array of category ids and/or objects. * @return array $events of selected events or an empty array if there aren't any (or there was an error) */ function calendar_get_legacy_events($tstart, $tend, $users, $groups, $courses, diff --git a/calendar/tests/externallib_test.php b/calendar/tests/externallib_test.php index d1235c3330d..2eb0f340174 100644 --- a/calendar/tests/externallib_test.php +++ b/calendar/tests/externallib_test.php @@ -297,7 +297,18 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase { // Create a few stuff to test with. $user = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); + + $category = $this->getDataGenerator()->create_category(); + + $category2 = $this->getDataGenerator()->create_category(); + $category2b = $this->getDataGenerator()->create_category(['parent' => $category2->id]); + $course3 = $this->getDataGenerator()->create_course(['category' => $category2b->id]); + + $role = $DB->get_record('role', array('shortname' => 'student')); + $this->getDataGenerator()->enrol_user($user2->id, $course3->id, $role->id); + $record = new stdClass(); $record->courseid = $course->id; $group = $this->getDataGenerator()->create_group($record); @@ -341,7 +352,9 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase { $record->groupid = $group->id; $groupevent = $this->create_calendar_event('group', $USER->id, 'group', 0, time(), $record); - $paramevents = array ('eventids' => array($siteevent->id), 'courseids' => array($course->id), 'groupids' => array($group->id)); + $paramevents = array ('eventids' => array($siteevent->id), 'courseids' => array($course->id), + 'groupids' => array($group->id), 'categoryids' => array($category->id)); + $options = array ('siteevents' => true, 'userevents' => true); $events = core_calendar_external::get_calendar_events($paramevents, $options); $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); @@ -468,6 +481,35 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase { $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); // Expect one less. $this->assertCount(4, $events['events']); + + // Create some category events. + $this->setAdminUser(); + $record = new stdClass(); + $record->categoryid = $category->id; + $this->create_calendar_event('category a', $USER->id, 'category', 0, time(), $record); + + $record->categoryid = $category2->id; + $this->create_calendar_event('category b', $USER->id, 'category', 0, time(), $record); + + // Now as student, make sure we get the events of the courses I am enrolled. + $this->setUser($user2); + $paramevents = array('categoryids' => array($category2b->id)); + $options = array('timeend' => time() + 7 * WEEKSECS); + $events = core_calendar_external::get_calendar_events($paramevents, $options); + $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); + + // Should be just one, since there's just one category event of the course I am enrolled (course3 - cat2b). + $this->assertEquals(1, count($events['events'])); + $this->assertEquals(0, count($events['warnings'])); + + // Admin can see all category events. + $this->setAdminUser(); + $paramevents = array('categoryids' => array($category->id, $category2->id, $category2b->id)); + $options = array('timeend' => time() + 7 * WEEKSECS); + $events = core_calendar_external::get_calendar_events($paramevents, $options); + $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events); + $this->assertEquals(2, count($events['events'])); + $this->assertEquals(0, count($events['warnings'])); } /**