feat: import users` real names from bpn API
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20260817090000 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Store the BusPro first and last name on the user';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// Left empty on purpose: the names are synced from BPN on the next login of each account.
|
||||||
|
$this->addSql('ALTER TABLE user ADD first_name VARCHAR(100) DEFAULT NULL, ADD last_name VARCHAR(100) DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE user DROP first_name, DROP last_name');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -129,7 +129,13 @@ class PersonalDataController extends AbstractController
|
|||||||
|
|
||||||
// Update profile completeness flag on user entity
|
// Update profile completeness flag on user entity
|
||||||
$isComplete = $this->completenessChecker->isComplete($personalData);
|
$isComplete = $this->completenessChecker->isComplete($personalData);
|
||||||
$user->setProfileComplete($isComplete);
|
$user
|
||||||
|
->setProfileComplete($isComplete)
|
||||||
|
// Otherwise a rename here would stay invisible in the backend until the next
|
||||||
|
// login, which is the only other place the name is synced from BPN.
|
||||||
|
->setFirstName($personalData->firstName)
|
||||||
|
->setLastName($personalData->name)
|
||||||
|
;
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$this->addFlash('success', 'Deine persönlichen Daten wurden aktualisiert');
|
$this->addFlash('success', 'Deine persönlichen Daten wurden aktualisiert');
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
|||||||
#[ORM\Column(type: 'integer', nullable: true)]
|
#[ORM\Column(type: 'integer', nullable: true)]
|
||||||
private ?int $addressId = null;
|
private ?int $addressId = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'string', length: 100, nullable: true)]
|
||||||
|
private ?string $firstName = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'string', length: 100, nullable: true)]
|
||||||
|
private ?string $lastName = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string>
|
* @var array<string>
|
||||||
*/
|
*/
|
||||||
@@ -91,6 +97,46 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFirstName(): ?string
|
||||||
|
{
|
||||||
|
return $this->firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFirstName(?string $firstName): static
|
||||||
|
{
|
||||||
|
$this->firstName = $firstName;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLastName(): ?string
|
||||||
|
{
|
||||||
|
return $this->lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLastName(?string $lastName): static
|
||||||
|
{
|
||||||
|
$this->lastName = $lastName;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFullName(): ?string
|
||||||
|
{
|
||||||
|
$fullName = trim(sprintf('%s %s', $this->firstName, $this->lastName));
|
||||||
|
|
||||||
|
return '' !== $fullName ? $fullName : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name to identify this account by in the UI. BusPro only fills the name in on login, so
|
||||||
|
* accounts that have not signed in since the columns were added fall back to their e-mail.
|
||||||
|
*/
|
||||||
|
public function getDisplayName(): string
|
||||||
|
{
|
||||||
|
return $this->getFullName() ?? (string) $this->email;
|
||||||
|
}
|
||||||
|
|
||||||
public function getPassword(): ?string
|
public function getPassword(): ?string
|
||||||
{
|
{
|
||||||
return base64_decode($this->password);
|
return base64_decode($this->password);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ final readonly class AccommodationBookingFilterOptionsProvider
|
|||||||
$managers[(int) $manager->getId()] = $manager;
|
$managers[(int) $manager->getId()] = $manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
uasort($managers, static fn (User $a, User $b) => strcasecmp((string) $a->getEmail(), (string) $b->getEmail()));
|
uasort($managers, static fn (User $a, User $b) => strcasecmp($a->getDisplayName(), $b->getDisplayName()));
|
||||||
|
|
||||||
return array_values($managers);
|
return array_values($managers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class AccommodationBookingFilterType extends AbstractListFilterType
|
|||||||
'label' => 'Betreuer:in',
|
'label' => 'Betreuer:in',
|
||||||
'class' => User::class,
|
'class' => User::class,
|
||||||
'choices' => $options['managers'],
|
'choices' => $options['managers'],
|
||||||
'choice_label' => 'email',
|
'choice_label' => 'displayName',
|
||||||
'placeholder' => 'alle',
|
'placeholder' => 'alle',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
]];
|
]];
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ class AccommodationBookingType extends AbstractType
|
|||||||
'class' => User::class,
|
'class' => User::class,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'choices' => $options['assignable_managers'],
|
'choices' => $options['assignable_managers'],
|
||||||
'choice_label' => 'email',
|
'choice_label' => 'displayName',
|
||||||
'placeholder' => 'keine Zuordnung',
|
'placeholder' => 'keine Zuordnung',
|
||||||
'label' => 'Bearbeiter:in',
|
'label' => 'Bearbeiter:in',
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class AccommodationBookingFilterDto extends AbstractListFilterDto
|
|||||||
} elseif (null !== $this->managedBy) {
|
} elseif (null !== $this->managedBy) {
|
||||||
$chips[] = new ListFilterChip(
|
$chips[] = new ListFilterChip(
|
||||||
'Betreuer:in',
|
'Betreuer:in',
|
||||||
(string) $this->managedBy->getEmail(),
|
$this->managedBy->getDisplayName(),
|
||||||
$this->managedByLocked ? [] : ['managedBy'],
|
$this->managedByLocked ? [] : ['managedBy'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ class UserRepository extends ServiceEntityRepository
|
|||||||
|
|
||||||
$this->applySearchTerm($qb, $filter->searchTerm(), [
|
$this->applySearchTerm($qb, $filter->searchTerm(), [
|
||||||
'user.email',
|
'user.email',
|
||||||
|
'user.firstName',
|
||||||
|
'user.lastName',
|
||||||
'user.addressId',
|
'user.addressId',
|
||||||
'user.personId',
|
'user.personId',
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent
|
|||||||
->setPassword($encryptedPassword)
|
->setPassword($encryptedPassword)
|
||||||
->setPersonId($personalData->personId)
|
->setPersonId($personalData->personId)
|
||||||
->setAddressId($personalData->addressId)
|
->setAddressId($personalData->addressId)
|
||||||
|
->setFirstName($personalData->firstName)
|
||||||
|
->setLastName($personalData->name)
|
||||||
->setLastLoginAt(new \DateTimeImmutable())
|
->setLastLoginAt(new \DateTimeImmutable())
|
||||||
->setProfileComplete($this->completenessChecker->isComplete($personalData))
|
->setProfileComplete($this->completenessChecker->isComplete($personalData))
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if booking.managedBy is not null %}
|
{% if booking.managedBy is not null %}
|
||||||
{{ booking.managedBy.email }}
|
{{ booking.managedBy.displayName }}
|
||||||
{% else %}
|
{% else %}
|
||||||
keine Zuordnung
|
keine Zuordnung
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -13,6 +13,9 @@
|
|||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th>
|
||||||
|
{{ knp_pagination_sortable(pagination, 'Name', 'user.lastName') }}
|
||||||
|
</th>
|
||||||
<th>
|
<th>
|
||||||
{{ knp_pagination_sortable(pagination, 'E-Mail', 'user.email') }}
|
{{ knp_pagination_sortable(pagination, 'E-Mail', 'user.email') }}
|
||||||
</th>
|
</th>
|
||||||
@@ -37,6 +40,9 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for user in pagination %}
|
{% for user in pagination %}
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ user.fullName | default('-') }}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ user.email }}
|
{{ user.email }}
|
||||||
</td>
|
</td>
|
||||||
@@ -65,7 +71,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="7">
|
<td colspan="8">
|
||||||
{{ filter.isActive ? 'Keine Treffer für diesen Filter.' : 'Keine Daten...' }}
|
{{ filter.isActive ? 'Keine Treffer für diesen Filter.' : 'Keine Daten...' }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user