Skip to content

Commit

Permalink
MDL-83164 core: Add support for namespaced renderer methods
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Sep 23, 2024
1 parent 525fc81 commit 11216cc
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 28 deletions.
57 changes: 57 additions & 0 deletions .upgradenotes/MDL-83164-2024092002215993.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
issueNumber: MDL-83164
notes:
core:
- message: >
When rendering a renderable located within a namespace, the namespace
will now be included in the renderer method name with double-underscores
separating the namespace parts.
Note: Only those renderables within an `output` namespace will be
considered, for example `\core\output\action_menu\link` and only the
parts of the namespace after `output` will be included.
The following are examples of the new behaviour:
| Renderable name | Renderer method
name |
| --- |
--- |
| `\core\output\action_menu\link` |
`render_action_menu__link` |
| `\core\output\action_menu\link_primary` |
`render_action_menu__link_primary` |
| `\core\output\action\menu\link` |
`render_action__menu__link` |
| `\core\output\user_menu\link` |
`render_user_menu__link` |
type: improved
- message: >
The following renderer methods have been deprecated from the core
renderer:
| method |
replacement |
| --- |
--- |
| `render_action_menu_link` |
`render_action_menu__link` |
| `render_action_menu_link_primary` |
`render_action_menu__link_primary` |
| `render_action_menu_link_secondary` |
`render_action_menu__link_secondary` |
| `render_action_menu_filler` |
`render_action_menu__filler` |
type: deprecated
90 changes: 69 additions & 21 deletions lib/classes/output/core_renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,9 @@
use core\hook\output\before_http_headers;
use core\hook\output\before_standard_footer_html_generation;
use core\hook\output\before_standard_top_of_body_html_generation;
use core\output\action_menu\link as action_menu_link;
use core\output\action_menu\link_primary as action_menu_link_primary;
use core\output\actions\component_action;
use core\output\actions\popup_action;
use core\output\action_menu\filler as action_menu_filler;
use core\plugin_manager;
use core\output\action_menu\link_secondary as action_menu_link_secondary;
use moodleform;
use moodle_page;
use moodle_url;
Expand Down Expand Up @@ -1304,7 +1300,7 @@ public function render_action_menu(action_menu $menu) {

// We don't want the class icon there!
foreach ($menu->get_secondary_actions() as $action) {
if ($action instanceof \action_menu_link && $action->has_class('icon')) {
if ($action instanceof action_menu\link && $action->has_class('icon')) {
$action->attributes['class'] = preg_replace('/(^|\s+)icon(\s+|$)/i', '', $action->attributes['class']);
}
}
Expand Down Expand Up @@ -1391,43 +1387,95 @@ public function check_result(check_result $result) {
}

/**
* Renders an action_menu_link item.
* @deprecated Since Moodle 4.5. Will be removed in MDL-83221
*/
#[\core\attribute\deprecated(
replacement: 'render_action_menu__link',
since: '4.5',
mdl: 'MDL-83164',
)]
protected function render_action_menu_link(\action_menu_link $action) {
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
return $this->render_action_menu__link($action);
}

/**
* @deprecated Since Moodle 4.5. Will be removed in MDL-83221
*/
#[\core\attribute\deprecated(
replacement: 'render_action_menu__filler',
since: '4.5',
mdl: 'MDL-83164',
)]
protected function render_action_menu_filler(\action_menu_filler $action) {
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
return $this->render_action_menu__filler($action);
}

/**
* @deprecated Since Moodle 4.5. Will be removed in MDL-83221
*/
#[\core\attribute\deprecated(
replacement: 'render_action_menu__link_primary',
since: '4.5',
mdl: 'MDL-83164',
)]
protected function render_action_menu_primary(\action_menu_link $action) {
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
return $this->render_action_menu__link_primary($action);
}

/**
* @deprecated Since Moodle 4.5. Will be removed in MDL-83221
*/
#[\core\attribute\deprecated(
replacement: 'render_action_menu__link_secondary',
since: '4.5',
mdl: 'MDL-83164',
)]
protected function render_action_menu_secondary(\action_menu_link $action) {
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
return $this->render_action_menu__link_secondary($action);
}

