From d0e56d847a13880b0cefcff995abe77222a4c8fe Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 19 Sep 2017 11:17:04 +0800 Subject: [PATCH] MDL-59890 calendar: Display category events on calendars --- .../calendar_month/block_calendar_month.php | 15 ++++++++- calendar/amd/build/calendar.min.js | 2 +- calendar/amd/build/calendar_threemonth.min.js | 2 +- calendar/amd/build/repository.min.js | 2 +- calendar/amd/build/selectors.min.js | 2 +- calendar/amd/build/view_manager.min.js | 2 +- calendar/amd/src/calendar.js | 2 +- calendar/amd/src/calendar_threemonth.js | 13 +++++--- calendar/amd/src/repository.js | 6 ++-- calendar/amd/src/view_manager.js | 33 ++++++++++++------- .../external/calendar_event_exporter.php | 2 ++ calendar/classes/external/event_exporter.php | 13 -------- .../classes/external/event_exporter_base.php | 6 ++++ .../classes/external/event_icon_exporter.php | 17 +++++++--- calendar/classes/external/month_exporter.php | 9 +++++ calendar/event.php | 2 +- calendar/export.php | 2 +- calendar/externallib.php | 31 +++++++++++++---- calendar/lib.php | 2 +- .../templates/event_summary_body.mustache | 3 ++ calendar/templates/month_detailed.mustache | 3 +- calendar/templates/month_mini.mustache | 1 + calendar/view.php | 23 +++++++++++-- lang/en/calendar.php | 1 + .../output/icon_system_fontawesome.php | 1 + pix/i/categoryevent.svg | 3 ++ theme/boost/scss/moodle/calendar.scss | 17 ++++++++++ theme/bootstrapbase/less/moodle/calendar.less | 15 +++++++++ theme/bootstrapbase/style/moodle.css | 14 ++++++++ 29 files changed, 189 insertions(+), 55 deletions(-) create mode 100644 pix/i/categoryevent.svg diff --git a/blocks/calendar_month/block_calendar_month.php b/blocks/calendar_month/block_calendar_month.php index 6833188c997ca..082579809e4e7 100644 --- a/blocks/calendar_month/block_calendar_month.php +++ b/blocks/calendar_month/block_calendar_month.php @@ -51,11 +51,24 @@ public function get_content() { $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 @@ public function get_content() { $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); diff --git a/calendar/amd/build/calendar.min.js b/calendar/amd/build/calendar.min.js index a1c8e36ee517f..4778e33b726ae 100644 --- a/calendar/amd/build/calendar.min.js +++ b/calendar/amd/build/calendar.min.js @@ -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)}}}); \ No newline at end of file +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)}}}); \ No newline at end of file diff --git a/calendar/amd/build/calendar_threemonth.min.js b/calendar/amd/build/calendar_threemonth.min.js index ac1bb8cce4b2f..f0300b1d2b115 100644 --- a/calendar/amd/build/calendar_threemonth.min.js +++ b/calendar/amd/build/calendar_threemonth.min.js @@ -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("");n.attr("data-template","core_calendar/threemonth_month"),n.attr("data-includenavigation",!1);var o=a("
");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)}}}); \ No newline at end of file +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("");o.attr("data-template","core_calendar/threemonth_month"),o.attr("data-includenavigation",!1);var p=a("
");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)}}}); \ No newline at end of file diff --git a/calendar/amd/build/repository.min.js b/calendar/amd/build/repository.min.js index c970b3f7a6ce9..57dbe976851dc 100644 --- a/calendar/amd/build/repository.min.js +++ b/calendar/amd/build/repository.min.js @@ -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}}); \ No newline at end of file +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}}); \ No newline at end of file diff --git a/calendar/amd/build/selectors.min.js b/calendar/amd/build/selectors.min.js index a81f332e0c2ac..ed31fd7e78cf6 100644 --- a/calendar/amd/build/selectors.min.js +++ b/calendar/amd/build/selectors.min.js @@ -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']"}}}); \ No newline at end of file +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']"}}}); \ No newline at end of file diff --git a/calendar/amd/build/view_manager.min.js b/calendar/amd/build/view_manager.min.js index 61f7942b88cdb..04dc7f6196b27 100644 --- a/calendar/amd/build/view_manager.min.js +++ b/calendar/amd/build/view_manager.min.js @@ -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}}); \ No newline at end of file +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}}); \ No newline at end of file diff --git a/calendar/amd/src/calendar.js b/calendar/amd/src/calendar.js index 0c436cc0a6a09..90213bc486808 100644 --- a/calendar/amd/src/calendar.js +++ b/calendar/amd/src/calendar.js @@ -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); diff --git a/calendar/amd/src/calendar_threemonth.js b/calendar/amd/src/calendar_threemonth.js index a043887dca363..3743d4b9e9007 100644 --- a/calendar/amd/src/calendar_threemonth.js +++ b/calendar/amd/src/calendar_threemonth.js @@ -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() { diff --git a/calendar/amd/src/repository.js b/calendar/amd/src/repository.js index 5e1321ced3654..0b59073c3ae14 100644 --- a/calendar/amd/src/repository.js +++ b/calendar/amd/src/repository.js @@ -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, } }; diff --git a/calendar/amd/src/view_manager.js b/calendar/amd/src/view_manager.js index 0934a510161c9..1ed771aaa8915 100644 --- a/calendar/amd/src/view_manager.js +++ b/calendar/amd/src/view_manager.js @@ -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); + }; /** diff --git a/calendar/classes/external/calendar_event_exporter.php b/calendar/classes/external/calendar_event_exporter.php index c27e3647a1a08..423cd92cc7505 100644 --- a/calendar/classes/external/calendar_event_exporter.php +++ b/calendar/classes/external/calendar_event_exporter.php @@ -82,6 +82,8 @@ protected function get_other_values(renderer_base $output) { $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 { diff --git a/calendar/classes/external/event_exporter.php b/calendar/classes/external/event_exporter.php index d687eb1b191b5..5ebfdcef957b4 100644 --- a/calendar/classes/external/event_exporter.php +++ b/calendar/classes/external/event_exporter.php @@ -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 @@ protected function get_other_values(renderer_base $output) { $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 @@ protected function get_other_values(renderer_base $output) { $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); diff --git a/calendar/classes/external/event_exporter_base.php b/calendar/classes/external/event_exporter_base.php index 9af9ce2139b16..85457e2d05a15 100644 --- a/calendar/classes/external/event_exporter_base.php +++ b/calendar/classes/external/event_exporter_base.php @@ -217,6 +217,9 @@ protected static function define_other_properties() { 'iscourseevent' => [ 'type' => PARAM_BOOL ], + 'iscategoryevent' => [ + 'type' => PARAM_BOOL + ], 'groupname' => [ 'type' => PARAM_RAW, 'optional' => true, @@ -239,10 +242,13 @@ protected function get_other_values(renderer_base $output) { $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]); diff --git a/calendar/classes/external/event_icon_exporter.php b/calendar/classes/external/event_icon_exporter.php index 5f4bd4dc29ed2..79d30daab2648 100644 --- a/calendar/classes/external/event_icon_exporter.php +++ b/calendar/classes/external/event_icon_exporter.php @@ -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 @@ public function __construct(event_interface $event, $related = []) { $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 @@ public function __construct(event_interface $event, $related = []) { } 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(); diff --git a/calendar/classes/external/month_exporter.php b/calendar/classes/external/month_exporter.php index 3fff7fe22b7f6..e67c8431d1002 100644 --- a/calendar/classes/external/month_exporter.php +++ b/calendar/classes/external/month_exporter.php @@ -108,6 +108,11 @@ protected static function define_other_properties() { 'courseid' => [ 'type' => PARAM_INT, ], + 'categoryid' => [ + 'type' => PARAM_INT, + 'optional' => true, + 'default' => 0, + ], 'filter_selector' => [ 'type' => PARAM_RAW, ], @@ -211,6 +216,10 @@ protected function get_other_values(renderer_base $output) { $return['defaulteventcontext'] = $context->id; } + if ($this->calendar->categoryid) { + $return['categoryid'] = $this->calendar->categoryid; + } + return $return; } diff --git a/calendar/event.php b/calendar/event.php index dad5fd5815954..9eb3708bdfcfe 100644 --- a/calendar/event.php +++ b/calendar/event.php @@ -108,7 +108,7 @@ } $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) { diff --git a/calendar/export.php b/calendar/export.php index 04e6cf39a1ceb..89ca1e0b401e9 100644 --- a/calendar/export.php +++ b/calendar/export.php @@ -100,7 +100,7 @@ $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'); diff --git a/calendar/externallib.php b/calendar/externallib.php index 336c0f0916ff5..35bc6e738d4f8 100644 --- a/calendar/externallib.php +++ b/calendar/externallib.php @@ -882,10 +882,11 @@ public static function submit_create_update_form_returns() { * @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 @@ public static function get_calendar_monthly_view($year, $month, $courseid, $incl '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 @@ public static function get_calendar_monthly_view_parameters() { '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', diff --git a/calendar/lib.php b/calendar/lib.php index 4d86ee291435d..68bdc5b73c8b8 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -1059,7 +1059,7 @@ public function set_sources(stdClass $course, array $courses, stdClass $category $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; diff --git a/calendar/templates/event_summary_body.mustache b/calendar/templates/event_summary_body.mustache index b139b692dd395..e5b646f84aeaa 100644 --- a/calendar/templates/event_summary_body.mustache +++ b/calendar/templates/event_summary_body.mustache @@ -46,6 +46,9 @@ {{/description}}

{{#str}} eventtype, core_calendar {{/str}}

{{eventtype}} + {{#iscategoryevent}} +
{{{category.nestedname}}}
+ {{/iscategoryevent}} {{#iscourseevent}} {{/iscourseevent}} diff --git a/calendar/templates/month_detailed.mustache b/calendar/templates/month_detailed.mustache index baa901345f1ff..8d1e49552ed45 100644 --- a/calendar/templates/month_detailed.mustache +++ b/calendar/templates/month_detailed.mustache @@ -33,7 +33,8 @@ }} diff --git a/calendar/templates/month_mini.mustache b/calendar/templates/month_mini.mustache index bc180c7802f4f..6f6773fd215b6 100644 --- a/calendar/templates/month_mini.mustache +++ b/calendar/templates/month_mini.mustache @@ -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}}"{{! }}> diff --git a/calendar/view.php b/calendar/view.php index fe6a82eae065a..9265b19738b79 100644 --- a/calendar/view.php +++ b/calendar/view.php @@ -49,6 +49,7 @@ 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 @@ $url->param('course', $courseid); } +if ($categoryid) { + $url->param('categoryid', $categoryid); +} + if ($view !== 'upcoming') { $time = usergetmidnight($time); $url->param('view', $view); @@ -76,18 +81,30 @@ // 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 = ''; diff --git a/lang/en/calendar.php b/lang/en/calendar.php index 7401c9d85306d..3572a91a5e562 100644 --- a/lang/en/calendar.php +++ b/lang/en/calendar.php @@ -32,6 +32,7 @@ $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'; diff --git a/lib/classes/output/icon_system_fontawesome.php b/lib/classes/output/icon_system_fontawesome.php index b1085cac9436f..b3f326b1a0a38 100644 --- a/lib/classes/output/icon_system_fontawesome.php +++ b/lib/classes/output/icon_system_fontawesome.php @@ -203,6 +203,7 @@ public function get_core_icon_map() { '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', diff --git a/pix/i/categoryevent.svg b/pix/i/categoryevent.svg new file mode 100644 index 0000000000000..b511f8ffc3d5b --- /dev/null +++ b/pix/i/categoryevent.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/theme/boost/scss/moodle/calendar.scss b/theme/boost/scss/moodle/calendar.scss index b13af94c9bdbd..38d1e95277518 100644 --- a/theme/boost/scss/moodle/calendar.scss +++ b/theme/boost/scss/moodle/calendar.scss @@ -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; diff --git a/theme/bootstrapbase/less/moodle/calendar.less b/theme/bootstrapbase/less/moodle/calendar.less index 72fb4a4840258..19fc8a434b164 100644 --- a/theme/bootstrapbase/less/moodle/calendar.less +++ b/theme/bootstrapbase/less/moodle/calendar.less @@ -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; diff --git a/theme/bootstrapbase/style/moodle.css b/theme/bootstrapbase/style/moodle.css index f8141957a2267..e178774d2dab3 100644 --- a/theme/bootstrapbase/style/moodle.css +++ b/theme/bootstrapbase/style/moodle.css @@ -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;