// 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.
});
},
- /**
- * 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.
});
},
- /**
- * Wrap M.core.exception.
- *
- * @method exception
- * @param {Error} ex
- */
exception: function(ex) {
// Fudge some parameters.
if (ex.backtrace) {
});
}
};
+
+ 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
+ };
});
--- /dev/null
+<?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);
+ }
+}
*/
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.
*/
}
}
+ /**
+ * 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.
*
return array(
'message' => clean_text($this->message),
'extraclasses' => implode(' ', $this->extraclasses),
+ 'announce' => $this->announce,
+ 'closebutton' => $this->closebutton,
);
}
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 {
'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 ===
)
);
}
+
+ /**
+ * 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'));
+ }
}
* @return string HTML fragment
*/
public function footer() {
- global $CFG, $DB;
+ global $CFG, $DB, $PAGE;
$output = $this->container_end_all(true);
}
$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);
*/
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;
}
/**
/**
* 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.
*/
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);
*/
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);
*/
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);
*/
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);
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>
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>
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>
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>
--- /dev/null
+<?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);
+ }
+}
\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);
$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);
$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']);
$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']);
$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']);
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.