mirror of
https://github.com/moodle/moodle.git
synced 2025-04-20 16:04:25 +02:00
MDL-30811 output: Add support for session notifications
This commit is contained in:
parent
243468030a
commit
0346323cec
2
lib/amd/build/notification.min.js
vendored
2
lib/amd/build/notification.min.js
vendored
@ -1 +1 @@
|
||||
define(["core/yui"],function(a){return{alert:function(b,c,d){a.use("moodle-core-notification-alert",function(){var a=new M.core.alert({title:b,message:c,yesLabel:d});a.show()})},confirm:function(b,c,d,e,f){a.use("moodle-core-notification-confirm",function(){var a=new M.core.confirm({title:b,question:c,yesLabel:d,noLabel:e});a.on("complete-yes",function(){f()}),a.show()})},exception:function(b){b.backtrace&&(b.lineNumber=b.backtrace[0].line,b.fileName=b.backtrace[0].file,b.fileName="..."+b.fileName.substr(b.fileName.length-20),b.stack=b.debuginfo,b.name=b.errorcode),a.use("moodle-core-notification-exception",function(){var a=new M.core.exception(b);a.show()})}}});
|
||||
define(["core/yui","jquery","theme_bootstrapbase/bootstrap","core/templates","core/ajax","core/log"],function(a,b,c,d,e,f){var g={types:{success:"core/notification_success",info:"core/notification_info",warning:"core/notification_warning",error:"core/notification_error"},fieldName:"user-notifications",fetchNotifications:function(){var a=e.call([{methodname:"core_fetch_notifications",args:{contextid:g.contextid}}]);a[0].done(g.addNotifications)},addNotifications:function(a){a||(a=[]),b.each(a,function(a,b){g.renderNotification(b.template,b.variables)})},setupTargetRegion:function(){var a=b("#"+g.fieldName);if(!a.length){var c=b("<span>").attr("id",g.fieldName);return a=b("#region-main"),a.length?a.prepend(c):(a=b('[role="main"]'),a.length?a.prepend(c):(a=b("body"),a.prepend(c)))}},addNotification:function(a){var c=g.types.error;return a=b.extend({closebutton:!0,announce:!0,type:"error"},a),a.template?(c=a.template,delete a.template):a.type&&("undefined"!=typeof g.types[a.type]&&(c=g.types[a.type]),delete a.type),g.renderNotification(c,a)},renderNotification:function(a,c){return"undefined"!=typeof c.message&&c.message?void d.render(a,c).done(function(a){b("#"+g.fieldName).prepend(a)}).fail(g.exception):void f.debug("Notification received without content. Skipping.")},alert:function(b,c,d){a.use("moodle-core-notification-alert",function(){var a=new M.core.alert({title:b,message:c,yesLabel:d});a.show()})},confirm:function(b,c,d,e,f){a.use("moodle-core-notification-confirm",function(){var a=new M.core.confirm({title:b,question:c,yesLabel:d,noLabel:e});a.on("complete-yes",function(){f()}),a.show()})},exception:function(b){b.backtrace&&(b.lineNumber=b.backtrace[0].line,b.fileName=b.backtrace[0].file,b.fileName="..."+b.fileName.substr(b.fileName.length-20),b.stack=b.debuginfo,b.name=b.errorcode),a.use("moodle-core-notification-exception",function(){var a=new M.core.exception(b);a.show()})}};return{init:function(a,c){g.contextid=a,g.setupTargetRegion(),b().alert(),g.addNotifications(c),g.fetchNotifications()},fetchNotifications:g.fetchNotifications,addNotification:g.addNotification,alert:g.alert,confirm:g.confirm,exception:g.exception}});
|
@ -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.
|
||||
*
|
||||
@ -24,20 +26,99 @@
|
||||
* @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
|
||||
};
|
||||
});
|
||||
|
165
lib/classes/notification.php
Normal file
165
lib/classes/notification.php
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
// 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/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* User Alert notifications.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
class notification {
|
||||
/**
|
||||
* A notification of level 'success'.
|
||||
*/
|
||||
const SUCCESS = 'success';
|
||||
|
||||
/**
|
||||
* A notification of level 'warning'.
|
||||
*/
|
||||
const WARNING = 'warning';
|
||||
|
||||
/**
|
||||
* A notification of level 'info'.
|
||||
*/
|
||||
const INFO = 'info';
|
||||
|
||||
/**
|
||||
* A notification of level 'error'.
|
||||
*/
|
||||
const ERROR = 'error';
|
||||
|
||||
/**
|
||||
* Add a message to the session notification stack.
|
||||
*
|
||||
* @param string $message The message to add to the stack
|
||||
* @param string $level The type of message to add to the stack
|
||||
*/
|
||||
public static function add($message, $level = null) {
|
||||
global $PAGE, $SESSION;
|
||||
|
||||
if ($PAGE && $PAGE->state === \moodle_page::STATE_IN_BODY) {
|
||||
// Currently in the page body - just render and exit immediately.
|
||||
// We insert some code to immediately insert this into the user-notifications created by the header.
|
||||
$id = uniqid();
|
||||
echo \html_writer::span(
|
||||
$PAGE->get_renderer('core')->render(new \core\output\notification($message, $level)),
|
||||
'', array('id' => $id));
|
||||
|
||||
// Insert this JS here using a script directly rather than waiting for the page footer to load to avoid
|
||||
// ensure that the message is added to the user-notifications section as soon as possible after it is created.
|
||||
echo \html_writer::script(
|
||||
"(function() {" .
|
||||
"var notificationHolder = document.getElementById('user-notifications');" .
|
||||
"if (!notificationHolder) { return; }" .
|
||||
"var thisNotification = document.getElementById('{$id}');" .
|
||||
"if (!thisNotification) { return; }" .
|
||||
"notificationHolder.appendChild(thisNotification.firstChild);" .
|
||||
"thisNotification.remove();" .
|
||||
"})();"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the notification directly to the session.
|
||||
// This will either be fetched in the header, or by JS in the footer.
|
||||
$SESSION->notifications[] = (object) array(
|
||||
'message' => $message,
|
||||
'type' => $level,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all of the notifications in the stack and clear the stack.
|
||||
*
|
||||
* @return array All of the notifications in the stack
|
||||
*/
|
||||
public static function fetch() {
|
||||
global $SESSION;
|
||||
|
||||
if (!isset($SESSION) || !isset($SESSION->notifications)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$notifications = $SESSION->notifications;
|
||||
$SESSION->notifications = [];
|
||||
|
||||
$renderables = [];
|
||||
foreach ($notifications as $notification) {
|
||||
$renderable = new \core\output\notification($notification->message, $notification->type);
|
||||
$renderables[] = $renderable;
|
||||
}
|
||||
|
||||
return $renderables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all of the notifications in the stack and clear the stack.
|
||||
*
|
||||
* @return array All of the notifications in the stack
|
||||
*/
|
||||
public static function fetch_as_array(\renderer_base $renderer) {
|
||||
$notifications = [];
|
||||
foreach (self::fetch() as $notification) {
|
||||
$notifications[] = [
|
||||
'template' => $notification->get_template_name(),
|
||||
'variables' => $notification->export_for_template($renderer),
|
||||
];
|
||||
}
|
||||
return $notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a success message to the notification stack.
|
||||
*
|
||||
* @param string $message The message to add to the stack
|
||||
*/
|
||||
public static function success($message) {
|
||||
return self::add($message, self::SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a info message to the notification stack.
|
||||
*
|
||||
* @param string $message The message to add to the stack
|
||||
*/
|
||||
public static function info($message) {
|
||||
return self::add($message, self::INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a warning message to the notification stack.
|
||||
*
|
||||
* @param string $message The message to add to the stack
|
||||
*/
|
||||
public static function warning($message) {
|
||||
return self::add($message, self::WARNING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a error message to the notification stack.
|
||||
*
|
||||
* @param string $message The message to add to the stack
|
||||
*/
|
||||
public static function error($message) {
|
||||
return self::add($message, self::ERROR);
|
||||
}
|
||||
}
|
@ -83,6 +83,16 @@ class notification implements \renderable, \templatable {
|
||||
*/
|
||||
protected $messagetype = self::NOTIFY_WARNING;
|
||||
|
||||
/**
|
||||
* @var bool $announce Whether this notification should be announced assertively to screen readers.
|
||||
*/
|
||||
protected $announce = true;
|
||||
|
||||
/**
|
||||
* @var bool $closebutton Whether this notification should inlcude a button to dismiss itself.
|
||||
*/
|
||||
protected $closebutton = true;
|
||||
|
||||
/**
|
||||
* @var array $extraclasses A list of any extra classes that may be required.
|
||||
*/
|
||||
@ -111,6 +121,30 @@ class notification implements \renderable, \templatable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this notification should be announced assertively to screen readers.
|
||||
*
|
||||
* @param bool $announce
|
||||
* @return $this
|
||||
*/
|
||||
public function set_announce($announce = false) {
|
||||
$this->announce = (bool) $announce;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this notification should include a button to disiss itself.
|
||||
*
|
||||
* @param bool $button
|
||||
* @return $this
|
||||
*/
|
||||
public function set_show_closebutton($button = false) {
|
||||
$this->closebutton = (bool) $button;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any extra classes that this notification requires.
|
||||
*
|
||||
@ -133,6 +167,8 @@ class notification implements \renderable, \templatable {
|
||||
return array(
|
||||
'message' => clean_text($this->message),
|
||||
'extraclasses' => implode(' ', $this->extraclasses),
|
||||
'announce' => $this->announce,
|
||||
'closebutton' => $this->closebutton,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -157,10 +157,18 @@ class manager {
|
||||
public static function init_empty_session() {
|
||||
global $CFG;
|
||||
|
||||
// Backup notifications. These should be preserved across session changes until the user fetches and clears them.
|
||||
$notifications = [];
|
||||
if (isset($GLOBALS['SESSION']->notifications)) {
|
||||
$notifications = $GLOBALS['SESSION']->notifications;
|
||||
}
|
||||
$GLOBALS['SESSION'] = new \stdClass();
|
||||
|
||||
$GLOBALS['USER'] = new \stdClass();
|
||||
$GLOBALS['USER']->id = 0;
|
||||
|
||||
// Restore notifications.
|
||||
$GLOBALS['SESSION']->notifications = $notifications;
|
||||
if (isset($CFG->mnet_localhost_id)) {
|
||||
$GLOBALS['USER']->mnethostid = $CFG->mnet_localhost_id;
|
||||
} else {
|
||||
|
@ -1067,7 +1067,17 @@ $functions = array(
|
||||
'description' => 'Generic service to update title',
|
||||
'type' => 'write',
|
||||
'loginrequired' => true,
|
||||
'ajax' => true
|
||||
'ajax' => true,
|
||||
),
|
||||
|
||||
'core_fetch_notifications' => array(
|
||||
'classname' => 'core_external',
|
||||
'methodname' => 'fetch_notifications',
|
||||
'classpath' => 'lib/external/externallib.php',
|
||||
'description' => 'Return a list of notifications for the current session',
|
||||
'type' => 'read',
|
||||
'loginrequired' => false,
|
||||
'ajax' => true,
|
||||
),
|
||||
|
||||
// === Calendar related functions ===
|
||||
|
54
lib/external/externallib.php
vendored
54
lib/external/externallib.php
vendored
@ -407,4 +407,58 @@ class core_external extends external_api {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of fetch_notifications() parameters.
|
||||
*
|
||||
* @return external_function_parameters
|
||||
* @since Moodle 3.1
|
||||
*/
|
||||
public static function fetch_notifications_parameters() {
|
||||
return new external_function_parameters(
|
||||
array(
|
||||
'contextid' => new external_value(PARAM_INT, 'Context ID', VALUE_REQUIRED),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of fetch_notifications() result value.
|
||||
*
|
||||
* @return external_description
|
||||
* @since Moodle 3.1
|
||||
*/
|
||||
public static function fetch_notifications_returns() {
|
||||
return new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
'template' => new external_value(PARAM_RAW, 'Name of the template'),
|
||||
'variables' => new external_single_structure(array(
|
||||
'message' => new external_value(PARAM_RAW, 'HTML content of the Notification'),
|
||||
'extraclasses' => new external_value(PARAM_RAW, 'Extra classes to provide to the tmeplate'),
|
||||
'announce' => new external_value(PARAM_RAW, 'Whether to announce'),
|
||||
'closebutton' => new external_value(PARAM_RAW, 'Whether to close'),
|
||||
)),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of notifications against the current session.
|
||||
*
|
||||
* @return array
|
||||
* @since Moodle 3.1
|
||||
*/
|
||||
public static function fetch_notifications($contextid) {
|
||||
global $PAGE;
|
||||
|
||||
self::validate_parameters(self::fetch_notifications_parameters(), [
|
||||
'contextid' => $contextid,
|
||||
]);
|
||||
|
||||
$context = \context::instance_by_id($contextid);
|
||||
$PAGE->set_context($context);
|
||||
|
||||
return \core\notification::fetch_as_array($PAGE->get_renderer('core'));
|
||||
}
|
||||
}
|
||||
|
@ -1032,7 +1032,7 @@ class core_renderer extends renderer_base {
|
||||
* @return string HTML fragment
|
||||
*/
|
||||
public function footer() {
|
||||
global $CFG, $DB;
|
||||
global $CFG, $DB, $PAGE;
|
||||
|
||||
$output = $this->container_end_all(true);
|
||||
|
||||
@ -1057,6 +1057,7 @@ class core_renderer extends renderer_base {
|
||||
}
|
||||
$footer = str_replace($this->unique_performance_info_token, $performanceinfo, $footer);
|
||||
|
||||
$this->page->requires->js_call_amd('core/notification', 'init', array($PAGE->context->id, \core\notification::fetch_as_array($this)));
|
||||
$footer = str_replace($this->unique_end_html_token, $this->page->requires->get_end_code(), $footer);
|
||||
|
||||
$this->page->set_state(moodle_page::STATE_DONE);
|
||||
@ -1086,22 +1087,37 @@ class core_renderer extends renderer_base {
|
||||
*/
|
||||
public function course_content_header($onlyifnotcalledbefore = false) {
|
||||
global $CFG;
|
||||
if ($this->page->course->id == SITEID) {
|
||||
// return immediately and do not include /course/lib.php if not necessary
|
||||
return '';
|
||||
}
|
||||
static $functioncalled = false;
|
||||
if ($functioncalled && $onlyifnotcalledbefore) {
|
||||
// we have already output the content header
|
||||
return '';
|
||||
}
|
||||
|
||||
// Output any session notification.
|
||||
$notifications = \core\notification::fetch();
|
||||
|
||||
$bodynotifications = '';
|
||||
foreach ($notifications as $notification) {
|
||||
$bodynotifications .= $this->render_from_template(
|
||||
$notification->get_template_name(),
|
||||
$notification->export_for_template($this)
|
||||
);
|
||||
}
|
||||
|
||||
$output = html_writer::span($bodynotifications, 'notifications', array('id' => 'user-notifications'));
|
||||
|
||||
if ($this->page->course->id == SITEID) {
|
||||
// return immediately and do not include /course/lib.php if not necessary
|
||||
return $output;
|
||||
}
|
||||
|
||||
require_once($CFG->dirroot.'/course/lib.php');
|
||||
$functioncalled = true;
|
||||
$courseformat = course_get_format($this->page->course);
|
||||
if (($obj = $courseformat->course_content_header()) !== null) {
|
||||
return html_writer::div($courseformat->get_renderer($this->page)->render($obj), 'course-content-header');
|
||||
$output .= html_writer::div($courseformat->get_renderer($this->page)->render($obj), 'course-content-header');
|
||||
}
|
||||
return '';
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2780,6 +2796,8 @@ EOD;
|
||||
/**
|
||||
* Output a notification (that is, a status message about something that has just happened).
|
||||
*
|
||||
* Note: \core\notification::add() may be more suitable for your usage.
|
||||
*
|
||||
* @param string $message The message to print out.
|
||||
* @param string $type The type of notification. See constants on \core\output\notification.
|
||||
* @return string the HTML to output.
|
||||
@ -2848,7 +2866,7 @@ EOD;
|
||||
*/
|
||||
public function notify_problem($message) {
|
||||
debugging(__FUNCTION__ . ' is deprecated.' .
|
||||
'Please use notification() or \core\output\notification as required',
|
||||
'Please use \core\notification::add, or \core\output\notification as required',
|
||||
DEBUG_DEVELOPER);
|
||||
$n = new \core\output\notification($message, \core\output\notification::NOTIFY_ERROR);
|
||||
return $this->render($n);
|
||||
@ -2865,7 +2883,7 @@ EOD;
|
||||
*/
|
||||
public function notify_success($message) {
|
||||
debugging(__FUNCTION__ . ' is deprecated.' .
|
||||
'Please use notification() or \core\output\notification as required',
|
||||
'Please use \core\notification::add, or \core\output\notification as required',
|
||||
DEBUG_DEVELOPER);
|
||||
$n = new \core\output\notification($message, \core\output\notification::NOTIFY_SUCCESS);
|
||||
return $this->render($n);
|
||||
@ -2882,7 +2900,7 @@ EOD;
|
||||
*/
|
||||
public function notify_message($message) {
|
||||
debugging(__FUNCTION__ . ' is deprecated.' .
|
||||
'Please use notification() or \core\output\notification as required',
|
||||
'Please use \core\notification::add, or \core\output\notification as required',
|
||||
DEBUG_DEVELOPER);
|
||||
$n = new \core\output\notification($message, \core\output\notification::NOTIFY_INFO);
|
||||
return $this->render($n);
|
||||
@ -2899,7 +2917,7 @@ EOD;
|
||||
*/
|
||||
public function notify_redirect($message) {
|
||||
debugging(__FUNCTION__ . ' is deprecated.' .
|
||||
'Please use notification() or \core\output\notification as required',
|
||||
'Please use \core\notification::add, or \core\output\notification as required',
|
||||
DEBUG_DEVELOPER);
|
||||
$n = new \core\output\notification($message, \core\output\notification::NOTIFY_INFO);
|
||||
return $this->render($n);
|
||||
|
@ -30,10 +30,15 @@
|
||||
Context variables required for this template:
|
||||
* message A cleaned string (use clean_text()) to display.
|
||||
* extraclasses Additional classes to apply to the notification.
|
||||
* closebutton Whether a close button should be displayed to dismiss the message.
|
||||
* announce Whether the notification should be announced to screen readers.
|
||||
|
||||
Example context (json):
|
||||
{ "message": "Your pants are on fire!", "extraclasses": "foo bar"}
|
||||
{ "message": "Your pants are on fire!", "closebutton": 1, "announce": 1, "extraclasses": "foo bar"}
|
||||
}}
|
||||
<div class="alert alert-error alert-block fade in {{ extraclasses }}">
|
||||
<div class="alert alert-error alert-block fade in {{ extraclasses }}" {{!
|
||||
}}{{# announce }} aria-live="assertive"{{/ announce }}{{!
|
||||
}}>
|
||||
{{# closebutton }}<button type="button" class="close" data-dismiss="alert">×</button>{{/ closebutton }}
|
||||
{{{ message }}}
|
||||
</div>
|
||||
|
@ -30,10 +30,15 @@
|
||||
Context variables required for this template:
|
||||
* message A cleaned string (use clean_text()) to display.
|
||||
* extraclasses Additional classes to apply to the notification.
|
||||
* closebutton Whether a close button should be displayed to dismiss the message.
|
||||
* announce Whether the notification should be announced to screen readers.
|
||||
|
||||
Example context (json):
|
||||
{ "message": "Your pants are on fire!", "extraclasses": "foo bar"}
|
||||
{ "message": "Your pants are on fire!", "closebutton": 1, "announce": 1, "extraclasses": "foo bar"}
|
||||
}}
|
||||
<div class="alert alert-info alert-block fade in {{ extraclasses }}">
|
||||
<div class="alert alert-info alert-block fade in {{ extraclasses }}" {{!
|
||||
}}{{# announce }} aria-live="assertive"{{/ announce }}{{!
|
||||
}}>
|
||||
{{# closebutton }}<button type="button" class="close" data-dismiss="alert">×</button>{{/ closebutton }}
|
||||
{{{ message }}}
|
||||
</div>
|
||||
|
@ -30,10 +30,15 @@
|
||||
Context variables required for this template:
|
||||
* message A cleaned string (use clean_text()) to display.
|
||||
* extraclasses Additional classes to apply to the notification.
|
||||
* closebutton Whether a close button should be displayed to dismiss the message.
|
||||
* announce Whether the notification should be announced to screen readers.
|
||||
|
||||
Example context (json):
|
||||
{ "message": "Your pants are on fire!", "extraclasses": "foo bar"}
|
||||
{ "message": "Your pants are on fire!", "closebutton": 1, "announce": 1, "extraclasses": "foo bar"}
|
||||
}}
|
||||
<div class="alert alert-success alert-block fade in {{ extraclasses }}">
|
||||
<div class="alert alert-success alert-block fade in {{ extraclasses }}" {{!
|
||||
}}{{# announce }} aria-live="assertive"{{/ announce }}{{!
|
||||
}}>
|
||||
{{# closebutton }}<button type="button" class="close" data-dismiss="alert">×</button>{{/ closebutton }}
|
||||
{{{ message }}}
|
||||
</div>
|
||||
|
@ -30,10 +30,15 @@
|
||||
Context variables required for this template:
|
||||
* message A cleaned string (use clean_text()) to display.
|
||||
* extraclasses Additional classes to apply to the notification.
|
||||
* closebutton Whether a close button should be displayed to dismiss the message.
|
||||
* announce Whether the notification should be announced to screen readers.
|
||||
|
||||
Example context (json):
|
||||
{ "message": "Your pants are on fire!", "extraclasses": "foo bar"}
|
||||
{ "message": "Your pants are on fire!", "closebutton": 1, "announce": 1, "extraclasses": "foo bar"}
|
||||
}}
|
||||
<div class="alert alert-warning alert-block fade in {{ extraclasses }}">
|
||||
<div class="alert alert-warning alert-block fade in {{ extraclasses }}" {{!
|
||||
}}{{# announce }} aria-live="assertive"{{/ announce }}{{!
|
||||
}}>
|
||||
{{# closebutton }}<button type="button" class="close" data-dismiss="alert">×</button>{{/ closebutton }}
|
||||
{{{ message }}}
|
||||
</div>
|
||||
|
122
lib/tests/notification_test.php
Normal file
122
lib/tests/notification_test.php
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Unit tests for core\notification.
|
||||
*
|
||||
* @package core
|
||||
* @category phpunit
|
||||
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Unit tests for core\notification.
|
||||
*
|
||||
* @package core
|
||||
* @category phpunit
|
||||
* @category phpunit
|
||||
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_notification_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Setup required for all notification tests.
|
||||
*
|
||||
* This includes emptying the list of notifications on the session, resetting any session which exists, and setting
|
||||
* up a new moodle_page object.
|
||||
*/
|
||||
public function setUp() {
|
||||
global $PAGE, $SESSION;
|
||||
|
||||
parent::setUp();
|
||||
$PAGE = new moodle_page();
|
||||
\core\session\manager::init_empty_session();
|
||||
$SESSION->notifications = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tear down required for all notification tests.
|
||||
*
|
||||
* This includes emptying the list of notifications on the session, resetting any session which exists, and setting
|
||||
* up a new moodle_page object.
|
||||
*/
|
||||
public function tearDown() {
|
||||
global $PAGE, $SESSION;
|
||||
|
||||
$PAGE = null;
|
||||
\core\session\manager::init_empty_session();
|
||||
$SESSION->notifications = [];
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the way in which notifications are added to the session in different stages of the page load.
|
||||
*/
|
||||
public function test_add_during_output_stages() {
|
||||
global $PAGE, $SESSION;
|
||||
|
||||
\core\notification::add('Example before header', \core\notification::INFO);
|
||||
$this->assertCount(1, $SESSION->notifications);
|
||||
|
||||
$PAGE->set_state(\moodle_page::STATE_PRINTING_HEADER);
|
||||
\core\notification::add('Example during header', \core\notification::INFO);
|
||||
$this->assertCount(2, $SESSION->notifications);
|
||||
|
||||
$PAGE->set_state(\moodle_page::STATE_IN_BODY);
|
||||
\core\notification::add('Example in body', \core\notification::INFO);
|
||||
$this->expectOutputRegex('/Example in body/');
|
||||
$this->assertCount(2, $SESSION->notifications);
|
||||
|
||||
$PAGE->set_state(\moodle_page::STATE_DONE);
|
||||
\core\notification::add('Example after page', \core\notification::INFO);
|
||||
$this->assertCount(3, $SESSION->notifications);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fetching of notifications from the session.
|
||||
*/
|
||||
public function test_fetch() {
|
||||
// Initially there won't be any notifications.
|
||||
$this->assertCount(0, \core\notification::fetch());
|
||||
|
||||
// Adding a notification should make one available to fetch.
|
||||
\core\notification::success('Notification created');
|
||||
$this->assertCount(1, \core\notification::fetch());
|
||||
$this->assertCount(0, \core\notification::fetch());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that session notifications are persisted across session clears.
|
||||
*/
|
||||
public function test_session_persistance() {
|
||||
global $PAGE, $SESSION;
|
||||
|
||||
// Initially there won't be any notifications.
|
||||
$this->assertCount(0, $SESSION->notifications);
|
||||
|
||||
// Adding a notification should make one available to fetch.
|
||||
\core\notification::success('Notification created');
|
||||
$this->assertCount(1, $SESSION->notifications);
|
||||
|
||||
// Re-creating the session will not empty the notification bag.
|
||||
\core\session\manager::init_empty_session();
|
||||
$this->assertCount(1, $SESSION->notifications);
|
||||
}
|
||||
}
|
@ -59,7 +59,7 @@ class core_session_manager_testcase extends advanced_testcase {
|
||||
\core\session\manager::init_empty_session();
|
||||
|
||||
$this->assertInstanceOf('stdClass', $SESSION);
|
||||
$this->assertEmpty((array)$SESSION);
|
||||
$this->assertCount(1, (array)$SESSION);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
|
||||
@ -149,7 +149,7 @@ class core_session_manager_testcase extends advanced_testcase {
|
||||
$this->assertEquals(0, $USER->id);
|
||||
|
||||
$this->assertInstanceOf('stdClass', $SESSION);
|
||||
$this->assertEmpty((array)$SESSION);
|
||||
$this->assertCount(1, (array)$SESSION);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
|
||||
|
@ -76,7 +76,7 @@ class core_sessionlib_testcase extends advanced_testcase {
|
||||
$this->assertSame($PAGE->context, context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
$this->assertObjectNotHasAttribute('test1', $SESSION);
|
||||
$this->assertEmpty((array)$SESSION);
|
||||
$this->assertCount(1, (array)$SESSION);
|
||||
$usersession1 = $SESSION;
|
||||
$SESSION->test2 = true;
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
@ -99,7 +99,7 @@ class core_sessionlib_testcase extends advanced_testcase {
|
||||
$this->assertSame($PAGE->context, context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
$this->assertNotSame($usersession1, $SESSION);
|
||||
$this->assertEmpty((array)$SESSION);
|
||||
$this->assertCount(1, (array)$SESSION);
|
||||
$usersession2 = $SESSION;
|
||||
$usersession2->test3 = true;
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
@ -123,7 +123,7 @@ class core_sessionlib_testcase extends advanced_testcase {
|
||||
$this->assertSame($PAGE->context, context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
$this->assertNotSame($usersession1, $SESSION);
|
||||
$this->assertEmpty((array)$SESSION);
|
||||
$this->assertCount(1, (array)$SESSION);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2016022500.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2016030100.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user