Passed
Push — master ( 265f24...4533bd )
by vistart
03:55
created

UserOrganizationTrait::getOfMembers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 1
b 0
f 1
cc 1
eloc 2
nc 1
nop 0
crap 1
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\organization\queries\MemberQuery;
16
use rhosocial\organization\queries\OrganizationQuery;
17
use rhosocial\organization\rbac\permissions\SetUpOrganization;
18
use rhosocial\organization\rbac\permissions\SetUpDepartment;
19
use rhosocial\organization\rbac\permissions\RevokeOrganization;
20
use rhosocial\organization\rbac\permissions\RevokeDepartment;
21
use rhosocial\organization\rbac\roles\DepartmentAdmin;
22
use rhosocial\organization\rbac\roles\DepartmentCreator;
23
use rhosocial\organization\rbac\roles\OrganizationAdmin;
24
use rhosocial\organization\rbac\roles\OrganizationCreator;
25
use Yii;
26
use yii\base\InvalidConfigException;
27
use yii\base\InvalidParamException;
28
29
/**
30
 * @property string $guidAttribute GUID Attribute.
31
 * @property-read Member[] $ofMembers
32
 * @property-read Organization[] $atOrganizations
33
 * @property-read Organization[] $creatorsAtOrganizations
34
 * @property-read Organization[] $administratorsAtOrganizations
35
 *
36
 * @version 1.0
37
 * @author vistart <[email protected]>
38
 */
