From f17b124b7b572330b38ef93658bf2983c1a9cae5 Mon Sep 17 00:00:00 2001 From: abgreeve Date: Mon, 29 Nov 2021 14:01:19 +0800 Subject: [PATCH 1/8] MDL-72930 navigation: Creation of new secondary nav methods This adds new static methods for general use when dealing with third party navigation nodes that have been added via callbacks. --- lib/classes/navigation/views/secondary.php | 181 +++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/lib/classes/navigation/views/secondary.php b/lib/classes/navigation/views/secondary.php index 5e15666ebcc..b28a586c985 100644 --- a/lib/classes/navigation/views/secondary.php +++ b/lib/classes/navigation/views/secondary.php @@ -751,4 +751,185 @@ class secondary extends view { } } } + + /** + * Takes the given navigation nodes and searches for children and formats it all into an array in a format to be used by a + * url_select element. + * + * @param navigation_node[] $navigationnodes Navigation nodes to format into a menu. + * @param bool $forceheadings Whether the returned array should be forced to use headings. + * @return array|null A url select element for navigating through the navigation nodes. + */ + public static function create_menu_element(array $navigationnodes, bool $forceheadings = false): ?array { + if (empty($navigationnodes)) { + return null; + } + + // If one item, do we put this into a url_select? + if (count($navigationnodes) < 2) { + // Check if there are children. + $navnode = array_shift($navigationnodes); + $menudata = []; + if (!$navnode->has_children()) { + // Just one item. + if (!$navnode->has_action()) { + return null; + } + $menudata[$navnode->action->out(false)] = static::format_node_text($navnode); + } else { + if (static::does_menu_need_headings($navnode) || $forceheadings) { + // Let's do headings. + $menudata = static::get_headings_nav_array($navnode); + } else { + // Simple flat nav. + $menudata = static::get_flat_nav_array($navnode); + } + } + return $menudata; + } else { + // We have more than one navigation node to handle. Put each node in it's own heading. + $menudata = []; + $titledata = []; + foreach ($navigationnodes as $navigationnode) { + if ($navigationnode->has_children()) { + $menuarray = []; + // Add a heading and flatten out everything else. + if ($navigationnode->has_action()) { + $menuarray[static::format_node_text($navigationnode)][$navigationnode->action->out(false)] = + static::format_node_text($navigationnode); + $menuarray[static::format_node_text($navigationnode)] += static::get_whole_tree_flat($navigationnode); + } else { + $menuarray[static::format_node_text($navigationnode)] = static::get_whole_tree_flat($navigationnode); + } + + $titledata += $menuarray; + } else { + // Add with no heading. + if (!$navigationnode->has_action()) { + return null; + } + $menudata[$navigationnode->action->out(false)] = static::format_node_text($navigationnode); + } + } + $menudata += [$titledata]; + return $menudata; + } + } + + /** + * Recursively goes through the provided navigation node and returns a flat version. + * + * @param navigation_node $navigationnode The navigationnode. + * @return array The whole tree flat. + */ + protected static function get_whole_tree_flat(navigation_node $navigationnode): array { + $nodes = []; + foreach ($navigationnode->children as $child) { + if ($child->has_action()) { + $nodes[$child->action->out()] = $child->text; + } + if ($child->has_children()) { + $childnodes = static::get_whole_tree_flat($child); + $nodes = array_merge($nodes, $childnodes); + } + } + return $nodes; + } + + /** + * Checks to see if the provided navigation node has children and determines if we want headings for a url select element. + * + * @param navigation_node $navigationnode The navigation node we are checking. + * @return bool Whether we want headings or not. + */ + protected static function does_menu_need_headings(navigation_node $navigationnode): bool { + if (!$navigationnode->has_children()) { + return false; + } + foreach ($navigationnode->children as $child) { + if ($child->has_children()) { + return true; + } + } + return false; + } + + /** + * Takes the navigation node and returns it in a flat fashion. This is not recursive. + * + * @param navigation_node $navigationnode The navigation node that we want to format into an array in a flat structure. + * @return array The flat navigation array. + */ + protected static function get_flat_nav_array(navigation_node $navigationnode): array { + $menuarray = []; + if ($navigationnode->has_action()) { + $menuarray[$navigationnode->action->out(false)] = static::format_node_text($navigationnode); + } + + foreach ($navigationnode->children as $child) { + if ($child->has_action()) { + $menuarray[$child->action->out(false)] = static::format_node_text($child); + } + } + return $menuarray; + } + + /** + * For any navigation node that we have determined needs headings we return a more tree like array structure. + * + * @param navigation_node $navigationnode The navigation node to use for the formatted array structure. + * @return array The headings navigation array structure. + */ + protected static function get_headings_nav_array(navigation_node $navigationnode): array { + $menublock = []; + // We know that this single node has headings, so grab this for the first heading. + $firstheading = []; + if ($navigationnode->has_action()) { + $firstheading[static::format_node_text($navigationnode)][$navigationnode->action->out(false)] = + static::format_node_text($navigationnode); + $firstheading[static::format_node_text($navigationnode)] += static::get_more_child_nodes($navigationnode, $menublock); + } else { + $firstheading[static::format_node_text($navigationnode)] = static::get_more_child_nodes($navigationnode, $menublock); + } + return [$firstheading + $menublock]; + } + + /** + * Recursively goes and gets all children nodes. + * + * @param navigation_node $node The node to get the children of. + * @param array $menublock Used to put all child nodes in its own container. + * @return array The additional child nodes. + */ + protected static function get_more_child_nodes(navigation_node $node, array &$menublock): array { + $nodes = []; + foreach ($node->children as $child) { + if (!$child->has_children()) { + if (!$child->has_action()) { + continue; + } + $nodes[$child->action->out(false)] = static::format_node_text($child); + } else { + $newarray = []; + if ($child->has_action()) { + $newarray[static::format_node_text($child)][$child->action->out(false)] = static::format_node_text($child); + $newarray[static::format_node_text($child)] += static::get_more_child_nodes($child, $menublock); + } else { + $newarray[static::format_node_text($child)] = static::get_more_child_nodes($child, $menublock); + } + $menublock += $newarray; + } + } + return $nodes; + } + + /** + * Returns the navigation node text in a string. + * + * @param navigation_node $navigationnode The navigationnode to return the text string of. + * @return string The navigation node text string. + */ + protected static function format_node_text(navigation_node $navigationnode): string { + return (is_a($navigationnode->text, 'lang_string')) ? $navigationnode->text->out() : $navigationnode->text; + } } From 060e5dedeeeac7ba0e6b578338e6a541cd351a07 Mon Sep 17 00:00:00 2001 From: abgreeve Date: Tue, 7 Dec 2021 13:47:33 +0800 Subject: [PATCH 2/8] MDL-72930 navigation: Change over old code to use new static methods. This removes the old and less good methods for retrieving an array for use with a url_select and uses the newer better ones instead. --- lib/classes/navigation/views/secondary.php | 67 ++-------------------- 1 file changed, 4 insertions(+), 63 deletions(-) diff --git a/lib/classes/navigation/views/secondary.php b/lib/classes/navigation/views/secondary.php index b28a586c985..b51659720a6 100644 --- a/lib/classes/navigation/views/secondary.php +++ b/lib/classes/navigation/views/secondary.php @@ -215,65 +215,6 @@ 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; - $text = (is_a($child->text, 'lang_string')) ? $child->text->out() : $child->text; - $urldata[][$text] = $additionalnode + $this->get_additional_child_nodes($child); - } else { - $initialchildren += $additionalnode; - } - } - if ($additionalchildren) { - $text = (is_a($node->text, 'lang_string')) ? $node->text->out() : $node->text; - $urldata[][$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). * @@ -514,7 +455,7 @@ class secondary extends view { if (is_null($courseoverflownode)) { return null; } - $menuarray = $this->get_menu_array($courseoverflownode); + $menuarray = static::create_menu_element([$courseoverflownode]); if ($activenode->key != 'courseadmin') { $inmenu = false; foreach ($menuarray as $key => $value) { @@ -576,9 +517,9 @@ class secondary extends view { if (!isset($menunode) || !$menunode->has_children()) { return null; } - $selectdata = $this->get_menu_array($menunode); - $urlselect = new url_select($selectdata, $matchednode->action->out(), null); - $urlselect->set_label(get_string('browsesettingindex', 'course'), ['class' => 'sr_only']); + $selectdata = static::create_menu_element([$menunode], false); + $urlselect = new url_select($selectdata, $matchednode->action->out(false), null); + $urlselect->set_label(get_string('browsesettingindex', 'course'), ['class' => 'sr-only']); return $urlselect; } From f85662d744946a724607f6c845d281c6446a1ff4 Mon Sep 17 00:00:00 2001 From: abgreeve Date: Mon, 6 Dec 2021 10:56:15 +0800 Subject: [PATCH 3/8] MDL-72930 navigation: Changes to the secondary view unit tests. The display of the arrays has been changed and so updates were necessary for the unit tests as well due to changing output. --- lib/tests/navigation/views/secondary_test.php | 90 ++----------------- 1 file changed, 6 insertions(+), 84 deletions(-) diff --git a/lib/tests/navigation/views/secondary_test.php b/lib/tests/navigation/views/secondary_test.php index a2d5d34288a..7ce4d1ede78 100644 --- a/lib/tests/navigation/views/secondary_test.php +++ b/lib/tests/navigation/views/secondary_test.php @@ -514,9 +514,7 @@ class secondary_test extends \advanced_testcase { $secondary = new secondary($PAGE); $secondary->add_node($this->generate_node_tree_construct($structure, 'primarynode')); $selectednode = $secondary->find($selected, null); - $method = new ReflectionMethod('core\navigation\views\secondary', 'get_menu_array'); - $method->setAccessible(true); - $response = $method->invoke($secondary, $selectednode); + $response = \core\navigation\views\secondary::create_menu_element([$selectednode]); $this->assertSame($expected, $response); } @@ -544,7 +542,7 @@ class secondary_test extends \advanced_testcase { "Fetch information from a node with children" => [ 'child2', [ - 'https://www.example.com/moodle/test.php' => 'child2', + 'https://www.example.com/moodle/test.php' => 'child2.3', 'https://www.example.com/moodle/view/course.php?child=2' => 'child2.1', 'https://www.example.com/moodle/view/admin.php?child=2' => 'child2.2' ], @@ -557,22 +555,18 @@ class secondary_test extends \advanced_testcase { 'parentnode1', [ [ + 'parentnode1' => [ + 'https://www.example.com/moodle/my' => 'child1' + ], 'child2' => [ 'https://www.example.com/moodle/test.php' => 'child2', 'https://www.example.com/moodle/view/course.php?child=2' => 'child2.1', 'https://www.example.com/moodle/view/admin.php?child=2' => 'child2.2', - ] - ], - [ + ], 'child3' => [ 'https://www.example.com/moodle/view/course.php?child=3' => 'child3.1', 'https://www.example.com/moodle/view/admin.php?child=3' => 'child3.2' ] - ], - [ - 'parentnode1' => [ - 'https://www.example.com/moodle/my' => 'child1' - ] ] ], ], @@ -657,78 +651,6 @@ class secondary_test extends \advanced_testcase { ]; } - /** - * Test for get_additional_child_nodes - * - * @param string $selectedkey - * @param array $expected - * @dataProvider test_get_additional_child_nodes_provider - */ - public function test_get_additional_child_nodes(string $selectedkey, array $expected) { - global $PAGE; - $structure = [ - 'parentnode1' => [ - 'child1' => '/my', - 'child2' => [ - 'action' => '/test.php', - 'children' => [ - 'child2.1' => '/view/course.php?child=2', - 'child2.2' => '/view/admin.php?child=2', - ] - ], - 'child3' => [ - 'child3.1' => '/view/course.php?child=3', - 'child3.2' => '/view/admin.php?child=3', - ] - ], - 'parentnode2' => "/view/module.php" - ]; - - $secondary = new secondary($PAGE); - $nodes = $this->generate_node_tree_construct($structure, 'primarynode'); - $selectednode = $nodes->find($selectedkey, null); - $method = new ReflectionMethod('core\navigation\views\secondary', 'get_additional_child_nodes'); - $method->setAccessible(true); - $response = $method->invoke($secondary, $selectednode); - - $this->assertSame($expected, $response); - } - - /** - * Provider for test_get_additional_child_nodes - * - * @return array[] - */ - public function test_get_additional_child_nodes_provider(): array { - return [ - "Get nodes with deep nested children" => [ - "parentnode1", - [ - 'https://www.example.com/moodle/my' => 'child1', - 'https://www.example.com/moodle/test.php' => 'child2', - 'https://www.example.com/moodle/view/course.php?child=2' => 'child2.1', - 'https://www.example.com/moodle/view/admin.php?child=2' => 'child2.2', - 'https://www.example.com/moodle/view/course.php?child=3' => 'child3.1', - 'https://www.example.com/moodle/view/admin.php?child=3' => 'child3.2', - ] - ], - "Get children from parent without action " => [ - "child3", - [ - 'https://www.example.com/moodle/view/course.php?child=3' => 'child3.1', - 'https://www.example.com/moodle/view/admin.php?child=3' => 'child3.2' - ] - ], - "Get children from parent with action " => [ - "child2", - [ - 'https://www.example.com/moodle/view/course.php?child=2' => 'child2.1', - 'https://www.example.com/moodle/view/admin.php?child=2' => 'child2.2' - ] - ], - ]; - } - /** * Test the get_overflow_menu_data function * From ffdf3855473966bf9c42f095f68876637768b829 Mon Sep 17 00:00:00 2001 From: abgreeve Date: Mon, 29 Nov 2021 14:02:57 +0800 Subject: [PATCH 4/8] MDL-72930 navigation: Update to the participants page navigation. This adds the ability for the participants page to display injected navigation nodes in the users navigation node. --- .../output/participants_action_bar.php | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/classes/output/participants_action_bar.php b/lib/classes/output/participants_action_bar.php index 045c4eb93ff..a16482f2c3a 100644 --- a/lib/classes/output/participants_action_bar.php +++ b/lib/classes/output/participants_action_bar.php @@ -110,10 +110,31 @@ class participants_action_bar implements \renderable { } } - // TODO: Implement MDL-72930. + // Need to do some funky code here to find out if we have added third party navigation nodes. + $thirdpartynodearray = $this->get_thirdparty_node_array() ?: []; + $formattedcontent = array_merge($formattedcontent, $thirdpartynodearray); return $formattedcontent; } + /** + * Gets an array of third party navigation nodes in an array formatted for a url_select element. + * + * @return array|null The thirdparty node array. + */ + protected function get_thirdparty_node_array(): ?array { + $results = []; + + $flatnodes = array_merge(...(array_values($this->get_ordered_nodes()))); + + foreach ($this->node->children as $child) { + if (array_search($child->key, $flatnodes) === false) { + $results[] = $child; + } + } + + return \core\navigation\views\secondary::create_menu_element($results, true); + } + /** * Recursively tries to find a matching url * @param array $urlcontent The content for the url_select From f0ecd475d01b8c9b2734aefc54d1fc4776d7ce0f Mon Sep 17 00:00:00 2001 From: abgreeve Date: Thu, 2 Dec 2021 10:12:21 +0800 Subject: [PATCH 5/8] MDL-72930 navigation: Update the reports helper navigation. This allows injected navigation nodes into the reports to be shown in the report selector. --- lib/classes/report_helper.php | 45 +++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/classes/report_helper.php b/lib/classes/report_helper.php index 10cdaeafefc..e66d47c0f36 100644 --- a/lib/classes/report_helper.php +++ b/lib/classes/report_helper.php @@ -45,26 +45,41 @@ class report_helper { global $OUTPUT, $PAGE; if ($reportnode = $PAGE->settingsnav->find('coursereports', \navigation_node::TYPE_CONTAINER)) { - if ($children = $reportnode->children) { - // Menu to select report pages to navigate. - $activeurl = ''; - foreach ($children as $key => $node) { - $name = $node->text; - $url = $node->action()->out(false); - $menu[$url] = $name; - if ($name === $pluginname) { - $activeurl = $url; + $menuarray = \core\navigation\views\secondary::create_menu_element([$reportnode]); + if (empty($menuarray)) { + return; + } + + $coursereports = get_string('reports'); + $activeurl = ''; + if (isset($menuarray[0])) { + // Remove the reports entry. + $result = array_search($coursereports, $menuarray[0][$coursereports]); + unset($menuarray[0][$coursereports][$result]); + + // Find the active node. + foreach ($menuarray[0] as $key => $value) { + $check = array_search($pluginname, $value); + if ($check !== false) { + $activeurl = $check; } } + } else { + $result = array_search($coursereports, $menuarray); + unset($menuarray[$result]); + + $check = array_search($pluginname, $menuarray); + if ($check !== false) { + $activeurl = $check; + } + } - if (!empty($menu)) { - $select = new url_select($menu, $activeurl, null, 'choosecoursereport'); - $select->set_label(get_string('reporttype'), ['class' => 'accesshide']); - $select->class .= " mb-3"; - echo $OUTPUT->render($select); - } + $select = new url_select($menuarray, $activeurl, null, 'choosecoursereport'); + $select->set_label(get_string('reporttype'), ['class' => 'accesshide']); + $select->class .= " mb-3"; + echo $OUTPUT->render($select); } } From f1d22c9830257ba3ea82c0edb48c0c4b8f64525a Mon Sep 17 00:00:00 2001 From: abgreeve Date: Thu, 2 Dec 2021 15:27:36 +0800 Subject: [PATCH 6/8] MDL-72930 navigation: Update to badges navigation. This allows navigation nodes injected into the badges section to be displayed as either a button or a dropdown url_select. --- badges/classes/output/base_action_bar.php | 52 ++++++++++++++++++- .../output/manage_badge_action_bar.php | 2 + .../classes/output/recipients_action_bar.php | 2 + badges/classes/output/standard_action_bar.php | 6 ++- badges/templates/award_badge.mustache | 1 + badges/templates/badge_more_nav.mustache | 37 +++++++++++++ badges/templates/manage_badge.mustache | 1 + badges/templates/manage_badges.mustache | 1 + lang/en/badges.php | 1 + 9 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 badges/templates/badge_more_nav.mustache diff --git a/badges/classes/output/base_action_bar.php b/badges/classes/output/base_action_bar.php index 6b89d331338..2cc11b6a686 100644 --- a/badges/classes/output/base_action_bar.php +++ b/badges/classes/output/base_action_bar.php @@ -17,8 +17,10 @@ namespace core_badges\output; use renderable; -use templatable; +use renderer_base; use moodle_page; +use navigation_node; +use templatable; /** * Abstract class for the badges tertiary navigation. The class initialises the page and type class variables. @@ -50,4 +52,52 @@ abstract class base_action_bar implements renderable, templatable { * @return string */ abstract public function get_template(): string; + + /** + * Gets additional third party navigation nodes for display. + * + * @param renderer_base $output The output + * @return array All that sweet third party navigation action. + */ + public function get_third_party_nav_action(renderer_base $output): array { + $badgenode = $this->page->settingsnav->find('coursebadges', navigation_node::TYPE_CONTAINER); + if (!$badgenode) { + return []; + } + $leftovernodes = []; + foreach ($badgenode->children as $key => $value) { + if (array_search($value->key, $this->expected_items()) === false) { + $leftovernodes[] = $value; + } + } + $result = \core\navigation\views\secondary::create_menu_element($leftovernodes); + + if ($result == false) { + return []; + } else { + $data ['thirdpartybutton'] = true; + if (count($result) == 1) { + // Return a button. + $link = key($result); + $text = current($result); + $data['thirdpartynodes'] = ['link' => $link, 'text' => $text]; + } else { + // Return a url_select. + $selectobject = new \url_select($result, $this->page->url, get_string('othernavigation', 'badges')); + $data['thirdpartynodes'] = $selectobject->export_for_template($output); + $data['thirdpartybutton'] = false; + } + } + + return $data; + } + + /** + * Expected navigation node keys for badges. + * + * @return array default badge navigation node keys. + */ + protected function expected_items(): array { + return ['coursebadges', 'newbadge']; + } } diff --git a/badges/classes/output/manage_badge_action_bar.php b/badges/classes/output/manage_badge_action_bar.php index 5cc712b5237..7344b72d47d 100644 --- a/badges/classes/output/manage_badge_action_bar.php +++ b/badges/classes/output/manage_badge_action_bar.php @@ -71,6 +71,8 @@ class manage_badge_action_bar extends base_action_bar { foreach ($elements as $key => $element) { $elements[$key] = $element->export_for_template($output); } + $additional = $this->get_third_party_nav_action($output); + $elements += $additional ?: []; return $elements; } diff --git a/badges/classes/output/recipients_action_bar.php b/badges/classes/output/recipients_action_bar.php index 6720aff31cd..cae5ba7ce91 100644 --- a/badges/classes/output/recipients_action_bar.php +++ b/badges/classes/output/recipients_action_bar.php @@ -53,6 +53,8 @@ class recipients_action_bar extends manage_badge_action_bar { $button = new single_button($url, get_string('award', 'badges'), 'post', true); $elements['awardbutton'] = $button->export_for_template($output); } + $thirdpartynav = $this->get_third_party_nav_action($output); + $elements += $thirdpartynav ?: []; return $elements; } diff --git a/badges/classes/output/standard_action_bar.php b/badges/classes/output/standard_action_bar.php index 951d91e24cf..93026a087b2 100644 --- a/badges/classes/output/standard_action_bar.php +++ b/badges/classes/output/standard_action_bar.php @@ -96,6 +96,10 @@ class standard_action_bar extends base_action_bar { $buttons[$key] = $button->export_for_template($output); } - return ['buttons' => $buttons]; + $data = ['buttons' => $buttons]; + $additional = $this->get_third_party_nav_action($output); + $data += $additional ?: []; + + return $data; } } diff --git a/badges/templates/award_badge.mustache b/badges/templates/award_badge.mustache index 7e2ab5e50cb..bdae5a7c451 100644 --- a/badges/templates/award_badge.mustache +++ b/badges/templates/award_badge.mustache @@ -103,5 +103,6 @@ {{> core/single_button }} {{/awardbutton}} + {{> core_badges/badge_more_nav }} diff --git a/badges/templates/badge_more_nav.mustache b/badges/templates/badge_more_nav.mustache new file mode 100644 index 00000000000..622d5a57e31 --- /dev/null +++ b/badges/templates/badge_more_nav.mustache @@ -0,0 +1,37 @@ +{{! + 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_badges/badge_more_nav + + Example context (json): + { + "thirdpartybutton": true, + "thirdpartynodes": { + "link": "http://example.org/help", + "text": "Example text" + } + } +}} +{{#thirdpartybutton}} + {{#thirdpartynodes}} +
+ {{text}} +
+ {{/thirdpartynodes}} +{{/thirdpartybutton}} +{{^thirdpartybutton}} + {{#thirdpartynodes}} + {{> core/url_select}} + {{/thirdpartynodes}} +{{/thirdpartybutton}} diff --git a/badges/templates/manage_badge.mustache b/badges/templates/manage_badge.mustache index 04803875cc1..081c3eeb45b 100644 --- a/badges/templates/manage_badge.mustache +++ b/badges/templates/manage_badge.mustache @@ -83,5 +83,6 @@ {{> core/url_select }} {{/urlselect}} + {{> core_badges/badge_more_nav }} diff --git a/badges/templates/manage_badges.mustache b/badges/templates/manage_badges.mustache index 4fe4c3c56e3..7c4c4b8143d 100644 --- a/badges/templates/manage_badges.mustache +++ b/badges/templates/manage_badges.mustache @@ -59,5 +59,6 @@ {{> core/single_button }} {{/buttons}} + {{> core_badges/badge_more_nav }} diff --git a/lang/en/badges.php b/lang/en/badges.php index 499d1ed47b6..35313af6957 100644 --- a/lang/en/badges.php +++ b/lang/en/badges.php @@ -456,6 +456,7 @@ $string['oauth2issuer'] = 'OAuth 2 services'; $string['openbadgesv1'] = 'Open Badges v1.0'; $string['openbadgesv2'] = 'Open Badges v2.0'; $string['openbadgesv2p1'] = 'Open Badges v2.1'; +$string['othernavigation'] = 'Other navigation ...'; $string['potentialrecipients'] = 'Potential badge recipients'; $string['preferences'] = 'Badge preferences'; $string['privacy:metadata:backpack'] = 'A record of user\'s backpacks'; From f56ecc27f974ca3f1a608413323d5e2a5f3e61d8 Mon Sep 17 00:00:00 2001 From: abgreeve Date: Mon, 6 Dec 2021 16:04:02 +0800 Subject: [PATCH 7/8] MDL-72930 navigation: Update to quiz and questionbank navigation. Alterations here allowed the removal of renderables, and mustache templates for quiz report navigation. The questionbank was also updated to show injected navigation nodes from third party plugins. --- mod/quiz/classes/output/resultsaction.php | 73 ------------------- mod/quiz/renderer.php | 10 --- mod/quiz/report/default.php | 3 - .../templates/quiz_results_action.mustache | 58 --------------- question/classes/output/qbank_actionbar.php | 29 ++++++++ 5 files changed, 29 insertions(+), 144 deletions(-) delete mode 100644 mod/quiz/classes/output/resultsaction.php delete mode 100644 mod/quiz/templates/quiz_results_action.mustache diff --git a/mod/quiz/classes/output/resultsaction.php b/mod/quiz/classes/output/resultsaction.php deleted file mode 100644 index 02e330270cb..00000000000 --- a/mod/quiz/classes/output/resultsaction.php +++ /dev/null @@ -1,73 +0,0 @@ -. - -namespace mod_quiz\output; - -use templatable; -use renderable; -use renderer_base; -use moodle_url; -use url_select; - -/** - * Render results action - * - * @package mod_quiz - * @copyright 2021 Sujith Haridasan - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class resultsaction implements templatable, renderable { - /** @var int */ - private $id; - - /** - * resultsaction constructor. - * - * @param int $id The course module id. - */ - public function __construct(int $id) { - $this->id = $id; - } - - /** - * Provide data for the template - * - * @param renderer_base $output renderer_base object. - * @return array data for template. - */ - public function export_for_template(renderer_base $output): array { - global $PAGE; - - $gradeslink = new moodle_url('/mod/quiz/report.php', ['id' => $this->id, 'mode' => 'overview']); - $responseslink = new moodle_url('/mod/quiz/report.php', ['id' => $this->id, 'mode' => 'responses']); - $statisticslink = new moodle_url('/mod/quiz/report.php', ['id' => $this->id, 'mode' => 'statistics']); - $manualgrading = new moodle_url('/mod/quiz/report.php', ['id' => $this->id, 'mode' => 'grading']); - - $menu = [ - $gradeslink->out(false) => get_string('grades', 'grades'), - $responseslink->out(false) => get_string('responses', 'quiz_responses'), - $statisticslink->out(false) => get_string('statistics', 'quiz_statistics'), - $manualgrading->out(false) => get_string('grading', 'quiz_grading') - ]; - - $urlselect = new url_select($menu, $PAGE->url->out(false), null, 'quizresults'); - - $data = [ - 'resultaction' => $urlselect->export_for_template($output) - ]; - return $data; - } -} diff --git a/mod/quiz/renderer.php b/mod/quiz/renderer.php index dcf2126abb7..2e5a41c76db 100644 --- a/mod/quiz/renderer.php +++ b/mod/quiz/renderer.php @@ -1398,16 +1398,6 @@ class mod_quiz_renderer extends plugin_renderer_base { return $this->render_from_template('mod_quiz/quiz_edit_action', $overwriteedit->export_for_template($this)); } - /** - * Get rendered HTML for the action area of the results page. - * - * @param \mod_quiz\output\resultsaction $resultsaction resultsaction object. - * @return string rendered HTML string from the template. - */ - public function get_results_action(\mod_quiz\output\resultsaction $resultsaction): string { - return $this->render_from_template('mod_quiz/quiz_results_action', $resultsaction->export_for_template($this)); - } - /** * Get rendered HTML for the action area of the overrides page. * diff --git a/mod/quiz/report/default.php b/mod/quiz/report/default.php index 6b45bfdd08e..03725fbd31f 100644 --- a/mod/quiz/report/default.php +++ b/mod/quiz/report/default.php @@ -71,9 +71,6 @@ abstract class quiz_default_report { if (!$PAGE->has_secondary_navigation()) { echo $OUTPUT->heading(format_string($quiz->name, true, array('context' => $context))); } - $resultsaction = new \mod_quiz\output\resultsaction($cm->id); - $renderer = $PAGE->get_renderer('mod_quiz'); - echo $renderer->get_results_action($resultsaction); if (!empty($CFG->enableplagiarism)) { require_once($CFG->libdir . '/plagiarismlib.php'); echo plagiarism_update_status($course, $cm); diff --git a/mod/quiz/templates/quiz_results_action.mustache b/mod/quiz/templates/quiz_results_action.mustache deleted file mode 100644 index 8caa96ff6b4..00000000000 --- a/mod/quiz/templates/quiz_results_action.mustache +++ /dev/null @@ -1,58 +0,0 @@ -{{! - 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 mod_quiz/quiz_results_action - Actions bar at the top of the report page UI. - Classes required for JS: - * none - Data attributes required for JS: - * none - Context variables required for this template: - * see mod/quiz/classes/output/resultsaction.php - Example context (json): - { - "resultaction": { - "id": "url_select_test", - "options": [{ - "name": "Grades", - "value": "/mod/quiz/report.php?id=4&mode=overview" - }, - { - "name": "Responses", - "value": "/mod/quiz/report.php?id=4&mode=responses" - }, - { - "name": "Statistics", - "value": "/mod/quiz/report.php?id=4&mode=statistics" - }, - { - "name": "Manual grading", - "value": "/mod/quiz/report.php?id=4&mode=grading" - } - ] - } - } -}} -
-
-
- {{#resultaction}} - {{>core/url_select}} - {{/resultaction}} -
-
-
diff --git a/question/classes/output/qbank_actionbar.php b/question/classes/output/qbank_actionbar.php index 7933b562fdb..58646b4c9ac 100644 --- a/question/classes/output/qbank_actionbar.php +++ b/question/classes/output/qbank_actionbar.php @@ -65,10 +65,39 @@ class qbank_actionbar implements templatable, renderable { } $menu[$importlink->out(false)] = get_string('import', 'question'); $menu[$exportlink->out(false)] = get_string('export', 'question'); + $additional = $this->get_additional_menu_elements(); + $menu += $additional ?: []; $urlselect = new url_select($menu, $this->currenturl, null, 'questionbankaction'); $urlselect->set_label('questionbankactionselect', ['class' => 'accesshide']); return ['questionbankselect' => $urlselect->export_for_template($output)]; } + + /** + * Gets the additional third party navigation nodes. + * + * @return array|null The additional menu elements. + */ + protected function get_additional_menu_elements(): ?array { + global $PAGE; + $qbnode = $PAGE->settingsnav->find('questionbank', \navigation_node::TYPE_CONTAINER); + $othernodes = []; + foreach ($qbnode->children as $key => $value) { + if (array_search($value->key, $this->expected_nodes()) === false) { + $othernodes[] = $value; + } + } + $result = \core\navigation\views\secondary::create_menu_element($othernodes, true); + return $result; + } + + /** + * Returns a list of expected child navigation nodes for 'questionbank'. + * + * @return array The expected nodes + */ + protected function expected_nodes(): array { + return ['questions', 'categories', 'import', 'export']; + } } From 0a04ebde11f85632f9b54fb2f9de0884a4845aa5 Mon Sep 17 00:00:00 2001 From: Adrian Greeve Date: Wed, 22 Dec 2021 09:23:24 +0800 Subject: [PATCH 8/8] MDL-72930 navigation: Remove duplicated navigation boxes. Some pages have settings that really aren't required anymore. These have been removed and a function has been aded so that other developers can turn off the navigation overflow if they want. --- grade/grading/lib.php | 22 +------------------- lib/classes/navigation/views/secondary.php | 5 +++++ lib/pagelib.php | 24 ++++++++++++++++++++++ mod/data/lib.php | 8 +------- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/grade/grading/lib.php b/grade/grading/lib.php index 7af2811ea0f..3615e98e8ae 100644 --- a/grade/grading/lib.php +++ b/grade/grading/lib.php @@ -453,33 +453,13 @@ class grading_manager { // no money, no funny return; - } else if (count($areas) == 1) { + } else { // make just a single node for the management screen $areatitle = reset($areas); $areaname = key($areas); $this->set_area($areaname); - $method = $this->get_active_method(); $managementnode = $modulenode->add(get_string('gradingmanagement', 'core_grading'), $this->get_management_url(), settings_navigation::TYPE_CUSTOM, null, 'advgrading'); - if ($method) { - $controller = $this->get_controller($method); - $controller->extend_settings_navigation($settingsnav, $managementnode); - } - - } else { - // make management screen node for each area - $managementnode = $modulenode->add(get_string('gradingmanagement', 'core_grading'), - null, settings_navigation::TYPE_CUSTOM, null, 'advgrading'); - foreach ($areas as $areaname => $areatitle) { - $this->set_area($areaname); - $method = $this->get_active_method(); - $node = $managementnode->add($areatitle, - $this->get_management_url(), settings_navigation::TYPE_CUSTOM); - if ($method) { - $controller = $this->get_controller($method); - $controller->extend_settings_navigation($settingsnav, $node); - } - } } } diff --git a/lib/classes/navigation/views/secondary.php b/lib/classes/navigation/views/secondary.php index b51659720a6..8da27ce0c78 100644 --- a/lib/classes/navigation/views/secondary.php +++ b/lib/classes/navigation/views/secondary.php @@ -433,6 +433,11 @@ class secondary extends view { * @return url_select|null The overflow menu data. */ public function get_overflow_menu_data(): ?url_select { + + if (!$this->page->get_navigation_overflow_state()) { + return null; + } + $activenode = $this->find_active_node(); $incourseadmin = false; diff --git a/lib/pagelib.php b/lib/pagelib.php index eb7a00117f0..98801ad2237 100644 --- a/lib/pagelib.php +++ b/lib/pagelib.php @@ -408,6 +408,11 @@ class moodle_page { */ protected $_activityheader; + /** + * @var bool The value of displaying the navigation overflow. + */ + protected $_navigationoverflow = true; + /** * Force the settings menu to be displayed on this page. This will only force the * settings menu on an activity / resource page that is being displayed on a theme that @@ -2258,4 +2263,23 @@ class moodle_page { public function get_primary_activate_tab(): ?string { return $this->_activenodeprimary; } + + /** + * Sets the navigation overflow state. This allows developers to turn off the overflow menu if they perhaps are using + * some other navigation to show settings. + * + * @param bool $state The state of whether to show the navigation overflow. + */ + public function set_navigation_overflow_state(bool $state): void { + $this->_navigationoverflow = $state; + } + + /** + * Gets the navigation overflow state. + * + * @return bool The navigation overflow state. + */ + public function get_navigation_overflow_state(): bool { + return $this->_navigationoverflow; + } } diff --git a/mod/data/lib.php b/mod/data/lib.php index 34ba3d45d13..5364e0d136d 100644 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -3633,14 +3633,8 @@ function data_extend_settings_navigation(settings_navigation $settings, navigati $datanode->add(get_string('fields', 'data'), new moodle_url('/mod/data/field.php', array('d' => $data->id))); - $templates = $datanode->add(get_string('templates', 'data'), + $datanode->add(get_string('templates', 'data'), new moodle_url('/mod/data/templates.php', array('d' => $data->id))); - - $templatelist = array ('listtemplate', 'singletemplate', 'asearchtemplate', 'addtemplate', 'rsstemplate', 'csstemplate', 'jstemplate'); - foreach ($templatelist as $template) { - $templates->add(get_string($template, 'data'), new moodle_url('/mod/data/templates.php', array('d'=>$data->id,'mode'=>$template))); - } - $datanode->add(get_string('presets', 'data'), new moodle_url('/mod/data/preset.php', array('d' => $data->id))); }