Failed Conditions
Push — master ( 81605b...f7b62b )
by Sam
09:20
created

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