MDL-59890 calendar: Display category events on calendars

This commit is contained in:
Andrew Nicols 2017-09-19 11:17:04 +08:00
parent 5ba6507e5b
commit d0e56d847a
29 changed files with 194 additions and 60 deletions

View File

@ -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);

View File

@ -1 +1 @@
define(["jquery","core/ajax","core/str","core/templates","core/notification","core/custom_interaction_events","core/modal_events","core/modal_factory","core_calendar/modal_event_form","core_calendar/summary_modal","core_calendar/repository","core_calendar/events","core_calendar/view_manager"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n={ROOT:"[data-region='calendar']",DAY:"[data-region='day']",EVENT_ITEM:"[data-region='event-item']",EVENT_LINK:"[data-action='view-event']",NEW_EVENT_BUTTON:"[data-action='new-event-button']",DAY_CONTENT:"[data-region='day-content']",LOADING_ICON:".loading-icon",VIEW_DAY_LINK:"[data-action='view-day-link']",CALENDAR_MONTH_WRAPPER:".calendarwrapper",COURSE_SELECTOR:'select[name="course"]',TODAY:".today"},o=function(a){var b="type"+a;return c.get_string(b,"core_calendar").then(function(a){return a})},p=function(a){k.getEventById(a).then(function(b){if(!b.event)throw new Error("Error encountered while trying to fetch calendar event with ID: "+a);var c=b.event;return o(c.eventtype).then(function(a){return c.eventtype=a,c})}).then(function(a){var b={title:a.name,type:j.TYPE,body:d.render("core_calendar/event_summary_body",a),templateContext:{canedit:a.canedit,candelete:a.candelete,isactionevent:a.isactionevent,url:a.url}};return h.create(b)}).done(function(a){a.getRoot().on(g.hidden,function(){a.destroy()}),a.show()}).fail(e.exception)},q=function(b,c,f,g){var h=null,i=g.attr("data-day-timestamp");f&&(h=f.attr("data-day-timestamp")),f&&h==i||d.render("core/loading",{}).then(function(a,b){g.find(n.DAY_CONTENT).addClass("hidden"),d.appendNodeContents(g,a,b),f&&(f.find(n.DAY_CONTENT).addClass("hidden"),d.appendNodeContents(f,a,b))}).then(function(){return k.updateEventStartDay(c,i)}).then(function(){a("body").trigger(l.eventMoved,[c,f,g])}).always(function(){var a=g.find(n.LOADING_ICON);if(g.find(n.DAY_CONTENT).removeClass("hidden"),d.replaceNode(a,"",""),f){var b=f.find(n.LOADING_ICON);f.find(n.DAY_CONTENT).removeClass("hidden"),d.replaceNode(b,"","")}}).fail(e.exception)},r=function(a){var b=a.find(n.NEW_EVENT_BUTTON),c=b.attr("data-context-id");return h.create({type:i.TYPE,large:!0,templateContext:{contextid:c}})},s=function(b,c){var d=a("body"),f=a(b).find(n.CALENDAR_MONTH_WRAPPER).data("courseid");d.on(l.created,function(){m.reloadCurrentMonth(b)}),d.on(l.deleted,function(){m.reloadCurrentMonth(b)}),d.on(l.updated,function(){m.reloadCurrentMonth(b)}),d.on(l.editActionEvent,function(a,b){window.location.assign(b)}),d.on(l.moveEvent,q),d.on(l.eventMoved,function(){m.reloadCurrentMonth(b)}),c.then(function(a){d.on(l.editEvent,function(b,c){a.setEventId(c),a.show()}),a.setCourseId(f)}).fail(e.exception)},t=function(b){b.on("click",n.EVENT_ITEM,function(b){b.preventDefault(),b.stopPropagation();var c=a(b.target),d=null;d=c.is(n.EVENT_LINK)?c.attr("data-event-id"):c.find(n.EVENT_LINK).attr("data-event-id"),p(d)}),b.on("change",n.COURSE_SELECTOR,function(){var c=a(this),d=c.val();m.reloadCurrentMonth(b,d).then(function(){return b.find(n.COURSE_SELECTOR).val(d)}).fail(e.exception)});var c=r(b);s(b,c),b.on("click",n.NEW_EVENT_BUTTON,function(a){c.then(function(a){var c=b.find(n.TODAY);c.length||a.setStartTime(b.find(n.DAY).attr("data-new-event-timestamp")),a.show()}).fail(e.exception),a.preventDefault()}),b.on("click",n.DAY,function(b){var d=a(b.target);if(!d.is(n.VIEW_DAY_LINK)){var f=a(this).attr("data-new-event-timestamp");c.then(function(a){a.setStartTime(f),a.show()}).fail(e.exception),b.preventDefault()}})};return{init:function(b){b=a(b),m.init(b),t(b)}}});
define(["jquery","core/ajax","core/str","core/templates","core/notification","core/custom_interaction_events","core/modal_events","core/modal_factory","core_calendar/modal_event_form","core_calendar/summary_modal","core_calendar/repository","core_calendar/events","core_calendar/view_manager"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n={ROOT:"[data-region='calendar']",DAY:"[data-region='day']",EVENT_ITEM:"[data-region='event-item']",EVENT_LINK:"[data-action='view-event']",NEW_EVENT_BUTTON:"[data-action='new-event-button']",DAY_CONTENT:"[data-region='day-content']",LOADING_ICON:".loading-icon",VIEW_DAY_LINK:"[data-action='view-day-link']",CALENDAR_MONTH_WRAPPER:".calendarwrapper",COURSE_SELECTOR:'select[name="course"]',TODAY:".today"},o=function(a){var b="type"+a;return c.get_string(b,"core_calendar").then(function(a){return a})},p=function(a){k.getEventById(a).then(function(b){if(!b.event)throw new Error("Error encountered while trying to fetch calendar event with ID: "+a);var c=b.event;return o(c.eventtype).then(function(a){return c.eventtype=a,c})}).then(function(a){var b={title:a.name,type:j.TYPE,body:d.render("core_calendar/event_summary_body",a),templateContext:{canedit:a.canedit,candelete:a.candelete,isactionevent:a.isactionevent,url:a.url}};return h.create(b)}).done(function(a){a.getRoot().on(g.hidden,function(){a.destroy()}),a.show()}).fail(e.exception)},q=function(b,c,f,g){var h=null,i=g.attr("data-day-timestamp");f&&(h=f.attr("data-day-timestamp")),f&&h==i||d.render("core/loading",{}).then(function(a,b){g.find(n.DAY_CONTENT).addClass("hidden"),d.appendNodeContents(g,a,b),f&&(f.find(n.DAY_CONTENT).addClass("hidden"),d.appendNodeContents(f,a,b))}).then(function(){return k.updateEventStartDay(c,i)}).then(function(){a("body").trigger(l.eventMoved,[c,f,g])}).always(function(){var a=g.find(n.LOADING_ICON);if(g.find(n.DAY_CONTENT).removeClass("hidden"),d.replaceNode(a,"",""),f){var b=f.find(n.LOADING_ICON);f.find(n.DAY_CONTENT).removeClass("hidden"),d.replaceNode(b,"","")}}).fail(e.exception)},r=function(a){var b=a.find(n.NEW_EVENT_BUTTON),c=b.attr("data-context-id");return h.create({type:i.TYPE,large:!0,templateContext:{contextid:c}})},s=function(b,c){var d=a("body"),f=a(b).find(n.CALENDAR_MONTH_WRAPPER).data("courseid");d.on(l.created,function(){m.reloadCurrentMonth(b)}),d.on(l.deleted,function(){m.reloadCurrentMonth(b)}),d.on(l.updated,function(){m.reloadCurrentMonth(b)}),d.on(l.editActionEvent,function(a,b){window.location.assign(b)}),d.on(l.moveEvent,q),d.on(l.eventMoved,function(){m.reloadCurrentMonth(b)}),c.then(function(a){d.on(l.editEvent,function(b,c){a.setEventId(c),a.show()}),a.setCourseId(f)}).fail(e.exception)},t=function(b){b.on("click",n.EVENT_ITEM,function(b){b.preventDefault(),b.stopPropagation();var c=a(b.target),d=null;d=c.is(n.EVENT_LINK)?c.attr("data-event-id"):c.find(n.EVENT_LINK).attr("data-event-id"),p(d)}),b.on("change",n.COURSE_SELECTOR,function(){var c=a(this),d=c.val();m.reloadCurrentMonth(b,d,null).then(function(){return b.find(n.COURSE_SELECTOR).val(d)}).fail(e.exception)});var c=r(b);s(b,c),b.on("click",n.NEW_EVENT_BUTTON,function(a){c.then(function(a){var c=b.find(n.TODAY);c.length||a.setStartTime(b.find(n.DAY).attr("data-new-event-timestamp")),a.show()}).fail(e.exception),a.preventDefault()}),b.on("click",n.DAY,function(b){var d=a(b.target);if(!d.is(n.VIEW_DAY_LINK)){var f=a(this).attr("data-new-event-timestamp");c.then(function(a){a.setStartTime(f),a.show()}).fail(e.exception),b.preventDefault()}})};return{init:function(b){b=a(b),m.init(b),t(b)}}});

