<?php
declare(strict_types=1);
namespace App\Entity\User;
use App\Entity\Core\Institution;
use App\Entity\Core\SelfStudy\SelfStudy;
use App\Repository\User\UserRepository;
use App\Services\User\Canonicalizer;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
use JsonSerializable;
use Scheb\TwoFactorBundle\Model\Totp\TotpConfiguration;
use Scheb\TwoFactorBundle\Model\Totp\TotpConfigurationInterface;
use Scheb\TwoFactorBundle\Model\Totp\TwoFactorInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Table(name: 'user')]
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[UniqueEntity(fields: ['username'], message: 'user.username.taken')]
class User implements JsonSerializable, TwoFactorInterface, UserInterface, PasswordAuthenticatedUserInterface
{
public const string role_admin = 'ROLE_ADMIN';
public const string role_student = 'ROLE_STUDENT';
public const string role_teacher = 'ROLE_TEACHER';
#[ORM\Column(name: 'password', type: 'string', length: 255, nullable: true)]
protected ?string $password = null;
#[ORM\Column(name: 'email', type: 'string', length: 255, unique: false, nullable: true)]
protected ?string $email = null;
#[ORM\Column(name: 'salt', type: 'string', length: 255, nullable: true)]
protected ?string $salt = null;
#[ORM\Column(name: 'email_canonical', type: 'string', length: 255, unique: false, nullable: true)]
protected string $emailCanonical;
#[ORM\Id]
#[ORM\Column(type: 'integer')]
#[ORM\GeneratedValue(strategy: 'AUTO')]
protected int $id;
#[ORM\Column(name: 'first_name', type: 'string', length: 255)]
protected string $firstName;
#[ORM\Column(name: 'surname', type: 'string', length: 255)]
protected string $surname;
#[ORM\JoinColumn(name: 'institution', referencedColumnName: 'id', onDelete: 'CASCADE', nullable: true)]
#[ORM\ManyToOne(targetEntity: Institution::class, cascade: ['persist'])]
protected ?Institution $institution = null;
#[ORM\Column(name: 'nickname', type: 'string', length: 255, nullable: true)]
protected string $nickname;
#[ORM\Column(name: 'first_login', type: 'boolean', options: ['default' => true])]
protected bool $firstLogin = true;
#[ORM\Column(name: 'is_subscribed', type: 'boolean', options: ['default' => false])]
protected bool $isSubscribed = false;
#[ORM\Column(name: 'is_demo', type: 'boolean', options: ['default' => false])]
protected bool $isDemo = false;
#[ORM\Column(name: 'desired_grade', type: 'smallint', nullable: true)]
private int $desiredGrade;
#[ORM\Column(name: 'class_level', type: 'smallint', nullable: true)]
private ?int $classLevel = null;
#[ORM\Column(name: 'self_study_level_adjustment', type: 'smallint', options: ['default' => 0])]
private int $selfStudyLevelAdjustment = 0;
#[ORM\Column(type: 'string', nullable: true)]
private ?string $userAgent = null;
#[ORM\Column(name: 'show_audio', type: 'boolean', options: ['default' => false], nullable: true)]
private ?bool $showAudio = null;
#[ORM\Column(name: 'classroom_overview', type: 'boolean', options: ['default' => true], nullable: true)]
private ?bool $classroomOverview = null;
#[ORM\Column(name: 'expiration_basis', type: 'datetime', nullable: true)]
private ?DateTime $expirationBasis;
#[ORM\Column(name: 'expiration_self_study', type: 'datetime', nullable: true)]
private ?DateTime $expirationSelfStudy;
#[ORM\Column(name: 'expiration_textbook', type: 'datetime', nullable: true)]
private ?DateTime $expirationTextbook;
#[ORM\Column(name: 'last_login_with_unilogin', type: 'datetime', nullable: true)]
private ?DateTime $lastLoginWithUnilogin; // note that the last_login in the database is handled by FOSUserBundle and will be removed
#[ORM\JoinColumn(name: 'selected_self_study', referencedColumnName: 'id', nullable: true)]
#[ORM\ManyToOne(targetEntity: SelfStudy::class)]
private ?SelfStudy $selectedSelfStudy = null;
#[ORM\Column(name: 'ekeys_user_id', type: 'string', nullable: true)]
private ?string $ekeysUserId;
#[ORM\Column(name: 'totp_secret', type: 'string', nullable: true)]
private ?string $totpSecret;
#[ORM\Column(name: 'totp_validated', type: 'boolean', options: ['default' => false])]
private bool $totpValidated = false;
#[ORM\Column(name: 'class_name', type: 'string', nullable: true)]
private ?string $className;
#[ORM\Column(name: 'username', type: 'string', length: 180, nullable: false)]
protected string $username;
#[ORM\Column(name: 'username_canonical', type: 'string', length: 180, nullable: false, unique: true)]
protected string $usernameCanonical;
#[ORM\Column(name: 'enabled', type: 'boolean', nullable: false)]
protected bool $enabled = false;
#[ORM\Column(name: 'roles', type: 'array', nullable: false)]
protected array $roles = [];
#[ORM\Column(name: 'last_login', type: 'datetime', nullable: true)]
private ?DateTime $lastLogin;
public function setUsername(string $username): User
{
$this->username = $username;
$this->usernameCanonical = Canonicalizer::canonicalize($username);
return $this;
}
public function getClassName(): ?string
{
return $this->className;
}
public function setClassName(?string $className): void
{
$this->className = $className;
}
public function setInstitution(?Institution $institution): User
{
$this->institution = $institution;
return $this;
}
public function getInstitution(): ?Institution
{
return $this->institution;
}
public function getEkeysUserId(): ?string
{
return $this->ekeysUserId;
}
public function setEkeysUserId(?string $ekeysUserId): void
{
$this->ekeysUserId = $ekeysUserId;
}
public function getClassLevelName(?bool $isSelfStudy = false): string {
$levelName = '';
$classLevel = $isSelfStudy ? $this->getSelfStudyLevel() : $this->getClassLevel();
if ($classLevel <= 3) {
$levelName = 'indskoling';
}
elseif ($classLevel >= 4 && $classLevel <= 6) {
$levelName = 'mellemtrin';
}
elseif ($classLevel >= 7) {
$levelName = 'udskoling';
}
return $levelName;
}
public function getNickname(): string
{
if (!empty($this->nickname)) {
return $this->nickname;
}
if (!empty($this->getFirstName()) && !empty($this->getSurname())) {
return $this->getFirstName() . ' ' . $this->getSurname();
}
$nickname = $this->getFirstName();
if ($surname = $this->getSurname()) {
preg_match_all('/(?<=\s|^)[a-z]/i', $surname, $matches);
$nickname .= implode('', $matches[0]);
}
return $nickname;
}
public function setNickname($nickname = null): User
{
if (!empty($nickname)) {
$this->nickname = $nickname;
return $this;
}
if (!empty($this->getFirstName()) && !empty($this->getSurname())) {
$this->nickname = $this->getFirstName() . ' ' . $this->getSurname();
return $this;
}
$nickname = $this->getFirstName();
if ($surname = $this->getSurname()) {
preg_match_all('/(?<=\s|^)[a-z]/i', $surname, $matches);
$nickname .= implode('', $matches[0]);
}
$this->nickname = $nickname;
return $this;
}
public function getFirstLogin(): bool
{
return $this->firstLogin;
}
public function setFirstLogin(bool $firstLogin): User
{
$this->firstLogin = $firstLogin;
return $this;
}
public function getId(): int
{
return $this->id;
}
public function setId(int $id): void
{
$this->id = $id;
}
public function getSurname(): string
{
return $this->surname;
}
public function setSurname(string $surname): User
{
$this->surname = $surname;
return $this;
}
public function getIsSubscribed(): bool
{
return $this->isSubscribed;
}
public function setIsSubscribed(bool $isSubscribed): void
{
$this->isSubscribed = $isSubscribed;
}
public function getFirstName(): string
{
return $this->firstName;
}
public function setFirstName(string $firstName): User
{
$this->firstName = $firstName;
return $this;
}
public function isDemo(): bool
{
return $this->isDemo;
}
public function setIsDemo(bool $isDemo): void
{
$this->isDemo = $isDemo;
}
public function isDebugUser(): bool
{
return $this->getUsername() === "teacher1_demo";
}
public function getDesiredGrade(): ?int
{
return $this->desiredGrade;
}
public function setDesiredGrade(?int $desiredGrade): void
{
$this->desiredGrade = $desiredGrade;
}
public function getSelfStudyLevelAdjustment(): int
{
return $this->selfStudyLevelAdjustment;
}
public function setSelfStudyLevelAdjustment(int $levelAdjustment)
{
$this->selfStudyLevelAdjustment = $levelAdjustment;
}
public function getSelfStudyLevel(): ?int
{
return $this->getClassLevel() === null ? null : $this->getClassLevel() + $this->getSelfStudyLevelAdjustment();
}
public function getUserAgent(): ?string
{
return $this->userAgent;
}
public function setUserAgent(string $userAgent)
{
$this->userAgent = $userAgent;
}
public function getFullName(): string
{
return ucwords($this->firstName) . ' ' . ucwords($this->surname);
}
public function getCompactName(): string
{
return ucwords($this->firstName) . ' ' . strtoupper(substr($this->surname, 0, 2));
}
public function getClassLevel(): ?int
{
return $this->classLevel;
}
public function setClassLevel(?int $classLevel): void
{
$this->classLevel = $classLevel;
}
public function isShowAudio(): ?bool
{
return $this->showAudio;
}
public function setShowAudio(bool $showAudio): void
{
$this->showAudio = $showAudio;
}
public function getClassroomOverview(): ?bool
{
return $this->classroomOverview;
}
public function setClassroomOverview(bool $classroomOverview): void
{
$this->classroomOverview = $classroomOverview;
}
public function getExpirationBasis(): ?DateTime
{
return $this->expirationBasis;
}
public function setExpirationBasis(?DateTime $expirationBasis): void
{
$this->expirationBasis = $expirationBasis;
}
public function getExpirationSelfStudy(): ?DateTime
{
return $this->expirationSelfStudy;
}
public function setExpirationSelfStudy(?DateTime $expirationSelfStudy): void
{
$this->expirationSelfStudy = $expirationSelfStudy;
}
public function getExpirationTextbook(): ?DateTime
{
return $this->expirationTextbook;
}
public function setExpirationTextbook(?DateTime $expirationTextbook): void
{
$this->expirationTextbook = $expirationTextbook;
}
public function jsonSerialize(): mixed
{
return [
"id" => $this->getId(),
"name" => $this->getFullName(),
"classLevel" => $this->getClassLevel()
];
}
public function getLastLoginWithUnilogin(): ?DateTime
{
return $this->lastLoginWithUnilogin;
}
public function getLastLogin(): ?DateTime
{
return $this->lastLogin;
}
public function setLastLogin(?DateTime $time = null)
{
$this->lastLogin = $time;
return $this;
}
public function setEnabled($boolean): User
{
$this->enabled = (bool) $boolean;
return $this;
}
public function setLastLoginWithUnilogin(DateTime $lastLoginWithUnilogin): void
{
$this->lastLoginWithUnilogin = $lastLoginWithUnilogin;
}
public function getSelectedSelfStudy(): ?SelfStudy
{
return $this->selectedSelfStudy;
}
public function setSelectedSelfStudy(SelfStudy $selectedSelfStudy): void
{
$this->selectedSelfStudy = $selectedSelfStudy;
}
public function setTotpSecret(string $secret): void
{
$this->totpSecret = $secret;
}
public function isTotpAuthenticationEnabled(): bool
{
return $this->totpValidated;
}
public function getTotpAuthenticationUsername(): string
{
return $this->username;
}
public function getTotpAuthenticationConfiguration(): ?TotpConfigurationInterface
{
// SHA1, 30 seconds and 6 digits is will make it work with Google authenticator and equivalent.
return new TotpConfiguration($this->totpSecret, TotpConfiguration::ALGORITHM_SHA1, 30, 6);
}
public function isTotpValidated(): bool
{
return $this->totpValidated;
}
public function setTotpValidated(bool $totpValidated): void
{
$this->totpValidated = $totpValidated;
}
public function getRoles(): array
{
return $this->roles;
}
public function getPassword(): ?string
{
return $this->password;
}
public function getSalt(): ?string
{
return $this->salt;
}
public function eraseCredentials(): void
{
}
public function getUsername(): string
{
return $this->username;
}
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Avoid deprecation warning
*/
public function getUserIdentifier(): string
{
return $this->username;
}
public function setRoles(array $roles): User
{
$this->roles = [];
foreach ($roles as $role) {
$this->addRole($role);
}
return $this;
}
public function addRole(string $role): void
{
$role = strtoupper($role);
if (!in_array($role, $this->roles, true)) {
$this->roles[] = $role;
}
}
public function removeRole(string $role): void
{
if (false !== $key = array_search(strtoupper($role), $this->roles, true)) {
unset($this->roles[$key]);
$this->roles = array_values($this->roles);
}
}
public function hasRole(string $role): bool
{
return in_array(strtoupper($role), $this->getRoles(), true);
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): void
{
$this->email = $email;
$this->emailCanonical = Canonicalizer::canonicalize($email);
}
public function setSalt(string $salt): void
{
$this->salt = $salt;
}
public function setPassword(string $password): void
{
$this->password = $password;
}
public function __toString(): string
{
return $this->getUsername();
}
}