Completed
Pull Request — master (#607)
by Richard
14:27
created

XoopsMemberHandler::getGroup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 1
1
<?php
2
/**
3
 * XOOPS Kernel Class
4
 *
5
 * You may not change or alter any portion of this comment or credits
6
 * of supporting developers from this source code or any supporting source code
7
 * which is considered copyrighted (c) material of the original comment or credit authors.
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * @copyright       XOOPS Project (http://xoops.org)
13
 * @license         GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package         kernel
15
 * @since           2.0.0
16
 * @author          Kazumi Ono (AKA onokazu) http://www.myweb.ne.jp/, http://jp.xoops.org/
17
 * @version         $Id$
18
 */
19
20
namespace Xoops\Core\Kernel\Handlers;
21
22
use Xoops\Core\Database\Connection;
23
use Xoops\Core\FixedGroups;
24
use Xoops\Core\Kernel\Criteria;
25
use Xoops\Core\Kernel\CriteriaCompo;
26
use Xoops\Core\Kernel\CriteriaElement;
27
use Doctrine\DBAL\FetchMode;
28
29
/**
30
 * XOOPS member handler class.
31
 * This class provides simple interface (a facade class) for handling groups/users/
32
 * membership data.
33
 *
34
 * @category  Xoops\Core\Kernel\Handlers\XoopsMemberHandler
35
 * @package   Xoops\Core\Kernel
36
 * @author    Kazumi Ono <[email protected]>
37
 * @copyright 2000-2015 XOOPS Project (http://xoops.org)
38
 * @license   GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
39
 * @link      http://xoops.org
40
 */
41
class XoopsMemberHandler
42
{
43
44
    /**
45
     * @var XoopsGroupPermHandler group handler(DAO) class
46
     */
47
    private $groupHandler;
48
49
    /**
50
     * @var XoopsUserHandler user handler(DAO) class
51
     */
52
    private $userHandler;
53
54
    /**
55
     * @var XoopsMembershipHandler membership handler(DAO) class
56
     */
57
    private $membershipHandler;
58
59
    /**
60
     * holds temporary user objects
61
     */
62
    private $membersWorkingList = array();
63
64
    /**
65
     * Constructor
66
     *
67
     * @param Connection|null $db database connection
68
     */
69 8
    public function __construct(Connection $db = null)
0 ignored issues
show
Unused Code introduced by
The parameter $db is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

69
    public function __construct(/** @scrutinizer ignore-unused */ Connection $db = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
70
    {
71 8
        $this->groupHandler = \Xoops::getInstance()->getHandlerGroup();
0 ignored issues
show
Documentation Bug introduced by
It seems like Xoops::getInstance()->getHandlerGroup() of type Xoops\Core\Kernel\Handlers\XoopsGroupHandler is incompatible with the declared type Xoops\Core\Kernel\Handlers\XoopsGroupPermHandler of property $groupHandler.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
72 8
        $this->userHandler = \Xoops::getInstance()->getHandlerUser();
73 8
        $this->membershipHandler = \Xoops::getInstance()->getHandlerMembership();
74 8
    }
75
76
    /**
77
     * create a new group
78
     *
79
     * @return XoopsGroup reference to the new group
80
     */
81 2
    public function createGroup()
82
    {
83 2
        $inst = $this->groupHandler->create();
84 2
        return $inst;
85
    }
86
87
    /**
88
     * create a new user
89
     *
90
     * @return XoopsUser reference to the new user
91
     */
92 2
    public function createUser()
93
    {
94 2
        $inst = $this->userHandler->create();
95 2
        return $inst;
96
    }
97
98
    /**
99
     * retrieve a group
100
     *
101
     * @param int $id ID for the group
102
     *
103
     * @return XoopsGroup reference to the group
104
     */
105 1
    public function getGroup($id)
106
    {
107 1
        return $this->groupHandler->get($id);
108
    }
109
110
    /**
111
     * retrieve a user
112
     *
113
     * @param int $id ID for the user
114
     *
115
     * @return XoopsUser
116
     */
117 5
    public function getUser($id)
118
    {
119 5
        if (!isset($this->membersWorkingList[$id])) {
120 2
            $this->membersWorkingList[$id] = $this->userHandler->get($id);
121
        }
122 5
        return $this->membersWorkingList[$id];
123
    }
124
125
    /**
126
     * delete a group
127
     *
128
     * @param XoopsGroup $group the group to delete
129
     *
130
     * @return bool FALSE if failed
131
     */
132 1
    public function deleteGroup(XoopsGroup $group)
133
    {
134 1
        $ret = $this->groupHandler->delete($group);
135 1
        $this->membershipHandler->deleteAll(new Criteria('groupid', $group->getVar('groupid')));
0 ignored issues
show
Bug introduced by
It seems like $group->getVar('groupid') can also be of type string[]; however, parameter $value of Xoops\Core\Kernel\Criteria::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

135
        $this->membershipHandler->deleteAll(new Criteria('groupid', /** @scrutinizer ignore-type */ $group->getVar('groupid')));
Loading history...
136 1
        return $ret;
137
    }
138
139
    /**
140
     * delete a user
141
     *
142
     * @param XoopsUser $user reference to the user to delete
143
     *
144
     * @return bool FALSE if failed
145
     */
146 1
    public function deleteUser(XoopsUser $user)
147
    {
148 1
        $ret = $this->userHandler->delete($user);
149 1
        $this->membershipHandler->deleteAll(new Criteria('uid', $user->getVar('uid')));
0 ignored issues
show
Bug introduced by
It seems like $user->getVar('uid') can also be of type string[]; however, parameter $value of Xoops\Core\Kernel\Criteria::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

149
        $this->membershipHandler->deleteAll(new Criteria('uid', /** @scrutinizer ignore-type */ $user->getVar('uid')));
Loading history...
150 1
        return $ret;
151
    }
152
153
    /**
154
     * insert a group into the database
155
     *
156
     * @param XoopsGroup $group the group to insert
157
     *
158
     * @return bool TRUE if already in database and unchanged, FALSE on failure
159
     */
160
    public function insertGroup(XoopsGroup $group)
161
    {
162
        return $this->groupHandler->insert($group);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->groupHandler->insert($group) also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
163
    }
164
165
    /**
166
     * insert a user into the database
167
     *
168
     * @param XoopsUser $user  the user to insert
169
     * @param bool      $force force insert
170
     *
171
     * @return bool TRUE if already in database and unchanged, FALSE on failure
172
     */
173 1
    public function insertUser(XoopsUser $user, $force = false)
174
    {
175 1
        return $this->userHandler->insert($user, $force);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->userHandler->insert($user, $force) also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
176
    }
177
178
    /**
179
     * retrieve groups from the database
180
     *
181
     * @param CriteriaElement|null $criteria  criteria to match
182
     * @param bool                 $id_as_key use the group's ID as key for the array?
183
     *
184
     * @return XoopsGroup[]
185
     */
186
    public function getGroups(CriteriaElement $criteria = null, $id_as_key = false)
187
    {
188
        return $this->groupHandler->getObjects($criteria, $id_as_key);
189
    }
190
191
    /**
192
     * retrieve users from the database
193
     *
194
     * @param CriteriaElement|null $criteria  criteria to match
195
     * @param bool                 $id_as_key use the group's ID as key for the array?
196
     *
197
     * @return XoopsUser[]
198
     */
199 2
    public function getUsers(CriteriaElement $criteria = null, $id_as_key = false)
200
    {
201 2
        return $this->userHandler->getObjects($criteria, $id_as_key);
202
    }
203
204
    /**
205
     * get a list of groupnames and their IDs
206
     *
207
     * @param CriteriaElement|null $criteria criteria to match
208
     *
209
     * @return array associative array of group-IDs and names
210
     */
211 6
    public function getGroupList(CriteriaElement $criteria = null)
212
    {
213 6
        $realCriteria = new CriteriaCompo($criteria);
214 6
        $realCriteria->add(new Criteria('groupid', FixedGroups::REMOVED, '!='));
215 6
        $groups = $this->groupHandler->getObjects($realCriteria, true);
216 6
        $ret = array();
217 6
        foreach (array_keys($groups) as $i) {
218 6
            $ret[$i] = $groups[$i]->getVar('name');
219
        }
220 6
        return $ret;
221
    }
222
223
    /**
224
     * get a list of usernames and their IDs
225
     *
226
     * @param CriteriaElement|null $criteria criteria to match
227
     *
228
     * @return array associative array of user-IDs and names
229
     */
230 2
    public function getUserList(CriteriaElement $criteria = null)
231
    {
232 2
        $users = $this->userHandler->getObjects($criteria, true);
233 2
        $ret = array();
234 2
        foreach (array_keys($users) as $i) {
235 2
            $ret[$i] = $users[$i]->getVar('uname');
236
        }
237 2
        return $ret;
238
    }
239
240
    /**
241
     * add a user to a group
242
     *
243
     * @param int $group_id ID of the group
244
     * @param int $user_id  ID of the user
245
     *
246
     * @return XoopsMembership
247
     */
248
    public function addUserToGroup($group_id, $user_id)
249
    {
250
        $mship = $this->membershipHandler->create();
251
        $mship->setVar('groupid', $group_id);
252
        $mship->setVar('uid', $user_id);
253
        return $this->membershipHandler->insert($mship);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->membershipHandler->insert($mship) returns the type false|integer which is incompatible with the documented return type Xoops\Core\Kernel\Handlers\XoopsMembership.
Loading history...
254
    }
255
256
    /**
257
     * remove a list of users from a group
258
     *
259
     * @param int   $group_id ID of the group
260
     * @param array $user_ids array of user-IDs
261
     *
262
     * @return bool success?
263
     */
264
    public function removeUsersFromGroup($group_id, $user_ids = array())
265
    {
266
        $criteria = new CriteriaCompo();
267
        $criteria->add(new Criteria('groupid', $group_id));
268
        $criteria2 = new CriteriaCompo();
269
        foreach ($user_ids as $uid) {
270
            $criteria2->add(new Criteria('uid', $uid), 'OR');
271
        }
272
        $criteria->add($criteria2);
273
        return $this->membershipHandler->deleteAll($criteria);
274
    }
275
276
    /**
277
     * get a list of users belonging to a group
278
     *
279
     * @param int  $group_id ID of the group
280
     * @param bool $asobject return the users as objects?
281
     * @param int  $limit    number of users to return
282
     * @param int  $start    index of the first user to return
283
     *
284
     * @return array Array of XoopsUser objects (if $asobject is TRUE)
285
     *                or of associative arrays matching the record structure
286
     */
287 1
    public function getUsersByGroup($group_id, $asobject = false, $limit = 0, $start = 0)
288
    {
289 1
        $user_ids = $this->membershipHandler->getUsersByGroup($group_id, $limit, $start);
290 1
        if (!$asobject) {
291
            return $user_ids;
292
        } else {
293 1
            $ret = array();
294 1
            foreach ($user_ids as $u_id) {
295 1
                $user = $this->getUser($u_id);
296 1
                if (is_object($user)) {
297 1
                    $ret[] = $user;
298
                }
299 1
                unset($user);
300
            }
301 1
            return $ret;
302
        }
303
    }
304
305
    /**
306
     * get a list of groups that a user is member of
307
     *
308
     * @param int  $user_id  ID of the user
309
     * @param bool $asobject return groups as XoopsGroup objects or arrays?
310
     *
311
     * @return array array of objects or arrays
312
     */
313 4
    public function getGroupsByUser($user_id, $asobject = false)
314
    {
315 4
        $ret = array();
316 4
        $group_ids = $this->membershipHandler->getGroupsByUser($user_id);
317 4
        if (!$asobject) {
318 4
            return $group_ids;
319
        } else {
320
            foreach ($group_ids as $g_id) {
321
                $ret[] = $this->getGroup($g_id);
322
            }
323
            return $ret;
324
        }
325
    }
326
327
    /**
328
     * log in a user
329
     *
330
     * @param string $uname username as entered in the login form
331
     * @param string $pwd   password entered in the login form
332
     *
333
     * @return mixed object  XoopsUser reference to the logged in user
334
     *               boolean FALSE if failed to log in
335
     *
336
     * @todo - md5 support should be completely removed eventually
337
     */
338 2
    public function loginUser($uname, $pwd)
339
    {
340 2
        $criteria = new Criteria('uname', $uname);
341
        //$criteria->add(new Criteria('pass', md5($pwd)));
342 2
        $user = $this->userHandler->getObjects($criteria, false);
343 2
        if (!$user || count($user) != 1) {
344 1
            return false;
345
        }
346
347 1
        $hash = $user[0]->pass();
348 1
        $type = substr($user[0]->pass(), 0, 1);
349
        // see if we have a crypt like signature, old md5 hash is just hex digits
350 1
        if ($type==='$') {
351 1
            if (!password_verify($pwd, $hash)) {
352 1
                return false;
353
            }
354
            // check if hash uses the best algorithm (i.e. after a PHP upgrade)
355
            $rehash = password_needs_rehash($hash, PASSWORD_DEFAULT);
356
        } else {
357
            if ($hash!=md5($pwd)) {
358
                return false;
359
            }
360
            $rehash = true; // automatically update old style
361
        }
362
        // hash used an old algorithm, so make it stronger
363
        if ($rehash) {
364
            $user[0]->setVar('pass', password_hash($pwd, PASSWORD_DEFAULT));
365
            $this->userHandler->insert($user[0]);
366
        }
367
        return $user[0];
368
    }
369
370
    /**
371
     * count users matching certain conditions
372
     *
373
     * @param CriteriaElement|null $criteria criteria to match
374
     *
375
     * @return int
376
     */
377 2
    public function getUserCount(CriteriaElement $criteria = null)
378
    {
379 2
        return $this->userHandler->getCount($criteria);
380
    }
381
382
    /**
383
     * count users belonging to a group
384
     *
385
     * @param int $group_id ID of the group
386
     *
387
     * @return int
388
     */
389
    public function getUserCountByGroup($group_id)
390
    {
391
        return $this->membershipHandler->getCount(new Criteria('groupid', $group_id));
392
    }
393
394
    /**
395
     * updates a single field in a users record
396
     *
397
     * @param XoopsUser $user       user object to update
398
     * @param string    $fieldName  name of the field to update
399
     * @param string    $fieldValue updated value for the field
400
     *
401
     * @return bool TRUE if success or unchanged, FALSE on failure
402
     */
403 1
    public function updateUserByField(XoopsUser $user, $fieldName, $fieldValue)
404
    {
405 1
        $user->setVar($fieldName, $fieldValue);
406 1
        return $this->insertUser($user);
407
    }
408
409
    /**
410
     * updates a single field in a users record
411
     *
412
     * @param string          $fieldName  name of the field to update
413
     * @param string          $fieldValue updated value for the field
414
     * @param CriteriaElement $criteria   criteria to match
415
     *
416
     * @return bool TRUE if success or unchanged, FALSE on failure
417
     */
418
    public function updateUsersByField($fieldName, $fieldValue, CriteriaElement $criteria = null)
419
    {
420
        if (is_null($criteria)) {
421
            $criteria = new Criteria(''); // empty criteria resolves to 'WHERE (1)'
422
        }
423
        return $this->userHandler->updateAll($fieldName, $fieldValue, $criteria);
424
    }
425
426
    /**
427
     * activate a user
428
     *
429
     * @param XoopsUser $user the user object
430
     *
431
     * @return bool successful?
432
     */
433
    public function activateUser(XoopsUser $user)
434
    {
435
        if ($user->getVar('level') != 0) {
436
            return true;
437
        }
438
        $user->setVar('level', 1);
439
        return $this->userHandler->insert($user, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->userHandler->insert($user, true) also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
440
    }
441
442
    /**
443
     * Get a list of users belonging to certain groups and matching criteria
444
     * Temporary solution
445
     *
446
     * @param array           $groups    IDs of groups
447
     * @param CriteriaElement $criteria  criteria to match
448
     * @param bool            $asobject  return the users as objects?
449
     * @param bool            $id_as_key use the UID as key for the array if $asobject is TRUE
450
     *
451
     * @return array Array of XoopsUser objects (if $asobject is TRUE)
452
     * or of associative arrays matching the record structure in the database.
453
     */
454
    public function getUsersByGroupLink(
455
        $groups,
456
        CriteriaElement $criteria = null,
457
        $asobject = false,
458
        $id_as_key = false
459
    ) {
460
461
        $qb = $this->userHandler->db2->createXoopsQueryBuilder();
462
        $eb = $qb->expr();
463
464
        $qb ->select('DISTINCT ' . ($asobject ? 'u.*' : 'u.uid'))
465
            ->fromPrefix('system_user', 'u')
466
            ->leftJoinPrefix('u', 'system_usergroup', 'm', 'm.uid = u.uid');
467
468
        $where = false;
469
        if (!empty($groups)) {
470
            $qb->where($eb->in('m.groupid', $groups));
471
            $where = true;
472
        }
473
        if (isset($criteria) && ($criteria instanceof CriteriaElement)) {
474
            $whereMode = $where ? 'AND' : '';
475
            $sql[] = $criteria->renderQb($qb, $whereMode);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sql was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql = array(); before regardless.
Loading history...
476
        }
477
478
        $ret = array();
479
480
        if (!$result = $qb->execute()) {
481
            return $ret;
482
        }
483
484
        while ($myrow = $result->fetch(FetchMode::ASSOCIATIVE)) {
485
            if ($asobject) {
486
                $user = new XoopsUser();
487
                $user->assignVars($myrow);
488
                if (!$id_as_key) {
489
                    $ret[] = $user;
490
                } else {
491
                    $ret[$myrow['uid']] = $user;
492
                }
493
                unset($user);
494
            } else {
495
                $ret[] = $myrow['uid'];
496
            }
497
        }
498
        return $ret;
499
    }
500
501
    /**
502
     * Get count of users belonging to certain groups and matching criteria
503
     * Temporary solution
504
     *
505
     * @param array                $groups   IDs of groups
506
     * @param CriteriaElement|null $criteria criteria to match
507
     *
508
     * @return int count of users
509
     */
510
    public function getUserCountByGroupLink($groups, $criteria = null)
511
    {
512
        $qb = $this->userHandler->db2->createXoopsQueryBuilder();
513
        $eb = $qb->expr();
514
515
        $qb ->select('COUNT(DISTINCT u.uid)')
516
            ->fromPrefix('system_user', 'u')
517
            ->leftJoinPrefix('u', 'system_usergroup', 'm', 'm.uid = u.uid');
518
519
        $where = false;
520
        if (!empty($groups)) {
521
            $qb->where($eb->in('m.groupid', $groups));
522
            $where = true;
523
        }
524
        if (isset($criteria) && ($criteria instanceof CriteriaElement)) {
525
            $whereMode = $where ? 'AND' : '';
526
            $criteria->renderQb($qb, $whereMode);
527
        }
528
529
        $result = $qb->execute();
530
        $ret = $result->fetchColumn(0);
531
532
        return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret could also return false which is incompatible with the documented return type integer. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
533
    }
534
}
535