mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2026-03-16 19:07:48 +00:00
Add role management for users and update related translations and templates
- Added a `roles` column to the `users` table using a JSONB data type. - Updated the `User` entity to include role management methods (`getRoles`, `addRole`, `removeRole`). - Modified the `UserType` form to allow selecting special roles. - Updated Twig templates to display assigned roles in the user list. - Added new translations for roles and updated French translation keys in `messages.fr.yml`.
This commit is contained in:
@@ -17,6 +17,7 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
@@ -38,20 +39,22 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
public const NOTIF_FLAG_IMMEDIATE_EMAIL = 'immediate-email';
|
||||
public const NOTIF_FLAG_DAILY_DIGEST = 'daily-digest';
|
||||
|
||||
public const ROLE_SEE_AUDIT_TRAILS = 'ROLE_SEE_AUDIT_TRAILS';
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
||||
#[ORM\Column(name: 'id', type: Types::INTEGER)]
|
||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
protected ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true)]
|
||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
||||
private ?\DateTimeImmutable $absenceStart = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true)]
|
||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
||||
private ?\DateTimeImmutable $absenceEnd = null;
|
||||
/**
|
||||
* Array where SAML attributes's data are stored.
|
||||
*/
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]', 'jsonb' => true])]
|
||||
#[ORM\Column(type: Types::JSON, nullable: false, options: ['default' => '[]', 'jsonb' => true])]
|
||||
private array $attributes = [];
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Civility::class)]
|
||||
@@ -60,13 +63,13 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
#[ORM\ManyToOne(targetEntity: Location::class)]
|
||||
private ?Location $currentLocation = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 150, nullable: true)]
|
||||
#[ORM\Column(type: Types::STRING, length: 150, nullable: true)]
|
||||
private ?string $email = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 150, nullable: true, unique: true)]
|
||||
#[ORM\Column(type: Types::STRING, length: 150, nullable: true, unique: true)]
|
||||
private ?string $emailCanonical = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN)]
|
||||
#[ORM\Column(type: Types::BOOLEAN)]
|
||||
private bool $enabled = true;
|
||||
|
||||
/**
|
||||
@@ -76,10 +79,10 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
#[ORM\Cache(usage: 'NONSTRICT_READ_WRITE')]
|
||||
private Collection $groupCenters;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 200)]
|
||||
#[ORM\Column(type: Types::STRING, length: 200)]
|
||||
private string $label = '';
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN)] // sf4 check: in yml was false by default !?
|
||||
#[ORM\Column(type: Types::BOOLEAN)] // sf4 check: in yml was false by default !?
|
||||
private bool $locked = true;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Center::class)]
|
||||
@@ -94,13 +97,13 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
#[ORM\OneToMany(mappedBy: 'user', targetEntity: UserScopeHistory::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
|
||||
private Collection&Selectable $scopeHistories;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 255)]
|
||||
#[ORM\Column(type: Types::STRING, length: 255)]
|
||||
private string $password = '';
|
||||
|
||||
/**
|
||||
* @internal must be set to null if we use bcrypt
|
||||
*/
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 255, nullable: true)]
|
||||
#[ORM\Column(type: Types::STRING, length: 255, nullable: true)]
|
||||
private ?string $salt = null;
|
||||
|
||||
/**
|
||||
@@ -109,10 +112,10 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
#[ORM\OneToMany(mappedBy: 'user', targetEntity: UserJobHistory::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
|
||||
private Collection&Selectable $jobHistories;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 80)]
|
||||
#[ORM\Column(type: Types::STRING, length: 80)]
|
||||
private string $username = '';
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 80, unique: true, nullable: true)]
|
||||
#[ORM\Column(type: Types::STRING, length: 80, unique: true, nullable: true)]
|
||||
private ?string $usernameCanonical = null;
|
||||
|
||||
/**
|
||||
@@ -125,15 +128,18 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
/**
|
||||
* @var array<string, list<string>>
|
||||
*/
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]', 'jsonb' => true])]
|
||||
#[ORM\Column(type: Types::JSON, nullable: false, options: ['default' => '[]', 'jsonb' => true])]
|
||||
private array $notificationFlags = [];
|
||||
|
||||
/**
|
||||
* User's preferred locale.
|
||||
*/
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 5, nullable: false, options: ['default' => 'fr'])]
|
||||
#[ORM\Column(type: Types::STRING, length: 5, nullable: false, options: ['default' => 'fr'])]
|
||||
private string $locale = 'fr';
|
||||
|
||||
#[ORM\Column(type: Types::JSON, nullable: false, options: ['default' => "'[]'::jsonb", 'jsonb' => true])]
|
||||
private array $roles = [];
|
||||
|
||||
/**
|
||||
* User constructor.
|
||||
*/
|
||||
@@ -280,7 +286,24 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
|
||||
public function getRoles(): array
|
||||
{
|
||||
return ['ROLE_USER'];
|
||||
return [...$this->roles, 'ROLE_USER'];
|
||||
}
|
||||
|
||||
public function addRole(string $role): void
|
||||
{
|
||||
// we do not accept "ROLE_USER"
|
||||
if ('ROLE_USER' === $role) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array($role, $this->roles, true)) {
|
||||
$this->roles[] = $role;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeRole(string $role): void
|
||||
{
|
||||
$this->roles = array_diff($this->roles, [$role]);
|
||||
}
|
||||
|
||||
public function getSalt(): ?string
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Chill\MainBundle\Form;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Location;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
|
||||
@@ -107,6 +108,14 @@ class UserType extends AbstractType
|
||||
'required' => false,
|
||||
'input' => 'datetime_immutable',
|
||||
'label' => 'absence.Absence end',
|
||||
])
|
||||
->add('roles', ChoiceType::class, [
|
||||
'label' => 'user.profile.special_roles',
|
||||
'choices' => [
|
||||
'roles.role_see_audit_trails' => User::ROLE_SEE_AUDIT_TRAILS,
|
||||
],
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
@@ -172,7 +181,7 @@ class UserType extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => \Chill\MainBundle\Entity\User::class,
|
||||
'data_class' => User::class,
|
||||
]);
|
||||
|
||||
$resolver
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
{% block table_entities_thead_tr %}
|
||||
<th>{{ 'Active'|trans }}</th>
|
||||
<th>{{ 'absence.Is absent'|trans }}</th>
|
||||
<th>{{ 'Username'|trans }}</th>
|
||||
<th>{{ 'user.profile.label'|trans }}</th>
|
||||
<th>{{ 'Datas'|trans }}</th>
|
||||
<th>{{ 'Actions'|trans }}</th>
|
||||
{% endblock %}
|
||||
@@ -46,6 +46,15 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul class="unbullet">
|
||||
{# #}
|
||||
{% if entity.roles|length > 1 %}
|
||||
<li>
|
||||
<span class="dt">{{ 'user.profile.special_roles'|trans }}:</span>
|
||||
{% if constant('Chill\\MainBundle\\Entity\\User::ROLE_SEE_AUDIT_TRAILS') in entity.roles %}
|
||||
<span class="badge" style="background-color: var(--bs-beige)" >{{ 'roles.role_see_audit_trails'|trans }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<span class="dt">login:</span>
|
||||
{{ entity.username|e('html_attr') }}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260302202811 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add a role column for table users';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE users ADD roles JSONB DEFAULT \'[]\'::jsonb NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE users DROP roles');
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,8 @@ user:
|
||||
no job: Pas de métier assigné
|
||||
no scope: Pas de service assigné
|
||||
notification_preferences: Préférences pour mes notifications
|
||||
label: Nom affiché
|
||||
special_roles: Droits spécifiques
|
||||
locale:
|
||||
label: Langue de communication
|
||||
help: Langue utilisée pour les notifications par email et autres communications.
|
||||
@@ -544,6 +546,9 @@ CHILL_FOO_SEE: Voir un élément
|
||||
CHILL_FOO_EDIT: Modifier un élément
|
||||
chill_export: Exports (statistiques)
|
||||
|
||||
roles:
|
||||
role_see_audit_trails: Voir les accès aux éléments
|
||||
|
||||
#Show templates
|
||||
Date: Date
|
||||
By: Par
|
||||
@@ -920,6 +925,8 @@ admin:
|
||||
center_name: Territoire
|
||||
permissionsGroup_id: Identifiant du groupe de permissions
|
||||
permissionsGroup_name: Groupe de permissions
|
||||
mainLanguage: Langue principale
|
||||
isRoleSeeAuditTrails: Rôle "voir les audits"
|
||||
job_scope_histories:
|
||||
Show history: Voir l'historique
|
||||
index:
|
||||
|
||||
Reference in New Issue
Block a user