Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions modules/access_badges/access_badges.install
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
8 changes: 8 additions & 0 deletions modules/access_badges/access_badges.module
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

use Drupal\Core\Form\FormStateInterface;
use Drupal\user\UserInterface;

/**
* Implements hook_form_alter().
Expand All @@ -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().
*/
Expand Down
10 changes: 10 additions & 0 deletions modules/access_badges/access_badges.services.yml
Original file line number Diff line number Diff line change
@@ -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 }
6 changes: 6 additions & 0 deletions modules/access_badges/drush.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
access_badges.badges_commands:
class: Drupal\access_badges\Commands\BadgesCommands
arguments: ['@access_badges.badge_sorter']
tags:
- { name: drush.command }
32 changes: 32 additions & 0 deletions modules/access_badges/src/Commands/BadgesCommands.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Drupal\access_badges\Commands;

use Drupal\access_badges\Service\BadgeSorter;
use Drush\Commands\DrushCommands;

/**
* Drush commands for access_badges.
*/
class BadgesCommands extends DrushCommands {

public function __construct(
protected BadgeSorter $badgeSorter,
) {
parent::__construct();
}

/**
* Sort field_user_badges for all users to match badges taxonomy order.
*
* @command badges:sort
* @aliases badges-sort
* @usage drush badges:sort
*/
public function sortBadges(): void {
$this->output()->writeln('Sorting badges for all users...');
$count = $this->badgeSorter->sortAllUsers();
$this->output()->writeln("Done. Resaved $count user(s).");
}

}
125 changes: 125 additions & 0 deletions modules/access_badges/src/Service/BadgeSorter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

namespace Drupal\access_badges\Service;

use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\user\UserInterface;

/**
* Sorts user badges to match badges taxonomy term order.
*/
class BadgeSorter {

public function __construct(
protected EntityTypeManagerInterface $entityTypeManager,
protected Connection $database,
) {}

/**
* Sort field_user_badges on a user entity to match taxonomy weight/name order.
*/
public function sortUserBadges(UserInterface $user): void {
$badges = $user->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;
}

}
2 changes: 1 addition & 1 deletion modules/cssn/cssn.module
Original file line number Diff line number Diff line change
Expand Up @@ -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 .= "<li class='me-2 mb-1 position-relative float-right' data-placement='left' data-toggle='tooltip' title='$name'>";
$badges .= "<li class='me-2 mb-1 position-relative inline-block d-inline-block' data-placement='left' data-toggle='tooltip' title='$name'>";
}
else {
$badges .= "<li class='ms-2 mb-1'>";
Expand Down