From ecd5274e74d20572006ee58b62377f5d5c4d78ba Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Wed, 6 Mar 2024 22:33:21 +0800 Subject: [PATCH] MDL-81144 core: Convert add_htmlattributes to hook --- lib/classes/hook/output/before_html_attributes.php | 98 ++++++++++++++++++++++ lib/outputrenderers.php | 28 +++---- lib/tests/core_renderer_test.php | 33 ++++++++ .../core_renderer/htmlattributes_callbacks.php | 36 ++++++++ .../core_renderer/htmlattributes_hooks.php | 33 ++++++++ 5 files changed, 212 insertions(+), 16 deletions(-) create mode 100644 lib/classes/hook/output/before_html_attributes.php create mode 100644 lib/tests/fixtures/core_renderer/htmlattributes_callbacks.php create mode 100644 lib/tests/fixtures/core_renderer/htmlattributes_hooks.php diff --git a/lib/classes/hook/output/before_html_attributes.php b/lib/classes/hook/output/before_html_attributes.php new file mode 100644 index 00000000000..e1f46ebc286 --- /dev/null +++ b/lib/classes/hook/output/before_html_attributes.php @@ -0,0 +1,98 @@ +. + +namespace core\hook\output; + +/** + * Class before_html_attributes + * + * @package core + * @copyright 2024 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @property-read \renderer_base $renderer The page renderer object + * @property array $attributes The list of HTML attributes to be added to the tag. + */ +#[\core\attribute\tags('output')] +#[\core\attribute\label('Allows plugins to add, remove or modify any attributes of the html tag.')] +#[\core\attribute\hook\replaces_callbacks('add_htmlattributes')] +final class before_html_attributes { + /** + * Constructor for the before_html_attributes hook. + * + * @param \renderer_base $renderer The page renderer object + * @param array $attributes The list of HTML attributes initially on the tag + */ + public function __construct( + /** @var \renderer_base The page renderer */ + public readonly \renderer_base $renderer, + /** @var array The list of HTML attributes initially on the tag */ + private array $attributes = [], + ) { + } + + /** + * Add an HTML attribute to the list. + * + * @param string $name + * @param string $value + */ + public function add_attribute(string $name, string $value): void { + $this->attributes[$name] = $value; + } + + /** + * Get the list of attributes. + * + * @return array + */ + public function get_attributes(): array { + return $this->attributes; + } + + /** + * Remove an HTML attribute from the list. + * + * @param string $name + */ + public function remove_attribute(string $name): void { + unset($this->attributes[$name]); + } + + /** + * Process legacy callbacks. + */ + public function process_legacy_callbacks(): void { + // Legacy callback 'add_htmlattributes' is deprecated since Moodle 4.4. + + // This function should return an array of html attribute names => values. + $pluginswithfunction = get_plugins_with_function( + function: 'add_htmlattributes', + migratedtohook: true, + ); + foreach ($pluginswithfunction as $plugins) { + foreach ($plugins as $function) { + $newattrs = $function(); + unset($newattrs['dir']); + unset($newattrs['lang']); + unset($newattrs['xmlns']); + unset($newattrs['xml:lang']); + foreach ($newattrs as $name => $value) { + $this->add_attribute($name, $value); + } + } + } + } +} diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index dc36da8b0e0..062c8a86707 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -656,26 +656,22 @@ class core_renderer extends renderer_base { */ public function htmlattributes() { $return = get_html_lang(true); - $attributes = array(); + + // Ensure that the callback exists prior to cache purge. + // This is a critical page path. + // TODO MDL-81134 Remove after LTS+1. + require_once(__DIR__ . '/classes/hook/output/before_html_attributes.php'); + + $hook = new before_html_attributes($this); + if ($this->page->theme->doctype !== 'html5') { - $attributes['xmlns'] = 'http://www.w3.org/1999/xhtml'; + $hook->add_attribute('xmlns', 'http://www.w3.org/1999/xhtml'); } - // Give plugins an opportunity to add things like xml namespaces to the html element. - // This function should return an array of html attribute names => values. - $pluginswithfunction = get_plugins_with_function('add_htmlattributes', 'lib.php'); - foreach ($pluginswithfunction as $plugins) { - foreach ($plugins as $function) { - $newattrs = $function(); - unset($newattrs['dir']); - unset($newattrs['lang']); - unset($newattrs['xmlns']); - unset($newattrs['xml:lang']); - $attributes += $newattrs; - } - } + di::get(hook_manager::class)->dispatch($hook); + $hook->process_legacy_callbacks(); - foreach ($attributes as $key => $val) { + foreach ($hook->get_attributes() as $key => $val) { $val = s($val); $return .= " $key=\"$val\""; } diff --git a/lib/tests/core_renderer_test.php b/lib/tests/core_renderer_test.php index 7c17183937c..16cc3690cfa 100644 --- a/lib/tests/core_renderer_test.php +++ b/lib/tests/core_renderer_test.php @@ -94,4 +94,37 @@ final class core_renderer_test extends \advanced_testcase { $this->assertIsString($html); $this->assertStringContainsString('A heading can be added', $html); } + + /** + * @covers \core\hook\before_html_attributes + */ + public function test_htmlattributes(): void { + $page = new moodle_page(); + $renderer = new core_renderer($page, RENDERER_TARGET_GENERAL); + + $attributes = $renderer->htmlattributes(); + $this->assertIsString($attributes); + $this->assertStringNotContainsString('data-test="test"', $attributes); + } + + /** + * @covers \core\hook\before_html_attributes + */ + public function test_htmlattributes_hooked(): void { + require_once(__DIR__ . '/fixtures/core_renderer/htmlattributes_callbacks.php'); + + \core\di::set( + \core\hook\manager::class, + \core\hook\manager::phpunit_get_instance([ + 'test_plugin1' => __DIR__ . '/fixtures/core_renderer/htmlattributes_hooks.php', + ]), + ); + + $page = new moodle_page(); + $renderer = new core_renderer($page, RENDERER_TARGET_GENERAL); + + $attributes = $renderer->htmlattributes(); + $this->assertIsString($attributes); + $this->assertStringContainsString('data-test="test"', $attributes); + } } diff --git a/lib/tests/fixtures/core_renderer/htmlattributes_callbacks.php b/lib/tests/fixtures/core_renderer/htmlattributes_callbacks.php new file mode 100644 index 00000000000..c5a1f2b7f14 --- /dev/null +++ b/lib/tests/fixtures/core_renderer/htmlattributes_callbacks.php @@ -0,0 +1,36 @@ +. + +namespace test_fixtures\core_renderer; + +/** + * Hook fixture for \core_renderer::htmlattributes. + * + * @package core + * @category test + * @copyright 2024 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +final class htmlattributes { + /** + * Fixture for adding a data attribute to the HTML element. + * + * @param \core\hook\output\before_html_attributes $hook + */ + public static function before_html_attributes(\core\hook\output\before_html_attributes $hook): void { + $hook->add_attribute('data-test', 'test'); + } +} diff --git a/lib/tests/fixtures/core_renderer/htmlattributes_hooks.php b/lib/tests/fixtures/core_renderer/htmlattributes_hooks.php new file mode 100644 index 00000000000..fa8bf33279f --- /dev/null +++ b/lib/tests/fixtures/core_renderer/htmlattributes_hooks.php @@ -0,0 +1,33 @@ +. + +/** + * Hook fixture for \core_renderer::htmlattributes. + * + * @package core + * @category test + * @copyright 2024 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$callbacks = [ + [ + 'hook' => \core\hook\output\before_html_attributes::class, + 'callback' => \test_fixtures\core_renderer\htmlattributes::class . '::before_html_attributes', + ], +]; -- 2.11.4.GIT