diff --git a/blocks/calendar_month/block_calendar_month.php b/blocks/calendar_month/block_calendar_month.php index cc3d6f69f61..18a57d72e13 100644 --- a/blocks/calendar_month/block_calendar_month.php +++ b/blocks/calendar_month/block_calendar_month.php @@ -70,29 +70,26 @@ class block_calendar_month extends block_base { 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. - $filtercourse = calendar_get_default_courses(); + $course = get_site(); + $courses = calendar_get_default_courses(); } else { // Forcibly filter events to include only those from the particular course we are in. - $filtercourse = array($courseid => $this->page->course); + $course = $this->page->course; + $courses = [$course->id => $course]; } - list($courses, $group, $user) = calendar_set_filters($filtercourse); - if ($issite) { - // For the front page. - $this->content->text .= calendar_get_mini($courses, $group, $user, false, false, - 'frontpage', $courseid, $time); - // No filters for now. - } else { - // For any other course. - $this->content->text .= calendar_get_mini($courses, $group, $user, false, false, - 'course', $courseid, $time); - $this->content->text .= '

'.get_string('eventskey', 'calendar').'

'; - $this->content->text .= '
' . - calendar_filter_controls($this->page->url) . '
'; + $renderer = $this->page->get_renderer('core_calendar'); + + $calendar = new calendar_information(0, 0, 0, $time); + $calendar->prepare_for_view($course, $courses); + + list($data, $template) = calendar_get_view($calendar, 'mini'); + $this->content->text .= $renderer->render_from_template($template, $data); + + if (!$issite) { + $this->content->text .= $renderer->event_filter(); } return $this->content; } } - - diff --git a/blocks/calendar_month/tests/behat/block_calendar_month.feature b/blocks/calendar_month/tests/behat/block_calendar_month.feature index 8753167f63b..86baea3b524 100644 --- a/blocks/calendar_month/tests/behat/block_calendar_month.feature +++ b/blocks/calendar_month/tests/behat/block_calendar_month.feature @@ -23,7 +23,7 @@ Feature: Enable the calendar block in a course and test it's functionality Given I log in as "teacher1" And I am on "Course 1" course homepage with editing mode on When I add the "Calendar" block - Then I should see "Events key" in the "Calendar" "block" + Then "Calendar" "block" should exist @javascript Scenario: View a global event in the calendar block @@ -136,9 +136,14 @@ Feature: Enable the calendar block in a course and test it's functionality And I press "Save and display" And I turn editing mode on And I add the "Calendar" block - And I create a calendar event with form data: + And I click on "This month" "link" + And I click on "New event" "button" + And I set the following fields to these values: | id_eventtype | Group | | id_name | Group Event | + And I set the following fields to these values: + | Group | Group 1 | + And I press "Save" And I log out Then I log in as "student1" And I am on "Course 1" course homepage @@ -150,7 +155,7 @@ Feature: Enable the calendar block in a course and test it's functionality And I hover over today in the calendar And I should not see "Group Event" - @javascript + @javascript @arn Scenario: Filter group events in the calendar block Given the following "groups" exist: | name | course | idnumber | @@ -173,9 +178,14 @@ Feature: Enable the calendar block in a course and test it's functionality | id_eventtype | Course | | id_name | Course Event 1 | And I am on "Course 1" course homepage - And I create a calendar event with form data: + And I click on "This month" "link" + And I click on "New event" "button" + And I set the following fields to these values: | id_eventtype | Group | | id_name | Group Event 1 | + And I set the following fields to these values: + | Group | Group 1 | + And I press "Save" And I log out Then I log in as "student1" And I am on "Course 1" course homepage diff --git a/calendar/amd/build/calendar.min.js b/calendar/amd/build/calendar.min.js index aac1c29032b..19997029788 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"]'},o=function(a){var b="type"+a;return c.get_string(b,"core_calendar").then(function(a){return a})},p=function(a){return c.get_string("subsource","core_calendar",a).then(function(b){return a.url?''+b+"":b})},q=function(b){k.getEventById(b).then(function(c){if(!c.event)throw new Error("Error encountered while trying to fetch calendar event with ID: "+b);var d=c.event,e=o(d.eventtype);if(d.displayeventsource){d.subscription=JSON.parse(d.subscription);var f={url:d.subscription.url,name:d.subscription.name},g=p(f);return a.when(e,g).then(function(a,b){return d.eventtype=a,d.source=b,d})}return e.then(function(a){return d.eventtype=a,d})}).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)},r=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)},s=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}},[a,n.NEW_EVENT_BUTTON])},t=function(b,c){var d=a("body"),e=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,r),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(e)})},u=function(){var b=a(n.ROOT);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"),q(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=s(b);t(b,c),b.on("click",n.DAY,function(b){var d=a(b.target);if(!d.is(n.VIEW_DAY_LINK)){var e=a(this).attr("data-new-event-timestamp");c.then(function(a){a.setStartTime(e),a.show()}),b.preventDefault()}})};return{init:function(){m.init(),u()}}}); \ 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"]'},o=function(a){var b="type"+a;return c.get_string(b,"core_calendar").then(function(a){return a})},p=function(a){return c.get_string("subsource","core_calendar",a).then(function(b){return a.url?''+b+"":b})},q=function(b){k.getEventById(b).then(function(c){if(!c.event)throw new Error("Error encountered while trying to fetch calendar event with ID: "+b);var d=c.event,e=o(d.eventtype);if(d.displayeventsource){d.subscription=JSON.parse(d.subscription);var f={url:d.subscription.url,name:d.subscription.name},g=p(f);return a.when(e,g).then(function(a,b){return d.eventtype=a,d.source=b,d})}return e.then(function(a){return d.eventtype=a,d})}).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)},r=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)},s=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}},[a,n.NEW_EVENT_BUTTON])},t=function(b,c){var d=a("body"),e=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,r),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(e)})},u=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"),q(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=s(b);t(b,c),b.on("click",n.DAY,function(b){var d=a(b.target);if(!d.is(n.VIEW_DAY_LINK)){var e=a(this).attr("data-new-event-timestamp");c.then(function(a){a.setStartTime(e),a.show()}),b.preventDefault()}})};return{init:function(b){b=a(b),m.init(b),u(b)}}}); \ No newline at end of file diff --git a/calendar/amd/build/calendar_filter.min.js b/calendar/amd/build/calendar_filter.min.js new file mode 100644 index 00000000000..ef94d150844 --- /dev/null +++ b/calendar/amd/build/calendar_filter.min.js @@ -0,0 +1 @@ +define(["jquery","core_calendar/selectors","core_calendar/events","core/str","core/templates"],function(a,b,c,d,e){var f=function(d){d.on("click",b.eventFilterItem,function(b){var c=a(b.currentTarget);g(c),b.preventDefault()}),a("body").on(c.viewUpdated,function(){var c=d.find(b.eventFilterItem);c.each(function(b,c){if(c=a(c),c.data("eventtype-hidden")){var d=i(c);h(d)}})})},g=function(a){var b=i(a);return b.hidden=!b.hidden,d.get_string("eventtype"+b.type,"calendar").then(function(a){return b.name=a,b}).then(function(a){return e.render("core_calendar/event_filter_key",a)}).then(function(b,c){return e.replaceNode(a,b,c)}).then(function(){h(b)})},h=function(b){M.util.js_pending("month-mini-filterChanged"),a("body").trigger(c.filterChanged,{type:b.type,hidden:b.hidden}),M.util.js_complete("month-mini-filterChanged")},i=function(a){return{type:a.data("eventtype"),hidden:a.data("eventtype-hidden")}};return{init:function(b){b=a(b),f(b)}}}); \ No newline at end of file diff --git a/calendar/amd/build/calendar_mini.min.js b/calendar/amd/build/calendar_mini.min.js new file mode 100644 index 00000000000..979e92e5ebd --- /dev/null +++ b/calendar/amd/build/calendar_mini.min.js @@ -0,0 +1 @@ +define(["jquery","core_calendar/selectors","core_calendar/events","core_calendar/view_manager"],function(a,b,c,d){var e=function(d){a("body").on(c.filterChanged,function(a,c){var e=d.find(b.eventType[c.type]);e.toggleClass("calendar_event_"+c.type,!c.hidden)})};return{init:function(b){b=a(b),d.init(b),e(b)}}}); \ No newline at end of file diff --git a/calendar/amd/build/events.min.js b/calendar/amd/build/events.min.js index 762a824034c..77b21536776 100644 --- a/calendar/amd/build/events.min.js +++ b/calendar/amd/build/events.min.js @@ -1 +1 @@ -define([],function(){return{created:"calendar-events:created",deleted:"calendar-events:deleted",updated:"calendar-events:updated",editEvent:"calendar-events:edit_event",editActionEvent:"calendar-events:edit_action_event",eventMoved:"calendar-events:event_moved",monthChanged:"calendar-events:month_changed",moveEvent:"calendar-events:move_event"}}); \ No newline at end of file +define([],function(){return{created:"calendar-events:created",deleted:"calendar-events:deleted",updated:"calendar-events:updated",editEvent:"calendar-events:edit_event",editActionEvent:"calendar-events:edit_action_event",eventMoved:"calendar-events:event_moved",monthChanged:"calendar-events:month_changed",moveEvent:"calendar-events:move_event",filterChanged:"calendar-events:filter_changed",viewUpdated:"calendar-events:view_updated"}}); \ No newline at end of file diff --git a/calendar/amd/build/selectors.min.js b/calendar/amd/build/selectors.min.js new file mode 100644 index 00000000000..8dbeb185f37 --- /dev/null +++ b/calendar/amd/build/selectors.min.js @@ -0,0 +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]"}}}); \ 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 d50282461f1..301ef420904 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(e.attr("href"),e.data("time"),d),c.preventDefault()})},h=function(e,g){var h=a(f.ROOT);return k(h),d.getCalendarMonthData(e,g).then(function(a){return b.render("core_calendar/month_detailed",a)}).then(function(a,c){return b.replaceNode(f.CALENDAR_MONTH_WRAPPER,a,c)}).always(function(){return l(h)}).fail(c.exception)},i=function(b,c,d){return h(c,d).then(function(){return window.history.pushState({},"",b),arguments}).then(function(){return a("body").trigger(e.monthChanged,[c,d]),arguments})},j=function(a,b){var c=a.find(f.CALENDAR_MONTH_WRAPPER).data("current-time");return b||(b=a.find(f.CALENDAR_MONTH_WRAPPER).data("courseid")),h(c,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(){g(f.ROOT)},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=a(b).find(f.CALENDAR_MONTH_WRAPPER).data("courseid"),e=a(c.currentTarget);i(b,e.attr("href"),e.data("time"),d),c.preventDefault()})},h=function(g,h,i){return k(g),d.getCalendarMonthData(h,i).then(function(a){return b.render(g.attr("data-template"),a)}).then(function(a,c){return b.replaceNode(g.find(f.CALENDAR_MONTH_WRAPPER),a,c)}).then(function(){a("body").trigger(e.viewUpdated)}).always(function(){return l(g)}).fail(c.exception)},i=function(b,c,d,f){return h(b,d,f).then(function(){return c.length&&"#"!==c&&window.history.pushState({},"",c),arguments}).then(function(){return a("body").trigger(e.monthChanged,[d,f]),arguments})},j=function(a,b){var c=a.find(f.CALENDAR_MONTH_WRAPPER).data("current-time");return b||(b=a.find(f.CALENDAR_MONTH_WRAPPER).data("courseid")),h(a,c,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 diff --git a/calendar/amd/src/calendar.js b/calendar/amd/src/calendar.js index 947bac06cdb..2958c837534 100644 --- a/calendar/amd/src/calendar.js +++ b/calendar/amd/src/calendar.js @@ -296,9 +296,7 @@ define([ /** * Register event listeners for the module. */ - var registerEventListeners = function() { - var root = $(SELECTORS.ROOT); - + var registerEventListeners = function(root) { // Bind click events to event links. root.on('click', SELECTORS.EVENT_ITEM, function(e) { e.preventDefault(); @@ -350,9 +348,11 @@ define([ }; return { - init: function() { - CalendarViewManager.init(); - registerEventListeners(); + init: function(root) { + root = $(root); + + CalendarViewManager.init(root); + registerEventListeners(root); } }; }); diff --git a/calendar/amd/src/calendar_filter.js b/calendar/amd/src/calendar_filter.js new file mode 100644 index 00000000000..5352a7c038f --- /dev/null +++ b/calendar/amd/src/calendar_filter.js @@ -0,0 +1,119 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * This module is responsible for the calendar filter. + * + * @module core_calendar/calendar_filter + * @package core_calendar + * @copyright 2017 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define([ + 'jquery', + 'core_calendar/selectors', + 'core_calendar/events', + 'core/str', + 'core/templates', +], +function( + $, + CalendarSelectors, + CalendarEvents, + Str, + Templates +) { + + var registerEventListeners = function(root) { + root.on('click', CalendarSelectors.eventFilterItem, function(e) { + var target = $(e.currentTarget); + + toggleFilter(target); + + e.preventDefault(); + }); + + $('body').on(CalendarEvents.viewUpdated, function() { + var filters = root.find(CalendarSelectors.eventFilterItem); + + filters.each(function(i, filter) { + filter = $(filter); + if (filter.data('eventtype-hidden')) { + var data = getFilterData(filter); + fireFilterChangedEvent(data); + } + }); + }); + }; + + var toggleFilter = function(target) { + var data = getFilterData(target); + + // Toggle the hidden. We need to render the template before we change the value. + data.hidden = !data.hidden; + + return Str.get_string('eventtype' + data.type, 'calendar') + .then(function(nameStr) { + data.name = nameStr; + + return data; + }) + .then(function(context) { + return Templates.render('core_calendar/event_filter_key', context); + }) + .then(function(html, js) { + return Templates.replaceNode(target, html, js); + }) + .then(function() { + fireFilterChangedEvent(data); + return; + }); + }; + + /** + * Fire the filterChanged event for the specified data. + * + * @param {object} data The data to include + */ + var fireFilterChangedEvent = function(data) { + M.util.js_pending("month-mini-filterChanged"); + $('body').trigger(CalendarEvents.filterChanged, { + type: data.type, + hidden: data.hidden, + }); + M.util.js_complete("month-mini-filterChanged"); + }; + + /** + * Get the filter data for the specified target. + * + * @param {jQuery} target The target node + * @return {Object} + */ + var getFilterData = function(target) { + return { + type: target.data('eventtype'), + hidden: target.data('eventtype-hidden'), + }; + }; + + return { + init: function(root) { + root = $(root); + + registerEventListeners(root); + } + }; +}); diff --git a/calendar/amd/src/calendar_mini.js b/calendar/amd/src/calendar_mini.js new file mode 100644 index 00000000000..48a4f441e9d --- /dev/null +++ b/calendar/amd/src/calendar_mini.js @@ -0,0 +1,57 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * This module is the highest level module for the calendar. It is + * responsible for initialising all of the components required for + * the calendar to run. It also coordinates the interaction between + * components by listening for and responding to different events + * triggered within the calendar UI. + * + * @module core_calendar/calendar + * @package core_calendar + * @copyright 2017 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define([ + 'jquery', + 'core_calendar/selectors', + 'core_calendar/events', + 'core_calendar/view_manager', +], +function( + $, + CalendarSelectors, + CalendarEvents, + CalendarViewManager +) { + + var registerEventListeners = function(root) { + $('body').on(CalendarEvents.filterChanged, function(e, data) { + var daysWithEvent = root.find(CalendarSelectors.eventType[data.type]); + + daysWithEvent.toggleClass('calendar_event_' + data.type, !data.hidden); + }); + }; + + return { + init: function(root) { + root = $(root); + + CalendarViewManager.init(root); + registerEventListeners(root); + } + }; +}); diff --git a/calendar/amd/src/events.js b/calendar/amd/src/events.js index 95e0968d726..29b5fd263cb 100644 --- a/calendar/amd/src/events.js +++ b/calendar/amd/src/events.js @@ -31,6 +31,8 @@ define([], function() { editActionEvent: 'calendar-events:edit_action_event', eventMoved: 'calendar-events:event_moved', monthChanged: 'calendar-events:month_changed', - moveEvent: 'calendar-events:move_event' + moveEvent: 'calendar-events:move_event', + filterChanged: 'calendar-events:filter_changed', + viewUpdated: 'calendar-events:view_updated', }; }); diff --git a/calendar/amd/src/selectors.js b/calendar/amd/src/selectors.js new file mode 100644 index 00000000000..89bb65a5126 --- /dev/null +++ b/calendar/amd/src/selectors.js @@ -0,0 +1,40 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * This module is responsible for the calendar filter. + * + * @module core_calendar/calendar_selectors + * @package core_calendar + * @copyright 2017 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +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]", + }, + }; +}); diff --git a/calendar/amd/src/view_manager.js b/calendar/amd/src/view_manager.js index 5604d6eeb6d..f8636ef6049 100644 --- a/calendar/amd/src/view_manager.js +++ b/calendar/amd/src/view_manager.js @@ -42,7 +42,7 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito root.on('click', SELECTORS.CALENDAR_NAV_LINK, function(e) { var courseId = $(root).find(SELECTORS.CALENDAR_MONTH_WRAPPER).data('courseid'); var link = $(e.currentTarget); - changeMonth(link.attr('href'), link.data('time'), courseId); + changeMonth(root, link.attr('href'), link.data('time'), courseId); e.preventDefault(); }); @@ -55,17 +55,19 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito * @param {Number} courseid The id of the course whose events are shown * @return {promise} */ - var refreshMonthContent = function(time, courseid) { - var root = $(SELECTORS.ROOT); - + var refreshMonthContent = function(root, time, courseid) { startLoading(root); return CalendarRepository.getCalendarMonthData(time, courseid) .then(function(context) { - return Templates.render('core_calendar/month_detailed', context); + return Templates.render(root.attr('data-template'), context); }) .then(function(html, js) { - return Templates.replaceNode(SELECTORS.CALENDAR_MONTH_WRAPPER, html, js); + return Templates.replaceNode(root.find(SELECTORS.CALENDAR_MONTH_WRAPPER), html, js); + }) + .then(function() { + $('body').trigger(CalendarEvents.viewUpdated); + return; }) .always(function() { return stopLoading(root); @@ -81,10 +83,12 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito * @param {Number} courseid The id of the course whose events are shown * @return {promise} */ - var changeMonth = function(url, time, courseid) { - return refreshMonthContent(time, courseid) + var changeMonth = function(root, url, time, courseid) { + return refreshMonthContent(root, time, courseid) .then(function() { - window.history.pushState({}, '', url); + if (url.length && url !== '#') { + window.history.pushState({}, '', url); + } return arguments; }) .then(function() { @@ -106,7 +110,7 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito if (!courseId) { courseId = root.find(SELECTORS.CALENDAR_MONTH_WRAPPER).data('courseid'); } - return refreshMonthContent(time, courseId); + return refreshMonthContent(root, time, courseId); }; /** @@ -134,8 +138,8 @@ define(['jquery', 'core/templates', 'core/notification', 'core_calendar/reposito }; return { - init: function() { - registerEventListeners(SELECTORS.ROOT); + init: function(root) { + registerEventListeners(root); }, reloadCurrentMonth: reloadCurrentMonth, changeMonth: changeMonth, diff --git a/calendar/classes/external/calendar_event_exporter.php b/calendar/classes/external/calendar_event_exporter.php index 7ba5805ddf5..f83f2738267 100644 --- a/calendar/classes/external/calendar_event_exporter.php +++ b/calendar/classes/external/calendar_event_exporter.php @@ -47,6 +47,16 @@ class calendar_event_exporter extends event_exporter_base { $values = parent::define_other_properties(); $values['url'] = ['type' => PARAM_URL]; + $values['islastday'] = [ + 'type' => PARAM_BOOL, + 'default' => false, + ]; + $values['calendareventtype'] = [ + 'type' => PARAM_TEXT, + ]; + $values['popupname'] = [ + 'type' => PARAM_RAW, + ]; return $values; } @@ -58,6 +68,8 @@ class calendar_event_exporter extends event_exporter_base { * @return array Keys are the property names, values are their values. */ protected function get_other_values(renderer_base $output) { + global $CFG; + $values = parent::get_other_values($output); $eventid = $this->event->get_id(); @@ -65,6 +77,55 @@ class calendar_event_exporter extends event_exporter_base { $url = new \moodle_url($this->related['daylink'], [], "event_{$eventid}"); $values['url'] = $url->out(false); + $values['islastday'] = false; + $today = $this->related['type']->timestamp_to_date_array($this->related['today']); + + $values['popupname'] = $this->event->get_name(); + + $times = $this->event->get_times(); + if ($duration = $times->get_duration()) { + $enddate = $this->related['type']->timestamp_to_date_array($times->get_end_time()->getTimestamp()); + $values['islastday'] = true; + $values['islastday'] = $values['islastday'] && $enddate['year'] == $today['year']; + $values['islastday'] = $values['islastday'] && $enddate['mon'] == $today['mon']; + $values['islastday'] = $values['islastday'] && $enddate['mday'] == $today['mday']; + } + + $subscription = $this->event->get_subscription(); + if ($subscription && !empty($subscription->get('id')) && $CFG->calendar_showicalsource) { + $a = (object) [ + 'name' => $values['popupname'], + 'source' => $subscription->get('name'), + ]; + $values['popupname'] = get_string('namewithsource', 'calendar', $a); + } else { + if ($values['islastday']) { + $startdate = $this->related['type']->timestamp_to_date_array($times->get_start_time()->getTimestamp()); + $samedate = true; + $samedate = $samedate && $startdate['mon'] == $enddate['mon']; + $samedate = $samedate && $startdate['year'] == $enddate['year']; + $samedate = $samedate && $startdate['mday'] == $enddate['mday']; + + if (!$samedate) { + $values['popupname'] = get_string('eventendtimewrapped', 'calendar', $values['popupname']); + } + } + } + + // Include course's shortname into the event name, if applicable. + $course = $this->event->get_course(); + if ($course && $course->get('id') && $course->get('id') !== SITEID) { + $eventnameparams = (object) [ + 'name' => $values['popupname'], + 'course' => format_string($course->get('shortname'), true, [ + 'context' => $this->related['context'], + ]) + ]; + $values['popupname'] = get_string('eventnameandcourse', 'calendar', $eventnameparams); + } + + $values['calendareventtype'] = $this->get_calendar_event_type(); + return $values; } @@ -76,7 +137,24 @@ class calendar_event_exporter extends event_exporter_base { protected static function define_related() { $related = parent::define_related(); $related['daylink'] = \moodle_url::class; + $related['type'] = '\core_calendar\type_base'; + $related['today'] = 'int'; return $related; } + + /** + * Return the normalised event type. + * Activity events are normalised to be course events. + * + * @return string + */ + public function get_calendar_event_type() { + $type = $this->event->get_type(); + if ($type == 'open' || $type == 'close') { + $type = 'course'; + } + + return $type; + } } diff --git a/calendar/classes/external/day_exporter.php b/calendar/classes/external/day_exporter.php index 4a713bc0d9a..0f5b92b2bac 100644 --- a/calendar/classes/external/day_exporter.php +++ b/calendar/classes/external/day_exporter.php @@ -39,6 +39,24 @@ use moodle_url; */ class day_exporter extends exporter { + /** + * @var \calendar_information $calendar The calendar being displayed. + */ + protected $calendar; + + /** + * Constructor. + * + * @param \calendar_information $calendar The calendar information for the period being displayed + * @param mixed $data Either an stdClass or an array of values. + * @param array $related Related objects. + */ + public function __construct(\calendar_information $calendar, $data, $related) { + $this->calendar = $calendar; + + parent::__construct($data, $related); + } + /** * Return the list of properties. * @@ -69,6 +87,15 @@ class day_exporter extends exporter { 'yday' => [ 'type' => PARAM_INT, ], + // These are additional params. + 'istoday' => [ + 'type' => PARAM_BOOL, + 'default' => false, + ], + 'isweekend' => [ + 'type' => PARAM_BOOL, + 'default' => false, + ], ]; } @@ -85,14 +112,6 @@ class day_exporter extends exporter { 'neweventtimestamp' => [ 'type' => PARAM_INT, ], - 'istoday' => [ - 'type' => PARAM_BOOL, - 'default' => false, - ], - 'isweekend' => [ - 'type' => PARAM_BOOL, - 'default' => false, - ], 'viewdaylink' => [ 'type' => PARAM_URL, 'optional' => true, @@ -100,7 +119,19 @@ class day_exporter extends exporter { 'events' => [ 'type' => calendar_event_exporter::read_properties_definition(), 'multiple' => true, - ] + ], + 'calendareventtypes' => [ + 'type' => PARAM_RAW, + 'multiple' => true, + ], + 'popovertitle' => [ + 'type' => PARAM_RAW, + 'default' => '', + ], + 'haslastdayofevent' => [ + 'type' => PARAM_BOOL, + 'default' => false, + ], ]; } @@ -131,22 +162,46 @@ class day_exporter extends exporter { $url = new moodle_url('/calendar/view.php', [ 'view' => 'day', 'time' => $timestamp, + 'course' => $this->calendar->course->id, ]); $return['viewdaylink'] = $url->out(false); $cache = $this->related['cache']; - $return['events'] = array_map(function($event) use ($cache, $output, $url) { + $eventexporters = array_map(function($event) use ($cache, $output, $url) { $context = $cache->get_context($event); $course = $cache->get_course($event); $exporter = new calendar_event_exporter($event, [ 'context' => $context, 'course' => $course, 'daylink' => $url, + 'type' => $this->related['type'], + 'today' => $this->data[0], ]); - return $exporter->export($output); + return $exporter; }, $this->related['events']); + $return['events'] = array_map(function($exporter) use ($output) { + return $exporter->export($output); + }, $eventexporters); + + if ($popovertitle = $this->get_popover_title()) { + $return['popovertitle'] = $popovertitle; + } + + $return['calendareventtypes'] = array_map(function($exporter) { + return $exporter->get_calendar_event_type(); + }, $eventexporters); + $return['calendareventtypes'] = array_values(array_unique($return['calendareventtypes'])); + + $return['haslastdayofevent'] = false; + foreach ($return['events'] as $event) { + if ($event->islastday) { + $return['haslastdayofevent'] = true; + break; + } + } + return $return; } @@ -162,4 +217,26 @@ class day_exporter extends exporter { 'type' => '\core_calendar\type_base', ]; } + + /** + * Get the title for this popover. + * + * @return string + */ + protected function get_popover_title() { + $title = null; + + $userdate = userdate($this->data[0], get_string('strftimedayshort')); + if (count($this->related['events'])) { + $title = get_string('eventsfor', 'calendar', $userdate); + } else if ($this->data['istoday']) { + $title = $userdate; + } + + if ($this->data['istoday']) { + $title = get_string('todayplustitle', 'calendar', $userdate); + } + + return $title; + } } diff --git a/calendar/classes/external/month_exporter.php b/calendar/classes/external/month_exporter.php index 9b99498e910..6d180aa4ee1 100644 --- a/calendar/classes/external/month_exporter.php +++ b/calendar/classes/external/month_exporter.php @@ -76,7 +76,19 @@ class month_exporter extends exporter { $related['type'] = $type; - parent::__construct([], $related); + $data = [ + 'url' => $this->url->out(false), + ]; + + parent::__construct($data, $related); + } + + protected static function define_properties() { + return [ + 'url' => [ + 'type' => PARAM_URL, + ], + ]; } /** @@ -106,15 +118,38 @@ class month_exporter extends exporter { 'view' => [ 'type' => PARAM_ALPHA, ], + 'time' => [ + 'type' => PARAM_INT, + ], + 'periodname' => [ + // Note: We must use RAW here because the calendar type returns the formatted month name based on a + // calendar format. + 'type' => PARAM_RAW, + ], 'previousperiod' => [ 'type' => PARAM_INT, ], + 'previousperiodname' => [ + // Note: We must use RAW here because the calendar type returns the formatted month name based on a + // calendar format. + 'type' => PARAM_RAW, + ], 'nextperiod' => [ 'type' => PARAM_INT, ], - 'time' => [ - 'type' => PARAM_INT, - ] + 'nextperiodname' => [ + // Note: We must use RAW here because the calendar type returns the formatted month name based on a + // calendar format. + 'type' => PARAM_RAW, + ], + 'larrow' => [ + // The left arrow defined by the theme. + 'type' => PARAM_RAW, + ], + 'rarrow' => [ + // The right arrow defined by the theme. + 'type' => PARAM_RAW, + ], ]; } @@ -125,16 +160,24 @@ class month_exporter extends exporter { * @return array Keys are the property names, values are their values. */ protected function get_other_values(renderer_base $output) { + $previousperiod = $this->get_previous_month_timestamp(); + $nextperiod = $this->get_next_month_timestamp(); + return [ 'courseid' => $this->calendar->courseid, - 'view' => 'month', - 'previousperiod' => $this->get_previous_month_timestamp(), - 'nextperiod' => $this->get_next_month_timestamp(), 'filter_selector' => $this->get_course_filter_selector($output), 'navigation' => $this->get_navigation($output), 'weeks' => $this->get_weeks($output), 'daynames' => $this->get_day_names($output), - 'time' => $this->calendar->time + 'view' => 'month', + 'time' => $this->calendar->time, + 'periodname' => userdate($this->calendar->time, get_string('strftimemonthyear')), + 'previousperiod' => $previousperiod, + 'previousperiodname' => userdate($previousperiod, get_string('strftimemonthyear')), + 'nextperiod' => $nextperiod, + 'nextperiodname' => userdate($nextperiod, get_string('strftimemonthyear')), + 'larrow' => $output->larrow(), + 'rarrow' => $output->rarrow(), ]; } @@ -213,13 +256,13 @@ class month_exporter extends exporter { $prepadding = ($firstdayno + $daysinweek - 1) % $daysinweek; $daysinfirstweek = $daysinweek - $prepadding; $days = array_slice($alldays, 0, $daysinfirstweek); - $week = new week_exporter($days, $prepadding, ($daysinweek - count($days) - $prepadding), $this->related); + $week = new week_exporter($this->calendar, $days, $prepadding, ($daysinweek - count($days) - $prepadding), $this->related); $weeks[] = $week->export($output); // Now chunk up the remaining day. and turn them into weeks. $daychunks = array_chunk(array_slice($alldays, $daysinfirstweek), $daysinweek); foreach ($daychunks as $days) { - $week = new week_exporter($days, 0, ($daysinweek - count($days)), $this->related); + $week = new week_exporter($this->calendar, $days, 0, ($daysinweek - count($days)), $this->related); $weeks[] = $week->export($output); } @@ -279,7 +322,7 @@ class month_exporter extends exporter { */ protected function get_next_month_timestamp() { $date = $this->related['type']->timestamp_to_date_array($this->calendar->time); - $month = calendar_sub_month($date['mon'], $date['year']); + $month = calendar_add_month($date['mon'], $date['year']); $monthtime = $this->related['type']->convert_to_gregorian($month[1], $month[0], 1); return make_timestamp($monthtime['year'], $monthtime['month'], $monthtime['day'], $monthtime['hour'], $monthtime['minute']); diff --git a/calendar/classes/external/week_day_exporter.php b/calendar/classes/external/week_day_exporter.php new file mode 100644 index 00000000000..67442f55296 --- /dev/null +++ b/calendar/classes/external/week_day_exporter.php @@ -0,0 +1,180 @@ +. + +/** + * Contains event class for displaying the day on month view. + * + * @package core_calendar + * @copyright 2017 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_calendar\external; + +defined('MOODLE_INTERNAL') || die(); + +use renderer_base; +use moodle_url; + +/** + * Class for displaying the day on month view. + * + * @package core_calendar + * @copyright 2017 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class week_day_exporter extends day_exporter { + + /** + * Return the list of additional properties. + * + * @return array + */ + protected static function define_other_properties() { + $return = parent::define_other_properties(); + $return = array_merge($return, [ + 'timestamp' => [ + 'type' => PARAM_INT, + ], + 'neweventtimestamp' => [ + 'type' => PARAM_INT, + ], + 'viewdaylink' => [ + 'type' => PARAM_URL, + 'optional' => true, + ], + 'calendareventtypes' => [ + 'type' => PARAM_RAW, + 'multiple' => true, + ], + 'popovertitle' => [ + 'type' => PARAM_RAW, + 'default' => '', + ], + 'haslastdayofevent' => [ + 'type' => PARAM_BOOL, + 'default' => false, + ], + ]); + + return $return; + } + + /** + * Get the additional values to inject while exporting. + * + * @param renderer_base $output The renderer. + * @return array Keys are the property names, values are their values. + */ + protected function get_other_values(renderer_base $output) { + $timestamp = $this->data[0]; + // Need to account for user's timezone. + $usernow = usergetdate(time()); + $today = new \DateTimeImmutable(); + // The start time should use the day's date but the current + // time of the day (adjusted for user's timezone). + $neweventstarttime = $today->setTimestamp($timestamp)->setTime( + $usernow['hours'], + $usernow['minutes'], + $usernow['seconds'] + ); + + $return = [ + 'timestamp' => $timestamp, + 'neweventtimestamp' => $neweventstarttime->getTimestamp() + ]; + + $url = new moodle_url('/calendar/view.php', [ + 'view' => 'day', + 'time' => $timestamp, + 'course' => $this->calendar->course->id, + ]); + $return['viewdaylink'] = $url->out(false); + + $cache = $this->related['cache']; + $eventexporters = array_map(function($event) use ($cache, $output, $url) { + $context = $cache->get_context($event); + $course = $cache->get_course($event); + $exporter = new calendar_event_exporter($event, [ + 'context' => $context, + 'course' => $course, + 'daylink' => $url, + 'type' => $this->related['type'], + 'today' => $this->data[0], + ]); + + return $exporter; + }, $this->related['events']); + + $return['events'] = array_map(function($exporter) use ($output) { + return $exporter->export($output); + }, $eventexporters); + + if ($popovertitle = $this->get_popover_title()) { + $return['popovertitle'] = $popovertitle; + } + + $return['calendareventtypes'] = array_map(function($exporter) { + return $exporter->get_calendar_event_type(); + }, $eventexporters); + $return['calendareventtypes'] = array_values(array_unique($return['calendareventtypes'])); + + $return['haslastdayofevent'] = false; + foreach ($return['events'] as $event) { + if ($event->islastday) { + $return['haslastdayofevent'] = true; + break; + } + } + + return $return; + } + + /** + * Returns a list of objects that are related. + * + * @return array + */ + protected static function define_related() { + return [ + 'events' => '\core_calendar\local\event\entities\event_interface[]', + 'cache' => '\core_calendar\external\events_related_objects_cache', + 'type' => '\core_calendar\type_base', + ]; + } + + /** + * Get the title for this popover. + * + * @return string + */ + protected function get_popover_title() { + $title = null; + + $userdate = userdate($this->data[0], get_string('strftimedayshort')); + if (count($this->related['events'])) { + $title = get_string('eventsfor', 'calendar', $userdate); + } else if ($this->data['istoday']) { + $title = $userdate; + } + + if ($this->data['istoday']) { + $title = get_string('todayplustitle', 'calendar', $userdate); + } + + return $title; + } +} diff --git a/calendar/classes/external/week_exporter.php b/calendar/classes/external/week_exporter.php index afc92b2f012..ff75a35d328 100644 --- a/calendar/classes/external/week_exporter.php +++ b/calendar/classes/external/week_exporter.php @@ -53,18 +53,25 @@ class week_exporter extends exporter { */ protected $postpadding = 0; + /** + * @var \calendar_information $calendar The calendar being displayed. + */ + protected $calendar; + /** * Constructor. * + * @param \calendar_information $calendar The calendar information for the period being displayed * @param mixed $days An array of day_exporter objects. * @param int $prepadding The number of pre-padding days at the start of the week. * @param int $postpadding The number of post-padding days at the start of the week. * @param array $related Related objects. */ - public function __construct($days, $prepadding, $postpadding, $related) { + public function __construct(\calendar_information $calendar, $days, $prepadding, $postpadding, $related) { $this->days = $days; $this->prepadding = $prepadding; $this->postpadding = $postpadding; + $this->calendar = $calendar; parent::__construct([], $related); } @@ -85,7 +92,7 @@ class week_exporter extends exporter { 'multiple' => true, ], 'days' => [ - 'type' => day_exporter::read_properties_definition(), + 'type' => week_day_exporter::read_properties_definition(), 'multiple' => true, ], ]; @@ -98,6 +105,7 @@ class week_exporter extends exporter { * @return array Keys are the property names, values are their values. */ protected function get_other_values(renderer_base $output) { + global $CFG; $return = [ 'prepadding' => [], 'postpadding' => [], @@ -112,6 +120,14 @@ class week_exporter extends exporter { } $return['days'] = []; + $today = $this->related['type']->timestamp_to_date_array(time()); + + $weekend = CALENDAR_DEFAULT_WEEKEND; + if (isset($CFG->calendar_weekend)) { + $weekend = intval($CFG->calendar_weekend); + } + $numberofdaysinweek = $this->related['type']->get_num_weekdays(); + foreach ($this->days as $daydata) { $events = []; foreach ($this->related['events'] as $event) { @@ -132,7 +148,14 @@ class week_exporter extends exporter { $events[] = $event; } - $day = new day_exporter($daydata, [ + $istoday = true; + $istoday = $istoday && $today['year'] == $daydata['year']; + $istoday = $istoday && $today['yday'] == $daydata['yday']; + $daydata['istoday'] = $istoday; + + $daydata['isweekend'] = !!($weekend & (1 << ($daydata['wday'] % $numberofdaysinweek))); + + $day = new week_day_exporter($this->calendar, $daydata, [ 'events' => $events, 'cache' => $this->related['cache'], 'type' => $this->related['type'], diff --git a/calendar/lib.php b/calendar/lib.php index f574472f314..c1b342b9629 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -1049,7 +1049,7 @@ class calendar_information { public function add_sidecalendar_blocks(core_calendar_renderer $renderer, $showfilters=false, $view=null) { if ($showfilters) { $filters = new block_contents(); - $filters->content = $renderer->fake_block_filters($this->courseid, 0, 0, 0, $view, $this->courses); + $filters->content = $renderer->event_filter(); $filters->footer = ''; $filters->title = get_string('eventskey', 'calendar'); $renderer->add_pretend_calendar_block($filters, BLOCK_POS_RIGHT); @@ -1478,7 +1478,7 @@ function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyea // Class and cell content. if (isset($typesbyday[$day]['startglobal'])) { - $class .= ' calendar_event_global'; + $class .= ' calendar_event_site'; } else if (isset($typesbyday[$day]['startcourse'])) { $class .= ' calendar_event_course'; } else if (isset($typesbyday[$day]['startgroup'])) { @@ -1981,88 +1981,6 @@ function calendar_top_controls($type, $data) { return $content; } -/** - * Formats a filter control element. - * - * @param moodle_url $url of the filter - * @param int $type constant defining the type filter - * @return string html content of the element - */ -function calendar_filter_controls_element(moodle_url $url, $type) { - global $OUTPUT; - - switch ($type) { - case CALENDAR_EVENT_GLOBAL: - $typeforhumans = 'global'; - $class = 'calendar_event_global'; - break; - case CALENDAR_EVENT_COURSE: - $typeforhumans = 'course'; - $class = 'calendar_event_course'; - break; - case CALENDAR_EVENT_GROUP: - $typeforhumans = 'groups'; - $class = 'calendar_event_group'; - break; - case CALENDAR_EVENT_USER: - $typeforhumans = 'user'; - $class = 'calendar_event_user'; - break; - } - - if (calendar_show_event_type($type)) { - $icon = $OUTPUT->pix_icon('t/hide', get_string('hide')); - $str = get_string('hide' . $typeforhumans . 'events', 'calendar'); - } else { - $icon = $OUTPUT->pix_icon('t/show', get_string('show')); - $str = get_string('show' . $typeforhumans . 'events', 'calendar'); - } - $content = \html_writer::start_tag('li', array('class' => 'calendar_event')); - $content .= \html_writer::start_tag('a', array('href' => $url, 'rel' => 'nofollow')); - $content .= \html_writer::tag('span', $icon, array('class' => $class)); - $content .= \html_writer::tag('span', $str, array('class' => 'eventname')); - $content .= \html_writer::end_tag('a'); - $content .= \html_writer::end_tag('li'); - - return $content; -} - -/** - * Get the controls filter for calendar. - * - * Filter is used to hide calendar info from the display page. - * - - * @param moodle_url $returnurl return-url for filter controls - * @return string $content return filter controls in html - */ -function calendar_filter_controls(moodle_url $returnurl) { - $groupevents = true; - - $seturl = new \moodle_url('/calendar/set.php', array('return' => base64_encode($returnurl->out_as_local_url(false)), - 'sesskey' => sesskey())); - $content = \html_writer::start_tag('ul'); - - $seturl->param('var', 'showglobal'); - $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_GLOBAL); - - $seturl->param('var', 'showcourses'); - $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_COURSE); - - if (isloggedin() && !isguestuser()) { - if ($groupevents) { - // This course MIGHT have group events defined, so show the filter. - $seturl->param('var', 'showgroups'); - $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_GROUP); - } - $seturl->param('var', 'showuser'); - $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_USER); - } - $content .= \html_writer::end_tag('ul'); - - return $content; -} - /** * Return the representation day. * @@ -3464,6 +3382,11 @@ function calendar_get_view(\calendar_information $calendar, $view) { $monthdays = $type->get_num_days_in_month($date['year'], $date['mon']); $tend = $tstart + ($monthdays * DAYSECS) - 1; $selectortitle = get_string('detailedmonthviewfor', 'calendar'); + if ($view === 'mini') { + $template = 'core_calendar/calendar_mini'; + } else { + $template = 'core_calendar/calendar_month'; + } } list($userparam, $groupparam, $courseparam) = array_map(function($param) { @@ -3518,7 +3441,6 @@ function calendar_get_view(\calendar_information $calendar, $view) { $month = new \core_calendar\external\month_exporter($calendar, $type, $related); $data = $month->export($renderer); - $template = 'core_calendar/month_detailed'; return [$data, $template]; } @@ -3654,3 +3576,25 @@ function calendar_get_footer_options($calendar) { return [$data, $template]; } + +/** + * Get the list of potential calendar filter types as a type => name + * combination. + * + * @return array + */ +function calendar_get_filter_types() { + $types = [ + 'site', + 'course', + 'group', + 'user', + ]; + + return array_map(function($type) { + return [ + 'type' => $type, + 'name' => get_string("eventtype{$type}", "calendar"), + ]; + }, $types); +} diff --git a/calendar/renderer.php b/calendar/renderer.php index 181823461a1..dc597abf2e6 100644 --- a/calendar/renderer.php +++ b/calendar/renderer.php @@ -50,24 +50,6 @@ class core_calendar_renderer extends plugin_renderer_base { return html_writer::end_tag('div'); } - /** - * Produces the content for the filters block (pretend block) - * - * @param int $courseid - * @param int $day - * @param int $month - * @param int $year - * @param int $view - * @param int $courses - * @return string - */ - public function fake_block_filters($courseid, $day, $month, $year, $view, $courses) { - $returnurl = $this->page->url; - $returnurl->param('course', $courseid); - return html_writer::tag('div', calendar_filter_controls($returnurl), - array('class' => 'calendar_filters filters')); - } - /** * Produces the content for the three months block (pretend block) * @@ -502,4 +484,16 @@ class core_calendar_renderer extends plugin_renderer_base { $html .= html_writer::end_tag('form'); return $html; } + + /** + * Render the event filter region. + * + * @return string + */ + public function event_filter() { + $data = [ + 'eventtypes' => calendar_get_filter_types(), + ]; + return $this->render_from_template('core_calendar/event_filter', $data); + } } diff --git a/calendar/set.php b/calendar/set.php deleted file mode 100644 index c69de934a1a..00000000000 --- a/calendar/set.php +++ /dev/null @@ -1,60 +0,0 @@ -. - -/** - * Sets the events filter for the calendar view. - * - * @package core_calendar - * @copyright 2003 Jon Papaioannou (pj@moodle.org) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -require_once('../config.php'); -require_once($CFG->dirroot.'/calendar/lib.php'); - -$var = required_param('var', PARAM_ALPHA); -$return = clean_param(base64_decode(required_param('return', PARAM_RAW)), PARAM_LOCALURL); -$courseid = optional_param('id', -1, PARAM_INT); -if ($courseid != -1) { - $return = new moodle_url($return, array('course' => $courseid)); -} else { - $return = new moodle_url($return); -} - -if (!confirm_sesskey()) { - // Do not call require_sesskey() since this page may be accessed without session (for example by bots). - redirect($return); -} - -$url = new moodle_url('/calendar/set.php', array('return'=>base64_encode($return->out_as_local_url(false)), 'course' => $courseid, 'var'=>$var, 'sesskey'=>sesskey())); -$PAGE->set_url($url); -$PAGE->set_context(context_system::instance()); - -switch($var) { - case 'showgroups': - calendar_set_event_type_display(CALENDAR_EVENT_GROUP); - break; - case 'showcourses': - calendar_set_event_type_display(CALENDAR_EVENT_COURSE); - break; - case 'showglobal': - calendar_set_event_type_display(CALENDAR_EVENT_GLOBAL); - break; - case 'showuser': - calendar_set_event_type_display(CALENDAR_EVENT_USER); - break; -} - -redirect($return); diff --git a/calendar/templates/calendar_mini.mustache b/calendar/templates/calendar_mini.mustache new file mode 100644 index 00000000000..6e28cbfab57 --- /dev/null +++ b/calendar/templates/calendar_mini.mustache @@ -0,0 +1,45 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template calendar/calendar_mini + + Calendar month view for blocks. + + The purpose of this template is to render the month view for blocks. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Example context (json): + { + } +}} + + {{> core_calendar/month_mini}} + +{{#js}} +require(['jquery', 'core_calendar/calendar_mini'], function($, CalendarMini) { + CalendarMini.init($("#calendar-month-{{uniqid}}-{{time}}")); +}); +{{/js}} diff --git a/calendar/templates/calendar_month.mustache b/calendar/templates/calendar_month.mustache new file mode 100644 index 00000000000..9e5866b68be --- /dev/null +++ b/calendar/templates/calendar_month.mustache @@ -0,0 +1,41 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template calendar/calendar_month + + Calendar month view for blcoks. + + The purpose of this template is to render the main, detailed monthly view. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Example context (json): + { + } +}} +
+ {{> core_calendar/month_detailed}} +
+{{#js}} +require(['jquery', 'core_calendar/calendar'], function($, Calendar) { + Calendar.init($("#calendar-month-{{uniqid}}")); +}); +{{/js}} diff --git a/calendar/templates/event_filter.mustache b/calendar/templates/event_filter.mustache new file mode 100644 index 00000000000..4e6e789e143 --- /dev/null +++ b/calendar/templates/event_filter.mustache @@ -0,0 +1,45 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template calendar/event_filter + + Filter to select which event types displayed on this page. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Example context (json): + { + } +}} +
+
    + {{#eventtypes}} +
  • + {{> core_calendar/event_filter_key }} +
  • + {{/eventtypes}} +
+
+{{#js}} +require(['jquery', 'core_calendar/calendar_filter'], function($, CalendarFilter) { + CalendarFilter.init($("#event-filter-{{uniqid}}")); +}); +{{/js}} diff --git a/calendar/templates/event_filter_key.mustache b/calendar/templates/event_filter_key.mustache new file mode 100644 index 00000000000..be1154b035c --- /dev/null +++ b/calendar/templates/event_filter_key.mustache @@ -0,0 +1,45 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template calendar/event_filter_key + + Key item for the event filter. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Example context (json): + { + } +}} + + {{^hidden}} + + {{#pix}}i/hide, core, {{#str}}hideeventtype, calendar, {{name}}{{/str}}{{/pix}} + + {{#str}}hideeventtype, calendar, {{name}}{{/str}} + {{/hidden}} + {{#hidden}} + + {{#pix}}i/show, core, {{#str}}showeventtype, calendar, {{name}}{{/str}}{{/pix}} + + {{#str}}showeventtype, calendar, {{name}}{{/str}} + {{/hidden}} + diff --git a/calendar/templates/minicalendar_day_link.mustache b/calendar/templates/minicalendar_day_link.mustache index bdb5e676181..d6c91afc76b 100644 --- a/calendar/templates/minicalendar_day_link.mustache +++ b/calendar/templates/minicalendar_day_link.mustache @@ -29,10 +29,10 @@ }} {{< core/hover_tooltip }} {{$anchor}} - {{day}} + {{$day}}{{day}}{{/day}} {{/anchor}} {{$tooltip}} - {{title}} - {{{content}}} + {{$title}}{{title}}{{/title}} + {{$content}}{{{content}}}{{/content}} {{/tooltip}} {{/ core/hover_tooltip }} diff --git a/calendar/templates/month_detailed.mustache b/calendar/templates/month_detailed.mustache index 80005dc134c..463b687216b 100644 --- a/calendar/templates/month_detailed.mustache +++ b/calendar/templates/month_detailed.mustache @@ -77,8 +77,9 @@
  • [{{name}}]
  • {{/underway}} {{^underway}} -
  • {{#js}} -require(['jquery', 'core_calendar/month_view_drag_drop'], function($, DragDrop) { +require([ + 'jquery', + 'core_calendar/month_view_drag_drop', + 'core_calendar/selectors', + 'core_calendar/events', +], function( + $, + DragDrop, + CalendarSelectors, + CalendarEvents +) { var root = $('#month-detailed-{{uniqid}}'); DragDrop.init(root); + + $('body').on(CalendarEvents.filterChanged, function(e, data) { + M.util.js_pending("month-detailed-{{uniqid}}-filterChanged"); + // A filter value has been changed. + // Find all matching cells in the popover data, and hide them. + $("#month-detailed-{{uniqid}}") + .find(CalendarSelectors.eventType[data.type]) + .toggleClass('hidden', !!data.hidden); + M.util.js_complete("month-detailed-{{uniqid}}-filterChanged"); + }); }); {{/js}} diff --git a/calendar/templates/month_mini.mustache b/calendar/templates/month_mini.mustache new file mode 100644 index 00000000000..b14afc906c0 --- /dev/null +++ b/calendar/templates/month_mini.mustache @@ -0,0 +1,154 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template calendar/month_mini + + Calendar month view for blocks. + + The purpose of this template is to render the month view for blocks. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Example context (json): + { + } +}} +
    + {{> core/overlay_loading}} + + + + + {{# daynames }} + + {{/ daynames }} + + + +{{#weeks}} + + {{#prepadding}} + + {{/prepadding}} + {{#days}} + + {{/days}} + {{#postpadding}} + + {{/postpadding}} + +{{/weeks}} + +
    + + | + + {{periodname}} + + | + +
    + {{shortname}} +
     {{! + }}{{#popovertitle}} + {{< core_calendar/minicalendar_day_link }} + {{$day}}{{mday}}{{/day}} + {{$url}}{{viewdaylink}}{{/url}} + {{$title}}{{popovertitle}}{{/title}} + {{$nocontent}}{{#str}}eventnone, calendar{{/str}}{{/nocontent}} + {{$content}} + {{#events}} +
    + {{#modulename}} + {{#pix}} icon, {{modulename}} {{/pix}} + {{/modulename}} + {{^modulename}} + {{#pix}} i/{{eventtype}}event, core {{/pix}} + {{/modulename}} + {{{popupname}}} +
    + {{/events}} + {{/content}} + {{/ core_calendar/minicalendar_day_link }} + {{/popovertitle}}{{! + }}{{^popovertitle}} + {{mday}} + {{/popovertitle}}{{! + }}
     
    +
    +{{#js}} +require([ + 'jquery', + 'core_calendar/selectors', + 'core_calendar/events', +], function( + $, + CalendarSelectors, + CalendarEvents +) { + + $('body').on(CalendarEvents.filterChanged, function(e, data) { + M.util.js_pending("month-mini-{{uniqid}}-filterChanged"); + // A filter value has been changed. + // Find all matching cells in the popover data, and hide them. + $("#month-mini-{{uniqid}}-{{time}}") + .find(CalendarSelectors.popoverType[data.type]) + .toggleClass('hidden', !!data.hidden); + M.util.js_complete("month-mini-{{uniqid}}-filterChanged"); + }); +}); +{{/js}} diff --git a/calendar/tests/behat/behat_calendar.php b/calendar/tests/behat/behat_calendar.php index ef8fa66aa9f..0bd835cc666 100644 --- a/calendar/tests/behat/behat_calendar.php +++ b/calendar/tests/behat/behat_calendar.php @@ -84,9 +84,9 @@ class behat_calendar extends behat_base { * @param int $day The day of the current month */ public function i_hover_over_day_of_this_month_in_calendar($day) { - $summarytitle = get_string('calendarheading', 'calendar', userdate(time(), get_string('strftimemonthyear'))); + $summarytitle = userdate(time(), get_string('strftimemonthyear')); // The current month table. - $currentmonth = "table[contains(concat(' ', normalize-space(@summary), ' '), ' {$summarytitle} ')]"; + $currentmonth = "table[descendant::*[self::caption[contains(concat(' ', normalize-space(.), ' '), ' {$summarytitle} ')]]]"; // Strings for the class cell match. $cellclasses = "contains(concat(' ', normalize-space(@class), ' '), ' day ')"; @@ -95,6 +95,7 @@ class behat_calendar extends behat_base { $dayofmonth = "a[{$daycontains}]"; $xpath = '//' . $currentmonth . '/descendant::' . $daycell . '/' . $dayofmonth; + $this->execute("behat_general::wait_until_the_page_is_ready"); $this->execute("behat_general::i_hover", array($xpath, "xpath_element")); } diff --git a/calendar/view.php b/calendar/view.php index ba0bc947c6d..6447c8d906a 100644 --- a/calendar/view.php +++ b/calendar/view.php @@ -161,5 +161,4 @@ echo html_writer::end_tag('div'); list($data, $template) = calendar_get_footer_options($calendar); echo $renderer->render_from_template($template, $data); -$PAGE->requires->js_call_amd('core_calendar/calendar', 'init'); echo $OUTPUT->footer(); diff --git a/lang/en/access.php b/lang/en/access.php index 6e8789ee219..9eb6d21fbaf 100644 --- a/lang/en/access.php +++ b/lang/en/access.php @@ -30,8 +30,6 @@ $string['activitynext'] = 'Next activity'; $string['activityprev'] = 'Previous activity'; $string['breadcrumb'] = 'Breadcrumb trail'; $string['hideblocka'] = 'Hide {$a} block'; -$string['monthnext'] = 'Next month'; -$string['monthprev'] = 'Previous month'; $string['showblocka'] = 'Show {$a} block'; $string['sitemap'] = 'Site map'; $string['skipa'] = 'Skip {$a}'; @@ -41,3 +39,7 @@ $string['skipto'] = 'Skip to {$a}'; $string['tocontent'] = 'Skip to main content'; $string['tonavigation'] = 'Go to navigation'; $string['youarehere'] = 'You are here'; + +// Deprecated since Moodle 3.4. +$string['monthnext'] = 'Next month'; +$string['monthprev'] = 'Previous month'; diff --git a/lang/en/calendar.php b/lang/en/calendar.php index 405046de3d0..30a936c1387 100644 --- a/lang/en/calendar.php +++ b/lang/en/calendar.php @@ -90,6 +90,7 @@ $string['eventdate'] = 'Date'; $string['eventdescription'] = 'Description'; $string['eventduration'] = 'Duration'; $string['eventendtime'] = 'End time'; +$string['eventendtimewrapped'] = '{$a} (End time)'; $string['eventinstanttime'] = 'Time'; $string['eventkind'] = 'Type of event'; $string['eventname'] = 'Event title'; @@ -135,10 +136,14 @@ $string['gotocalendar'] = 'Go to calendar'; $string['group'] = 'Group'; $string['groupevent'] = 'Group event'; $string['groupevents'] = 'Group events'; -$string['hidecourseevents'] = 'Hide course events'; -$string['hideglobalevents'] = 'Hide global events'; -$string['hidegroupsevents'] = 'Hide group events'; -$string['hideuserevents'] = 'Hide user events'; +$string['eventtypeglobal'] = 'global'; +$string['eventtypesite'] = 'global'; +$string['eventtypecourse'] = 'course'; +$string['eventtypemodule'] = 'module'; +$string['eventtypegroup'] = 'group'; +$string['eventtypeuser'] = 'user'; +$string['hideeventtype'] = 'Hide {$a} events'; +$string['showeventtype'] = 'Show {$a} events'; $string['hourly'] = 'Hourly'; $string['ical'] = 'iCal'; $string['importcalendar'] = 'Import calendar'; @@ -159,9 +164,10 @@ $string['monday'] = 'Monday'; $string['monthly'] = 'Monthly'; $string['monthlyview'] = 'Monthly view'; $string['monthnext'] = 'Next month'; +$string['monthprev'] = 'Previous month'; $string['monththis'] = 'This month'; $string['more'] = 'More'; -$string['namewithsource'] = '{$a->name}({$a->source})'; +$string['namewithsource'] = '{$a->name} ({$a->source})'; $string['never'] = 'Never'; $string['newevent'] = 'New event'; $string['notitle'] = 'no title'; @@ -194,10 +200,6 @@ $string['repeatweeksl'] = 'Repeat weekly, creating altogether'; $string['repeatweeksr'] = 'events'; $string['sat'] = 'Sat'; $string['saturday'] = 'Saturday'; -$string['showcourseevents'] = 'Show course events'; -$string['showglobalevents'] = 'Show global events'; -$string['showgroupsevents'] = 'Show group events'; -$string['showuserevents'] = 'Show user events'; $string['shown'] = 'shown'; $string['siteevents'] = 'Site events'; $string['spanningevents'] = 'Events underway'; @@ -214,17 +216,10 @@ $string['timeformat_12'] = '12-hour (am/pm)'; $string['timeformat_24'] = '24-hour'; $string['timeperiod'] = 'Time period'; $string['today'] = 'Today'; +$string['todayplustitle'] = 'Today {$a}'; $string['tomorrow'] = 'Tomorrow'; $string['tt_deleteevent'] = 'Delete event'; $string['tt_editevent'] = 'Edit event'; -$string['tt_hidecourse'] = 'Course events are shown (click to hide)'; -$string['tt_hideglobal'] = 'Global events are shown (click to hide)'; -$string['tt_hidegroups'] = 'Group events are shown (click to hide)'; -$string['tt_hideuser'] = 'User events are shown (click to hide)'; -$string['tt_showcourse'] = 'Course events are hidden (click to show)'; -$string['tt_showglobal'] = 'Global events are hidden (click to show)'; -$string['tt_showgroups'] = 'Group events are hidden (click to show)'; -$string['tt_showuser'] = 'User events are hidden (click to show)'; $string['tue'] = 'Tue'; $string['tuesday'] = 'Tuesday'; $string['typeclose'] = 'Close event'; @@ -252,3 +247,21 @@ $string['youcandeleteallrepeats'] = 'This event is part of a repeating event ser // Deprecated since Moodle 3.2. $string['for'] = 'for'; + +// Deprecated since Moodle 3.4. +$string['tt_hidecourse'] = 'Course events are shown (click to hide)'; +$string['tt_hideglobal'] = 'Global events are shown (click to hide)'; +$string['tt_hidegroups'] = 'Group events are shown (click to hide)'; +$string['tt_hideuser'] = 'User events are shown (click to hide)'; +$string['tt_showcourse'] = 'Course events are hidden (click to show)'; +$string['tt_showglobal'] = 'Global events are hidden (click to show)'; +$string['tt_showgroups'] = 'Group events are hidden (click to show)'; +$string['tt_showuser'] = 'User events are hidden (click to show)'; +$string['hidecourseevents'] = 'Hide course events'; +$string['hideglobalevents'] = 'Hide global events'; +$string['hidegroupsevents'] = 'Hide group events'; +$string['hideuserevents'] = 'Hide user events'; +$string['showcourseevents'] = 'Show course events'; +$string['showglobalevents'] = 'Show global events'; +$string['showgroupsevents'] = 'Show group events'; +$string['showuserevents'] = 'Show user events'; diff --git a/lang/en/deprecated.txt b/lang/en/deprecated.txt index 55a0e70b83b..cd7bb58f4ea 100644 --- a/lang/en/deprecated.txt +++ b/lang/en/deprecated.txt @@ -61,3 +61,21 @@ error:backpackloginfailed,core_badges signinwithyouremail,core_badges sectionusedefaultname,core registermoochtips,core_hub +monthnext,core_access +monthprev,core_access +tt_hidecourse,core_calendar +tt_hideglobal,core_calendar +tt_hidegroups,core_calendar +tt_hideuser,core_calendar +tt_showcourse,core_calendar +tt_showglobal,core_calendar +tt_showgroups,core_calendar +tt_showuser,core_calendar +hidecourseevents,core_calendar +hideglobalevents,core_calendar +hidegroupsevents,core_calendar +hideuserevents,core_calendar +showcourseevents,core_calendar +showglobalevents,core_calendar +showgroupsevents,core_calendar +showuserevents,core_calendar diff --git a/theme/boost/scss/moodle/calendar.scss b/theme/boost/scss/moodle/calendar.scss index 2c047052fc3..b13af94c9bd 100644 --- a/theme/boost/scss/moodle/calendar.scss +++ b/theme/boost/scss/moodle/calendar.scss @@ -355,3 +355,13 @@ table.calendartable caption { } } } + +.calendarwrapper { + position: relative; +} + +.day-popover-content { + &:empty + .day-popover-alternate { + display: block; + } +} diff --git a/theme/boost/templates/core_calendar/minicalendar_day_link.mustache b/theme/boost/templates/core_calendar/minicalendar_day_link.mustache index d4ce07caca4..072345dad23 100644 --- a/theme/boost/templates/core_calendar/minicalendar_day_link.mustache +++ b/theme/boost/templates/core_calendar/minicalendar_day_link.mustache @@ -27,12 +27,38 @@ "content": "Test site event" } }} -{{day}} +{{$day}}{{day}}{{/day}} + +{{#js}} +require(['jquery'], function($) { + require(['theme_boost/popover'], function() { + var target = $("#calendar-day-popover-link-{{courseid}}-{{year}}-{{yday}}-{{uniqid}}"); + target.popover({ + content: function() { + var source = target.next().find("> *:not('.hidden')"); + var content = $('
    '); + + if (source.length) { + content.html(source.clone(false)); + } else { + content.html(target.data('alternate')); + } + + return content.html(); + } + }); + }); +}); +{{/js}} diff --git a/theme/bootstrapbase/less/moodle/calendar.less b/theme/bootstrapbase/less/moodle/calendar.less index 0e84a086b1d..72fb4a48402 100644 --- a/theme/bootstrapbase/less/moodle/calendar.less +++ b/theme/bootstrapbase/less/moodle/calendar.less @@ -345,3 +345,8 @@ } } } + + +.calendarwrapper { + position: relative; +} diff --git a/theme/bootstrapbase/style/moodle.css b/theme/bootstrapbase/style/moodle.css index b6f4cfd6caa..2e34106198f 100644 --- a/theme/bootstrapbase/style/moodle.css +++ b/theme/bootstrapbase/style/moodle.css @@ -5852,6 +5852,9 @@ img.iconsmall { min-width: 1024px; } } +.calendarwrapper { + position: relative; +} /* course.less */ /* COURSE CONTENT */ /* stylelint-disable unit-blacklist */ diff --git a/theme/bootstrapbase/templates/core_calendar/minicalendar_day_link.mustache b/theme/bootstrapbase/templates/core_calendar/minicalendar_day_link.mustache index c3a003417c7..56f0e7bb463 100644 --- a/theme/bootstrapbase/templates/core_calendar/minicalendar_day_link.mustache +++ b/theme/bootstrapbase/templates/core_calendar/minicalendar_day_link.mustache @@ -27,18 +27,38 @@ "content": "Test site event" } }} -{{day}} +{{$day}}{{day}}{{/day}} + {{#js}} -require(['jquery'], function($) { +require(['jquery', 'theme_bootstrapbase/bootstrap'], function($) { require(['theme_bootstrapbase/bootstrap'], function() { - $('#{{uniqid}}').popover(); + var target = $("#calendar-day-popover-link-{{courseid}}-{{year}}-{{yday}}-{{uniqid}}"); + target.popover({ + content: function() { + var source = target.next().find("> *:not('.hidden')"); + var content = $('
    '); + + if (source.length) { + content.html(source.clone(false)); + } else { + content.html(target.data('alternate')); + } + + return content.html(); + } + }); }); }); {{/js}}