View File

@ -1 +1 @@
define(["jquery","core_calendar/selectors","core_calendar/events","core/templates","core_calendar/view_manager"],function(a,b,c,d,e){var f=function(d){var f=a("body");f.on(c.monthChanged,function(a,b,c,e){d.queue(function(d){return g(a,b,c,e).then(function(){return d()})})});var g=function(c,f,g,h){var i=d.find('[data-year="'+f+'"][data-month="'+g+'"]'),j=i.closest(b.calendarPeriods.month),k=d.find(b.calendarPeriods.month),l=a(k[0]),m=a(k[2]),n=a("<span>");n.attr("data-template","core_calendar/threemonth_month"),n.attr("data-includenavigation",!1);var o=a("<div>");o.hide(),o.append(n);var p,q,r;return j.is(l)?(o.insertBefore(l),p=l.data("previousYear"),q=l.data("previousMonth"),r=m):j.is(m)&&(o.insertAfter(m),p=m.data("nextYear"),q=m.data("nextMonth"),r=l),e.refreshMonthContent(n,p,q,h,n).then(function(){var b=a.Deferred(),c=a.Deferred();return r.slideUp("fast",function(){a(this).remove(),b.resolve()}),o.slideDown("fast",function(){c.resolve()}),a.when(b,c)})}};return{init:function(b){b=a(b),f(b)}}});
define(["jquery","core/notification","core_calendar/selectors","core_calendar/events","core/templates","core_calendar/view_manager"],function(a,b,c,d,e,f){var g=function(e){var g=a("body");g.on(d.monthChanged,function(a,c,d,f,g){e.queue(function(e){return h(a,c,d,f,g).then(function(){return e()}).fail(b.exception)})});var h=function(b,d,g,h,i){var j=e.find('[data-year="'+d+'"][data-month="'+g+'"]'),k=j.closest(c.calendarPeriods.month),l=e.find(c.calendarPeriods.month),m=a(l[0]),n=a(l[2]),o=a("<span>");o.attr("data-template","core_calendar/threemonth_month"),o.attr("data-includenavigation",!1);var p=a("<div>");p.hide(),p.append(o);var q,r,s;return k.is(m)?(p.insertBefore(m),q=m.data("previousYear"),r=m.data("previousMonth"),s=n):k.is(n)&&(p.insertAfter(n),q=n.data("nextYear"),r=n.data("nextMonth"),s=m),f.refreshMonthContent(o,q,r,h,i,o).then(function(){var b=a.Deferred(),c=a.Deferred();return s.slideUp("fast",function(){a(this).remove(),b.resolve()}),p.slideDown("fast",function(){c.resolve()}),a.when(b,c)})}};return{init:function(b){b=a(b),g(b)}}});

