diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 50ea6d2aed133..497fdb0cc3f9d 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -225,6 +225,9 @@ $string['byname'] = 'by {$a}'; $string['bypassed'] = 'Bypassed'; $string['cachecontrols'] = 'Cache controls'; +$string['calltofeedback'] = 'Moodle HQ would like your feedback on the Moodle LMS.'; +$string['calltofeedback_give'] = 'Give feedback'; +$string['calltofeedback_remind'] = 'Remind me later'; $string['cancel'] = 'Cancel'; $string['cancelled'] = 'Cancelled'; $string['categories'] = 'Course categories'; diff --git a/lib/classes/notification.php b/lib/classes/notification.php index 3c8783682f402..59fceeada5bf9 100644 --- a/lib/classes/notification.php +++ b/lib/classes/notification.php @@ -96,6 +96,51 @@ public static function add($message, $level = null) { ); } + /** + * @param string[] $icon The icon to use. Required keys are 'pix' and 'component'. + * @param string $message The message to display. + * @param array $actions An array of action links + * @param string $region Optional region name + * @throws \coding_exception + */ + public static function add_call_to_action(array $icon, string $message, array $actions, string $region = ''): void { + global $OUTPUT, $PAGE; + + $context = new stdClass(); + $context->icon = $icon; + $context->message = $message; + $context->region = $region; + + $context->actions = array_map(function($action) { + $data = []; + foreach ($action['data'] as $name => $value) { + $data[] = ['name' => $name, 'value' => $value]; + } + $action['data'] = $data; + + return $action; + }, $actions); + + $notification = $OUTPUT->render_from_template('core/local/notification/cta', $context); + + if ($PAGE && $PAGE->state === \moodle_page::STATE_IN_BODY) { + $id = uniqid(); + echo \html_writer::span($notification, '', ['id' => $id]); + echo \html_writer::script( + "(function() {" . + "var notificationHolder = document.getElementById('user-notifications');" . + "if (!notificationHolder) { return; }" . + "var thisNotification = document.getElementById('{$id}');" . + "if (!thisNotification) { return; }" . + "notificationHolder.insertBefore(thisNotification.firstChild, notificationHolder.firstChild);" . + "thisNotification.remove();" . + "})();" + ); + } else { + throw new \coding_exception('You are calling add_call_to_action() either too early or too late.'); + } + } + /** * Fetch all of the notifications in the stack and clear the stack. * diff --git a/lib/templates/local/notification/cta.mustache b/lib/templates/local/notification/cta.mustache new file mode 100644 index 0000000000000..1af3c1c3c9f40 --- /dev/null +++ b/lib/templates/local/notification/cta.mustache @@ -0,0 +1,68 @@ +{{! + 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 . +}} +{{! + @template core/local/notification/cta + + Moodle cta notification template. + + The purpose of this template is to render a call to action notification. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Context variables required for this template: + * message A cleaned string (use clean_text()) to display. + * extraclasses Additional classes to apply to the notification. + * actions List of action links. + * icon An icon.pix and icon.componrnt for the icon to be displauyed as the icon of CTA notification. + + Example context (json): + { + "message": "What do you think about Moodle?", + "actions": [ + { + "title": "Give feedback", + "url": "#", + "data": [ + {"name": "action", "value": "give"}, + {"name": "contextid", "value": "3"} + ] + } + ] + } +}} + +
+
+
+ {{# pix }} {{ icon.pix }}, {{ icon.component }} {{/ pix }} +
+
+ {{{ message }}}
+ {{# actions }} + {{ title }} + {{/ actions }} +
+
+
diff --git a/theme/boost/scss/moodle/core.scss b/theme/boost/scss/moodle/core.scss index 2bfa166876b95..cf02227caa208 100644 --- a/theme/boost/scss/moodle/core.scss +++ b/theme/boost/scss/moodle/core.scss @@ -2653,4 +2653,22 @@ $picker-emojis-per-row: 7 !default; position: relative; z-index: inherit; } -} \ No newline at end of file +} + +.link-underline { + text-decoration: underline; + &:focus { + text-decoration: none; + } +} + +.alert.cta { + .icon { + padding: 0.3rem; + &.fa { + border-radius: 50%; + border-style: solid; + border-width: 0.125rem; + } + } +} diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 0a03d632c7b73..2b419040eb8c4 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -11822,6 +11822,18 @@ body.h5p-embed .h5pmessages { position: relative; z-index: inherit; } } +.link-underline { + text-decoration: underline; } + .link-underline:focus { + text-decoration: none; } + +.alert.cta .icon { + padding: 0.3rem; } + .alert.cta .icon.fa { + border-radius: 50%; + border-style: solid; + border-width: 0.125rem; } + .icon { font-size: 16px; width: 16px; diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index 9212c9654b2e5..961aae71fed72 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -12036,6 +12036,18 @@ body.h5p-embed .h5pmessages { position: relative; z-index: inherit; } } +.link-underline { + text-decoration: underline; } + .link-underline:focus { + text-decoration: none; } + +.alert.cta .icon { + padding: 0.3rem; } + .alert.cta .icon.fa { + border-radius: 50%; + border-style: solid; + border-width: 0.125rem; } + .icon { font-size: 16px; width: 16px;