diff --git a/calendar/amd/src/selectors.js b/calendar/amd/src/selectors.js index a22a86d9963..ef7e52f90b2 100644 --- a/calendar/amd/src/selectors.js +++ b/calendar/amd/src/selectors.js @@ -26,12 +26,14 @@ define([], function() { eventFilterItem: "[data-action='filter-event-type']", eventType: { site: "[data-eventtype-site]", + category: "[data-eventtype-category]", course: "[data-eventtype-course]", group: "[data-eventtype-group]", user: "[data-eventtype-user]", }, popoverType: { site: "[data-popover-eventtype-site]", + category: "[data-popover-eventtype-category]", course: "[data-popover-eventtype-course]", group: "[data-popover-eventtype-group]", user: "[data-popover-eventtype-user]", diff --git a/calendar/classes/local/event/forms/create.php b/calendar/classes/local/event/forms/create.php index 0a6b757d6d1..8892bfb3fcd 100644 --- a/calendar/classes/local/event/forms/create.php +++ b/calendar/classes/local/event/forms/create.php @@ -55,7 +55,7 @@ class create extends \moodleform { /** * The form definition */ - public function definition () { + public function definition() { global $PAGE; $mform = $this->_form; @@ -203,6 +203,9 @@ class create extends \moodleform { if (isset($eventtypes['course'])) { $options['course'] = get_string('course'); } + if (isset($eventtypes['category'])) { + $options['category'] = get_string('category'); + } if (isset($eventtypes['site'])) { $options['site'] = get_string('site'); } @@ -223,6 +226,16 @@ class create extends \moodleform { $mform->addElement('select', 'eventtype', get_string('eventkind', 'calendar'), $options); } + if (isset($eventtypes['category'])) { + $categoryoptions = []; + foreach ($eventtypes['category'] as $id => $category) { + $categoryoptions[$id] = $category; + } + + $mform->addElement('select', 'categoryid', get_string('category'), $categoryoptions); + $mform->hideIf('categoryid', 'eventtype', 'noteq', 'category'); + } + if (isset($eventtypes['course'])) { $courseoptions = []; foreach ($eventtypes['course'] as $course) { diff --git a/calendar/lib.php b/calendar/lib.php index d511497c850..fc92c73fa77 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -89,6 +89,10 @@ define('CALENDAR_EVENT_GROUP', 4); */ define('CALENDAR_EVENT_USER', 8); +/** + * CALENDAR_EVENT_COURSECAT - Course category calendar event types + */ +define('CALENDAR_EVENT_COURSECAT', 16); /** * CALENDAR_IMPORT_FROM_FILE - import the calendar from a file @@ -360,8 +364,7 @@ class calendar_event { if ($this->editorcontext === null) { // Switch on the event type to decide upon the appropriate context to use for this event. $this->editorcontext = $this->properties->context; - if ($this->properties->eventtype != 'user' && $this->properties->eventtype != 'course' - && $this->properties->eventtype != 'site' && $this->properties->eventtype != 'group') { + if (!calendar_is_valid_eventtype($this->properties->eventtype)) { return clean_text($this->properties->description, $this->properties->format); } } @@ -461,6 +464,11 @@ class calendar_event { $this->properties->groupid = 0; $this->properties->userid = $USER->id; break; + case 'category': + $this->properties->groupid = 0; + $this->properties->category = 0; + $this->properties->userid = $USER->id; + break; case 'group': $this->properties->userid = $USER->id; break; @@ -752,6 +760,17 @@ class calendar_event { // We have a course and are within the course context so we had // better use the courses max bytes value. $this->editoroptions['maxbytes'] = $course->maxbytes; + } else if ($properties->eventtype === 'category') { + // First check the course is valid. + $category = $DB->get_record('course_categories', array('id' => $properties->categoryid)); + if (!$category) { + print_error('invalidcourse'); + } + // Course context. + $this->editorcontext = $this->properties->context; + // We have a course and are within the course context so we had + // better use the courses max bytes value. + $this->editoroptions['maxbytes'] = $course->maxbytes; } else { // If we get here we have a custom event type as used by some // modules. In this case the event will have been added by @@ -896,8 +915,7 @@ class calendar_event { // Switch on the event type to decide upon the appropriate context to use for this event. $this->editorcontext = $this->properties->context; - if ($this->properties->eventtype != 'user' && $this->properties->eventtype != 'course' - && $this->properties->eventtype != 'site' && $this->properties->eventtype != 'group') { + if (!calendar_is_valid_eventtype($this->properties->eventtype)) { // We don't have a context here, do a normal format_text. return external_format_text($this->properties->description, $this->properties->format, $this->editorcontext->id); } @@ -2237,7 +2255,8 @@ function calendar_show_event_type($type, $user = null) { */ function calendar_set_event_type_display($type, $display = null, $user = null) { $persist = get_user_preferences('calendar_persistflt', 0, $user); - $default = CALENDAR_EVENT_GLOBAL + CALENDAR_EVENT_COURSE + CALENDAR_EVENT_GROUP + CALENDAR_EVENT_USER; + $default = CALENDAR_EVENT_GLOBAL + CALENDAR_EVENT_COURSE + CALENDAR_EVENT_GROUP + + CALENDAR_EVENT_USER + CALENDAR_EVENT_COURSECAT; if ($persist === 0) { global $SESSION; if (!isset($SESSION->calendarshoweventtype)) { @@ -2273,14 +2292,16 @@ function calendar_set_event_type_display($type, $display = null, $user = null) { * @param stdClass $allowed list of allowed edit for event type * @param stdClass|int $course object of a course or course id * @param array $groups array of groups for the given course + * @param stdClass|int $category object of a category */ -function calendar_get_allowed_types(&$allowed, $course = null, $groups = null) { +function calendar_get_allowed_types(&$allowed, $course = null, $groups = null, $category = null) { global $USER, $DB; $allowed = new \stdClass(); $allowed->user = has_capability('moodle/calendar:manageownentries', \context_system::instance()); $allowed->groups = false; $allowed->courses = false; + $allowed->categories = false; $allowed->site = has_capability('moodle/calendar:manageentries', \context_course::instance(SITEID)); $getgroupsfunc = function($course, $context, $user) use ($groups) { if ($course->groupmode != NOGROUPS || !$course->groupmodeforce) { @@ -2316,6 +2337,13 @@ function calendar_get_allowed_types(&$allowed, $course = null, $groups = null) { } } } + + if (!empty($category)) { + $catcontext = \context_coursecat::instance($category->id); + if (has_capability('moodle/category:manage', $catcontext)) { + $allowed->categories = [$category->id => 1]; + } + } } /** @@ -2325,6 +2353,7 @@ function calendar_get_allowed_types(&$allowed, $course = null, $groups = null) { * The returned array will optionally have 5 keys: * 'user' : true if the logged in user can create user events * 'site' : true if the logged in user can create site events + * 'category' : array of course categories that the user can create events for * 'course' : array of courses that the user can create events for * 'group': array of groups that the user can create events for * 'groupcourses' : array of courses that the groups belong to (can @@ -2333,7 +2362,7 @@ function calendar_get_allowed_types(&$allowed, $course = null, $groups = null) { * @return array The array of allowed types. */ function calendar_get_all_allowed_types() { - global $CFG, $USER; + global $CFG, $USER, $DB; require_once($CFG->libdir . '/enrollib.php'); @@ -2349,6 +2378,10 @@ function calendar_get_all_allowed_types() { $types['site'] = true; } + if (coursecat::has_manage_capability_on_any()) { + $types['category'] = coursecat::make_categories_list('moodle/category:manage'); + } + // This function warms the context cache for the course so the calls // to load the course context in calendar_get_allowed_types don't result // in additional DB queries. @@ -2401,7 +2434,7 @@ function calendar_user_can_add_event($course) { calendar_get_allowed_types($allowed, $course); - return (bool)($allowed->user || $allowed->groups || $allowed->courses || $allowed->site); + return (bool)($allowed->user || $allowed->groups || $allowed->courses || $allowed->category || $allowed->site); } /** @@ -2426,6 +2459,8 @@ function calendar_add_event_allowed($event) { } switch ($event->eventtype) { + case 'category': + return has_capability('moodle/category:manage', $event->context); case 'course': return has_capability('moodle/calendar:manageentries', $event->context); case 'group': @@ -2485,6 +2520,9 @@ function calendar_get_eventtype_choices($courseid) { if (!empty($allowed->courses)) { $choices['course'] = get_string('courseevents', 'calendar'); } + if (!empty($allowed->categories)) { + $choices['category'] = get_string('categoryevents', 'calendar'); + } if (!empty($allowed->groups) and is_array($allowed->groups)) { $choices['group'] = get_string('group'); } @@ -2505,6 +2543,8 @@ function calendar_add_subscription($sub) { $sub->courseid = $SITE->id; } else if ($sub->eventtype === 'group' || $sub->eventtype === 'course') { $sub->courseid = $sub->course; + } else if ($sub->eventtype === 'category') { + $sub->categoryid = $sub->category; } else { // User events. $sub->courseid = 0; @@ -3262,6 +3302,7 @@ function calendar_get_footer_options($calendar) { function calendar_get_filter_types() { $types = [ 'site', + 'category', 'course', 'group', 'user', @@ -3274,3 +3315,20 @@ function calendar_get_filter_types() { ]; }, $types); } + +/** + * Check whether the specified event type is valid. + * + * @param string $type + * @return bool + */ +function calendar_is_valid_eventtype($type) { + $validtypes = [ + 'user', + 'group', + 'course', + 'category', + 'site', + ]; + return in_array($type, $validtypes); +} diff --git a/lang/en/calendar.php b/lang/en/calendar.php index a0bf7d0f4f5..28280d02cf5 100644 --- a/lang/en/calendar.php +++ b/lang/en/calendar.php @@ -44,6 +44,7 @@ $string['confirmeventseriesdelete'] = 'The "{$a->name}" event is part of a serie $string['course'] = 'Course'; $string['courseevent'] = 'Course event'; $string['courseevents'] = 'Course events'; +$string['categoryevents'] = 'Category events'; $string['courses'] = 'Courses'; $string['customexport'] = 'Custom range ({$a->timestart} - {$a->timeend})'; $string['daily'] = 'Daily'; @@ -141,6 +142,7 @@ $string['groupevent'] = 'Group event'; $string['groupevents'] = 'Group events'; $string['eventtypeglobal'] = 'global'; $string['eventtypesite'] = 'global'; +$string['eventtypecategory'] = 'category'; $string['eventtypecourse'] = 'course'; $string['eventtypemodule'] = 'module'; $string['eventtypegroup'] = 'group'; @@ -227,6 +229,7 @@ $string['tue'] = 'Tue'; $string['tuesday'] = 'Tuesday'; $string['typeclose'] = 'Close event'; $string['typecourse'] = 'Course event'; +$string['typecategory'] = 'Category event'; $string['typedue'] = 'Due event'; $string['typegradingdue'] = 'Grading due event'; $string['typegroup'] = 'Group event'; diff --git a/lib/coursecatlib.php b/lib/coursecatlib.php index d8c8ef12855..d4b56c2e936 100644 --- a/lib/coursecatlib.php +++ b/lib/coursecatlib.php @@ -291,6 +291,53 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate { return $categories; } + /** + * Load all coursecat objects. + * + * @param array $options Options: + * @param bool $options.returnhidden Return categories even if they are hidden + * @return coursecat[] + */ + public static function get_all($options = null) { + global $DB; + + if (null === $options) { + $options = []; + } + + $coursecatrecordcache = cache::make('core', 'coursecatrecords'); + + $catcontextsql = \context_helper::get_preload_record_columns_sql('ctx'); + $catsql = "SELECT cc.*, {$catcontextsql} + FROM {course_categories} cc + JOIN {context} ctx ON cc.id = ctx.instanceid"; + $catsqlwhere = "WHERE ctx.contextlevel = :contextlevel"; + $catsqlorder = "ORDER BY cc.depth ASC, cc.sortorder ASC"; + + $catrs = $DB->get_recordset_sql("{$catsql} {$catsqlwhere} {$catsqlorder}", [ + 'contextlevel' => CONTEXT_COURSECAT, + ]); + + $types['categories'] = []; + $categories = []; + $toset = []; + foreach ($catrs as $record) { + \context_helper::preload_from_record($record); + $category = new coursecat($record); + $toset[$category->id] = $category; + + if (!empty($options['returnhidden']) || $category->is_uservisible()) { + $categories[$record->id] = new coursecat($record); + } + } + $catrs->close(); + + $coursecatrecordcache->set_many($toset); + + return $categories; + + } + /** * Returns the first found category *