View File

@ -1 +1 @@
define(["jquery","core/ajax"],function(a,b){var c=function(a,c){"undefined"==typeof c&&(c=!1);var d={methodname:"core_calendar_delete_calendar_events",args:{events:[{eventid:a,repeat:c}]}};return b.call([d])[0]},d=function(a){var c={methodname:"core_calendar_get_calendar_event_by_id",args:{eventid:a}};return b.call([c])[0]},e=function(a){var c={methodname:"core_calendar_submit_create_update_form",args:{formdata:a}};return b.call([c])[0]},f=function(a,c,d,e){var f={methodname:"core_calendar_get_calendar_monthly_view",args:{year:a,month:c,courseid:d,includenavigation:e}};return b.call([f])[0]},g=function(a,c){var d={methodname:"core_calendar_update_event_start_day",args:{eventid:a,daytimestamp:c}};return b.call([d])[0]};return{getEventById:d,deleteEvent:c,updateEventStartDay:g,submitCreateUpdateForm:e,getCalendarMonthData:f}});
define(["jquery","core/ajax"],function(a,b){var c=function(a,c){"undefined"==typeof c&&(c=!1);var d={methodname:"core_calendar_delete_calendar_events",args:{events:[{eventid:a,repeat:c}]}};return b.call([d])[0]},d=function(a){var c={methodname:"core_calendar_get_calendar_event_by_id",args:{eventid:a}};return b.call([c])[0]},e=function(a){var c={methodname:"core_calendar_submit_create_update_form",args:{formdata:a}};return b.call([c])[0]},f=function(a,c,d,e,f){var g={methodname:"core_calendar_get_calendar_monthly_view",args:{year:a,month:c,courseid:d,categoryid:e,includenavigation:f}};return b.call([g])[0]},g=function(a,c){var d={methodname:"core_calendar_update_event_start_day",args:{eventid:a,daytimestamp:c}};return b.call([d])[0]};return{getEventById:d,deleteEvent:c,updateEventStartDay:g,submitCreateUpdateForm:e,getCalendarMonthData:f}});

