@@ -107,7 +107,8 @@ public function export_for_template(?renderer_base $output = null): array {
107
107
$ locationbottom = smartmenu::get_menus_forlocation (smartmenu::LOCATION_BOTTOM , $ smartmenus );
108
108
109
109
// 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 );
111
112
$ moremenu = new \core \navigation \output \more_menu ((object ) $ menudata , 'navbar-nav ' , false );
112
113
113
114
// Menubar.
@@ -119,9 +120,10 @@ public function export_for_template(?renderer_base $output = null): array {
119
120
120
121
// Bottom bar.
121
122
// 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 );
122
124
$ 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 ) ;
125
127
126
128
if (!empty ($ mobileprimarynav )) {
127
129
$ bottombar = new \core \navigation \output \more_menu ((object ) $ mobileprimarynav , 'navbar-nav-bottom-bar ' , false );
@@ -306,4 +308,86 @@ public function build_usermenus(&$usermenu, $menus) {
306
308
array_push ($ usermenu ['items ' ], $ logout );
307
309
}
308
310
}
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
+ }
309
393
}
0 commit comments