Completed
Push — master ( 64cb63...78a26b )
by vistart
09:14
created

Organization::setIsDisallowMemberJoinOther()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
ccs 3
cts 3
cp 1
cc 2
eloc 2
nc 2
nop 1
crap 2
1
<?php
2
3
/**
4
 *  _   __ __ _____ _____ ___  ____  _____
5
 * | | / // // ___//_  _//   ||  __||_   _|
6
 * | |/ // /(__  )  / / / /| || |     | |
7
 * |___//_//____/  /_/ /_/ |_||_|     |_|
8
 * @link https://vistart.me/
9
 * @copyright Copyright (c) 2016 - 2017 vistart
10
 * @license https://vistart.me/license/
11
 */
12
13
namespace rhosocial\organization;
14
15
use rhosocial\base\models\traits\SelfBlameableTrait;
16
use rhosocial\base\models\queries\BaseBlameableQuery;
17
use rhosocial\base\models\queries\BaseUserQuery;
18
use rhosocial\organization\exceptions\DisallowMemberJoinOtherException;
19
use rhosocial\organization\exceptions\ExcludeOtherMembersException;
20
use rhosocial\organization\exceptions\OnlyAcceptCurrentOrgMemberException;
21
use rhosocial\organization\exceptions\OnlyAcceptSuperiorOrgMemberException;
22
use rhosocial\organization\rbac\roles\DepartmentAdmin;
23
use rhosocial\organization\rbac\roles\DepartmentCreator;
24
use rhosocial\organization\rbac\roles\OrganizationAdmin;
25
use rhosocial\organization\rbac\roles\OrganizationCreator;
26
use rhosocial\organization\queries\MemberQuery;
27
use rhosocial\organization\queries\OrganizationQuery;
28
use rhosocial\user\User;
29
use Yii;
30
use yii\base\Event;
31
use yii\base\InvalidParamException;
32
use yii\db\IntegrityException;
33
34
/**
35
 * Organization.
36
 * This class is used to describe an organization or department, depending on the type property.
37
 * Organization or department should be created by the user, it is best not to directly implement their own such.
38
 *
39
 * In general, the organization needs to have `setUpOrganization` permission, and the user does not have this permission
40
 * by default. You need to give this permission to the user who created the organization in advance.
41
 * Department, affiliated with the organization or other department, also need the appropriate permission to set up.
42
 *
43
 * While this can work independently, we still strongly recommend that you declare the Organization class yourself and
44
 * inherit this.
45
 * Then you need to specify the Profile and Member class yourself, like following:
46
```php
47
class Organization extends \rhosocial\organization\Organization
48
{
49
    public $profileClass = Profile::class;
50
    public $memberClass = Member::class;
51
}
52
```
53
 * If you need to limit the number of subordinates, the number of members, you need to specify the appropriate class
54
 * name.
55
 * If there is no limit, you need to set it to false manually.
56
 *
57
 * @method Member createMember(array $config) Create member who is subordinate to this.
58
 * @property int $type Whether indicate this instance is an organization or a department.
59
 * @property int $eom Fit for [[$isExcludeOtherMembers]]. Do not modify it directly.
60
 * @property int $djo Fit for [[$isDisallowMemberJoinOther]]. Do not modify it directly.
61
 * @property int $oacm Fit for [[$isOnlyAcceptCurrentOrgMember]]. Do not modify it directly.
62
 * @property int $oasm Fit for [[$isOnlyAcceptSuperiorOrgMember]]. Do not modify it directly.
63
 *
64
 * @property bool $isExcludeOtherMembers Determine whether the other organization and its subordinate departments
65
 * members could join in the current organization and its subordinate departments. (Only fit for Organization)
66
 * @property bool $isDisallowMemberJoinOther Determine whether the current organization and its subordinate
67
 * departments members could join in the other organization and its subordinate departments. (Only fit for Organization)
68
 * @property bool $isOnlyAcceptCurrentOrgMember Determine whether the current department only accept the member of
69
 * the top level organization. (Only fit for Department)
70
 * @property bool $isOnlyAcceptSuperiorOrgMember Determine whether the current department only accept the member of
71
 * the superior organization or department. (Only fit for Department)
72
 *
73
 * @property-read Member[] $members Get all member models of this organization/department.
74
 * @property-read User[] $memberUsers Get all members of this organization/department.
75
 * @property-read User $creator Get creator of this organization/department.
76
 * @property-read User[] $administrators Get administrators of this organization/department.
77
 * @property-read SubordinateLimit subordinateLimit
78
 * @property-read MemberLimit memberLimit
79
 * @property-read static|null $topOrganization The top level organization of current organization or departments.
80
 *
81
 * @version 1.0
82
 * @author vistart <[email protected]>
83
 */
