Completed
Push — master ( 473578...44ffdf )
by Adrien
06:51
created

User::setOwner()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 8.0368

Importance

Changes 0
Metric Value
cc 8
eloc 11
nc 10
nop 1
dl 0
loc 21
ccs 11
cts 12
cp 0.9167
crap 8.0368
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Model;
6
7
use Application\Acl\Acl;
8
use Application\Api\Exception;
9
use Application\DBAL\Types\BillingTypeType;
10
use Application\DBAL\Types\RelationshipType;
11
use Application\ORM\Query\Filter\AclFilter;
12
use Application\Traits\HasAddress;
13
use Application\Traits\HasDoorAccess;
14
use Application\Traits\HasRemarks;
15
use Application\Utility;
16
use Cake\Chronos\Chronos;
17
use Cake\Chronos\Date;
18
use Doctrine\Common\Collections\ArrayCollection;
19
use Doctrine\Common\Collections\Collection;
20
use Doctrine\ORM\Mapping as ORM;
21
use GraphQL\Doctrine\Annotation as API;
22
23
/**
24
 * User
25
 *
26
 * @ORM\Entity(repositoryClass="Application\Repository\UserRepository")
27
 * @ORM\AssociationOverrides({
28
 *     @ORM\AssociationOverride(name="owner", inversedBy="users")
29
 * })
30
 */
