User::class])] #[ORM\Entity] #[ORM\Cache(usage: 'NONSTRICT_READ_WRITE', region: 'acl_cache_region')] #[ORM\Table(name: 'users')] class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInterface { #[ORM\Id] #[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)] #[ORM\GeneratedValue(strategy: 'AUTO')] protected ?int $id = null; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true)] private ?\DateTimeImmutable $absenceStart = null; /** * Array where SAML attributes's data are stored. */ #[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false)] private array $attributes = []; #[ORM\ManyToOne(targetEntity: Civility::class)] private ?Civility $civility = null; #[ORM\ManyToOne(targetEntity: Location::class)] private ?Location $currentLocation = null; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 150, nullable: true)] private ?string $email = null; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 150, nullable: true, unique: true)] private ?string $emailCanonical = null; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN)] private bool $enabled = true; /** * @var Collection */ #[ORM\ManyToMany(targetEntity: GroupCenter::class, inversedBy: 'users')] #[ORM\Cache(usage: 'NONSTRICT_READ_WRITE')] private Collection $groupCenters; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 200)] private string $label = ''; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN)] // sf4 check: in yml was false by default !? private bool $locked = true; #[ORM\ManyToOne(targetEntity: Center::class)] private ?Center $mainCenter = null; #[ORM\ManyToOne(targetEntity: Location::class)] private ?Location $mainLocation = null; /** * @var Collection&Selectable */ #[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)] 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)] private ?string $salt = null; /** * @var Collection&Selectable */ #[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)] private string $username = ''; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 80, unique: true, nullable: true)] private ?string $usernameCanonical = null; /** * The user's mobile phone number. */ #[ORM\Column(type: 'phone_number', nullable: true)] #[PhonenumberConstraint] private ?PhoneNumber $phonenumber = null; /** * User constructor. */ public function __construct() { $this->groupCenters = new ArrayCollection(); $this->scopeHistories = new ArrayCollection(); $this->jobHistories = new ArrayCollection(); } public function __toString(): string { return $this->getLabel(); } /** * @return User */ public function addGroupCenter(GroupCenter $groupCenter) { $this->groupCenters->add($groupCenter); return $this; } public function eraseCredentials() {} public function getAbsenceStart(): ?\DateTimeImmutable { return $this->absenceStart; } /** * Get attributes. * * @return array */ public function getAttributes() { if (null === $this->attributes) { $this->attributes = []; } return $this->attributes; } public function getCivility(): ?Civility { return $this->civility; } public function getCurrentLocation(): ?Location { return $this->currentLocation; } public function getEmail(): ?string { return $this->email; } /** * @return string */ public function getEmailCanonical() { return $this->emailCanonical; } /** * @return Collection */ public function getGroupCenters(): Collection { return $this->groupCenters; } /** * Get id. */ public function getId(): ?int { return $this->id; } public function getLabel(): string { return $this->label; } public function getMainCenter(): ?Center { return $this->mainCenter; } public function getMainLocation(): ?Location { return $this->mainLocation; } public function getMainScope(?\DateTimeImmutable $atDate = null): ?Scope { $atDate ??= new \DateTimeImmutable('now'); foreach ($this->scopeHistories as $scopeHistory) { if ($atDate >= $scopeHistory->getStartDate() && ( null === $scopeHistory->getEndDate() || $atDate < $scopeHistory->getEndDate() )) { return $scopeHistory->getScope(); } } return null; } public function getMainScopeHistories(): Collection { return $this->scopeHistories; } /** * @return ArrayCollection|UserScopeHistory[] */ public function getMainScopeHistoriesOrdered(): ArrayCollection { $scopeHistories = $this->getMainScopeHistories(); $sortedScopeHistories = $scopeHistories->toArray(); usort($sortedScopeHistories, fn ($a, $b) => $a->getStartDate() < $b->getStartDate() ? 1 : -1); return new ArrayCollection($sortedScopeHistories); } public function getPassword(): ?string { return $this->password; } public function getRoles(): array { return ['ROLE_USER']; } public function getSalt(): ?string { return $this->salt; } public function getUserJob(?\DateTimeImmutable $atDate = null): ?UserJob { $atDate ??= new \DateTimeImmutable('now'); foreach ($this->jobHistories as $jobHistory) { if ($atDate >= $jobHistory->getStartDate() && ( null === $jobHistory->getEndDate() || $atDate < $jobHistory->getEndDate() )) { return $jobHistory->getJob(); } } return null; } public function getUserJobHistories(): Collection { return $this->jobHistories; } public function getUserScopeHistories(): Collection { return $this->scopeHistories; } /** * @return ArrayCollection|UserJobHistory[] */ public function getUserJobHistoriesOrdered(): ArrayCollection { $jobHistories = $this->getUserJobHistories(); $sortedJobHistories = $jobHistories->toArray(); usort($sortedJobHistories, fn ($a, $b) => $a->getStartDate() < $b->getStartDate() ? 1 : -1); return new ArrayCollection($sortedJobHistories); } /** * @return string */ public function getUsername() { return $this->username; } public function getUserIdentifier(): string { return $this->username; } /** * @return string */ public function getUsernameCanonical() { return $this->usernameCanonical; } public function isAbsent(): bool { return null !== $this->getAbsenceStart() && $this->getAbsenceStart() <= new \DateTimeImmutable('now'); } /** * @return bool */ public function isAccountNonExpired() { return true; } /** * @return bool */ public function isAccountNonLocked() { return $this->locked; } /** * @return bool */ public function isCredentialsNonExpired() { return true; } /** * @return bool */ public function isEnabled() { return $this->enabled; } /** * This function check that groupCenter are present only once. The validator * use this function to avoid a user to be associated to the same groupCenter * more than once. */ public function isGroupCenterPresentOnce(ExecutionContextInterface $context) { $groupCentersIds = []; foreach ($this->getGroupCenters() as $groupCenter) { if (\in_array($groupCenter->getId(), $groupCentersIds, true)) { $context->buildViolation('The user has already those permissions') ->addViolation(); } else { $groupCentersIds[] = $groupCenter->getId(); } } } public function getPhonenumber(): ?PhoneNumber { return $this->phonenumber; } /** * @throws \RuntimeException if the groupCenter is not in the collection */ public function removeGroupCenter(GroupCenter $groupCenter) { if (false === $this->groupCenters->removeElement($groupCenter)) { throw new \RuntimeException('The groupCenter could not be removed, it seems not to be associated with the user. Aborting.'); } } public function setAbsenceStart(?\DateTimeImmutable $absenceStart): void { $this->absenceStart = $absenceStart; } public function setAttributeByDomain(string $domain, string $key, $value): self { $this->attributes[$domain][$key] = $value; return $this; } /** * Merge the attributes with existing attributes. * * Only the key provided will be created or updated. For a two-level array, use @see{User::setAttributeByDomain} */ public function setAttributes(array $attributes): self { $this->attributes = array_merge($this->attributes, $attributes); return $this; } public function setCivility(?Civility $civility): User { $this->civility = $civility; return $this; } public function setCurrentLocation(?Location $currentLocation): User { $this->currentLocation = $currentLocation; return $this; } /** * @return $this */ public function setEmail(?string $email) { $this->email = $email; return $this; } /** * @return $this */ public function setEmailCanonical(?string $emailCanonical) { $this->emailCanonical = $emailCanonical; return $this; } public function setEnabled(bool $enabled) { $this->enabled = $enabled; return $this; } public function setLabel(string $label): User { $this->label = $label; return $this; } public function setMainCenter(?Center $mainCenter): User { $this->mainCenter = $mainCenter; return $this; } public function setMainLocation(?Location $mainLocation): User { $this->mainLocation = $mainLocation; return $this; } public function setMainScope(?Scope $mainScope): User { if ($mainScope === $this->getMainScope()) { return $this; } $newScope = new UserScopeHistory(); $newScope ->setStartDate(new \DateTimeImmutable('now')) ->setScope($mainScope) ->setUser($this); $this->scopeHistories[] = $newScope; $criteria = new Criteria(); $criteria->orderBy(['startDate' => 'ASC', 'id' => 'ASC']); /** @var \Iterator $scopes */ $scopes = $this->scopeHistories->matching($criteria)->getIterator(); $scopes->rewind(); do { /** @var UserScopeHistory $current */ $current = $scopes->current(); $scopes->next(); if ($scopes->valid()) { $next = $scopes->current(); $current->setEndDate($next->getStartDate()); } } while ($scopes->valid()); return $this; } /** * @return $this */ public function setPassword(string $password) { $this->password = $password; return $this; } /** * @return $this */ public function setSalt(?string $salt) { $this->salt = $salt; return $this; } public function setUserJob(?UserJob $userJob): User { if ($userJob === $this->getUserJob()) { return $this; } $newJob = new UserJobHistory(); $newJob ->setStartDate(new \DateTimeImmutable('now')) ->setJob($userJob) ->setUser($this); $this->jobHistories[] = $newJob; $criteria = new Criteria(); $criteria->orderBy(['startDate' => \Doctrine\Common\Collections\Order::Ascending, 'id' => \Doctrine\Common\Collections\Order::Ascending]); /** @var \Iterator $jobs */ $jobs = $this->jobHistories->matching($criteria)->getIterator(); $jobs->rewind(); do { /** @var UserJobHistory $current */ $current = $jobs->current(); $jobs->next(); if ($jobs->valid()) { $next = $jobs->current(); $current->setEndDate($next->getStartDate()); } } while ($jobs->valid()); return $this; } /** * Set username. * * @return User */ public function setUsername(?string $name) { $this->username = (string) $name; if ('' === trim($this->getLabel())) { $this->setLabel($name); } return $this; } /** * @return $this */ public function setUsernameCanonical(?string $usernameCanonical) { $this->usernameCanonical = $usernameCanonical; return $this; } public function unsetAttribute($key): self { unset($this->attributes[$key]); return $this; } public function setPhonenumber(?PhoneNumber $phonenumber): self { $this->phonenumber = $phonenumber; return $this; } }