Member::assignAdministrator()   C
last analyzed

Complexity

Conditions 7
Paths 17

Size

Total Lines 30
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 8.8142

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 14
cts 21
cp 0.6667
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 22
nc 17
nop 0
crap 8.8142
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\models\BaseBlameableModel;
16
use rhosocial\user\rbac\Item;
17
use rhosocial\user\rbac\Role;
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\OrganizationQuery;
24
use rhosocial\organization\queries\MemberQuery;
25
use Yii;
26
use yii\base\InvalidCallException;
27
use yii\base\InvalidParamException;
28
use yii\base\InvalidValueException;
29
use yii\db\IntegrityException;
30
31
/**
32
 * Organization member.
33
 *
34
 * @property string $organization_guid
35
 * @property string $user_guid store guid of user who represents this member.
36
 * @property string $nickname
37
 * @property string $role
38
 * @property string $position
39
 * @property string $description
40
 * 
41
 * @property string $department_guid
42
 * @property string $member_guid
43
 * @property Organization $organization
44
 * @property User $memberUser
45
 *
46
 * @version 1.0
47
 * @author vistart <[email protected]>
48
 */
49
class Member extends BaseBlameableModel
50
{
51
    public $createdByAttribute = 'organization_guid';
52
    public $updatedByAttribute = false;
53
    public $hostClass = Organization::class;
54
55
    public $memberAttribute = 'user_guid';
56
    public $memberUserClass = User::class;
57
    public $contentAttribute = 'nickname';
58
    public $searchClass = MemberSearch::class;
59
    private $noInitMemberUser;
60
    /**
61
     * @return User
62
     */
63 50
    public function getNoInitMemberUser()
64
    {
65 50
        if (!$this->noInitMemberUser) {
66 50
            $class = $this->memberUserClass;
67 50
            $this->noInitMemberUser = $class::buildNoInitModel();
68
        }
69 50
        return $this->noInitMemberUser;
70
    }
71
72
    /**
73
     * @return null|MemberSearch
74
     */
75
    public function getSearchModel()
76
    {
77
        $class = $this->searchClass;
78
        if (empty($class) || !class_exists($class)) {
79
            return null;
80
        }
81
        return new $class;
82
    }
83
84
    /**
85
     * @inheritdoc
86
     */
87 51
    public function init()
88
    {
89 51
        if (!is_string($this->queryClass)) {
90 51
            $this->queryClass = MemberQuery::class;
91
        }
92 51
        if ($this->skipInit) {
93 51
            return;
94
        }
95 50
        parent::init();
96 50
    }
97
98
    public $descriptionAttribute = 'description';
99
100 50
    public function getMemberUserRules()
101
    {
102
        return [
103 50
            [$this->memberAttribute, 'required'],
104 50
            [$this->memberAttribute, 'string', 'max' => 36],
105
        ];
106
    }
107
108 50
    public function getMemberRoleRules()
109
    {
110
        return [
111 50
            ['role', 'string', 'max' => 255],
112
        ];
113
    }
114
115 50
    public function getMemberPositionRules()
116
    {
117
        return [
118 50
            ['position', 'default', 'value' => ''],
119
            ['position', 'string', 'max' => 255],
120
        ];
121
    }
122
123
    /**
124
     * Set member user.
125
     * @param User|string|integer $user
126
     */
127 50
    public function setMemberUser($user)
128
    {
129 50
        $class = $this->memberUserClass;
130 50
        if (is_numeric($user)) {
131
            $user = $class::find()->id($user)->one();
132
        }
133 50
        if ($user instanceof $class) {
134 50
            $user = $user->getGUID();
135
        }
136 50
        $this->{$this->memberAttribute} = $user;
137 50
    }
138
139 50
    public function getMemberUser()
140
    {
141 50
        return $this->hasOne($this->memberUserClass, [$this->getNoInitMemberUser()->guidAttribute => $this->memberAttribute]);
142
    }
143
144
    /**
145
     * Get Organization Query.
146
     * Alias of `getHost` method.
147
     * @return OrganizationQuery
148
     */
149 43
    public function getOrganization()
150
    {
151 43
        return $this->getHost();
152
    }
153
154
    /**
155
     * Set Organization.
156
     * @param BaseOrganization $organization
157
     * @return boolean
158
     */
159 50
    public function setOrganization($organization)
160
    {
161 50
        return $this->setHost($organization);
162
    }
163
164
    /**
165
     * Assign role.
166
     * The setting role operation will not take effect immediately. You should
167
     * wrap this method and the subsequent save operations together into a
168
     * transaction, in order to ensure data cosistency.
169
     * @param Role $role
170
     * @return boolean
171
     */
172 50
    public function assignRole($role)
173
    {
174 50
        $user = $this->memberUser;
175 50
        if (!$user) {
176
            throw new InvalidValueException('Invalid User');
177
        }
178 50
        if ($role instanceof Item) {
179 17
            $role = $role->name;
180
        }
181 50
        $assignment = Yii::$app->authManager->getAssignment($role, $user);
182 50
        if (!$assignment) {
183 50
            $assignment = Yii::$app->authManager->assign($role, $user->getGUID());
0 ignored issues
show
Unused Code introduced by
$assignment is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
184
        }
185 50
        return $this->setRole($role);
0 ignored issues
show
Documentation introduced by
$role is of type string, but the function expects a object<rhosocial\user\rbac\Role>|null.

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...
186
    }
187
188
    /**
189
     * Assign administrator.
190
     * @return boolean
191
     * @throws \Exception
192
     * @throws IntegrityException
193
     */
194 17
    public function assignAdministrator()
195
    {
196 17
        $host = $this->organization;
197
        /* @var $host Organization */
198 17
        if ($this->isCreator()) {
199
            throw new InvalidCallException(Yii::t('organization', 'The user is already a creator.'));
200
        }
201 17
        if ($this->isAdministrator()) {
202
            throw new InvalidCallException(Yii::t('organization', 'The user is already an administrator.'));
203
        }
204 17
        $role = null;
205 17
        if ($host->type == Organization::TYPE_ORGANIZATION) {
206 17
            $role = new OrganizationAdmin();
207 10
        } elseif ($host->type == Organization::TYPE_DEPARTMENT) {
208 10
            $role = new DepartmentAdmin();
209
        }
210 17
        $transaction = Yii::$app->db->beginTransaction();
211
        try {
212 17
            $this->assignRole($role);
0 ignored issues
show
Bug introduced by
It seems like $role defined by null on line 204 can also be of type null; however, rhosocial\organization\Member::assignRole() does only seem to accept object<rhosocial\user\rbac\Role>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
213 17
            if (!$this->save()) {
214
                throw new IntegrityException(Yii::t('organization', 'Failed to assign administrator.'));
215
            }
216 17
            $transaction->commit();
217
        } catch (\Exception $ex) {
218
            $transaction->rollBack();
219
            Yii::error($ex->getMessage(), __METHOD__);
220
            throw $ex;
221
        }
222 17
        return true;
223
    }
224
225
    /**
226
     * Set role.
227
     * @param Role $role
228
     * @return boolean
229
     */
230 50
    public function setRole($role = null)
231
    {
232 50
        if (empty($role)) {
233 20
            $role = '';
234
        }
235 50
        if ($role instanceof Item) {
236
            $role = $role->name;
237
        }
238 50
        $this->role = $role;
239 50
        return true;
240
    }
241
242
    /**
243
     * Revoke role.
244
     * @param Role $role
245
     * @throws InvalidParamException
246
     * @throws IntegrityException
247
     * @throws \Exception
248
     * @return boolean
249
     */
250 20
    public function revokeRole($role)
251
    {
252 20
        $user = $this->memberUser;
253 20
        if (!$user) {
254
            throw new InvalidValueException('Invalid User');
255
        }
256 20
        if ($role instanceof Item) {
257 1
            $role = $role->name;
258
        }
259 20
        $transaction = Yii::$app->db->beginTransaction();
260
        try {
261 20
            $assignment = Yii::$app->authManager->getAssignment($role, $user);
262 20
            if ($assignment) {
263 20
                $count = (int)($user->getOfMembers()->role($role)->count());
264 20
                if ($count <= 1) {
265 18
                    Yii::$app->authManager->revoke($role, $user);
266
                }
267
            }
268 20
            $this->setRole();
269 20
            if (!$this->save()) {
270
                throw new IntegrityException('Save failed.');
271
            }
272 20
            $transaction->commit();
273
        } catch (\Exception $ex) {
274
            $transaction->rollBack();
275
            Yii::error($ex->getMessage(), __METHOD__);
276
            throw $ex;
277
        }
278 20
        return true;
279
    }
280
281
    /**
282
     * Revoke administrator.
283
     * @return boolean
284
     * @throws IntegrityException
285
     * @throws \Exception
286
     */
287 1
    public function revokeAdministrator()
288
    {
289 1
        $host = $this->organization;
290
        /* @var $host Organization */
291 1
        if ($this->isCreator()) {
292
            throw new InvalidCallException(Yii::t('organization', 'The user is already a creator.'));
293
        }
294 1
        if (!$this->isAdministrator()) {
295
            throw new InvalidCallException(Yii::t('organization', 'The user is not administrator yet.'));
296
        }
297 1
        $role = null;
298 1
        if ($host->type == Organization::TYPE_ORGANIZATION) {
299 1
            $role = new OrganizationAdmin();
300 1
        } elseif ($host->type == Organization::TYPE_DEPARTMENT) {
301 1
            $role = new DepartmentAdmin();
302
        }
303 1
        $transaction = Yii::$app->db->beginTransaction();
304
        try {
305 1
            $this->revokeRole($role);
0 ignored issues
show
Bug introduced by
It seems like $role defined by null on line 297 can also be of type null; however, rhosocial\organization\Member::revokeRole() does only seem to accept object<rhosocial\user\rbac\Role>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
306 1
            if (!$this->save()) {
307
                throw new IntegrityException("Failed to revoke administrator.");
308
            }
309 1
            $transaction->commit();
310
        } catch (\Exception $ex) {
311
            $transaction->rollBack();
312
            Yii::error($ex->getMessage(), __METHOD__);
313
            throw $ex;
314
        }
315 1
        return true;
316
    }
317
318 50
    public function rules()
319
    {
320 50
        return array_merge($this->getMemberUserRules(), $this->getMemberRoleRules(), $this->getMemberPositionRules(), parent::rules());
321
    }
322
323
    /**
324
     * @inheritdoc
325
     */
326 51
    public static function tableName()
327
    {
328 51
        return '{{%organization_member}}';
329
    }
330
331
    /**
332
     * Find.
333
     * Friendly to IDE.
334
     * @return MemberQuery
335
     */
336 51
    public static function find()
337
    {
338 51
        return parent::find();
339
    }
340
341
    /**
342
     * @inheritdoc
343
     */
344 1
    public function attributeLabels()
345
    {
346
        return [
347 1
            $this->guidAttribute => Yii::t('user', 'GUID'),
348 1
            $this->idAttribute => Yii::t('user', 'ID'),
349 1
            $this->createdByAttribute => Yii::t('organization', 'Organization GUID'),
350 1
            $this->memberAttribute => Yii::t('organization', 'User GUID'),
351 1
            $this->contentAttribute => Yii::t('user', 'Nickname'),
352 1
            'role' => Yii::t('organization', 'Role'),
353 1
            'position' => Yii::t('organization', 'Member Position'),
354 1
            $this->descriptionAttribute => Yii::t('organization', 'Description'),
355 1
            $this->ipAttribute => Yii::t('user', 'IP Address'),
356 1
            $this->ipTypeAttribute => Yii::t('user', 'IP Address Type'),
357 1
            $this->createdAtAttribute => Yii::t('organization', 'Join Time'),
358 1
            $this->updatedAtAttribute => Yii::t('user', 'Last Updated Time'),
359
        ];
360
    }
361
362
    /**
363
     * @return bool
364
     */
365 18
    public function isDepartmentAdministrator()
366
    {
367 18
        return $this->role == (new DepartmentAdmin)->name;
368
    }
369
370
    /**
371
     * @return bool
372
     */
373 34
    public function isDepartmentCreator()
374
    {
375 34
        return $this->role == (new DepartmentCreator)->name;
376
    }
377
378
    /**
379
     * @return bool
380
     */
381 18
    public function isOrganizationAdministrator()
382
    {
383 18
        return $this->role == (new OrganizationAdmin)->name;
384
    }
385
386
    /**
387
     * @return bool
388
     */
389 34
    public function isOrganizationCreator()
390
    {
391 34
        return $this->role == (new OrganizationCreator)->name;
392
    }
393
394
    /**
395
     * Check whether current member is administrator.
396
     * @return boolean
397
     */
398 18
    public function isAdministrator()
399
    {
400 18
        return $this->isDepartmentAdministrator() || $this->isOrganizationAdministrator();
401
    }
402
403
    /**
404
     * Check whether current member is creator.
405
     * @return boolean
406
     */
407 34
    public function isCreator()
408
    {
409 34
        return $this->isDepartmentCreator() || $this->isOrganizationCreator();
410
    }
411
412
    /**
413
     * We think it a `member` if `role` property is empty.
414
     * @return boolean
415
     */
416 2
    public function isOnlyMember()
417
    {
418 2
        return empty($this->role);
419
    }
420
421
    const SCENARIO_UPDATE = 'update';
422
    const SCENARIO_ADMIN_UPDATE = 'admin_update';
423
424 50
    public function scenarios()
425
    {
426 50
        return array_merge(parent::scenarios(), [
427 50
            self::SCENARIO_UPDATE => [$this->contentAttribute,],
428
        ], [
429 50
            self::SCENARIO_ADMIN_UPDATE => [$this->contentAttribute, 'position', $this->descriptionAttribute],
430
        ]);
431
    }
432
}
433