Skip to content

Commit 4200f3e

Browse files
Adopt changes in /lib/classes/navigation/output/primary.php for Moodle 4.3, resolves moodle-an-hochschulen#436 (moodle-an-hochschulen#648)
1 parent 458086f commit 4200f3e

File tree

2 files changed

+88
-3
lines changed

2 files changed

+88
-3
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Changes
66

77
### Unreleased
88

9+
* 2024-11-18 - Upstream change: Adopt changes from MDL-77732 ('Custom menu items do not receive active behaviour'), resolves #436 #620 #384 #715.
910
* 2024-11-13 - Upstream change: Adopt changes from MDL-78999 ('Site logo does not appear in mobile view'), resolves #753.
1011
* 2024-11-11 - Release: Add ssystems GmbH to the list of maintainers in README.md.
1112

classes/output/navigation/primary.php

+87-3
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ public function export_for_template(?renderer_base $output = null): array {
107107
$locationbottom = smartmenu::get_menus_forlocation(smartmenu::LOCATION_BOTTOM, $smartmenus);
108108

109109
// Merge the smart menu nodes which contain the main menu location with the primary and custom menu nodes.
110-
$menudata = array_merge($this->get_primary_nav(), $this->get_custom_menu($output), $mainmenu);
110+
$mainsmartmenumergedcustom = array_merge($this->get_custom_menu($output), $mainmenu);
111+
$menudata = (object) $this->merge_primary_and_custom($this->get_primary_nav(), $mainsmartmenumergedcustom);
111112
$moremenu = new \core\navigation\output\more_menu((object) $menudata, 'navbar-nav', false);
112113

113114
// Menubar.
@@ -119,9 +120,10 @@ public function export_for_template(?renderer_base $output = null): array {
119120

120121
// Bottom bar.
121122
// Include the menu navigation menus to the mobile menu when the bottom bar doesn't have any menus.
123+
$mergecustombottommenus = array_merge($this->get_custom_menu($output), $locationbottom);
122124
$mobileprimarynav = (!empty($locationbottom))
123-
? array_merge($this->get_primary_nav(), $this->get_custom_menu($output), $locationbottom)
124-
: $mobileprimarynav = $menudata;
125+
? $this->merge_primary_and_custom($this->get_primary_nav(), $mergecustombottommenus, true)
126+
: $this->merge_primary_and_custom($this->get_primary_nav(), $mainsmartmenumergedcustom, true);
125127

126128
if (!empty($mobileprimarynav)) {
127129
$bottombar = new \core\navigation\output\more_menu((object) $mobileprimarynav, 'navbar-nav-bottom-bar', false);
@@ -306,4 +308,86 @@ public function build_usermenus(&$usermenu, $menus) {
306308
array_push($usermenu['items'], $logout);
307309
}
308310
}
311+
312+
/**
313+
* Recursive checks if any of the children is active. If that's the case this node (the parent) is active as
314+
* well. If the node has no children, check if the node itself is active. Use pass by reference for the node
315+
* object because we actively change/set the "isactive" flag inside the method and this needs to be kept at the
316+
* callers side.
317+
* Set $expandedmenu to true, if the mobile menu is done, in this case the active flag gets the node that is
318+
* actually active, while the parent hierarchy of the active node gets the flag isopen.
319+
*
320+
* Modifications compared to the original function:
321+
* * Updated the children node type to object
322+
*
323+
* @param object $node
324+
* @param bool $expandedmenu
325+
* @return bool
326+
*/
327+
protected function flag_active_nodes(object $node, bool $expandedmenu = false): bool {
328+
global $FULLME;
329+
$active = false;
330+
foreach (array_keys($node->children ?? []) as $c) {
331+
332+
// Update the type of child nodes (smart menu).
333+
// To prevent issues with already configured menus,
334+
// The type of children is not updated during the smart menu build process.
335+
$child = (object) $node->children[$c];
336+
337+
if ($this->flag_active_nodes($child, $expandedmenu)) {
338+
$active = true;
339+
}
340+
}
341+
// One of the children is active, so this node (the parent) is active as well.
342+
if ($active) {
343+
if ($expandedmenu) {
344+
$node->isopen = true;
345+
} else {
346+
$node->isactive = true;
347+
}
348+
return true;
349+
}
350+
351+
// By default, the menu item node to check is not active.
352+
$node->isactive = false;
353+
354+
// Check if the node url matches the called url. The node url may omit the trailing index.php, therefore check
355+
// this as well.
356+
if (empty($node->url)) {
357+
// Current menu node has no url set, so it can't be active.
358+
return false;
359+
}
360+
$nodeurl = parse_url($node->url);
361+
$current = parse_url($FULLME ?? '');
362+
363+
$pathmatches = false;
364+
365+
// Exact match of the path of node and current url.
366+
$nodepath = $nodeurl['path'] ?? '/';
367+
$currentpath = $current['path'] ?? '/';
368+
if ($nodepath === $currentpath) {
369+
$pathmatches = true;
370+
}
371+
// The current url may be trailed by a index.php, otherwise it's the same as the node path.
372+
if (!$pathmatches && $nodepath . 'index.php' === $currentpath) {
373+
$pathmatches = true;
374+
}
375+
// No path did match, so the node can't be active.
376+
if (!$pathmatches) {
377+
return false;
378+
}
379+
// We are here because the path matches, so now look at the query string.
380+
$nodequery = $nodeurl['query'] ?? '';
381+
$currentquery = $current['query'] ?? '';
382+
// If the node has no query string defined, then the patch match is sufficient.
383+
if (empty($nodeurl['query'])) {
384+
$node->isactive = true;
385+
return true;
386+
}
387+
// If the node contains a query string then also the current url must match this query.
388+
if ($nodequery === $currentquery) {
389+
$node->isactive = true;
390+
}
391+
return $node->isactive;
392+
}
309393
}

0 commit comments

Comments
 (0)