31
class User extends AbstractModel
32
{
33
    const ROLE_ANONYMOUS = 'anonymous';
34
    const ROLE_BOOKING_ONLY = 'booking_only';
35
    const ROLE_INDIVIDUAL = 'individual';
36
    const ROLE_MEMBER = 'member';
37
    const ROLE_RESPONSIBLE = 'responsible';
38
    const ROLE_ADMINISTRATOR = 'administrator';
39
40
    const STATUS_INACTIVE = 'inactive';
41
    const STATUS_NEW = 'new';
42
    const STATUS_ACTIVE = 'active';
43
    const STATUS_ARCHIVED = 'archived';
44
45
    use HasDoorAccess;
46
    use HasRemarks;
47
    use HasAddress;
48
49
    /**
50
     * @var User
51
     */
52
    private static $currentUser;
53
54
    /**
55
     * Set currently logged in user
56
     * WARNING: this method should only be called from \Application\Authentication\AuthenticationListener
57
     *
58
     * @param \Application\Model\User $user
59
     */
60 48
    public static function setCurrent(?self $user): void
61
    {
62 48
        self::$currentUser = $user;
63
64
        // Initalize ACL filter with current user if a logged in one exists
65 48
        _em()->getFilters()->getFilter(AclFilter::class)->setUser($user);
66 48
    }
67
68
    /**
69
     * Returns currently logged user or null
70
     *
71
     * @return null|self
72
     */
73 38
    public static function getCurrent(): ?self
74
    {
75 38
        return self::$currentUser;
76
    }
77
78
    /**
79
     * @var string
80
     *
81
     * @ORM\Column(type="string", length=50, unique=true)
82
     */
83
    private $login;
84
85
    /**
86
     * @var string
87
     * @ORM\Column(type="string", length=191)
88
     */
89
    private $firstName;
90
91
    /**
92
     * @var string
93
     * @ORM\Column(type="string", length=191)
94
     */
95
    private $lastName;
96
97
    /**
98
     * @var null|string
99
     *
100
     * @ORM\Column(type="string", length=255)
101
     */
102
    private $password;
103
104
    /**
105
     * @var string
106
     * @ORM\Column(type="string", length=191)
107
     */
108
    private $email;
109
110
    /**
111
     * @var string
112
     * @ORM\Column(type="UserRole", options={"default" = User::ROLE_INDIVIDUAL})
113
     */
114
    private $role = self::ROLE_INDIVIDUAL;
115
116
    /**
117
     * @var string
118
     * @ORM\Column(type="UserStatus", options={"default" = User::STATUS_NEW})
119
     */
120
    private $status = self::STATUS_NEW;
121
122
    /**
123
     * @var null|Chronos
124
     * @ORM\Column(type="datetime", nullable=true)
125
     */
126
    private $lastLogin;
127
128
    /**
129
     * @var null|Chronos
130
     * @ORM\Column(type="datetime", nullable=true)
131
     */
132
    private $welcomeSessionDate;
133
134
    /**
135
     * @var int sex according to ISO/IEC 5218
136
     * @ORM\Column(type="smallint", options={"default" = 0}))
137
     */
138
    private $sex = 0;
139
140
    /**
141
     * @var string
142
     * @ORM\Column(type="string", length=25, options={"default" = ""})
143
     */
144
    private $phone = '';
145
146
    /**
147
     * @var string
148
     * @ORM\Column(type="string", length=25, options={"default" = ""})
149
     */
150
    private $mobilePhone = '';
151
152
    /**
153
     * @var string
154
     * @ORM\Column(type="string", length=25, options={"default" = ""})
155
     */
156
    private $ichtusSwissSailing = '';
157
158
    /**
159
     * @var null|Date
160
     * @ORM\Column(type="date", nullable=true)
161
     */
162
    private $birthday;
163
164
    /**
165
     * @var bool
166
     * @ORM\Column(type="boolean", options={"default" = 0})
167
     */
168
    private $termsAgreement = false;
169
170
    /**
171
     * @var bool
172
     * @ORM\Column(type="boolean", options={"default" = 0})
173
     */
174
    private $hasInsurance = false;
175
176
    /**
177
     * @var bool
178
     * @ORM\Column(type="boolean", options={"default" = 0})
179
     */
180
    private $receivesNewsletter = false;
181
182
    /**
183
     * @var string
184
     * @ORM\Column(type="Relationship", options={"default" = RelationshipType::HOUSEHOLDER})
185
     */
186
    private $familyRelationship = RelationshipType::HOUSEHOLDER;
187
188
    /**
189
     * @var string
190
     * @ORM\Column(type="BillingType", options={"default" = BillingTypeType::ALL_ELECTRONIC})
191
     */
192
    private $billingType = BillingTypeType::ALL_ELECTRONIC;
193
194
    /**
195
     * @var Collection
196
     * @ORM\OneToMany(targetEntity="Booking", mappedBy="owner")
197
     */
198
    private $bookings;
199
200
    /**
201
     * @var Collection
202
     * @ORM\ManyToMany(targetEntity="License", mappedBy="users")
203
     */
204
    private $licenses;
205
206
    /**
207
     * @var Collection
208
     * @ORM\ManyToMany(targetEntity="UserTag", mappedBy="users")
209
     */
210
    private $userTags;
211
212
    /**
213
     * @var Collection
214
     * @ORM\OneToMany(targetEntity="Message", mappedBy="recipient")
215
     */
216
    private $messages;
217
218
    /**
219
     * There is actually at 0 to 1 account, never more. And this is
220
     * enforced by DB unique constraints
221
     *
222
     * @var Collection
223
     * @ORM\OneToMany(targetEntity="Account", mappedBy="owner")
224
     */
225
    private $accounts;
226
227
    /**
228
     * @var Collection
229
     * @ORM\OneToMany(targetEntity="User", mappedBy="owner")
230
     */
231
    private $users;
232
233
    /**
234
     * Constructor
235
     *
236
     * @param string $role role for new user
237
     */
238 26
    public function __construct(string $role = self::ROLE_INDIVIDUAL)
239
    {
240 26
        $this->role = $role;
241 26
        $this->bookings = new ArrayCollection();
242 26
        $this->accounts = new ArrayCollection();
243 26
        $this->licenses = new ArrayCollection();
244 26
        $this->userTags = new ArrayCollection();
245 26
        $this->messages = new ArrayCollection();
246 26
        $this->users = new ArrayCollection();
247 26
    }
248
249
    /**
250
     * Set login (eg: johndoe)
251
     *
252
     * @API\Input(type="Application\Api\Scalar\LoginType")
253
     *
254
     * @param string $login
255
     */
256 1
    public function setLogin(string $login): void
257
    {
258 1
        $this->login = $login;
259 1
    }
260
261
    /**
262
     * Get login (eg: johndoe)
263
     *
264
     * @API\Field(type="Application\Api\Scalar\LoginType")
265
     *
266
     * @return string
267
     */
268 10
    public function getLogin(): string
269
    {
270 10
        return (string) $this->login;
271
    }
272
273
    /**
274
     * Encrypt and change the user password
275
     *
276
     * @param string $password
277
     */
278 3
    public function setPassword(string $password): void
279
    {
280
        // Ignore empty password that could be sent "by mistake" by the client
281
        // when agreeing to terms
282 3
        if ($password === '') {
283 1
            return;
284
        }
285
286 3
        $this->password = password_hash($password, PASSWORD_DEFAULT);
287 3
    }
288
289
    /**
290
     * Returns the hashed password
291
     *
292
     * @API\Exclude
293
     *
294
     * @return null|string
295
     */
296 3
    public function getPassword(): ?string
297
    {
298 3
        return $this->password;
299
    }
300
301
    /**
302
     * Set first name
303
     *
304
     * @param string $firstName
305
     */
306 1
    public function setFirstName($firstName): void
307
    {
308 1
        $this->firstName = $firstName;
309 1
    }
310
311
    /**
312
     * Get first name
313
     *
314
     * @return string
315
     */
316 1
    public function getFirstName(): string
317
    {
318 1
        return (string) $this->firstName;
319
    }
320
321
    /**
322
     * Set last name
323
     *
324
     * @param string $lastName
325
     */
326 1
    public function setLastName($lastName): void
327
    {
328 1
        $this->lastName = $lastName;
329 1
    }
330
331
    /**
332
     * Get last name
333
     *
334
     * @return string
335
     */
336 1
    public function getLastName(): string
337
    {
338 1
        return (string) $this->lastName;
339
    }
340
341
    /**
342
     * Get full name
343
     *
344
     * @return string
345
     */
346 1
    public function getName(): string
347
    {
348 1
        return implode(' ', [$this->getFirstName(), $this->getLastName()]);
349
    }
350
351
    /**
352
     * Set email
353
     *
354
     * @API\Input(type="Email")
355
     *
356
     * @param string $email
357
     */
358 2
    public function setEmail(string $email): void
359
    {
360 2
        $this->email = $email;
361 2
    }
362
363
    /**
364
     * Get email
365
     *
366
     * @API\Field(type="Email")
367
     *
368
     * @return string
369
     */
370 2
    public function getEmail(): string
371
    {
372 2
        return $this->email;
373
    }
374
375
    /**
376
     * Returns whether the user is administrator and thus have can do anything.
377
     *
378
     * @API\Field(type="Application\Api\Enum\UserRoleType")
379
     */
380 30
    public function getRole(): string
381
    {
382 30
        return $this->role;
383
    }
384
385
    /**
386
     * Sets the user role
387
     *
388
     * The current user is allowed to promote another user up to the same role as himself. So
389
     * a Senior can promote a Student to Senior. Or an Admin can promote a Junior to Admin.
390
     *
391
     * But the current user is **not** allowed to demote a user who has a higher role than himself.
392
     * That means that a Senior cannot demote an Admin to Student.
393
     *
394
     * @param string $role
395
     */
396 7
    public function setRole(string $role): void
397
    {
398 7
        if ($role === $this->role) {
399 2
            return;
400
        }
401
402 5
        $currentRole = self::getCurrent() ? self::getCurrent()->getRole() : self::ROLE_ANONYMOUS;
403
        $orderedRoles = [
404 5
            self::ROLE_ANONYMOUS,
405 5
            self::ROLE_INDIVIDUAL,
406 5
            self::ROLE_MEMBER,
407 5
            self::ROLE_RESPONSIBLE,
408 5
            self::ROLE_ADMINISTRATOR,
409
        ];
410
411 5
        $newFound = false;
412 5
        $oldFound = false;
413 5
        foreach ($orderedRoles as $r) {
414 5
            if ($r === $this->role) {
415 3
                $oldFound = true;
416
            }
417 5
            if ($r === $role) {
418 2
                $newFound = true;
419
            }
420
421 5
            if ($r === $currentRole) {
422 5
                break;
423
            }
424
        }
425
426 5
        if (!$newFound || !$oldFound) {
427 3
            throw new Exception($currentRole . ' is not allowed to change role to ' . $role);
428
        }
429
430 2
        $this->role = $role;
431 2
    }
432
433 4
    public function setOwner(self $owner = null): void
434
    {
435 4
        if ($owner && $owner !== $this) {
436 4
            if ($owner->getOwner() && $owner !== $owner->getOwner()) {
437 1
                throw new Exception('This user cannot be owned by a user who is himself owned by somebody else');
438
            }
439
440 4
            if ($this->users->count()) {
441 1
                throw new Exception('This user owns other users, so he cannot himself be owned by somebody else');
442
            }
443
        }
444
445 4
        if ($this->getOwner()) {
446
            $this->getOwner()->users->removeElement($this);
447
        }
448
449 4
        parent::setOwner($owner);
450
451 4
        if ($this->getOwner()) {
452 4
            $this->getOwner()->users->add($this);
453 4
            $this->setStatus($this->getOwner()->getStatus());
454
        }
455 4
    }
456
457
    /**
458
     * @API\Field(type="Application\Api\Enum\UserStatusType")
459
     *
460
     * @return string
461
     */
462 4
    public function getStatus(): string
463
    {
464 4
        return $this->status;
465
    }
466
467
    /**
468
     * @API\Input(type="Application\Api\Enum\UserStatusType")
469
     *
470
     * @param string $status
471
     */
472 4
    public function setStatus(string $status): void
473
    {
474 4
        $this->status = $status;
475
476 4
        foreach ($this->users as $user) {
477 3
            if ($user !== $this) {
478 1
                $user->setStatus($status);
479
            }
480
        }
481 4
    }
482
483
    /**
484
     * Get a list of global permissions for this user
485
     *
486
     * @API\Field(type="GlobalPermissionsList")
487
     *
488
     * @return array
489
     */
490 3
    public function getGlobalPermissions(): array
491
    {
492 3
        $acl = new Acl();
493
        $types = [
494 3
            Account::class,
495
            AccountingDocument::class,
496
            Bookable::class,
497
            BookableMetadata::class,
498
            BookableTag::class,
499
            Booking::class,
500
            Category::class,
501
            Country::class,
502
            ExpenseClaim::class,
503
            Image::class,
504
            License::class,
505
            Message::class,
506
            self::class,
507
            UserTag::class,
508
        ];
509
510 3
        $permissions = ['create'];
511 3
        $result = [];
512
513 3
        $previousUser = self::getCurrent();
514 3
        self::setCurrent($this);
515 3
        foreach ($types as $type) {
516 3
            $instance = new $type();
517
518 3
            if ($instance instanceof AccountingDocument) {
519 3
                $instance->setExpenseClaim(new ExpenseClaim());
520
            }
521
522 3
            $sh = lcfirst(Utility::getShortClassName($instance));
523 3
            $result[$sh] = [];
524
525 3
            foreach ($permissions as $p) {
526 3
                $result[$sh][$p] = $acl->isCurrentUserAllowed($instance, $p);
527
            }
528
        }
529
530 3
        self::setCurrent($previousUser);
531
532 3
        return $result;
533
    }
534
535
    /**
536
     * @return string
537
     */
538
    public function getPhone(): string
539
    {
540
        return $this->phone;
541
    }
542
543
    /**
544
     * @param string $phone
545
     */
546
    public function setPhone(string $phone): void
547
    {
548
        $this->phone = $phone;
549
    }
550
551
    /**
552
     * @return string
553
     */
554
    public function getMobilePhone(): string
555
    {
556
        return $this->mobilePhone;
557
    }
558
559
    /**
560
     * @param string $mobilePhone
561
     */
562
    public function setMobilePhone(string $mobilePhone): void
563
    {
564
        $this->mobilePhone = $mobilePhone;
565
    }
566
567
    /**
568
     * @return null|Date
569
     */
570
    public function getBirthday(): ?Date
571
    {
572
        return $this->birthday;
573
    }
574
575
    /**
576
     * @param null|Date $birthday
577
     */
578
    public function setBirthday(?Date $birthday): void
579
    {
580
        $this->birthday = $birthday;
581
    }
582
583
    /**
584
     * Get bookings
585
     *
586
     * @return Collection
587
     */
588 1
    public function getBookings(): Collection
589
    {
590 1
        return $this->bookings;
591
    }
592
593
    /**
594
     * Notify the user that it has a new booking.
595
     * This should only be called by Booking::setResponsible()
596
     *
597
     * @param Booking $booking
598
     */
599 7
    public function bookingAdded(Booking $booking): void
600
    {
601 7
        $this->bookings->add($booking);
602 7
    }
603
604
    /**
605
     * Notify the user that it has a booking was removed.
606
     * This should only be called by Booking::setResponsible()
607
     *
608
     * @param Booking $booking
609
     */
610 3
    public function bookingRemoved(Booking $booking): void
611
    {
612 3
        $this->bookings->removeElement($booking);
613 3
    }
614
615
    /**
616
     * @return Collection
617
     */
618 1
    public function getLicenses(): Collection
619
    {
620 1
        return $this->licenses;
621
    }
622
623
    /**
624
     * @return Collection
625
     */
626 1
    public function getUserTags(): Collection
627
    {
628 1
        return $this->userTags;
629
    }
630
631
    /**
632
     * Notify the user that it has a new license.
633
     * This should only be called by License::addUser()
634
     *
635
     * @param License $license
636
     */
637 1
    public function licenseAdded(License $license): void
638
    {
639 1
        $this->licenses->add($license);
640 1
    }
641
642
    /**
643
     * Notify the user that it a license was removed.
644
     * This should only be called by License::removeUser()
645
     *
646
     * @param License $license
647
     */
648 1
    public function licenseRemoved(License $license): void
649
    {
650 1
        $this->licenses->removeElement($license);
651 1
    }
652
653
    /**
654
     * Notify the user that it has a new userTag.
655
     * This should only be called by UserTag::addUser()
656
     *
657
     * @param UserTag $userTag
658
     */
659 1
    public function userTagAdded(UserTag $userTag): void
660
    {
661 1
        $this->userTags->add($userTag);
662 1
    }
663
664
    /**
665
     * Notify the user that a userTag was removed.
666
     * This should only be called by UserTag::removeUser()
667
     *
668
     * @param UserTag $userTag
669
     */
670 1
    public function userTagRemoved(UserTag $userTag): void
671
    {
672 1
        $this->userTags->removeElement($userTag);
673 1
    }
674
675
    /**
676
     * @return bool
677
     */
678
    public function isTermsAgreement(): bool
679
    {
680
        return $this->termsAgreement;
681
    }
682
683
    /**
684
     * @param bool $termsAgreement
685
     */
686
    public function setTermsAgreement(bool $termsAgreement): void
687
    {
688
        $this->termsAgreement = $termsAgreement;
689
    }
690
691
    /**
692
     * @return bool
693
     */
694
    public function getHasInsurance(): bool
695
    {
696
        return $this->hasInsurance;
697
    }
698
699
    /**
700
     * @param bool $hasInsurance
701
     */
702
    public function setHasInsurance(bool $hasInsurance): void
703
    {
704
        $this->hasInsurance = $hasInsurance;
705
    }
706
707
    /**
708
     * @return null|Chronos
709
     */
710
    public function getWelcomeSessionDate(): ?Chronos
711
    {
712
        return $this->welcomeSessionDate;
713
    }
714
715
    /**
716
     * @param null|Chronos $welcomeSessionDate
717
     */
718
    public function setWelcomeSessionDate(?Chronos $welcomeSessionDate): void
719
    {
720
        $this->welcomeSessionDate = $welcomeSessionDate;
721
    }
722
723
    /**
724
     * @return bool
725
     */
726
    public function isReceivesNewsletter(): bool
727
    {
728
        return $this->receivesNewsletter;
729
    }
730
731
    /**
732
     * @param bool $receivesNewsletter
733
     */
734
    public function setReceivesNewsletter(bool $receivesNewsletter): void
735
    {
736
        $this->receivesNewsletter = $receivesNewsletter;
737
    }
738
739
    /**
740
     * Get the ISO/IEC 5218 sex
741
     *
742
     * @API\Field(type="Sex")
743
     *
744
     * @return int
745
     */
746
    public function getSex(): int
747
    {
748
        return $this->sex;
749
    }
750
751
    /**
752
     * Set the ISO/IEC 5218 sex
753
     *
754
     * @API\Input(type="Sex")
755
     *
756
     * @param int $sex
757
     */
758
    public function setSex(int $sex): void
759
    {
760
        $this->sex = $sex;
761
    }
762
763
    /**
764
     * @return string
765
     */
766
    public function getIchtusSwissSailing(): string
767
    {
768
        return $this->ichtusSwissSailing;
769
    }
770
771
    /**
772
     * @param string $ichtusSwissSailing
773
     */
774
    public function setIchtusSwissSailing(string $ichtusSwissSailing): void
775
    {
776
        $this->ichtusSwissSailing = $ichtusSwissSailing;
777
    }
778
779
    /**
780
     * Get the last login
781
     *
782
     * @return null|Chronos
783
     */
784
    public function getLastLogin(): ?Chronos
785
    {
786
        return $this->lastLogin;
787
    }
788
789
    /**
790
     * @param null|Chronos $lastLogin
791
     */
792 2
    public function setLastLogin(?Chronos $lastLogin): void
793
    {
794 2
        $this->lastLogin = $lastLogin;
795 2
    }
796
797
    /**
798
     * @API\Field(type="Relationship")
799
     *
800
     * @return string
801
     */
802
    public function getFamilyRelationship(): string
803
    {
804
        return $this->familyRelationship;
805
    }
806
807
    /**
808
     * @API\Input(type="Relationship")
809
     *
810
     * @param string $familyRelationship
811
     */
812
    public function setFamilyRelationship(string $familyRelationship): void
813
    {
814
        $this->familyRelationship = $familyRelationship;
815
    }
816
817
    /**
818
     * @return string
819
     */
820
    public function getBillingType(): string
821
    {
822
        return $this->billingType;
823
    }
824
825
    /**
826
     * @param string $billingType
827
     */
828
    public function setBillingType(string $billingType): void
829
    {
830
        $this->billingType = $billingType;
831
    }
832
833
    /**
834
     * Get the user transaction account
835
     *
836
     * @return null|Account
837
     */
838 1
    public function getAccount(): ?Account
839
    {
840 1
        return $this->accounts->count() ? $this->accounts->first() : null;
841
    }
842
843
    /**
844
     * Notify the user that it has a new account
845
     * This should only be called by Account::setOwner()
846
     *
847
     * @param Account $account
848
     */
849 1
    public function accountAdded(Account $account): void
850
    {
851 1
        $this->accounts->clear();
852 1
        $this->accounts->add($account);
853 1
    }
854
855
    /**
856
     * Notify the user that a account was removed
857
     * This should only be called by Account::setOwner()
858
     */
859 1
    public function accountRemoved(): void
860
    {
861 1
        $this->accounts->clear();
862 1
    }
863
864
    /**
865
     * Get messages sent to the user
866
     *
867
     * @return Collection
868
     */
869 1
    public function getMessages(): Collection
870
    {
871 1
        return $this->messages;
872
    }
873
874
    /**
875
     * Notify the user that it has a new message
876
     * This should only be called by Message::setRecipient()
877
     *
878
     * @param Message $message
879
     */
880 1
    public function messageAdded(Message $message): void
881
    {
882 1
        $this->messages->add($message);
883 1
    }
884
885
    /**
886
     * Notify the user that a message was removed
887
     * This should only be called by Message::setRecipient()
888
     *
889
     * @param Message $message
890
     */
891 1
    public function messageRemoved(Message $message): void
892
    {
893 1
        $this->messages->removeElement($message);
894 1
    }
895
}
896