39
trait UserOrganizationTrait
40
{
41
    public $organizationClass = Organization::class;
42
    public $memberClass = Member::class;
43
    private $noInitOrganization;
44
    private $noInitMember;
45
    public $lastSetUpOrganization;
46
    /**
47
     * @return Organization
48
     */
49
    protected function getNoInitOrganization()
50
    {
51
        if (!$this->noInitOrganization) {
52
            $class = $this->organizationClass;
53
            $this->noInitOrganization = $class::buildNoInitModel();
54
        }
55
        return $this->noInitOrganization;
56
    }
57
    /**
58
     * @return Member
59
     */
60 25
    protected function getNoInitMember()
61
    {
62 25
        if (!$this->noInitMember) {
63 25
            $class = $this->memberClass;
64 25
            $this->noInitMember = $class::buildNoInitModel();
65
        }
66 25
        return $this->noInitMember;
67
    }
68
69
    /**
70
     * 
71
     * @return MemberQuery
72
     */
73 25
    public function getOfMembers()
74
    {
75 25
        return $this->hasMany($this->memberClass, [$this->getNoInitMember()->memberAttribute => $this->guidAttribute])->inverseOf('memberUser');
0 ignored issues
show
Bug introduced by
It seems like hasMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
76
    }
77
78
    /**
79
     * 
80
     * @return MemberQuery
81
     */
82 2
    public function getOfCreators()
83
    {
84 2
        return $this->getOfMembers()->andWhere(['role' => [(new DepartmentCreator)->name, (new OrganizationCreator)->name]]);
85
    }
86
87
    /**
88
     * 
89
     * @return MemberQuery
90
     */
91 2
    public function getOfAdministrators()
92
    {
93 2
        return $this->getOfMembers()->andWhere(['role' => [(new DepartmentAdmin)->name, (new OrganizationAdmin)->name]]);
94
    }
95
96
    /**
97
     * 
98
     * @return OrganizationQuery
99
     */
100 10
    public function getAtOrganizations()
101
    {
102 10
        return $this->hasMany($this->organizationClass, [$this->guidAttribute => $this->getNoInitMember()->createdByAttribute])->via('ofMembers');
0 ignored issues
show
Bug introduced by
It seems like hasMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
103
    }
104
105
    /**
106
     * 
107
     * @return OrganizationQuery
108
     */
109 2
    public function getCreatorsAtOrganizations()
110
    {
111 2
        return $this->hasMany($this->organizationClass, [$this->guidAttribute => $this->getNoInitMember()->createdByAttribute])->via('ofCreators');
0 ignored issues
show
Bug introduced by
It seems like hasMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
112
    }
113
114
    /**
115
     * 
116
     * @return OrganizationQuery
117
     */
118 2
    public function getAdministratorsAtOrganizations()
119
    {
120 2
        return $this->hasMany($this->organizationClass, [$this->guidAttribute => $this->getNoInitMember()->createdByAttribute])->via('ofAdministrators');
0 ignored issues
show
Bug introduced by
It seems like hasMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
121
    }
122
123
    /**
124
     * Set up organization.
125
     * @param string $name
126
     * @param Organization $parent
127
     * @param string $nickname
128
     * @param integer $gravatar_type
129
     * @param string $gravatar
130
     * @param string $timezone
131
     * @param string $description
132
     * @return boolean Whether indicate the setting-up succeeded or not.
133
     */
134 32
    public function setUpOrganization($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '')
0 ignored issues
show
Unused Code introduced by
The parameter $nickname 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...
Unused Code introduced by
The parameter $gravatar_type 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...
Unused Code introduced by
The parameter $gravatar 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...
Unused Code introduced by
The parameter $timezone 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...
Unused Code introduced by
The parameter $description 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...
135
    {
136 32
        $accessChecker = Yii::$app->authManager;
137 32
        if (!$accessChecker->checkAccess($this, (new SetUpOrganization)->name)) {
138
            throw new InvalidParamException("You do not have permission to set up organization.");
139
        }
140 32
        $transaction = Yii::$app->db->beginTransaction();
141
        try {
142 32
            $models = $this->createOrganization($name, $parent, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '');
143 32
            $this->setUpBaseOrganization($models);
144 31
            $transaction->commit();
145 1
        } catch (\Exception $ex) {
146 1
            $transaction->rollBack();
147 1
            Yii::error($ex->getMessage(), __METHOD__);
148 1
            throw $ex;
149
        }
150 31
        $this->lastSetUpOrganization = is_array($models) ? $models[0] : $models;
151 31
        return true;
152
    }
153
154
    /**
155
     * Set up organization.
156
     * @param string $name
157
     * @param Organization $parent
158
     * @param string $nickname
159
     * @param integer $gravatar_type
160
     * @param string $gravatar
161
     * @param string $timezone
162
     * @param string $description
163
     * @return boolean Whether indicate the setting-up succeeded or not.
164
     */
165 3
    public function setUpDepartment($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '')
0 ignored issues
show
Unused Code introduced by
The parameter $nickname 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...
Unused Code introduced by
The parameter $gravatar_type 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...
Unused Code introduced by
The parameter $gravatar 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...
Unused Code introduced by
The parameter $timezone 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...
Unused Code introduced by
The parameter $description 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...
166
    {
167 3
        $accessChecker = Yii::$app->authManager;
168 3
        if (!$accessChecker->checkAccess($this, (new SetUpDepartment())->name)) {
169
            throw new InvalidParamException("You do not have permission to set up department.");
170
        }
171 3
        if ($parent == null) {
172 1
            throw new InvalidParamException('Invalid Parent Parameter.');
173
        }
174 2
        $transaction = Yii::$app->db->beginTransaction();
175
        try {
176 2
            $models = $this->createDepartment($name, $parent, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '');
177 2
            $this->setUpBaseOrganization($models);
178 1
            $transaction->commit();
179 1
        } catch (\Exception $ex) {
180 1
            $transaction->rollBack();
181 1
            Yii::error($ex->getMessage(), __METHOD__);
182 1
            throw $ex;
183
        }
184 1
        $this->lastSetUpOrganization = is_array($models) ? $models[0] : $models;
185 1
        return true;
186
    }
187
188
    /**
189
     * Set up base organization.
190
     * @param Organization $models
191
     * @return boolean
192
     * @throws InvalidConfigException
193
     * @throws \Exception
194
     */
195 32
    protected function setUpBaseOrganization($models)
196
    {
197 32
        $model = null;
198 32
        $associatedModels = [];
199 32
        if (is_array($models)) {
200 2
            if (!array_key_exists(0, $models)) {
201 2
                throw new InvalidConfigException('Invalid Organization Model.');
202
            }
203
            $model = $models[0];
204
            $associatedModels = array_key_exists('associatedModels', $models) ? $models['associatedModels'] : [];
205
        } elseif ($models instanceof Organization) {
206 31
            $model = $models;
207
        }
208 31
        $result = $model->register($associatedModels);
209 31
        if ($result instanceof \Exception) {
210
            throw $result;
211
        }
212 31
        if ($result !== true) {
213
            throw new \Exception('Failed to set up.');
214
        }
215 31
        return true;
216
    }
217
218
    /**
219
     * Create organization.
220
     * @param string $name
221
     * @param Organization $parent
222
     * @param string $nickname
223
     * @param string $gravatar_type
224
     * @param string $gravatar
225
     * @param string $timezone
226
     * @param string $description
227
     * @return Organization
228
     */
229 31
    public function createOrganization($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '')
230
    {
231 31
        return $this->createBaseOrganization($name, $parent, $nickname, $gravatar_type, $gravatar, $timezone, $description);
232
    }
233
234
    /**
235
     * Create department.
236
     * @param string $name
237
     * @param Organization $parent
238
     * @param string $nickname
239
     * @param string $gravatar_type
240
     * @param string $gravatar
241
     * @param string $timezone
242
     * @param string $description
243
     * @return Organization
244
     */
245 1
    public function createDepartment($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '')
246
    {
247 1
        return $this->createBaseOrganization($name, $parent, $nickname, $gravatar_type, $gravatar, $timezone, $description, Organization::TYPE_DEPARTMENT);
248
    }
249
250
    /**
251
     * Create Base Organization.
252
     * @param string $name
253
     * @param Organization $parent
254
     * @param string $nickname
255
     * @param integer $gravatar_type
256
     * @param string $gravatar
257
     * @param string $timezone
258
     * @param string $description
259
     * @param integer $type
260
     * @return Organization
261
     * @throws InvalidParamException throw if setting parent failed. Possible reasons include:
262
     * - The parent is itself.
263
     * - The parent has already been its ancestor.
264
     * - The current organization has reached the limit of ancestors.
265
     */
266 31
    protected function createBaseOrganization($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '', $type = Organization::TYPE_ORGANIZATION)
267
    {
268 31
        $class = $this->organizationClass;
269
        $profileConfig = [
270 31
            'name' => $name,
271 31
            'nickname' => $nickname,
272 31
            'gravatar_type' => $gravatar_type,
273 31
            'gravatar' => $gravatar,
274 31
            'timezone' => $timezone,
275 31
            'description' => $description,
276
        ];
277 31
        $organization = new $class(['type' => $type, 'creatorModel' => $this, 'profileConfig' => $profileConfig]);
278 31
        if (empty($parent)) {
279 31
            $organization->setNullParent();
280 4
        } elseif ($organization->setParent($parent) === false) {
281
            throw new InvalidParamException("Failed to set parent.");
282
        }
283 31
        return $organization;
284
    }
285
286
    /**
287
     * Revoke organization or department.
288
     * @param static|string|integer $organization
289
     * @param boolean $revokeIfHasChildren
290
     * @throws InvalidParamException throw if current user is not the creator of organization.
291
     */
292 8
    public function revokeOrganization($organization, $revokeIfHasChildren = false)
0 ignored issues
show
Unused Code introduced by
The parameter $revokeIfHasChildren 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...
293
    {
294 8
        if (!($organization instanceof $this->organizationClass))
295
        {
296 2
            $class = $this->organizationClass;
297 2
            if (is_numeric($organization)) {
298 1
                $organization = $class::find()->id($organization)->one();
299 1
            } elseif (is_string($organization)) {
300 1
                $organization = $class::find()->guid($organization)->one();
301
            }
302
        }
303 8
        if (!($organization instanceof Organization)) {
304
            throw new InvalidParamException('Invalid Organization.');
305
        }
306 8
        $accessChecker = Yii::$app->authManager;
307 8
        if ($organization->type == Organization::TYPE_ORGANIZATION) {
308 8
            if (!$accessChecker->checkAccess($this, (new RevokeOrganization)->name)) {
309 8
                throw new InvalidParamException("You do not have permission to revoke it.");
310
            }
311
        } elseif ($organization->type == Organization::TYPE_DEPARTMENT) {
312
            if (!$accessChecker->checkAccess($this, (new RevokeDepartment)-name)) {
313
                throw new InvalidParamException("You do not have permission to revoke it.");
314
            }
315
        }
316 8
        $transaction = Yii::$app->db->beginTransaction();
317
        try {
318 8
            $result = $organization->deregister();
319 8
            if ($result instanceof \Exception){
320
                throw $result;
321
            }
322 8
            if ($result !== true) {
323
                throw new InvalidParamException("Failed to revoke.");
324
            }
325 8
            $transaction->commit();
326
        } catch (\Exception $ex) {
327
            $transaction->rollBack();
328
            Yii::error($ex->getMessage(), __METHOD__);
329
            throw $ex;
330
        }
331 8
        return true;
332
    }
333
334
    /**
335
     * 
336
     * @param Organization $organization
337
     */
338 1
    public function isOrganizationCreator($organization)
339
    {
340 1
        $member = $organization->getMember($this);
341 1
        if (!$member) {
342 1
            return false;
343
        }
344 1
        return $member->isCreator();
345
    }
346
347
    /**
348
     * 
349
     * @param Organization $organization
350
     */
351 2
    public function isOrganizationAdministrator($organization)
352
    {
353 2
        $member = $organization->getMember($this);
354 2
        if (!$member) {
355 2
            return false;
356
        }
357 2
        return $member->isAdministrator();
358
    }
359
}
360