From 676c7ef886aece2d134dcd200d24149d570831b8 Mon Sep 17 00:00:00 2001
From: quietone <quietone@2572884.no-reply.drupal.org>
Date: Sun, 19 Jan 2025 22:33:11 +1300
Subject: [PATCH 1/2] sort local tasks

---
 .../lib/Drupal/Core/Menu/LocalTaskManager.php |  13 ++
 .../Tests/Core/Menu/LocalTaskManagerTest.php  | 113 ++++++++++++++++++
 2 files changed, 126 insertions(+)

diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
index 2655f2dd612c..0763bdf1a683 100644
--- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
@@ -359,6 +359,19 @@ public function getLocalTasks($route_name, $level = 0) {
         foreach ($local_tasks as $tab_level => $items) {
           $data[$tab_level] = empty($data[$tab_level]) ? $items : array_merge($data[$tab_level], $items);
         }
+
+        // Sort by weight and alphabetically if weights are the same.
+        foreach ($data as $key => $values) {
+          $weights = array_map(function ($input) {
+            return $input['#weight'];
+          }, $values);
+          array_multisort(
+            $weights, SORT_ASC, SORT_NUMERIC,
+            array_keys($values), SORT_ASC, SORT_NATURAL,
+            $data[$key],
+          );
+        }
+
         $this->taskData[$route_name]['tabs'] = $data;
         // Allow modules to alter local tasks.
         $this->moduleHandler->alter('menu_local_tasks', $this->taskData[$route_name], $route_name, $cacheability);
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
index 4b9504eb3b35..6bd21642543f 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
@@ -479,4 +479,117 @@ protected function setupNullCacheabilityMetadataValidation(): void {
     \Drupal::setContainer($container);
   }
 
+  /**
+   * Tests the getLocalTasksForRoute method.
+   *
+   * @dataProvider providerTestGetLocalTasks
+   */
+  public function testGetLocalTasks($new_weights, $expected): void {
+    $definitions = $this->getLocalTaskFixtures();
+
+    // Add another child, that will be first in an alphabetical sort.
+    $definitions['menu_local_task_test_tasks_view_a_child'] = [
+      'route_name' => 'menu_local_task_test_tasks_a_child_page',
+      'title' => 'Settings child a_child',
+      'parent_id' => 'menu_local_task_test_tasks_view.tab',
+      'id' => 'menu_local_task_test_tasks_view_a_child',
+      'route_parameters' => [],
+      'base_route' => '',
+      'weight' => 0,
+      'options' => [],
+      'class' => 'Drupal\Core\Menu\LocalTaskDefault',
+    ];
+
+    // Update the task weights.
+    foreach ($new_weights as $local_task => $weight) {
+      $definitions[$local_task] = array_merge($definitions[$local_task], $weight);
+    }
+
+    $this->pluginDiscovery->expects($this->once())
+      ->method('getDefinitions')
+      ->willReturn($definitions);
+
+    $this->setupFactoryAndLocalTaskPlugins($definitions, 'menu_local_task_test_tasks_view');
+    $this->setupLocalTaskManager();
+
+    $this->argumentResolver->expects($this->any())
+      ->method('getArguments')
+      ->willReturn([]);
+
+    $this->routeMatch->expects($this->any())
+      ->method('getRouteName')
+      ->willReturn('menu_local_task_test_tasks_view');
+    $this->routeMatch->expects($this->any())
+      ->method('getRawParameters')
+      ->willReturn(new InputBag());
+
+    $cacheability = new CacheableMetadata();
+    $this->manager->getTasksBuild('menu_local_task_test_tasks_view', $cacheability);
+
+    // Get the local tasks for each level and assert that the order is as
+    // expected.
+    foreach ([0, 1] as $level) {
+      $local_tasks = $this->manager->getLocalTasks('menu_local_task_test_tasks_view', $level);
+      $data = $local_tasks['tabs'];
+      $this->assertEquals($expected[$level], array_keys($data));
+    }
+  }
+
+  /**
+   * Data provider for testGetLocalTasks.
+   */
+  public static function providerTestGetLocalTasks(): array {
+    return [
+      // Weights as setup in getLocalTaskFixtures.
+      'weights_from_fixture' => [
+        'new_weights' => [],
+        'expected' => [
+          // Level 0.
+          [
+            'menu_local_task_test_tasks_settings',
+            'menu_local_task_test_tasks_view.tab',
+            'menu_local_task_test_tasks_edit',
+          ],
+          // Level 1. All weights are 0, so the sort is alphabetical.
+          [
+            'menu_local_task_test_tasks_view_a_child',
+            'menu_local_task_test_tasks_view_child1',
+            'menu_local_task_test_tasks_view_child2',
+          ],
+        ],
+      ],
+      // Change the weights in both levels.
+      'both_levels' => [
+        'new_weights' => [
+          'menu_local_task_test_tasks_view_a_child' => [
+            'weight' => 99,
+          ],
+          'menu_local_task_test_tasks_view_child1' => [
+            'weight' => 100,
+          ],
+          'menu_local_task_test_tasks_view_child2' => [
+            'weight' => -1,
+          ],
+          'menu_local_task_test_tasks_settings' => [
+            'weight' => 100,
+          ],
+        ],
+        'expected' => [
+          // Level 0.
+          [
+            'menu_local_task_test_tasks_view.tab',
+            'menu_local_task_test_tasks_edit',
+            'menu_local_task_test_tasks_settings',
+          ],
+          // Level 1.
+          [
+            'menu_local_task_test_tasks_view_child2',
+            'menu_local_task_test_tasks_view_a_child',
+            'menu_local_task_test_tasks_view_child1',
+          ],
+        ],
+      ],
+    ];
+  }
+
 }
-- 
GitLab


From 37620037feca301917300aa1f64a177c5c685384 Mon Sep 17 00:00:00 2001
From: Stephen Mustgrave <smustgrave@gmail.com>
Date: Mon, 17 Mar 2025 13:54:17 -0400
Subject: [PATCH 2/2] Apply suggestion

---
 core/lib/Drupal/Core/Menu/LocalTaskManager.php | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
index a69fdfd47892..b70e77fbce5d 100644
--- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
@@ -363,9 +363,7 @@ public function getLocalTasks($route_name, $level = 0) {
 
         // Sort by weight and alphabetically if weights are the same.
         foreach ($data as $key => $values) {
-          $weights = array_map(function ($input) {
-            return $input['#weight'];
-          }, $values);
+          $weights = array_column($values, '#weight');
           array_multisort(
             $weights, SORT_ASC, SORT_NUMERIC,
             array_keys($values), SORT_ASC, SORT_NATURAL,
-- 
GitLab