MDL-30811 output: Add support for session notifications
[moodle.git] / lib / amd / src / notification.js
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * A system for displaying notifications to users from the session.
18  *
19  * Wrapper for the YUI M.core.notification class. Allows us to
20  * use the YUI version in AMD code until it is replaced.
21  *
22  * @module     core/notification
23  * @class      notification
24  * @package    core
25  * @copyright  2015 Damyon Wiese <damyon@moodle.com>
26  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27  * @since      2.9
28  */
29 define(['core/yui', 'jquery', 'theme_bootstrapbase/bootstrap', 'core/templates', 'core/ajax', 'core/log'],
30 function(Y, $, bootstrap, templates, ajax, log) {
31     var notificationModule = {
32         types: {
33             'success':  'core/notification_success',
34             'info':     'core/notification_info',
35             'warning':  'core/notification_warning',
36             'error':    'core/notification_error',
37         },
39         fieldName: 'user-notifications',
41         fetchNotifications: function() {
42             var promises = ajax.call([{
43                 methodname: 'core_fetch_notifications',
44                 args: {
45                     contextid: notificationModule.contextid
46                 }
47             }]);
49             promises[0]
50                 .done(notificationModule.addNotifications)
51                 ;
53         },
55         addNotifications: function(notifications) {
56             if (!notifications) {
57                 notifications = [];
58             }
60             $.each(notifications, function(i, notification) {
61                 notificationModule.renderNotification(notification.template, notification.variables);
62             });
63         },
65         setupTargetRegion: function() {
66             var targetRegion = $('#' + notificationModule.fieldName);
67             if (targetRegion.length) {
68                 return;
69             }
71             var newRegion = $('<span>').attr('id', notificationModule.fieldName);
73             targetRegion = $('#region-main');
74             if (targetRegion.length) {
75                 return targetRegion.prepend(newRegion);
76             }
78             targetRegion = $('[role="main"]');
79             if (targetRegion.length) {
80                 return targetRegion.prepend(newRegion);
81             }
83             targetRegion = $('body');
84             return targetRegion.prepend(newRegion);
85         },
87         addNotification: function(notification) {
88             var template = notificationModule.types.error;
90             notification = $.extend({
91                     closebutton:    true,
92                     announce:       true,
93                     type:           'error'
94                 }, notification);
96             if (notification.template) {
97                 template = notification.template;
98                 delete notification.template;
99             } else if (notification.type){
100                 if (typeof notificationModule.types[notification.type] !== 'undefined') {
101                     template = notificationModule.types[notification.type];
102                 }
103                 delete notification.type;
104             }
106             return notificationModule.renderNotification(template, notification);
107         },
109         renderNotification: function(template, variables) {
110             if (typeof variables.message === 'undefined' || !variables.message) {
111                 log.debug('Notification received without content. Skipping.');
112                 return;
113             }
114             templates.render(template, variables)
115                 .done(function(html) {
116                     $('#' + notificationModule.fieldName).prepend(html);
117                 })
118                 .fail(notificationModule.exception)
119                 ;
120         },
122         alert: function(title, message, yesLabel) {
123             // Here we are wrapping YUI. This allows us to start transitioning, but
124             // wait for a good alternative without having inconsistent dialogues.
125             Y.use('moodle-core-notification-alert', function () {
126                 var alert = new M.core.alert({
127                     title : title,
128                     message : message,
129                     yesLabel: yesLabel
130                 });
132                 alert.show();
133             });
134         },
136         confirm: function(title, question, yesLabel, noLabel, callback) {
137             // Here we are wrapping YUI. This allows us to start transitioning, but
138             // wait for a good alternative without having inconsistent dialogues.
139             Y.use('moodle-core-notification-confirm', function () {
140                 var modal = new M.core.confirm({
141                     title : title,
142                     question : question,
143                     yesLabel: yesLabel,
144                     noLabel: noLabel
145                 });
147                 modal.on('complete-yes', function() {
148                     callback();
149                 });
150                 modal.show();
151             });
152         },
154         exception: function(ex) {
155             // Fudge some parameters.
156             if (ex.backtrace) {
157                 ex.lineNumber = ex.backtrace[0].line;
158                 ex.fileName = ex.backtrace[0].file;
159                 ex.fileName = '...' + ex.fileName.substr(ex.fileName.length - 20);
160                 ex.stack = ex.debuginfo;
161                 ex.name = ex.errorcode;
162             }
163             Y.use('moodle-core-notification-exception', function () {
164                 var modal = new M.core.exception(ex);
166                 modal.show();
167             });
168         }
169     };
171     return /** @alias module:core/notification */{
172         init: function(contextid, notifications) {
173             notificationModule.contextid = contextid;
175             // Setup the message target region if it isn't setup already
176             notificationModule.setupTargetRegion();
178             // Setup closing of bootstrap alerts.
179             $().alert();
181             // Add provided notifications.
182             notificationModule.addNotifications(notifications);
184             // Poll for any new notifications.
185             notificationModule.fetchNotifications();
186         },
188         /**
189          * Poll the server for any new notifications.
190          *
191          * @method fetchNotifications
192          */
193         fetchNotifications: notificationModule.fetchNotifications,
195         /**
196          * Add a notification to the page.
197          *
198          * Note: This does not cause the notification to be added to the session.
199          *
200          * @method addNotification
201          * @param {Object}  notification                The notification to add.
202          * @param {string}  notification.message        The body of the notification
203          * @param {string}  notification.type           The type of notification to add (error, warning, info, success).
204          * @param {Boolean} notification.closebutton    Whether to show the close button.
205          * @param {Boolean} notification.announce       Whether to announce to screen readers.
206          */
207         addNotification: notificationModule.addNotification,
209         /**
210          * Wrap M.core.alert.
211          *
212          * @method alert
213          * @param {string} title
214          * @param {string} message
215          * @param {string} yesLabel
216          */
217         alert: notificationModule.alert,
219         /**
220          * Wrap M.core.confirm.
221          *
222          * @method confirm
223          * @param {string} title
224          * @param {string} question
225          * @param {string} yesLabel
226          * @param {string} noLabel
227          * @param {function} callback
228          */
229         confirm: notificationModule.confirm,
231         /**
232          * Wrap M.core.exception.
233          *
234          * @method exception
235          * @param {Error} ex
236          */
237         exception: notificationModule.exception
238     };
239 });