MDL-72352 navigation: Third party settings with children displayed.

This makes sure that we are backwards compatible with plugins that
have extended the course or module settings navigation and have
containers.
This commit is contained in:
Adrian Greeve
2021-10-20 14:55:11 +08:00
committed by Peter Dias
parent e746dc75af
commit f7e33da502
3 changed files with 207 additions and 3 deletions
+194 -2
View File
@@ -190,13 +190,129 @@ class secondary extends view {
$this->initialised = true;
}
/**
* Recursively goes and gets all children nodes.
*
* @param navigation_node $node The node to get the children of.
* @return array The additional child nodes.
*/
protected function get_additional_child_nodes(navigation_node $node): array {
$nodes = [];
foreach ($node->children as $child) {
if ($child->has_action()) {
$nodes[$child->action->out()] = $child->text;
}
if ($child->has_children()) {
$childnodes = $this->get_additional_child_nodes($child);
$nodes = array_merge($nodes, $childnodes);
}
}
return $nodes;
}
/**
* Returns an array of sections, actions, and text for a url select menu.
*
* @param navigation_node $node The node to use for a url select menu.
* @return array The menu array.
*/
protected function get_menu_array(navigation_node $node): array {
$urldata = [];
// Check that children have children.
$additionalchildren = false;
$initialchildren = [];
if ($node->has_action()) {
$initialchildren[$node->action->out()] = $node->text;
}
foreach ($node->children as $child) {
$additionalnode = [];
if ($child->has_action()) {
$additionalnode[$child->action->out()] = $child->text;
}
if ($child->has_children()) {
$additionalchildren = true;
$urldata[][$child->text] = $additionalnode + $this->get_additional_child_nodes($child);
} else {
$initialchildren += $additionalnode;
}
}
if ($additionalchildren) {
$urldata[][$node->text] = $initialchildren;
} else {
$urldata = $initialchildren;
}
return $urldata;
}
/**
* Returns a node with the action being from the first found child node that has an action (Recursive).
*
* @param navigation_node $node The part of the node tree we are checking.
* @param navigation_node $basenode The very first node to be used for the return.
* @return navigation_node|null
*/
protected function get_node_with_first_action(navigation_node $node, navigation_node $basenode): ?navigation_node {
$newnode = null;
if (!$node->has_children()) {
return null;
}
// Find the first child with an action and update the main node.
foreach ($node->children as $child) {
if ($child->has_action()) {
$newnode = $basenode;
$newnode->action = $child->action;
return $newnode;
}
}
if (is_null($newnode)) {
// Check for children and go again.
foreach ($node->children as $child) {
if ($child->has_children()) {
$newnode = $this->get_node_with_first_action($child, $basenode);
if (!is_null($newnode)) {
return $newnode;
}
}
}
}
return null;
}
/**
* Some nodes are containers only with no action. If this container has an action then nothing is done. If it does not have
* an action then a search is done through the children looking for the first node that has an action. This action is then given
* to the parent node that is initially provided as a parameter.
*
* @param navigation_node $node The navigation node that we want to ensure has an action tied to it.
* @return navigation_node The node intact with an action to use.
*/
protected function get_first_action_for_node(navigation_node $node): ?navigation_node {
// If the node does not have children OR has an action no further processing needed.
$newnode = null;
if ($node->has_children()) {
if (!$node->has_action()) {
// We want to find the first child with an action.
// We want to check all children on this level before going further down.
// Note that new node gets changed here.
$newnode = $this->get_node_with_first_action($node, $node);
} else {
$newnode = $node;
}
}
return $newnode;
}
/**
* Load the course secondary navigation. Since we are sourcing all the info from existing objects that already do
* the relevant checks, we don't do it again here.
*/
protected function load_course_navigation(): void {
$course = $this->page->course;
// Initialise the main navigation and settings nav.
// It is important that this is done before we try anything.
$settingsnav = $this->page->settingsnav;
@@ -217,6 +333,74 @@ class secondary extends view {
$url = new \moodle_url('/course/admin.php', array('courseid' => $this->page->course->id));
$this->add($text, $url, null, null, 'courseadmin', new \pix_icon('t/edit', $text));
}
// Try to get any custom nodes defined by a user which may include containers.
$expectedcourseadmin = ['editsettings', 'coursecompletion', 'users', 'coursereports', 'gradebooksetup', 'coursebadges',
'backup', 'restore', 'import', 'copy', 'reset', 'questionbank'];
foreach ($settingsnav->children as $value) {
if ($value->key == 'courseadmin') {
foreach ($value->children as $other) {
if (array_search($other->key, $expectedcourseadmin) === false) {
$othernode = $this->get_first_action_for_node($other);
// Get the first node and check whether it's been added already.
if ($othernode && !$this->get($othernode->key)) {
$this->add_node($othernode);
}
}
}
}
}
}
/**
* Recursively looks for a match to the current page url.
*
* @param navigation_node $node The node to look through.
* @return navigation_node|null The node that matches this page's url.
*/
protected function nodes_match_current_url(navigation_node $node): ?navigation_node {
$pagenode = $this->page->url;
if ($node->has_action()) {
// Check this node first.
if ($node->action->compare($pagenode)) {
return $node;
}
}
if ($node->has_children()) {
foreach ($node->children as $child) {
$result = $this->nodes_match_current_url($child);
if ($result) {
return $result;
}
}
}
return null;
}
/**
* Returns a url_select object with overflow navigation nodes.
*
* @return \url_select|null The overflow menu data.
*/
public function get_overflow_menu_data(): ?\url_select {
$activenode = $this->find_active_node();
if ($activenode && $activenode->has_action() && $activenode->has_children() && $activenode->key != 'coursehome') {
// This needs to be expanded to does the active node have children and does the page url match any of the children.
$menunode = $this->page->settingsnav->find($activenode->key, null);
if ($menunode instanceof navigation_node) {
// Loop through all children and try and find a match to the current url.
$matchednode = $this->nodes_match_current_url($menunode);
if (is_null($matchednode)) {
return null;
}
if (!isset($menunode) || !$menunode->has_children()) {
return null;
}
$selectdata = $this->get_menu_array($menunode);
return new \url_select($selectdata, $matchednode->action->out(), null);
}
}
return null;
}
/**
@@ -335,7 +519,15 @@ class secondary extends view {
$leftover = array_diff($existingkeys, $populatedkeys);
foreach ($leftover as $key) {
if (!in_array($key, $flattenednodes) && $leftovernode = $completenode->get($key)) {
$this->add_node($leftovernode);
// Check for nodes with children and potentially no action to direct to.
if ($leftovernode->has_children()) {
$leftovernode = $this->get_first_action_for_node($leftovernode);
}
// Confirm we have a valid object to add.
if ($leftovernode) {
$this->add_node($leftovernode);
}
}
}
}
+10 -1
View File
@@ -71,11 +71,19 @@ $buildregionmainsettings = !$PAGE->include_region_main_settings_in_header_action
$regionmainsettingsmenu = $buildregionmainsettings ? $OUTPUT->region_main_settings_menu() : false;
$secondarynavigation = false;
$overflow = false;
if (!defined('BEHAT_SITE_RUNNING')) {
$buildsecondarynavigation = $PAGE->has_secondary_navigation();
if ($buildsecondarynavigation) {
$moremenu = new \core\navigation\output\more_menu($PAGE->secondarynav, 'nav-tabs');
$secondary = $PAGE->secondarynav;
$moremenu = new \core\navigation\output\more_menu($secondary, 'nav-tabs');
$secondarynavigation = $moremenu->export_for_template($OUTPUT);
// Get the pre-content stuff.
$overflowdata = $secondary->get_overflow_menu_data();
if (!is_null($overflowdata)) {
$overflow = $overflowdata->export_for_template($OUTPUT);
}
}
}
@@ -100,6 +108,7 @@ $templatecontext = [
'usermenu' => $primarymenu['user'],
'langmenu' => $primarymenu['lang'],
'forceblockdraweropen' => $forceblockdraweropen,
'overflow' => $overflow
];
$nav = $PAGE->flatnav;
+3
View File
@@ -129,6 +129,9 @@
<div class="region_main_settings_menu_proxy"></div>
{{/hasregionmainsettingsmenu}}
{{{ output.course_content_header }}}
{{#overflow}}
{{> core/url_select}}
{{/overflow}}
{{{ output.main_content }}}
{{{ output.activity_navigation }}}
{{{ output.course_content_footer }}}