From 94cf6459a445a9f308e581a7adbc2cf163e460df Mon Sep 17 00:00:00 2001 From: protitude Date: Fri, 17 Apr 2026 10:53:00 -0600 Subject: [PATCH 1/6] cssn: d8-2701 people badges tweak layout --- modules/cssn/cssn.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cssn/cssn.module b/modules/cssn/cssn.module index 2ef7f93e..2cdd9ade 100644 --- a/modules/cssn/cssn.module +++ b/modules/cssn/cssn.module @@ -58,7 +58,7 @@ function cssn_preprocess_views_view_field(&$variables) { $image = \Drupal::service('file_url_generator')->generateAbsoluteString($image_url); if ($image) { if ($name) { - $badges .= "
  • "; + $badges .= "
  • "; } else { $badges .= "
  • "; From 42a98019ba35e507b1fa4a3b5cd1bc07b6f76fb7 Mon Sep 17 00:00:00 2001 From: protitude Date: Fri, 17 Apr 2026 11:50:05 -0600 Subject: [PATCH 2/6] badges: d8-2701 Order badges based on taxonomy --- modules/access_badges/access_badges.install | 39 +++++++++++++++++++++ modules/access_badges/access_badges.module | 38 ++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/modules/access_badges/access_badges.install b/modules/access_badges/access_badges.install index 6a3aaebf..7a4d84ba 100644 --- a/modules/access_badges/access_badges.install +++ b/modules/access_badges/access_badges.install @@ -130,6 +130,45 @@ function access_badges_install() { } +/** + * Sort field_user_badges for all users to match badges taxonomy order. + */ +function access_badges_update_9001(&$sandbox) { + // Load ordered tids from the badges vocabulary. + $tids = \Drupal::entityQuery('taxonomy_term') + ->condition('vid', 'badges') + ->accessCheck(FALSE) + ->sort('weight') + ->sort('name') + ->execute(); + + $order = array_flip(array_values($tids)); + + $uids = \Drupal::entityQuery('user') + ->condition('uid', 0, '>') + ->accessCheck(FALSE) + ->execute(); + + foreach ($uids as $uid) { + $user = \Drupal\user\Entity\User::load($uid); + $badges = $user->get('field_user_badges')->getValue(); + if (empty($badges)) { + continue; + } + + $user_tids = array_column($badges, 'target_id'); + usort($user_tids, function ($a, $b) use ($order) { + $pos_a = $order[$a] ?? PHP_INT_MAX; + $pos_b = $order[$b] ?? PHP_INT_MAX; + return $pos_a - $pos_b; + }); + + $sorted = array_map(fn($tid) => ['target_id' => $tid], $user_tids); + $user->set('field_user_badges', $sorted); + $user->save(); + } +} + /** * Remove Badges from taxonomy. */ diff --git a/modules/access_badges/access_badges.module b/modules/access_badges/access_badges.module index 0002cd70..e178a617 100644 --- a/modules/access_badges/access_badges.module +++ b/modules/access_badges/access_badges.module @@ -5,6 +5,7 @@ */ use Drupal\Core\Form\FormStateInterface; +use Drupal\user\UserInterface; /** * Implements hook_form_alter(). @@ -22,6 +23,43 @@ function access_badges_form_alter(&$form, FormStateInterface $form_state, $form_ } } +/** + * Implements hook_user_presave(). + */ +function access_badges_user_presave(UserInterface $user) { + _access_badges_sort_user_badges($user); +} + +/** + * Sort field_user_badges to match the badges taxonomy term order. + */ +function _access_badges_sort_user_badges(UserInterface $user) { + $badges = $user->get('field_user_badges')->getValue(); + if (empty($badges)) { + return; + } + + // Load ordered tids from the badges vocabulary. + $tids = \Drupal::entityQuery('taxonomy_term') + ->condition('vid', 'badges') + ->accessCheck(FALSE) + ->sort('weight') + ->sort('name') + ->execute(); + + $order = array_flip(array_values($tids)); + $user_tids = array_column($badges, 'target_id'); + + usort($user_tids, function ($a, $b) use ($order) { + $pos_a = $order[$a] ?? PHP_INT_MAX; + $pos_b = $order[$b] ?? PHP_INT_MAX; + return $pos_a - $pos_b; + }); + + $sorted = array_map(fn($tid) => ['target_id' => $tid], $user_tids); + $user->set('field_user_badges', $sorted); +} + /** * Implements hook_cron(). */ From a80e493df9bf12dde310ad79260d35e56cccaa72 Mon Sep 17 00:00:00 2001 From: protitude Date: Fri, 17 Apr 2026 12:25:01 -0600 Subject: [PATCH 3/6] badges: d8-2701 remove update --- modules/access_badges/access_badges.install | 39 --------------------- 1 file changed, 39 deletions(-) diff --git a/modules/access_badges/access_badges.install b/modules/access_badges/access_badges.install index 7a4d84ba..6a3aaebf 100644 --- a/modules/access_badges/access_badges.install +++ b/modules/access_badges/access_badges.install @@ -130,45 +130,6 @@ function access_badges_install() { } -/** - * Sort field_user_badges for all users to match badges taxonomy order. - */ -function access_badges_update_9001(&$sandbox) { - // Load ordered tids from the badges vocabulary. - $tids = \Drupal::entityQuery('taxonomy_term') - ->condition('vid', 'badges') - ->accessCheck(FALSE) - ->sort('weight') - ->sort('name') - ->execute(); - - $order = array_flip(array_values($tids)); - - $uids = \Drupal::entityQuery('user') - ->condition('uid', 0, '>') - ->accessCheck(FALSE) - ->execute(); - - foreach ($uids as $uid) { - $user = \Drupal\user\Entity\User::load($uid); - $badges = $user->get('field_user_badges')->getValue(); - if (empty($badges)) { - continue; - } - - $user_tids = array_column($badges, 'target_id'); - usort($user_tids, function ($a, $b) use ($order) { - $pos_a = $order[$a] ?? PHP_INT_MAX; - $pos_b = $order[$b] ?? PHP_INT_MAX; - return $pos_a - $pos_b; - }); - - $sorted = array_map(fn($tid) => ['target_id' => $tid], $user_tids); - $user->set('field_user_badges', $sorted); - $user->save(); - } -} - /** * Remove Badges from taxonomy. */ From d43b209600ac8e9719fbed162ba4f5fd47eee5bd Mon Sep 17 00:00:00 2001 From: protitude Date: Fri, 17 Apr 2026 12:45:02 -0600 Subject: [PATCH 4/6] drush/service: d8-2701 Move badge sort to a service - add drush command to sort badges --- modules/access_badges/access_badges.module | 32 +-------- .../access_badges/access_badges.services.yml | 10 +++ .../src/Commands/BadgesCommands.php | 32 +++++++++ .../access_badges/src/Service/BadgeSorter.php | 70 +++++++++++++++++++ 4 files changed, 113 insertions(+), 31 deletions(-) create mode 100644 modules/access_badges/src/Commands/BadgesCommands.php create mode 100644 modules/access_badges/src/Service/BadgeSorter.php diff --git a/modules/access_badges/access_badges.module b/modules/access_badges/access_badges.module index e178a617..1960c199 100644 --- a/modules/access_badges/access_badges.module +++ b/modules/access_badges/access_badges.module @@ -27,37 +27,7 @@ function access_badges_form_alter(&$form, FormStateInterface $form_state, $form_ * Implements hook_user_presave(). */ function access_badges_user_presave(UserInterface $user) { - _access_badges_sort_user_badges($user); -} - -/** - * Sort field_user_badges to match the badges taxonomy term order. - */ -function _access_badges_sort_user_badges(UserInterface $user) { - $badges = $user->get('field_user_badges')->getValue(); - if (empty($badges)) { - return; - } - - // Load ordered tids from the badges vocabulary. - $tids = \Drupal::entityQuery('taxonomy_term') - ->condition('vid', 'badges') - ->accessCheck(FALSE) - ->sort('weight') - ->sort('name') - ->execute(); - - $order = array_flip(array_values($tids)); - $user_tids = array_column($badges, 'target_id'); - - usort($user_tids, function ($a, $b) use ($order) { - $pos_a = $order[$a] ?? PHP_INT_MAX; - $pos_b = $order[$b] ?? PHP_INT_MAX; - return $pos_a - $pos_b; - }); - - $sorted = array_map(fn($tid) => ['target_id' => $tid], $user_tids); - $user->set('field_user_badges', $sorted); + \Drupal::service('access_badges.badge_sorter')->sortUserBadges($user); } /** diff --git a/modules/access_badges/access_badges.services.yml b/modules/access_badges/access_badges.services.yml index aa565c61..7abd3b7a 100644 --- a/modules/access_badges/access_badges.services.yml +++ b/modules/access_badges/access_badges.services.yml @@ -1,3 +1,13 @@ services: access_badges.badgeTools: class: Drupal\access_badges\Plugin\BadgeTools + + access_badges.badge_sorter: + class: Drupal\access_badges\Service\BadgeSorter + arguments: ['@entity_type.manager'] + + access_badges.badges_commands: + class: Drupal\access_badges\Commands\BadgesCommands + arguments: ['@access_badges.badge_sorter'] + tags: + - { name: drush.command } diff --git a/modules/access_badges/src/Commands/BadgesCommands.php b/modules/access_badges/src/Commands/BadgesCommands.php new file mode 100644 index 00000000..d51b6405 --- /dev/null +++ b/modules/access_badges/src/Commands/BadgesCommands.php @@ -0,0 +1,32 @@ +output()->writeln('Sorting badges for all users...'); + $count = $this->badgeSorter->sortAllUsers(); + $this->output()->writeln("Done. Resaved $count user(s)."); + } + +} diff --git a/modules/access_badges/src/Service/BadgeSorter.php b/modules/access_badges/src/Service/BadgeSorter.php new file mode 100644 index 00000000..238a5bd1 --- /dev/null +++ b/modules/access_badges/src/Service/BadgeSorter.php @@ -0,0 +1,70 @@ +get('field_user_badges')->getValue(); + if (count($badges) < 2) { + return; + } + + $tids = $this->entityTypeManager->getStorage('taxonomy_term') + ->getQuery() + ->condition('vid', 'badges') + ->accessCheck(FALSE) + ->sort('weight') + ->sort('name') + ->execute(); + + $order = array_flip(array_values($tids)); + $user_tids = array_column($badges, 'target_id'); + + usort($user_tids, function ($a, $b) use ($order) { + $pos_a = $order[$a] ?? PHP_INT_MAX; + $pos_b = $order[$b] ?? PHP_INT_MAX; + return $pos_a - $pos_b; + }); + + $user->set('field_user_badges', array_map(fn($tid) => ['target_id' => $tid], $user_tids)); + } + + /** + * Sort badges for all users and return count of users saved. + */ + public function sortAllUsers(): int { + $uids = $this->entityTypeManager->getStorage('user') + ->getQuery() + ->condition('uid', 0, '>') + ->accessCheck(FALSE) + ->execute(); + + $count = 0; + foreach ($uids as $uid) { + $user = $this->entityTypeManager->getStorage('user')->load($uid); + $badges = $user->get('field_user_badges')->getValue(); + if (count($badges) < 2) { + continue; + } + $this->sortUserBadges($user); + $user->save(); + $count++; + } + return $count; + } + +} From 5a2af128a0adcb43a72418776d0755be537f2024 Mon Sep 17 00:00:00 2001 From: protitude Date: Fri, 17 Apr 2026 12:52:23 -0600 Subject: [PATCH 5/6] drush: d8-2701 Adding drush.services file --- modules/access_badges/drush.services.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 modules/access_badges/drush.services.yml diff --git a/modules/access_badges/drush.services.yml b/modules/access_badges/drush.services.yml new file mode 100644 index 00000000..d487af26 --- /dev/null +++ b/modules/access_badges/drush.services.yml @@ -0,0 +1,6 @@ +services: + access_badges.badges_commands: + class: Drupal\access_badges\Commands\BadgesCommands + arguments: ['@access_badges.badge_sorter'] + tags: + - { name: drush.command } From 6c04936bc871c2d1ef23e1f23275fc2ce74ceac5 Mon Sep 17 00:00:00 2001 From: protitude Date: Fri, 17 Apr 2026 14:56:22 -0600 Subject: [PATCH 6/6] badges: d8-2701 Fix badgeSorter to update db and re-save all users - add hook_update --- modules/access_badges/access_badges.install | 8 ++ .../access_badges/access_badges.services.yml | 2 +- .../access_badges/src/Service/BadgeSorter.php | 73 ++++++++++++++++--- 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/modules/access_badges/access_badges.install b/modules/access_badges/access_badges.install index 6a3aaebf..3ae2640b 100644 --- a/modules/access_badges/access_badges.install +++ b/modules/access_badges/access_badges.install @@ -130,6 +130,14 @@ function access_badges_install() { } +/** + * Sort all user badges to match taxonomy term weight/name order. + */ +function access_badges_update_10001(): string { + $count = \Drupal::service('access_badges.badge_sorter')->sortAllUsers(); + return "Sorted badges for $count users."; +} + /** * Remove Badges from taxonomy. */ diff --git a/modules/access_badges/access_badges.services.yml b/modules/access_badges/access_badges.services.yml index 7abd3b7a..dc210b05 100644 --- a/modules/access_badges/access_badges.services.yml +++ b/modules/access_badges/access_badges.services.yml @@ -4,7 +4,7 @@ services: access_badges.badge_sorter: class: Drupal\access_badges\Service\BadgeSorter - arguments: ['@entity_type.manager'] + arguments: ['@entity_type.manager', '@database'] access_badges.badges_commands: class: Drupal\access_badges\Commands\BadgesCommands diff --git a/modules/access_badges/src/Service/BadgeSorter.php b/modules/access_badges/src/Service/BadgeSorter.php index 238a5bd1..f3fd77b0 100644 --- a/modules/access_badges/src/Service/BadgeSorter.php +++ b/modules/access_badges/src/Service/BadgeSorter.php @@ -2,6 +2,7 @@ namespace Drupal\access_badges\Service; +use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\user\UserInterface; @@ -12,6 +13,7 @@ class BadgeSorter { public function __construct( protected EntityTypeManagerInterface $entityTypeManager, + protected Connection $database, ) {} /** @@ -44,24 +46,77 @@ public function sortUserBadges(UserInterface $user): void { } /** - * Sort badges for all users and return count of users saved. + * Sort badges for all users via direct DB writes, bypassing entity save. */ public function sortAllUsers(): int { - $uids = $this->entityTypeManager->getStorage('user') + $tids = $this->entityTypeManager->getStorage('taxonomy_term') ->getQuery() - ->condition('uid', 0, '>') + ->condition('vid', 'badges') ->accessCheck(FALSE) + ->sort('weight') + ->sort('name') ->execute(); + $order = array_flip(array_values($tids)); + + // Load all badge rows grouped by user. + $rows = $this->database->select('user__field_user_badges', 'b') + ->fields('b', ['entity_id', 'revision_id', 'langcode', 'delta', 'field_user_badges_target_id']) + ->condition('deleted', 0) + ->orderBy('entity_id') + ->orderBy('delta') + ->execute() + ->fetchAll(); + + $by_user = []; + foreach ($rows as $row) { + $by_user[$row->entity_id][] = $row; + } $count = 0; - foreach ($uids as $uid) { - $user = $this->entityTypeManager->getStorage('user')->load($uid); - $badges = $user->get('field_user_badges')->getValue(); - if (count($badges) < 2) { + foreach ($by_user as $uid => $user_rows) { + if (count($user_rows) < 2) { continue; } - $this->sortUserBadges($user); - $user->save(); + + usort($user_rows, function ($a, $b) use ($order) { + $pos_a = $order[$a->field_user_badges_target_id] ?? PHP_INT_MAX; + $pos_b = $order[$b->field_user_badges_target_id] ?? PHP_INT_MAX; + return $pos_a - $pos_b; + }); + + // Check if already sorted to avoid unnecessary writes. + $already_sorted = TRUE; + foreach ($user_rows as $delta => $row) { + if ((int) $row->delta !== $delta) { + $already_sorted = FALSE; + break; + } + } + if ($already_sorted) { + continue; + } + + // Delete and re-insert to avoid unique key conflicts on delta. + $this->database->delete('user__field_user_badges') + ->condition('entity_id', $uid) + ->condition('deleted', 0) + ->execute(); + + $insert = $this->database->insert('user__field_user_badges') + ->fields(['bundle', 'deleted', 'entity_id', 'revision_id', 'langcode', 'delta', 'field_user_badges_target_id']); + + foreach ($user_rows as $delta => $row) { + $insert->values([ + 'bundle' => 'user', + 'deleted' => 0, + 'entity_id' => $row->entity_id, + 'revision_id' => $row->revision_id, + 'langcode' => $row->langcode, + 'delta' => $delta, + 'field_user_badges_target_id' => $row->field_user_badges_target_id, + ]); + } + $insert->execute(); $count++; } return $count;