Commit d0e56d84 authored by Andrew Nicols's avatar Andrew Nicols
Browse files

MDL-59890 calendar: Display category events on calendars

parent 5ba6507e
......@@ -51,11 +51,24 @@ class block_calendar_month extends block_base {
$courseid = $this->page->course->id;
$issite = ($courseid == SITEID);
$course = null;
$courses = null;
$categories = null;
if ($issite) {
// Being displayed at site level. This will cause the filter to fall back to auto-detecting
// the list of courses it will be grabbing events from.
$course = get_site();
$courses = calendar_get_default_courses();
if ($this->page->context->contextlevel === CONTEXT_COURSECAT) {
// Restrict to categories, and their parents, and the courses that the user is enrolled in within those
// categories.
$categories = array_keys($this->page->categories);
$courses = array_filter($courses, function($course) use ($categories) {
return array_search($course->category, $categories) !== false;
});
}
} else {
// Forcibly filter events to include only those from the particular course we are in.
$course = $this->page->course;
......@@ -65,7 +78,7 @@ class block_calendar_month extends block_base {
$renderer = $this->page->get_renderer('core_calendar');
$calendar = new calendar_information();
$calendar->prepare_for_view($course, $courses);
$calendar->set_sources($course, $courses, $this->page->category);
list($data, $template) = calendar_get_view($calendar, 'mini');
$this->content->text .= $renderer->render_from_template($template, $data);
......
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
......@@ -287,7 +287,7 @@ define([
root.on('change', SELECTORS.COURSE_SELECTOR, function() {
var selectElement = $(this);
var courseId = selectElement.val();
CalendarViewManager.reloadCurrentMonth(root, courseId)
CalendarViewManager.reloadCurrentMonth(root, courseId, null)
.then(function() {
// We need to get the selector again because the content has changed.
return root.find(SELECTORS.COURSE_SELECTOR).val(courseId);
......
......@@ -24,6 +24,7 @@
*/
define([
'jquery',
'core/notification',
'core_calendar/selectors',
'core_calendar/events',
'core/templates',
......@@ -31,6 +32,7 @@ define([
],
function(
$,
Notification,
CalendarSelectors,
CalendarEvents,
Templates,
......@@ -45,18 +47,20 @@ function(
*/
var registerCalendarEventListeners = function(root) {
var body = $('body');
body.on(CalendarEvents.monthChanged, function(e, year, month, courseId) {
body.on(CalendarEvents.monthChanged, function(e, year, month, courseId, categoryId) {
// We have to use a queue here because the calling code is decoupled from these listeners.
// It's possible for the event to be called multiple times before one call is fully resolved.
root.queue(function(next) {
return processRequest(e, year, month, courseId)
return processRequest(e, year, month, courseId, categoryId)
.then(function() {
return next();
});
})
.fail(Notification.exception)
;
});
});
var processRequest = function(e, year, month, courseId) {
var processRequest = function(e, year, month, courseId, categoryId) {
var newCurrentMonth = root.find('[data-year="' + year + '"][data-month="' + month + '"]');
var newParent = newCurrentMonth.closest(CalendarSelectors.calendarPeriods.month);
var allMonths = root.find(CalendarSelectors.calendarPeriods.month);
......@@ -95,6 +99,7 @@ function(
requestYear,
requestMonth,
courseId,
categoryId,
placeHolder
)
.then(function() {
......
......@@ -29,7 +29,7 @@ define(['jquery', 'core/ajax'], function($, Ajax) {
*
* @method deleteEvent
* @param {int} eventId The event id.
* @arapm {bool} deleteSeries Whether to delete all events in the series
* @param {bool} deleteSeries Whether to delete all events in the series
* @return {promise} Resolved with requested calendar event
*/
var deleteEvent = function(eventId, deleteSeries) {
......@@ -93,16 +93,18 @@ define(['jquery', 'core/ajax'], function($, Ajax) {
* @param {Number} year Year
* @param {Number} month Month
* @param {Number} courseid The course id.
* @param {Number} categoryid The category id.
* @param {Bool} includenavigation Whether to include navigation.
* @return {promise} Resolved with the month view data.
*/
var getCalendarMonthData = function(year, month, courseid, includenavigation) {
var getCalendarMonthData = function(year, month, courseid, categoryid, includenavigation) {
var request = {
methodname: 'core_calendar_get_calendar_monthly_view',
args: {
year: year,
month: month,
courseid: courseid,
categoryid: categoryid,
includenavigation: includenavigation,
}
};
......
......@@ -40,9 +40,11 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito
root = $(root);
root.on('click', SELECTORS.CALENDAR_NAV_LINK, function(e) {
var courseId = $(root).find(SELECTORS.CALENDAR_MONTH_WRAPPER).data('courseid');
var wrapper = root.find(SELECTORS.CALENDAR_MONTH_WRAPPER);
var courseId = wrapper.data('courseid');
var categoryId = wrapper.data('categoryid');
var link = $(e.currentTarget);
changeMonth(root, link.attr('href'), link.data('year'), link.data('month'), courseId);
changeMonth(root, link.attr('href'), link.data('year'), link.data('month'), courseId, categoryId);
e.preventDefault();
});
......@@ -55,17 +57,18 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito
* @param {Number} year Year
* @param {Number} month Month
* @param {Number} courseid The id of the course whose events are shown
* @param {Number} categoryid The id of the category whose events are shown
* @param {object} target The element being replaced. If not specified, the calendarwrapper is used.
* @return {promise}
*/
var refreshMonthContent = function(root, year, month, courseid, target) {
var refreshMonthContent = function(root, year, month, courseid, categoryid, target) {
startLoading(root);
target = target || root.find(SELECTORS.CALENDAR_MONTH_WRAPPER);
M.util.js_pending([root.get('id'), year, month, courseid].join('-'));
var includenavigation = root.data('includenavigation');
return CalendarRepository.getCalendarMonthData(year, month, courseid, includenavigation)
return CalendarRepository.getCalendarMonthData(year, month, courseid, categoryid, includenavigation)
.then(function(context) {
return Templates.render(root.attr('data-template'), context);
})
......@@ -86,15 +89,16 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito
/**
* Handle changes to the current calendar view.
*
* @param {object} root The root element.
* @param {object} root The container element
* @param {String} url The calendar url to be shown
* @param {Number} year Year
* @param {Number} month Month
* @param {Number} courseid The id of the course whose events are shown
* @param {Number} categoryid The id of the category whose events are shown
* @return {promise}
*/
var changeMonth = function(root, url, year, month, courseid) {
return refreshMonthContent(root, year, month, courseid)
var changeMonth = function(root, url, year, month, courseid, categoryid) {
return refreshMonthContent(root, year, month, courseid, categoryid)
.then(function() {
if (url.length && url !== '#') {
window.history.pushState({}, '', url);
......@@ -102,7 +106,7 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito
return arguments;
})
.then(function() {
$('body').trigger(CalendarEvents.monthChanged, [year, month, courseid]);
$('body').trigger(CalendarEvents.monthChanged, [year, month, courseid, categoryid]);
return arguments;
});
};
......@@ -112,16 +116,23 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito
*
* @param {object} root The container element.
* @param {Number} courseId The course id.
* @param {Number} categoryId The id of the category whose events are shown
* @return {promise}
*/
var reloadCurrentMonth = function(root, courseId) {
var reloadCurrentMonth = function(root, courseId, categoryId) {
var year = root.find(SELECTORS.CALENDAR_MONTH_WRAPPER).data('year');
var month = root.find(SELECTORS.CALENDAR_MONTH_WRAPPER).data('month');
if (!courseId) {
if (typeof courseId === 'undefined') {
courseId = root.find(SELECTORS.CALENDAR_MONTH_WRAPPER).data('courseid');
}
return refreshMonthContent(root, year, month, courseId);
if (typeof categoryId === 'undefined') {
categoryId = root.find(SELECTORS.CALENDAR_MONTH_WRAPPER).data('categoryid');
}
return refreshMonthContent(root, year, month, courseId, categoryId);
};
/**
......
......@@ -82,6 +82,8 @@ class calendar_event_exporter extends event_exporter_base {
$params = array('update' => $moduleid, 'return' => true, 'sesskey' => sesskey());
$editurl = new \moodle_url('/course/mod.php', $params);
$values['editurl'] = $editurl->out(false);
} else if ($event->get_type() == 'category') {
$url = $event->get_category()->get_proxied_instance()->get_view_link();
} else if ($event->get_type() == 'course') {
$url = course_get_url($event->get_course()->get('id') ?: SITEID);
} else {
......
......@@ -48,22 +48,13 @@ class event_exporter extends event_exporter_base {
* @return array
*/
protected static function define_other_properties() {
$values = parent::define_other_properties();
$values['isactionevent'] = ['type' => PARAM_BOOL];
$values['iscourseevent'] = ['type' => PARAM_BOOL];
$values['iscategoryevent'] = ['type' => PARAM_BOOL];
$values['candelete'] = ['type' => PARAM_BOOL];
$values['url'] = ['type' => PARAM_URL];
$values['action'] = [
'type' => event_action_exporter::read_properties_definition(),
'optional' => true,
];
$values['editurl'] = [
'type' => PARAM_URL,
'optional' => true,
];
return $values;
}
......@@ -82,9 +73,6 @@ class event_exporter extends event_exporter_base {
$event = $this->event;
$context = $this->related['context'];
$values['isactionevent'] = false;
$values['iscourseevent'] = false;
$values['iscategoryevent'] = false;
if ($moduleproxy = $event->get_course_module()) {
$modulename = $moduleproxy->get('modname');
$moduleid = $moduleproxy->get('id');
......@@ -95,7 +83,6 @@ class event_exporter extends event_exporter_base {
$editurl = new \moodle_url('/course/mod.php', $params);
$values['editurl'] = $editurl->out(false);
} else if ($event->get_type() == 'category') {
$values['iscategoryevent'] = true;
$url = $event->get_category()->get_proxied_instance()->get_view_link();
} else if ($event->get_type() == 'course') {
$url = \course_get_url($this->related['course'] ?: SITEID);
......
......@@ -217,6 +217,9 @@ class event_exporter_base extends exporter {
'iscourseevent' => [
'type' => PARAM_BOOL
],
'iscategoryevent' => [
'type' => PARAM_BOOL
],
'groupname' => [
'type' => PARAM_RAW,
'optional' => true,
......@@ -239,10 +242,13 @@ class event_exporter_base extends exporter {
$context = $this->related['context'];
$values['isactionevent'] = false;
$values['iscourseevent'] = false;
$values['iscategoryevent'] = false;
if ($moduleproxy = $event->get_course_module()) {
$values['isactionevent'] = true;
} else if ($event->get_type() == 'course') {
$values['iscourseevent'] = true;
} else if ($event->get_type() == 'category') {
$values['iscategoryevent'] = true;
}
$timesort = $event->get_times()->get_sort_time()->getTimestamp();
$iconexporter = new event_icon_exporter($event, ['context' => $context]);
......
......@@ -46,6 +46,8 @@ class event_icon_exporter extends exporter {
*/
public function __construct(event_interface $event, $related = []) {
$coursemodule = $event->get_course_module();
$category = $event->get_category();
$categoryid = $category ? $category->get('id') : null;
$course = $event->get_course();
$courseid = $course ? $course->get('id') : null;
$group = $event->get_group();
......@@ -54,6 +56,7 @@ class event_icon_exporter extends exporter {
$userid = $user ? $user->get('id') : null;
$isactivityevent = !empty($coursemodule);
$isglobalevent = ($course && $courseid == SITEID);
$iscategoryevent = ($category && !empty($categoryid));
$iscourseevent = ($course && !empty($courseid) && $courseid != SITEID && empty($groupid));
$isgroupevent = ($group && !empty($groupid));
$isuserevent = ($user && !empty($userid));
......@@ -70,24 +73,28 @@ class event_icon_exporter extends exporter {
} else if ($isglobalevent) {
$key = 'i/siteevent';
$component = 'core';
$alttext = get_string('globalevent', 'calendar');
$alttext = get_string('typesite', 'calendar');
} else if ($iscategoryevent) {
$key = 'i/categoryevent';
$component = 'core';
$alttext = get_string('typecategory', 'calendar');
} else if ($iscourseevent) {
$key = 'i/courseevent';
$component = 'core';
$alttext = get_string('courseevent', 'calendar');
$alttext = get_string('typecourse', 'calendar');
} else if ($isgroupevent) {
$key = 'i/groupevent';
$component = 'core';
$alttext = get_string('groupevent', 'calendar');
$alttext = get_string('typegroup', 'calendar');
} else if ($isuserevent) {
$key = 'i/userevent';
$component = 'core';
$alttext = get_string('userevent', 'calendar');
$alttext = get_string('typeuser', 'calendar');
} else {
// Default to site event icon?
$key = 'i/siteevent';
$component = 'core';
$alttext = get_string('globalevent', 'calendar');
$alttext = get_string('typesite', 'calendar');
}
$data = new \stdClass();
......
......@@ -108,6 +108,11 @@ class month_exporter extends exporter {
'courseid' => [
'type' => PARAM_INT,
],
'categoryid' => [
'type' => PARAM_INT,
'optional' => true,
'default' => 0,
],
'filter_selector' => [
'type' => PARAM_RAW,
],
......@@ -211,6 +216,10 @@ class month_exporter extends exporter {
$return['defaulteventcontext'] = $context->id;
}
if ($this->calendar->categoryid) {
$return['categoryid'] = $this->calendar->categoryid;
}
return $return;
}
......
......@@ -108,7 +108,7 @@ if ($action === 'delete' && $eventid > 0) {
}
$calendar = new calendar_information(0, 0, 0, $time);
$calendar->prepare_for_view($course, $courses);
$calendar->set_sources($course, $courses);
$formoptions = new stdClass;
if ($eventid !== 0) {
......
......@@ -100,7 +100,7 @@ if ($course !== NULL) {
$PAGE->set_url($url);
$calendar = new calendar_information(0, 0, 0, $time);
$calendar->prepare_for_view($course, $courses);
$calendar->set_sources($course, $courses);
$pagetitle = get_string('export', 'calendar');
......
......@@ -882,10 +882,11 @@ class core_calendar_external extends external_api {
* @param int $year The year to be shown
* @param int $month The month to be shown
* @param int $courseid The course to be included
* @param int $categoryid The category to be included
* @param bool $includenavigation Whether to include navigation
* @return array
*/
public static function get_calendar_monthly_view($year, $month, $courseid, $includenavigation) {
public static function get_calendar_monthly_view($year, $month, $courseid, $categoryid, $includenavigation) {
global $CFG, $DB, $USER, $PAGE;
require_once($CFG->dirroot."/calendar/lib.php");
......@@ -894,27 +895,44 @@ class core_calendar_external extends external_api {
'year' => $year,
'month' => $month,
'courseid' => $courseid,
'categoryid' => $categoryid,
'includenavigation' => $includenavigation,
]);
// TODO: Copy what we do in calendar/view.php.
$context = \context_user::instance($USER->id);
self::validate_context($context);
if ($courseid != SITEID && !empty($courseid)) {
// Course ID must be valid and existing.
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
$courses = [$course->id => $course];
$coursecat = \coursecat::get($course->category);
$category = $coursecat->get_db_record();
} else {
$course = get_site();
$courses = calendar_get_default_courses();
$category = null;
if ($categoryid) {
self::validate_context(context_coursecat::instance($categoryid));
$ids = [$categoryid];
$category = \coursecat::get($categoryid);
$ids += $category->get_parents();
$categories = \coursecat::get_many($ids);
$courses = array_filter($courses, function($course) use ($categories) {
return array_search($course->category, $categories) !== false;
});
$category = $category->get_db_record();
}
}
// TODO: Copy what we do in calendar/view.php.
$context = \context_user::instance($USER->id);
self::validate_context($context);
$type = \core_calendar\type_factory::get_calendar_instance();
$time = $type->convert_to_timestamp($year, $month, 1);
$calendar = new calendar_information(0, 0, 0, $time);
$calendar->prepare_for_view($course, $courses);
$calendar->set_sources($course, $courses, $category);
list($data, $template) = calendar_get_view($calendar, 'month', $params['includenavigation']);
......@@ -932,6 +950,7 @@ class core_calendar_external extends external_api {
'year' => new external_value(PARAM_INT, 'Month to be viewed', VALUE_REQUIRED),
'month' => new external_value(PARAM_INT, 'Year to be viewed', VALUE_REQUIRED),
'courseid' => new external_value(PARAM_INT, 'Course being viewed', VALUE_DEFAULT, SITEID, NULL_ALLOWED),
'categoryid' => new external_value(PARAM_INT, 'Category being viewed', VALUE_DEFAULT, null, NULL_ALLOWED),
'includenavigation' => new external_value(
PARAM_BOOL,
'Whether to show course navigation',
......
......@@ -1059,7 +1059,7 @@ class calendar_information {
$this->categoryid = null;
$this->categories = null;
if (null !== $category) {
if (null !== $category && $category->id > 0) {
// A specific category was requested - set the current category, and include all parents of that category.
$category = \coursecat::get($category->id);
$this->categoryid = $category->id;
......
......@@ -46,6 +46,9 @@
{{/description}}
<h4>{{#str}} eventtype, core_calendar {{/str}}</h4>
{{eventtype}}
{{#iscategoryevent}}
<div>{{{category.nestedname}}}</div>
{{/iscategoryevent}}
{{#iscourseevent}}
<div><a href="{{url}}">{{course.fullname}}</a></div>
{{/iscourseevent}}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment