<?php
namespace Whater\Domain\User\Model;
use BornFree\TacticianDomainEvent\Recorder\ContainsRecordedEvents;
use BornFree\TacticianDomainEvent\Recorder\EventRecorderCapabilities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Ramsey\Uuid\Uuid;
use Whater\Domain\Common\Exception\InvalidUUIDException;
use Whater\Domain\Security\ValueObject\AuthUser;
use Whater\Domain\Security\ValueObject\EncodedPasswordInterface;
use Assert\Assertion;
use Whater\Infrastructure\SecurityBundle\ValueObject\EncodedPassword;
use Whater\Domain\Media\Model\Media;
use Whater\Domain\Product\Model\Product;
use Whater\Domain\Product\Model\WalletOperation;
use Whater\Domain\User\Exception\IllegalPermissionObjectException;
use Whater\Domain\User\Exception\InvalidPermissionException;
use Whater\Domain\User\Exception\UserOrganizationAlreadyExistsException;
use Whater\Domain\Whater\Model\DistributionNetwork;
use Whater\Domain\Whater\Model\DistributionNetworkValoration;
use Whater\Domain\Whater\Model\WhaterOrganization;
use Whater\Domain\Whater\Model\WhaterPoint;
use Whater\Domain\Whater\Model\WhaterValoration;
use Whater\Domain\Zones\Model\CountryArea;
use Whater\Domain\Zones\Model\Town;
use Whater\Domain\Zones\Model\TownLocation;
use Whater\Domain\Zones\Model\Ubication;
/**
* Class User
*
* @package Whater\Domain\User\Model
*/
class User implements ContainsRecordedEvents
{
use EventRecorderCapabilities;
const DEFAULT_ROLES = [
Role::ROLE_USER
];
/**
* @var string
*/
private $uuid;
/**
* @var string
*/
private $email;
/**
* @var AuthUser
*/
private $auth;
/**
* @var \DateTime
*/
private $createdAt;
/**
* @var null|\DateTime
*/
private $updatedAt;
/**
* @var Collection
*/
private $roles;
/**
* @var \Whater\Domain\User\Model\Role
*/
private $defaultRole;
/**
* @var \Whater\Domain\User\Model\Role
*/
private $activeRole;
/**
* @var bool
*/
private $isEnabled;
/**
* @var string
*/
private $firstName;
/**
* @var string
*/
private $lastName;
/**
* @var string
*/
private $phone;
/**
* @var boolean
*/
private $isDeleted;
/**
* @var string
*/
private $plainPassword;
/**
* @var string
*/
private $resetTokenHash;
/**
* @var \DateTime
*/
private $resetTokenExpireAt;
/**
* @var Collection
*/
private $organizationLicenses;
/**
* @var Collection
*/
private $valorationsOfWhaterPoints;
/**
* @var Collection
*/
private $valorationsOfDistributionNetworks;
/**
* @var Collection
*/
private $productValorations;
/**
* @var Collection
*/
private $cartOrders;
/**
* @var Collection
*/
private $buyerWalletOperations;
/**
* @var Collection
*/
private $createdWhaterPoints;
/**
* @var Collection
*/
private $uploadedImportDataFiles;
/**
* @var Town
*/
private $town;
/**
* @var Media
*/
private $avatarImage;
/**
* @var string
*/
private $publicComments;
/**
* @var string
*/
private $privateComments;
/**
* @var WhaterOrganization
*/
private $whaterOrganization;
/**
* @var Collection
*/
private $myLocations;
/**
* @var Collection
*/
private $myEstablishments;
/**
* @var Collection
*/
private $notifications;
/**
* @var float
*/
private $currentBalance;
/**
* @var Collection
*/
private $ownershipRequests;
/**
* @var string
*/
private $stripeCustomerId;
/**
* @var string
*/
private $referralCodeUsed;
/**
* @var WhaterOrganization
*/
private $referralWhaterOrganization;
/**
* @var Collection
*/
private $whatercoinsCoupons;
/**
* User constructor.
* @param string|null $userId
* @param string $username
* @param string $email
* @param null|EncodedPasswordInterface|null $encodedPassword
* @param bool $enabled
*/
public function __construct(
string $userId = null,
string $username,
string $email,
?EncodedPasswordInterface $encodedPassword = null,
bool $isEnabled = true,
string $publicComments = null,
string $privateComments = null,
Media $avatarImage = null,
string $referralCodeUsed = null
) {
try {
$this->uuid = Uuid::fromString($userId ?: Uuid::uuid4())->toString();
} catch (\InvalidArgumentException $e) {
throw new InvalidUUIDException();
}
if ($encodedPassword == null) {
$this->plainPassword = $this->generatePlainPassword();
$encodedPassword = new EncodedPassword($this->plainPassword);
}
$this->auth = new AuthUser($username, $encodedPassword);
$this->setEmail($email);
$this->avatarImage = $avatarImage;
$this->publicComments = $publicComments;
$this->privateComments = $privateComments;
$this->createdAt = new \DateTime();
$this->updatedAt = new \DateTime();
$this->roles = new ArrayCollection();
$this->cartOrders = new ArrayCollection();
$this->organizationLicenses = new ArrayCollection();
$this->valorationsOfWhaterPoints = new ArrayCollection();
$this->valorationsOfDistributionNetworks = new ArrayCollection();
$this->productValorations = new ArrayCollection();
$this->buyerWalletOperations = new ArrayCollection();
$this->createdWhaterPoints = new ArrayCollection();
$this->uploadedImportDataFiles = new ArrayCollection();
$this->whatercoinsCoupons = new ArrayCollection();
$this->isEnabled = $isEnabled;
$this->isDeleted = false;
$this->currentBalance = 0;
$this->myLocations = new ArrayCollection();
$this->myEstablishments = new ArrayCollection();
$this->notifications = new ArrayCollection();
$this->ownershipRequests = new ArrayCollection();
$this->referralCodeUsed = $referralCodeUsed;
}
public function __toString()
{
return $this->fullUsername(true);
}
public function fullUsername(?bool $showusername = false): string
{
$fullname = $this->firstName . ' ' . $this->lastName;
if (trim($fullname) == '') {
return $this->username();
}
if ($showusername) {
$fullname .= ' (' . $this->username() . ')';
}
return $fullname;
}
public function registerReferralWhaterOrganization(
WhaterOrganization $whaterOrganization = null
) {
$this->referralWhaterOrganization = $whaterOrganization;
}
/**
* Update User
*
* @param null|string $email
* @param EncodedPasswordInterface|null $encodedPassword
* @param null|string $firstName
* @param null|string $lastName
* @param null|string $phone
*
* @return $this
*/
public function updateUser(
?string $email = '',
?EncodedPasswordInterface $encodedPassword = null,
?string $firstName = '',
?string $lastName = '',
?string $phone = '',
?string $publicComments = '',
?string $privateComments = '',
?Media $avatarImage = null
) {
if (!empty($email)) {
$this->setEmail($email);
}
if (!empty($encodedPassword)) {
$this->auth = new AuthUser($this->email(), $encodedPassword);
}
if (!empty($firstName)) {
$this->firstName = $firstName;
}
if (!empty($lastName)) {
$this->lastName = $lastName;
}
if (!empty($phone)) {
$this->phone = $phone;
}
if (!empty($publicComments)) {
$this->publicComments = $publicComments;
}
if (!empty($privateComments)) {
$this->privateComments = $privateComments;
}
if (!empty($avatarImage)) {
$this->avatarImage = $avatarImage;
}
$this->updatedAt = new \DateTime();
return $this;
}
public function updateUserTown(
Town $town
) {
$this->town = $town;
$this->updatedAt = new \DateTime();
return $this;
}
public function updateOrganization(
WhaterOrganization $whaterOrganization = null
) {
if ($whaterOrganization == null) {
$this->whaterOrganization = null;
$this->updatedAt = new \DateTime();
} else if ($this->whaterOrganization() == null) {
$this->whaterOrganization = $whaterOrganization;
$this->updatedAt = new \DateTime();
} else {
throw new UserOrganizationAlreadyExistsException();
}
return $this;
}
public function hasActiveLicense(string $licenseScope, string $licenseType = null): bool
{
if ($this->hasRole(Role::ROLE_ADMIN)) {
return true;
}
if ($this->whaterOrganization() != null && $this->whaterOrganization()->hasActiveLicense($licenseScope, $licenseType)) {
return true;
}
return false;
}
public function checkPermission(string $permissionCode, $object = null)
{
switch ($permissionCode) {
case UserPermission::PERMISSION_CREATE_PRODUCT:
if ($object != null) {
throw new IllegalPermissionObjectException();
}
if ($this->hasActiveLicense(OrganizationLicense::LICENSE_SCOPE_SELLERS)) {
return true;
}
break;
case UserPermission::PERMISSION_EDIT_PRODUCT:
if ($object == null || !($object instanceof Product)) {
throw new IllegalPermissionObjectException();
}
if ($this->hasActiveLicense(OrganizationLicense::LICENSE_SCOPE_SELLERS)) {
return true;
}
break;
case UserPermission::PERMISSION_EDIT_WHATER_POINT:
if ($object == null || !($object instanceof WhaterPoint)) {
throw new IllegalPermissionObjectException();
}
/** @var WhaterPoint $whaterpoint */
$whaterpoint = $object;
if (
$whaterpoint->createdBy() != null &&
$whaterpoint->createdBy()->equals($this)
) {
return true;
}
if ($this->whaterOrganization() != null) {
if ($this->whaterOrganization()->checkPermission($permissionCode, $object)) {
return true;
}
}
break;
case UserPermission::PERMISSION_CREATE_ANALYTICAL:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_ANALYTICAL:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_WHATER_ORGANIZATION:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_CREATE_DISTRIBUTION_NETWORK:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_DISTRIBUTION_NETWORK:
if ($object == null || !($object instanceof DistributionNetwork)) {
throw new IllegalPermissionObjectException();
}
if ($this->whaterOrganization() != null) {
if ($this->whaterOrganization()->checkPermission($permissionCode, $object)) {
return true;
}
}
break;
case UserPermission::PERMISSION_CREATE_WHATER_DEVICE:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_WHATER_DEVICE:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_COUNTRY:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_CREATE_COUNTRY_AREA:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_COUNTRY_AREA:
if ($object == null || !($object instanceof CountryArea)) {
throw new IllegalPermissionObjectException();
}
if ($this->whaterOrganization() != null) {
if ($this->whaterOrganization()->checkPermission($permissionCode, $object)) {
return true;
}
}
break;
case UserPermission::PERMISSION_CREATE_TOWN:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_TOWN:
if ($object == null || !($object instanceof Town)) {
throw new IllegalPermissionObjectException();
}
if ($this->whaterOrganization() != null) {
if ($this->whaterOrganization()->checkPermission($permissionCode, $object)) {
return true;
}
}
break;
case UserPermission::PERMISSION_CREATE_UBICATION:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_UBICATION:
if ($object == null || !($object instanceof Ubication)) {
throw new IllegalPermissionObjectException();
}
if ($this->whaterOrganization() != null) {
if ($this->whaterOrganization()->checkPermission($permissionCode, $object)) {
return true;
}
}
break;
case UserPermission::PERMISSION_CREATE_TOWN_LOCATION:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_TOWN_LOCATION:
if ($object == null || !($object instanceof TownLocation)) {
throw new IllegalPermissionObjectException();
}
if ($this->whaterOrganization() != null) {
if ($this->whaterOrganization()->checkPermission($permissionCode, $object)) {
return true;
}
}
break;
case UserPermission::PERMISSION_CREATE_DISTRICT:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_DISTRICT:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_CREATE_ESTABLISHMENT:
throw new \Exception($permissionCode . 'need code implementation!');
break;
case UserPermission::PERMISSION_EDIT_ESTABLISHMENT:
throw new \Exception($permissionCode . 'need code implementation!');
break;
default:
throw new InvalidPermissionException();
}
return false;
}
public function setStripeCustomerId(string $stripeCustomerId)
{
if (!empty($stripeCustomerId)) {
$this->stripeCustomerId = $stripeCustomerId;
$this->updatedAt = new \DateTime();
}
return $this;
}
/**
* @return string
*/
public function id(): string
{
return $this->uuid;
}
/**
* @return string
*/
public function email(): string
{
return $this->email;
}
/**
* @param $email
* @return string
*/
protected function setEmail($email)
{
$email = trim($email);
if (!$email) {
throw new \InvalidArgumentException('email');
}
Assertion::email($email);
$this->email = strtolower($email);
$this->updatedAt = new \DateTime();
return $email;
}
/**
* @return Media
*/
public function avatarImage(): ?Media
{
return $this->avatarImage;
}
/**
* @return string
*/
public function username(): string
{
if (empty($this->auth)) {
return '';
}
return $this->auth->username();
}
/**
* @return AuthUser
*/
public function auth(): AuthUser
{
return $this->auth;
}
/**
* @return \DateTime
*/
public function createdAt(): \DateTime
{
return $this->createdAt;
}
/**
* @return \DateTime|null
*/
public function updatedAt()
{
return $this->updatedAt;
}
/**
* @return Collection
*/
public function roles(): Collection
{
return $this->roles;
}
/**
* @return array
*/
public function rolesAsArray(): array
{
$roles = self::DEFAULT_ROLES;
foreach ($this->roles as $role) {
if (!$role->isEnabled()) {
continue;
}
if (!in_array($role->type(), $roles)) {
$roles[] = $role->type();
}
$baseRole = Role::baseRole($role->type());
if (!in_array($baseRole, $roles)) {
$roles[] = $baseRole;
}
}
return $roles;
}
/**
* return bool
*/
public function hasRole($role): bool
{
$roles = $this->rolesAsArray();
foreach ($roles as $userRole) {
if ($role === $userRole) {
return true;
}
}
return false;
}
/**
* @return bool
*/
public function equals(User $user)
{
return $this->id() === $user->id();
}
/**
* @return Role
*/
public function defaultRole()
{
return $this->defaultRole;
}
/**
* Assign default role
*/
public function assignDefaultRole(Role $role)
{
if (!$this->hasRole($role->type())) {
$this->roles()->add($role);
}
$this->defaultRole = $role;
}
/**
* @return Role
*/
public function activeRole()
{
if (empty($this->activeRole)) {
return $this->defaultRole;
}
return $this->activeRole;
}
/**
* Assign active role
*/
public function assignActiveRole(Role $role)
{
if (!$this->hasRole($role->type())) {
$this->roles()->add($role);
}
$this->activeRole = $role;
}
/**
* @return string
*/
public function resetTokenHash(): ?string
{
return $this->resetTokenHash;
}
/**
* @return \DateTime
*/
public function resetTokenExpireAt(): ?\DateTime
{
return $this->resetTokenExpireAt;
}
/**
* @return bool
*/
public function isEnabled()
{
return (bool) $this->isEnabled;
}
/**
* set Enable
*/
public function enableUser()
{
$this->isEnabled = true;
}
/**
* set Disable
*/
public function disableUser()
{
$this->isEnabled = false;
}
/**
* Saldo disponible (ingresos por ventas - comisiones por ventas - pagos a cc )
* @return float
*/
public function currentBalance(): float
{
return $this->currentBalance;
}
/**
* @return float
* @return bool
*/
public function updateWallet()
{
$balance = 0;
foreach ($this->buyerWalletOperations() as $buyerWalletOperation) {
if ($buyerWalletOperation->checkWalletOperationChecksum()) {
$balance = $balance + $buyerWalletOperation->buyerUserCoins();
}
}
$this->currentBalance = $balance;
}
/**
* @return string
*/
public function firstName()
{
return $this->firstName;
}
/**
* @return string
*/
public function lastName()
{
return $this->lastName;
}
/**
* @return string
*/
public function publicComments()
{
return $this->publicComments;
}
/**
* @return string
*/
public function privateComments()
{
return $this->privateComments;
}
/**
* @return Town
*/
public function town()
{
return $this->town;
}
/**
* @return string
*/
public function fullName()
{
if (empty($this->lastName)) {
return $this->firstName();
} else {
return $this->firstName() . ' ' . $this->lastName();
}
}
/**
* @return bool
*/
public function isDeleted(): bool
{
return $this->isDeleted;
}
/**
* @return WhaterOrganization|null
*/
public function whaterOrganization(): ?WhaterOrganization
{
return $this->whaterOrganization;
}
/**
* @return Collection
*/
public function valorationsOfWhaterPoints(): Collection
{
return $this->valorationsOfWhaterPoints;
}
/**
* @return Collection
*/
public function valorationsOfDistributionNetworks(): Collection
{
return $this->valorationsOfDistributionNetworks;
}
/**
* @return Collection
*/
public function productValorations(): Collection
{
return $this->productValorations;
}
/**
* @return WhaterValoration
*/
public function valorationForWhaterpoint(string $whaterpointId): ?WhaterValoration
{
foreach ($this->valorationsOfWhaterpoints() as $valoration) {
if ($valoration->whaterPoint()->id() == $whaterpointId) {
return $valoration;
}
}
return null;
}
/**
* @return DistributionNetworkValoration
*/
public function valorationForDistributionNetwork(string $distributionNetworkId): ?DistributionNetworkValoration
{
foreach ($this->valorationsOfDistributionNetworks() as $valoration) {
if ($valoration->distributionNetwork()->id() == $distributionNetworkId) {
return $valoration;
}
}
return null;
}
/**
* @return Collection
*/
public function buyerWalletOperations(): Collection
{
return $this->buyerWalletOperations;
}
/**
* @return Collection
*/
public function createdWhaterPoints(): Collection
{
return $this->createdWhaterPoints;
}
/**
* @return Collection
*/
public function uploadedImportDataFiles(): Collection
{
return $this->uploadedImportDataFiles;
}
/**
* @return Collection
*/
public function whatercoinsCoupons(): Collection
{
return $this->whatercoinsCoupons;
}
/**
* @return null|Collection
*/
public function myLocations(): ?Collection
{
return $this->myLocations;
}
/**
* @return null|Collection
*/
public function cartOrders(): ?Collection
{
return $this->cartOrders;
}
/**
* @return null|Collection
*/
public function myEstablishments(): ?Collection
{
return $this->myEstablishments;
}
/**
* @return Collection
*/
public function notifications(): Collection
{
if (!$this->notifications) {
$this->notifications = new ArrayCollection();
}
return $this->notifications;
}
/**
* @return Collection
*/
public function ownershipRequests(): Collection
{
if (!$this->ownershipRequests) {
$this->ownershipRequests = new ArrayCollection();
}
return $this->ownershipRequests;
}
/**
* @return int
*/
public function unreadNotifications(): int
{
$unreadNotifications = 0;
foreach ($this->notifications() as $notification) {
if ($notification->readAt() == null) {
$unreadNotifications += 1;
}
}
return $unreadNotifications;
}
public function addNotification($notification)
{
if (!$this->notifications) {
$this->notifications = new ArrayCollection();
}
$this->notifications->add($notification);
$this->updatedAt = new \DateTime();
return $this;
}
/**
* @return string
*/
public function phone(): ?string
{
return $this->phone;
}
/**
* @return string
*/
public function stripeCustomerId(): ?string
{
return $this->stripeCustomerId;
}
public function avatarUrl(string $size = 'default')
{
switch ($size) {
case 'mini':
return '/assets/images/avatar_default_mini.png';
case 'default':
default:
return '/assets/images/avatar_default.png';
}
}
/*
* Remove (logically) of the property
*/
public function removeUser()
{
$this->isDeleted = true;
$this->isEnabled = false;
$this->email = $this->email . '@DELETE@' . time();
$this->auth->removeAuth();
}
/**
* @return string
*/
public function plainPassword(): ?string
{
return $this->plainPassword;
}
private function generatePlainPassword($lenght = 13): string
{
if (function_exists("random_bytes")) {
$bytes = random_bytes(ceil($lenght / 2));
} elseif (function_exists("openssl_random_pseudo_bytes")) {
$bytes = openssl_random_pseudo_bytes(ceil($lenght / 2));
} else {
throw new \Exception("no cryptographically secure random function available");
}
return substr(bin2hex($bytes), 0, $lenght);
}
/**
* @param string $ramdom should be base62 encoded (A-Z a-z 0-9) to avoid problems with the Url
*/
public function updateResetToken(string $ramdom)
{
$this->resetTokenHash = md5($ramdom);
$expiredAt = new \DateTime();
$expiredAt = $expiredAt->modify("+1 day");
$this->resetTokenExpireAt = $expiredAt;
}
/**
* @return string
*/
public function referralCodeUsed(): ?string
{
return $this->referralCodeUsed;
}
/**
* @return WhaterOrganization
*/
public function referralWhaterOrganization(): ?WhaterOrganization
{
return $this->referralWhaterOrganization;
}
}