mirror of
https://github.com/moodle/moodle.git
synced 2025-08-07 18:06:51 +02:00
MDL-59382 calendar: add modal to create and update events
This commit is contained in:
parent
6103fd2efe
commit
aa0912258d
23 changed files with 2392 additions and 157 deletions
2
calendar/amd/build/calendar.min.js
vendored
2
calendar/amd/build/calendar.min.js
vendored
|
@ -1 +1 @@
|
|||
define(["jquery","core/ajax","core/str","core/templates","core/notification","core/custom_interaction_events","core/modal_factory","core_calendar/summary_modal","core/modal_events","core_calendar/calendar_repository"],function(a,b,c,d,e,f,g,h,i,j){var k={ROOT:"[data-region='calendar']",EVENT_LINK:"[data-action='view-event']"},l=function(a){var b="type"+a;return c.get_string(b,"core_calendar").then(function(a){return a})},m=function(a){return c.get_string("subsource","core_calendar",a).then(function(b){return a.url?'<a href="'+a.url+'">'+b+"</a>":b})},n=function(b){j.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=l(d.eventtype);if(d.displayeventsource){d.subscription=JSON.parse(d.subscription);var f={url:d.subscription.url,name:d.subscription.name},g=m(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:h.TYPE,body:d.render("core_calendar/event_summary_body",a)};return a.caneditevent||(b.footer=""),g.create(b)}).done(function(a){a.getRoot().on(i.hidden,function(){a.destroy()}),a.show()}).fail(e.exception)},o=function(){a(k.EVENT_LINK).click(function(b){b.preventDefault();var c=a(this).attr("data-event-id");n(c)})};return{init:function(){o()}}});
|
||||
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"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m={ROOT:"[data-region='calendar']",EVENT_LINK:"[data-action='view-event']",NEW_EVENT_BUTTON:"[data-action='new-event-button']"},n=function(a){var b="type"+a;return c.get_string(b,"core_calendar").then(function(a){return a})},o=function(a){return c.get_string("subsource","core_calendar",a).then(function(b){return a.url?'<a href="'+a.url+'">'+b+"</a>":b})},p=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=n(d.eventtype);if(d.displayeventsource){d.subscription=JSON.parse(d.subscription);var f={url:d.subscription.url,name:d.subscription.name},g=o(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)};return a.caneditevent||(b.footer=""),h.create(b)}).done(function(a){a.getRoot().on(g.hidden,function(){a.destroy()}),a.show()}).fail(e.exception)},q=function(a){var b=a.find(m.NEW_EVENT_BUTTON),c=b.attr("data-context-id");return h.create({type:i.TYPE,large:!0,templateContext:{contextid:c}},b)},r=function(b,c){var d=a("body");d.on(l.created,function(){window.location.reload()}),d.on(l.deleted,function(){window.location.reload()}),d.on(l.updated,function(){window.location.reload()}),c.then(function(a){d.on(l.editEvent,function(b,c){a.setEventId(c),a.show()})})},s=function(){var b=a(m.ROOT);a(m.EVENT_LINK).click(function(b){b.preventDefault();var c=a(this).attr("data-event-id");p(c)});var c=q(b);r(b,c)};return{init:function(){s()}}});
|
1
calendar/amd/build/event_form.min.js
vendored
Normal file
1
calendar/amd/build/event_form.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery","core/templates"],function(a,b){var c={EVENT_TYPE:'[name="eventtype"]',EVENT_COURSE_ID:'[name="courseid"]',EVENT_GROUP_COURSE_ID:'[name="groupcourseid"]',EVENT_GROUP_ID:'[name="groupid"]',FORM_GROUP:".form-group",SELECT_OPTION:"option",ADVANCED_ELEMENT:".fitem.advanced",FIELDSET_ADVANCED_ELEMENTS:"fieldset.containsadvancedelements",MORELESS_TOGGLE:".moreless-actions"},d={USER:"user",SITE:"site",COURSE:"course",GROUP:"group"},e={SHOW_ADVANCED:"event_form-show-advanced",HIDE_ADVANCED:"event_form-hide-advanced",ADVANCED_SHOWN:"event_form-advanced-shown",ADVANCED_HIDDEN:"event_form-advanced-hidden"},f=function(a){a.find(c.FIELDSET_ADVANCED_ELEMENTS).removeClass("containsadvancedelements");var d=a.find(c.MORELESS_TOGGLE);b.replaceNode(d,"","")},g=function(a){a.find(c.ADVANCED_ELEMENT).removeClass("hidden"),a.trigger(e.ADVANCED_SHOWN)},h=function(a){a.find(c.ADVANCED_ELEMENT).addClass("hidden"),a.trigger(e.ADVANCED_HIDDEN)},i=function(a){a.on(e.SHOW_ADVANCED,function(){g(a)}),a.on(e.HIDE_ADVANCED,function(){h(a)})},j=function(b){b.find(c.EVENT_GROUP_ID).find(c.SELECT_OPTION).each(function(b,c){var c=a(c),d=c.attr("value"),e=d.split("-"),f=e[0];c.attr("data-course-id",f)})},k=function(a){var b=a.find(c.EVENT_TYPE),e=b.val(),f=a.find(c.EVENT_COURSE_ID).closest(c.FORM_GROUP).removeClass("hidden"),g=a.find(c.EVENT_GROUP_COURSE_ID).closest(c.FORM_GROUP).removeClass("hidden"),h=a.find(c.EVENT_GROUP_ID).closest(c.FORM_GROUP).removeClass("hidden");switch(e){case d.COURSE:g.addClass("hidden"),h.addClass("hidden");break;case d.GROUP:f.addClass("hidden");break;default:f.addClass("hidden"),g.addClass("hidden"),h.addClass("hidden")}},l=function(a){var b=a.find(c.EVENT_TYPE);b.on("change",function(){k(a)})},m=function(b){var d=b.find(c.EVENT_GROUP_COURSE_ID),e=b.find(c.EVENT_GROUP_ID),f=e.find(c.SELECT_OPTION),g=function(){var b=d.val(),c=null;f.each(function(d,e){e=a(e),e.attr("data-course-id")==b?(e.removeClass("hidden"),e.prop("disabled",!1),null===c&&(c=d)):(e.addClass("hidden"),e.prop("disabled",!0))}),e.prop("selectedIndex",c)};d.on("change",g),g()},n=function(b,c){var d=a("#"+b);i(d),f(d),k(d),j(d),l(d),m(d),c?g(d):h(d)};return{init:n,events:e}});
|
1
calendar/amd/build/modal_event_form.min.js
vendored
Normal file
1
calendar/amd/build/modal_event_form.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery","core/event","core/str","core/notification","core/templates","core/custom_interaction_events","core/modal","core/modal_registry","core/fragment","core_calendar/events","core_calendar/repository","core_calendar/event_form"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=!1,n={MORELESS_BUTTON:'[data-action="more-less-toggle"]',SAVE_BUTTON:'[data-action="save"]',LOADING_ICON_CONTAINER:'[data-region="loading-icon-container"]'},o=function(a){g.call(this,a),this.eventId=null,this.reloadingBody=!1,this.reloadingTitle=!1,this.saveButton=this.getFooter().find(n.SAVE_BUTTON),this.moreLessButton=this.getFooter().find(n.MORELESS_BUTTON)};return o.TYPE="core_calendar-modal_event_form",o.prototype=Object.create(g.prototype),o.prototype.constructor=o,o.prototype.setEventId=function(a){this.eventId=a},o.prototype.getEventId=function(){return this.eventId},o.prototype.hasEventId=function(){return null!==this.eventId},o.prototype.getForm=function(){return this.getBody().find("form")},o.prototype.disableButtons=function(){this.saveButton.prop("disabled",!0),this.moreLessButton.prop("disabled",!0)},o.prototype.enableButtons=function(){this.saveButton.prop("disabled",!1),this.moreLessButton.prop("disabled",!1)},o.prototype.setMoreButton=function(){this.moreLessButton.attr("data-collapsed","true"),c.get_string("more","calendar").then(function(a){this.moreLessButton.text(a)}.bind(this))},o.prototype.setLessButton=function(){this.moreLessButton.attr("data-collapsed","false"),c.get_string("less","calendar").then(function(a){this.moreLessButton.text(a)}.bind(this))},o.prototype.toggleMoreLessButton=function(){var a=this.getForm();"true"==this.moreLessButton.attr("data-collapsed")?(a.trigger(l.events.SHOW_ADVANCED),this.setLessButton()):(a.trigger(l.events.HIDE_ADVANCED),this.setMoreButton())},o.prototype.reloadTitleContent=function(){return this.reloadingTitle?this.titlePromise:(this.reloadingTitle=!0,this.hasEventId()?this.titlePromise=c.get_string("editevent","calendar"):this.titlePromise=c.get_string("newevent","calendar"),this.titlePromise.then(function(a){return this.setTitle(a),a}.bind(this)).always(function(){this.reloadingTitle=!1}.bind(this)),this.titlePromise)},o.prototype.reloadBodyContent=function(a,b){if(this.reloadingBody)return this.bodyPromise;this.reloadingBody=!0,this.disableButtons();var c=this.saveButton.attr("data-context-id"),e={};return this.hasEventId()&&(e.eventid=this.getEventId()),"undefined"!=typeof a&&(e.formdata=a),e.haserror="undefined"!=typeof b&&b,this.bodyPromise=i.loadFragment("calendar","event_form",c,e),this.setBody(this.bodyPromise),this.bodyPromise.then(function(){this.enableButtons()}.bind(this))["catch"](d.exception).always(function(){this.reloadingBody=!1}.bind(this)),this.bodyPromise},o.prototype.reloadAllContent=function(){return a.when(this.reloadTitleContent(),this.reloadBodyContent())},o.prototype.show=function(){this.reloadAllContent(),g.prototype.show.call(this)},o.prototype.hide=function(){g.prototype.hide.call(this),this.setEventId(null)},o.prototype.getFormData=function(){return this.getForm().serialize()},o.prototype.save=function(){var b=this.saveButton.find(n.LOADING_ICON_CONTAINER);b.removeClass("hidden"),this.disableButtons();var c=this.getFormData();return k.submitCreateUpdateForm(c).then(function(b){return b.validationerror?this.reloadBodyContent(c,!0):(this.hide(),void(this.hasEventId()?a("body").trigger(j.updated,[b.event]):a("body").trigger(j.created,[b.event])))}.bind(this)).always(function(){b.addClass("hidden"),this.enableButtons()}.bind(this))["catch"](d.exception)},o.prototype.registerEventListeners=function(){g.prototype.registerEventListeners.call(this),this.getModal().on(f.events.activate,n.SAVE_BUTTON,function(a,b){this.getForm().submit(),b.originalEvent.preventDefault(),a.stopPropagation()}.bind(this)),this.getModal().on("submit",function(a){this.save(),a.preventDefault(),a.stopPropagation()}.bind(this)),this.getModal().on(f.events.activate,n.MORELESS_BUTTON,function(a,b){this.toggleMoreLessButton(),b.originalEvent.preventDefault(),a.stopPropagation()}.bind(this)),this.getModal().on(l.events.ADVANCED_SHOWN,function(){this.setLessButton()}.bind(this)),this.getModal().on(l.events.ADVANCED_HIDDEN,function(){this.setMoreButton()}.bind(this))},m||(h.register(o.TYPE,o,"calendar/modal_event_form"),m=!0),o});
|
2
calendar/amd/build/summary_modal.min.js
vendored
2
calendar/amd/build/summary_modal.min.js
vendored
|
@ -1 +1 @@
|
|||
define(["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/calendar_repository","core_calendar/calendar_events"],function(a,b,c,d,e,f,g,h,i,j){var k=!1,l={ROOT:"[data-region='summary-modal-container']",EDIT_BUTTON:'[data-action="edit"]',DELETE_BUTTON:'[data-action="delete"]',EVENT_LINK:'[data-action="event-link"]'},m=function(a){e.call(this,a),this.getFooter().find(l.EDIT_BUTTON).length||c.exception({message:"No edit button found"}),this.getFooter().find(l.DELETE_BUTTON).length||c.exception({message:"No delete button found"})};return m.TYPE="core_calendar-event_summary",m.prototype=Object.create(e.prototype),m.prototype.constructor=m,m.prototype.registerEventListeners=function(){e.prototype.registerEventListeners.call(this);var a=g.create({type:g.types.CONFIRM},this.getFooter().find(l.DELETE_BUTTON)).then(function(a){return b.get_string("confirm").then(function(b){a.setTitle(b)}.bind(this))["catch"](c.exception),a.getRoot().on(h.yes,function(){var b=this.getBody().find(l.ROOT).attr("data-event-id");i.deleteEvent(b).done(function(){a.getRoot().trigger(j.deleted,b),window.location.reload()}).fail(c.exception)}.bind(this)),a}.bind(this));this.getRoot().on(h.bodyRendered,function(){var c=this.getBody().find(l.ROOT).attr("data-event-title");a.then(function(a){a.setBody(b.get_string("confirmeventdelete","core_calendar",c))})}.bind(this))},k||(f.register(m.TYPE,m,"core_calendar/event_summary_modal"),k=!0),m});
|
||||
define(["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/repository","core_calendar/events"],function(a,b,c,d,e,f,g,h,i,j){var k=!1,l={ROOT:"[data-region='summary-modal-container']",EDIT_BUTTON:'[data-action="edit"]',DELETE_BUTTON:'[data-action="delete"]'},m=function(a){e.call(this,a),this.getEditButton().length||c.exception({message:"No edit button found"}),this.getDeleteButton().length||c.exception({message:"No delete button found"})};return m.TYPE="core_calendar-event_summary",m.prototype=Object.create(e.prototype),m.prototype.constructor=m,m.prototype.getEditButton=function(){return"undefined"==typeof this.editButton&&(this.editButton=this.getFooter().find(l.EDIT_BUTTON)),this.editButton},m.prototype.getDeleteButton=function(){return"undefined"==typeof this.deleteButton&&(this.deleteButton=this.getFooter().find(l.DELETE_BUTTON)),this.deleteButton},m.prototype.getEventId=function(){return this.getBody().find(l.ROOT).attr("data-event-id")},m.prototype.registerEventListeners=function(){e.prototype.registerEventListeners.call(this);var f=g.create({type:g.types.CONFIRM},this.getDeleteButton()).then(function(b){return b.getRoot().on(h.yes,function(){var b=this.getEventId();i.deleteEvent(b).then(function(){a("body").trigger(j.deleted,[b]),this.hide()}.bind(this))["catch"](c.exception)}.bind(this)),b}.bind(this));this.getRoot().on(h.bodyRendered,function(){var a=this.getBody().find(l.ROOT).attr("data-event-title");f.then(function(c){c.setBody(b.get_string("confirmeventdelete","core_calendar",a))})}.bind(this)),d.define(this.getEditButton(),[d.events.activate]),this.getEditButton().on(d.events.activate,function(b,c){a("body").trigger(j.editEvent,[this.getEventId()]),this.hide(),b.preventDefault(),b.stopPropagation(),c.originalEvent.preventDefault(),c.originalEvent.stopPropagation()}.bind(this))},k||(f.register(m.TYPE,m,"core_calendar/event_summary_modal"),k=!0),m});
|
|
@ -14,128 +14,215 @@
|
|||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* A javascript module to calendar events.
|
||||
* 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 Simey Lameze <simey@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/ajax', 'core/str', 'core/templates', 'core/notification', 'core/custom_interaction_events',
|
||||
'core/modal_factory', 'core_calendar/summary_modal', 'core/modal_events', 'core_calendar/calendar_repository'],
|
||||
function($, Ajax, Str, Templates, Notification, CustomEvents, ModalFactory, SummaryModal, ModalEvents, CalendarRepository) {
|
||||
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'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
Ajax,
|
||||
Str,
|
||||
Templates,
|
||||
Notification,
|
||||
CustomEvents,
|
||||
ModalEvents,
|
||||
ModalFactory,
|
||||
ModalEventForm,
|
||||
SummaryModal,
|
||||
CalendarRepository,
|
||||
CalendarEvents
|
||||
) {
|
||||
|
||||
var SELECTORS = {
|
||||
ROOT: "[data-region='calendar']",
|
||||
EVENT_LINK: "[data-action='view-event']",
|
||||
};
|
||||
var SELECTORS = {
|
||||
ROOT: "[data-region='calendar']",
|
||||
EVENT_LINK: "[data-action='view-event']",
|
||||
NEW_EVENT_BUTTON: "[data-action='new-event-button']"
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the event type lang string.
|
||||
*
|
||||
* @param {String} eventType The event type.
|
||||
* @return {promise} The lang string promise.
|
||||
*/
|
||||
var getEventType = function(eventType) {
|
||||
var lang = 'type' + eventType;
|
||||
return Str.get_string(lang, 'core_calendar').then(function(langStr) {
|
||||
return langStr;
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Get the event type lang string.
|
||||
*
|
||||
* @param {String} eventType The event type.
|
||||
* @return {promise} The lang string promise.
|
||||
*/
|
||||
var getEventType = function(eventType) {
|
||||
var lang = 'type' + eventType;
|
||||
return Str.get_string(lang, 'core_calendar').then(function(langStr) {
|
||||
return langStr;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the event source.
|
||||
*
|
||||
* @param {Object} subscription The event subscription object.
|
||||
* @return {promise} The lang string promise.
|
||||
*/
|
||||
var getEventSource = function(subscription) {
|
||||
return Str.get_string('subsource', 'core_calendar', subscription).then(function(langStr) {
|
||||
if (subscription.url) {
|
||||
return '<a href="' + subscription.url + '">' + langStr + '</a>';
|
||||
}
|
||||
return langStr;
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Get the event source.
|
||||
*
|
||||
* @param {Object} subscription The event subscription object.
|
||||
* @return {promise} The lang string promise.
|
||||
*/
|
||||
var getEventSource = function(subscription) {
|
||||
return Str.get_string('subsource', 'core_calendar', subscription).then(function(langStr) {
|
||||
if (subscription.url) {
|
||||
return '<a href="' + subscription.url + '">' + langStr + '</a>';
|
||||
}
|
||||
return langStr;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the event summary modal.
|
||||
*
|
||||
* @param {Number} eventId The calendar event id.
|
||||
*/
|
||||
var renderEventSummaryModal = function(eventId) {
|
||||
// Calendar repository promise.
|
||||
CalendarRepository.getEventById(eventId).then(function(getEventResponse) {
|
||||
if (!getEventResponse.event) {
|
||||
throw new Error('Error encountered while trying to fetch calendar event with ID: ' + eventId);
|
||||
}
|
||||
var eventData = getEventResponse.event;
|
||||
var eventTypePromise = getEventType(eventData.eventtype);
|
||||
/**
|
||||
* Render the event summary modal.
|
||||
*
|
||||
* @param {Number} eventId The calendar event id.
|
||||
*/
|
||||
var renderEventSummaryModal = function(eventId) {
|
||||
// Calendar repository promise.
|
||||
CalendarRepository.getEventById(eventId).then(function(getEventResponse) {
|
||||
if (!getEventResponse.event) {
|
||||
throw new Error('Error encountered while trying to fetch calendar event with ID: ' + eventId);
|
||||
}
|
||||
var eventData = getEventResponse.event;
|
||||
var eventTypePromise = getEventType(eventData.eventtype);
|
||||
|
||||
// If the calendar event has event source, get the source's language string/link.
|
||||
if (eventData.displayeventsource) {
|
||||
eventData.subscription = JSON.parse(eventData.subscription);
|
||||
var eventSourceParams = {
|
||||
url: eventData.subscription.url,
|
||||
name: eventData.subscription.name
|
||||
};
|
||||
var eventSourcePromise = getEventSource(eventSourceParams);
|
||||
// If the calendar event has event source, get the source's language string/link.
|
||||
if (eventData.displayeventsource) {
|
||||
eventData.subscription = JSON.parse(eventData.subscription);
|
||||
var eventSourceParams = {
|
||||
url: eventData.subscription.url,
|
||||
name: eventData.subscription.name
|
||||
};
|
||||
var eventSourcePromise = getEventSource(eventSourceParams);
|
||||
|
||||
// Return event data with event type and event source info.
|
||||
return $.when(eventTypePromise, eventSourcePromise).then(function(eventType, eventSource) {
|
||||
eventData.eventtype = eventType;
|
||||
eventData.source = eventSource;
|
||||
return eventData;
|
||||
});
|
||||
}
|
||||
|
||||
// Return event data with event type info.
|
||||
return eventTypePromise.then(function(eventType) {
|
||||
// Return event data with event type and event source info.
|
||||
return $.when(eventTypePromise, eventSourcePromise).then(function(eventType, eventSource) {
|
||||
eventData.eventtype = eventType;
|
||||
eventData.source = eventSource;
|
||||
return eventData;
|
||||
});
|
||||
|
||||
}).then(function(eventData) {
|
||||
// Build the modal parameters from the event data.
|
||||
var modalParams = {
|
||||
title: eventData.name,
|
||||
type: SummaryModal.TYPE,
|
||||
body: Templates.render('core_calendar/event_summary_body', eventData)
|
||||
};
|
||||
if (!eventData.caneditevent) {
|
||||
modalParams.footer = '';
|
||||
}
|
||||
// Create the modal.
|
||||
return ModalFactory.create(modalParams);
|
||||
|
||||
}).done(function(modal) {
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
// Finally, render the modal!
|
||||
modal.show();
|
||||
|
||||
}).fail(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register event listeners for the module.
|
||||
*/
|
||||
var registerEventListeners = function() {
|
||||
// Bind click events to event links.
|
||||
$(SELECTORS.EVENT_LINK).click(function(e) {
|
||||
e.preventDefault();
|
||||
var eventId = $(this).attr('data-event-id');
|
||||
renderEventSummaryModal(eventId);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
registerEventListeners();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Return event data with event type info.
|
||||
return eventTypePromise.then(function(eventType) {
|
||||
eventData.eventtype = eventType;
|
||||
return eventData;
|
||||
});
|
||||
|
||||
}).then(function(eventData) {
|
||||
// Build the modal parameters from the event data.
|
||||
var modalParams = {
|
||||
title: eventData.name,
|
||||
type: SummaryModal.TYPE,
|
||||
body: Templates.render('core_calendar/event_summary_body', eventData)
|
||||
};
|
||||
if (!eventData.caneditevent) {
|
||||
modalParams.footer = '';
|
||||
}
|
||||
// Create the modal.
|
||||
return ModalFactory.create(modalParams);
|
||||
|
||||
}).done(function(modal) {
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
// Finally, render the modal!
|
||||
modal.show();
|
||||
|
||||
}).fail(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the event form modal for creating new events and
|
||||
* editing existing events.
|
||||
*
|
||||
* @method registerEventFormModal
|
||||
* @param {object} root The calendar root element
|
||||
* @return {object} The create modal promise
|
||||
*/
|
||||
var registerEventFormModal = function(root) {
|
||||
var newEventButton = root.find(SELECTORS.NEW_EVENT_BUTTON);
|
||||
var contextId = newEventButton.attr('data-context-id');
|
||||
|
||||
return ModalFactory.create(
|
||||
{
|
||||
type: ModalEventForm.TYPE,
|
||||
large: true,
|
||||
templateContext: {
|
||||
contextid: contextId
|
||||
}
|
||||
},
|
||||
newEventButton
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen to and handle any calendar events fired by the calendar UI.
|
||||
*
|
||||
* @method registerCalendarEventListeners
|
||||
* @param {object} root The calendar root element
|
||||
* @param {object} eventFormModalPromise A promise reolved with the event form modal
|
||||
*/
|
||||
var registerCalendarEventListeners = function(root, eventFormModalPromise) {
|
||||
var body = $('body');
|
||||
|
||||
// TODO: Replace these with actual logic to update
|
||||
// the UI without having to force a page reload.
|
||||
body.on(CalendarEvents.created, function() { window.location.reload(); });
|
||||
body.on(CalendarEvents.deleted, function() { window.location.reload(); });
|
||||
body.on(CalendarEvents.updated, function() { window.location.reload(); });
|
||||
|
||||
eventFormModalPromise.then(function(modal) {
|
||||
// When something within the calendar tells us the user wants
|
||||
// to edit an event then show the event form modal.
|
||||
body.on(CalendarEvents.editEvent, function(e, eventId) {
|
||||
modal.setEventId(eventId);
|
||||
modal.show();
|
||||
});
|
||||
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Register event listeners for the module.
|
||||
*/
|
||||
var registerEventListeners = function() {
|
||||
var root = $(SELECTORS.ROOT);
|
||||
|
||||
// Bind click events to event links.
|
||||
$(SELECTORS.EVENT_LINK).click(function(e) {
|
||||
e.preventDefault();
|
||||
var eventId = $(this).attr('data-event-id');
|
||||
renderEventSummaryModal(eventId);
|
||||
});
|
||||
|
||||
var eventFormPromise = registerEventFormModal(root);
|
||||
registerCalendarEventListeners(root, eventFormPromise);
|
||||
};
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
registerEventListeners();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
282
calendar/amd/src/event_form.js
Normal file
282
calendar/amd/src/event_form.js
Normal file
|
@ -0,0 +1,282 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* A javascript module to enhance the event form.
|
||||
*
|
||||
* @module core_calendar/event_form
|
||||
* @package core_calendar
|
||||
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/templates'], function($, Templates) {
|
||||
|
||||
var SELECTORS = {
|
||||
EVENT_TYPE: '[name="eventtype"]',
|
||||
EVENT_COURSE_ID: '[name="courseid"]',
|
||||
EVENT_GROUP_COURSE_ID: '[name="groupcourseid"]',
|
||||
EVENT_GROUP_ID: '[name="groupid"]',
|
||||
FORM_GROUP: '.form-group',
|
||||
SELECT_OPTION: 'option',
|
||||
ADVANCED_ELEMENT: '.fitem.advanced',
|
||||
FIELDSET_ADVANCED_ELEMENTS: 'fieldset.containsadvancedelements',
|
||||
MORELESS_TOGGLE: '.moreless-actions'
|
||||
};
|
||||
|
||||
var EVENT_TYPES = {
|
||||
USER: 'user',
|
||||
SITE: 'site',
|
||||
COURSE: 'course',
|
||||
GROUP: 'group'
|
||||
};
|
||||
|
||||
var EVENTS = {
|
||||
SHOW_ADVANCED: 'event_form-show-advanced',
|
||||
HIDE_ADVANCED: 'event_form-hide-advanced',
|
||||
ADVANCED_SHOWN: 'event_form-advanced-shown',
|
||||
ADVANCED_HIDDEN: 'event_form-advanced-hidden',
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the old show more / show less toggle added by the mform and destroy it.
|
||||
* We are handling the visibility of the advanced fields with the more/less button
|
||||
* in the footer of the modal that this form is rendered within.
|
||||
*
|
||||
* @method destroyOldMoreLessToggle
|
||||
* @param {object} formElement The root form element
|
||||
*/
|
||||
var destroyOldMoreLessToggle = function(formElement) {
|
||||
formElement.find(SELECTORS.FIELDSET_ADVANCED_ELEMENTS).removeClass('containsadvancedelements');
|
||||
var element = formElement.find(SELECTORS.MORELESS_TOGGLE);
|
||||
Templates.replaceNode(element, '', '');
|
||||
};
|
||||
|
||||
/**
|
||||
* Find each of the advanced form elements and make them visible.
|
||||
*
|
||||
* This function triggers the ADVANCED_SHOWN event for any other
|
||||
* component to handle (e.g. the event form modal).
|
||||
*
|
||||
* @method destroyOldMoreLessToggle
|
||||
* @param {object} formElement The root form element
|
||||
*/
|
||||
var showAdvancedElements = function(formElement) {
|
||||
formElement.find(SELECTORS.ADVANCED_ELEMENT).removeClass('hidden');
|
||||
formElement.trigger(EVENTS.ADVANCED_SHOWN);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find each of the advanced form elements and hide them.
|
||||
*
|
||||
* This function triggers the ADVANCED_HIDDEN event for any other
|
||||
* component to handle (e.g. the event form modal).
|
||||
*
|
||||
* @method hideAdvancedElements
|
||||
* @param {object} formElement The root form element
|
||||
*/
|
||||
var hideAdvancedElements = function(formElement) {
|
||||
formElement.find(SELECTORS.ADVANCED_ELEMENT).addClass('hidden');
|
||||
formElement.trigger(EVENTS.ADVANCED_HIDDEN);
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for any events telling this module to show or hide it's
|
||||
* advanced elements.
|
||||
*
|
||||
* This function listens for SHOW_ADVANCED and HIDE_ADVANCED.
|
||||
*
|
||||
* @method listenForShowHideEvents
|
||||
* @param {object} formElement The root form element
|
||||
*/
|
||||
var listenForShowHideEvents = function(formElement) {
|
||||
formElement.on(EVENTS.SHOW_ADVANCED, function() {
|
||||
showAdvancedElements(formElement);
|
||||
});
|
||||
|
||||
formElement.on(EVENTS.HIDE_ADVANCED, function() {
|
||||
hideAdvancedElements(formElement);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the group id select element in the event form and pull out
|
||||
* the course id from the value to allow us to toggle other select
|
||||
* elements based on the course id for the group a user selects.
|
||||
*
|
||||
* This is a little hacky but I couldn't find a better way to pass
|
||||
* the course id for each group id with the limitations of mforms.
|
||||
*
|
||||
* The group id options are rendered with a value like:
|
||||
* "<courseid>-<groupid>"
|
||||
* E.g.
|
||||
* For a group with id 10 in a course with id 3 the value of the
|
||||
* option will be 3-10.
|
||||
*
|
||||
* @method parseGroupSelect
|
||||
* @param {object} formElement The root form element
|
||||
*/
|
||||
var parseGroupSelect = function(formElement) {
|
||||
formElement.find(SELECTORS.EVENT_GROUP_ID)
|
||||
.find(SELECTORS.SELECT_OPTION)
|
||||
.each(function(index, element) {
|
||||
var element = $(element);
|
||||
var value = element.attr('value');
|
||||
var splits = value.split('-');
|
||||
var courseId = splits[0];
|
||||
|
||||
element.attr('data-course-id', courseId);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the visibility of the secondary select elements based on
|
||||
* the event type the user has selected.
|
||||
*
|
||||
* There are 3 secondary select elements within the form:
|
||||
* - course: a list of all courses a user can add course events to
|
||||
* - group course: a list of all courses a user can add group events to.
|
||||
* this list can be different from the course list above.
|
||||
* - group: a list of all groups a user can add an event to. This list will
|
||||
* be filtered further based on the group course selected.
|
||||
*
|
||||
* There are 4 event types:
|
||||
* - user: none of the secondary selects should be visible.
|
||||
* - site: none of the secondary selects should be visible.
|
||||
* - course: "course" select should be visible and both "group course"
|
||||
* and "group" should be hidden.
|
||||
* - group: "group course" and "group" should be visible and "course"
|
||||
* should be hidden.
|
||||
*
|
||||
* @method hideTypeSubSelects
|
||||
* @param {object} formElement The root form element
|
||||
*/
|
||||
var hideTypeSubSelects = function(formElement) {
|
||||
var typeSelect = formElement.find(SELECTORS.EVENT_TYPE);
|
||||
var eventType = typeSelect.val();
|
||||
var courseIdSelect = formElement.find(SELECTORS.EVENT_COURSE_ID)
|
||||
.closest(SELECTORS.FORM_GROUP)
|
||||
.removeClass('hidden');
|
||||
var groupCourseIdSelect = formElement.find(SELECTORS.EVENT_GROUP_COURSE_ID)
|
||||
.closest(SELECTORS.FORM_GROUP)
|
||||
.removeClass('hidden');
|
||||
var groupIdSelect = formElement.find(SELECTORS.EVENT_GROUP_ID)
|
||||
.closest(SELECTORS.FORM_GROUP)
|
||||
.removeClass('hidden');
|
||||
|
||||
// Hide the unreleated selectors for the given event type.
|
||||
switch (eventType) {
|
||||
case EVENT_TYPES.COURSE:
|
||||
groupCourseIdSelect.addClass('hidden');
|
||||
groupIdSelect.addClass('hidden');
|
||||
break;
|
||||
case EVENT_TYPES.GROUP:
|
||||
courseIdSelect.addClass('hidden');
|
||||
break;
|
||||
default:
|
||||
courseIdSelect.addClass('hidden');
|
||||
groupCourseIdSelect.addClass('hidden');
|
||||
groupIdSelect.addClass('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for when the user changes the event type select in the
|
||||
* form and then toggle the visibility of the appropriate secondary
|
||||
* select elements.
|
||||
*
|
||||
* See: hideTypeSubSelects.
|
||||
*
|
||||
* @method addTypeSelectListeners
|
||||
* @param {object} formElement The root form element
|
||||
*/
|
||||
var addTypeSelectListeners = function(formElement) {
|
||||
var typeSelect = formElement.find(SELECTORS.EVENT_TYPE);
|
||||
|
||||
typeSelect.on('change', function() {
|
||||
hideTypeSubSelects(formElement);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for when the user changes the group course when configuring
|
||||
* a group event and filter the options in the group select to only
|
||||
* show the groups available within the course the user has selected.
|
||||
*
|
||||
* @method addCourseGroupSelectListeners
|
||||
* @param {object} formElement The root form element
|
||||
*/
|
||||
var addCourseGroupSelectListeners = function(formElement) {
|
||||
var courseGroupSelect = formElement.find(SELECTORS.EVENT_GROUP_COURSE_ID);
|
||||
var groupSelect = formElement.find(SELECTORS.EVENT_GROUP_ID);
|
||||
var groupSelectOptions = groupSelect.find(SELECTORS.SELECT_OPTION);
|
||||
var filterGroupSelectOptions = function() {
|
||||
var selectedCourseId = courseGroupSelect.val();
|
||||
var selectedIndex = null;
|
||||
|
||||
groupSelectOptions.each(function(index, element) {
|
||||
element = $(element);
|
||||
|
||||
if (element.attr('data-course-id') == selectedCourseId) {
|
||||
element.removeClass('hidden');
|
||||
element.prop('disabled', false);
|
||||
|
||||
if (selectedIndex === null) {
|
||||
selectedIndex = index;
|
||||
}
|
||||
} else {
|
||||
element.addClass('hidden');
|
||||
element.prop('disabled', true);
|
||||
}
|
||||
});
|
||||
|
||||
groupSelect.prop('selectedIndex', selectedIndex);
|
||||
};
|
||||
|
||||
courseGroupSelect.on('change', filterGroupSelectOptions);
|
||||
filterGroupSelectOptions();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise all of the form enhancementds.
|
||||
*
|
||||
* @method init
|
||||
* @param {string} formId The value of the form's id attribute
|
||||
* @param {bool} hasError If the form has errors rendered form the server.
|
||||
*/
|
||||
var init = function(formId, hasError) {
|
||||
var formElement = $('#' + formId);
|
||||
|
||||
listenForShowHideEvents(formElement);
|
||||
destroyOldMoreLessToggle(formElement);
|
||||
hideTypeSubSelects(formElement);
|
||||
parseGroupSelect(formElement);
|
||||
addTypeSelectListeners(formElement);
|
||||
addCourseGroupSelectListeners(formElement);
|
||||
|
||||
// If we know that the form has been rendered with server side
|
||||
// errors then we need to display all of the elements in the form
|
||||
// in case one of those elements has the error.
|
||||
if (hasError) {
|
||||
showAdvancedElements(formElement);
|
||||
} else {
|
||||
hideAdvancedElements(formElement);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
init: init,
|
||||
events: EVENTS,
|
||||
};
|
||||
});
|
429
calendar/amd/src/modal_event_form.js
Normal file
429
calendar/amd/src/modal_event_form.js
Normal file
|
@ -0,0 +1,429 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contain the logic for the quick add or update event modal.
|
||||
*
|
||||
* @module calendar/modal_quick_add_event
|
||||
* @class modal_quick_add_event
|
||||
* @package core
|
||||
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define([
|
||||
'jquery',
|
||||
'core/event',
|
||||
'core/str',
|
||||
'core/notification',
|
||||
'core/templates',
|
||||
'core/custom_interaction_events',
|
||||
'core/modal',
|
||||
'core/modal_registry',
|
||||
'core/fragment',
|
||||
'core_calendar/events',
|
||||
'core_calendar/repository',
|
||||
'core_calendar/event_form'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
Event,
|
||||
Str,
|
||||
Notification,
|
||||
Templates,
|
||||
CustomEvents,
|
||||
Modal,
|
||||
ModalRegistry,
|
||||
Fragment,
|
||||
CalendarEvents,
|
||||
Repository,
|
||||
EventForm
|
||||
) {
|
||||
|
||||
var registered = false;
|
||||
var SELECTORS = {
|
||||
MORELESS_BUTTON: '[data-action="more-less-toggle"]',
|
||||
SAVE_BUTTON: '[data-action="save"]',
|
||||
LOADING_ICON_CONTAINER: '[data-region="loading-icon-container"]',
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor for the Modal.
|
||||
*
|
||||
* @param {object} root The root jQuery element for the modal
|
||||
*/
|
||||
var ModalEventForm = function(root) {
|
||||
Modal.call(this, root);
|
||||
this.eventId = null;
|
||||
this.reloadingBody = false;
|
||||
this.reloadingTitle = false;
|
||||
this.saveButton = this.getFooter().find(SELECTORS.SAVE_BUTTON);
|
||||
this.moreLessButton = this.getFooter().find(SELECTORS.MORELESS_BUTTON);
|
||||
};
|
||||
|
||||
ModalEventForm.TYPE = 'core_calendar-modal_event_form';
|
||||
ModalEventForm.prototype = Object.create(Modal.prototype);
|
||||
ModalEventForm.prototype.constructor = ModalEventForm;
|
||||
|
||||
/**
|
||||
* Set the event id to the given value.
|
||||
*
|
||||
* @method setEventId
|
||||
* @param {int} id The event id
|
||||
*/
|
||||
ModalEventForm.prototype.setEventId = function(id) {
|
||||
this.eventId = id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the current event id, if any.
|
||||
*
|
||||
* @method getEventId
|
||||
* @return {int|null} The event id
|
||||
*/
|
||||
ModalEventForm.prototype.getEventId = function() {
|
||||
return this.eventId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the modal has an event id.
|
||||
*
|
||||
* @method hasEventId
|
||||
* @return {bool}
|
||||
*/
|
||||
ModalEventForm.prototype.hasEventId = function() {
|
||||
return this.eventId !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the form element from the modal.
|
||||
*
|
||||
* @method getForm
|
||||
* @return {object}
|
||||
*/
|
||||
ModalEventForm.prototype.getForm = function() {
|
||||
return this.getBody().find('form');
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable the buttons in the footer.
|
||||
*
|
||||
* @method disableButtons
|
||||
*/
|
||||
ModalEventForm.prototype.disableButtons = function() {
|
||||
this.saveButton.prop('disabled', true);
|
||||
this.moreLessButton.prop('disabled', true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable the buttons in the footer.
|
||||
*
|
||||
* @method enableButtons
|
||||
*/
|
||||
ModalEventForm.prototype.enableButtons = function() {
|
||||
this.saveButton.prop('disabled', false);
|
||||
this.moreLessButton.prop('disabled', false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the more/less button in the footer to the "more"
|
||||
* state.
|
||||
*
|
||||
* @method setMoreButton
|
||||
*/
|
||||
ModalEventForm.prototype.setMoreButton = function() {
|
||||
this.moreLessButton.attr('data-collapsed', 'true');
|
||||
Str.get_string('more', 'calendar').then(function(string) {
|
||||
this.moreLessButton.text(string);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the more/less button in the footer to the "less"
|
||||
* state.
|
||||
*
|
||||
* @method setLessButton
|
||||
*/
|
||||
ModalEventForm.prototype.setLessButton = function() {
|
||||
this.moreLessButton.attr('data-collapsed', 'false');
|
||||
Str.get_string('less', 'calendar').then(function(string) {
|
||||
this.moreLessButton.text(string);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the more/less button in the footer from the current
|
||||
* state to it's opposite state.
|
||||
*
|
||||
* @method toggleMoreLessButton
|
||||
*/
|
||||
ModalEventForm.prototype.toggleMoreLessButton = function() {
|
||||
var form = this.getForm();
|
||||
|
||||
if (this.moreLessButton.attr('data-collapsed') == 'true') {
|
||||
form.trigger(EventForm.events.SHOW_ADVANCED);
|
||||
this.setLessButton();
|
||||
} else {
|
||||
form.trigger(EventForm.events.HIDE_ADVANCED);
|
||||
this.setMoreButton();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reload the title for the modal to the appropriate value
|
||||
* depending on whether we are creating a new event or
|
||||
* editing an existing event.
|
||||
*
|
||||
* @method reloadTitleContent
|
||||
* @return {object} A promise resolved with the new title text
|
||||
*/
|
||||
ModalEventForm.prototype.reloadTitleContent = function() {
|
||||
if (this.reloadingTitle) {
|
||||
return this.titlePromise;
|
||||
}
|
||||
|
||||
this.reloadingTitle = true;
|
||||
|
||||
if (this.hasEventId()) {
|
||||
this.titlePromise = Str.get_string('editevent', 'calendar');
|
||||
} else {
|
||||
this.titlePromise = Str.get_string('newevent', 'calendar');
|
||||
}
|
||||
|
||||
this.titlePromise.then(function(string) {
|
||||
this.setTitle(string);
|
||||
return string;
|
||||
}.bind(this))
|
||||
.always(function() {
|
||||
this.reloadingTitle = false;
|
||||
return;
|
||||
}.bind(this));
|
||||
|
||||
return this.titlePromise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a request to the server to get the event_form in a fragment
|
||||
* and render the result in the body of the modal.
|
||||
*
|
||||
* If serialised form data is provided then it will be sent in the
|
||||
* request to the server to have the form rendered with the data. This
|
||||
* is used when the form had a server side error and we need the server
|
||||
* to re-render it for us to display the error to the user.
|
||||
*
|
||||
* @method reloadBodyContent
|
||||
* @param {string} formData The serialised form data
|
||||
* @param {bool} hasError True if we know the form data is erroneous
|
||||
* @return {object} A promise resolved with the fragment html and js from
|
||||
*/
|
||||
ModalEventForm.prototype.reloadBodyContent = function(formData, hasError) {
|
||||
if (this.reloadingBody) {
|
||||
return this.bodyPromise;
|
||||
}
|
||||
|
||||
this.reloadingBody = true;
|
||||
this.disableButtons();
|
||||
|
||||
var contextId = this.saveButton.attr('data-context-id');
|
||||
var args = {};
|
||||
|
||||
if (this.hasEventId()) {
|
||||
args.eventid = this.getEventId();
|
||||
}
|
||||
|
||||
if (typeof formData !== 'undefined') {
|
||||
args.formdata = formData;
|
||||
}
|
||||
|
||||
args.haserror = (typeof hasError == 'undefined') ? false : hasError;
|
||||
|
||||
this.bodyPromise = Fragment.loadFragment('calendar', 'event_form', contextId, args);
|
||||
|
||||
this.setBody(this.bodyPromise);
|
||||
|
||||
this.bodyPromise.then(function() {
|
||||
this.enableButtons();
|
||||
return;
|
||||
}.bind(this))
|
||||
.catch(Notification.exception)
|
||||
.always(function() {
|
||||
this.reloadingBody = false;
|
||||
return;
|
||||
}.bind(this));
|
||||
|
||||
return this.bodyPromise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reload both the title and body content.
|
||||
*
|
||||
* @method reloadAllContent
|
||||
* @return {object} promise
|
||||
*/
|
||||
ModalEventForm.prototype.reloadAllContent = function() {
|
||||
return $.when(this.reloadTitleContent(), this.reloadBodyContent());
|
||||
};
|
||||
|
||||
/**
|
||||
* Kick off a reload the modal content before showing it. This
|
||||
* is to allow us to re-use the same modal for creating and
|
||||
* editing different events within the page.
|
||||
*
|
||||
* We do the reload when showing the modal rather than hiding it
|
||||
* to save a request to the server if the user closes the modal
|
||||
* and never re-opens it.
|
||||
*
|
||||
* @method show
|
||||
*/
|
||||
ModalEventForm.prototype.show = function() {
|
||||
this.reloadAllContent();
|
||||
Modal.prototype.show.call(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the event id from the modal when it's closed so
|
||||
* that it is loaded fresh next time it's displayed.
|
||||
*
|
||||
* The event id will be set by the calling code if it wants
|
||||
* to edit a specific event.
|
||||
*
|
||||
* @method hide
|
||||
*/
|
||||
ModalEventForm.prototype.hide = function() {
|
||||
Modal.prototype.hide.call(this);
|
||||
this.setEventId(null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the serialised form data.
|
||||
*
|
||||
* @method getFormData
|
||||
* @return {string} serialised form data
|
||||
*/
|
||||
ModalEventForm.prototype.getFormData = function() {
|
||||
return this.getForm().serialize();
|
||||
};
|
||||
|
||||
/**
|
||||
* Send the form data to the server to create or update
|
||||
* an event.
|
||||
*
|
||||
* If there is a server side validation error then we re-request the
|
||||
* rendered form (with the data) from the server in order to get the
|
||||
* server side errors to display.
|
||||
*
|
||||
* On success the modal is hidden and the page is reloaded so that the
|
||||
* new event will display.
|
||||
*
|
||||
* @method save
|
||||
* @return {object} A promise
|
||||
*/
|
||||
ModalEventForm.prototype.save = function() {
|
||||
var loadingContainer = this.saveButton.find(SELECTORS.LOADING_ICON_CONTAINER);
|
||||
|
||||
loadingContainer.removeClass('hidden');
|
||||
this.disableButtons();
|
||||
|
||||
var formData = this.getFormData();
|
||||
// Send the form data to the server for processing.
|
||||
return Repository.submitCreateUpdateForm(formData)
|
||||
.then(function(response) {
|
||||
if (response.validationerror) {
|
||||
// If there was a server side validation error then
|
||||
// we need to re-request the rendered form from the server
|
||||
// in order to display the error for the user.
|
||||
return this.reloadBodyContent(formData, true);
|
||||
} else {
|
||||
// No problemo! Our work here is done.
|
||||
this.hide();
|
||||
|
||||
// Trigger the appropriate calendar event so that the view can
|
||||
// be updated.
|
||||
if (this.hasEventId()) {
|
||||
$('body').trigger(CalendarEvents.updated, [response.event]);
|
||||
} else {
|
||||
$('body').trigger(CalendarEvents.created, [response.event]);
|
||||
}
|
||||
}
|
||||
}.bind(this))
|
||||
.always(function() {
|
||||
// Regardless of success or error we should always stop
|
||||
// the loading icon and re-enable the buttons.
|
||||
loadingContainer.addClass('hidden');
|
||||
this.enableButtons();
|
||||
}.bind(this))
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up all of the event handling for the modal.
|
||||
*
|
||||
* @method registerEventListeners
|
||||
*/
|
||||
ModalEventForm.prototype.registerEventListeners = function() {
|
||||
// Apply parent event listeners.
|
||||
Modal.prototype.registerEventListeners.call(this);
|
||||
|
||||
// When the user clicks the save button we trigger the form submission. We need to
|
||||
// trigger an actual submission because there is some JS code in the form that is
|
||||
// listening for this event and doing some stuff (e.g. saving draft areas etc).
|
||||
this.getModal().on(CustomEvents.events.activate, SELECTORS.SAVE_BUTTON, function(e, data) {
|
||||
this.getForm().submit();
|
||||
data.originalEvent.preventDefault();
|
||||
e.stopPropagation();
|
||||
}.bind(this));
|
||||
|
||||
// Catch the submit event before it is actually processed by the browser and
|
||||
// prevent the submission. We'll take it from here.
|
||||
this.getModal().on('submit', function(e) {
|
||||
this.save();
|
||||
|
||||
// Stop the form from actually submitting and prevent it's
|
||||
// propagation because we have already handled the event.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}.bind(this));
|
||||
|
||||
// Toggle the state of the more/less button in the footer.
|
||||
this.getModal().on(CustomEvents.events.activate, SELECTORS.MORELESS_BUTTON, function(e, data) {
|
||||
this.toggleMoreLessButton();
|
||||
|
||||
data.originalEvent.preventDefault();
|
||||
e.stopPropagation();
|
||||
}.bind(this));
|
||||
|
||||
// When the event form tells us that the advanced fields are shown
|
||||
// then the more/less button should be set to less to allow the user
|
||||
// to hide the advanced fields.
|
||||
this.getModal().on(EventForm.events.ADVANCED_SHOWN, function() {
|
||||
this.setLessButton();
|
||||
}.bind(this));
|
||||
|
||||
// When the event form tells us that the advanced fields are hidden
|
||||
// then the more/less button should be set to more to allow the user
|
||||
// to show the advanced fields.
|
||||
this.getModal().on(EventForm.events.ADVANCED_HIDDEN, function() {
|
||||
this.setMoreButton();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
// Automatically register with the modal registry the first time this module is imported so that you can create modals
|
||||
// of this type using the modal factory.
|
||||
if (!registered) {
|
||||
ModalRegistry.register(ModalEventForm.TYPE, ModalEventForm, 'calendar/modal_event_form');
|
||||
registered = true;
|
||||
}
|
||||
|
||||
return ModalEventForm;
|
||||
});
|
|
@ -22,8 +22,8 @@
|
|||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/str', 'core/notification', 'core/custom_interaction_events', 'core/modal',
|
||||
'core/modal_registry', 'core/modal_factory', 'core/modal_events', 'core_calendar/calendar_repository',
|
||||
'core_calendar/calendar_events'],
|
||||
'core/modal_registry', 'core/modal_factory', 'core/modal_events', 'core_calendar/repository',
|
||||
'core_calendar/events'],
|
||||
function($, Str, Notification, CustomEvents, Modal, ModalRegistry, ModalFactory, ModalEvents, CalendarRepository,
|
||||
CalendarEvents) {
|
||||
|
||||
|
@ -32,7 +32,6 @@ define(['jquery', 'core/str', 'core/notification', 'core/custom_interaction_even
|
|||
ROOT: "[data-region='summary-modal-container']",
|
||||
EDIT_BUTTON: '[data-action="edit"]',
|
||||
DELETE_BUTTON: '[data-action="delete"]',
|
||||
EVENT_LINK: '[data-action="event-link"]'
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -43,11 +42,11 @@ define(['jquery', 'core/str', 'core/notification', 'core/custom_interaction_even
|
|||
var ModalEventSummary = function(root) {
|
||||
Modal.call(this, root);
|
||||
|
||||
if (!this.getFooter().find(SELECTORS.EDIT_BUTTON).length) {
|
||||
if (!this.getEditButton().length) {
|
||||
Notification.exception({message: 'No edit button found'});
|
||||
}
|
||||
|
||||
if (!this.getFooter().find(SELECTORS.DELETE_BUTTON).length) {
|
||||
if (!this.getDeleteButton().length) {
|
||||
Notification.exception({message: 'No delete button found'});
|
||||
}
|
||||
};
|
||||
|
@ -56,6 +55,48 @@ define(['jquery', 'core/str', 'core/notification', 'core/custom_interaction_even
|
|||
ModalEventSummary.prototype = Object.create(Modal.prototype);
|
||||
ModalEventSummary.prototype.constructor = ModalEventSummary;
|
||||
|
||||
/**
|
||||
* Get the edit button element from the footer. The button is cached
|
||||
* as it's not expected to change.
|
||||
*
|
||||
* @method getEditButton
|
||||
* @return {object} button element
|
||||
*/
|
||||
ModalEventSummary.prototype.getEditButton = function() {
|
||||
if (typeof this.editButton == 'undefined') {
|
||||
this.editButton = this.getFooter().find(SELECTORS.EDIT_BUTTON);
|
||||
}
|
||||
|
||||
return this.editButton;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the delete button element from the footer. The button is cached
|
||||
* as it's not expected to change.
|
||||
*
|
||||
* @method getDeleteButton
|
||||
* @return {object} button element
|
||||
*/
|
||||
ModalEventSummary.prototype.getDeleteButton = function() {
|
||||
if (typeof this.deleteButton == 'undefined') {
|
||||
this.deleteButton = this.getFooter().find(SELECTORS.DELETE_BUTTON);
|
||||
}
|
||||
|
||||
return this.deleteButton;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the id for the event being shown in this modal. This value is
|
||||
* not cached because it will change depending on which event is
|
||||
* being displayed.
|
||||
*
|
||||
* @method getEventId
|
||||
* @return {int}
|
||||
*/
|
||||
ModalEventSummary.prototype.getEventId = function() {
|
||||
return this.getBody().find(SELECTORS.ROOT).attr('data-event-id');
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up all of the event handling for the modal.
|
||||
*
|
||||
|
@ -64,28 +105,51 @@ define(['jquery', 'core/str', 'core/notification', 'core/custom_interaction_even
|
|||
ModalEventSummary.prototype.registerEventListeners = function() {
|
||||
// Apply parent event listeners.
|
||||
Modal.prototype.registerEventListeners.call(this);
|
||||
var confirmPromise = ModalFactory.create({
|
||||
type: ModalFactory.types.CONFIRM,
|
||||
}, this.getFooter().find(SELECTORS.DELETE_BUTTON)).then(function(modal) {
|
||||
Str.get_string('confirm').then(function(languagestring) {
|
||||
modal.setTitle(languagestring);
|
||||
}.bind(this)).catch(Notification.exception);
|
||||
|
||||
var confirmPromise = ModalFactory.create(
|
||||
{ type: ModalFactory.types.CONFIRM },
|
||||
this.getDeleteButton()
|
||||
).then(function(modal) {
|
||||
modal.getRoot().on(ModalEvents.yes, function() {
|
||||
var eventId = this.getBody().find(SELECTORS.ROOT).attr('data-event-id');
|
||||
CalendarRepository.deleteEvent(eventId).done(function() {
|
||||
modal.getRoot().trigger(CalendarEvents.deleted, eventId);
|
||||
window.location.reload();
|
||||
}).fail(Notification.exception);
|
||||
var eventId = this.getEventId();
|
||||
|
||||
CalendarRepository.deleteEvent(eventId)
|
||||
.then(function() {
|
||||
$('body').trigger(CalendarEvents.deleted, [eventId]);
|
||||
this.hide();
|
||||
}.bind(this))
|
||||
.catch(Notification.exception);
|
||||
}.bind(this));
|
||||
|
||||
return modal;
|
||||
}.bind(this));
|
||||
|
||||
// We have to wait for the modal to finish rendering in order to ensure that
|
||||
// the data-event-title property is available to use as the modal title.
|
||||
this.getRoot().on(ModalEvents.bodyRendered, function() {
|
||||
var eventTitle = this.getBody().find(SELECTORS.ROOT).attr('data-event-title');
|
||||
confirmPromise.then(function(modal) {
|
||||
modal.setBody(Str.get_string('confirmeventdelete', 'core_calendar', eventTitle));
|
||||
});
|
||||
}.bind(this));
|
||||
|
||||
CustomEvents.define(this.getEditButton(), [
|
||||
CustomEvents.events.activate
|
||||
]);
|
||||
|
||||
this.getEditButton().on(CustomEvents.events.activate, function(e, data) {
|
||||
// When the edit button is clicked we fire an event for the calendar UI to handle.
|
||||
// We don't care how the UI chooses to handle it.
|
||||
$('body').trigger(CalendarEvents.editEvent, [this.getEventId()]);
|
||||
// There is nothing else for us to do so let's hide.
|
||||
this.hide();
|
||||
|
||||
// We've handled this event so no need to propagate it.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
data.originalEvent.preventDefault();
|
||||
data.originalEvent.stopPropagation();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
// Automatically register with the modal registry the first time this module is imported so that you can create modals
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue