Test Failed
Push — master ( 872c3d...09ccfa )
by vistart
06:13
created

Organization::setIsDisallowMemberJoinInOther()   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
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
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\user\User;
19
use rhosocial\organization\rbac\roles\DepartmentAdmin;
20
use rhosocial\organization\rbac\roles\DepartmentCreator;
21
use rhosocial\organization\rbac\roles\OrganizationAdmin;
22
use rhosocial\organization\rbac\roles\OrganizationCreator;
23
use rhosocial\organization\queries\MemberQuery;
24
use rhosocial\organization\queries\OrganizationQuery;
25
use Yii;
26
use yii\base\Event;
27
use yii\base\InvalidParamException;
28
use yii\db\IntegrityException;
29
30
/**
31
 * Base Organization.
32
 * This class is an abstract class that can not be instantiated directly.
33
 * You can use [[Organization]] or [[Department]] instead.
34
 *
35
 * @method Member createMember(array $config) Create member who is subordinate to this.
36
 * @property int $type Whether indicate this instance is an organization or a department.
37
 * @property int $eom Fit for [[$isExcludeOtherMembers]]. Do not modify it directly.
38
 * @property int $djo Fit for [[$isDisallowMemberJoinInOther]]. Do not modify it directly.
39
 * @property int $oacm Fit for [[$isOnlyAcceptCurrentOrgMember]]. Do not modify it directly.
40
 * @property int $oasm Fit for [[$isOnlyAcceptSuperiorOrgMember]]. Do not modify it directly.
41
 *
42
 * @property bool $isExcludeOtherMembers Determine whether the other organization and its subordinate departments
43
 * members could join in the current organization and its subordinate departments. (Only fit for Organization)
44
 * @property bool $isDisallowMemberJoinInOther Determine whether the current organization and its subordinate
45
 * departments members could join in the other organization and its subordinate departments. (Only fit for Organization)
46
 * @property bool $isOnlyAcceptCurrentOrgMember Determine whether the current department only accept the member of
47
 * the top level organization. (Only fit for Department)
48
 * @property bool $isOnlyAcceptSuperiorOrgMember Determine whether the current department only accept the member of
49
 * the superior organization or department. (Only fit for Department)
50
 *
51
 * @property-read Member[] $members Get all member models of this organization/department.
52
 * @property-read User[] $memberUsers Get all members of this organization/department.
53
 * @property-read User $creator Get creator of this organization/department.
54
 * @property-read User[] $administrators Get administrators of this organization/department.
55
 * @property-read SubordinateLimit subordinateLimit
56
 * @property-read MemberLimit memberLimit
57
 *
58
 * @version 1.0
59
 * @author vistart <[email protected]>
60
 */
