Completed
Push — master ( 94fceb...32340d )
by vistart
07:04
created

Member::revokeRole()   C

Complexity

Conditions 7
Paths 37

Size

Total Lines 30
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 8.2162

Importance

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

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
182
        /* @var $host Organization */
183 7
        $role = null;
184 7
        if ($host->type == Organization::TYPE_ORGANIZATION) {
185 7
            $role = new OrganizationAdmin();
186 7
        } elseif ($host->type == Organization::TYPE_DEPARTMENT) {
187
            $role = new DepartmentAdmin();
188
        }
189 7
        $transaction = Yii::$app->db->beginTransaction();
190
        try {
191 7
            $this->assignRole($role);
0 ignored issues
show
Bug introduced by
It seems like $role defined by null on line 183 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...
192 7
            if (!$this->save()) {
193
                throw new IntegrityException("Failed to assign administrator.");
194
            }
195 7
            $transaction->commit();
196 7
        } catch (\Exception $ex) {
197
            $transaction->rollBack();
198
            Yii::error($ex->getMessage(), __METHOD__);
199
            throw $ex;
200
        }
201 7
        return true;
202
    }
203
204
    /**
205
     * Set role.
206
     * @param Role $role
207
     * @return boolean
208
     */
209 36
    public function setRole($role = null)
210
    {
211 36
        if (empty($role)) {
212 16
            $role = '';
213 16
        }
214 36
        if ($role instanceof Item) {
215
            $role = $role->name;
216
        }
217 36
        $this->role = $role;
218 36
        return true;
219
    }
220
221
    /**
222
     * Revoke role.
223
     * @param Role $role
224
     * @throws InvalidParamException
225
     * @throws IntegrityException
226
     * @throws \Exception
227
     * @return boolean
228
     */
229 16
    public function revokeRole($role)
230
    {
231 16
        $user = $this->memberUser;
232 16
        if (!$user) {
233
            throw new InvalidValueException('Invalid User');
234
        }
235 16
        if ($role instanceof Item) {
236
            $role = $role->name;
237
        }
238 16
        $transaction = Yii::$app->db->beginTransaction();
239
        try {
240 16
            $assignment = Yii::$app->authManager->getAssignment($role, $user);
241 16
            if ($assignment) {
242 16
                $count = (int)($user->getOfMembers()->role($role)->count());
243 16
                if ($count <= 1) {
244 14
                    Yii::$app->authManager->revoke($role, $user);
245 14
                }
246 16
            }
247 16
            $this->setRole();
248 16
            if (!$this->save()) {
249
                throw new IntegrityException('Save failed.');
250
            }
251 16
            $transaction->commit();
252 16
        } catch (\Exception $ex) {
253
            $transaction->rollBack();
254
            Yii::error($ex->getMessage(), __METHOD__);
255
            throw $ex;
256
        }
257 16
        return true;
258
    }
259
260
    /**
261
     * Revoke administrator.
262
     * @return boolean
263
     * @throws IntegrityException
264
     * @throws \Exception
265
     */
266
    public function revokeAdministrator()
267
    {
268
        $host = $this->organization;
0 ignored issues
show
Bug introduced by
The property organization does not seem to exist. Did you mean organization_guid?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
269
        /* @var $host Organization */
270
        $role = null;
271
        if ($host->type == Organization::TYPE_ORGANIZATION) {
272
            $role = new OrganizationAdmin();
273
        } elseif ($host->type == Organization::TYPE_DEPARTMENT) {
274
            $role = new DepartmentAdmin();
275
        }
276
        $transaction = Yii::$app->db->beginTransaction();
277
        try {
278
            $this->revokeRole($role);
0 ignored issues
show
Bug introduced by
It seems like $role defined by null on line 270 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...
279
            if (!$this->save()) {
280
                throw new IntegrityException("Failed to revoke administrator.");
281
            }
282
            $transaction->commit();
283
        } catch (\Exception $ex) {
284
            $transaction->rollBack();
285
            Yii::error($ex->getMessage(), __METHOD__);
286
            throw $ex;
287
        }
288
        return true;
289
    }
290
291 36
    public function rules()
292
    {
293 36
        return array_merge($this->getMemberUserRules(), $this->getMemberRoleRules(), $this->getMemberPositionRules(), parent::rules());
294
    }
295
296
    /**
297
     * @inheritdoc
298
     */
299 37
    public static function tableName()
300
    {
301 37
        return '{{%organization_member}}';
302
    }
303
304
    /**
305
     * @inheritdoc
306
     */
307 1
    public function attributeLabels()
308
    {
309
        return [
310 1
            'guid' => Yii::t('user', 'GUID'),
311 1
            'id' => Yii::t('user', 'ID'),
312 1
            'organization_guid' => Yii::t('organization', 'Organization GUID'),
313 1
            'user_guid' => Yii::t('organization', 'User GUID'),
314 1
            'nickname' => Yii::t('user', 'Nickname'),
315 1
            'role' => Yii::t('organization', 'Role'),
316 1
            'position' => Yii::t('organization', 'Member Position'),
317 1
            'description' => Yii::t('organization', 'Description'),
318 1
            'ip' => Yii::t('user', 'IP Address'),
319 1
            'ip_type' => Yii::t('user', 'IP Address Type'),
320 1
            'created_at' => Yii::t('user', 'Creation Time'),
321 1
            'updated_at' => Yii::t('user', 'Last Updated Time'),
322 1
        ];
323
    }
324
325 5
    public function isDepartmentAdministrator()
326
    {
327 5
        return $this->role == (new DepartmentAdmin)->name;
328
    }
329
    
330 15
    public function isDepartmentCreator()
331
    {
332 15
        return $this->role == (new DepartmentCreator)->name;
333
    }
334
335 5
    public function isOrganizationAdministrator()
336
    {
337 5
        return $this->role == (new OrganizationAdmin)->name;
338
    }
339
    
340 15
    public function isOrganizationCreator()
341
    {
342 15
        return $this->role == (new OrganizationCreator)->name;
343
    }
344
345
    /**
346
     * Check whether current member is administrator.
347
     * @return boolean
348
     */
349 5
    public function isAdministrator()
350
    {
351 5
        return $this->isDepartmentAdministrator() || $this->isOrganizationAdministrator();
352
    }
353
354
    /**
355
     * Check whether current member is creator.
356
     * @return boolean
357
     */
358 15
    public function isCreator()
359
    {
360 15
        return $this->isDepartmentCreator() || $this->isOrganizationCreator();
361
    }
362
363
    /**
364
     * We think it a `member` if `role` property is empty.
365
     * @return boolean
366
     */
367 2
    public function isOnlyMember()
368
    {
369 2
        return empty($this->role);
370
    }
371
}
372