84
class Organization extends User
85
{
86
    use SelfBlameableTrait;
87
88
    const TYPE_ORGANIZATION = 1;
89
    const TYPE_DEPARTMENT = 2;
90
91
    /**
92
     * @var boolean Organization does not need password and corresponding features.
93
     */
94
    public $passwordHashAttribute = false;
95
96
    /**
97
     * @var boolean Organization does not need password and corresponding features.
98
     */
99
    public $passwordResetTokenAttribute = false;
100
101
    /**
102
     * @var boolean Organization does not need password and corresponding features.
103
     */
104
    public $passwordHistoryClass = false;
105
106
    /**
107
     * @var boolean Organization does not need source.
108
     */
109
    public $sourceAttribute = false;
110
111
    /**
112
     * @var boolean Organization does not need auth key.
113
     */
114
    public $authKeyAttribute = false;
115
116
    /**
117
     * @var boolean Organization does not need access token.
118
     */
119
    public $accessTokenAttribute = false;
120
121
    /**
122
     * @var boolean Organization does not need login log.
123
     */
124
    public $loginLogClass = false;
125
126
    /**
127
     * @var string The Organization Profile Class
128
     */
129
    public $profileClass = Profile::class;
130
131
    /**
132
     * @var string The Member Class.
133
     */
134
    public $memberClass = Member::class;
135
136
    /**
137
     * @var string The Subordinate Limit Class
138
     */
139
    public $subordinateLimitClass = SubordinateLimit::class;
140
141
    /**
142
     * @var string The Member Limit Class
143
     */
144
    public $memberLimitClass = MemberLimit::class;
145
146
    /**
147
     * @var string The Organization Search Class
148
     */
149
    public $searchClass = OrganizationSearch::class;
150
151
    /**
152
     * @var Member
153
     */
154
    private $noInitMember;
155
156
    /**
157
     * @var SubordinateLimit
158
     */
159
    private $noInitSubordinateLimit;
160
161
    /**
162
     * @var MemberLimit
163
     */
164
    private $noInitMemberLimit;
165
166
    /**
167
     * @var User the creator of current Organization or Department.
168
     * This property is only available after registration.
169
     * Please do not access it at other times.
170
     * If you want to get creator model except registration, please
171
     * access [[$creator]] magic-property instead.
172
     */
173
    public $creatorModel;
174
175
    /**
176
     * @var array The configuration array of Organization Profile.
177
     * This property is only available after registration.
178
     * Please do not access it at other times.
179
     * If you want to get profile model except registration, please
180
     * access [[$profile]] magic-property instead.
181
     */
182
    public $profileConfig;
183
184
    const EVENT_BEFORE_ADD_MEMBER = 'eventBeforeAddMember';
185
    const EVENT_AFTER_ADD_MEMBER = 'eventAfterAddMember';
186
    const EVENT_BEFORE_REMOVE_MEMBER = 'eventBeforeRemoveMember';
187
    const EVENT_AFTER_REMOVE_MEMBER = 'eventAfterRemoveMember';
188
189
    /**
190
     * @return Member
191
     */
192 51
    public function getNoInitMember()
193
    {
194 51
        if (!$this->noInitMember) {
195 51
            $class = $this->memberClass;
196 51
            $this->noInitMember = $class::buildNoInitModel();
197
        }
198 51
        return $this->noInitMember;
199
    }
200
201
    /**
202
     * @return SubordinateLimit
203
     */
204 2
    public function getNoInitSubordinateLimit()
205
    {
206 2
        if (!$this->noInitSubordinateLimit) {
207 2
            $class = $this->subordinateLimitClass;
208 2
            $this->noInitSubordinateLimit = $class::buildNoInitModel();
209
        }
210 2
        return $this->noInitSubordinateLimit;
211
    }
212
213
    /**
214
     * @return MemberLimit
215
     */
216 1
    public function getNoInitMemberLimit()
217
    {
218 1
        if (!$this->noInitMemberLimit) {
219 1
            $class = $this->memberLimitClass;
220 1
            $this->noInitMemberLimit = $class::buildNoInitModel();
221
        }
222 1
        return $this->noInitMemberLimit;
223
    }
224
225
    /**
226
     * @return null|OrganizationSearch
227
     */
228
    public function getSearchModel()
229
    {
230
        $class = $this->searchClass;
231
        if (empty($class) || !class_exists($class)) {
232
            return null;
233
        }
234
        return new $class;
235
    }
236
237 52
    public function init()
238
    {
239 52
        $this->parentAttribute = 'parent_guid';
240 52
        if (class_exists($this->memberClass)) {
241 52
            $this->addSubsidiaryClass('Member', ['class' => $this->memberClass]);
242
        }
243 52
        if ($this->skipInit) {
244 52
            return;
245
        }
246 52
        $this->on(static::$eventAfterRegister, [$this, 'onAddProfile'], $this->profileConfig);
247 52
        $this->on(static::$eventAfterRegister, [$this, 'onAssignCreator'], $this->creatorModel);
248 52
        $this->on(static::EVENT_BEFORE_DELETE, [$this, 'onRevokeCreator']);
249 52
        $this->on(static::EVENT_BEFORE_DELETE, [$this, 'onRevokeAdministrators']);
250 52
        $this->on(static::EVENT_BEFORE_DELETE, [$this, 'onRevokePermissions']);
251 52
        $this->initSelfBlameableEvents();
252 52
        parent::init();
253 52
    }
254
255
    /**
256
     * @inheritdoc
257
     */
258 1
    public function attributeLabels()
259
    {
260
        return [
261 1
            'guid' => Yii::t('user', 'GUID'),
262 1
            'id' => Yii::t('user', 'ID'),
263 1
            'ip' => Yii::t('user', 'IP Address'),
264 1
            'ip_type' => Yii::t('user', 'IP Address Type'),
265 1
            'parent' => Yii::t('organization', 'Parent'),
266 1
            'created_at' => Yii::t('user', 'Creation Time'),
267 1
            'updated_at' => Yii::t('user', 'Last Updated Time'),
268 1
            'status' => Yii::t('user', 'Status'),
269 1
            'type' => Yii::t('user', 'Type'),
270 1
            'isExcludeOtherMembers' => Yii::t('organization', 'Exclude Other Members'),
271 1
            'isDisallowMemberJoinOther' => Yii::t('organization', 'Disallow Member to Join in Other Organizations'),
272 1
            'isOnlyAcceptCurrentOrgMember' => Yii::t('organization', 'Only Accept Current Organization Members'),
273 1
            'isOnlyAcceptSuperiorOrgMember' => Yii::t('organization', 'Only Accept Superior Organization Members'),
274
        ];
275
    }
276
277
    /**
278
     * @inheritdoc
279
     */
280 52
    public static function tableName()
281
    {
282 52
        return '{{%organization}}';
283
    }
284
285
    /**
286
     * Find.
287
     * Friendly to IDE.
288
     * @return OrganizationQuery
289
     */
290 52
    public static function find()
291
    {
292 52
        return parent::find();
293
    }
294
295 51
    protected function getTypeRules()
296
    {
297
        return [
298 51
            ['type', 'default', 'value' => static::TYPE_ORGANIZATION],
299
            ['type', 'required'],
300 51
            ['type', 'in', 'range' => [static::TYPE_ORGANIZATION, static::TYPE_DEPARTMENT]],
301
            [['eom', 'djo', 'oacm', 'oasm'], 'default', 'value' => 0],
302
        ];
303
    }
304
305 51
    public function rules()
306
    {
307 51
        return array_merge(parent::rules(), $this->getTypeRules(), $this->getSelfBlameableRules());
308
    }
309
310
    /**
311
     * Get Member Query.
312
     * @return MemberQuery
313
     */
314 50
    public function getMembers()
315
    {
316 50
        return $this->hasMany($this->memberClass, [
317 50
            $this->getNoInitMember()->createdByAttribute => $this->guidAttribute
318 50
        ])->inverseOf('organization');
319
    }
320
321
    /**
322
     * Get organization member users' query.
323
     * @return BaseUserQuery
324
     */
325 6
    public function getMemberUsers()
326
    {
327 6
        $noInit = $this->getNoInitMember();
328 6
        $class = $noInit->memberUserClass;
329 6
        $noInitUser = $class::buildNoInitModel();
330 6
        return $this->hasMany($class, [
331 6
            $noInitUser->guidAttribute => $this->getNoInitMember()->memberAttribute
332 6
        ])->via('members')->inverseOf('atOrganizations');
333
    }
334
335
    /**
336
     * Get subordinate limit query.
337
     * @return null|BaseBlameableQuery
338
     */
339 2
    public function getSubordinateLimit()
340
    {
341 2
        if (empty($this->subordinateLimitClass)) {
342
            return null;
343
        }
344 2
        return $this->hasOne($this->subordinateLimitClass, [
345 2
            $this->getNoInitSubordinateLimit()->createdByAttribute => $this->guidAttribute
346
        ]);
347
    }
348
349
    /**
350
     * Get member limit query.
351
     * @return null|BaseBlameableQuery
352
     */
353 1
    public function getMemberLimit()
354
    {
355 1
        if (empty($this->memberLimitClass)) {
356
            return null;
357
        }
358 1
        return $this->hasOne($this->memberLimitClass, [
359 1
            $this->getNoInitMemberLimit()->createdByAttribute => $this->guidAttribute
360
        ]);
361
    }
362
363
    /**
364
     * Get member with specified user.
365
     * @param User|string|integer $user
366
     * @return Member Null if `user` is not in this organization.
367
     */
368 50
    public function getMember($user)
369
    {
370 50
        return $this->getMembers()->user($user)->one();
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->getMembers()->user($user)->one(); of type yii\db\ActiveRecord|array|null adds the type array to the return on line 370 which is incompatible with the return type documented by rhosocial\organization\Organization::getMember of type rhosocial\organization\Member|null.
Loading history...
371
    }
372
373
    /**
374
     * Add member to organization.
375
     * @param Member|User|string|integer $member Member or User model, or User ID or GUID.
376
     * If member is created, it will be re-assigned to this parameter.
377
     * @see createMemberModel
378
     * @see createMemberModelWithUser
379
     * @return boolean
380
     * @throws DisallowMemberJoinOtherException
381
     * @throws ExcludeOtherMembersException
382
     * @throws OnlyAcceptCurrentOrgMemberException
383
     * @throws OnlyAcceptSuperiorOrgMemberException
384
     */
385 50
    public function addMember(&$member)
386
    {
387 50
        if ($this->getIsNewRecord()) {
388
            return false;
389
        }
390 50
        if ($this->hasReachedMemberLimit()) {
391 1
            return false;
392
        }
393 50
        $user = null;
394 50
        if ($member instanceof Member) {
395
            if ($member->getIsNewRecord()) {
396
                return false;
397
            }
398
            $user = $member->memberUser;
399
        }
400 50
        if ($member instanceof User) {
401 50
            $user = $member;
402
        }
403 50
        if (is_string($member) || is_int($member)) {
404
            $class = Yii::$app->user->identityClass;
405
            $user = $class::find()->guidOrId($member)->one();
406
        }
407 50
        if ($this->hasMember($user)) {
408
            return false;
409
        }
410 50
        $orgs = $user->getAtOrganizations()->all();
411
        /* @var $orgs Organization[] */
412 50
        foreach ($orgs as $org) {
413 31
            if ($org->topOrganization->isDisallowMemberJoinOther && !$org->topOrganization->equals($this->topOrganization)) {
414 1
                throw new DisallowMemberJoinOtherException(Yii::t('organization', "An organization in which the user is located does not allow its members to join other organizations."));
415
            }
416 31
            if ($this->topOrganization->isExcludeOtherMembers && !$org->topOrganization->equals($this->topOrganization)) {
417 31
                throw new ExcludeOtherMembersException(Yii::t('organization', "The organization does not allow users who have joined other organizations to join."));
418
            }
419
        }
420 50
        if ($this->isDepartment() && $this->isOnlyAcceptCurrentOrgMember && !$this->topOrganization->hasMember($user)) {
421 1
            throw new OnlyAcceptCurrentOrgMemberException(Yii::t('organization' ,'This department is only accepted by members of the organization.'));
422
        }
423 50
        if ($this->isDepartment() && $this->isOnlyAcceptSuperiorOrgMember && !$this->parent->hasMember($user)) {
424 1
            throw new OnlyAcceptSuperiorOrgMemberException(Yii::t('organization', 'This department only accepts members of the parent organization or department.'));
425
        }
426
427 50
        $this->trigger(self::EVENT_BEFORE_ADD_MEMBER);
428 50
        $model = null;
429 50
        if ($member instanceof Member) {
430
            $model = $this->createMemberModel($member);
431 50
        } elseif (($member instanceof User) || is_string($member) || is_int($member)) {
432 50
            $model = $this->createMemberModelWithUser($member);
433
        }
434 50
        $member = $model;
435 50
        $result = ($member instanceof Member) ? $member->save() : false;
436 50
        $this->trigger(self::EVENT_AFTER_ADD_MEMBER);
437 50
        return $result;
438
    }
439
440
    /**
441
     * Create member model, and set organization with this.
442
     * @param Member $member If this parameter is not new record, it's organization
443
     * will be set with this, and return it. Otherwise, it will extract `User`
444
     * model and create new `Member` model.
445
     * @see createMemberModelWithUser
446
     * @return Member
447
     */
448
    public function createMemberModel($member)
449
    {
450
        if (!$member->getIsNewRecord()) {
451
            $member->setOrganization($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<rhosocial\organization\Organization>, but the function expects a object<rhosocial\organization\BaseOrganization>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
452
            return $member;
453
        }
454
        return $this->createMemberModelWithUser($member->memberUser);
455
    }
456
457
    /**
458
     * Create member model with user, and set organization with this.
459
     * @param User|string|integer $user
460
     * @return Member
461
     */
462 50
    public function createMemberModelWithUser($user)
463
    {
464
        $config = [
465 50
            'memberUser' => $user,
466 50
            'organization' => $this,
467 50
            'nickname' => '',
468
        ];
469 50
        $member = $this->createMember($config);
470 50
        $member->nickname = $member->memberUser->profile->nickname;
471 50
        return $member;
472
    }
473
474
    /**
475
     * Remove member.
476
     * Note: the creator cannot be removed.
477
     * @param Member|User $member
478
     * @return boolean
479
     */
480 4
    public function removeMember(&$member)
481
    {
482 4
        if ($this->getIsNewRecord()) {
483
            return false;
484
        }
485 4
        $this->trigger(self::EVENT_BEFORE_REMOVE_MEMBER);
486 4
        if ($member instanceof $this->memberClass) {
487 4
            $member = $member->{$member->memberAttribute};
488
        }
489 4
        $member = $this->getMember($member);
490 4
        if (!$member || $member->isCreator()) {
491
            return false;
492
        }
493 4
        $result = $member->delete() > 0;
494 4
        $this->trigger(self::EVENT_AFTER_REMOVE_MEMBER);
495 4
        return $result;
496
    }
497
498
    /**
499
     * Remove administrator.
500
     * @param Member|User|integer|string $member Member instance, or User instance or its GUID or ID.
501
     * @param boolean $keep Keep member after administrator being revoked.
502
     * @return boolean
503
     * @throws IntegrityException
504
     */
505
    public function removeAdministrator(&$member, $keep = true)
506
    {
507
        if ($this->getIsNewRecord()) {
508
            return false;
509
        }
510
        if ($member instanceof $this->memberClass) {
511
            $member = $member->{$member->memberAttribute};
512
        }
513
        $member = $this->getMember($member);
514
        if ($member && $member->isAdministrator()) {
515
            if ($keep) {
516
                return $member->revokeAdministrator();
517
            }
518
            return $this->removeMember($member);
519
        }
520
        return false;
521
    }
522
523
    /**
524
     * 
525
     * @param Event $event
526
     * @throws IntegrityException
527
     * @return boolean
528
     */
529 51
    public function onAddProfile($event)
530
    {
531 51
        $profile = $event->sender->createProfile($event->data);
532 51
        if (!$profile->save()) {
533
            throw new IntegrityException('Profile Save Failed.');
534
        }
535 51
        return true;
536
    }
537
538
    /**
539
     * 
540
     * @param Event $event
541
     */
542 51
    public function onAssignCreator($event)
543
    {
544 51
        return $event->sender->addCreator($event->data);
545
    }
546
547
    /**
548
     * 
549
     * @param Event $event
550
     * @return boolean
551
     */
552 20
    public function onRevokeCreator($event)
553
    {
554 20
        $sender = $event->sender;
555
        /* @var $sender static */
556 20
        $member = $sender->getMemberCreators()->one();
557
        /* @var $member Member */
558 20
        $role = $this->isOrganization() ? (new OrganizationCreator)->name : (new DepartmentCreator)->name;
559 20
        return $member->revokeRole($role);
0 ignored issues
show
Documentation introduced by
$role is of type string, but the function expects a object<rhosocial\user\rbac\Role>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
560
    }
561
562
    /**
563
     * 
564
     * @param Event $event
565
     * @return boolean
566
     */
567 20
    public function onRevokeAdministrators($event)
568
    {
569 20
        $sender = $event->sender;
570
        /* @var $sender static */
571 20
        $members = $sender->getMemberAdministrators()->all();
572
        /* @var $members Member[] */
573 20
        foreach ($members as $member)
574
        {
575 1
            $member->revokeAdministrator();
576
        }
577 20
        return true;
578
    }
579
580
    /**
581
     * 
582
     * @param Event $event
583
     */
584 20
    public function onRevokePermissions($event)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
585
    {
586
        
587 20
    }
588
589
    /**
590
     * Check whether current instance is an organization.
591
     * @return boolean
592
     */
593 50
    public function isOrganization()
594
    {
595 50
        return $this->type == static::TYPE_ORGANIZATION;
596
    }
597
598
    /**
599
     * Check whether current instance if a department.
600
     * @return boolean
601
     */
602 50
    public function isDepartment()
603
    {
604 50
        return $this->type == static::TYPE_DEPARTMENT;
605
    }
606
607
    /**
608
     * Check whether the current organization has a member.
609
     * @param User|string|integer $user User instance, GUID or ID.
610
     * @return boolean
611
     */
612 50
    public function hasMember($user)
613
    {
614 50
        return !empty($this->getMember($user));
615
    }
616
617
    /**
618
     * Get member query which role is specified `Creator`.
619
     * @return MemberQuery
620
     */
621 24
    public function getMemberCreators()
622
    {
623 24
        return $this->getMembers()->andWhere(['role' => [(new DepartmentCreator)->name, (new OrganizationCreator)->name]]);
624
    }
625
626
    /**
627
     * Get member query which role is specified `Administrator`.
628
     * @return MemberQuery
629
     */
630 22
    public function getMemberAdministrators()
631
    {
632 22
        return $this->getMembers()->andWhere(['role' => [(new DepartmentAdmin)->name, (new OrganizationAdmin)->name]]);
633
    }
634
635
    /**
636
     * Get user query which role is specified `Creator`.
637
     * @return BaseUserQuery
638
     */
639 4
    public function getCreator()
640
    {
641 4
        $noInit = $this->getNoInitMember();
642 4
        $class = $noInit->memberUserClass;
643 4
        $noInitUser = $class::buildNoInitModel();
644 4
        return $this->hasOne($class, [
645 4
            $noInitUser->guidAttribute => $this->getNoInitMember()->memberAttribute
646 4
        ])->via('memberCreators')->inverseOf('creatorsAtOrganizations');
647
    }
648
649
    /**
650
     * Get user query which role is specified `Administrator`.
651
     * @return BaseUserQuery
652
     */
653 2
    public function getAdministrators()
654
    {
655 2
        $noInit = $this->getNoInitMember();
656 2
        $class = $noInit->memberUserClass;
657 2
        $noInitUser = $class::buildNoInitModel();
658 2
        return $this->hasMany($class, [
659 2
            $noInitUser->guidAttribute => $this->getNoInitMember()->memberAttribute
660 2
        ])->via('memberAdministrators')->inverseOf('administratorsAtOrganizations');
661
    }
662
663
    /**
664
     * 
665
     * @param User $user
666
     * @return boolean
667
     * @throws \Exception
668
     * @throws IntegrityException
669
     */
670 51
    protected function addCreator($user)
671
    {
672 51
        if (!$user) {
673 1
            throw new InvalidParamException('Creator Invalid.');
674
        }
675 50
        $member = $user;
676 50
        $transaction = Yii::$app->db->beginTransaction();
677
        try {
678 50
            if (!$this->addMember($member)) {
679
                throw new IntegrityException('Failed to add member.');
680
            }
681 50
            $role = $this->isOrganization() ? (new OrganizationCreator)->name : (new DepartmentCreator)->name;
682 50
            $member->assignRole($role);
0 ignored issues
show
Documentation introduced by
$role is of type string, but the function expects a object<rhosocial\user\rbac\Role>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
The method assignRole does only exist in rhosocial\organization\Member, but not in rhosocial\user\User.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
683 50
            if (!$member->save()) {
684
                throw new IntegrityException('Failed to assign creator.');
685
            }
686 50
            $transaction->commit();
687
        } catch (\Exception $ex) {
688
            $transaction->rollBack();
689
            Yii::error($ex->getMessage(), __METHOD__);
690
            throw $ex;
691
        }
692 50
        return true;
693
    }
694
695
    /**
696
     * Add administrator.
697
     * @param User|integer|string $user User instance, or its GUID or ID.
698
     * @return boolean
699
     * @throws \Exception
700
     * @throws IntegrityException
701
     */
702 17
    public function addAdministrator($user)
703
    {
704 17
        $transaction = Yii::$app->db->beginTransaction();
705
        try {
706 17
            if (!$this->hasMember($user) && !$this->addMember($user)) {
707
                throw new IntegrityException(Yii::t('organization', 'Failed to add member.'));
708
            }
709 17
            $member = $this->getMember($user);
710 17
            $member->assignAdministrator();
711 17
            $transaction->commit();
712 2
        } catch (\Exception $ex) {
713 2
            $transaction->rollBack();
714 2
            Yii::error($ex->getMessage(), __METHOD__);
715 2
            throw $ex;
716
        }
717 17
        return true;
718
    }
719
720
    /**
721
     * Check whether the current organization has administrator.
722
     * @param User|integer|string $user
723
     * @return boolean
724
     */
725 2
    public function hasAdministrator($user)
726
    {
727 2
        $member = $this->getMember($user);
728 2
        if (!$member) {
729
            return false;
730
        }
731 2
        return $member->isAdministrator();
732
    }
733
734
    /**
735
     * Check whether this organization has reached the upper limit of subordinates.
736
     * @return boolean
737
     */
738 18
    public function hasReachedSubordinateLimit()
739
    {
740 18
        $class = $this->subordinateLimitClass;
741 18
        if (empty($class)) {
742
            return false;
743
        }
744 18
        $limit = $class::getLimit($this);
745 18
        if ($limit === false) {
746
            return false;
747
        }
748 18
        $count = (int)$this->getChildren()->count();
749 18
        return $count >= $limit;
750
    }
751
752
    /**
753
     * Check whether this organization has reached the upper limit of members.
754
     * @return boolean
755
     */
756 50
    public function hasReachedMemberLimit()
757
    {
758 50
        $class = $this->memberLimitClass;
759 50
        if (empty($class)) {
760
            return false;
761
        }
762 50
        $limit = $class::getLimit($this);
763 50
        if ($limit === false) {
764
            return false;
765
        }
766 50
        $count = (int)$this->getMembers()->count();
767 50
        return $count >= $limit;
768
    }
769
770
    /**
771
     * @return bool
772
     */
773 31
    public function getIsExcludeOtherMembers()
774
    {
775 31
        return $this->eom > 0;
776
    }
777
778
    /**
779
     * @param bool $value
780
     */
781 2
    public function setIsExcludeOtherMembers($value = true)
782
    {
783 2
        $this->eom = ($value) ? 1 : 0;
784 2
    }
785
786
    /**
787
     * @return bool
788
     */
789 31
    public function getIsDisallowMemberJoinOther()
790
    {
791 31
        return $this->djo > 0;
792
    }
793
794
    /**
795
     * @param bool $value
796
     */
797 2
    public function setIsDisallowMemberJoinOther($value = true)
798
    {
799 2
        $this->djo = ($value) ? 1 : 0;
800 2
    }
801
802
    /**
803
     * @return bool
804
     */
805 17
    public function getIsOnlyAcceptCurrentOrgMember()
806
    {
807 17
        return $this->oacm > 0;
808
    }
809
810
    /**
811
     * @param bool $value
812
     */
813 2
    public function setIsOnlyAcceptCurrentOrgMember($value = true)
814
    {
815 2
        $this->oacm = ($value) ? 1 : 0;
816 2
    }
817
818
    /**
819
     * @return bool
820
     */
821 17
    public function getIsOnlyAcceptSuperiorOrgMember()
822
    {
823 17
        return $this->oasm > 0;
824
    }
825
826
    /**
827
     * @param bool $value
828
     */
829 2
    public function setIsOnlyAcceptSuperiorOrgMember($value = true)
830
    {
831 2
        $this->oasm = ($value) ? 1 : 0;
832 2
    }
833
834
    /**
835
     * @return $this|null|static
836
     */
837 31
    public function getTopOrganization()
838
    {
839 31
        if ($this->isOrganization()) {
840 31
            return $this;
841
        }
842 17
        $chain = $this->getAncestorChain();
843 17
        return static::findOne(end($chain));
844
    }
845
846
    /**
847
     * Check whether the subordinates have the [[$user]]
848
     * Note, this operation may consume the quantity of database selection.
849
     * @param User $user
850
     * @return bool
851
     */
852 2
    public function hasMemberInSubordinates($user)
853
    {
854 2
        if ($this->getChildren()->joinWith(['memberUsers mu_alias'])
855 2
            ->andWhere(['mu_alias.' . $user->guidAttribute => $user->getGUID()])->exists()) {
856 1
            return true;
857
        }
858 2
        $children = $this->children;
859
        /* @var $children static[] */
860 2
        foreach ($children as $child) {
861 2
            if ($child->hasMemberInSubordinates($user)) {
862 2
                return true;
863
            }
864
        }
865 2
        return false;
866
    }
867
}
868