View File

@ -1 +1 @@
define([],function(){return{eventFilterItem:"[data-action='filter-event-type']",eventType:{site:"[data-eventtype-site]",course:"[data-eventtype-course]",group:"[data-eventtype-group]",user:"[data-eventtype-user]"},popoverType:{site:"[data-popover-eventtype-site]",course:"[data-popover-eventtype-course]",group:"[data-popover-eventtype-group]",user:"[data-popover-eventtype-user]"},calendarPeriods:{month:"[data-period='month']"}}});
define([],function(){return{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]"},calendarPeriods:{month:"[data-period='month']"}}});

View File

@ -1 +1 @@
define(["jquery","core/templates","core/notification","core_calendar/repository","core_calendar/events"],function(a,b,c,d,e){var f={ROOT:"[data-region='calendar']",CALENDAR_NAV_LINK:".calendarwrapper .arrow_link",CALENDAR_MONTH_WRAPPER:".calendarwrapper",LOADING_ICON_CONTAINER:'[data-region="overlay-icon-container"]'},g=function(b){b=a(b),b.on("click",f.CALENDAR_NAV_LINK,function(c){var d=a(b).find(f.CALENDAR_MONTH_WRAPPER).data("courseid"),e=a(c.currentTarget);i(b,e.attr("href"),e.data("year"),e.data("month"),d),c.preventDefault()})},h=function(g,h,i,j,m){k(g),m=m||g.find(f.CALENDAR_MONTH_WRAPPER),M.util.js_pending([g.get("id"),h,i,j].join("-"));var n=g.data("includenavigation");return d.getCalendarMonthData(h,i,j,n).then(function(a){return b.render(g.attr("data-template"),a)}).then(function(a,c){return b.replaceNode(m,a,c)}).then(function(){a("body").trigger(e.viewUpdated)}).always(function(){return M.util.js_complete([g.get("id"),h,i,j].join("-")),l(g)}).fail(c.exception)},i=function(b,c,d,f,g){return h(b,d,f,g).then(function(){return c.length&&"#"!==c&&window.history.pushState({},"",c),arguments}).then(function(){return a("body").trigger(e.monthChanged,[d,f,g]),arguments})},j=function(a,b){var c=a.find(f.CALENDAR_MONTH_WRAPPER).data("year"),d=a.find(f.CALENDAR_MONTH_WRAPPER).data("month");return b||(b=a.find(f.CALENDAR_MONTH_WRAPPER).data("courseid")),h(a,c,d,b)},k=function(a){var b=a.find(f.LOADING_ICON_CONTAINER);b.removeClass("hidden")},l=function(a){var b=a.find(f.LOADING_ICON_CONTAINER);b.addClass("hidden")};return{init:function(a){g(a)},reloadCurrentMonth:j,changeMonth:i,refreshMonthContent:h}});
define(["jquery","core/templates","core/notification","core_calendar/repository","core_calendar/events"],function(a,b,c,d,e){var f={ROOT:"[data-region='calendar']",CALENDAR_NAV_LINK:".calendarwrapper .arrow_link",CALENDAR_MONTH_WRAPPER:".calendarwrapper",LOADING_ICON_CONTAINER:'[data-region="overlay-icon-container"]'},g=function(b){b=a(b),b.on("click",f.CALENDAR_NAV_LINK,function(c){var d=b.find(f.CALENDAR_MONTH_WRAPPER),e=d.data("courseid"),g=d.data("categoryid"),h=a(c.currentTarget);i(b,h.attr("href"),h.data("year"),h.data("month"),e,g),c.preventDefault()})},h=function(g,h,i,j,m,n){k(g),n=n||g.find(f.CALENDAR_MONTH_WRAPPER),M.util.js_pending([g.get("id"),h,i,j].join("-"));var o=g.data("includenavigation");return d.getCalendarMonthData(h,i,j,m,o).then(function(a){return b.render(g.attr("data-template"),a)}).then(function(a,c){return b.replaceNode(n,a,c)}).then(function(){a("body").trigger(e.viewUpdated)}).always(function(){return M.util.js_complete([g.get("id"),h,i,j].join("-")),l(g)}).fail(c.exception)},i=function(b,c,d,f,g,i){return h(b,d,f,g,i).then(function(){return c.length&&"#"!==c&&window.history.pushState({},"",c),arguments}).then(function(){return a("body").trigger(e.monthChanged,[d,f,g,i]),arguments})},j=function(a,b,c){var d=a.find(f.CALENDAR_MONTH_WRAPPER).data("year"),e=a.find(f.CALENDAR_MONTH_WRAPPER).data("month");return"undefined"==typeof b&&(b=a.find(f.CALENDAR_MONTH_WRAPPER).data("courseid")),"undefined"==typeof c&&(c=a.find(f.CALENDAR_MONTH_WRAPPER).data("categoryid")),h(a,d,e,b,c)},k=function(a){var b=a.find(f.LOADING_ICON_CONTAINER);b.removeClass("hidden")},l=function(a){var b=a.find(f.LOADING_ICON_CONTAINER);b.addClass("hidden")};return{init:function(a){g(a)},reloadCurrentMonth:j,changeMonth:i,refreshMonthContent:h}});

View File

@ -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);

View File

@ -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() {

View File

@ -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,
}
};

