From 35b132b2c216359dcd1788d5be56147e21867012 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Wed, 28 Jan 2026 19:41:31 +0800 Subject: [PATCH 1/4] MDL-87694 core: `headinglevel` display option for $OUTPUT->confirm() `\core\output\core_renderer::confirm()`'s `$displayoptions` parameter now also accepts a `headinglevel` option that developers can use to specify the heading level of the confirmation's heading. If not specified, the confirmation heading will be rendered in an `h4` tag. --- .upgradenotes/MDL-87694-2026012811392962.yml | 9 +++++ lib/classes/output/core_renderer.php | 36 +++++++++++++------- 2 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 .upgradenotes/MDL-87694-2026012811392962.yml diff --git a/.upgradenotes/MDL-87694-2026012811392962.yml b/.upgradenotes/MDL-87694-2026012811392962.yml new file mode 100644 index 00000000000..fec1da92b9d --- /dev/null +++ b/.upgradenotes/MDL-87694-2026012811392962.yml @@ -0,0 +1,9 @@ +issueNumber: MDL-87694 +notes: + core: + - message: >- + `\core\output\core_renderer::confirm()`'s `$displayoptions` parameter + now also accepts a `headinglevel` option that developers can use to + specify the heading level of the confirmation's heading. If not + specified, the confirmation heading will be rendered in an `h4` tag. + type: changed diff --git a/lib/classes/output/core_renderer.php b/lib/classes/output/core_renderer.php index 7f12077b923..ad0f530f260 100644 --- a/lib/classes/output/core_renderer.php +++ b/lib/classes/output/core_renderer.php @@ -1684,23 +1684,35 @@ class core_renderer extends renderer_base { return $this->action_link($url, $text . $icon, $action, $attributes); } - /** - * Print a message along with button choices for Continue/Cancel - * - * If a string or moodle_url is given instead of a single_button, method defaults to post. - * - * @param string $message The question to ask the user - * @param single_button|moodle_url|string $continue The single_button component representing the Continue answer. Can also be a moodle_url or string URL - * @param single_button|moodle_url|string $cancel The single_button component representing the Cancel answer. Can also be a moodle_url or string URL - * @param array $displayoptions optional extra display options - * @return string HTML fragment - */ + /** + * Print a message along with button choices for Continue/Cancel + * + * If a string or moodle_url is given instead of a single_button, method defaults to post. + * + * @param string $message The question to ask the user + * @param single_button|moodle_url|string $continue The single_button component representing the Continue answer. + * Can also be a moodle_url or string URL + * @param single_button|moodle_url|string $cancel The single_button component representing the Cancel answer. + * Can also be a moodle_url or string URL + * @param array $displayoptions Display options (Optional). + * Possible options: + * - confirmtitle: The title to display above the message + * - continuestr: The label to use for the continue button (if $continue is not a single_button) + * - cancelstr: The label to use for the cancel button (if $cancel is not a single_button) + * - headinglevel: The heading level to use for the title (1-6). Default is 4. + * - type: The button type to use for the continue button (if $continue is not a single_button). Default is BUTTON_PRIMARY. + * @return string HTML fragment + */ public function confirm($message, $continue, $cancel, array $displayoptions = []) { // Check existing displayoptions. $displayoptions['confirmtitle'] = $displayoptions['confirmtitle'] ?? get_string('confirm'); $displayoptions['continuestr'] = $displayoptions['continuestr'] ?? get_string('continue'); $displayoptions['cancelstr'] = $displayoptions['cancelstr'] ?? get_string('cancel'); + $headinglevel = $displayoptions['headinglevel'] ?? 4; + if ($headinglevel < 1 || $headinglevel > 6) { + throw new coding_exception('The headinglevel option to $OUTPUT->confirm() must be between 1 and 6.'); + } if ($continue instanceof single_button) { // Continue button should be primary if set to secondary type as it is the fefault. @@ -1745,7 +1757,7 @@ class core_renderer extends renderer_base { $output = $this->box_start('generalbox modal modal-dialog modal-in-page show', 'notice', $attributes); $output .= $this->box_start('modal-content', 'modal-content'); $output .= $this->box_start('modal-header px-3', 'modal-header'); - $output .= html_writer::tag('h4', $displayoptions['confirmtitle']); + $output .= html_writer::tag('h' . $headinglevel, $displayoptions['confirmtitle'], ['class' => 'h4']); $output .= $this->box_end(); $attributes = [ 'role' => 'alert', From 76cb6c49b03eadca3afc40a3b5cb21b1c3945501 Mon Sep 17 00:00:00 2001 From: Stefan Topfstedt Date: Mon, 2 Feb 2026 10:13:40 -0800 Subject: [PATCH 2/4] MDL-87694 profile: set top level heading when accessed as guest user. --- user/profile.php | 12 +++++++++--- user/view.php | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/user/profile.php b/user/profile.php index 31ca4f6d4c7..34fbaa1d64e 100644 --- a/user/profile.php +++ b/user/profile.php @@ -52,9 +52,15 @@ if (!empty($CFG->forceloginforprofiles)) { $PAGE->set_context(context_system::instance()); $PAGE->set_title(get_string('user')); echo $OUTPUT->header(); - echo $OUTPUT->confirm(get_string('guestcantaccessprofiles', 'error'), - get_login_url(), - $CFG->wwwroot); + echo $OUTPUT->confirm( + get_string('guestcantaccessprofiles', 'error'), + get_login_url(), + $CFG->wwwroot, + [ + 'headinglevel' => 1, + 'confirmtitle' => get_string('loginrequired'), + ], + ); echo $OUTPUT->footer(); die; } diff --git a/user/view.php b/user/view.php index f724a95d65d..361233d6f85 100644 --- a/user/view.php +++ b/user/view.php @@ -68,9 +68,15 @@ if (!empty($CFG->forceloginforprofiles)) { $PAGE->set_secondary_navigation(false); $PAGE->set_title(get_string('loginrequired')); echo $OUTPUT->header(); - echo $OUTPUT->confirm(get_string('guestcantaccessprofiles', 'error'), - get_login_url(), - $CFG->wwwroot); + echo $OUTPUT->confirm( + get_string('guestcantaccessprofiles', 'error'), + get_login_url(), + $CFG->wwwroot, + [ + 'headinglevel' => 1, + 'confirmtitle' => get_string('loginrequired'), + ], + ); echo $OUTPUT->footer(); die; } From 4f2deeadf0ee88298aa42625b38116da7eac7344 Mon Sep 17 00:00:00 2001 From: Stefan Topfstedt Date: Mon, 2 Feb 2026 10:23:24 -0800 Subject: [PATCH 3/4] MDL-87694 profile: change page title to "Login required". This realigns the page title with user/view.php. --- user/profile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user/profile.php b/user/profile.php index 34fbaa1d64e..67d0fba158c 100644 --- a/user/profile.php +++ b/user/profile.php @@ -50,7 +50,7 @@ if (!empty($CFG->forceloginforprofiles)) { require_login(); if (isguestuser()) { $PAGE->set_context(context_system::instance()); - $PAGE->set_title(get_string('user')); + $PAGE->set_title(get_string('loginrequired')); echo $OUTPUT->header(); echo $OUTPUT->confirm( get_string('guestcantaccessprofiles', 'error'), From a23bd2ad53c942a81cc1161111f7e5455707b984 Mon Sep 17 00:00:00 2001 From: Stefan Topfstedt Date: Mon, 2 Feb 2026 10:38:14 -0800 Subject: [PATCH 4/4] MDL-87694 core: prevent empty heading in context header. --- lib/classes/output/context_header.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/classes/output/context_header.php b/lib/classes/output/context_header.php index b3030ced3af..7327669401b 100644 --- a/lib/classes/output/context_header.php +++ b/lib/classes/output/context_header.php @@ -111,8 +111,11 @@ class context_header implements renderable, templatable { */ public function export_for_template(renderer_base $output): array { // Heading. + $heading = ''; $headingtext = isset($this->heading) ? $this->heading : $output->get_page()->heading; - $heading = $output->heading($headingtext, $this->headinglevel, "h2 mb-0"); + if ('' !== $headingtext) { + $heading = $output->heading($headingtext, $this->headinglevel, "h2 mb-0"); + } // Buttons. if (isset($this->additionalbuttons)) {