MDL-30811 output: Add support for session notifications
[moodle.git] / lib / amd / src / notification.js
index 1880b80..845a2e6 100644 (file)
@@ -14,6 +14,8 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
+ * A system for displaying notifications to users from the session.
+ *
  * Wrapper for the YUI M.core.notification class. Allows us to
  * use the YUI version in AMD code until it is replaced.
  *
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @since      2.9
  */
-define(['core/yui'], function(Y) {
+define(['core/yui', 'jquery', 'theme_bootstrapbase/bootstrap', 'core/templates', 'core/ajax', 'core/log'],
+function(Y, $, bootstrap, templates, ajax, log) {
+    var notificationModule = {
+        types: {
+            'success':  'core/notification_success',
+            'info':     'core/notification_info',
+            'warning':  'core/notification_warning',
+            'error':    'core/notification_error',
+        },
 
-    // Private variables and functions.
+        fieldName: 'user-notifications',
+
+        fetchNotifications: function() {
+            var promises = ajax.call([{
+                methodname: 'core_fetch_notifications',
+                args: {
+                    contextid: notificationModule.contextid
+                }
+            }]);
+
+            promises[0]
+                .done(notificationModule.addNotifications)
+                ;
+
+        },
+
+        addNotifications: function(notifications) {
+            if (!notifications) {
+                notifications = [];
+            }
+
+            $.each(notifications, function(i, notification) {
+                notificationModule.renderNotification(notification.template, notification.variables);
+            });
+        },
+
+        setupTargetRegion: function() {
+            var targetRegion = $('#' + notificationModule.fieldName);
+            if (targetRegion.length) {
+                return;
+            }
+
+            var newRegion = $('<span>').attr('id', notificationModule.fieldName);
+
+            targetRegion = $('#region-main');
+            if (targetRegion.length) {
+                return targetRegion.prepend(newRegion);
+            }
+
+            targetRegion = $('[role="main"]');
+            if (targetRegion.length) {
+                return targetRegion.prepend(newRegion);
+            }
+
+            targetRegion = $('body');
+            return targetRegion.prepend(newRegion);
+        },
+
+        addNotification: function(notification) {
+            var template = notificationModule.types.error;
+
+            notification = $.extend({
+                    closebutton:    true,
+                    announce:       true,
+                    type:           'error'
+                }, notification);
+
+            if (notification.template) {
+                template = notification.template;
+                delete notification.template;
+            } else if (notification.type){
+                if (typeof notificationModule.types[notification.type] !== 'undefined') {
+                    template = notificationModule.types[notification.type];
+                }
+                delete notification.type;
+            }
+
+            return notificationModule.renderNotification(template, notification);
+        },
+
+        renderNotification: function(template, variables) {
+            if (typeof variables.message === 'undefined' || !variables.message) {
+                log.debug('Notification received without content. Skipping.');
+                return;
+            }
+            templates.render(template, variables)
+                .done(function(html) {
+                    $('#' + notificationModule.fieldName).prepend(html);
+                })
+                .fail(notificationModule.exception)
+                ;
+        },
 
-    return /** @alias module:core/notification */ {
-        // Public variables and functions.
-        /**
-         * Wrap M.core.alert.
-         *
-         * @method alert
-         * @param {string} title
-         * @param {string} message
-         * @param {string} yesLabel
-         */
         alert: function(title, message, yesLabel) {
             // Here we are wrapping YUI. This allows us to start transitioning, but
             // wait for a good alternative without having inconsistent dialogues.
@@ -52,16 +133,6 @@ define(['core/yui'], function(Y) {
             });
         },
 
-        /**
-         * Wrap M.core.confirm.
-         *
-         * @method confirm
-         * @param {string} title
-         * @param {string} question
-         * @param {string} yesLabel
-         * @param {string} noLabel
-         * @param {function} callback
-         */
         confirm: function(title, question, yesLabel, noLabel, callback) {
             // Here we are wrapping YUI. This allows us to start transitioning, but
             // wait for a good alternative without having inconsistent dialogues.
@@ -80,12 +151,6 @@ define(['core/yui'], function(Y) {
             });
         },
 
-        /**
-         * Wrap M.core.exception.
-         *
-         * @method exception
-         * @param {Error} ex
-         */
         exception: function(ex) {
             // Fudge some parameters.
             if (ex.backtrace) {
@@ -102,4 +167,73 @@ define(['core/yui'], function(Y) {
             });
         }
     };
+
+    return /** @alias module:core/notification */{
+        init: function(contextid, notifications) {
+            notificationModule.contextid = contextid;
+
+            // Setup the message target region if it isn't setup already
+            notificationModule.setupTargetRegion();
+
+            // Setup closing of bootstrap alerts.
+            $().alert();
+
+            // Add provided notifications.
+            notificationModule.addNotifications(notifications);
+
+            // Poll for any new notifications.
+            notificationModule.fetchNotifications();
+        },
+
+        /**
+         * Poll the server for any new notifications.
+         *
+         * @method fetchNotifications
+         */
+        fetchNotifications: notificationModule.fetchNotifications,
+
+        /**
+         * Add a notification to the page.
+         *
+         * Note: This does not cause the notification to be added to the session.
+         *
+         * @method addNotification
+         * @param {Object}  notification                The notification to add.
+         * @param {string}  notification.message        The body of the notification
+         * @param {string}  notification.type           The type of notification to add (error, warning, info, success).
+         * @param {Boolean} notification.closebutton    Whether to show the close button.
+         * @param {Boolean} notification.announce       Whether to announce to screen readers.
+         */
+        addNotification: notificationModule.addNotification,
+
+        /**
+         * Wrap M.core.alert.
+         *
+         * @method alert
+         * @param {string} title
+         * @param {string} message
+         * @param {string} yesLabel
+         */
+        alert: notificationModule.alert,
+
+        /**
+         * Wrap M.core.confirm.
+         *
+         * @method confirm
+         * @param {string} title
+         * @param {string} question
+         * @param {string} yesLabel
+         * @param {string} noLabel
+         * @param {function} callback
+         */
+        confirm: notificationModule.confirm,
+
+        /**
+         * Wrap M.core.exception.
+         *
+         * @method exception
+         * @param {Error} ex
+         */
+        exception: notificationModule.exception
+    };
 });