View File

@ -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);
};
/**

View File

@ -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 {

View File

@ -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);

View File

@ -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]);

View File

@ -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();

View File

@ -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;
}

View File

@ -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) {

View File

@ -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');

View File

@ -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,
]);
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];
} else {
$course = get_site();
$courses = calendar_get_default_courses();
}
// 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();
}
}
$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',

View File

@ -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;

View File

@ -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}}

View File

@ -33,7 +33,8 @@
}}
<div{{!
}} class="calendarwrapper"{{!
}} data-courseid="{{courseid}}"{{!
}}{{#courseid}} data-courseid="{{courseid}}"{{/courseid}}{{!
}}{{#categoryid}} data-categoryid="{{categoryid}}"{{/categoryid}}{{!
}} data-month="{{date.mon}}"{{!
}} data-year="{{date.year}}"{{!
}}>

View File

@ -35,6 +35,7 @@
}} id="month-mini-{{date.year}}-{{date.month}}-{{uniqid}}"{{!
}} class="calendarwrapper"{{!
}} data-courseid="{{courseid}}"{{!
}} data-categoryid="{{categoryid}}"{{!
}} data-month="{{date.mon}}"{{!
}} data-year="{{date.year}}"{{!
}}>

View File

@ -49,6 +49,7 @@ require_once('../config.php');
require_once($CFG->dirroot.'/course/lib.php');
require_once($CFG->dirroot.'/calendar/lib.php');
$categoryid = optional_param('category', null, PARAM_INT);
$courseid = optional_param('course', SITEID, PARAM_INT);
$view = optional_param('view', 'upcoming', PARAM_ALPHA);
$time = optional_param('time', 0, PARAM_INT);
@ -63,6 +64,10 @@ if ($courseid != SITEID) {
$url->param('course', $courseid);
}
if ($categoryid) {
$url->param('categoryid', $categoryid);
}
if ($view !== 'upcoming') {
$time = usergetmidnight($time);
$url->param('view', $view);
@ -76,18 +81,30 @@ if ($courseid != SITEID && !empty($courseid)) {
// Course ID must be valid and existing.
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
$courses = array($course->id => $course);
$issite = false;
navigation_node::override_active_url(new moodle_url('/course/view.php', array('id' => $course->id)));
} else {
$course = get_site();
$courses = calendar_get_default_courses();
$issite = true;
if ($categoryid) {
$PAGE->set_category_by_id($categoryid);
} else {
$PAGE->set_context(context_system::instance());
}
if ($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($PAGE->categories);
$courses = array_filter($courses, function($course) use ($categories) {
return array_search($course->category, $categories) !== false;
});
navigation_node::override_active_url(new moodle_url('/course/index.php', array('categoryid' => $categoryid)));
}
}
require_login($course, false);
$calendar = new calendar_information(0, 0, 0, $time);
$calendar->prepare_for_view($course, $courses);
$calendar->set_sources($course, $courses, $PAGE->category);
$pagetitle = '';

View File

@ -32,6 +32,7 @@ $string['calendarheading'] = '{$a} Calendar';
$string['calendarpreferences'] = 'Calendar preferences';
$string['calendartypes'] = 'Calendar types';
$string['calendarurl'] = 'Calendar URL: {$a}';
$string['categoryevent'] = 'Category event';
$string['clickhide'] = 'click to hide';
$string['clickshow'] = 'click to show';
$string['colcalendar'] = 'Calendar';

View File

@ -203,6 +203,7 @@ class icon_system_fontawesome extends icon_system_font {
'core:i/completion_self' => 'fa-user-o',
'core:i/dashboard' => 'fa-tachometer',
'core:i/lock' => 'fa-lock',
'core:i/categoryevent' => 'fa-users',
'core:i/courseevent' => 'fa-calendar',
'core:i/db' => 'fa-database',
'core:i/delete' => 'fa-trash',

3
pix/i/categoryevent.svg Normal file
View File

@ -0,0 +1,3 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M8 4c1.6 0 2.9 1.7 2.9 3.8 0 1.4-.9 2.5-.9 2.5-.3.4-.2 1 .3 1.2l3.6 1.7c.5.3 1.1.9 1.1 1.5V16H1v-1.2c0-.5.6-1.2 1.1-1.5l3.6-1.8c.5-.3.6-.8.3-1.3 0 0-.9-1.1-.9-2.5C5.1 5.7 6.4 4 8 4zm3.9 3.8c0 1.1-.4 2.1-.4 2.1-.2.5 0 1.1.5 1.4l1.5.7H16V8.4l-1.7-.8c-.5-.3-.6-.8-.3-1.2 0 0 .9-1.1.9-2.5C14.9 1.7 13.6 0 12 0c-1.5 0-2.6 1.4-2.8 3.2 1.5.7 2.7 2.4 2.7 4.6zM1.7 7.6L0 8.4V12h2.6l1.4-.7c.5-.3.8-.9.5-1.4 0 0-.4-1-.4-2.1 0-2.1 1.2-3.9 2.8-4.6C6.6 1.4 5.5 0 4 0 2.4 0 1.1 1.7 1.1 3.8c0 1.4.9 2.5.9 2.5.3.4.2 1-.3 1.3z" fill="#999"/></svg>

After

Width:  |  Height:  |  Size: 833 B

View File

@ -1,12 +1,16 @@
/* calendar.less */
// Calendar colour variables defined.
$calendarEventCategoryColor: #d8bfd8 !default; // Pale purple.
$calendarEventCourseColor: #ffd3bd !default; // Pale red.
$calendarEventGlobalColor: #d6f8cd !default; // Pale green.
$calendarEventGroupColor: #fee7ae !default; // Pale yellow.
$calendarEventUserColor: #dce7ec !default; // Pale blue.
// Calendar event background colours defined.
.calendar_event_category {
background-color: $calendarEventCategoryColor;
}
.calendar_event_course {
background-color: $calendarEventCourseColor;
}
@ -129,6 +133,7 @@ $calendarEventUserColor: #dce7ec !default; // Pale blue.
margin: 10px auto;
}
.calendar_event_category,
.calendar_event_course,
.calendar_event_site,
.calendar_event_group,
@ -141,6 +146,9 @@ $calendarEventUserColor: #dce7ec !default; // Pale blue.
}
}
.calendar_event_category {
border-color: $calendarEventCategoryColor;
}
.calendar_event_course {
border-color: $calendarEventCourseColor;
}
@ -231,6 +239,15 @@ $calendarEventUserColor: #dce7ec !default; // Pale blue.
}
}
&.duration_category {
border-top: 1px solid $calendarEventCategoryColor;
border-bottom: 1px solid $calendarEventCategoryColor;
&.duration_finish {
background-color: $calendarEventCategoryColor;
}
}
&.duration_course {
border-top: 1px solid $calendarEventCourseColor;
border-bottom: 1px solid $calendarEventCourseColor;

View File

@ -1,12 +1,16 @@
/* calendar.less */
// Calendar colour variables defined.
@calendarEventCategoryColor: #d8bfd8; // Pale purple.
@calendarEventCourseColor: #ffd3bd; // Pale red.
@calendarEventGlobalColor: #d6f8cd; // Pale green.
@calendarEventGroupColor: #fee7ae; // Pale yellow.
@calendarEventUserColor: #dce7ec; // Pale blue.
// Calendar event background colours defined.
.calendar_event_category {
background-color: @calendarEventCategoryColor;
}
.calendar_event_course {
background-color: @calendarEventCourseColor;
}
@ -110,6 +114,7 @@
width: 98%;
margin: 10px auto;
}
.calendar_event_category,
.calendar_event_course,
.calendar_event_site,
.calendar_event_group,
@ -121,6 +126,9 @@
}
}
}
.calendar_event_category {
border-color: @calendarEventCategoryColor;
}
.calendar_event_course {
border-color: @calendarEventCourseColor;
}
@ -244,6 +252,13 @@
background-color: @calendarEventGlobalColor;
}
}
&.duration_category {
border-top: 1px solid @calendarEventCategoryColor;
border-bottom: 1px solid @calendarEventCategoryColor;
&.duration_finish {
background-color: @calendarEventCategoryColor;
}
}
&.duration_course {
border-top: 1px solid @calendarEventCourseColor;
border-bottom: 1px solid @calendarEventCourseColor;

View File

@ -5563,6 +5563,9 @@ img.iconsmall {
background-color: #fcf8e3;
}
/* calendar.less */
.calendar_event_category {
background-color: #d8bfd8;
}
.calendar_event_course {
background-color: #ffd3bd;
}
@ -5655,6 +5658,7 @@ img.iconsmall {
width: 98%;
margin: 10px auto;
}
.path-calendar .maincalendar .calendar_event_category:hover a,
.path-calendar .maincalendar .calendar_event_course:hover a,
.path-calendar .maincalendar .calendar_event_site:hover a,
.path-calendar .maincalendar .calendar_event_group:hover a,
@ -5662,6 +5666,9 @@ img.iconsmall {
color: #003d5c;
text-decoration: underline;
}
.path-calendar .maincalendar .calendar_event_category {
border-color: #d8bfd8;
}
.path-calendar .maincalendar .calendar_event_course {
border-color: #ffd3bd;
}
@ -5768,6 +5775,13 @@ img.iconsmall {
.block .minicalendar td.duration_global.duration_finish {
background-color: #d6f8cd;
}
.block .minicalendar td.duration_category {
border-top: 1px solid #d8bfd8;
border-bottom: 1px solid #d8bfd8;
}
.block .minicalendar td.duration_category.duration_finish {
background-color: #d8bfd8;
}
.block .minicalendar td.duration_course {
border-top: 1px solid #ffd3bd;
border-bottom: 1px solid #ffd3bd;