From e6af0ea94e7a097d8ddce8db782de18a130750c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikel=20Mart=C3=ADn?= Date: Mon, 13 May 2024 11:39:10 +0200 Subject: [PATCH 1/2] MDL-81597 renderer: Add page getter method to renderer_base --- lib/outputrenderers.php | 9 +++++++++ lib/upgrade.txt | 1 + question/engine/renderer.php | 3 --- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index eadf44544d3..f8947a93e8a 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -440,6 +440,15 @@ class renderer_base { return false; } + + /** + * Returns the moodle page object. + * + * @return moodle_page + */ + public function get_page(): moodle_page { + return $this->page; + } } diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 49ec5e12f8e..32f3e444bb9 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -151,6 +151,7 @@ information provided here is intended especially for developers. * groups_get_user_groups() in grouplib has a new $includehidden paramater, which will return groups for user even if they have GROUP_VISIBILITY_NONE. This is false by default, and should be used with care. The calling code must ensure that these groups are not exposed to the user as this may be a privacy issue. +* Added a new `renderer_base::get_page` getter method. === 4.3 === diff --git a/question/engine/renderer.php b/question/engine/renderer.php index 29458b8fc76..b5139a60736 100644 --- a/question/engine/renderer.php +++ b/question/engine/renderer.php @@ -37,9 +37,6 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class core_question_renderer extends plugin_renderer_base { - public function get_page() { - return $this->page; - } /** * @deprecated since Moodle 4.0 From 4d23fe8a58512c4d79f3eba71ddb44a409fb7928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikel=20Mart=C3=ADn?= Date: Wed, 8 May 2024 13:37:29 +0200 Subject: [PATCH 2/2] MDL-81597 theme_boost: Fix header bar alignment - Refactot context_header class to implement named templatable so render_context_header in core and theme_boost can be removed - Refactor context_header to use templates - Fix context header layout and styles --- lib/outputcomponents.php | 54 +++++++++++++- lib/outputrenderers.php | 74 ++----------------- lib/templates/contextheader.mustache | 75 ++++++++++++++++++++ theme/boost/classes/output/core_renderer.php | 69 ++---------------- theme/boost/scss/moodle/core.scss | 18 ++--- theme/boost/style/moodle.css | 13 +--- theme/classic/style/moodle.css | 13 +--- theme/upgrade.txt | 1 + 8 files changed, 150 insertions(+), 167 deletions(-) create mode 100644 lib/templates/contextheader.mustache diff --git a/lib/outputcomponents.php b/lib/outputcomponents.php index 50db844c528..6af82ec8f6a 100644 --- a/lib/outputcomponents.php +++ b/lib/outputcomponents.php @@ -27,6 +27,7 @@ */ use core\output\local\action_menu\subpanel; + use core\output\named_templatable; defined('MOODLE_INTERNAL') || die(); @@ -4109,7 +4110,7 @@ class tabobject implements renderable, templatable { * @copyright 2015 Adrian Greeve * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class context_header implements renderable { +class context_header implements renderable, named_templatable { /** * @var string $heading Main heading. @@ -4190,6 +4191,57 @@ class context_header implements renderable { array('class' => $class)); } } + + /** + * Export for template. + * + * @param renderer_base $output Renderer. + * @return array + */ + public function export_for_template(renderer_base $output): array { + // Heading. + $headingtext = isset($this->heading) ? $this->heading : $output->get_page()->heading; + $heading = $output->heading($headingtext, $this->headinglevel, "h2 mb-0"); + + // Buttons. + if (isset($this->additionalbuttons)) { + $additionalbuttons = []; + foreach ($this->additionalbuttons as $button) { + if (!isset($button->page)) { + // Include js for messaging. + if ($button['buttontype'] === 'togglecontact') { + \core_message\helper::togglecontact_requirejs(); + } + if ($button['buttontype'] === 'message') { + \core_message\helper::messageuser_requirejs(); + } + } + foreach ($button['linkattributes'] as $key => $value) { + $button['attributes'][] = ['name' => $key, 'value' => $value]; + } + $additionalbuttons[] = $button; + } + } + + return [ + 'heading' => $heading, + 'headinglevel' => $this->headinglevel, + 'imagedata' => $this->imagedata, + 'prefix' => $this->prefix, + 'hasadditionalbuttons' => !empty($additionalbuttons), + 'additionalbuttons' => $additionalbuttons ?? [], + ]; + } + + /** + * Get the template name. + * + * @param renderer_base $renderer Renderer. + * @return string + */ + public function get_template_name(renderer_base $renderer): string { + return 'core/contextheader'; + } } /** diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index f8947a93e8a..5006992228d 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -4602,9 +4602,13 @@ EOD; } } + // Return the heading wrapped in an sr-only element so it is only visible to screen-readers. + if (!empty($this->page->layout_options['nocontextheader'])) { + return html_writer::div($heading, 'sr-only'); + } $contextheader = new context_header($heading, $headinglevel, $imagedata, $userbuttons); - return $this->render_context_header($contextheader); + return $this->render($contextheader); } /** @@ -4623,74 +4627,6 @@ EOD; return $this->render_from_template('core/skip_links', $context); } - /** - * Renders the header bar. - * - * @param context_header $contextheader Header bar object. - * @return string HTML for the header bar. - */ - protected function render_context_header(context_header $contextheader) { - - // Generate the heading first and before everything else as we might have to do an early return. - if (!isset($contextheader->heading)) { - $heading = $this->heading($this->page->heading, $contextheader->headinglevel); - } else { - $heading = $this->heading($contextheader->heading, $contextheader->headinglevel); - } - - $showheader = empty($this->page->layout_options['nocontextheader']); - if (!$showheader) { - // Return the heading wrapped in an sr-only element so it is only visible to screen-readers. - return html_writer::div($heading, 'sr-only'); - } - - // All the html stuff goes here. - $html = html_writer::start_div('page-context-header'); - - // Image data. - if (isset($contextheader->imagedata)) { - // Header specific image. - $html .= html_writer::div($contextheader->imagedata, 'page-header-image icon-size-7'); - } - - // Headings. - if (isset($contextheader->prefix)) { - $prefix = html_writer::div($contextheader->prefix, 'text-muted'); - $heading = $prefix . $heading; - } - $html .= html_writer::tag('div', $heading, array('class' => 'page-header-headings')); - - // Buttons. - if (isset($contextheader->additionalbuttons)) { - $html .= html_writer::start_div('btn-group header-button-group'); - foreach ($contextheader->additionalbuttons as $button) { - if (!isset($button->page)) { - // Include js for messaging. - if ($button['buttontype'] === 'togglecontact') { - \core_message\helper::togglecontact_requirejs(); - } - if ($button['buttontype'] === 'message') { - \core_message\helper::messageuser_requirejs(); - } - $image = $this->pix_icon($button['formattedimage'], '', 'moodle', array( - 'class' => 'iconsmall', - )); - $image .= html_writer::span($button['title'], 'header-button-title'); - } else { - $image = html_writer::empty_tag('img', array( - 'src' => $button['formattedimage'], - 'alt' => $button['title'], - )); - } - $html .= html_writer::link($button['url'], html_writer::tag('span', $image), $button['linkattributes']); - } - $html .= html_writer::end_div(); - } - $html .= html_writer::end_div(); - - return $html; - } - /** * Wrapper for header elements. * diff --git a/lib/templates/contextheader.mustache b/lib/templates/contextheader.mustache new file mode 100644 index 00000000000..69364aae1af --- /dev/null +++ b/lib/templates/contextheader.mustache @@ -0,0 +1,75 @@ +{{! + 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/contextheader + + Context header template. + + Example context (json): + { + "heading": "

Page title

", + "prefix": "Page prefix", + "hasadditionalbuttons": true, + "additionalbuttons": [ + { + "url": "http://example.com", + "title": "Button title", + "formattedimage": "http://example.com/image.jpg", + "attributes": [ + { + "name": "data-attribute", + "value": "attribute value" + }, + { + "name": "class", + "value": "btn btn-primary" + } + ] + } + ] + } +}} +
+ {{#imagedata}} +
+ {{{imagedata}}} +
+ {{/imagedata}} +
+ {{#prefix}} +
+ {{{prefix}}} +
+ {{/prefix}} + {{{heading}}} +
+ {{#hasadditionalbuttons}} + + {{/hasadditionalbuttons}} +
diff --git a/theme/boost/classes/output/core_renderer.php b/theme/boost/classes/output/core_renderer.php index 38ea3b9852f..f19be0089ab 100644 --- a/theme/boost/classes/output/core_renderer.php +++ b/theme/boost/classes/output/core_renderer.php @@ -178,70 +178,13 @@ class core_renderer extends \core_renderer { } } + // Return the heading wrapped in an sr-only element so it is only visible to screen-readers. + if (!empty($this->page->layout_options['nocontextheader'])) { + return html_writer::div($heading, 'sr-only'); + } + $contextheader = new \context_header($heading, $headinglevel, $imagedata, $userbuttons, $prefix); - return $this->render_context_header($contextheader); - } - - /** - * Renders the header bar. - * - * @param context_header $contextheader Header bar object. - * @return string HTML for the header bar. - */ - protected function render_context_header(\context_header $contextheader) { - - // Generate the heading first and before everything else as we might have to do an early return. - if (!isset($contextheader->heading)) { - $heading = $this->heading($this->page->heading, $contextheader->headinglevel, 'h2'); - } else { - $heading = $this->heading($contextheader->heading, $contextheader->headinglevel, 'h2'); - } - - // All the html stuff goes here. - $html = html_writer::start_div('page-context-header'); - - // Image data. - if (isset($contextheader->imagedata)) { - // Header specific image. - $html .= html_writer::div($contextheader->imagedata, 'page-header-image mr-2'); - } - - // Headings. - if (isset($contextheader->prefix)) { - $prefix = html_writer::div($contextheader->prefix, 'text-muted text-uppercase small line-height-3'); - $heading = $prefix . $heading; - } - $html .= html_writer::tag('div', $heading, array('class' => 'page-header-headings')); - - // Buttons. - if (isset($contextheader->additionalbuttons)) { - $html .= html_writer::start_div('btn-group header-button-group'); - foreach ($contextheader->additionalbuttons as $button) { - if (!isset($button->page)) { - // Include js for messaging. - if ($button['buttontype'] === 'togglecontact') { - \core_message\helper::togglecontact_requirejs(); - } - if ($button['buttontype'] === 'message') { - \core_message\helper::messageuser_requirejs(); - } - $image = $this->pix_icon($button['formattedimage'], '', 'moodle', array( - 'class' => 'iconsmall', - )); - $image .= html_writer::span($button['title'], 'header-button-title'); - } else { - $image = html_writer::empty_tag('img', array( - 'src' => $button['formattedimage'], - 'alt' => $button['title'], - )); - } - $html .= html_writer::link($button['url'], html_writer::tag('span', $image), $button['linkattributes']); - } - $html .= html_writer::end_div(); - } - $html .= html_writer::end_div(); - - return $html; + return $this->render($contextheader); } /** diff --git a/theme/boost/scss/moodle/core.scss b/theme/boost/scss/moodle/core.scss index 12278c2b686..fd79fc60fd4 100644 --- a/theme/boost/scss/moodle/core.scss +++ b/theme/boost/scss/moodle/core.scss @@ -1955,24 +1955,14 @@ nav.navbar .logo img { overflow: hidden; padding: 0.25rem 0; - display: flex; - .page-header-image { & > a { display: inline-block; } - } - - .page-header-headings, - .header-button-group { - position: relative; - line-height: 24px; - vertical-align: middle; - } - - .header-button-group { - display: block; - float: left; + .userinitials, + .userpicture { + margin-right: map-get($spacers, 3); + } } } diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index d2db14b2741..bdeb4c0fd60 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -24954,20 +24954,13 @@ nav.navbar .logo img { .page-context-header { overflow: hidden; padding: 0.25rem 0; - display: flex; } .page-context-header .page-header-image > a { display: inline-block; } -.page-context-header .page-header-headings, -.page-context-header .header-button-group { - position: relative; - line-height: 24px; - vertical-align: middle; -} -.page-context-header .header-button-group { - display: block; - float: left; +.page-context-header .page-header-image .userinitials, +.page-context-header .page-header-image .userpicture { + margin-right: 1rem; } ul.dragdrop-keyboard-drag li { diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index 009232f9704..b481b044b13 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -24954,20 +24954,13 @@ nav.navbar .logo img { .page-context-header { overflow: hidden; padding: 0.25rem 0; - display: flex; } .page-context-header .page-header-image > a { display: inline-block; } -.page-context-header .page-header-headings, -.page-context-header .header-button-group { - position: relative; - line-height: 24px; - vertical-align: middle; -} -.page-context-header .header-button-group { - display: block; - float: left; +.page-context-header .page-header-image .userinitials, +.page-context-header .page-header-image .userpicture { + margin-right: 1rem; } ul.dragdrop-keyboard-drag li { diff --git a/theme/upgrade.txt b/theme/upgrade.txt index 5610a75127d..61b7c5d55a2 100644 --- a/theme/upgrade.txt +++ b/theme/upgrade.txt @@ -6,6 +6,7 @@ information provided here is intended especially for theme designer. * The 'choosereadme' string for each theme is expected to be plain text. HTML content will not render when displayed in the modal. * Theme selector page has been moved to admin/themeselector.php (previously theme/index.php) * Moodleforms no longer use tag 'fieldset' for groups without labels (legends). Avoid targeting tag 'fieldset' in the theme CSS. +* New `core/contextheader` mustache template has been added. This template can be overridden by themes to modify the context header. === 4.3 === * The core_renderer::htmllize_file_tree method has been deprecated. This was missed before Moodle 2.0.