From 876289e0b34b65dc68d8ea329010ed21c9c5cdaf Mon Sep 17 00:00:00 2001 From: Luke Leber <24370-lleber@users.noreply.drupalcode.org> Date: Thu, 27 Mar 2025 16:35:40 +0000 Subject: [PATCH 1/7] Initial commit -- likely very wrong in certain ways! --- modules/base/src/Plugin/Action/ListSort.php | 26 +++++ .../tests/src/Kernel/ListOperationTest.php | 62 +++++++++++ src/Plugin/Action/ListSortBase.php | 103 ++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 modules/base/src/Plugin/Action/ListSort.php create mode 100644 src/Plugin/Action/ListSortBase.php diff --git a/modules/base/src/Plugin/Action/ListSort.php b/modules/base/src/Plugin/Action/ListSort.php new file mode 100644 index 000000000..159d535f3 --- /dev/null +++ b/modules/base/src/Plugin/Action/ListSort.php @@ -0,0 +1,26 @@ +<?php + +namespace Drupal\eca_base\Plugin\Action; + +use Drupal\eca\Plugin\Action\ListSortBase; + +/** + * Sorts a list. + * + * @Action( + * id = "eca_list_sort", + * label = @Translation("List: sort items"), + * description = @Translation("Sorts a list."), + * eca_version_introduced = "2.2.0" + * ) + */ +class ListSort extends ListSortBase { + + /** + * {@inheritdoc} + */ + public function execute(): void { + $this->sortItems(); + } + +} diff --git a/modules/base/tests/src/Kernel/ListOperationTest.php b/modules/base/tests/src/Kernel/ListOperationTest.php index 07ddc06b9..09329505a 100644 --- a/modules/base/tests/src/Kernel/ListOperationTest.php +++ b/modules/base/tests/src/Kernel/ListOperationTest.php @@ -379,4 +379,66 @@ class ListOperationTest extends KernelTestBase { $this->assertNull(User::load(2)); } + /** + * Data provider for the list sort test case. + * + * @return array[] + * Test case data for in-place and copy based sorting. + */ + public function listSortProvider(): array { + return [ + 'without dest token' => [''], + 'with dest token' => ['dest_token'] + ]; + } + + /** + * Tests the "eca_list_sort" action plugin. + * + * @dataProvider listSortProvider + */ + public function testListSortDestToken(string $dest_token): void { + + $chaos = [ + 'img1.png' => 'img1.png', + 'img10.png' => 'img10.png', + 'img12.png' => 'img12.png', + 'img2.png' => 'img2.png', + 'Img1.png' => 'Img1.png', + 'Img10.png' => 'Img10.png', + 'Img12.png' => 'Img12.png', + 'Img2.png' => 'Img2.png', + ]; + + /** @var \Drupal\Core\Action\ActionManager $action_manager */ + $action_manager = \Drupal::service('plugin.manager.action'); + /** @var \Drupal\eca\Token\TokenInterface $token_services */ + $token_services = \Drupal::service('eca.token_services'); + + $action = $action_manager->createInstance('eca_list_sort', []); + + foreach (array_keys($action->getSupportedSortMethods()) as $method) { + + $action->setConfiguration([ + 'list_token' => 'list', + 'sort_method' => $method, + 'token_name' => $dest_token, + ]); + + // Copy the unsorted list into a new variable, then sort it. + $expected = $chaos; + $method($expected); + + // Reset the incoming list. + $list = DataTransferObject::create($chaos); + $token_services->addTokenData('list', $list); + + // Sort the list into a new token. + $action->execute(); + + $list = $token_services->getTokenData($dest_token ?: 'list'); + $this->assertSame($expected, $list->getValue()['values']); + } + } + } diff --git a/src/Plugin/Action/ListSortBase.php b/src/Plugin/Action/ListSortBase.php new file mode 100644 index 000000000..642a4ca86 --- /dev/null +++ b/src/Plugin/Action/ListSortBase.php @@ -0,0 +1,103 @@ +<?php + +namespace Drupal\eca\Plugin\Action; + +use Drupal\Core\Form\FormStateInterface; +use Drupal\eca\Plugin\DataType\DataTransferObject; +use Drupal\eca\Plugin\ECA\PluginFormTrait; + +/** + * Base class for actions sorting a list. + */ +abstract class ListSortBase extends ListOperationBase { + + use PluginFormTrait; + + /** + * Gets all of the supported sorting methods. + * + * @TODO: User-defined callbacks *or* ECA based compare processes?! + * + * @return array + * All supported sorting methods. + */ + public function getSupportedSortMethods() { + return [ + 'asort' => $this->t('Ascending by value'), + 'arsort' => $this->t('Descending by value'), + 'ksort' => $this->t('Ascending by key'), + 'krsort' => $this->t('Descending by key'), + 'natsort' => $this->t('Natural'), + 'natcasesort' => $this->t('Natural (case insensitive)'), + ]; + } + + /** + * Sorts the list as configured. + */ + public function sortItems(): void { + if (!($list = $this->getItemList())) { + return; + } + + $items = $list->getValue()['values']; + + $sort_method = $this->configuration['sort_method']; + if (array_key_exists($sort_method, $this->getSupportedSortMethods())) { + $sort_method($items); + } + + // @TODO: I know in my heart this is nowhere close to being correct! Jürgen!! Help!! + $sorted_list = DataTransferObject::create($items); + + $dest_token = trim((string) $this->configuration['token_name']); + // If a destination token is not defined, sort the list in-place. + if (!$dest_token) { + $dest_token = trim((string) $this->configuration['list_token']); + } + $this->tokenService->addTokenData($dest_token, $sorted_list); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration(): array { + return [ + 'sort_method' => 'sort', + 'token_name' => '' + ] + parent::defaultConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { + $form['sort_method'] = [ + '#type' => 'select', + '#title' => $this->t('Sort method'), + '#options' => $this->getSupportedSortMethods(), + '#default_value' => $this->configuration['sort_method'], + '#weight' => 0, + ]; + + $form['token_name'] = [ + '#type' => 'textfield', + '#title' => $this->t('Name of token'), + '#description' => $this->t('The sorted list will be loaded into this specified token. If this is not provided, the list will be sorted in-place.'), + '#default_value' => $this->configuration['token_name'], + '#weight' => 4, + ]; + + return parent::buildConfigurationForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void { + parent::submitConfigurationForm($form, $form_state); + $this->configuration['sort_method'] = $form_state->getValue('sort_method'); + $this->configuration['token_name'] = $form_state->getValue('token_name'); + } + +} -- GitLab From 8944f19d81d59ad3888affd5d288c65e6adee8bf Mon Sep 17 00:00:00 2001 From: Luke Leber <24370-lleber@users.noreply.drupalcode.org> Date: Thu, 27 Mar 2025 17:50:30 +0000 Subject: [PATCH 2/7] Clean up; resolve phpcs + cspell --- .../tests/src/Kernel/ListOperationTest.php | 31 ++++++++++++------- src/Plugin/Action/ListSortBase.php | 20 +++++------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/modules/base/tests/src/Kernel/ListOperationTest.php b/modules/base/tests/src/Kernel/ListOperationTest.php index 09329505a..1ab1cf51c 100644 --- a/modules/base/tests/src/Kernel/ListOperationTest.php +++ b/modules/base/tests/src/Kernel/ListOperationTest.php @@ -388,7 +388,7 @@ class ListOperationTest extends KernelTestBase { public function listSortProvider(): array { return [ 'without dest token' => [''], - 'with dest token' => ['dest_token'] + 'with dest token' => ['dest_token'], ]; } @@ -400,14 +400,14 @@ class ListOperationTest extends KernelTestBase { public function testListSortDestToken(string $dest_token): void { $chaos = [ + 'Img12.png' => 'Img12.png', 'img1.png' => 'img1.png', 'img10.png' => 'img10.png', + 'Img10.png' => 'Img10.png', + 'Img2.png' => 'Img2.png', 'img12.png' => 'img12.png', 'img2.png' => 'img2.png', 'Img1.png' => 'Img1.png', - 'Img10.png' => 'Img10.png', - 'Img12.png' => 'Img12.png', - 'Img2.png' => 'Img2.png', ]; /** @var \Drupal\Core\Action\ActionManager $action_manager */ @@ -415,6 +415,7 @@ class ListOperationTest extends KernelTestBase { /** @var \Drupal\eca\Token\TokenInterface $token_services */ $token_services = \Drupal::service('eca.token_services'); + /** @var \Drupal\eca\Plugin\Action\ListSortBase $action */ $action = $action_manager->createInstance('eca_list_sort', []); foreach (array_keys($action->getSupportedSortMethods()) as $method) { @@ -424,20 +425,28 @@ class ListOperationTest extends KernelTestBase { 'sort_method' => $method, 'token_name' => $dest_token, ]); + $token_services->addTokenData( + $action->getConfiguration()['list_token'], + DataTransferObject::create($chaos) + ); // Copy the unsorted list into a new variable, then sort it. $expected = $chaos; $method($expected); - // Reset the incoming list. - $list = DataTransferObject::create($chaos); - $token_services->addTokenData('list', $list); - - // Sort the list into a new token. $action->execute(); - $list = $token_services->getTokenData($dest_token ?: 'list'); - $this->assertSame($expected, $list->getValue()['values']); + $dest_token = trim((string) $action->getConfiguration()['token_name']); + if (!$dest_token) { + $dest_token = trim((string) $action->getConfiguration()['list_token']); + } + + $list = $token_services->getTokenData($dest_token); + + $this->assertSame($expected, $list->toArray()); + + // Reset the input token data. + $token_services->clearTokenData(); } } diff --git a/src/Plugin/Action/ListSortBase.php b/src/Plugin/Action/ListSortBase.php index 642a4ca86..048df31f2 100644 --- a/src/Plugin/Action/ListSortBase.php +++ b/src/Plugin/Action/ListSortBase.php @@ -7,19 +7,17 @@ use Drupal\eca\Plugin\DataType\DataTransferObject; use Drupal\eca\Plugin\ECA\PluginFormTrait; /** - * Base class for actions sorting a list. + * Base class for actions adding an item to a list. */ abstract class ListSortBase extends ListOperationBase { use PluginFormTrait; /** - * Gets all of the supported sorting methods. - * - * @TODO: User-defined callbacks *or* ECA based compare processes?! - * + * Gets the supported sorting methods. + * * @return array - * All supported sorting methods. + * The supported sorting methods. */ public function getSupportedSortMethods() { return [ @@ -40,22 +38,18 @@ abstract class ListSortBase extends ListOperationBase { return; } - $items = $list->getValue()['values']; + $items = ($list instanceof DataTransferObject) ? $list->toArray() : \iterator_to_array($list); $sort_method = $this->configuration['sort_method']; if (array_key_exists($sort_method, $this->getSupportedSortMethods())) { $sort_method($items); } - // @TODO: I know in my heart this is nowhere close to being correct! Jürgen!! Help!! - $sorted_list = DataTransferObject::create($items); - $dest_token = trim((string) $this->configuration['token_name']); - // If a destination token is not defined, sort the list in-place. if (!$dest_token) { $dest_token = trim((string) $this->configuration['list_token']); } - $this->tokenService->addTokenData($dest_token, $sorted_list); + $this->tokenService->addTokenData($dest_token, DataTransferObject::create($items)); } /** @@ -64,7 +58,7 @@ abstract class ListSortBase extends ListOperationBase { public function defaultConfiguration(): array { return [ 'sort_method' => 'sort', - 'token_name' => '' + 'token_name' => '', ] + parent::defaultConfiguration(); } -- GitLab From 46b6c73c99f56c2a20744227801a7835e52baba9 Mon Sep 17 00:00:00 2001 From: Luke Leber <24370-lleber@users.noreply.drupalcode.org> Date: Thu, 27 Mar 2025 18:24:45 +0000 Subject: [PATCH 3/7] Embarrassing copy/paste error --- src/Plugin/Action/ListSortBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/Action/ListSortBase.php b/src/Plugin/Action/ListSortBase.php index 048df31f2..39bca6cac 100644 --- a/src/Plugin/Action/ListSortBase.php +++ b/src/Plugin/Action/ListSortBase.php @@ -7,7 +7,7 @@ use Drupal\eca\Plugin\DataType\DataTransferObject; use Drupal\eca\Plugin\ECA\PluginFormTrait; /** - * Base class for actions adding an item to a list. + * Base class for sorting lists. */ abstract class ListSortBase extends ListOperationBase { -- GitLab From 59b760c34e456a3bb82e6bfaac98ff932fae28b3 Mon Sep 17 00:00:00 2001 From: Luke Leber <24370-lleber@users.noreply.drupalcode.org> Date: Thu, 27 Mar 2025 18:49:38 +0000 Subject: [PATCH 4/7] May need cspell tweaks; unsure how to run cspell locally. --- modules/base/src/Plugin/Action/ListSort.php | 107 +++++++++++++++++++- src/Plugin/Action/ListSortBase.php | 97 ------------------ 2 files changed, 104 insertions(+), 100 deletions(-) delete mode 100644 src/Plugin/Action/ListSortBase.php diff --git a/modules/base/src/Plugin/Action/ListSort.php b/modules/base/src/Plugin/Action/ListSort.php index 159d535f3..eb07c0ce4 100644 --- a/modules/base/src/Plugin/Action/ListSort.php +++ b/modules/base/src/Plugin/Action/ListSort.php @@ -2,7 +2,10 @@ namespace Drupal\eca_base\Plugin\Action; -use Drupal\eca\Plugin\Action\ListSortBase; +use Drupal\Core\Form\FormStateInterface; +use Drupal\eca\Plugin\Action\ListOperationBase; +use Drupal\eca\Plugin\DataType\DataTransferObject; +use Drupal\eca\Plugin\ECA\PluginFormTrait; /** * Sorts a list. @@ -14,13 +17,111 @@ use Drupal\eca\Plugin\Action\ListSortBase; * eca_version_introduced = "2.2.0" * ) */ -class ListSort extends ListSortBase { +class ListSort extends ListOperationBase { + + use PluginFormTrait; + + /** + * Gets the supported sorting methods. + * + * @return array + * The supported sorting methods. + */ + public function getSupportedSortMethods() { + return [ + 'asort' => $this->t('Ascending by value'), + 'arsort' => $this->t('Descending by value'), + 'ksort' => $this->t('Ascending by key'), + 'krsort' => $this->t('Descending by key'), + 'natsort' => $this->t('Natural'), + 'natcasesort' => $this->t('Natural (case insensitive)'), + ]; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration(): array { + return [ + 'sort_method' => 'sort', + 'token_name' => '', + ] + parent::defaultConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { + $form['sort_method'] = [ + '#type' => 'select', + '#title' => $this->t('Sort method'), + '#options' => $this->getSupportedSortMethods(), + '#default_value' => $this->configuration['sort_method'], + '#weight' => 0, + ]; + + $form['token_name'] = [ + '#type' => 'textfield', + '#title' => $this->t('Name of token'), + '#description' => $this->t('The sorted list will be loaded into this specified token. If this is not provided, the list will be sorted in-place.'), + '#default_value' => $this->configuration['token_name'], + '#weight' => 4, + ]; + + return parent::buildConfigurationForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void { + parent::submitConfigurationForm($form, $form_state); + $this->configuration['sort_method'] = $form_state->getValue('sort_method'); + $this->configuration['token_name'] = $form_state->getValue('token_name'); + } /** * {@inheritdoc} */ public function execute(): void { - $this->sortItems(); + if (!($list = $this->getItemList())) { + return; + } + + $items = ($list instanceof DataTransferObject) ? $list->toArray() : \iterator_to_array($list); + + // Note - verbosity is intended to aid in static analysis. + switch ($this->configuration['sort_method']) { + case 'asort': + \asort($items); + break; + + case 'arsort': + \arsort($items); + break; + + case 'ksort': + \ksort($items); + break; + + case 'krsort': + \krsort($items); + break; + + case 'natsort': + \natsort($items); + break; + + case 'natcasesort': + \natcasesort($items); + break; + } + + $dest_token = trim((string) $this->configuration['token_name']); + if (!$dest_token) { + $dest_token = trim((string) $this->configuration['list_token']); + } + $this->tokenService->addTokenData($dest_token, DataTransferObject::create($items)); } } diff --git a/src/Plugin/Action/ListSortBase.php b/src/Plugin/Action/ListSortBase.php deleted file mode 100644 index 39bca6cac..000000000 --- a/src/Plugin/Action/ListSortBase.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -namespace Drupal\eca\Plugin\Action; - -use Drupal\Core\Form\FormStateInterface; -use Drupal\eca\Plugin\DataType\DataTransferObject; -use Drupal\eca\Plugin\ECA\PluginFormTrait; - -/** - * Base class for sorting lists. - */ -abstract class ListSortBase extends ListOperationBase { - - use PluginFormTrait; - - /** - * Gets the supported sorting methods. - * - * @return array - * The supported sorting methods. - */ - public function getSupportedSortMethods() { - return [ - 'asort' => $this->t('Ascending by value'), - 'arsort' => $this->t('Descending by value'), - 'ksort' => $this->t('Ascending by key'), - 'krsort' => $this->t('Descending by key'), - 'natsort' => $this->t('Natural'), - 'natcasesort' => $this->t('Natural (case insensitive)'), - ]; - } - - /** - * Sorts the list as configured. - */ - public function sortItems(): void { - if (!($list = $this->getItemList())) { - return; - } - - $items = ($list instanceof DataTransferObject) ? $list->toArray() : \iterator_to_array($list); - - $sort_method = $this->configuration['sort_method']; - if (array_key_exists($sort_method, $this->getSupportedSortMethods())) { - $sort_method($items); - } - - $dest_token = trim((string) $this->configuration['token_name']); - if (!$dest_token) { - $dest_token = trim((string) $this->configuration['list_token']); - } - $this->tokenService->addTokenData($dest_token, DataTransferObject::create($items)); - } - - /** - * {@inheritdoc} - */ - public function defaultConfiguration(): array { - return [ - 'sort_method' => 'sort', - 'token_name' => '', - ] + parent::defaultConfiguration(); - } - - /** - * {@inheritdoc} - */ - public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { - $form['sort_method'] = [ - '#type' => 'select', - '#title' => $this->t('Sort method'), - '#options' => $this->getSupportedSortMethods(), - '#default_value' => $this->configuration['sort_method'], - '#weight' => 0, - ]; - - $form['token_name'] = [ - '#type' => 'textfield', - '#title' => $this->t('Name of token'), - '#description' => $this->t('The sorted list will be loaded into this specified token. If this is not provided, the list will be sorted in-place.'), - '#default_value' => $this->configuration['token_name'], - '#weight' => 4, - ]; - - return parent::buildConfigurationForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void { - parent::submitConfigurationForm($form, $form_state); - $this->configuration['sort_method'] = $form_state->getValue('sort_method'); - $this->configuration['token_name'] = $form_state->getValue('token_name'); - } - -} -- GitLab From 6e828687f208a75595d839568ea5e9977a5e100d Mon Sep 17 00:00:00 2001 From: Luke Leber <lal65@psu.edu> Date: Thu, 17 Apr 2025 12:11:47 -0400 Subject: [PATCH 5/7] Remove redundant null check (already performed in parent class access method) --- modules/base/src/Plugin/Action/ListSort.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/base/src/Plugin/Action/ListSort.php b/modules/base/src/Plugin/Action/ListSort.php index eb07c0ce4..9dcaa10d1 100644 --- a/modules/base/src/Plugin/Action/ListSort.php +++ b/modules/base/src/Plugin/Action/ListSort.php @@ -3,6 +3,8 @@ namespace Drupal\eca_base\Plugin\Action; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Session\AccountInterface; +use Drupal\Core\TypedData\TraversableTypedDataInterface; use Drupal\eca\Plugin\Action\ListOperationBase; use Drupal\eca\Plugin\DataType\DataTransferObject; use Drupal\eca\Plugin\ECA\PluginFormTrait; @@ -38,6 +40,10 @@ class ListSort extends ListOperationBase { ]; } + protected function getItemList(): ?TraversableTypedDataInterface { + return parent::getItemList(); // TODO: Change the autogenerated stub + } + /** * {@inheritdoc} */ @@ -84,10 +90,7 @@ class ListSort extends ListOperationBase { * {@inheritdoc} */ public function execute(): void { - if (!($list = $this->getItemList())) { - return; - } - + $list = $this->getItemList(); $items = ($list instanceof DataTransferObject) ? $list->toArray() : \iterator_to_array($list); // Note - verbosity is intended to aid in static analysis. -- GitLab From 0372ea1dfa2e6b995345d28ed6b149ea86d1e528 Mon Sep 17 00:00:00 2001 From: Luke Leber <lal65@psu.edu> Date: Thu, 17 Apr 2025 12:22:35 -0400 Subject: [PATCH 6/7] Thanks code sniffer; you found debugging code :D --- modules/base/src/Plugin/Action/ListSort.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/base/src/Plugin/Action/ListSort.php b/modules/base/src/Plugin/Action/ListSort.php index 9dcaa10d1..fadb3fc34 100644 --- a/modules/base/src/Plugin/Action/ListSort.php +++ b/modules/base/src/Plugin/Action/ListSort.php @@ -3,7 +3,6 @@ namespace Drupal\eca_base\Plugin\Action; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Session\AccountInterface; use Drupal\Core\TypedData\TraversableTypedDataInterface; use Drupal\eca\Plugin\Action\ListOperationBase; use Drupal\eca\Plugin\DataType\DataTransferObject; @@ -40,10 +39,6 @@ class ListSort extends ListOperationBase { ]; } - protected function getItemList(): ?TraversableTypedDataInterface { - return parent::getItemList(); // TODO: Change the autogenerated stub - } - /** * {@inheritdoc} */ -- GitLab From d95359d6058a9654403e65218c77c9dae7894b1f Mon Sep 17 00:00:00 2001 From: Luke Leber <lal65@psu.edu> Date: Thu, 17 Apr 2025 12:23:13 -0400 Subject: [PATCH 7/7] ...one more sniff --- modules/base/src/Plugin/Action/ListSort.php | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/base/src/Plugin/Action/ListSort.php b/modules/base/src/Plugin/Action/ListSort.php index fadb3fc34..a2ff9a13a 100644 --- a/modules/base/src/Plugin/Action/ListSort.php +++ b/modules/base/src/Plugin/Action/ListSort.php @@ -3,7 +3,6 @@ namespace Drupal\eca_base\Plugin\Action; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\TypedData\TraversableTypedDataInterface; use Drupal\eca\Plugin\Action\ListOperationBase; use Drupal\eca\Plugin\DataType\DataTransferObject; use Drupal\eca\Plugin\ECA\PluginFormTrait; -- GitLab