61
class Organization extends User
62
{
63
    use SelfBlameableTrait;
64
65
    const TYPE_ORGANIZATION = 1;
66
    const TYPE_DEPARTMENT = 2;
67
68
    /**
69
     * @var boolean Organization does not need password and corresponding features.
70
     */
71
    public $passwordHashAttribute = false;
72
73
    /**
74
     * @var boolean Organization does not need password and corresponding features.
75
     */
76
    public $passwordResetTokenAttribute = false;
77
78
    /**
79
     * @var boolean Organization does not need password and corresponding features.
80
     */
81
    public $passwordHistoryClass = false;
82
83
    /**
84
     * @var boolean Organization does not need source.
85
     */
86
    public $sourceAttribute = false;
87
88
    /**
89
     * @var boolean Organization does not need auth key.
90
     */
91
    public $authKeyAttribute = false;
92
93
    /**
94
     * @var boolean Organization does not need access token.
95
     */
96
    public $accessTokenAttribute = false;
97
98
    /**
99
     *
100
     * @var boolean Organization does not need login log.
101
     */
102
    public $loginLogClass = false;
103
104
    public $profileClass = Profile::class;
105
106
    public $memberClass = Member::class;
107
    public $subordinateLimitClass = SubordinateLimit::class;
108
    public $memberLimitClass = MemberLimit::class;
109
    public $searchClass = OrganizationSearch::class;
110
    /**
111
     * @var Member
112
     */
113
    private $noInitMember;
114
    /**
115
     * @var SubordinateLimit
116
     */
117
    private $noInitSubordinateLimit;
118
    /**
119
     * @var MemberLimit
120
     */
121
    private $noInitMemberLimit;
122
    public $creatorModel;
123
    public $profileConfig;
124
125
    const EVENT_BEFORE_ADD_MEMBER = 'eventBeforeAddMember';
126
    const EVENT_AFTER_ADD_MEMBER = 'eventAfterAddMember';
127
    const EVENT_BEFORE_REMOVE_MEMBER = 'eventBeforeRemoveMember';
128
    const EVENT_AFTER_REMOVE_MEMBER = 'eventAfterRemoveMember';
129
130
    /**
131
     * @return Member
132
     */
133 46
    public function getNoInitMember()
134
    {
135 46
        if (!$this->noInitMember) {
136 46
            $class = $this->memberClass;
137 46
            $this->noInitMember = $class::buildNoInitModel();
138 46
        }
139 46
        return $this->noInitMember;
140
    }
141
142
    /**
143
     * @return SubordinateLimit
144
     */
145 2
    public function getNoInitSubordinateLimit()
146
    {
147 2
        if (!$this->noInitSubordinateLimit) {
148 2
            $class = $this->subordinateLimitClass;
149 2
            $this->noInitSubordinateLimit = $class::buildNoInitModel();
150 2
        }
151 2
        return $this->noInitSubordinateLimit;
152
    }
153
154
    /**
155
     * @return MemberLimit
156
     */
157 1
    public function getNoInitMemberLimit()
158
    {
159 1
        if (!$this->noInitMemberLimit) {
160 1
            $class = $this->memberLimitClass;
161 1
            $this->noInitMemberLimit = $class::buildNoInitModel();
162 1
        }
163 1
        return $this->noInitMemberLimit;
164
    }
165
166
    /**
167
     * @return null|OrganizationSearch
168
     */
169 45
    public function getSearchModel()
170
    {
171
        $class = $this->searchClass;
172 1
        if (empty($class) || !class_exists($class)) {
173
            return null;
174 45
        }
175
        return new $class;
176
    }
177
178 47
    public function init()
179
    {
180 47
        $this->parentAttribute = 'parent_guid';
181 47
        if (class_exists($this->memberClass)) {
182 47
            $this->addSubsidiaryClass('Member', ['class' => $this->memberClass]);
183 47
        }
184 47
        if ($this->skipInit) {
185 47
            return;
186
        }
187 47
        $this->on(static::$eventAfterRegister, [$this, 'onAddProfile'], $this->profileConfig);
188 47
        $this->on(static::$eventAfterRegister, [$this, 'onAssignCreator'], $this->creatorModel);
189 47
        $this->on(static::EVENT_BEFORE_DELETE, [$this, 'onRevokeCreator']);
190 47
        $this->on(static::EVENT_BEFORE_DELETE, [$this, 'onRevokeAdministrators']);
191 47
        $this->on(static::EVENT_BEFORE_DELETE, [$this, 'onRevokePermissions']);
192 47
        $this->initSelfBlameableEvents();
193 47
        parent::init();
194 47
    }
195
196
    /**
197
     * @inheritdoc
198
     */
199 11
    public function attributeLabels()
200
    {
201
        return [
202 1
            'guid' => Yii::t('user', 'GUID'),
203 1
            'id' => Yii::t('user', 'ID'),
204 1
            'ip' => Yii::t('user', 'IP Address'),
205 11
            'ip_type' => Yii::t('user', 'IP Address Type'),
206 1
            'parent' => Yii::t('organization', 'Parent'),
207 4
            'created_at' => Yii::t('user', 'Creation Time'),
208 1
            'updated_at' => Yii::t('user', 'Last Updated Time'),
209 1
            'status' => Yii::t('user', 'Status'),
210 11
            'type' => Yii::t('user', 'Type'),
211 1
            'isExcludeOtherMembers' => Yii::t('organization', 'Exclude Other Members'),
212 1
            'isDisallowMemberJoinOther' => Yii::t('organization', 'Disallow Member to Join in Other Organizations'),
213 1
            'isOnlyAcceptCurrentOrgMember' => Yii::t('organization', 'Only Accept Current Organization Members'),
214 1
            'isOnlyAcceptSuperiorOrgMember' => Yii::t('organization', 'Only Accept Superior Organization Members'),
215 1
        ];
216
    }
217
218
    /**
219
     * @inheritdoc
220
     */
221 47
    public static function tableName()
222
    {
223 47
        return '{{%organization}}';
224
    }
225
226
    /**
227
     * Find.
228
     * Friendly to IDE.
229
     * @return OrganizationQuery
230
     */
231 47
    public static function find()
232
    {
233 47
        return parent::find();
234
    }
235
236 46
    protected function getTypeRules()
237
    {
238
        return [
239 46
            ['type', 'default', 'value' => static::TYPE_ORGANIZATION],
240 46
            ['type', 'required'],
241 46
            ['type', 'in', 'range' => [static::TYPE_ORGANIZATION, static::TYPE_DEPARTMENT]],
242 46
        ];
243
    }
244
245 46
    public function rules()
246
    {
247 46
        return array_merge(parent::rules(), $this->getTypeRules(), $this->getSelfBlameableRules());
248
    }
249
250
    /**
251
     * Get Member Query.
252
     * @return MemberQuery
253
     */
254 45
    public function getMembers()
255
    {
256 45
        return $this->hasMany($this->memberClass, [$this->getNoInitMember()->createdByAttribute => $this->guidAttribute])->inverseOf('organization');
257
    }
258
259
    /**
260
     * Get organization member users' query.
261
     * @return BaseUserQuery
262
     */
263 20
    public function getMemberUsers()
264
    {
265 20
        $noInit = $this->getNoInitMember();
266 5
        $class = $noInit->memberUserClass;
267 5
        $noInitUser = $class::buildNoInitModel();
268 5
        return $this->hasMany($class, [$noInitUser->guidAttribute => $this->getNoInitMember()->memberAttribute])->via('members')->inverseOf('atOrganizations');
269
    }
270
271
    /**
272
     * Get subordinate limit query.
273
     * @return null|BaseBlameableQuery
274
     */
275 2
    public function getSubordinateLimit()
276
    {
277 2
        if (empty($this->subordinateLimitClass)) {
278
            return null;
279
        }
280 2
        return $this->hasOne($this->subordinateLimitClass, [$this->getNoInitSubordinateLimit()->createdByAttribute => $this->guidAttribute]);
281
    }
282
283
    /**
284
     * Get member limit query.
285
     * @return null|BaseBlameableQuery
286
     */
287 2
    public function getMemberLimit()
288
    {
289 2
        if (empty($this->memberLimitClass)) {
290
            return null;
291
        }
292 1
        return $this->hasOne($this->memberLimitClass, [$this->getNoInitMemberLimit()->createdByAttribute => $this->guidAttribute]);
293
    }
294
295
    /**
296
     * Get member with specified user.
297
     * @param User|string|integer $user
298
     * @return Member Null if `user` is not in this organization.
299
     */
300 45
    public function getMember($user)
301
    {
302 45
        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 302 which is incompatible with the return type documented by rhosocial\organization\Organization::getMember of type rhosocial\organization\Member|null.
Loading history...
303 1
    }
304
305
    /**
306
     * Add member to organization.
307
     * @param Member|User|string|integer $member Member or User model, or User ID or GUID.
308
     * If member is created, it will be re-assigned to this parameter.
309
     * @see createMemberModel
310
     * @see createMemberModelWithUser
311
     * @return boolean
312
     */
313 45
    public function addMember(&$member)
314
    {
315 45
        if ($this->getIsNewRecord()) {
316
            return false;
317
        }
318 45
        if ($this->hasReachedMemberLimit()) {
319 1
            return false;
320
        }
321 45
        $this->trigger(self::EVENT_BEFORE_ADD_MEMBER);
322 45
        $model = null;
323 45
        if ($member instanceof Member) {
324
            if (!$member->getIsNewRecord()) {
325
                return false;
326
            }
327
            $model = $this->createMemberModel($member);
328
        }
329 45
        if (($member instanceof User) || is_string($member) || is_int($member)) {
330 45
            if ($this->hasMember($member)) {
331
                return false;
332
            }
333 45
            $model = $this->createMemberModelWithUser($member);
334 45
        }
335 45
        $member = $model;
336 45
        $result = ($member instanceof Member) ? $member->save() : false;
337 45
        $this->trigger(self::EVENT_AFTER_ADD_MEMBER);
338 45
        return $result;
339
    }
340
341
    /**
342
     * Create member model, and set organization with this.
343
     * @param Member $member If this parameter is not new record, it's organization
344
     * will be set with this, and return it. Otherwise, it will extract `User`
345
     * model and create new `Member` model.
346
     * @see createMemberModelWithUser
347
     * @return Member
348
     */
349
    public function createMemberModel($member)
350
    {
351
        if (!$member->getIsNewRecord()) {
352
            $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...
353
            return $member;
354
        }
355
        return $this->createMemberModelWithUser($member->memberUser);
356
    }
357
358
    /**
359
     * Create member model with user, and set organization with this.
360
     * @param User|string|integer $user
361
     * @return Member
362
     */
363 45
    public function createMemberModelWithUser($user)
364
    {
365
        $config = [
366 45
            'memberUser' => $user,
367 45
            'organization' => $this,
368 45
            'nickname' => '',
369 45
        ];
370 45
        $member = $this->createMember($config);
371 45
        $member->nickname = $member->memberUser->profile->nickname;
372 45
        return $member;
373
    }
374
375
    /**
376
     * Remove member.
377
     * Note: the creator cannot be removed.
378
     * @param Member|User $member
379
     * @return boolean
380
     */
381 1
    public function removeMember(&$member)
382
    {
383 1
        if ($this->getIsNewRecord()) {
384
            return false;
385
        }
386 1
        $this->trigger(self::EVENT_BEFORE_REMOVE_MEMBER);
387 1
        if ($member instanceof $this->memberClass) {
388 1
            $member = $member->{$member->memberAttribute};
389 1
        }
390 1
        $member = $this->getMember($member);
391 1
        if (!$member || $member->isCreator()) {
392
            return false;
393
        }
394 1
        $result = $member->delete() > 0;
395 1
        $this->trigger(self::EVENT_AFTER_REMOVE_MEMBER);
396 1
        return $result;
397
    }
398
399
    /**
400
     * Remove administrator.
401
     * @param Member|User|integer|string $member Member instance, or User instance or its GUID or ID.
402
     * @param boolean $keep Keep member after administrator being revoked.
403
     * @return boolean
404
     * @throws IntegrityException
405
     */
406
    public function removeAdministrator(&$member, $keep = true)
407
    {
408
        if ($this->getIsNewRecord()) {
409
            return false;
410
        }
411
        if ($member instanceof $this->memberClass) {
412
            $member = $member->{$member->memberAttribute};
413
        }
414
        $member = $this->getMember($member);
415
        if ($member && $member->isAdministrator()) {
416
            if ($keep) {
417
                return $member->revokeAdministrator();
418
            }
419
            return $this->removeMember($member);
420
        }
421
        return false;
422
    }
423
424
    /**
425
     * 
426
     * @param Event $event
427
     * @throws IntegrityException
428
     * @return boolean
429
     */
430 46
    public function onAddProfile($event)
431
    {
432 46
        $profile = $event->sender->createProfile($event->data);
433 46
        if (!$profile->save()) {
434
            throw new IntegrityException('Profile Save Failed.');
435
        }
436 46
        return true;
437
    }
438
439
    /**
440
     * 
441
     * @param Event $event
442
     */
443 46
    public function onAssignCreator($event)
444
    {
445 46
        return $event->sender->addCreator($event->data);
446
    }
447
448
    /**
449
     * 
450
     * @param Event $event
451
     * @return boolean
452
     */
453 20
    public function onRevokeCreator($event)
454
    {
455 20
        $sender = $event->sender;
456
        /* @var $sender static */
457 20
        $member = $sender->getMemberCreators()->one();
458
        /* @var $member Member */
459 20
        $role = $this->type == static::TYPE_ORGANIZATION ? (new OrganizationCreator)->name : (new DepartmentCreator)->name;
460 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...
461
    }
462
463
    /**
464
     * 
465
     * @param Event $event
466
     * @return boolean
467
     */
468 20
    public function onRevokeAdministrators($event)
469
    {
470 20
        $sender = $event->sender;
471
        /* @var $sender static */
472 20
        $members = $sender->getMemberAdministrators()->all();
473
        /* @var $members Member[] */
474 20
        foreach ($members as $member)
475
        {
476 1
            $member->revokeAdministrator();
477 20
        }
478 20
        return true;
479
    }
480
481
    /**
482
     * 
483
     * @param Event $event
484
     */
485 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...
486
    {
487
        
488 20
    }
489
490
    /**
491
     * Check whether current instance is an organization.
492
     * @return boolean
493
     */
494 2
    public function isOrganization()
495
    {
496 2
        return $this->type == static::TYPE_ORGANIZATION;
497
    }
498
499
    /**
500
     * Check whether current instance if a department.
501
     * @return boolean
502
     */
503 2
    public function isDepartment()
504
    {
505 2
        return $this->type == static::TYPE_DEPARTMENT;
506
    }
507
508
    /**
509
     * Check whether the current organization has a member.
510
     * @param User|string|integer $user User instance, GUID or ID.
511
     * @return boolean
512
     */
513 45
    public function hasMember($user)
514
    {
515 45
        return !is_null($this->getMember($user));
516
    }
517
518
    /**
519
     * Get member query which role is specified `Creator`.
520
     * @return MemberQuery
521
     */
522 24
    public function getMemberCreators()
523
    {
524 24
        return $this->getMembers()->andWhere(['role' => [(new DepartmentCreator)->name, (new OrganizationCreator)->name]]);
525
    }
526
527
    /**
528
     * Get member query which role is specified `Administrator`.
529
     * @return MemberQuery
530
     */
531 22
    public function getMemberAdministrators()
532
    {
533 22
        return $this->getMembers()->andWhere(['role' => [(new DepartmentAdmin)->name, (new OrganizationAdmin)->name]]);
534
    }
535
536
    /**
537
     * Get user query which role is specified `Creator`.
538
     * @return BaseUserQuery
539
     */
540 4
    public function getCreator()
541
    {
542 4
        $noInit = $this->getNoInitMember();
543 4
        $class = $noInit->memberUserClass;
544 4
        $noInitUser = $class::buildNoInitModel();
545 4
        return $this->hasOne($class, [$noInitUser->guidAttribute => $this->getNoInitMember()->memberAttribute])->via('memberCreators')->inverseOf('creatorsAtOrganizations');
546
    }
547
548
    /**
549
     * Get user query which role is specified `Administrator`.
550
     * @return BaseUserQuery
551
     */
552 2
    public function getAdministrators()
553
    {
554 2
        $noInit = $this->getNoInitMember();
555 2
        $class = $noInit->memberUserClass;
556 2
        $noInitUser = $class::buildNoInitModel();
557 2
        return $this->hasMany($class, [$noInitUser->guidAttribute => $this->getNoInitMember()->memberAttribute])->via('memberAdministrators')->inverseOf('administratorsAtOrganizations');
558
    }
559
560
    /**
561
     * 
562
     * @param User $user
563
     * @return boolean
564
     * @throws \Exception
565
     * @throws IntegrityException
566
     */
567 46
    protected function addCreator($user)
568
    {
569 46
        if (!$user) {
570 1
            throw new InvalidParamException('Creator Invalid.');
571
        }
572 45
        $member = $user;
573 45
        $transaction = Yii::$app->db->beginTransaction();
574
        try {
575 45
            if (!$this->addMember($member)) {
576
                throw new IntegrityException('Failed to add member.');
577
            }
578 45
            $role = $this->type == static::TYPE_ORGANIZATION ? (new OrganizationCreator)->name : (new DepartmentCreator)->name;
579 45
            $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...
580 45
            if (!$member->save()) {
581
                throw new IntegrityException('Failed to assign creator.');
582
            }
583 45
            $transaction->commit();
584 45
        } catch (\Exception $ex) {
585
            $transaction->rollBack();
586
            Yii::error($ex->getMessage(), __METHOD__);
587
            throw $ex;
588
        }
589 45
        return true;
590
    }
591
592
    /**
593
     * Add administrator.
594
     * @param User|integer|string $user User instance, or its GUID or ID.
595
     * @return boolean
596
     * @throws \Exception
597
     * @throws IntegrityException
598
     */
599 10
    public function addAdministrator($user)
600
    {
601 10
        $transaction = Yii::$app->db->beginTransaction();
602
        try {
603 10
            if (!$this->hasMember($user) && !$this->addMember($user)) {
604
                throw new IntegrityException(Yii::t('organization', 'Failed to add member.'));
605
            }
606 10
            $member = $this->getMember($user);
607 10
            $member->assignAdministrator();
608 10
            $transaction->commit();
609 10
        } catch (\Exception $ex) {
610
            $transaction->rollBack();
611
            Yii::error($ex->getMessage(), __METHOD__);
612
            throw $ex;
613
        }
614 10
        return true;
615
    }
616
617
    /**
618
     * Check whether the current organization has administrator.
619
     * @param User|integer|string $user
620
     * @return boolean
621
     */
622 2
    public function hasAdministrator($user)
623
    {
624 2
        $member = $this->getMember($user);
625 2
        if (!$member) {
626
            return false;
627
        }
628 2
        return $member->isAdministrator();
629
    }
630
631
    /**
632
     * Check whether this organization has reached the upper limit of subordinates.
633
     * @return boolean
634
     */
635 11
    public function hasReachedSubordinateLimit()
636
    {
637 11
        $class = $this->subordinateLimitClass;
638 11
        if (empty($class)) {
639
            return false;
640
        }
641 11
        $limit = $class::getLimit($this);
642 11
        if ($limit === false) {
643
            return false;
644
        }
645 11
        $count = (int)$this->getChildren()->count();
646 11
        return $count >= $limit;
647
    }
648
649
    /**
650
     * Check whether this organization has reached the upper limit of members.
651
     * @return boolean
652
     */
653 45
    public function hasReachedMemberLimit()
654
    {
655 45
        $class = $this->memberLimitClass;
656 45
        if (empty($class)) {
657
            return false;
658
        }
659 45
        $limit = $class::getLimit($this);
660 45
        if ($limit === false) {
661
            return false;
662
        }
663 45
        $count = (int)$this->getMembers()->count();
664 45
        return $count >= $limit;
665
    }
666
667 2
    public function getIsExcludeOtherMembers()
668
    {
669 2
        return $this->eom > 0;
670
    }
671 1
    public function setIsExcludeOtherMembers($value = true)
672
    {
673 1
        $this->eom = ($value) ? 1 : 0;
674 1
    }
675 2
    public function getIsDisallowMemberJoinInOther()
676
    {
677 2
        return $this->djo > 0;
678
    }
679 1
    public function setIsDisallowMemberJoinInOther($value = true)
680
    {
681 1
        $this->djo = ($value) ? 1 : 0;
682 1
    }
683 2
    public function getIsOnlyAcceptCurrentOrgMember()
684
    {
685 2
        return $this->oacm > 0;
686
    }
687 1
    public function setIsOnlyAcceptCurrentOrgMember($value = true)
688
    {
689 1
        $this->oacm = ($value) ? 1 : 0;
690 1
    }
691 2
    public function getIsOnlyAcceptSuperiorOrgMember()
692
    {
693 2
        return $this->oasm > 0;
694
    }
695 1
    public function setIsOnlyAcceptSuperiorOrgMember($value = true)
696
    {
697 1
        $this->oasm = ($value) ? 1 : 0;
698 1
    }
699
700
    /**
701
     * @return $this|null|static
702
     */
703
    public function getTopOrganization()
704
    {
705
        if ($this->isOrganization()) {
706
            return $this;
707
        }
708
        return static::findOne(end($this->getAncestorChain()));
0 ignored issues
show
Bug introduced by
$this->getAncestorChain() cannot be passed to end() as the parameter $array expects a reference.
Loading history...
709
    }
710
711
    /**
712
     * Check whether the subordinates have the [[$user]]
713
     * Note, this operation may consume the quantity of database selection.
714
     * @param User $user
715
     * @return bool
716
     */
717 1
    public function hasMemberInSubordinates($user)
718
    {
719 1
        if ($this->getChildren()->joinWith(['memberUsers mu_alias'])
720 1
            ->andWhere(['mu_alias.' . $user->guidAttribute => $user->getGUID()])->exists()) {
721 1
            return true;
722
        }
723 1
        $children = $this->children;
724
        /* @var $children static[] */
725 1
        foreach ($children as $child) {
726 1
            if ($child->hasMemberInSubordinates($user)) {
727 1
                return true;
728
            }
729 1
        }
730 1
        return false;
731
    }
732
}
733