/**
* Renders an action_menu link item.
*
* @param action_menu_link $action
* @param action_menu\link $action
* @return string HTML fragment
*/
protected function render_action_menu_link(action_menu_link $action) {
protected function render_action_menu__link(action_menu\link $action) {
return $this->render_from_template('core/action_menu_link', $action->export_for_template($this));
}

/**
* Renders a primary action_menu_filler item.
* Renders a primary action_menu filler item.
*
* @param action_menu_filler $action
* @param action_menu\filler $action
* @return string HTML fragment
*/
protected function render_action_menu_filler(action_menu_filler $action) {
protected function render_action_menu__filler(action_menu\filler $action) {
return html_writer::span(' ', 'filler');
}

/**
* Renders a primary action_menu_link item.
* Renders a primary action_menu link item.
*
* @param action_menu_link_primary $action
* @param action_menu\link_primary $action
* @return string HTML fragment
*/
protected function render_action_menu_link_primary(action_menu_link_primary $action) {
return $this->render_action_menu_link($action);
protected function render_action_menu__link_primary(action_menu\link_primary $action) {
return $this->render_action_menu__link($action);
}

/**
* Renders a secondary action_menu_link item.
* Renders a secondary action_menu link item.
*
* @param action_menu_link_secondary $action
* @param action_menu\link_secondary $action
* @return string HTML fragment
*/
protected function render_action_menu_link_secondary(action_menu_link_secondary $action) {
return $this->render_action_menu_link($action);
protected function render_action_menu__link_secondary(action_menu\link_secondary $action) {
return $this->render_action_menu__link($action);
}

/**
Expand Down Expand Up @@ -3178,7 +3226,7 @@ public function user_menu($user = null, $withlinks = null) {
);

// Create a divider (well, a filler).
$divider = new action_menu_filler();
$divider = new action_menu\filler();
$divider->primary = false;

$am = new action_menu();
Expand Down Expand Up @@ -3215,7 +3263,7 @@ public function user_menu($user = null, $withlinks = null) {
) . $value->title;
}

$al = new action_menu_link_secondary(
$al = new action_menu\link_secondary(
$value->url,
$pix,
$value->title,
Expand Down
30 changes: 23 additions & 7 deletions lib/classes/output/renderer_base.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,29 @@ public function render_from_template($templatename, $context) {
public function render(renderable $widget) {
$classparts = explode('\\', get_class($widget));
// Strip namespaces.
$classname = array_pop($classparts);
$classpartname = array_pop($classparts);
// Remove _renderable suffixes.
$classname = preg_replace('/_renderable$/', '', $classname);
$classname = preg_replace('/_renderable$/', '', $classpartname);

$rendermethod = "render_{$classname}";
if (method_exists($this, $rendermethod)) {
// Call the render_[widget_name] function.
// Note: This has a higher priority than the named_templatable to allow the theme to override the template.
return $this->$rendermethod($widget);
$rendermethods = [];

// If the renderable is located within a namespace, and that namespace is within the `output` L2 API,
// include the namespace as a possible renderer method name.
if (array_search('output', $classparts) === 1 && count($classparts) > 2) {
$concatenators = array_slice($classparts, 2);
$concatenators[] = $classname;
$rendermethods[] = "render_" . implode('__', $concatenators);
}

// Fall back to the last part of the class name.
$rendermethods[] = "render_{$classname}";

foreach ($rendermethods as $rendermethod) {
if (method_exists($this, $rendermethod)) {
// Call the render_[widget_name] function.
// Note: This has a higher priority than the named_templatable to allow the theme to override the template.
return $this->$rendermethod($widget);
}
}

if ($widget instanceof named_templatable) {
Expand All @@ -250,6 +264,8 @@ public function render(renderable $widget) {
$context = $widget->export_for_template($this);
return $this->render_from_template($template, $context);
}

$rendermethod = reset($rendermethods);
throw new coding_exception("Can not render widget, renderer method ('{$rendermethod}') not found.");
}

Expand Down

0 comments on commit 11216cc

Please sign in to comment.