diff --git a/lib/outputcomponents.php b/lib/outputcomponents.php
index 88e34860cf492..80f614d6691a3 100644
--- a/lib/outputcomponents.php
+++ b/lib/outputcomponents.php
@@ -3759,7 +3759,7 @@ public function export_for_template(renderer_base $output) {
* @copyright 2013 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class action_menu implements renderable {
+class action_menu implements renderable, templatable {
/**
* Top right alignment.
@@ -4108,6 +4108,98 @@ public function set_nowrap_on_items($value = true) {
$this->attributes['class'] = $class;
}
}
+
+ /**
+ * Export for template.
+ *
+ * @param renderer_base $output The renderer.
+ * @return stdClass
+ */
+ public function export_for_template(renderer_base $output) {
+ $data = new stdClass();
+ $attributes = $this->attributes;
+ $attributesprimary = $this->attributesprimary;
+ $attributessecondary = $this->attributessecondary;
+
+ $data->instance = $this->instance;
+
+ $data->classes = isset($attributes['class']) ? $attributes['class'] : '';
+ unset($attributes['class']);
+
+ $data->attributes = array_map(function($key, $value) {
+ return [ 'name' => $key, 'value' => $value ];
+ }, array_keys($attributes), $attributes);
+
+ $primary = new stdClass();
+ $primary->title = '';
+ $primary->prioritise = $this->prioritise;
+
+ $primary->classes = isset($attributesprimary['class']) ? $attributesprimary['class'] : '';
+ unset($attributesprimary['class']);
+ $primary->attributes = array_map(function($key, $value) {
+ return [ 'name' => $key, 'value' => $value ];
+ }, array_keys($attributesprimary), $attributesprimary);
+
+ $actionicon = $this->actionicon;
+ if (!empty($this->menutrigger)) {
+ $primary->menutrigger = $this->menutrigger;
+ } else {
+ $primary->title = get_string('actions');
+ $actionicon = new pix_icon('t/edit_menu', '', 'moodle', ['class' => 'iconsmall actionmenu', 'title' => '']);
+ }
+
+ if ($actionicon instanceof pix_icon) {
+ $primary->icon = $actionicon->export_for_template($output);
+ $primary->title = !empty($actionicon->attributes['alt']) ? $this->actionicon->attributes['alt'] : '';
+ } else {
+ $primary->iconraw = $actionicon ? $output->render($actionicon) : '';
+ }
+
+ $primary->actiontext = $this->actiontext ? (string) $this->actiontext : '';
+ $primary->items = array_map(function($item) use ($output) {
+ $data = (object) [];
+ if ($item instanceof action_menu_link) {
+ $data->actionmenulink = $item->export_for_template($output);
+ } else if ($item instanceof action_menu_filler) {
+ $data->actionmenufiller = $item->export_for_template($output);
+ } else if ($item instanceof action_link) {
+ $data->actionlink = $item->export_for_template($output);
+ } else if ($item instanceof pix_icon) {
+ $data->pixicon = $item->export_for_template($output);
+ } else {
+ $data->rawhtml = ($item instanceof renderable) ? $output->render($item) : $item;
+ }
+ return $data;
+ }, $this->primaryactions);
+
+ $secondary = new stdClass();
+ $secondary->classes = isset($attributessecondary['class']) ? $attributessecondary['class'] : '';
+ unset($attributessecondary['class']);
+ $secondary->attributes = array_map(function($key, $value) {
+ return [ 'name' => $key, 'value' => $value ];
+ }, array_keys($attributessecondary), $attributessecondary);
+ $secondary->items = array_map(function($item) use ($output) {
+ $data = (object) [];
+ if ($item instanceof action_menu_link) {
+ $data->actionmenulink = $item->export_for_template($output);
+ } else if ($item instanceof action_menu_filler) {
+ $data->actionmenufiller = $item->export_for_template($output);
+ } else if ($item instanceof action_link) {
+ $data->actionlink = $item->export_for_template($output);
+ } else if ($item instanceof pix_icon) {
+ $data->pixicon = $item->export_for_template($output);
+ } else {
+ $data->rawhtml = ($item instanceof renderable) ? $output->render($item) : $item;
+ }
+ return $data;
+ }, $this->secondaryactions);
+
+ $data->primary = $primary;
+ $data->secondary = $secondary;
+
+ return $data;
+ }
+
}
/**
@@ -4170,6 +4262,64 @@ public function __construct(moodle_url $url, pix_icon $icon = null, $text, $prim
$this->add_class('menu-action');
$this->attributes['role'] = 'menuitem';
}
+
+ /**
+ * Export for template.
+ *
+ * @param renderer_base $output The renderer.
+ * @return stdClass
+ */
+ public function export_for_template(renderer_base $output) {
+ static $instance = 1;
+
+ $data = parent::export_for_template($output);
+ $data->instance = $instance++;
+
+ // Ignore what the parent did with the attributes, except for ID and class.
+ $data->attributes = [];
+ $attributes = $this->attributes;
+ unset($attributes['id']);
+ unset($attributes['class']);
+
+ // Handle text being a renderable.
+ $comparetoalt = $this->text;
+ if ($this->text instanceof renderable) {
+ $data->text = $this->render($this->text);
+ $comparetoalt = '';
+ }
+ $comparetoalt = (string) $comparetoalt;
+
+ $data->showtext = (!$this->icon || $this->primary === false);
+
+ $data->icon = null;
+ if ($this->icon) {
+ $icon = $this->icon;
+ if ($this->primary || !$this->actionmenu->will_be_enhanced()) {
+ $attributes['title'] = $data->text;
+ }
+ if (!$this->primary && $this->actionmenu->will_be_enhanced()) {
+ if ((string) $icon->attributes['alt'] === $comparetoalt) {
+ $icon->attributes['alt'] = '';
+ }
+ if (isset($icon->attributes['title']) && (string) $icon->attributes['title'] === $comparetoalt) {
+ unset($icon->attributes['title']);
+ }
+ }
+ $data->icon = $icon ? $icon->export_for_template($output) : null;
+ }
+
+ $data->disabled = !empty($attributes['disabled']);
+ unset($attributes['disabled']);
+
+ $data->attributes = array_map(function($key, $value) {
+ return [
+ 'name' => $key,
+ 'value' => $value
+ ];
+ }, array_keys($attributes), $attributes);
+
+ return $data;
+ }
}
/**
diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php
index e4c8392f3acd9..750456b2d7c2c 100644
--- a/lib/outputrenderers.php
+++ b/lib/outputrenderers.php
@@ -1310,31 +1310,8 @@ public function block_controls($actions, $blockid = null) {
* @return string HTML
*/
public function render_action_menu(action_menu $menu) {
- $menu->initialise_js($this->page);
-
- $output = html_writer::start_tag('div', $menu->attributes);
- $output .= html_writer::start_tag('ul', $menu->attributesprimary);
- foreach ($menu->get_primary_actions($this) as $action) {
- if ($action instanceof renderable) {
- $content = $this->render($action);
- } else {
- $content = $action;
- }
- $output .= html_writer::tag('li', $content, array('role' => 'presentation'));
- }
- $output .= html_writer::end_tag('ul');
- $output .= html_writer::start_tag('ul', $menu->attributessecondary);
- foreach ($menu->get_secondary_actions() as $action) {
- if ($action instanceof renderable) {
- $content = $this->render($action);
- } else {
- $content = $action;
- }
- $output .= html_writer::tag('li', $content, array('role' => 'presentation'));
- }
- $output .= html_writer::end_tag('ul');
- $output .= html_writer::end_tag('div');
- return $output;
+ $context = $menu->export_for_template($this);
+ return $this->render_from_template('core/action_menu', $context);
}
/**
@@ -1344,53 +1321,7 @@ public function render_action_menu(action_menu $menu) {
* @return string HTML fragment
*/
protected function render_action_menu_link(action_menu_link $action) {
- static $actioncount = 0;
- $actioncount++;
-
- $comparetoalt = '';
- $text = '';
- if (!$action->icon || $action->primary === false) {
- $text .= html_writer::start_tag('span', array('class'=>'menu-action-text', 'id' => 'actionmenuaction-'.$actioncount));
- if ($action->text instanceof renderable) {
- $text .= $this->render($action->text);
- } else {
- $text .= $action->text;
- $comparetoalt = (string)$action->text;
- }
- $text .= html_writer::end_tag('span');
- }
-
- $icon = '';
- if ($action->icon) {
- $icon = $action->icon;
- if ($action->primary || !$action->actionmenu->will_be_enhanced()) {
- $action->attributes['title'] = $action->text;
- }
- if (!$action->primary && $action->actionmenu->will_be_enhanced()) {
- if ((string)$icon->attributes['alt'] === $comparetoalt) {
- $icon->attributes['alt'] = '';
- }
- if (isset($icon->attributes['title']) && (string)$icon->attributes['title'] === $comparetoalt) {
- unset($icon->attributes['title']);
- }
- }
- $icon = $this->render($icon);
- }
-
- // A disabled link is rendered as formatted text.
- if (!empty($action->attributes['disabled'])) {
- // Do not use div here due to nesting restriction in xhtml strict.
- return html_writer::tag('span', $icon.$text, array('class'=>'currentlink', 'role' => 'menuitem'));
- }
-
- $attributes = $action->attributes;
- unset($action->attributes['disabled']);
- $attributes['href'] = $action->url;
- if ($text !== '') {
- $attributes['aria-labelledby'] = 'actionmenuaction-'.$actioncount;
- }
-
- return html_writer::tag('a', $icon.$text, $attributes);
+ return $this->render_from_template('core/action_menu_link', $action->export_for_template($this));
}
/**
@@ -3376,7 +3307,6 @@ public function user_menu($user = null, $withlinks = null) {
$divider->primary = false;
$am = new action_menu();
- $am->initialise_js($this->page);
$am->set_menu_trigger(
$returnstr
);
diff --git a/lib/templates/action_menu.mustache b/lib/templates/action_menu.mustache
new file mode 100644
index 0000000000000..6445e77ee847d
--- /dev/null
+++ b/lib/templates/action_menu.mustache
@@ -0,0 +1,49 @@
+{{!
+ 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