Passed
Push — master ( 72f34a...34d54d )
by Sam
09:21 queued 05:21
created

User::getLastName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Model;
6
7
use Application\Api\Exception;
8
use Application\ORM\Query\Filter\AclFilter;
9
use Application\Repository\LogRepository;
10
use Application\Traits\HasAddress;
11
use Application\Traits\HasInternalRemarks;
12
use Application\Traits\HasNumericCode;
13
use Application\Traits\HasUrl;
14
use Application\Utility;
15
use Cake\Chronos\Chronos;
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Doctrine\Common\Collections\Collection;
18
use Doctrine\ORM\Mapping as ORM;
19
use GraphQL\Doctrine\Annotation as API;
20
21
/**
22
 * User
23
 *
24
 * @ORM\Entity(repositoryClass="Application\Repository\UserRepository")
25
 * @ORM\HasLifecycleCallbacks
26
 * @ORM\AssociationOverrides({
27
 *     @ORM\AssociationOverride(name="owner", inversedBy="users")
28
 * })
29
 */
30
class User extends AbstractModel
31
{
32
    const ROLE_ANONYMOUS = 'anonymous';
33
    const ROLE_MEMBER = 'member';
34
    const ROLE_FACILITATOR = 'facilitator';
35
    const ROLE_ADMINISTRATOR = 'administrator';
36
37
    use HasInternalRemarks;
38
    use HasAddress;
39
    use HasNumericCode;
40
    use HasUrl;
41
42
    /**
43
     * @var User
44
     */
45
    private static $currentUser;
46
47
    /**
48
     * Set currently logged in user
49
     * WARNING: this method should only be called from \Application\Authentication\AuthenticationListener
50
     *
51
     * @param \Application\Model\User $user
52
     */
53 91
    public static function setCurrent(?self $user): void
54
    {
55 91
        self::$currentUser = $user;
56
57
        // Initalize ACL filter with current user if a logged in one exists
58
        /** @var AclFilter $aclFilter */
59 91
        $aclFilter = _em()->getFilters()->getFilter(AclFilter::class);
60 91
        $aclFilter->setUser($user);
61 91
    }
62
63
    /**
64
     * Returns currently logged user or null
65
     *
66
     * @return null|self
67
     */
68 54
    public static function getCurrent(): ?self
69
    {
70 54
        return self::$currentUser;
71
    }
72
73
    /**
74
     * @var null|string
75
     *
76
     * @ORM\Column(type="string", length=50, nullable=true, unique=true)
77
     */
78
    private $login;
79
80
    /**
81
     * @var string
82
     * @ORM\Column(type="string", length=191, options={"default" = ""})
83
     */
84
    private $firstName = '';
85
86
    /**
87
     * @var string
88
     * @ORM\Column(type="string", length=191, options={"default" = ""})
89
     */
90
    private $lastName = '';
91
92
    /**
93
     * @var string
94
     *
95
     * @ORM\Column(type="string", length=255)
96
     */
97
    private $password = '';
98
99
    /**
100
     * @var null|string
101
     * @ORM\Column(type="string", length=191, nullable=true, unique=true)
102
     */
103
    private $email;
104
105
    /**
106
     * @var string
107
     * @ORM\Column(type="UserRole", options={"default" = User::ROLE_MEMBER})
108
     */
109
    private $role = self::ROLE_MEMBER;
110
111
    /**
112
     * @var null|Chronos
113
     * @ORM\Column(type="datetime", nullable=true)
114
     */
115
    private $membershipBegin;
116
117
    /**
118
     * @var null|Chronos
119
     * @ORM\Column(type="datetime", nullable=true)
120
     */
121
    private $membershipEnd;
122
123
    /**
124
     * @var null|Chronos
125
     * @ORM\Column(type="datetime", nullable=true)
126
     */
127
    private $subscriptionBegin;
128
129
    /**
130
     * @var null|Product
131
     *
132
     * @ORM\ManyToOne(targetEntity="Product")
133
     * @ORM\JoinColumns({
134
     *     @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
135
     * })
136
     */
137
    private $subscriptionLastNumber;
138
139
    /**
140
     * @var string
141
     * @ORM\Column(type="ProductType", nullable=true)
142
     */
143
    private $subscriptionType;
144
145
    /**
146
     * @var string
147
     * @ORM\Column(type="string", length=25, options={"default" = ""})
148
     */
149
    private $phone = '';
150
151
    /**
152
     * @var bool
153
     * @ORM\Column(type="boolean", options={"default" = 0})
154
     */
155
    private $webTemporaryAccess = false;
156
157
    /**
158
     * @var null|string
159
     * @ORM\Column(type="string", length=32, nullable=true, unique=true)
160
     */
161
    private $token;
162
163
    /**
164
     * @var null|Chronos
165
     *
166
     * @ORM\Column(type="datetime", nullable=true)
167
     */
168
    private $tokenCreationDate;
169
170
    /**
171
     * @var Collection
172
     * @ORM\ManyToMany(targetEntity="UserTag", mappedBy="users")
173
     */
174
    private $userTags;
175
176
    /**
177
     * @var Collection
178
     * @ORM\ManyToMany(targetEntity="Session", mappedBy="facilitators")
179
     */
180
    private $sessions;
181
182
    /**
183
     * @var Collection
184
     * @ORM\OneToMany(targetEntity="Message", mappedBy="recipient")
185
     */
186
    private $messages;
187
188
    /**
189
     * @var Collection
190
     * @ORM\OneToMany(targetEntity="User", mappedBy="owner")
191
     */
192
    private $users;
193
194
    /**
195
     * Constructor
196
     *
197
     * @param string $role role for new user
198
     */
199 21
    public function __construct(string $role = self::ROLE_MEMBER)
200
    {
201 21
        $this->role = $role;
202 21
        $this->userTags = new ArrayCollection();
203 21
        $this->sessions = new ArrayCollection();
204 21
        $this->messages = new ArrayCollection();
205 21
        $this->users = new ArrayCollection();
206 21
    }
207
208
    /**
209
     * Set login (eg: johndoe)
210
     *
211
     * @API\Input(type="Login")
212
     *
213
     * @param string $login
214
     */
215 2
    public function setLogin(string $login): void
216
    {
217 2
        $this->login = $login;
218 2
    }
219
220
    /**
221
     * Get login (eg: johndoe)
222
     *
223
     * @API\Field(type="?Login")
224
     *
225
     * @return null|string
226
     */
227 8
    public function getLogin(): ?string
228
    {
229 8
        return $this->login;
230
    }
231
232
    /**
233
     * Hash and change the user password
234
     *
235
     * @param string $password
236
     */
237 6
    public function setPassword(string $password): void
238
    {
239
        // Ignore empty password that could be sent "by mistake" by the client
240
        // when agreeing to terms
241 6
        if ($password === '') {
242 1
            return;
243
        }
244
245 6
        $this->revokeToken();
246
247 6
        $this->password = password_hash($password, PASSWORD_DEFAULT);
248 6
    }
249
250
    /**
251
     * Returns the hashed password
252
     *
253
     * @API\Exclude
254
     *
255
     * @return string
256
     */
257 3
    public function getPassword(): string
258
    {
259 3
        return $this->password;
260
    }
261
262
    /**
263
     * Set first name
264
     *
265
     * @param string $firstName
266
     */
267 2
    public function setFirstName($firstName): void
268
    {
269 2
        $this->firstName = $firstName;
270 2
    }
271
272
    /**
273
     * Get first name
274
     *
275
     * @return string
276
     */
277 4
    public function getFirstName(): string
278
    {
279 4
        return (string) $this->firstName;
280
    }
281
282
    /**
283
     * Set last name
284
     *
285
     * @param string $lastName
286
     */
287 2
    public function setLastName($lastName): void
288
    {
289 2
        $this->lastName = $lastName;
290 2
    }
291
292
    /**
293
     * Get last name
294
     *
295
     * @return string
296
     */
297 2
    public function getLastName(): string
298
    {
299 2
        return (string) $this->lastName;
300
    }
301
302
    /**
303
     * Get full name
304
     *
305
     * @return string
306
     */
307 2
    public function getName(): string
308
    {
309 2
        return implode(' ', [$this->getFirstName(), $this->getLastName()]);
310
    }
311
312
    /**
313
     * Set email
314
     *
315
     * @API\Input(type="?Email")
316
     *
317
     * @param null|string $email
318
     */
319 2
    public function setEmail(?string $email): void
320
    {
321 2
        $this->email = $email;
322 2
    }
323
324
    /**
325
     * Get email
326
     *
327
     * @API\Field(type="?Email")
328
     *
329
     * @return null|string
330
     */
331 4
    public function getEmail(): ?string
332
    {
333 4
        return $this->email;
334
    }
335
336
    /**
337
     * Returns whether the user is administrator and thus have can do anything.
338
     *
339
     * @API\Field(type="Application\Api\Enum\UserRoleType")
340
     */
341 37
    public function getRole(): string
342
    {
343 37
        return $this->role;
344
    }
345
346
    /**
347
     * Sets the user role
348
     *
349
     * The current user is allowed to promote another user up to the same role as himself. So
350
     * a Responsible can promote a Member to Responsible. Or an Admin can promote a Individual to Admin.
351
     *
352
     * But the current user is **not** allowed to demote a user who has a higher role than himself.
353
     * That means that a Responsible cannot demote an Admin to Individual.
354
     *
355
     * @param string $role
356
     */
357 7
    public function setRole(string $role): void
358
    {
359 7
        if ($role === $this->role) {
360 2
            return;
361
        }
362
363 5
        $currentRole = self::getCurrent() ? self::getCurrent()->getRole() : self::ROLE_ANONYMOUS;
364
        $orderedRoles = [
365 5
            self::ROLE_ANONYMOUS,
366 5
            self::ROLE_MEMBER,
367 5
            self::ROLE_FACILITATOR,
368 5
            self::ROLE_ADMINISTRATOR,
369
        ];
370
371 5
        $newFound = false;
372 5
        $oldFound = false;
373 5
        foreach ($orderedRoles as $r) {
374 5
            if ($r === $this->role) {
375 3
                $oldFound = true;
376
            }
377 5
            if ($r === $role) {
378 2
                $newFound = true;
379
            }
380
381 5
            if ($r === $currentRole) {
382 5
                break;
383
            }
384
        }
385
386 5
        if (!$newFound || !$oldFound) {
387 3
            throw new Exception($currentRole . ' is not allowed to change role from ' . $this->role . ' to ' . $role);
388
        }
389
390 2
        $this->role = $role;
391 2
    }
392
393 1
    public function initialize(): void
394
    {
395 1
        $this->role = self::ROLE_MEMBER; // Bypass security
396 1
    }
397
398
    /**
399
     * @return string
400
     */
401
    public function getPhone(): string
402
    {
403
        return $this->phone;
404
    }
405
406
    /**
407
     * @param string $phone
408
     */
409
    public function setPhone(string $phone): void
410
    {
411
        $this->phone = $phone;
412
    }
413
414
    /**
415
     * @return Collection
416
     */
417 1
    public function getUserTags(): Collection
418
    {
419 1
        return $this->userTags;
420
    }
421
422
    /**
423
     * Notify the user that it has a new userTag.
424
     * This should only be called by UserTag::addUser()
425
     *
426
     * @param UserTag $userTag
427
     */
428 1
    public function userTagAdded(UserTag $userTag): void
429
    {
430 1
        $this->userTags->add($userTag);
431 1
    }
432
433
    /**
434
     * Notify the user that a userTag was removed.
435
     * This should only be called by UserTag::removeFacilitator()
436
     *
437
     * @param UserTag $userTag
438
     */
439 1
    public function userTagRemoved(UserTag $userTag): void
440
    {
441 1
        $this->userTags->removeElement($userTag);
442 1
    }
443
444
    /**
445
     * @return Collection
446
     */
447
    public function getSessions(): Collection
448
    {
449
        return $this->sessions;
450
    }
451
452
    /**
453
     * Notify the user that it has a new session.
454
     * This should only be called by Session::addFacilitator()
455
     *
456
     * @param Session $session
457
     */
458
    public function sessionAdded(Session $session): void
459
    {
460
        $this->sessions->add($session);
461
    }
462
463
    /**
464
     * Notify the user that a session was removed.
465
     * This should only be called by Session::removeFacilitator()
466
     *
467
     * @param Session $session
468
     */
469
    public function sessionRemoved(Session $session): void
470
    {
471
        $this->sessions->removeElement($session);
472
    }
473
474
    /**
475
     * @return null|Chronos
476
     */
477
    public function getMembershipBegin(): ?Chronos
478
    {
479
        return $this->membershipBegin;
480
    }
481
482
    /**
483
     * @param null|Chronos $membershipBegin
484
     */
485
    public function setMembershipBegin(?Chronos $membershipBegin): void
486
    {
487
        $this->membershipBegin = $membershipBegin;
488
    }
489
490
    /**
491
     * @return null|Chronos
492
     */
493
    public function getMembershipEnd(): ?Chronos
494
    {
495
        return $this->membershipEnd;
496
    }
497
498
    /**
499
     * @param null|Chronos $membershipEnd
500
     */
501
    public function setMembershipEnd(?Chronos $membershipEnd): void
502
    {
503
        $this->membershipEnd = $membershipEnd;
504
    }
505
506
    /**
507
     * @return bool
508
     */
509 1
    public function getWebTemporaryAccess(): bool
510
    {
511 1
        return $this->webTemporaryAccess;
512
    }
513
514
    /**
515
     * @param bool $webTemporaryAccess
516
     */
517
    public function setWebTemporaryAccess(bool $webTemporaryAccess): void
518
    {
519
        $this->webTemporaryAccess = $webTemporaryAccess;
520
    }
521
522
    /**
523
     * Get the first login date
524
     *
525
     * @return null|Chronos
526
     */
527
    public function getFirstLogin(): ?Chronos
528
    {
529
        /** @var LogRepository $logRepository */
530
        $logRepository = _em()->getRepository(Log::class);
531
532
        return $logRepository->getLoginDate($this, true);
533
    }
534
535
    /**
536
     * Get the last login date
537
     *
538
     * @return null|Chronos
539
     */
540
    public function getLastLogin(): ?Chronos
541
    {
542
        /** @var LogRepository $logRepository */
543
        $logRepository = _em()->getRepository(Log::class);
544
545
        return $logRepository->getLoginDate($this, false);
546
    }
547
548
    /**
549
     * Get messages sent to the user
550
     *
551
     * @return Collection
552
     */
553 1
    public function getMessages(): Collection
554
    {
555 1
        return $this->messages;
556
    }
557
558
    /**
559
     * Notify the user that it has a new message
560
     * This should only be called by Message::setRecipient()
561
     *
562
     * @param Message $message
563
     */
564 4
    public function messageAdded(Message $message): void
565
    {
566 4
        $this->messages->add($message);
567 4
    }
568
569
    /**
570
     * Notify the user that a message was removed
571
     * This should only be called by Message::setRecipient()
572
     *
573
     * @param Message $message
574
     */
575 1
    public function messageRemoved(Message $message): void
576
    {
577 1
        $this->messages->removeElement($message);
578 1
    }
579
580
    /**
581
     * Generate a new random token to reset password
582
     */
583 3
    public function createToken(): string
584
    {
585 3
        $this->token = bin2hex(random_bytes(16));
586 3
        $this->tokenCreationDate = new Chronos();
587
588 3
        return $this->token;
589
    }
590
591
    /**
592
     * Destroy existing token
593
     */
594 6
    public function revokeToken(): void
595
    {
596 6
        $this->token = null;
597 6
        $this->tokenCreationDate = null;
598 6
    }
599
600
    /**
601
     * Check if token is valid.
602
     *
603
     * @API\Exclude
604
     *
605
     * @return bool
606
     */
607 3
    public function isTokenValid(): bool
608
    {
609 3
        if (!$this->tokenCreationDate) {
610 1
            return false;
611
        }
612
613 3
        $timeLimit = $this->tokenCreationDate->addMinutes(30);
614
615 3
        return $timeLimit->isFuture();
616
    }
617
618
    /**
619
     * Override parent to prevents users created from administration to be family of the administrator
620
     *
621
     * The owner must be explicitly set for all users.
622
     *
623
     * @ORM\PrePersist
624
     */
625 2
    public function timestampCreation(): void
626
    {
627 2
        $this->setCreationDate(Utility::getNow());
628 2
        $this->setCreator(self::getCurrent());
629 2
    }
630
631
    /**
632
     * @return null|Chronos
633
     */
634
    public function getSubscriptionBegin(): ?Chronos
635
    {
636
        return $this->subscriptionBegin;
637
    }
638
639
    /**
640
     * @param null|Chronos $subscriptionBegin
641
     */
642
    public function setSubscriptionBegin(?Chronos $subscriptionBegin): void
643
    {
644
        $this->subscriptionBegin = $subscriptionBegin;
645
    }
646
647
    /**
648
     * Set subscription type
649
     *
650
     * @API\Input(type="?ProductType")
651
     *
652
     * @param null|string $subscriptionType
653
     */
654
    public function setSubscriptionType(?string $subscriptionType): void
655
    {
656
        $this->subscriptionType = $subscriptionType;
657
    }
658
659
    /**
660
     * Get subscription type
661
     *
662
     * @API\Field(type="ProductType")
663
     *
664
     * @return null|string
665
     */
666 1
    public function getSubscriptionType(): ?string
667
    {
668 1
        return $this->subscriptionType;
669
    }
670
671
    /**
672
     * Get last subscription related product
673
     *
674
     * @return null|Product
675
     */
676
    public function getSubscriptionLastNumber(): ?Product
677
    {
678
        return $this->subscriptionLastNumber;
679
    }
680
681
    /**
682
     * Set related product as last subscribed one
683
     *
684
     * @param null|Product $subscriptionLastNumber
685
     */
686
    public function setSubscriptionLastNumber(?Product $subscriptionLastNumber): void
687
    {
688
        $this->subscriptionLastNumber = $subscriptionLastNumber;
689
    }
690
}
691