Passed
Push — master ( 6ef08a...006406 )
by Julito
08:14
created

UserRepository::getCountUsersByUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
nc 1
nop 1
dl 0
loc 11
rs 10
c 2
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Repository\Node;
8
9
use Chamilo\CoreBundle\Entity\AccessUrl;
10
use Chamilo\CoreBundle\Entity\Course;
11
use Chamilo\CoreBundle\Entity\Message;
12
use Chamilo\CoreBundle\Entity\ResourceNode;
13
use Chamilo\CoreBundle\Entity\Session;
14
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
15
use Chamilo\CoreBundle\Entity\TrackELogin;
16
use Chamilo\CoreBundle\Entity\TrackEOnline;
17
use Chamilo\CoreBundle\Entity\User;
18
use Chamilo\CoreBundle\Entity\UserRelUser;
19
use Chamilo\CoreBundle\Repository\ResourceRepository;
20
use Chamilo\CourseBundle\Entity\CSurveyInvitation;
21
use Datetime;
22
use Doctrine\Common\Collections\Collection;
23
use Doctrine\Common\Collections\Criteria;
24
use Doctrine\DBAL\Types\Types;
25
use Doctrine\ORM\Query\Expr\Join;
26
use Doctrine\ORM\QueryBuilder;
27
use Doctrine\Persistence\ManagerRegistry;
28
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
29
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
30
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
31
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
32
33
class UserRepository extends ResourceRepository implements PasswordUpgraderInterface
34
{
35
    protected ?UserPasswordHasherInterface $hasher = null;
36
37
    public function __construct(ManagerRegistry $registry)
38
    {
39
        parent::__construct($registry, User::class);
40
    }
41
42
    public function loadUserByIdentifier(string $identifier): ?User
43
    {
44
        return $this->findOneBy([
45
            'username' => $identifier,
46
        ]);
47
    }
48
49
    public function setHasher(UserPasswordHasherInterface $hasher): void
50
    {
51
        $this->hasher = $hasher;
52
    }
53
54
    public function createUser(): User
55
    {
56
        return new User();
57
    }
58
59
    public function updateUser(User $user, bool $andFlush = true): void
60
    {
61
        $this->updateCanonicalFields($user);
62
        $this->updatePassword($user);
63
        $this->getEntityManager()->persist($user);
64
        if ($andFlush) {
65
            $this->getEntityManager()->flush();
66
        }
67
    }
68
69
    public function canonicalize(string $string): string
70
    {
71
        $encoding = mb_detect_encoding($string, mb_detect_order(), true);
72
73
        return $encoding
74
            ? mb_convert_case($string, MB_CASE_LOWER, $encoding)
75
            : mb_convert_case($string, MB_CASE_LOWER);
76
    }
77
78
    public function updateCanonicalFields(User $user): void
79
    {
80
        $user->setUsernameCanonical($this->canonicalize($user->getUsername()));
81
        $user->setEmailCanonical($this->canonicalize($user->getEmail()));
82
    }
83
84
    public function updatePassword(User $user): void
85
    {
86
        $password = (string) $user->getPlainPassword();
87
        if ('' !== $password) {
88
            $password = $this->hasher->hashPassword($user, $password);
0 ignored issues
show
Bug introduced by
The method hashPassword() does not exist on null. ( Ignorable by Annotation )

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

88
            /** @scrutinizer ignore-call */ 
89
            $password = $this->hasher->hashPassword($user, $password);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
89
            $user->setPassword($password);
90
            $user->eraseCredentials();
91
        }
92
    }
93
94
    public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void
95
    {
96
        /** @var User $user */
97
        $user->setPassword($newHashedPassword);
98
        $this->getEntityManager()->persist($user);
99
        $this->getEntityManager()->flush();
100
    }
101
102
    public function getRootUser(): User
103
    {
104
        $qb = $this->createQueryBuilder('u');
105
        $qb
106
            ->innerJoin(
107
                'u.resourceNode',
108
                'r'
109
            )
110
        ;
111
        $qb
112
            ->where('r.creator = u')
113
            ->andWhere('r.parent IS NULL')
114
            ->getFirstResult()
115
        ;
116
117
        $rootUser = $qb->getQuery()->getSingleResult();
118
119
        if (null === $rootUser) {
120
            throw new UserNotFoundException('Root user not found');
121
        }
122
123
        return $rootUser;
124
    }
125
126
    public function deleteUser(User $user): void
127
    {
128
        $em = $this->getEntityManager();
129
        $type = $user->getResourceNode()->getResourceType();
130
        $rootUser = $this->getRootUser();
131
132
        // User children will be set to the root user.
133
        $criteria = Criteria::create()->where(Criteria::expr()->eq('resourceType', $type));
134
        $userNodeCreatedList = $user->getResourceNodes()->matching($criteria);
135
        /** @var ResourceNode $userCreated */
136
        foreach ($userNodeCreatedList as $userCreated) {
137
            $userCreated->setCreator($rootUser);
138
        }
139
140
        $em->remove($user->getResourceNode());
141
142
        foreach ($user->getGroups() as $group) {
143
            $user->removeGroup($group);
144
        }
145
146
        $em->remove($user);
147
        $em->flush();
148
    }
149
150
    public function addUserToResourceNode(int $userId, int $creatorId): ResourceNode
151
    {
152
        /** @var User $user */
153
        $user = $this->find($userId);
154
        $creator = $this->find($creatorId);
155
156
        $resourceNode = new ResourceNode();
157
        $resourceNode
158
            ->setTitle($user->getUsername())
159
            ->setCreator($creator)
160
            ->setResourceType($this->getResourceType())
161
            //->setParent($resourceNode)
162
        ;
163
164
        $user->setResourceNode($resourceNode);
165
166
        $this->getEntityManager()->persist($resourceNode);
167
        $this->getEntityManager()->persist($user);
168
169
        return $resourceNode;
170
    }
171
172
    public function addRoleListQueryBuilder(array $roleList, QueryBuilder $qb = null): QueryBuilder
173
    {
174
        $qb = $this->getOrCreateQueryBuilder($qb, 'u');
175
        if (!empty($roleList)) {
176
            $qb
177
                ->andWhere('u.roles IN (:roles)')
178
                ->setParameter('roles', $roleList, Types::ARRAY)
179
            ;
180
        }
181
182
        return $qb;
183
    }
184
185
    public function findByUsername(string $username): ?User
186
    {
187
        $user = $this->findOneBy([
188
            'username' => $username,
189
        ]);
190
191
        if (null === $user) {
192
            throw new UserNotFoundException(sprintf("User with id '%s' not found.", $username));
193
        }
194
195
        return $user;
196
    }
197
198
    /**
199
     * Get a filtered list of user by role and (optionally) access url.
200
     *
201
     * @param string $keyword     The query to filter
202
     * @param int    $accessUrlId The access URL ID
203
     *
204
     * @return User[]
205
     */
206
    public function findByRole(string $role, string $keyword, int $accessUrlId = 0)
207
    {
208
        $qb = $this->createQueryBuilder('u');
209
210
        $this->addAccessUrlQueryBuilder($accessUrlId, $qb);
211
        $this->addRoleQueryBuilder($role, $qb);
212
        $this->addSearchByKeywordQueryBuilder($keyword, $qb);
213
214
        return $qb->getQuery()->getResult();
215
    }
216
217
    /**
218
     * Get course user relationship based in the course_rel_user table.
219
     *
220
     * @return Course[]
221
     */
222
    public function getCourses(User $user, AccessUrl $url, int $status, string $keyword = '')
223
    {
224
        $qb = $this->createQueryBuilder('u');
225
226
        $qb
227
            //->select('DISTINCT course')
228
            ->innerJoin('u.courses', 'courseRelUser')
229
            ->innerJoin('courseRelUser.course', 'course')
230
            ->innerJoin('course.urls', 'accessUrlRelCourse')
231
            ->innerJoin('accessUrlRelCourse.url', 'url')
232
            ->where('url = :url')
233
            ->andWhere('courseRelUser.user = :user')
234
            ->andWhere('courseRelUser.status = :status')
235
            ->setParameters(
236
                [
237
                    'user' => $user,
238
                    'url' => $url,
239
                    'status' => $status,
240
                ]
241
            )
242
        //    ->addSelect('courseRelUser')
243
        ;
244
245
        if (!empty($keyword)) {
246
            $qb
247
                ->andWhere('course.title like = :keyword OR course.code like = :keyword')
248
                ->setParameter('keyword', $keyword)
249
            ;
250
        }
251
252
        $qb->orderBy('course.title', Criteria::DESC);
253
254
        $query = $qb->getQuery();
255
256
        return $query->getResult();
257
    }
258
259
    /*
260
    public function getTeachers()
261
    {
262
        $queryBuilder = $this->repository->createQueryBuilder('u');
263
264
        // Selecting course info.
265
        $queryBuilder
266
            ->select('u')
267
            ->where('u.groups.id = :groupId')
268
            ->setParameter('groupId', 1);
269
270
        $query = $queryBuilder->getQuery();
271
272
        return $query->execute();
273
    }*/
274
275
    /*public function getUsers($group)
276
    {
277
        $queryBuilder = $this->repository->createQueryBuilder('u');
278
279
        // Selecting course info.
280
        $queryBuilder
281
            ->select('u')
282
            ->where('u.groups = :groupId')
283
            ->setParameter('groupId', $group);
284
285
        $query = $queryBuilder->getQuery();
286
287
        return $query->execute();
288
    }*/
289
290
    /**
291
     * Get the coaches for a course within a session.
292
     *
293
     * @param Session $session The session
294
     * @param Course  $course  The course
295
     */
296
    public function getCoachesForSessionCourse(Session $session, Course $course): Collection
297
    {
298
        $qb = $this->createQueryBuilder('u');
299
300
        $qb->select('u')
301
            ->innerJoin(
302
                'ChamiloCoreBundle:SessionRelCourseRelUser',
303
                'scu',
304
                Join::WITH,
305
                'scu.user = u'
306
            )
307
            ->where(
308
                $qb->expr()->andX(
309
                    $qb->expr()->eq('scu.session', $session->getId()),
310
                    $qb->expr()->eq('scu.course', $course->getId()),
311
                    $qb->expr()->eq('scu.status', SessionRelCourseRelUser::STATUS_COURSE_COACH)
312
                )
313
            )
314
        ;
315
316
        return $qb->getQuery()->getResult();
317
    }
318
319
    /**
320
     * Get course user relationship based in the course_rel_user table.
321
     *
322
     * @return array
323
     */
324
    /*public function getCourses(User $user)
325
    {
326
        $qb = $this->createQueryBuilder('user');
327
328
        // Selecting course info.
329
        $qb->select('c');
330
331
        // Loading User.
332
        //$qb->from('Chamilo\CoreBundle\Entity\User', 'u');
333
334
        // Selecting course
335
        $qb->innerJoin('Chamilo\CoreBundle\Entity\Course', 'c');
336
337
        //@todo check app settings
338
        //$qb->add('orderBy', 'u.lastname ASC');
339
340
        $wherePart = $qb->expr()->andx();
341
342
        // Get only users subscribed to this course
343
        $wherePart->add($qb->expr()->eq('user.userId', $user->getUserId()));
344
345
        $qb->where($wherePart);
346
        $query = $qb->getQuery();
347
348
        return $query->execute();
349
    }
350
351
    public function getTeachers()
352
    {
353
        $qb = $this->createQueryBuilder('u');
354
355
        // Selecting course info.
356
        $qb
357
            ->select('u')
358
            ->where('u.groups.id = :groupId')
359
            ->setParameter('groupId', 1);
360
361
        $query = $qb->getQuery();
362
363
        return $query->execute();
364
    }*/
365
366
    /*public function getUsers($group)
367
    {
368
        $qb = $this->createQueryBuilder('u');
369
370
        // Selecting course info.
371
        $qb
372
            ->select('u')
373
            ->where('u.groups = :groupId')
374
            ->setParameter('groupId', $group);
375
376
        $query = $qb->getQuery();
377
378
        return $query->execute();
379
    }*/
380
381
    /**
382
     * Get the sessions admins for a user.
383
     *
384
     * @return array
385
     */
386
    public function getSessionAdmins(User $user)
387
    {
388
        $qb = $this->createQueryBuilder('u');
389
        $qb
390
            ->distinct()
391
            ->innerJoin(
392
                'ChamiloCoreBundle:SessionRelUser',
393
                'su',
394
                Join::WITH,
395
                'u = su.user'
396
            )
397
            ->innerJoin(
398
                'ChamiloCoreBundle:SessionRelCourseRelUser',
399
                'scu',
400
                Join::WITH,
401
                'su.session = scu.session'
402
            )
403
            ->where(
404
                $qb->expr()->eq('scu.user', $user->getId())
405
            )
406
            ->andWhere(
407
                $qb->expr()->eq('su.relationType', SESSION_RELATION_TYPE_RRHH)
408
            )
409
        ;
410
411
        return $qb->getQuery()->getResult();
412
    }
413
414
    /**
415
     * Get number of users in URL.
416
     *
417
     * @return int
418
     */
419
    public function getCountUsersByUrl(AccessUrl $url)
420
    {
421
        return $this->createQueryBuilder('u')
422
            ->select('COUNT(a)')
423
            ->innerJoin('a.portals', 'p')
424
            ->where('p.portal = :p')
425
            ->setParameters([
426
                'p' => $url,
427
            ])
428
            ->getQuery()
429
            ->getSingleScalarResult()
430
        ;
431
    }
432
433
    /**
434
     * Get number of users in URL.
435
     *
436
     * @return int
437
     */
438
    public function getCountTeachersByUrl(AccessUrl $url)
439
    {
440
        $qb = $this->createQueryBuilder('u');
441
442
        return $qb
443
            ->select('COUNT(u)')
444
            ->innerJoin('a.portals', 'p')
445
            ->where('p.portal = :p')
446
            ->andWhere($qb->expr()->in('u.roles', ['ROLE_TEACHER']))
447
            ->setParameters([
448
                'p' => $url,
449
            ])
450
            ->getQuery()
451
            ->getSingleScalarResult()
452
        ;
453
    }
454
455
    /**
456
     * Find potential users to send a message.
457
     *
458
     * @todo remove  api_is_platform_admin
459
     *
460
     * @param int    $currentUserId The current user ID
461
     * @param string $searchFilter  Optional. The search text to filter the user list
462
     * @param int    $limit         Optional. Sets the maximum number of results to retrieve
463
     *
464
     * @return User[]
465
     */
466
    public function findUsersToSendMessage(int $currentUserId, string $searchFilter = null, int $limit = 10)
467
    {
468
        $allowSendMessageToAllUsers = api_get_setting('allow_send_message_to_all_platform_users');
469
        $accessUrlId = api_get_multiple_access_url() ? api_get_current_access_url_id() : 1;
470
471
        $messageTool = 'true' === api_get_setting('allow_message_tool');
472
        if (!$messageTool) {
473
            return [];
474
        }
475
476
        $qb = $this->createQueryBuilder('u');
477
        $this->addActiveAndNotAnonUserQueryBuilder($qb);
478
        $this->addAccessUrlQueryBuilder($accessUrlId, $qb);
479
480
        $dql = null;
481
        if ('true' === api_get_setting('allow_social_tool')) {
482
            // All users
483
            if ('true' === $allowSendMessageToAllUsers || api_is_platform_admin()) {
484
                $this->addNotCurrentUserQueryBuilder($currentUserId, $qb);
485
            /*$dql = "SELECT DISTINCT U
486
                    FROM ChamiloCoreBundle:User U
487
                    LEFT JOIN ChamiloCoreBundle:AccessUrlRelUser R
488
                    WITH U = R.user
489
                    WHERE
490
                        U.active = 1 AND
491
                        U.status != 6  AND
492
                        U.id != {$currentUserId} AND
493
                        R.url = {$accessUrlId}";*/
494
            } else {
495
                $this->addOnlyMyFriendsQueryBuilder($currentUserId, $qb);
496
                /*$dql = 'SELECT DISTINCT U
497
                        FROM ChamiloCoreBundle:AccessUrlRelUser R, ChamiloCoreBundle:UserRelUser UF
498
                        INNER JOIN ChamiloCoreBundle:User AS U
499
                        WITH UF.friendUserId = U
500
                        WHERE
501
                            U.active = 1 AND
502
                            U.status != 6 AND
503
                            UF.relationType NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.") AND
504
                            UF.user = {$currentUserId} AND
505
                            UF.friendUserId != {$currentUserId} AND
506
                            U = R.user AND
507
                            R.url = {$accessUrlId}";*/
508
            }
509
        } else {
510
            if ('true' === $allowSendMessageToAllUsers) {
511
                $this->addNotCurrentUserQueryBuilder($currentUserId, $qb);
512
            } else {
513
                return [];
514
            }
515
516
            /*else {
517
                $time_limit = (int) api_get_setting('time_limit_whosonline');
518
                $online_time = time() - ($time_limit * 60);
519
                $limit_date = api_get_utc_datetime($online_time);
520
                $dql = "SELECT DISTINCT U
521
                        FROM ChamiloCoreBundle:User U
522
                        INNER JOIN ChamiloCoreBundle:TrackEOnline T
523
                        WITH U.id = T.loginUserId
524
                        WHERE
525
                          U.active = 1 AND
526
                          T.loginDate >= '".$limit_date."'";
527
            }*/
528
        }
529
530
        if (!empty($searchFilter)) {
531
            $this->addSearchByKeywordQueryBuilder($searchFilter, $qb);
532
        }
533
534
        return $qb->getQuery()->getResult();
535
    }
536
537
    /**
538
     * Get the list of HRM who have assigned this user.
539
     *
540
     * @return User[]
541
     */
542
    public function getAssignedHrmUserList(int $userId, int $urlId)
543
    {
544
        $qb = $this->createQueryBuilder('u');
545
        $this->addAccessUrlQueryBuilder($urlId, $qb);
546
        $this->addActiveAndNotAnonUserQueryBuilder($qb);
547
        $this->addUserRelUserQueryBuilder($userId, UserRelUser::USER_RELATION_TYPE_RRHH, $qb);
548
549
        return $qb->getQuery()->getResult();
550
    }
551
552
    /**
553
     * Get the last login from the track_e_login table.
554
     * This might be different from user.last_login in the case of legacy users
555
     * as user.last_login was only implemented in 1.10 version with a default
556
     * value of NULL (not the last record from track_e_login).
557
     *
558
     * @return null|TrackELogin
559
     */
560
    public function getLastLogin(User $user)
561
    {
562
        $qb = $this->createQueryBuilder('u');
563
564
        return $qb
565
            ->select('l')
566
            ->innerJoin('u.logins', 'l')
567
            ->where(
568
                $qb->expr()->eq('l.user', $user)
569
            )
570
            ->setMaxResults(1)
571
            ->orderBy('u.loginDate', Criteria::DESC)
572
            ->getQuery()
573
            ->getOneOrNullResult()
574
        ;
575
    }
576
577
    public function addAccessUrlQueryBuilder(int $accessUrlId, QueryBuilder $qb = null): QueryBuilder
578
    {
579
        $qb = $this->getOrCreateQueryBuilder($qb, 'u');
580
        $qb
581
            ->innerJoin('u.portals', 'p')
582
            ->andWhere('p.url = :url')
583
            ->setParameter('url', $accessUrlId, Types::INTEGER)
584
        ;
585
586
        return $qb;
587
    }
588
589
    public function addActiveAndNotAnonUserQueryBuilder(QueryBuilder $qb = null): QueryBuilder
590
    {
591
        $qb = $this->getOrCreateQueryBuilder($qb, 'u');
592
        $qb
593
            ->andWhere('u.active = 1')
594
            ->andWhere('u.status <> :status')
595
            ->setParameter('status', User::ANONYMOUS, Types::INTEGER)
596
        ;
597
598
        return $qb;
599
    }
600
601
    public function addExpirationDateQueryBuilder(QueryBuilder $qb = null): QueryBuilder
602
    {
603
        $qb = $this->getOrCreateQueryBuilder($qb, 'u');
604
        $qb
605
            ->andWhere('u.registrationDate IS NULL OR u.registrationDate > :now')
606
            ->setParameter('now', new Datetime(), Types::DATETIME_MUTABLE)
607
        ;
608
609
        return $qb;
610
    }
611
612
    /**
613
     * @return CSurveyInvitation[]
614
     */
615
    public function getUserPendingInvitations(User $user)
616
    {
617
        $qb = $this->createQueryBuilder('u');
618
        $qb
619
            ->select('s')
620
            ->innerJoin('u.surveyInvitations', 's')
621
            ->andWhere('s.user = :u')
622
            ->andWhere('s.availFrom <= :now AND s.availTill >= :now')
623
            ->andWhere('s.answered = 0')
624
            ->setParameters([
625
                'now' => new Datetime(),
626
                'u' => $user,
627
            ])
628
            ->orderBy('s.availTill', Criteria::ASC)
629
        ;
630
631
        return $qb->getQuery()->getResult();
632
    }
633
634
    private function addRoleQueryBuilder(string $role, QueryBuilder $qb = null): QueryBuilder
635
    {
636
        $qb = $this->getOrCreateQueryBuilder($qb, 'u');
637
        $qb
638
            ->andWhere('u.roles LIKE :roles')
639
            ->setParameter('roles', '%"'.$role.'"%', Types::STRING)
640
        ;
641
642
        return $qb;
643
    }
644
645
    private function addSearchByKeywordQueryBuilder(string $keyword, QueryBuilder $qb = null): QueryBuilder
646
    {
647
        $qb = $this->getOrCreateQueryBuilder($qb, 'u');
648
        $qb
649
            ->andWhere('
650
                u.firstname LIKE :keyword OR
651
                u.lastname LIKE :keyword OR
652
                u.email LIKE :keyword OR
653
                u.username LIKE :keyword
654
            ')
655
            ->setParameter('keyword', "%$keyword%", Types::STRING)
656
            ->orderBy('u.firstname', Criteria::ASC)
657
        ;
658
659
        return $qb;
660
    }
661
662
    private function addUserRelUserQueryBuilder(int $userId, int $relationType, QueryBuilder $qb = null): QueryBuilder
663
    {
664
        $qb = $this->getOrCreateQueryBuilder($qb, 'u');
665
        $qb->leftJoin('u.friends', 'relations');
666
        $qb
667
            ->andWhere('relations.relationType = :relationType')
668
            ->andWhere('relations.user = :userRelation AND relations.friend <> :userRelation')
669
            ->setParameter('relationType', $relationType)
670
            ->setParameter('userRelation', $userId)
671
        ;
672
673
        return $qb;
674
    }
675
676
    private function addOnlyMyFriendsQueryBuilder(int $userId, QueryBuilder $qb = null): QueryBuilder
677
    {
678
        $qb = $this->getOrCreateQueryBuilder($qb, 'u');
679
        $qb
680
            ->leftJoin('u.friends', 'relations')
681
            ->andWhere(
682
                $qb->expr()->notIn(
683
                    'relations.relationType',
684
                    [UserRelUser::USER_RELATION_TYPE_DELETED, UserRelUser::USER_RELATION_TYPE_RRHH]
685
                )
686
            )
687
            ->andWhere('relations.user = :user AND relations.friend <> :user')
688
            ->setParameter('user', $userId, Types::INTEGER)
689
        ;
690
691
        return $qb;
692
    }
693
694
    private function addNotCurrentUserQueryBuilder(int $userId, QueryBuilder $qb = null): QueryBuilder
695
    {
696
        $qb = $this->getOrCreateQueryBuilder($qb, 'u');
697
        $qb
698
            ->andWhere('u.id <> :id')
699
            ->setParameter('id', $userId, Types::INTEGER)
700
        ;
701
702
        return $qb;
703
    }
704
}
705