From 6ee5f78c6634ec40e1f9bb2d1f97b852249092a3 Mon Sep 17 00:00:00 2001 From: Lee Rowlands Date: Fri, 22 Dec 2023 12:52:18 +1000 Subject: [PATCH] Issue #2852898 by smustgrave, _utsavsharma, mherchel, larowlan, bnjmnm, borisson_, kershme, DuaelFr: 508 Compliance Issue -Edit links on content page are not unique --- .../Drupal/Core/Entity/EntityListBuilder.php | 26 ++++++++- .../src/Functional/ConfigEntityListTest.php | 53 +++++++++++++++++-- .../tests/src/Functional/UserAdminTest.php | 5 +- .../src/Kernel/Plugin/RowRenderCacheTest.php | 4 +- 4 files changed, 79 insertions(+), 9 deletions(-) diff --git a/core/lib/Drupal/Core/Entity/EntityListBuilder.php b/core/lib/Drupal/Core/Entity/EntityListBuilder.php index 7d6aa5926fb..a9c649888df 100644 --- a/core/lib/Drupal/Core/Entity/EntityListBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityListBuilder.php @@ -142,13 +142,35 @@ class EntityListBuilder extends EntityHandlerBase implements EntityListBuilderIn protected function getDefaultOperations(EntityInterface $entity) { $operations = []; if ($entity->access('update') && $entity->hasLinkTemplate('edit-form')) { + $edit_url = $this->ensureDestination($entity->toUrl('edit-form')); + if (!empty($entity->label())) { + $label = $this->t('Edit @entity_label', ['@entity_label' => $entity->label()]); + } + else { + $label = $this->t('Edit @entity_bundle @entity_id', ['@entity_bundle' => $entity->bundle(), '@entity_id' => $entity->id()]); + } + $attributes = $edit_url->getOption('attributes') ?: []; + $attributes += ['aria-label' => $label]; + $edit_url->setOption('attributes', $attributes); + $operations['edit'] = [ 'title' => $this->t('Edit'), 'weight' => 10, - 'url' => $this->ensureDestination($entity->toUrl('edit-form')), + 'url' => $edit_url, ]; } if ($entity->access('delete') && $entity->hasLinkTemplate('delete-form')) { + $delete_url = $this->ensureDestination($entity->toUrl('delete-form')); + if (!empty($entity->label())) { + $label = $this->t('Delete @entity_label', ['@entity_label' => $entity->label()]); + } + else { + $label = $this->t('Delete @entity_bundle @entity_id', ['@entity_bundle' => $entity->bundle(), '@entity_id' => $entity->id()]); + } + $attributes = $delete_url->getOption('attributes') ?: []; + $attributes += ['aria-label' => $label]; + $delete_url->setOption('attributes', $attributes); + $operations['delete'] = [ 'title' => $this->t('Delete'), 'weight' => 100, @@ -159,7 +181,7 @@ class EntityListBuilder extends EntityHandlerBase implements EntityListBuilderIn 'width' => 880, ]), ], - 'url' => $this->ensureDestination($entity->toUrl('delete-form')), + 'url' => $delete_url, ]; } diff --git a/core/modules/config/tests/src/Functional/ConfigEntityListTest.php b/core/modules/config/tests/src/Functional/ConfigEntityListTest.php index 6bad38c77b1..05e4286cce2 100644 --- a/core/modules/config/tests/src/Functional/ConfigEntityListTest.php +++ b/core/modules/config/tests/src/Functional/ConfigEntityListTest.php @@ -58,11 +58,17 @@ class ConfigEntityListTest extends BrowserTestBase { $this->assertInstanceOf(ConfigTest::class, $entity); // Test getOperations() method. + $edit_url = $entity->toUrl()->setOption('query', $this->getRedirectDestination()->getAsArray()); + $edit_url->setOption('attributes', ['aria-label' => 'Edit ' . $entity->label()]); + + $delete_url = $entity->toUrl('delete-form')->setOption('query', $this->getRedirectDestination()->getAsArray()); + $delete_url->setOption('attributes', ['aria-label' => 'Delete ' . $entity->label()]); + $expected_operations = [ 'edit' => [ 'title' => 'Edit', 'weight' => 10, - 'url' => $entity->toUrl()->setOption('query', $this->getRedirectDestination()->getAsArray()), + 'url' => $edit_url, ], 'disable' => [ 'title' => 'Disable', @@ -79,7 +85,7 @@ class ConfigEntityListTest extends BrowserTestBase { 'width' => 880, ]), ], - 'url' => $entity->toUrl('delete-form')->setOption('query', $this->getRedirectDestination()->getAsArray()), + 'url' => $delete_url, ], ]; @@ -140,11 +146,16 @@ class ConfigEntityListTest extends BrowserTestBase { $entity = $list['default']; // Test getOperations() method. + $edit_url = $entity->toUrl()->setOption('query', $this->getRedirectDestination()->getAsArray()); + $edit_url->setOption('attributes', ['aria-label' => 'Edit ' . $entity->label()]); + + $delete_url = $entity->toUrl('delete-form')->setOption('query', $this->getRedirectDestination()->getAsArray()); + $delete_url->setOption('attributes', ['aria-label' => 'Delete ' . $entity->label()]); $expected_operations = [ 'edit' => [ 'title' => 'Edit', 'weight' => 10, - 'url' => $entity->toUrl()->setOption('query', $this->getRedirectDestination()->getAsArray()), + 'url' => $edit_url, ], 'delete' => [ 'title' => 'Delete', @@ -156,7 +167,41 @@ class ConfigEntityListTest extends BrowserTestBase { 'width' => 880, ]), ], - 'url' => $entity->toUrl('delete-form')->setOption('query', $this->getRedirectDestination()->getAsArray()), + 'url' => $delete_url, + ], + ]; + + $actual_operations = $controller->getOperations($entity); + // Sort the operations to normalize link order. + uasort($actual_operations, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']); + $this->assertEquals($expected_operations, $actual_operations, 'The operations are identical.'); + + // Test getOperations when label doesn't exist. + $entity->set('label', ''); + $entity->save(); + + $edit_url = $entity->toUrl()->setOption('query', $this->getRedirectDestination()->getAsArray()); + $edit_url->setOption('attributes', ['aria-label' => 'Edit ' . $entity->bundle() . ' ' . $entity->id()]); + + $delete_url = $entity->toUrl('delete-form')->setOption('query', $this->getRedirectDestination()->getAsArray()); + $delete_url->setOption('attributes', ['aria-label' => 'Delete ' . $entity->bundle() . ' ' . $entity->id()]); + $expected_operations = [ + 'edit' => [ + 'title' => 'Edit', + 'weight' => 10, + 'url' => $edit_url, + ], + 'delete' => [ + 'title' => 'Delete', + 'weight' => 100, + 'attributes' => [ + 'class' => ['use-ajax'], + 'data-dialog-type' => 'modal', + 'data-dialog-options' => Json::encode([ + 'width' => 880, + ]), + ], + 'url' => $delete_url, ], ]; diff --git a/core/modules/user/tests/src/Functional/UserAdminTest.php b/core/modules/user/tests/src/Functional/UserAdminTest.php index 8c79b8fa620..57cabd3d2fe 100644 --- a/core/modules/user/tests/src/Functional/UserAdminTest.php +++ b/core/modules/user/tests/src/Functional/UserAdminTest.php @@ -75,7 +75,10 @@ class UserAdminTest extends BrowserTestBase { $this->assertSession()->pageTextContains($admin_user->getAccountName()); // Test for existence of edit link in table. - $link = $user_a->toLink('Edit', 'edit-form', ['query' => ['destination' => $user_a->toUrl('collection')->toString()]])->toString(); + $link = $user_a->toLink('Edit', 'edit-form', [ + 'query' => ['destination' => $user_a->toUrl('collection')->toString()], + 'attributes' => ['aria-label' => 'Edit ' . $user_a->label()], + ])->toString(); $this->assertSession()->responseContains($link); // Test exposed filter elements. diff --git a/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php b/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php index 672d3823088..12db6c7e82c 100644 --- a/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php +++ b/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php @@ -184,8 +184,8 @@ class RowRenderCacheTest extends ViewsKernelTestBase { $output = $view->style_plugin->getField($index, 'delete_node'); $this->assertSame($expected, (string) $output); $expected = $access ? '
' : ''; $output = $view->style_plugin->getField($index, 'operations'); $this->assertSame($expected, (string) $output);