MDL-68076 core: Introduced \core\notification::cta()
[moodle.git] / lib / classes / notification.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 namespace core;
19 /**
20  * User Alert notifications.
21  *
22  * @package    core
23  * @copyright  2016 Andrew Nicols <andrew@nicols.co.uk>
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 use stdClass;
29 defined('MOODLE_INTERNAL') || die();
31 class notification {
32     /**
33      * A notification of level 'success'.
34      */
35     const SUCCESS = 'success';
37     /**
38      * A notification of level 'warning'.
39      */
40     const WARNING = 'warning';
42     /**
43      * A notification of level 'info'.
44      */
45     const INFO = 'info';
47     /**
48      * A notification of level 'error'.
49      */
50     const ERROR = 'error';
52     /**
53      * Add a message to the session notification stack.
54      *
55      * @param string $message The message to add to the stack
56      * @param string $level   The type of message to add to the stack
57      */
58     public static function add($message, $level = null) {
59         global $PAGE, $SESSION;
61         if ($PAGE && $PAGE->state === \moodle_page::STATE_IN_BODY) {
62             // Currently in the page body - just render and exit immediately.
63             // We insert some code to immediately insert this into the user-notifications created by the header.
64             $id = uniqid();
65             echo \html_writer::span(
66                 $PAGE->get_renderer('core')->render(new \core\output\notification($message, $level)),
67                 '', array('id' => $id));
69             // Insert this JS here using a script directly rather than waiting for the page footer to load to avoid
70             // ensure that the message is added to the user-notifications section as soon as possible after it is created.
71             echo \html_writer::script(
72                     "(function() {" .
73                         "var notificationHolder = document.getElementById('user-notifications');" .
74                         "if (!notificationHolder) { return; }" .
75                         "var thisNotification = document.getElementById('{$id}');" .
76                         "if (!thisNotification) { return; }" .
77                         "notificationHolder.appendChild(thisNotification.firstChild);" .
78                         "thisNotification.remove();" .
79                     "})();"
80                 );
81             return;
82         }
84         // Add the notification directly to the session.
85         // This will either be fetched in the header, or by JS in the footer.
86         if (!isset($SESSION->notifications) || !array($SESSION->notifications)) {
87             // Initialise $SESSION if necessary.
88             if (!is_object($SESSION)) {
89                 $SESSION = new stdClass();
90             }
91             $SESSION->notifications = [];
92         }
93         $SESSION->notifications[] = (object) array(
94             'message'   => $message,
95             'type'      => $level,
96         );
97     }
99     /**
100      * @param string[] $icon The icon to use. Required keys are 'pix' and 'component'.
101      * @param string $message The message to display.
102      * @param array $actions An array of action links
103      * @param string $region Optional region name
104      * @throws \coding_exception
105      */
106     public static function add_call_to_action(array $icon, string $message, array $actions, string $region = ''): void {
107         global $OUTPUT, $PAGE;
109         $context = new stdClass();
110         $context->icon = $icon;
111         $context->message = $message;
112         $context->region = $region;
114         $context->actions = array_map(function($action) {
115             $data = [];
116             foreach ($action['data'] as $name => $value) {
117                 $data[] = ['name' => $name, 'value' => $value];
118             }
119             $action['data'] = $data;
121             return $action;
122         }, $actions);
124         $notification = $OUTPUT->render_from_template('core/local/notification/cta', $context);
126         if ($PAGE && $PAGE->state === \moodle_page::STATE_IN_BODY) {
127             $id = uniqid();
128             echo \html_writer::span($notification, '', ['id' => $id]);
129             echo \html_writer::script(
130                     "(function() {" .
131                     "var notificationHolder = document.getElementById('user-notifications');" .
132                     "if (!notificationHolder) { return; }" .
133                     "var thisNotification = document.getElementById('{$id}');" .
134                     "if (!thisNotification) { return; }" .
135                     "notificationHolder.insertBefore(thisNotification.firstChild, notificationHolder.firstChild);" .
136                     "thisNotification.remove();" .
137                     "})();"
138             );
139         } else {
140             throw new \coding_exception('You are calling add_call_to_action() either too early or too late.');
141         }
142     }
144     /**
145      * Fetch all of the notifications in the stack and clear the stack.
146      *
147      * @return array All of the notifications in the stack
148      */
149     public static function fetch() {
150         global $SESSION;
152         if (!isset($SESSION) || !isset($SESSION->notifications)) {
153             return [];
154         }
156         $notifications = $SESSION->notifications;
157         unset($SESSION->notifications);
159         $renderables = [];
160         foreach ($notifications as $notification) {
161             $renderable = new \core\output\notification($notification->message, $notification->type);
162             $renderables[] = $renderable;
163         }
165         return $renderables;
166     }
168     /**
169      * Fetch all of the notifications in the stack and clear the stack.
170      *
171      * @return array All of the notifications in the stack
172      */
173     public static function fetch_as_array(\renderer_base $renderer) {
174         $notifications = [];
175         foreach (self::fetch() as $notification) {
176             $notifications[] = [
177                 'template'  => $notification->get_template_name(),
178                 'variables' => $notification->export_for_template($renderer),
179             ];
180         }
181         return $notifications;
182     }
184     /**
185      * Add a success message to the notification stack.
186      *
187      * @param string $message The message to add to the stack
188      */
189     public static function success($message) {
190         return self::add($message, self::SUCCESS);
191     }
193     /**
194      * Add a info message to the notification stack.
195      *
196      * @param string $message The message to add to the stack
197      */
198     public static function info($message) {
199         return self::add($message, self::INFO);
200     }
202     /**
203      * Add a warning message to the notification stack.
204      *
205      * @param string $message The message to add to the stack
206      */
207     public static function warning($message) {
208         return self::add($message, self::WARNING);
209     }
211     /**
212      * Add a error message to the notification stack.
213      *
214      * @param string $message The message to add to the stack
215      */
216     public static function error($message) {
217         return self::add($message, self::ERROR);
218     }