mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
666 lines
15 KiB
PHP
666 lines
15 KiB
PHP
<?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\MainBundle\Entity;
|
|
|
|
use Chill\MainBundle\Entity\User\UserJobHistory;
|
|
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
|
use Doctrine\Common\Collections\Collection;
|
|
use Doctrine\Common\Collections\Criteria;
|
|
use Doctrine\Common\Collections\Selectable;
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
use libphonenumber\PhoneNumber;
|
|
use Symfony\Component\Security\Core\User\UserInterface;
|
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|
use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
|
|
|
|
/**
|
|
* User.
|
|
*
|
|
* @ORM\Entity
|
|
*
|
|
* @ORM\Table(name="users")
|
|
*
|
|
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
|
*
|
|
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
|
|
* "user": User::class
|
|
* })
|
|
*/
|
|
class User implements UserInterface, \Stringable
|
|
{
|
|
/**
|
|
* @ORM\Id
|
|
*
|
|
* @ORM\Column(name="id", type="integer")
|
|
*
|
|
* @ORM\GeneratedValue(strategy="AUTO")
|
|
*/
|
|
protected ?int $id = null;
|
|
|
|
/**
|
|
* @ORM\Column(type="datetime_immutable", nullable=true)
|
|
*/
|
|
private ?\DateTimeImmutable $absenceStart = null;
|
|
|
|
/**
|
|
* Array where SAML attributes's data are stored.
|
|
*
|
|
* @ORM\Column(type="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="string", length=150, nullable=true)
|
|
*/
|
|
private ?string $email = null;
|
|
|
|
/**
|
|
* @ORM\Column(
|
|
* type="string",
|
|
* length=150,
|
|
* nullable=true,
|
|
* unique=true)
|
|
*/
|
|
private ?string $emailCanonical = null;
|
|
|
|
/**
|
|
* @ORM\Column(type="boolean")
|
|
*/
|
|
private bool $enabled = true;
|
|
|
|
/**
|
|
* @var Collection<GroupCenter>
|
|
*
|
|
* @ORM\ManyToMany(
|
|
* targetEntity="Chill\MainBundle\Entity\GroupCenter",
|
|
* inversedBy="users")
|
|
*
|
|
* @ORM\Cache(usage="NONSTRICT_READ_WRITE")
|
|
*/
|
|
private Collection $groupCenters;
|
|
|
|
/**
|
|
* @ORM\Column(type="string", length=200)
|
|
*/
|
|
private string $label = '';
|
|
|
|
/**
|
|
* @ORM\Column(type="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<int, UserScopeHistory>
|
|
*
|
|
* @ORM\OneToMany(targetEntity=UserScopeHistory::class,
|
|
* mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
|
|
*/
|
|
private Collection&Selectable $scopeHistories;
|
|
|
|
/**
|
|
* @ORM\Column(type="string", length=255)
|
|
*/
|
|
private string $password = '';
|
|
|
|
/**
|
|
* @internal must be set to null if we use bcrypt
|
|
*
|
|
* @ORM\Column(type="string", length=255, nullable=true)
|
|
*/
|
|
private ?string $salt = null;
|
|
|
|
/**
|
|
* @var Collection&Selectable<int, UserJobHistory>
|
|
*
|
|
* @ORM\OneToMany(targetEntity=UserJobHistory::class,
|
|
* mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
|
|
*/
|
|
private Collection&Selectable $jobHistories;
|
|
|
|
/**
|
|
* @ORM\Column(type="string", length=80)
|
|
*/
|
|
private string $username = '';
|
|
|
|
/**
|
|
* @ORM\Column(
|
|
* type="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 \Chill\MainBundle\Entity\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<GroupCenter>
|
|
*/
|
|
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 $at = null): ?Scope
|
|
{
|
|
$at ??= new \DateTimeImmutable('now');
|
|
|
|
foreach ($this->scopeHistories as $scopeHistory) {
|
|
if ($at >= $scopeHistory->getStartDate() && (
|
|
null === $scopeHistory->getEndDate() || $at < $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);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getPassword()
|
|
{
|
|
return $this->password;
|
|
}
|
|
|
|
public function getRoles(): array
|
|
{
|
|
return ['ROLE_USER'];
|
|
}
|
|
|
|
public function getSalt(): ?string
|
|
{
|
|
return $this->salt;
|
|
}
|
|
|
|
public function getUserJob(\DateTimeImmutable $at = null): ?UserJob
|
|
{
|
|
$at ??= new \DateTimeImmutable('now');
|
|
|
|
foreach ($this->jobHistories as $jobHistory) {
|
|
if ($at >= $jobHistory->getStartDate() && (
|
|
null === $jobHistory->getEndDate() || $at < $jobHistory->getEndDate()
|
|
)) {
|
|
return $jobHistory->getJob();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function getUserJobHistories(): Collection
|
|
{
|
|
return $this->jobHistories;
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
}
|
|
|
|
/**
|
|
* @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($email)
|
|
{
|
|
$this->email = $email;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setEmailCanonical($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' => Criteria::ASC, 'id' => Criteria::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($password)
|
|
{
|
|
$this->password = $password;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setSalt($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' => Criteria::ASC, 'id' => Criteria::ASC]);
|
|
|
|
/** @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($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;
|
|
}
|
|
}
|