1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/* For licensing terms, see /license.txt */ |
6
|
|
|
|
7
|
|
|
namespace Chamilo\CoreBundle\Repository; |
8
|
|
|
|
9
|
|
|
use Chamilo\CoreBundle\Entity\AccessUrl; |
10
|
|
|
use Chamilo\CoreBundle\Entity\Course; |
11
|
|
|
use Chamilo\CoreBundle\Entity\Session; |
12
|
|
|
use Chamilo\CoreBundle\Entity\SessionRelCourse; |
13
|
|
|
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser; |
14
|
|
|
use Chamilo\CoreBundle\Entity\SessionRelUser; |
15
|
|
|
use Chamilo\CoreBundle\Entity\User; |
16
|
|
|
use Chamilo\CoreBundle\Settings\SettingsManager; |
17
|
|
|
use DateTime; |
18
|
|
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; |
19
|
|
|
use Doctrine\Common\Collections\ArrayCollection; |
20
|
|
|
use Doctrine\ORM\Query\Expr\Join; |
21
|
|
|
use Doctrine\ORM\QueryBuilder; |
22
|
|
|
use Doctrine\Persistence\ManagerRegistry; |
23
|
|
|
use Exception; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @author Julio Montoya <[email protected]> |
27
|
|
|
*/ |
28
|
|
|
class SessionRepository extends ServiceEntityRepository |
29
|
|
|
{ |
30
|
|
|
public function __construct( |
31
|
|
|
ManagerRegistry $registry, |
32
|
|
|
private readonly SettingsManager $settingsManager, |
33
|
|
|
) { |
34
|
|
|
parent::__construct($registry, Session::class); |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
public function create(): ?Session |
38
|
|
|
{ |
39
|
|
|
return new Session(); |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
public function update(Session $session): void |
43
|
|
|
{ |
44
|
|
|
$this->getEntityManager()->persist($session); |
45
|
|
|
$this->getEntityManager()->flush(); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @return array<SessionRelUser> |
50
|
|
|
*/ |
51
|
|
|
public function getUsersByAccessUrl(Session $session, AccessUrl $url, array $relationTypeList = []): array |
52
|
|
|
{ |
53
|
|
|
if (0 === $session->getUsers()->count()) { |
54
|
|
|
return []; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
$qb = $this->addSessionRelUserFilterByUrl($session, $url); |
58
|
|
|
$qb->orderBy('sru.relationType'); |
59
|
|
|
|
60
|
|
|
if ($relationTypeList) { |
|
|
|
|
61
|
|
|
$qb->andWhere( |
62
|
|
|
$qb->expr()->in('sru.relationType', $relationTypeList) |
63
|
|
|
); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
return $qb->getQuery()->getResult(); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
public function getSessionsByUser(User $user, AccessUrl $url): QueryBuilder |
70
|
|
|
{ |
71
|
|
|
$qb = $this->createQueryBuilder('s'); |
72
|
|
|
$qb |
73
|
|
|
->innerJoin('s.users', 'sru') |
74
|
|
|
->leftJoin('s.urls', 'urls') |
75
|
|
|
->where($qb->expr()->eq('sru.user', ':user')) |
76
|
|
|
->andWhere($qb->expr()->eq('urls.url', ':url')) |
77
|
|
|
->setParameters([ |
78
|
|
|
'user' => $user, |
79
|
|
|
'url' => $url, |
80
|
|
|
]) |
81
|
|
|
; |
82
|
|
|
|
83
|
|
|
return $qb; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @return array<int, Session> |
88
|
|
|
* |
89
|
|
|
* @throws Exception |
90
|
|
|
*/ |
91
|
|
|
public function getPastSessionsOfUserInUrl(User $user, AccessUrl $url): array |
92
|
|
|
{ |
93
|
|
|
$sessions = $this->getSubscribedSessionsOfUserInUrl($user, $url); |
94
|
|
|
|
95
|
|
|
$filterPastSessions = function (Session $session) use ($user) { |
96
|
|
|
$now = new DateTime(); |
97
|
|
|
// Determine if the user is a coach |
98
|
|
|
$userIsCoach = $session->hasCoach($user); |
99
|
|
|
|
100
|
|
|
// Check if the session has a duration |
101
|
|
|
if ($session->getDuration() > 0) { |
102
|
|
|
$daysLeft = $session->getDaysLeftByUser($user); |
103
|
|
|
$session->setTitle($session->getTitle().'<-'.$daysLeft); |
104
|
|
|
|
105
|
|
|
return $daysLeft < 0 && !$userIsCoach; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
// Get the appropriate end date based on whether the user is a coach |
109
|
|
|
$sessionEndDate = $userIsCoach && $session->getCoachAccessEndDate() |
110
|
|
|
? $session->getCoachAccessEndDate() |
111
|
|
|
: $session->getAccessEndDate(); |
112
|
|
|
|
113
|
|
|
// If there's no end date, the session is not considered past |
114
|
|
|
if (!$sessionEndDate) { |
115
|
|
|
return false; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
// Check if the current date is after the end date |
119
|
|
|
return $now > $sessionEndDate; |
120
|
|
|
}; |
121
|
|
|
|
122
|
|
|
return array_filter($sessions, $filterPastSessions); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @return array<int, Session> |
127
|
|
|
* |
128
|
|
|
* @throws Exception |
129
|
|
|
*/ |
130
|
|
|
public function getCurrentSessionsOfUserInUrl(User $user, AccessUrl $url): array |
131
|
|
|
{ |
132
|
|
|
$sessions = $this->getSubscribedSessionsOfUserInUrl($user, $url); |
133
|
|
|
|
134
|
|
|
$filterCurrentSessions = function (Session $session) use ($user, $url) { |
135
|
|
|
$userIsGeneralCoach = $session->hasUserAsGeneralCoach($user); |
136
|
|
|
if (!$userIsGeneralCoach) { |
137
|
|
|
$coursesAsCoach = $this->getSessionCoursesByStatusInCourseSubscription($user, $session, Session::COURSE_COACH, $url); |
138
|
|
|
$coursesAsStudent = $this->getSessionCoursesByStatusInCourseSubscription($user, $session, Session::STUDENT, $url); |
139
|
|
|
$validCourses = array_merge($coursesAsCoach, $coursesAsStudent); |
140
|
|
|
|
141
|
|
|
if (empty($validCourses)) { |
142
|
|
|
return false; |
143
|
|
|
} |
144
|
|
|
$session->setCourses(new ArrayCollection($validCourses)); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
$userIsCoach = $session->hasCoach($user); |
148
|
|
|
|
149
|
|
|
// Check if session has a duration |
150
|
|
|
if ($session->getDuration() > 0) { |
151
|
|
|
$daysLeft = $session->getDaysLeftByUser($user); |
152
|
|
|
|
153
|
|
|
return $daysLeft >= 0 || $userIsCoach; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
// Determine the start date based on whether the user is a coach |
157
|
|
|
$sessionStartDate = $userIsCoach && $session->getCoachAccessStartDate() |
158
|
|
|
? $session->getCoachAccessStartDate() |
159
|
|
|
: $session->getAccessStartDate(); |
160
|
|
|
|
161
|
|
|
// If there is no start date, consider the session current |
162
|
|
|
if (!$sessionStartDate) { |
163
|
|
|
return true; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
// Get the current date and time |
167
|
|
|
$now = new DateTime(); |
168
|
|
|
|
169
|
|
|
// Determine the end date based on whether the user is a coach |
170
|
|
|
$sessionEndDate = $userIsCoach && $session->getCoachAccessEndDate() |
171
|
|
|
? $session->getCoachAccessEndDate() |
172
|
|
|
: $session->getAccessEndDate(); |
173
|
|
|
|
174
|
|
|
// Check if the current date is within the start and end dates |
175
|
|
|
return $now >= $sessionStartDate && (!$sessionEndDate || $now <= $sessionEndDate); |
176
|
|
|
}; |
177
|
|
|
|
178
|
|
|
return array_filter($sessions, $filterCurrentSessions); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* @return array<int, Session> |
183
|
|
|
* |
184
|
|
|
* @throws Exception |
185
|
|
|
*/ |
186
|
|
|
public function getUpcomingSessionsOfUserInUrl(User $user, AccessUrl $url): array |
187
|
|
|
{ |
188
|
|
|
$sessions = $this->getSubscribedSessionsOfUserInUrl($user, $url); |
189
|
|
|
|
190
|
|
|
$filterUpcomingSessions = function (Session $session) use ($user) { |
191
|
|
|
$now = new DateTime(); |
192
|
|
|
|
193
|
|
|
// All session with access by duration call be either current or past |
194
|
|
|
if ($session->getDuration() > 0) { |
195
|
|
|
return false; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
// Determine if the user is a coach |
199
|
|
|
$userIsCoach = $session->hasCoach($user); |
200
|
|
|
|
201
|
|
|
// Get the appropriate start date based on whether the user is a coach |
202
|
|
|
$sessionStartDate = $userIsCoach && $session->getCoachAccessStartDate() |
203
|
|
|
? $session->getCoachAccessStartDate() |
204
|
|
|
: $session->getAccessStartDate(); |
205
|
|
|
|
206
|
|
|
// If there's no start date, the session is not considered future |
207
|
|
|
if (!$sessionStartDate) { |
208
|
|
|
return false; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
// Check if the current date is before the start date |
212
|
|
|
return $now < $sessionStartDate; |
213
|
|
|
}; |
214
|
|
|
|
215
|
|
|
return array_filter($sessions, $filterUpcomingSessions); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
public function addUserInCourse(int $relationType, User $user, Course $course, Session $session): void |
219
|
|
|
{ |
220
|
|
|
if (!$user->isActive()) { |
221
|
|
|
throw new Exception('User not active'); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
if (!$session->hasCourse($course)) { |
225
|
|
|
$msg = \sprintf('Course %s is not subscribed to the session %s', $course->getTitle(), $session->getTitle()); |
226
|
|
|
|
227
|
|
|
throw new Exception($msg); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
if (!\in_array($relationType, Session::getRelationTypeList(), true)) { |
231
|
|
|
throw new Exception(\sprintf('Cannot handle relationType %s', $relationType)); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
$entityManager = $this->getEntityManager(); |
235
|
|
|
$existingRecord = $entityManager->getRepository(SessionRelUser::class)->findOneBy([ |
236
|
|
|
'session' => $session, |
237
|
|
|
'user' => $user, |
238
|
|
|
'relationType' => $relationType, |
239
|
|
|
]); |
240
|
|
|
|
241
|
|
|
if ($existingRecord) { |
242
|
|
|
$entityManager->remove($existingRecord); |
243
|
|
|
$entityManager->flush(); |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
switch ($relationType) { |
247
|
|
|
case Session::DRH: |
248
|
|
|
if ($user->hasRole('ROLE_HR')) { |
249
|
|
|
$session->addUserInSession(Session::DRH, $user); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
break; |
253
|
|
|
|
254
|
|
|
case Session::STUDENT: |
255
|
|
|
$session |
256
|
|
|
->addUserInSession(Session::STUDENT, $user) |
257
|
|
|
->addUserInCourse(Session::STUDENT, $user, $course) |
258
|
|
|
; |
259
|
|
|
|
260
|
|
|
break; |
261
|
|
|
|
262
|
|
|
case Session::COURSE_COACH: |
263
|
|
|
if ($user->hasRole('ROLE_TEACHER')) { |
264
|
|
|
$session |
265
|
|
|
->addUserInSession(Session::COURSE_COACH, $user) |
266
|
|
|
->addUserInCourse( |
267
|
|
|
Session::COURSE_COACH, |
268
|
|
|
$user, |
269
|
|
|
$course |
270
|
|
|
) |
271
|
|
|
; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
break; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
$entityManager->persist($session); |
278
|
|
|
$entityManager->flush(); |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* @return array<SessionRelCourse> |
283
|
|
|
*/ |
284
|
|
|
public function getSessionCoursesByStatusInUserSubscription(User $user, Session $session, int $relationType, ?AccessUrl $url = null): array |
285
|
|
|
{ |
286
|
|
|
$qb = $this->getEntityManager()->createQueryBuilder(); |
287
|
|
|
|
288
|
|
|
$qb->select('src') |
289
|
|
|
->from(SessionRelCourse::class, 'src') |
290
|
|
|
->innerJoin( |
291
|
|
|
SessionRelUser::class, |
292
|
|
|
'sru', |
293
|
|
|
Join::WITH, |
294
|
|
|
'src.session = sru.session' |
295
|
|
|
) |
296
|
|
|
->innerJoin('src.session', 'session') |
297
|
|
|
->where( |
298
|
|
|
$qb->expr()->eq('session', ':session') |
299
|
|
|
) |
300
|
|
|
->andWhere( |
301
|
|
|
$qb->expr()->eq('sru.user', ':user') |
302
|
|
|
) |
303
|
|
|
->andWhere( |
304
|
|
|
$qb->expr()->eq('sru.relationType', ':relation_type') |
305
|
|
|
) |
306
|
|
|
; |
307
|
|
|
|
308
|
|
|
$parameters = [ |
309
|
|
|
'session' => $session, |
310
|
|
|
'user' => $user, |
311
|
|
|
'relation_type' => $relationType, |
312
|
|
|
]; |
313
|
|
|
|
314
|
|
|
if ($url) { |
315
|
|
|
$qb->innerJoin('session.urls', 'urls') |
316
|
|
|
->andWhere( |
317
|
|
|
$qb->expr()->eq('urls.url', ':url') |
318
|
|
|
) |
319
|
|
|
; |
320
|
|
|
|
321
|
|
|
$parameters['url'] = $url; |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
$qb->setParameters($parameters); |
325
|
|
|
|
326
|
|
|
return $qb->getQuery()->getResult(); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* @return array<SessionRelCourse> |
331
|
|
|
*/ |
332
|
|
|
public function getSessionCoursesByStatusInCourseSubscription(User $user, Session $session, int $status, ?AccessUrl $url = null): array |
333
|
|
|
{ |
334
|
|
|
$qb = $this->getEntityManager()->createQueryBuilder(); |
335
|
|
|
|
336
|
|
|
$qb->select('src') |
337
|
|
|
->from(SessionRelCourse::class, 'src') |
338
|
|
|
->innerJoin( |
339
|
|
|
SessionRelCourseRelUser::class, |
340
|
|
|
'srcru', |
341
|
|
|
Join::WITH, |
342
|
|
|
'src.session = srcru.session AND src.course = srcru.course' |
343
|
|
|
) |
344
|
|
|
->innerJoin('srcru.session', 'session') |
345
|
|
|
->where( |
346
|
|
|
$qb->expr()->eq('session', ':session') |
347
|
|
|
) |
348
|
|
|
->andWhere( |
349
|
|
|
$qb->expr()->eq('srcru.user', ':user') |
350
|
|
|
) |
351
|
|
|
->andWhere( |
352
|
|
|
$qb->expr()->eq('srcru.status', ':status') |
353
|
|
|
) |
354
|
|
|
; |
355
|
|
|
|
356
|
|
|
$parameters = [ |
357
|
|
|
'session' => $session, |
358
|
|
|
'user' => $user, |
359
|
|
|
'status' => $status, |
360
|
|
|
]; |
361
|
|
|
|
362
|
|
|
if ($url) { |
363
|
|
|
$qb->innerJoin('session.urls', 'urls') |
364
|
|
|
->andWhere( |
365
|
|
|
$qb->expr()->eq('urls.url', ':url') |
366
|
|
|
) |
367
|
|
|
; |
368
|
|
|
|
369
|
|
|
$parameters['url'] = $url; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
$qb->setParameters($parameters); |
373
|
|
|
|
374
|
|
|
return $qb->getQuery()->getResult(); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
private function addSessionRelUserFilterByUrl(Session $session, AccessUrl $url): QueryBuilder |
378
|
|
|
{ |
379
|
|
|
$qb = $this->getEntityManager()->createQueryBuilder(); |
380
|
|
|
$qb |
381
|
|
|
->select('sru') |
382
|
|
|
->from(SessionRelUser::class, 'sru') |
383
|
|
|
->innerJoin('sru.user', 'u') |
384
|
|
|
->innerJoin('u.portals', 'p') |
385
|
|
|
->andWhere('sru.session = :session AND p.url = :url') |
386
|
|
|
->setParameters([ |
387
|
|
|
'session' => $session, |
388
|
|
|
'url' => $url, |
389
|
|
|
]) |
390
|
|
|
; |
391
|
|
|
|
392
|
|
|
return $qb; |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
public function getUserFollowedSessionsInAccessUrl(User $user, AccessUrl $url): QueryBuilder |
396
|
|
|
{ |
397
|
|
|
$callback = fn (Session $session) => $session->getId(); |
398
|
|
|
|
399
|
|
|
if ($user->isHRM()) { |
400
|
|
|
$idList = array_map($callback, $user->getDRHSessions()); |
401
|
|
|
} elseif ($user->isTeacher() || COURSEMANAGER === $user->getStatus()) { |
402
|
|
|
$idListAsCoach = $user |
403
|
|
|
->getSessionsByStatusInCourseSubscription(Session::COURSE_COACH) |
404
|
|
|
->map($callback) |
405
|
|
|
->getValues() |
406
|
|
|
; |
407
|
|
|
$idListAsGeneralCoach = array_map($callback, $user->getSessionsAsGeneralCoach()); |
408
|
|
|
$idList = array_merge($idListAsCoach, $idListAsGeneralCoach); |
409
|
|
|
} elseif ($user->isSessionAdmin()) { |
410
|
|
|
$idList = array_map($callback, $user->getSessionsAsAdmin()); |
411
|
|
|
} else { |
412
|
|
|
$idList = array_map($callback, $user->getSessionsAsStudent()); |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
$qb = $this->createQueryBuilder('s'); |
416
|
|
|
$qb |
417
|
|
|
->innerJoin('s.urls', 'u') |
418
|
|
|
->where($qb->expr()->eq('u.url', $url->getId())) |
419
|
|
|
->andWhere($qb->expr()->in('s.id', ':id_list')) |
420
|
|
|
->setParameter('id_list', $idList) |
421
|
|
|
; |
422
|
|
|
|
423
|
|
|
return $qb; |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
/** |
427
|
|
|
* @return array<int, Session> |
428
|
|
|
* |
429
|
|
|
* @throws Exception |
430
|
|
|
*/ |
431
|
|
|
public function getSubscribedSessionsOfUserInUrl( |
432
|
|
|
User $user, |
433
|
|
|
AccessUrl $url, |
434
|
|
|
bool $ignoreVisibilityForAdmins = false, |
435
|
|
|
): array { |
436
|
|
|
$sessions = $this->getSessionsByUser($user, $url)->getQuery()->getResult(); |
437
|
|
|
|
438
|
|
|
$filterSessions = function (Session $session) use ($user, $ignoreVisibilityForAdmins) { |
439
|
|
|
$visibility = $session->setAccessVisibilityByUser($user, $ignoreVisibilityForAdmins); |
440
|
|
|
|
441
|
|
|
if (Session::VISIBLE !== $visibility) { |
442
|
|
|
$closedOrHiddenCourses = $session->getClosedOrHiddenCourses(); |
443
|
|
|
|
444
|
|
|
if ($closedOrHiddenCourses->count() === $session->getCourses()->count()) { |
445
|
|
|
$visibility = Session::INVISIBLE; |
446
|
|
|
} |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
switch ($visibility) { |
450
|
|
|
case Session::READ_ONLY: |
451
|
|
|
case Session::VISIBLE: |
452
|
|
|
case Session::AVAILABLE: |
453
|
|
|
break; |
454
|
|
|
|
455
|
|
|
case Session::INVISIBLE: |
456
|
|
|
if (!$ignoreVisibilityForAdmins) { |
457
|
|
|
return false; |
458
|
|
|
} |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
return true; |
462
|
|
|
}; |
463
|
|
|
|
464
|
|
|
return array_filter($sessions, $filterSessions); |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
public function countUsersBySession(int $sessionId): int |
468
|
|
|
{ |
469
|
|
|
$qb = $this->createQueryBuilder('s'); |
470
|
|
|
$qb->select('COUNT(sru.id)') |
471
|
|
|
->innerJoin('s.users', 'sru') |
472
|
|
|
->where('s.id = :sessionId') |
473
|
|
|
->setParameter('sessionId', $sessionId); |
474
|
|
|
|
475
|
|
|
return (int) $qb->getQuery()->getSingleScalarResult(); |
476
|
|
|
} |
477
|
|
|
} |
478
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.