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.module b/modules/access_badges/access_badges.module index 0002cd70..1960c199 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,13 @@ function access_badges_form_alter(&$form, FormStateInterface $form_state, $form_ } } +/** + * Implements hook_user_presave(). + */ +function access_badges_user_presave(UserInterface $user) { + \Drupal::service('access_badges.badge_sorter')->sortUserBadges($user); +} + /** * Implements hook_cron(). */ diff --git a/modules/access_badges/access_badges.services.yml b/modules/access_badges/access_badges.services.yml index aa565c61..dc210b05 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', '@database'] + + 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/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 } 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..f3fd77b0 --- /dev/null +++ b/modules/access_badges/src/Service/BadgeSorter.php @@ -0,0 +1,125 @@ +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 via direct DB writes, bypassing entity save. + */ + public function sortAllUsers(): int { + $tids = $this->entityTypeManager->getStorage('taxonomy_term') + ->getQuery() + ->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 ($by_user as $uid => $user_rows) { + if (count($user_rows) < 2) { + continue; + } + + 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; + } + +} 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 .= "