OrganizationController::actionSetUpDepartment()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 0
cts 10
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 11
nc 3
nop 3
crap 12
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\console\controllers;
14
15
use rhosocial\user\User;
16
use rhosocial\organization\Organization;
17
use rhosocial\organization\rbac\permissions\SetUpOrganization;
18
use Yii;
19
use yii\console\Controller;
20
use yii\console\Exception;
21
use yii\db\IntegrityException;
22
23
/**
24
 * Organization commands.
25
 *
26
 * @version 1.0
27
 * @author vistart <[email protected]>
28
 */
29
class OrganizationController extends Controller
30
{
31
    public $userClass;
32
    public $organizationClass;
33
    public $defaultAction = 'show';  
34
35
    /**
36
     * Check user class.
37
     * @return User
38
     * @throws Exception throw if User is not an instance inherited from `\rhosocial\user\User`.
39
     */
40
    protected function checkUserClass()
41
    {
42
        $userClass = $this->userClass;
43
        if (!class_exists($userClass)) {
44
            throw new Exception('User Class Invalid.');
45
        }
46
        if (!((new $userClass()) instanceof User)) {
47
            throw new Exception('User Class(' . $userClass . ') does not inherited from `\rhosocial\user\User`.');
48
        }
49
        return $userClass;
50
    }
51
52
    /**
53
     * Get user from database.
54
     * @param User|string|integer $user User ID.
55
     * @throws Exception
56
     * @return User
57
     */
58
    protected function getUser($user)
59
    {
60
        $userClass = $this->checkUserClass();
61
        if (is_numeric($user)) {
62
            $user = $userClass::find()->id($user)->one();
63
        } elseif (is_string($user) && strlen($user)) {
64
            $user = $userClass::find()->guid($user)->one();
65
        }
66
        if (!$user || $user->getIsNewRecord()) {
0 ignored issues
show
Bug introduced by
It seems like $user is not always an object, but can also be of type array|string. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
67
            throw new Exception('User Not Registered.');
68
        }
69
        return $user;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $user; (yii\db\ActiveRecord|array|string) is incompatible with the return type documented by rhosocial\organization\c...tionController::getUser of type rhosocial\user\User.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
70
    }
71
72
    /**
73
     * Check organization class.
74
     * @return Organization
75
     * @throws Exception throw if Organization is not an instance inherited from `\rhosocial\organization\Organization`.
76
     */
77
    protected function checkOrganizationClass()
78
    {
79
        $organizationClass = $this->organizationClass;
80
        if (!class_exists($organizationClass)) {
81
            throw new Exception('Organization Class Invalid.');
82
        }
83
        if (!((new $organizationClass()) instanceof Organization)) {
84
            throw new Exception('Organization Class(' . $organizationClass . ') does not inherited from `\rhosocial\organization\Organization`.');
85
        }
86
        return $organizationClass;
87
    }
88
89
    /**
90
     * Get organization.
91
     * @param Organization|string|integer $organization
92
     * @throws Exception
93
     * @return Organization
94
     */
95
    protected function getOrganization($organization)
96
    {
97
        $organizationClass = $this->checkOrganizationClass();
98
        if (is_numeric($organization)) {
99
            $organization = $organizationClass::find()->id($organization)->one();
100
        } elseif (is_string($organization) && strlen($organization)) {
101
            $organization = $organizationClass::find()->guid($organization)->one();
102
        }
103
        if (!$organization || $organization->getIsNewRecord()) {
0 ignored issues
show
Bug introduced by
It seems like $organization is not always an object, but can also be of type array|string. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
104
            throw new Exception('Organization Not Set Up.');
105
        }
106
        return $organization;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $organization; (yii\db\ActiveRecord|array|string) is incompatible with the return type documented by rhosocial\organization\c...roller::getOrganization of type rhosocial\organization\Organization.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
107
    }
108
109
    /**
110
     * Assign SetUpOrganization permission and set upper limit.
111
     * If the permission has been assigned, it will only change the limit.
112
     * @param User|string|integer $user
113
     * @param string|integer $limit
114
     * @return boolean
115
     */
116
    public function actionAssignSetUpOrganization($user, $limit = 1)
117
    {
118
        $user = $this->getUser($user);
119
        $permission = new SetUpOrganization();
120
        $limit = is_numeric($limit) ? (int) $limit : 1;
121
        if ($limit <= 0) {
122
            $limit = 1;
123
        }
124
        $new = false;
125
        $transaction = Yii::$app->db->beginTransaction();
126
        try {
127
            $assignment = Yii::$app->authManager->getAssignment($permission->name, $user->getGUID());
128
            if (!$assignment) {
129
                $assignment = Yii::$app->authManager->assign($permission->name, $user->getGUID());
130
                $new = true;
131
            } else {
132
                echo "{$permission->name} has been assigned.\n";
133
            }
134
            $orgLimitClass = $user->organizationLimitClass;
135
            $orgLimitClass::setLimit($user, $limit);
136
            $transaction->commit();
137
        } catch (IntegrityException $ex) {
138
            $transaction->rollBack();
139
            echo "Failed to assign `" . $permission->name . "`.\n";
140
            echo "Maybe the permission has been assigned.\n";
141
            return static::EXIT_CODE_ERROR;
142
        }
143
        if ($assignment) {
144
            if ($new) {
145
                echo "`$permission->name`" . " assigned to User (" . $user->getID() . ") successfully.\n";
146
                $remaining = $user->getRemainingOrganizationPlaces();
147
                if ($remaining === false) {
148
                    echo "No upper limit.\n";
149
                } else {
150
                    echo "The upper limit is " . ($remaining + (int)$user->getCreatorsAtOrganizationsOnly()->count()). "\n";
151
                }
152
            } else {
153
                $remaining = $user->getRemainingOrganizationPlaces();
154
                if ($remaining === false) {
155
                    echo "No upper limit.\n";
156
                } else {
157
                    echo "The new upper limit is " . ($remaining + (int)$user->getCreatorsAtOrganizationsOnly()->count()) . "\n";
158
                }
159
            }
160
        } else {
161
            echo "Failed to assign `" . $permission->name . "`.\n";
162
        }
163
        return static::EXIT_CODE_NORMAL;
164
    }
165
166
    /**
167
     * Revoke SetUpOrganization permission.
168
     * @param User|string|integer $user
169
     * @return boolean
170
     */
171
    public function actionRevokeSetUpOrganization($user)
172
    {
173
        $user = $this->getUser($user);
174
        $permission = new SetUpOrganization();
175
        $transaction = Yii::$app->db->beginTransaction();
176
        try {
177
            $assignment = Yii::$app->authManager->revoke($permission->name, $user);
178
            $limitModel = $user->getOrganizationLimit()->one();
179
            if ($limitModel && !$limitModel->getIsNewRecord()) {
180
                $limitModel->delete();
181
            }
182
            $transaction->commit();
183
        } catch (\Exception $ex) {
184
            $transaction->rollBack();
185
        }
186
        if ($assignment) {
187
            echo "`$permission->name`" . " revoked from User (" . $user->getID() . ").\n";
188
        } else {
189
            echo "Failed to revoke `" . $permission->name . "`.\n";
190
            echo "Maybe the role has not been assigned yet.\n";
191
        }
192
        return static::EXIT_CODE_NORMAL;
193
    }
194
195
    /**
196
     * Show Organization Information.
197
     * @param Organization|string|integer $organization Organization's or department's ID.
198
     * @return integer
199
     */
200
    public function actionShow($organization)
201
    {
202
        $organization = $this->getOrganization($organization);
203
        echo $organization->getID() . "\n";
204
        return static::EXIT_CODE_NORMAL;
205
    }
206
207
    /**
208
     * Set up organization.
209
     * @param User|string|integer $user Organization creator.
210
     * @param string $name
211
     * @throws Exception
212
     * @return integer
213
     */
214
    public function actionSetUpOrganization($user, $name)
215
    {
216
        $user = $this->getUser($user);
217
        try {
218
            $result = $user->setUpOrganization($name);
219
            if ($result !== true) {
220
                throw new Exception('Failed to set up.');
221
            }
222
        } catch (\Exception $ex) {
223
            throw new Exception($ex->getMessage());
224
        }
225
        echo "Organization Set Up:\n";
226
        return $this->actionShow($user->lastSetUpOrganization);
227
    }
228
229
    /**
230
     * Set up department.
231
     * @param User|string|integer $user Department creator.
232
     * @param string $name
233
     * @param Organization|string|integer $parent
234
     * @throws Exception
235
     * @return integer
236
     */
237
    public function actionSetUpDepartment($user, $name, $parent)
238
    {
239
        $user = $this->getUser($user);
240
        $parent = $this->getOrganization($parent);
241
        try {
242
            $result = $user->setUpDepartment($name, $parent);
243
            if ($result !== true) {
244
                throw new Exception('Failed to set up.');
245
            }
246
        } catch (\Exception $ex) {
247
            throw new Exception($ex->getMessage());
248
        }
249
        echo "Department Set Up:\n";
250
        return $this->actionShow($user->lastSetUpOrganization);
251
    }
252
253
    /**
254
     * Revoke organization.
255
     * @param Organization|string|integer $organization
256
     * @throws Exception
257
     * @return integer
258
     */
259
    public function actionRevokeOrganization($organization)
260
    {
261
        $organization = $this->getOrganization($organization);
262
        $creator = $organization->creator;
263
        if (!$creator->revokeOrganization($organization)) {
264
            throw new Exception('Failed to revoke: ' . $organization->getID);
0 ignored issues
show
Documentation introduced by
The property getID does not exist on object<rhosocial\organization\Organization>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
265
        }
266
        echo "Organization ({$organization->getID()}) revoked.\n";
267
        return static::EXIT_CODE_NORMAL;
268
    }
269
270
    /**
271
     * Add administrator.
272
     * @param Organization|string|integer $organization
273
     * @param User|string|integer $user
274
     * @throws Exception
275
     * @return integer
276
     */
277
    public function actionAddAdministrator($organization, $user)
278
    {
279
        $organization = $this->getOrganization($organization);
280
        $user = $this->getUser($user);
281
        if (!$organization->addAdministrator($user)) {
282
            throw new Exception('Failed to add administrator.');
283
        }
284
        echo "User ({$user->getID()}) assigned administrator.\n";
285
        return static::EXIT_CODE_NORMAL;
286
    }
287
288
    /**
289
     * Remove administrator
290
     * @param Organization|string|integer $organization
291
     * @param User|string|integer $user
292
     * @param boolean $keepMember
293
     * @throws Exception
294
     * @return integer
295
     */
296
    public function actionRemoveAdministrator($organization, $user, $keepMember = "yes")
297
    {
298
        $keepMember = strtolower($keepMember) == "yes";
299
        $organization = $this->getOrganization($organization);
300
        $user = $this->getUser($user);
301
        $id = $user->getID();
302
        if (!$organization->removeAdministrator($user, $keepMember)) {
303
            throw new Exception('Failed to remove administrator.');
304
        }
305
        echo "Administrator ($id) removed.\n";
306
        echo ($keepMember) ? ("But he is still a member of it.\n") : ("At the same time, he was also removed from the organization.\n");
307
        return static::EXIT_CODE_NORMAL;
308
    }
309
310
    /**
311
     * Add member.
312
     * @param Organization|string|intger $organization
313
     * @param User|string|integer $user
314
     * @throws Exception
315
     * @return integer
316
     */
317
    public function actionAddMember($organization, $user)
318
    {
319
        $organization = $this->getOrganization($organization);
320
        $user = $this->getUser($user);
321
        $id = $user->getID();
322
        if (!$organization->addMember($user)) {
323
            throw new Exception('Failed to add member.');
324
        }
325
        echo "User ($id) added to Organization ({$organization->getID()}).\n";
326
    }
327
328
    /**
329
     * Remove member.
330
     * @param Organization|string|intger $organization
331
     * @param User|string|integer $user
332
     * @throws Exception
333
     * @return integer
334
     */
335
    public function actionRemoveMember($organization, $user)
336
    {
337
        $organization = $this->getOrganization($organization);
338
        $user = $this->getUser($user);
339
        $id = $user->getID();
340
        if (!$organization->removeMember($user)) {
341
            throw new Exception('Failed to remove member.');
342
        }
343
        echo "Member ($id) removed.\n";
344
        return static::EXIT_CODE_NORMAL;
345
    }
346
}
347