1036 lines
27 KiB
PHP
Executable file
1036 lines
27 KiB
PHP
Executable file
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Entity;
|
|
|
|
use App\Controller\Admin\AdministratorCrudController;
|
|
use App\Controller\Admin\PlaceCrudController;
|
|
use App\Controller\Admin\UserCrudController;
|
|
use App\Doctrine\Behavior\TimestampableEntity;
|
|
use App\Doctrine\Listener\UserListener;
|
|
use App\Doctrine\Manager\UserManager;
|
|
use App\Enum\User\UserType;
|
|
use App\Form\Type\Security\AccountCreateStep1FormType;
|
|
use App\Form\Type\Security\AccountCreateStep2FormType;
|
|
use App\Form\Type\User\ChangeLoginFormType;
|
|
use App\Form\Type\User\ChangePasswordFormType;
|
|
use App\Form\Type\User\EditProfileFormType;
|
|
use App\Repository\UserRepository;
|
|
use App\Validator\Constraints\User\MembershipPaid;
|
|
use App\Validator\Constraints\User\UniqueUser;
|
|
use Carbon\Carbon;
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
|
use Doctrine\Common\Collections\Collection;
|
|
use Doctrine\DBAL\Types\Types;
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
use libphonenumber\PhoneNumber;
|
|
use libphonenumber\PhoneNumberUtil;
|
|
use Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber as AssertPhoneNumber;
|
|
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
|
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
|
use Symfony\Component\Security\Core\User\EquatableInterface;
|
|
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
|
use Symfony\Component\Security\Core\User\UserInterface;
|
|
use Symfony\Component\Security\Core\Validator\Constraints as SecurityAssert;
|
|
use Symfony\Component\Uid\Uuid;
|
|
use Symfony\Component\Validator\Constraints as Assert;
|
|
|
|
use function Symfony\Component\String\u;
|
|
|
|
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
|
#[ORM\Index(columns: ['type'])]
|
|
#[ORM\Index(columns: ['email'])]
|
|
#[ORM\Index(columns: ['confirmation_token'])]
|
|
#[ORM\Index(columns: ['lost_password_token'])]
|
|
#[ORM\Table(name: '`user`')] // we also need escaping here
|
|
#[ORM\EntityListeners([UserListener::class])]
|
|
#[UniqueUser(groups: [AccountCreateStep1FormType::class, ChangeLoginFormType::class])]
|
|
#[UniqueEntity('email', groups: ['Default'])]
|
|
#[MembershipPaid]
|
|
class User implements UserInterface, PasswordAuthenticatedUserInterface, ImageInterface, EquatableInterface
|
|
{
|
|
use UserConfirmationTrait;
|
|
use UserLostPasswordTrait;
|
|
use TimestampableEntity;
|
|
|
|
final public const ROLE_USER = 'ROLE_USER';
|
|
final public const ROLE_ADMIN = 'ROLE_ADMIN';
|
|
final public const ROLE_GROUP_ADMIN = 'ROLE_GROUP_ADMIN';
|
|
final public const MEMBERSHIP_PAID = 'MEMBERSHIP_PAID';
|
|
|
|
private const EMAIL_MAX_LENGTH = 180;
|
|
private const NAME_LENGTH = 180;
|
|
private const PHONE_LENGTH = 15;
|
|
private const SCHEDULE_LENGTH = 180;
|
|
|
|
/**
|
|
* Generates a V6 uuid.
|
|
*/
|
|
#[ORM\Id]
|
|
#[ORM\Column(type: 'uuid', unique: true)]
|
|
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
|
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
|
private Uuid $id;
|
|
|
|
/**
|
|
* Type of account: user, admin or place. The type is nullable because it is
|
|
* choosen by the user at the second step of the account creation.
|
|
*/
|
|
#[ORM\Column(name: 'type', type: 'string', nullable: true, enumType: UserType::class)]
|
|
#[Assert\NotBlank]
|
|
protected ?UserType $type = null;
|
|
|
|
/**
|
|
* The email is unique and normalized (lowercase).
|
|
*/
|
|
#[ORM\Column(length: self::EMAIL_MAX_LENGTH, unique: true, nullable: false)]
|
|
#[Assert\Length(max: self::EMAIL_MAX_LENGTH, groups: [AccountCreateStep1FormType::class, ChangeLoginFormType::class, 'Default'])]
|
|
#[Assert\NotBlank(groups: [AccountCreateStep1FormType::class, ChangeLoginFormType::class, 'Default'])]
|
|
#[Assert\Email(groups: [AccountCreateStep1FormType::class, ChangeLoginFormType::class, 'Default'])]
|
|
private string $email;
|
|
|
|
/**
|
|
* Flag that tells if the user has confirmed his email with the account confirmation
|
|
* token he received. This flag should never be updated manually.
|
|
*/
|
|
#[ORM\Column(type: 'boolean', nullable: false)]
|
|
private bool $emailConfirmed = false;
|
|
|
|
/**
|
|
* Lastname of user or admin.
|
|
*/
|
|
#[ORM\Column(length: self::NAME_LENGTH, nullable: true)]
|
|
#[Assert\Length(max: self::NAME_LENGTH)]
|
|
#[Assert\When(
|
|
expression: '!this.isPlace()',
|
|
constraints: [
|
|
new Assert\NotBlank(message: 'account_create.lastname.empty.error'),
|
|
],
|
|
groups: [EditProfileFormType::class, AccountCreateStep2FormType::class],
|
|
)]
|
|
private ?string $lastname = null;
|
|
|
|
/**
|
|
* Firstname of user or admin.
|
|
*/
|
|
#[ORM\Column(length: self::NAME_LENGTH, nullable: true)]
|
|
#[Assert\Length(max: self::NAME_LENGTH)]
|
|
#[Assert\When(
|
|
expression: '!this.isPlace()',
|
|
constraints: [
|
|
new Assert\NotBlank(message: 'account_create.firstname.empty.error'),
|
|
],
|
|
groups: ['Default', EditProfileFormType::class]
|
|
)]
|
|
private ?string $firstname = null;
|
|
|
|
/**
|
|
* Name of place.
|
|
*/
|
|
#[ORM\Column(length: self::NAME_LENGTH, nullable: true)]
|
|
#[Assert\Length(max: self::NAME_LENGTH)]
|
|
#[Assert\When(
|
|
expression: 'this.isPlace()',
|
|
constraints: [
|
|
new Assert\NotBlank(message: 'account_create.name.empty.error'),
|
|
],
|
|
groups: ['Default', EditProfileFormType::class]
|
|
)]
|
|
private ?string $name = null;
|
|
|
|
/**
|
|
* Phone number of user or admin.
|
|
*/
|
|
#[ORM\Column(nullable: true)]
|
|
#[Assert\Length(max: self::PHONE_LENGTH)]
|
|
private ?string $phoneNumber = null;
|
|
|
|
/**
|
|
* Special field for country index+number in forms.
|
|
*/
|
|
#[AssertPhoneNumber]
|
|
public ?PhoneNumber $phone = null;
|
|
|
|
/**
|
|
* Avatar of user or admin (file upload).
|
|
*/
|
|
#[ORM\Column(nullable: true)]
|
|
private ?string $avatar = null;
|
|
|
|
/**
|
|
* A user must be enabled to be able to login. If a user tries to login with
|
|
* a deactivated account, then he will have a specific message indicating
|
|
* the reason he can't login. He should be adviced to contact the instance administrator.
|
|
*/
|
|
#[ORM\Column(type: 'boolean', nullable: false)]
|
|
protected bool $enabled = true;
|
|
|
|
/**
|
|
* Tells if the account is the main administrator account that was created at
|
|
* the instance creation. Account with this flag can't be deactivated or deleted.
|
|
* This flag can only be changed by SQL or with a CLI comand.
|
|
*/
|
|
#[ORM\Column(type: 'boolean', nullable: false)]
|
|
protected bool $mainAdminAccount = false;
|
|
|
|
/**
|
|
* Tells if it is a developper account. It allows to enable some debugging
|
|
* functionnalities that other admin won't see. This should only activated
|
|
* on dev accounts.
|
|
*/
|
|
#[ORM\Column(type: 'boolean', nullable: false)]
|
|
protected bool $devAccount = false;
|
|
|
|
/**
|
|
* Liste of roles (see constants).
|
|
*
|
|
* @see getRoles()
|
|
*
|
|
* @var array<string>
|
|
*/
|
|
#[ORM\Column]
|
|
private array $roles = [];
|
|
|
|
/**
|
|
* The hashed password.
|
|
*/
|
|
#[ORM\Column(nullable: true)]
|
|
private ?string $password = null;
|
|
|
|
/**
|
|
* The password before it is encrypted.
|
|
*/
|
|
#[Assert\Length(min: UserManager::PASWWORD_MIN_LENGTH, max: UserManager::PASWWORD_MAX_LENGTH, groups: [AccountCreateStep2FormType::class, ChangePasswordFormType::class, 'Default'])]
|
|
// #[Assert\NotCompromisedPassword] // enable to check the password with the https://haveibeenpwned.com/ service
|
|
#[Assert\NotBlank(groups: [AccountCreateStep2FormType::class, ChangePasswordFormType::class])]
|
|
private ?string $plainPassword = null;
|
|
|
|
#[SecurityAssert\UserPassword(groups: [ChangePasswordFormType::class])]
|
|
private ?string $oldPassword = null;
|
|
|
|
/**
|
|
* Last login date of the user, null if has never logged in. The email confirmation
|
|
* does not count as a valid login.
|
|
*/
|
|
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
|
private ?\DateTimeInterface $loginAt = null;
|
|
|
|
/**
|
|
* Tells if the user wants to receive sms notifications.
|
|
*/
|
|
#[ORM\Column(type: 'boolean', nullable: true)]
|
|
#[Assert\Type('bool')]
|
|
private bool $smsNotifications = false;
|
|
|
|
/**
|
|
* If it is a place, it tells its schedules.
|
|
*/
|
|
#[ORM\Column(length: self::SCHEDULE_LENGTH, nullable: true)]
|
|
#[Assert\Length(max: self::SCHEDULE_LENGTH)]
|
|
private ?string $schedule = null;
|
|
|
|
/**
|
|
* User's favorite category.
|
|
*/
|
|
#[ORM\ManyToOne(targetEntity: Category::class)]
|
|
#[ORM\JoinColumn(referencedColumnName: 'id')]
|
|
private ?Category $category = null;
|
|
|
|
/**
|
|
* User's description.
|
|
*/
|
|
#[ORM\Column(type: 'string', nullable: true, )]
|
|
private ?string $description = null;
|
|
|
|
/**
|
|
* Tells if the user in on vacation.
|
|
*/
|
|
#[ORM\Column(type: 'boolean')]
|
|
#[Assert\Type('bool')]
|
|
private bool $vacationMode = false;
|
|
|
|
/**
|
|
* Main address of the user/place.
|
|
*/
|
|
#[ORM\ManyToOne(targetEntity: Address::class, cascade: ['persist'])]
|
|
#[ORM\JoinColumn(referencedColumnName: 'id')]
|
|
private ?Address $address = null;
|
|
|
|
/**
|
|
* @var Collection<int, UserGroup>
|
|
*/
|
|
#[ORM\OneToMany(mappedBy: 'user', targetEntity: UserGroup::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
|
|
private Collection $userGroups;
|
|
|
|
/**
|
|
* @var Collection<int, Payment>
|
|
*/
|
|
#[ORM\OneToMany(mappedBy: 'user', targetEntity: Payment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
|
|
private Collection $payments;
|
|
|
|
#[Assert\IsTrue(groups: [AccountCreateStep2FormType::class])]
|
|
public bool $gdpr = true;
|
|
|
|
/**
|
|
* Paid for membership of the platform.
|
|
*/
|
|
#[ORM\Column(type: 'boolean', nullable: false)]
|
|
private bool $membershipPaid = false;
|
|
|
|
#[ORM\ManyToOne(targetEntity: PlatformOffer::class)]
|
|
#[ORM\JoinColumn(referencedColumnName: 'id', onDelete: 'SET NULL')]
|
|
private ?PlatformOffer $platformOffer = null;
|
|
|
|
/**
|
|
* Starting date of a paying membership. The starting date of a free membership
|
|
* is stored in the creation date.
|
|
*/
|
|
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
|
protected ?\DateTimeImmutable $startAt = null;
|
|
|
|
/**
|
|
* Ending date of the paying membership. If it only set for recurring membership.
|
|
* For one-shot payments, only the start date is filled.
|
|
*/
|
|
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
|
protected ?\DateTimeImmutable $endAt = null;
|
|
|
|
/**
|
|
* Date of the last payment of this membership.
|
|
*/
|
|
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
|
protected ?\DateTimeImmutable $payedAt = null;
|
|
|
|
/**
|
|
* Local cache to store groups (extracted from related userGroups).
|
|
*
|
|
* @var Collection<int, Group>|null
|
|
*/
|
|
private ?Collection $groups = null;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->userGroups = new ArrayCollection();
|
|
$this->payments = new ArrayCollection();
|
|
}
|
|
|
|
public function __toString(): string
|
|
{
|
|
return $this->email;
|
|
}
|
|
|
|
public function getId(): Uuid
|
|
{
|
|
return $this->id;
|
|
}
|
|
|
|
public function setId(Uuid $uuid): self
|
|
{
|
|
$this->id = $uuid;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getType(): ?UserType
|
|
{
|
|
return $this->type;
|
|
}
|
|
|
|
public function setType(?UserType $type): User
|
|
{
|
|
$this->type = $type;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getEmail(): string
|
|
{
|
|
return $this->email;
|
|
}
|
|
|
|
public function setEmail(string $email): self
|
|
{
|
|
$this->email = $email;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isEmailConfirmed(): bool
|
|
{
|
|
return $this->emailConfirmed;
|
|
}
|
|
|
|
public function setEmailConfirmed(bool $emailConfirmed): User
|
|
{
|
|
$this->emailConfirmed = $emailConfirmed;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getLastname(): ?string
|
|
{
|
|
return $this->lastname;
|
|
}
|
|
|
|
public function setLastname(?string $lastname): User
|
|
{
|
|
$this->lastname = $lastname;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getFirstname(): ?string
|
|
{
|
|
return $this->firstname;
|
|
}
|
|
|
|
public function setFirstname(?string $firstname): User
|
|
{
|
|
$this->firstname = $firstname;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getName(): ?string
|
|
{
|
|
return $this->name;
|
|
}
|
|
|
|
public function setName(?string $name): User
|
|
{
|
|
$this->name = $name;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getPhoneNumber(): ?string
|
|
{
|
|
return $this->phoneNumber;
|
|
}
|
|
|
|
public function setPhoneNumber(?string $phoneNumber): void
|
|
{
|
|
$this->phoneNumber = $phoneNumber;
|
|
}
|
|
|
|
/**
|
|
* Transforms the user phone number string into a phone object.
|
|
*/
|
|
public function getPhone(): ?PhoneNumber
|
|
{
|
|
if (u($this->phoneNumber)->isEmpty()) {
|
|
return null;
|
|
}
|
|
\Webmozart\Assert\Assert::notEmpty($this->phoneNumber);
|
|
|
|
try {
|
|
return PhoneNumberUtil::getInstance()->parse($this->phoneNumber, PhoneNumberUtil::UNKNOWN_REGION);
|
|
} catch (\Exception) {
|
|
// wrong data in the database, then ignore and return null so a new number can be put
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public function setPhone(?PhoneNumber $phone): void
|
|
{
|
|
$this->phone = $phone;
|
|
}
|
|
|
|
public function getAvatar(): ?string
|
|
{
|
|
return $this->avatar;
|
|
}
|
|
|
|
public function setAvatar(?string $avatar): self
|
|
{
|
|
$this->avatar = $avatar;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isEnabled(): bool
|
|
{
|
|
return $this->enabled;
|
|
}
|
|
|
|
public function setEnabled(bool $enabled): self
|
|
{
|
|
$this->enabled = $enabled;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isMainAdminAccount(): bool
|
|
{
|
|
return $this->mainAdminAccount;
|
|
}
|
|
|
|
public function setMainAdminAccount(bool $mainAdminAccount): self
|
|
{
|
|
$this->mainAdminAccount = $mainAdminAccount;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isDevAccount(): bool
|
|
{
|
|
return $this->devAccount;
|
|
}
|
|
|
|
public function setDevAccount(bool $devAccount): User
|
|
{
|
|
$this->devAccount = $devAccount;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* A visual identifier that represents this user.
|
|
*
|
|
* @see UserInterface
|
|
*/
|
|
public function getUserIdentifier(): string
|
|
{
|
|
return $this->email;
|
|
}
|
|
|
|
/**
|
|
* @see UserInterface
|
|
*/
|
|
public function getRoles(): array
|
|
{
|
|
$roles = $this->roles;
|
|
// guarantee every user at least has ROLE_USER
|
|
$roles[] = self::ROLE_USER;
|
|
|
|
// add specific group roles
|
|
foreach ($this->userGroups as $userGroup) {
|
|
if ($userGroup->getMembership()->isAdmin()) {
|
|
$roles[] = self::ROLE_GROUP_ADMIN;
|
|
}
|
|
}
|
|
|
|
if ($this->isMembershipPaid()) {
|
|
$roles[] = self::MEMBERSHIP_PAID;
|
|
}
|
|
|
|
return array_unique($roles);
|
|
}
|
|
|
|
/**
|
|
* @param array<string> $roles
|
|
*/
|
|
public function setRoles(array $roles): self
|
|
{
|
|
$this->roles = $roles;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @see PasswordAuthenticatedUserInterface
|
|
*/
|
|
public function getPassword(): string
|
|
{
|
|
return (string) $this->password;
|
|
}
|
|
|
|
public function setPassword(?string $password): self
|
|
{
|
|
$this->password = $password;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getPlainPassword(): ?string
|
|
{
|
|
return $this->plainPassword;
|
|
}
|
|
|
|
public function setPlainPassword(?string $plainPassword): self
|
|
{
|
|
$this->plainPassword = $plainPassword;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getOldPassword(): ?string
|
|
{
|
|
return $this->oldPassword;
|
|
}
|
|
|
|
public function setOldPassword(?string $oldPassword): void
|
|
{
|
|
$this->oldPassword = $oldPassword;
|
|
}
|
|
|
|
public function getLoginAt(): ?\DateTimeInterface
|
|
{
|
|
return $this->loginAt;
|
|
}
|
|
|
|
public function setLoginAt(?\DateTimeInterface $loginAt): User
|
|
{
|
|
$this->loginAt = $loginAt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getSmsNotifications(): bool
|
|
{
|
|
return $this->smsNotifications;
|
|
}
|
|
|
|
public function setSmsNotifications(bool $smsNotifications): self
|
|
{
|
|
$this->smsNotifications = $smsNotifications;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function canBeNotifiedBySms(): bool
|
|
{
|
|
return $this->getSmsNotifications()
|
|
&& !u($this->phoneNumber)->isEmpty();
|
|
}
|
|
|
|
public function getSchedule(): ?string
|
|
{
|
|
return $this->schedule;
|
|
}
|
|
|
|
public function setSchedule(?string $schedule): void
|
|
{
|
|
$this->schedule = $schedule;
|
|
}
|
|
|
|
public function getCategory(): ?Category
|
|
{
|
|
return $this->category;
|
|
}
|
|
|
|
public function setCategory(?Category $category): void
|
|
{
|
|
$this->category = $category;
|
|
}
|
|
|
|
public function getDescription(): ?string
|
|
{
|
|
return $this->description;
|
|
}
|
|
|
|
public function setDescription(?string $description): void
|
|
{
|
|
$this->description = $description;
|
|
}
|
|
|
|
public function getVacationMode(): bool
|
|
{
|
|
return $this->vacationMode;
|
|
}
|
|
|
|
public function isInVacation(): bool
|
|
{
|
|
return $this->vacationMode;
|
|
}
|
|
|
|
public function setVacationMode(bool $vacationMode): void
|
|
{
|
|
$this->vacationMode = $vacationMode;
|
|
}
|
|
|
|
public function switchVacationMode(bool $vacationMode): void
|
|
{
|
|
$this->vacationMode = !$vacationMode;
|
|
}
|
|
|
|
/**
|
|
* @see UserInterface
|
|
*/
|
|
public function eraseCredentials(): void
|
|
{
|
|
// If you store any temporary, sensitive data on the user, clear it here
|
|
$this->plainPassword = null;
|
|
}
|
|
|
|
public function isAdmin(): bool
|
|
{
|
|
return $this->type === UserType::ADMIN;
|
|
}
|
|
|
|
public function isPlace(): bool
|
|
{
|
|
return $this->type === UserType::PLACE;
|
|
}
|
|
|
|
public function setStep2Defaults(): self
|
|
{
|
|
$this->type = UserType::USER;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getImage(): ?string
|
|
{
|
|
return $this->getAvatar();
|
|
}
|
|
|
|
public function getAddress(): ?Address
|
|
{
|
|
return $this->address;
|
|
}
|
|
|
|
public function hasAddress(): bool
|
|
{
|
|
return $this->address !== null;
|
|
}
|
|
|
|
public function setAddress(?Address $address): User
|
|
{
|
|
$this->address = $address;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, UserGroup>
|
|
*/
|
|
public function getUserGroups(): Collection
|
|
{
|
|
return $this->userGroups;
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, UserGroup>
|
|
*/
|
|
public function getUserGroupsConfirmed(): Collection
|
|
{
|
|
/** @var Collection<int, UserGroup> $collection */
|
|
$collection = $this->userGroups->filter(fn (UserGroup $userGroup) => !$userGroup->getMembership()->isInvited());
|
|
|
|
return $collection;
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, UserGroup>
|
|
*/
|
|
public function getUserGroupsConfirmedWithServices(): Collection
|
|
{
|
|
/** @var Collection<int, UserGroup> $collection */
|
|
$collection = $this->userGroups->filter(fn (UserGroup $userGroup) => !$userGroup->getMembership()->isInvited() && $userGroup->getGroup()->getServicesEnabled());
|
|
|
|
return $collection;
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
public function getUserGroupsIds(): array
|
|
{
|
|
return $this->getUserGroupsConfirmed()->map(fn (UserGroup $userGroup) => (string) $userGroup->getGroup()->getId())->toArray();
|
|
}
|
|
|
|
public function addUserGroup(UserGroup $userGroup): self
|
|
{
|
|
if (!$this->userGroups->contains($userGroup)) {
|
|
$this->userGroups->add($userGroup);
|
|
$userGroup->setUser($this);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function removeUserGroup(UserGroup $userGroup): self
|
|
{
|
|
$this->userGroups->removeElement($userGroup);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, Payment>
|
|
*/
|
|
public function getPayments(): Collection
|
|
{
|
|
return $this->payments;
|
|
}
|
|
|
|
/**
|
|
* @param Collection<int, Payment> $payments
|
|
*/
|
|
public function setPayments(Collection $payments): User
|
|
{
|
|
$this->payments = $payments;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isMembershipPaid(): bool
|
|
{
|
|
return $this->membershipPaid;
|
|
}
|
|
|
|
public function setMembershipPaid(bool $membershipPaid): self
|
|
{
|
|
$this->membershipPaid = $membershipPaid;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getStartAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->startAt;
|
|
}
|
|
|
|
public function setStartAt(?\DateTimeImmutable $startAt): self
|
|
{
|
|
$this->startAt = $startAt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getEndAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->endAt;
|
|
}
|
|
|
|
public function setEndAt(?\DateTimeImmutable $endAt): self
|
|
{
|
|
$this->endAt = $endAt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getPayedAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->payedAt;
|
|
}
|
|
|
|
public function setPayedAt(?\DateTimeImmutable $payedAt): self
|
|
{
|
|
$this->payedAt = $payedAt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
// —— end of basic 'etters —————————————————————————————————————————————————
|
|
|
|
public function promoteToAdmin(): self
|
|
{
|
|
$this->setRoles([self::ROLE_ADMIN]);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getDisplayName(): string
|
|
{
|
|
if ($this->isPlace()) {
|
|
$shortName = $this->getName();
|
|
} else {
|
|
$shortName = $this->getFirstname();
|
|
}
|
|
|
|
return (string) $shortName;
|
|
}
|
|
|
|
/**
|
|
* @return class-string
|
|
*/
|
|
public function getAdminCrudClass(): string
|
|
{
|
|
return match ($this->type) {
|
|
UserType::USER => UserCrudController::class,
|
|
UserType::ADMIN => AdministratorCrudController::class,
|
|
UserType::PLACE => PlaceCrudController::class,
|
|
default => throw new \LogicException('No type assigned to user yet.'),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get the list of groups the user belong to as a collection (with local cache).
|
|
*
|
|
* @return Collection<int,Group>
|
|
*/
|
|
public function getMyGroups(): Collection
|
|
{
|
|
if ($this->groups !== null) {
|
|
return $this->groups;
|
|
}
|
|
|
|
$this->groups = new ArrayCollection(
|
|
array_map(static fn (UserGroup $userGroup) => $userGroup->getGroup(), $this->userGroups->toArray())
|
|
);
|
|
|
|
return $this->groups;
|
|
}
|
|
|
|
/**
|
|
* Get the groups only where the user has the group admin role.
|
|
*
|
|
* @return Collection<int,Group>
|
|
*/
|
|
public function getMyGroupsAsAdmin(bool $enabledServices = false): Collection
|
|
{
|
|
$adminUserGroups = $this->userGroups->filter(
|
|
static fn (UserGroup $userGroup) => $userGroup->getMembership()->isAdmin() || $userGroup->isMainAdminAccount()
|
|
);
|
|
|
|
$groups = new ArrayCollection(
|
|
array_map(static fn (UserGroup $userGroup) => $userGroup->getGroup(), $adminUserGroups->toArray())
|
|
);
|
|
|
|
if ($enabledServices) {
|
|
return $groups->filter(
|
|
static fn (Group $group) => $group->getServicesEnabled()
|
|
);
|
|
}
|
|
|
|
return $groups;
|
|
}
|
|
|
|
/**
|
|
* Get the groups only where the user has invitations.
|
|
*
|
|
* @return Collection<int,UserGroup>
|
|
*/
|
|
public function getMyUserGroupsAsInvited(): Collection
|
|
{
|
|
/** @var Collection<int,UserGroup> $collection */
|
|
$collection = $this->userGroups->filter(
|
|
static fn (UserGroup $userGroup) => $userGroup->getMembership()->isInvited()
|
|
);
|
|
|
|
return $collection;
|
|
}
|
|
|
|
/**
|
|
* Get the groups only where the user is confirmed (member or admin).
|
|
*
|
|
* @return Collection<int,UserGroup>
|
|
*/
|
|
public function getMyUserGroupsAsConfirmed(): Collection
|
|
{
|
|
/** @var Collection<int,UserGroup> $collection */
|
|
$collection = $this->userGroups->filter(
|
|
static fn (UserGroup $userGroup) => $userGroup->getMembership()->isConfirmed()
|
|
);
|
|
|
|
return $collection;
|
|
}
|
|
|
|
/**
|
|
* Get the groups only where the user has invitations.
|
|
*
|
|
* @return Collection<int,Group>
|
|
*/
|
|
public function getMyGroupsAsInvited(): Collection
|
|
{
|
|
return new ArrayCollection(
|
|
array_map(static fn (UserGroup $userGroup) => $userGroup->getGroup(), $this->getMyUserGroupsAsInvited()->toArray())
|
|
);
|
|
}
|
|
|
|
/**
|
|
* The invitation status is excluded because, we are only member of the group
|
|
* once the invitation is accepted. We consider we are also a member even we
|
|
* are an admin of the group.
|
|
*/
|
|
public function isMemberOf(Group $group): bool
|
|
{
|
|
$notInvited = $this->userGroups->filter(
|
|
fn (UserGroup $userGroup) => $userGroup->getGroup() === $group && !$userGroup->getMembership()->isInvited()
|
|
);
|
|
|
|
return !$notInvited->isEmpty();
|
|
}
|
|
|
|
/**
|
|
* Tells if the user has already an association with the group whatever the
|
|
* membership status is.
|
|
*/
|
|
public function hasLink(Group $group): bool
|
|
{
|
|
return $this->getMyGroups()->contains($group);
|
|
}
|
|
|
|
/**
|
|
* Return the membership for a given group if it exists for the user, null otherwise.
|
|
* We can safely use the first() function here. Because of Doctrine constraints,
|
|
* it's impossible to have 2 records for the same group and user.
|
|
*
|
|
* @see UserGroup
|
|
*/
|
|
public function getGroupMembership(Group $group): ?UserGroup
|
|
{
|
|
/** @var Collection<int, UserGroup> $contextUserGroup */
|
|
$contextUserGroup = $this->userGroups->filter(
|
|
static fn (UserGroup $userGroup) => $userGroup->getGroup() === $group
|
|
);
|
|
|
|
return $contextUserGroup->isEmpty() ? null : $contextUserGroup->first();
|
|
}
|
|
|
|
public function isGroupAdmin(Group $group): bool
|
|
{
|
|
$groupAdmin = $group->getUserGroups()->filter(
|
|
fn (UserGroup $userGroup) => $userGroup->getUser()->getId() === $this->getId() && $userGroup->getMembership()->isAdmin()
|
|
);
|
|
|
|
return !$groupAdmin->isEmpty();
|
|
}
|
|
|
|
public function isIndexable(): bool
|
|
{
|
|
return !$this->isInVacation();
|
|
}
|
|
|
|
public function deleteAvatar(): self
|
|
{
|
|
$this->avatar = null;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function changePhoneNumber(?PhoneNumber $phone): self
|
|
{
|
|
if ($phone === null) {
|
|
$this->setPhoneNumber(null);
|
|
} else {
|
|
$this->setPhoneNumber('+'.$phone->getCountryCode().$phone->getNationalNumber());
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function expiresIn(): ?int
|
|
{
|
|
$today = Carbon::today();
|
|
if ($this->endAt === null || $this->endAt < $today) {
|
|
return null;
|
|
}
|
|
|
|
$endAt = new Carbon($this->endAt);
|
|
|
|
return $today->diffInDays($endAt);
|
|
}
|
|
|
|
public function getPlatformOffer(): ?PlatformOffer
|
|
{
|
|
return $this->platformOffer;
|
|
}
|
|
|
|
public function setPlatformOffer(?PlatformOffer $platformOffer): void
|
|
{
|
|
$this->platformOffer = $platformOffer;
|
|
}
|
|
|
|
public function isEqualTo(UserInterface $user): bool
|
|
{
|
|
if (!$user instanceof self) {
|
|
return false;
|
|
}
|
|
|
|
return $this->email === $user->getUserIdentifier() && $this->password === $user->getPassword();
|
|
}
|
|
}
|