Passed
Push — master ( 017cdc...ca97c4 )
by Yannick
08:28
created

CourseController::searchCourseTemplates()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 12
nc 2
nop 3
dl 0
loc 22
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CoreBundle\Controller;
8
9
use Chamilo\CoreBundle\Entity\Course;
10
use Chamilo\CoreBundle\Entity\CourseRelUser;
11
use Chamilo\CoreBundle\Entity\ExtraField;
12
use Chamilo\CoreBundle\Entity\Session;
13
use Chamilo\CoreBundle\Entity\SessionRelUser;
14
use Chamilo\CoreBundle\Entity\Tag;
15
use Chamilo\CoreBundle\Entity\Tool;
16
use Chamilo\CoreBundle\Entity\User;
17
use Chamilo\CoreBundle\Framework\Container;
18
use Chamilo\CoreBundle\Repository\AssetRepository;
19
use Chamilo\CoreBundle\Repository\CourseCategoryRepository;
20
use Chamilo\CoreBundle\Repository\ExtraFieldValuesRepository;
21
use Chamilo\CoreBundle\Repository\LanguageRepository;
22
use Chamilo\CoreBundle\Repository\LegalRepository;
23
use Chamilo\CoreBundle\Repository\Node\CourseRepository;
24
use Chamilo\CoreBundle\Repository\Node\IllustrationRepository;
25
use Chamilo\CoreBundle\Repository\TagRepository;
26
use Chamilo\CoreBundle\Security\Authorization\Voter\CourseVoter;
27
use Chamilo\CoreBundle\Service\CourseService;
28
use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper;
29
use Chamilo\CoreBundle\ServiceHelper\UserHelper;
30
use Chamilo\CoreBundle\Settings\SettingsManager;
31
use Chamilo\CoreBundle\Tool\ToolChain;
32
use Chamilo\CourseBundle\Controller\ToolBaseController;
33
use Chamilo\CourseBundle\Entity\CCourseDescription;
34
use Chamilo\CourseBundle\Entity\CLink;
35
use Chamilo\CourseBundle\Entity\CShortcut;
36
use Chamilo\CourseBundle\Entity\CTool;
37
use Chamilo\CourseBundle\Entity\CToolIntro;
38
use Chamilo\CourseBundle\Repository\CCourseDescriptionRepository;
39
use Chamilo\CourseBundle\Repository\CLpRepository;
40
use Chamilo\CourseBundle\Repository\CQuizRepository;
41
use Chamilo\CourseBundle\Repository\CShortcutRepository;
42
use Chamilo\CourseBundle\Repository\CToolRepository;
43
use Chamilo\CourseBundle\Settings\SettingsCourseManager;
44
use Chamilo\CourseBundle\Settings\SettingsFormFactory;
45
use CourseManager;
46
use Database;
47
use Display;
48
use Doctrine\ORM\EntityManagerInterface;
49
use Event;
50
use Exception;
51
use Exercise;
52
use ExtraFieldValue;
53
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
54
use Symfony\Component\HttpFoundation\JsonResponse;
55
use Symfony\Component\HttpFoundation\RedirectResponse;
56
use Symfony\Component\HttpFoundation\Request;
57
use Symfony\Component\HttpFoundation\Response;
58
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
59
use Symfony\Component\Routing\Attribute\Route;
60
use Symfony\Component\Security\Http\Attribute\IsGranted;
61
use Symfony\Component\Serializer\SerializerInterface;
62
use Symfony\Component\Validator\Exception\ValidatorException;
63
use Symfony\Contracts\Translation\TranslatorInterface;
64
use UserManager;
65
66
/**
67
 * @author Julio Montoya <[email protected]>
68
 */
69
#[Route('/course')]
70
class CourseController extends ToolBaseController
71
{
72
    public function __construct(
73
        private readonly EntityManagerInterface $em,
74
        private readonly SerializerInterface $serializer,
75
        private readonly UserHelper $userHelper,
76
    ) {}
77
78
    #[IsGranted('ROLE_USER')]
79
    #[Route('/{cid}/checkLegal.json', name: 'chamilo_core_course_check_legal_json')]
80
    public function checkTermsAndConditionJson(
81
        Request $request,
82
        LegalRepository $legalTermsRepo,
83
        LanguageRepository $languageRepository,
84
        ExtraFieldValuesRepository $extraFieldValuesRepository,
85
        SettingsManager $settingsManager
86
    ): Response {
87
        $user = $this->userHelper->getCurrent();
88
        $course = $this->getCourse();
89
        $responseData = [
90
            'redirect' => false,
91
            'url' => '#',
92
        ];
93
94
        if ($user->hasRole('ROLE_STUDENT')
95
            && 'true' === $settingsManager->getSetting('registration.allow_terms_conditions')
96
            && 'course' === $settingsManager->getSetting('platform.load_term_conditions_section')
97
        ) {
98
            $termAndConditionStatus = false;
99
            $extraValue = $extraFieldValuesRepository->findLegalAcceptByItemId($user->getId());
100
            if (!empty($extraValue['value'])) {
101
                $result = $extraValue['value'];
102
                $userConditions = explode(':', $result);
103
                $version = $userConditions[0];
104
                $langId = (int) $userConditions[1];
105
                $realVersion = $legalTermsRepo->getLastVersion($langId);
106
                $termAndConditionStatus = ($version >= $realVersion);
107
            }
108
109
            if (false === $termAndConditionStatus) {
110
                $request->getSession()->set('term_and_condition', ['user_id' => $user->getId()]);
111
112
                $redirect = true;
113
114
                if ('true' === $settingsManager->getSetting('course.allow_public_course_with_no_terms_conditions')
115
                    && Course::OPEN_WORLD === $course->getVisibility()
116
                ) {
117
                    $redirect = false;
118
                }
119
120
                if ($redirect && !$this->isGranted('ROLE_ADMIN')) {
121
                    $responseData = [
122
                        'redirect' => true,
123
                        'url' => '/main/auth/inscription.php',
124
                    ];
125
                }
126
            } else {
127
                $request->getSession()->remove('term_and_condition');
128
            }
129
        }
130
131
        return new JsonResponse($responseData);
132
    }
133
134
    #[Route('/{cid}/home.json', name: 'chamilo_core_course_home_json')]
135
    public function indexJson(
136
        Request $request,
137
        CShortcutRepository $shortcutRepository,
138
        EntityManagerInterface $em,
139
        AssetRepository $assetRepository
140
    ): Response {
141
        $requestData = json_decode($request->getContent(), true);
142
        // Sort behaviour
143
        if (!empty($requestData) && isset($requestData['toolItem'])) {
144
            $index = $requestData['index'];
145
            $toolItem = $requestData['toolItem'];
146
            $toolId = (int) $toolItem['iid'];
147
148
            /** @var CTool $cTool */
149
            $cTool = $em->find(CTool::class, $toolId);
150
151
            if ($cTool) {
152
                $cTool->setPosition($index + 1);
153
                $em->persist($cTool);
154
                $em->flush();
155
            }
156
        }
157
158
        $course = $this->getCourse();
159
        $sessionId = $this->getSessionId();
160
        $isInASession = $sessionId > 0;
161
162
        if (null === $course) {
163
            throw $this->createAccessDeniedException();
164
        }
165
166
        if (empty($sessionId)) {
167
            $this->denyAccessUnlessGranted(CourseVoter::VIEW, $course);
168
        }
169
170
        $sessionHandler = $request->getSession();
171
172
        $userId = 0;
173
174
        $user = $this->userHelper->getCurrent();
175
        if (null !== $user) {
176
            $userId = $user->getId();
177
        }
178
179
        $courseCode = $course->getCode();
180
        $courseId = $course->getId();
181
182
        if ($user && $user->hasRole('ROLE_INVITEE')) {
183
            $isSubscribed = CourseManager::is_user_subscribed_in_course(
184
                $userId,
185
                $courseCode,
186
                $isInASession,
187
                $sessionId
188
            );
189
190
            if (!$isSubscribed) {
191
                throw $this->createAccessDeniedException();
192
            }
193
        }
194
195
        $isSpecialCourse = CourseManager::isSpecialCourse($courseId);
196
197
        if ($user && $isSpecialCourse && (isset($_GET['autoreg']) && 1 === (int) $_GET['autoreg'])
198
            && CourseManager::subscribeUser($userId, $courseId, STUDENT)
199
        ) {
200
            $sessionHandler->set('is_allowed_in_course', true);
201
        }
202
203
        $logInfo = [
204
            'tool' => 'course-main',
205
        ];
206
        Event::registerLog($logInfo);
207
208
        // Deleting the objects
209
        $sessionHandler->remove('toolgroup');
210
        $sessionHandler->remove('_gid');
211
        $sessionHandler->remove('oLP');
212
        $sessionHandler->remove('lpobject');
213
214
        api_remove_in_gradebook();
215
        Exercise::cleanSessionVariables();
216
217
        $shortcuts = [];
218
        if (null !== $user) {
219
            $shortcutQuery = $shortcutRepository->getResources($course->getResourceNode());
220
            $shortcuts = $shortcutQuery->getQuery()->getResult();
221
222
            /* @var CShortcut $shortcut */
223
            foreach ($shortcuts as $shortcut) {
224
                $resourceNode = $shortcut->getShortCutNode();
225
                $cLink = $em->getRepository(CLink::class)->findOneBy(['resourceNode' => $resourceNode]);
226
227
                if ($cLink) {
228
                    $shortcut->setCustomImageUrl(
229
                        $cLink->getCustomImage()
230
                            ? $assetRepository->getAssetUrl($cLink->getCustomImage())
231
                            : null
232
                    );
233
                } else {
234
                    $shortcut->setCustomImageUrl(null);
235
                }
236
            }
237
        }
238
        $responseData = [
239
            'shortcuts' => $shortcuts,
240
            'diagram' => '',
241
        ];
242
243
        $json = $this->serializer->serialize(
244
            $responseData,
245
            'json',
246
            [
247
                'groups' => ['course:read', 'ctool:read', 'tool:read', 'cshortcut:read'],
248
            ]
249
        );
250
251
        return new Response(
252
            $json,
253
            Response::HTTP_OK,
254
            [
255
                'Content-type' => 'application/json',
256
            ]
257
        );
258
    }
259
260
    /**
261
     * Redirects the page to a tool, following the tools settings.
262
     */
263
    #[Route('/{cid}/tool/{toolName}', name: 'chamilo_core_course_redirect_tool')]
264
    public function redirectTool(
265
        Request $request,
266
        string $toolName,
267
        CToolRepository $repo,
268
        ToolChain $toolChain
269
    ): RedirectResponse {
270
        /** @var CTool|null $tool */
271
        $tool = $repo->findOneBy([
272
            'title' => $toolName,
273
        ]);
274
275
        if (null === $tool) {
276
            throw new NotFoundHttpException($this->trans('Tool not found'));
277
        }
278
279
        $tool = $toolChain->getToolFromName($tool->getTool()->getTitle());
280
        $link = $tool->getLink();
281
282
        if (null === $this->getCourse()) {
283
            throw new NotFoundHttpException($this->trans('Course not found'));
284
        }
285
        $optionalParams = '';
286
287
        $optionalParams = $request->query->get('cert') ? '&cert='.$request->query->get('cert') : '';
288
289
        if (strpos($link, 'nodeId')) {
290
            $nodeId = (string) $this->getCourse()->getResourceNode()->getId();
291
            $link = str_replace(':nodeId', $nodeId, $link);
292
        }
293
294
        $url = $link.'?'.$this->getCourseUrlQuery().$optionalParams;
295
296
        return $this->redirect($url);
297
    }
298
299
    /*public function redirectToShortCut(string $toolName, CToolRepository $repo, ToolChain $toolChain): RedirectResponse
300
     * {
301
     * $tool = $repo->findOneBy([
302
     * 'name' => $toolName,
303
     * ]);
304
     * if (null === $tool) {
305
     * throw new NotFoundHttpException($this->trans('Tool not found'));
306
     * }
307
     * $tool = $toolChain->getToolFromName($tool->getTool()->getTitle());
308
     * $link = $tool->getLink();
309
     * if (strpos($link, 'nodeId')) {
310
     * $nodeId = (string) $this->getCourse()->getResourceNode()->getId();
311
     * $link = str_replace(':nodeId', $nodeId, $link);
312
     * }
313
     * $url = $link.'?'.$this->getCourseUrlQuery();
314
     * return $this->redirect($url);
315
     * }*/
316
317
    /**
318
     * Edit configuration with given namespace.
319
     */
320
    #[Route('/{course}/settings/{namespace}', name: 'chamilo_core_course_settings')]
321
    public function updateSettings(
322
        Request $request,
323
        #[MapEntity(expr: 'repository.find(cid)')]
324
        Course $course,
325
        string $namespace,
326
        SettingsCourseManager $manager,
327
        SettingsFormFactory $formFactory
328
    ): Response {
329
        $this->denyAccessUnlessGranted(CourseVoter::VIEW, $course);
330
331
        $schemaAlias = $manager->convertNameSpaceToService($namespace);
332
        $settings = $manager->load($namespace);
333
334
        $form = $formFactory->create($schemaAlias);
335
336
        $form->setData($settings);
337
        $form->handleRequest($request);
338
339
        if ($form->isSubmitted() && $form->isValid()) {
340
            $messageType = 'success';
341
342
            try {
343
                $manager->setCourse($course);
344
                $manager->save($form->getData());
345
                $message = $this->trans('Update');
346
            } catch (ValidatorException $validatorException) {
347
                $message = $this->trans($validatorException->getMessage());
348
                $messageType = 'error';
349
            }
350
            $this->addFlash($messageType, $message);
351
352
            if ($request->headers->has('referer')) {
353
                return $this->redirect($request->headers->get('referer'));
354
            }
355
        }
356
357
        $schemas = $manager->getSchemas();
358
359
        return $this->render(
360
            '@ChamiloCore/Course/settings.html.twig',
361
            [
362
                'course' => $course,
363
                'schemas' => $schemas,
364
                'settings' => $settings,
365
                'form' => $form,
366
            ]
367
        );
368
    }
369
370
    #[Route('/{id}/about', name: 'chamilo_core_course_about')]
371
    public function about(
372
        Course $course,
373
        IllustrationRepository $illustrationRepository,
374
        CCourseDescriptionRepository $courseDescriptionRepository,
375
        EntityManagerInterface $em,
376
        Request $request
377
    ): Response {
378
        $courseId = $course->getId();
379
380
        $user = $this->userHelper->getCurrent();
381
382
        $fieldsRepo = $em->getRepository(ExtraField::class);
383
384
        /** @var TagRepository $tagRepo */
385
        $tagRepo = $em->getRepository(Tag::class);
386
387
        $courseDescriptions = $courseDescriptionRepository->getResourcesByCourse($course)->getQuery()->getResult();
388
389
        $courseValues = new ExtraFieldValue('course');
390
391
        $urlCourse = api_get_path(WEB_PATH).\sprintf('course/%s/about', $courseId);
392
        $courseTeachers = $course->getTeachersSubscriptions();
393
        $teachersData = [];
394
395
        foreach ($courseTeachers as $teacherSubscription) {
396
            $teacher = $teacherSubscription->getUser();
397
            $userData = [
398
                'complete_name' => UserManager::formatUserFullName($teacher),
399
                'image' => $illustrationRepository->getIllustrationUrl($teacher),
400
                'diploma' => $teacher->getDiplomas(),
401
                'openarea' => $teacher->getOpenarea(),
402
            ];
403
404
            $teachersData[] = $userData;
405
        }
406
407
        /** @var ExtraField $tagField */
408
        $tagField = $fieldsRepo->findOneBy([
409
            'itemType' => ExtraField::COURSE_FIELD_TYPE,
410
            'variable' => 'tags',
411
        ]);
412
413
        $courseTags = [];
414
        if (null !== $tagField) {
415
            $courseTags = $tagRepo->getTagsByItem($tagField, $courseId);
416
        }
417
418
        $courseDescription = $courseObjectives = $courseTopics = $courseMethodology = '';
419
        $courseMaterial = $courseResources = $courseAssessment = '';
420
        $courseCustom = [];
421
        foreach ($courseDescriptions as $descriptionTool) {
422
            switch ($descriptionTool->getDescriptionType()) {
423
                case CCourseDescription::TYPE_DESCRIPTION:
424
                    $courseDescription = $descriptionTool->getContent();
425
426
                    break;
427
428
                case CCourseDescription::TYPE_OBJECTIVES:
429
                    $courseObjectives = $descriptionTool;
430
431
                    break;
432
433
                case CCourseDescription::TYPE_TOPICS:
434
                    $courseTopics = $descriptionTool;
435
436
                    break;
437
438
                case CCourseDescription::TYPE_METHODOLOGY:
439
                    $courseMethodology = $descriptionTool;
440
441
                    break;
442
443
                case CCourseDescription::TYPE_COURSE_MATERIAL:
444
                    $courseMaterial = $descriptionTool;
445
446
                    break;
447
448
                case CCourseDescription::TYPE_RESOURCES:
449
                    $courseResources = $descriptionTool;
450
451
                    break;
452
453
                case CCourseDescription::TYPE_ASSESSMENT:
454
                    $courseAssessment = $descriptionTool;
455
456
                    break;
457
458
                case CCourseDescription::TYPE_CUSTOM:
459
                    $courseCustom[] = $descriptionTool;
460
461
                    break;
462
            }
463
        }
464
465
        $topics = [
466
            'objectives' => $courseObjectives,
467
            'topics' => $courseTopics,
468
            'methodology' => $courseMethodology,
469
            'material' => $courseMaterial,
470
            'resources' => $courseResources,
471
            'assessment' => $courseAssessment,
472
            'custom' => array_reverse($courseCustom),
473
        ];
474
475
        $subscriptionUser = false;
476
477
        if ($user) {
478
            $subscriptionUser = CourseManager::is_user_subscribed_in_course($user->getId(), $course->getCode());
479
        }
480
481
        $allowSubscribe = CourseManager::canUserSubscribeToCourse($course->getCode());
482
483
        $image = Container::getIllustrationRepository()->getIllustrationUrl($course, 'course_picture_medium');
484
485
        $params = [
486
            'course' => $course,
487
            'description' => $courseDescription,
488
            'image' => $image,
489
            'syllabus' => $topics,
490
            'tags' => $courseTags,
491
            'teachers' => $teachersData,
492
            'extra_fields' => $courseValues->getAllValuesForAnItem(
493
                $course->getId(),
494
                null,
495
                true
496
            ),
497
            'subscription' => $subscriptionUser,
498
            'url' => '',
499
            'is_premium' => '',
500
            'token' => '',
501
            'base_url' => $request->getSchemeAndHttpHost(),
502
            'allow_subscribe' => $allowSubscribe,
503
        ];
504
505
        $metaInfo = '<meta property="og:url" content="'.$urlCourse.'" />';
506
        $metaInfo .= '<meta property="og:type" content="website" />';
507
        $metaInfo .= '<meta property="og:title" content="'.$course->getTitle().'" />';
508
        $metaInfo .= '<meta property="og:description" content="'.strip_tags($courseDescription).'" />';
509
        $metaInfo .= '<meta property="og:image" content="'.$image.'" />';
510
511
        $htmlHeadXtra[] = $metaInfo;
512
        $htmlHeadXtra[] = api_get_asset('readmore-js/readmore.js');
513
514
        return $this->render('@ChamiloCore/Course/about.html.twig', $params);
515
    }
516
517
    #[Route('/{id}/welcome', name: 'chamilo_core_course_welcome')]
518
    public function welcome(Course $course): Response
519
    {
520
        return $this->render('@ChamiloCore/Course/welcome.html.twig', [
521
            'course' => $course,
522
        ]);
523
    }
524
525
    private function findIntroOfCourse(Course $course): ?CTool
526
    {
527
        $qb = $this->em->createQueryBuilder();
528
529
        $query = $qb->select('ct')
530
            ->from(CTool::class, 'ct')
531
            ->where('ct.course = :c_id')
532
            ->andWhere('ct.title = :title')
533
            ->andWhere(
534
                $qb->expr()->orX(
535
                    $qb->expr()->eq('ct.session', ':session_id'),
536
                    $qb->expr()->isNull('ct.session')
537
                )
538
            )
539
            ->setParameters([
540
                'c_id' => $course->getId(),
541
                'title' => 'course_homepage',
542
                'session_id' => 0,
543
            ])
544
            ->getQuery()
545
        ;
546
547
        $results = $query->getResult();
548
549
        return \count($results) > 0 ? $results[0] : null;
550
    }
551
552
    #[Route('/{id}/getToolIntro', name: 'chamilo_core_course_gettoolintro')]
553
    public function getToolIntro(Request $request, Course $course, EntityManagerInterface $em): Response
554
    {
555
        $sessionId = (int) $request->get('sid');
556
557
        // $session = $this->getSession();
558
        $responseData = [];
559
        $ctoolRepo = $em->getRepository(CTool::class);
560
        $sessionRepo = $em->getRepository(Session::class);
561
        $createInSession = false;
562
563
        $session = null;
564
565
        if (!empty($sessionId)) {
566
            $session = $sessionRepo->find($sessionId);
567
        }
568
569
        $ctool = $this->findIntroOfCourse($course);
570
571
        if ($session) {
572
            $ctoolSession = $ctoolRepo->findOneBy(['title' => 'course_homepage', 'course' => $course, 'session' => $session]);
573
574
            if (!$ctoolSession) {
575
                $createInSession = true;
576
            } else {
577
                $ctool = $ctoolSession;
578
            }
579
        }
580
581
        if ($ctool) {
582
            $ctoolintroRepo = $em->getRepository(CToolIntro::class);
583
584
            /** @var CToolIntro $ctoolintro */
585
            $ctoolintro = $ctoolintroRepo->findOneBy(['courseTool' => $ctool]);
586
            if ($ctoolintro) {
587
                $responseData = [
588
                    'iid' => $ctoolintro->getIid(),
589
                    'introText' => $ctoolintro->getIntroText(),
590
                    'createInSession' => $createInSession,
591
                    'cToolId' => $ctool->getIid(),
592
                ];
593
            }
594
            $responseData['c_tool'] = [
595
                'iid' => $ctool->getIid(),
596
                'title' => $ctool->getTitle(),
597
            ];
598
        }
599
600
        return new JsonResponse($responseData);
601
    }
602
603
    #[Route('/{id}/addToolIntro', name: 'chamilo_core_course_addtoolintro')]
604
    public function addToolIntro(Request $request, Course $course, EntityManagerInterface $em): Response
605
    {
606
        $data = json_decode($request->getContent());
607
        $sessionId = $data->sid ?? ($data->resourceLinkList[0]->sid ?? 0);
608
        $introText = $data->introText ?? null;
609
610
        $session = $sessionId ? $em->getRepository(Session::class)->find($sessionId) : null;
611
        $ctoolRepo = $em->getRepository(CTool::class);
612
        $ctoolintroRepo = $em->getRepository(CToolIntro::class);
613
614
        $ctoolSession = $ctoolRepo->findOneBy([
615
            'title' => 'course_homepage',
616
            'course' => $course,
617
            'session' => $session,
618
        ]);
619
620
        if (!$ctoolSession) {
621
            $toolEntity = $em->getRepository(Tool::class)->findOneBy(['title' => 'course_homepage']);
622
            if ($toolEntity) {
623
                $ctoolSession = (new CTool())
624
                    ->setTool($toolEntity)
625
                    ->setTitle('course_homepage')
626
                    ->setCourse($course)
627
                    ->setPosition(1)
628
                    ->setVisibility(true)
629
                    ->setParent($course)
630
                    ->setCreator($course->getCreator())
631
                    ->setSession($session)
632
                    ->addCourseLink($course)
633
                ;
634
635
                $em->persist($ctoolSession);
636
                $em->flush();
637
            }
638
        }
639
640
        $ctoolIntro = $ctoolintroRepo->findOneBy(['courseTool' => $ctoolSession]);
641
        if (!$ctoolIntro) {
642
            $ctoolIntro = (new CToolIntro())
643
                ->setCourseTool($ctoolSession)
644
                ->setIntroText($introText ?? '')
645
                ->setParent($course)
646
            ;
647
648
            $em->persist($ctoolIntro);
649
            $em->flush();
650
651
            return new JsonResponse([
652
                'status' => 'created',
653
                'cToolId' => $ctoolSession->getIid(),
654
                'introIid' => $ctoolIntro->getIid(),
655
                'introText' => $ctoolIntro->getIntroText(),
656
            ]);
657
        }
658
659
        if (null !== $introText) {
660
            $ctoolIntro->setIntroText($introText);
661
            $em->persist($ctoolIntro);
662
            $em->flush();
663
664
            return new JsonResponse([
665
                'status' => 'updated',
666
                'cToolId' => $ctoolSession->getIid(),
667
                'introIid' => $ctoolIntro->getIid(),
668
                'introText' => $ctoolIntro->getIntroText(),
669
            ]);
670
        }
671
672
        return new JsonResponse(['status' => 'no_action']);
673
    }
674
675
    #[Route('/check-enrollments', name: 'chamilo_core_check_enrollments', methods: ['GET'])]
676
    public function checkEnrollments(EntityManagerInterface $em, SettingsManager $settingsManager): JsonResponse
677
    {
678
        $user = $this->userHelper->getCurrent();
679
680
        if (!$user) {
681
            return new JsonResponse(['error' => 'User not found'], Response::HTTP_UNAUTHORIZED);
682
        }
683
684
        $isEnrolledInCourses = $this->isUserEnrolledInAnyCourse($user, $em);
685
        $isEnrolledInSessions = $this->isUserEnrolledInAnySession($user, $em);
686
687
        if (!$isEnrolledInCourses && !$isEnrolledInSessions) {
688
            $defaultMenuEntry = $settingsManager->getSetting('platform.default_menu_entry_for_course_or_session');
689
            $isEnrolledInCourses = 'my_courses' === $defaultMenuEntry;
690
            $isEnrolledInSessions = 'my_sessions' === $defaultMenuEntry;
691
        }
692
693
        return new JsonResponse([
694
            'isEnrolledInCourses' => $isEnrolledInCourses,
695
            'isEnrolledInSessions' => $isEnrolledInSessions,
696
        ]);
697
    }
698
699
    #[Route('/categories', name: 'chamilo_core_course_form_lists')]
700
    public function getCategories(
701
        SettingsManager $settingsManager,
702
        AccessUrlHelper $accessUrlHelper,
703
        CourseCategoryRepository $courseCategoriesRepo
704
    ): JsonResponse {
705
        $allowBaseCourseCategory = 'true' === $settingsManager->getSetting('course.allow_base_course_category');
706
        $accessUrlId = $accessUrlHelper->getCurrent()->getId();
707
708
        $categories = $courseCategoriesRepo->findAllInAccessUrl(
709
            $accessUrlId,
710
            $allowBaseCourseCategory
711
        );
712
713
        $data = [];
714
        $categoryToAvoid = '';
715
        if (!$this->isGranted('ROLE_ADMIN')) {
716
            $categoryToAvoid = $settingsManager->getSetting('course.course_category_code_to_use_as_model');
717
        }
718
719
        foreach ($categories as $category) {
720
            $categoryCode = $category->getCode();
721
            if (!empty($categoryToAvoid) && $categoryToAvoid == $categoryCode) {
722
                continue;
723
            }
724
            $data[] = ['id' => $category->getId(), 'name' => $category->__toString()];
725
        }
726
727
        return new JsonResponse($data);
728
    }
729
730
    #[Route('/search_templates', name: 'chamilo_core_course_search_templates')]
731
    public function searchCourseTemplates(
732
        Request $request,
733
        AccessUrlHelper $accessUrlHelper,
734
        CourseRepository $courseRepository
735
    ): JsonResponse {
736
        $searchTerm = $request->query->get('search', '');
737
        $accessUrl = $accessUrlHelper->getCurrent();
738
739
        $user = $this->userHelper->getCurrent();
740
741
        $courseList = $courseRepository->getCoursesInfoByUser($user, $accessUrl, 1, $searchTerm);
742
        $results = ['items' => []];
743
        foreach ($courseList as $course) {
744
            $title = $course['title'];
745
            $results['items'][] = [
746
                'id' => $course['id'],
747
                'name' => $title.' ('.$course['code'].') ',
748
            ];
749
        }
750
751
        return new JsonResponse($results);
752
    }
753
754
    #[Route('/create', name: 'chamilo_core_course_create')]
755
    public function createCourse(
756
        Request $request,
757
        TranslatorInterface $translator,
758
        CourseService $courseService
759
    ): JsonResponse {
760
        $courseData = json_decode($request->getContent(), true);
761
762
        $title = $courseData['name'] ?? null;
763
        $wantedCode = $courseData['code'] ?? null;
764
        $courseLanguage = $courseData['language']['id'] ?? null;
765
        $categoryCode = $courseData['category'] ?? null;
766
        $exemplaryContent = $courseData['fillDemoContent'] ?? false;
767
        $template = $courseData['template'] ?? '';
768
769
        $params = [
770
            'title' => $title,
771
            'wanted_code' => $wantedCode,
772
            'course_language' => $courseLanguage,
773
            'exemplary_content' => $exemplaryContent,
774
            'course_template' => $template,
775
        ];
776
777
        if ($categoryCode) {
778
            $params['course_categories'] = $categoryCode;
779
        }
780
781
        try {
782
            $course = $courseService->createCourse($params);
783
            if ($course) {
784
                return new JsonResponse([
785
                    'success' => true,
786
                    'message' => $translator->trans('Course created successfully.'),
787
                    'courseId' => $course->getId(),
788
                ]);
789
            }
790
        } catch (Exception $e) {
791
            return new JsonResponse([
792
                'success' => false,
793
                'message' => $translator->trans($e->getMessage()),
794
            ], Response::HTTP_BAD_REQUEST);
795
        }
796
797
        return new JsonResponse(['success' => false, 'message' => $translator->trans('An error occurred while creating the course.')]);
798
    }
799
800
    #[Route('/{id}/getAutoLaunchExerciseId', name: 'chamilo_core_course_get_auto_launch_exercise_id', methods: ['GET'])]
801
    public function getAutoLaunchExerciseId(
802
        Request $request,
803
        Course $course,
804
        CQuizRepository $quizRepository,
805
        EntityManagerInterface $em
806
    ): JsonResponse {
807
        $data = $request->getContent();
808
        $data = json_decode($data);
809
        $sessionId = $data->sid ?? 0;
810
811
        $sessionRepo = $em->getRepository(Session::class);
812
        $session = null;
813
        if (!empty($sessionId)) {
814
            $session = $sessionRepo->find($sessionId);
815
        }
816
817
        $autoLaunchExerciseId = $quizRepository->findAutoLaunchableQuizByCourseAndSession($course, $session);
818
819
        return new JsonResponse(['exerciseId' => $autoLaunchExerciseId], Response::HTTP_OK);
820
    }
821
822
    #[Route('/{id}/getAutoLaunchLPId', name: 'chamilo_core_course_get_auto_launch_lp_id', methods: ['GET'])]
823
    public function getAutoLaunchLPId(
824
        Request $request,
825
        Course $course,
826
        CLpRepository $lpRepository,
827
        EntityManagerInterface $em
828
    ): JsonResponse {
829
        $data = $request->getContent();
830
        $data = json_decode($data);
831
        $sessionId = $data->sid ?? 0;
832
833
        $sessionRepo = $em->getRepository(Session::class);
834
        $session = null;
835
        if (!empty($sessionId)) {
836
            $session = $sessionRepo->find($sessionId);
837
        }
838
839
        $autoLaunchLPId = $lpRepository->findAutoLaunchableLPByCourseAndSession($course, $session);
840
841
        return new JsonResponse(['lpId' => $autoLaunchLPId], Response::HTTP_OK);
842
    }
843
844
    private function autoLaunch(): void
845
    {
846
        $autoLaunchWarning = '';
847
        $showAutoLaunchLpWarning = false;
848
        $course_id = api_get_course_int_id();
849
        $lpAutoLaunch = api_get_course_setting('enable_lp_auto_launch');
850
        $session_id = api_get_session_id();
851
        $allowAutoLaunchForCourseAdmins =
852
            api_is_platform_admin()
853
            || api_is_allowed_to_edit(true, true)
854
            || api_is_coach();
855
856
        if (!empty($lpAutoLaunch)) {
857
            if (2 === $lpAutoLaunch) {
858
                // LP list
859
                if ($allowAutoLaunchForCourseAdmins) {
860
                    $showAutoLaunchLpWarning = true;
861
                } else {
862
                    $session_key = 'lp_autolaunch_'.$session_id.'_'.$course_id.'_'.api_get_user_id();
863
                    if (!isset($_SESSION[$session_key])) {
864
                        // Redirecting to the LP
865
                        $url = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.api_get_cidreq();
866
                        $_SESSION[$session_key] = true;
867
                        header(\sprintf('Location: %s', $url));
868
869
                        exit;
870
                    }
871
                }
872
            } else {
873
                $lp_table = Database::get_course_table(TABLE_LP_MAIN);
874
                $condition = '';
875
                if (!empty($session_id)) {
876
                    $condition = api_get_session_condition($session_id);
877
                    $sql = "SELECT id FROM {$lp_table}
878
                            WHERE c_id = {$course_id} AND autolaunch = 1 {$condition}
879
                            LIMIT 1";
880
                    $result = Database::query($sql);
881
                    // If we found nothing in the session we just called the session_id =  0 autolaunch
882
                    if (0 === Database::num_rows($result)) {
883
                        $condition = '';
884
                    }
885
                }
886
887
                $sql = "SELECT iid FROM {$lp_table}
888
                        WHERE c_id = {$course_id} AND autolaunch = 1 {$condition}
889
                        LIMIT 1";
890
                $result = Database::query($sql);
891
                if (Database::num_rows($result) > 0) {
892
                    $lp_data = Database::fetch_array($result);
893
                    if (!empty($lp_data['iid'])) {
894
                        if ($allowAutoLaunchForCourseAdmins) {
895
                            $showAutoLaunchLpWarning = true;
896
                        } else {
897
                            $session_key = 'lp_autolaunch_'.$session_id.'_'.api_get_course_int_id().'_'.api_get_user_id();
898
                            if (!isset($_SESSION[$session_key])) {
899
                                // Redirecting to the LP
900
                                $url = api_get_path(WEB_CODE_PATH).
901
                                    'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.$lp_data['iid'];
902
903
                                $_SESSION[$session_key] = true;
904
                                header(\sprintf('Location: %s', $url));
905
906
                                exit;
907
                            }
908
                        }
909
                    }
910
                }
911
            }
912
        }
913
914
        if ($showAutoLaunchLpWarning) {
915
            $autoLaunchWarning = get_lang(
916
                'The learning path auto-launch setting is ON. When learners enter this course, they will be automatically redirected to the learning path marked as auto-launch.'
917
            );
918
        }
919
920
        $forumAutoLaunch = (int) api_get_course_setting('enable_forum_auto_launch');
921
        if (1 === $forumAutoLaunch) {
922
            if ($allowAutoLaunchForCourseAdmins) {
923
                if (empty($autoLaunchWarning)) {
924
                    $autoLaunchWarning = get_lang(
925
                        "The forum's auto-launch setting is on. Students will be redirected to the forum tool when entering this course."
926
                    );
927
                }
928
            } else {
929
                $url = api_get_path(WEB_CODE_PATH).'forum/index.php?'.api_get_cidreq();
930
                header(\sprintf('Location: %s', $url));
931
932
                exit;
933
            }
934
        }
935
936
        $exerciseAutoLaunch = (int) api_get_course_setting('enable_exercise_auto_launch');
937
        if (2 === $exerciseAutoLaunch) {
938
            if ($allowAutoLaunchForCourseAdmins) {
939
                if (empty($autoLaunchWarning)) {
940
                    $autoLaunchWarning = get_lang(
941
                        'TheExerciseAutoLaunchSettingIsONStudentsWillBeRedirectToTheExerciseList'
942
                    );
943
                }
944
            } else {
945
                // Redirecting to the document
946
                $url = api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'.api_get_cidreq();
947
                header(\sprintf('Location: %s', $url));
948
949
                exit;
950
            }
951
        } elseif (1 === $exerciseAutoLaunch) {
952
            if ($allowAutoLaunchForCourseAdmins) {
953
                if (empty($autoLaunchWarning)) {
954
                    $autoLaunchWarning = get_lang(
955
                        'TheExerciseAutoLaunchSettingIsONStudentsWillBeRedirectToAnSpecificExercise'
956
                    );
957
                }
958
            } else {
959
                // Redirecting to an exercise
960
                $table = Database::get_course_table(TABLE_QUIZ_TEST);
961
                $condition = '';
962
                if (!empty($session_id)) {
963
                    $condition = api_get_session_condition($session_id);
964
                    $sql = "SELECT iid FROM {$table}
965
                            WHERE c_id = {$course_id} AND autolaunch = 1 {$condition}
966
                            LIMIT 1";
967
                    $result = Database::query($sql);
968
                    // If we found nothing in the session we just called the session_id = 0 autolaunch
969
                    if (0 === Database::num_rows($result)) {
970
                        $condition = '';
971
                    }
972
                }
973
974
                $sql = "SELECT iid FROM {$table}
975
                        WHERE c_id = {$course_id} AND autolaunch = 1 {$condition}
976
                        LIMIT 1";
977
                $result = Database::query($sql);
978
                if (Database::num_rows($result) > 0) {
979
                    $row = Database::fetch_array($result);
980
                    $exerciseId = $row['iid'];
981
                    $url = api_get_path(WEB_CODE_PATH).
982
                        'exercise/overview.php?exerciseId='.$exerciseId.'&'.api_get_cidreq();
983
                    header(\sprintf('Location: %s', $url));
984
985
                    exit;
986
                }
987
            }
988
        }
989
990
        $documentAutoLaunch = (int) api_get_course_setting('enable_document_auto_launch');
991
        if (1 === $documentAutoLaunch) {
992
            if ($allowAutoLaunchForCourseAdmins) {
993
                if (empty($autoLaunchWarning)) {
994
                    $autoLaunchWarning = get_lang(
995
                        'The document auto-launch feature configuration is enabled. Learners will be automatically redirected to document tool.'
996
                    );
997
                }
998
            } else {
999
                // Redirecting to the document
1000
                $url = api_get_path(WEB_CODE_PATH).'document/document.php?'.api_get_cidreq();
1001
                header("Location: $url");
1002
1003
                exit;
1004
            }
1005
        }
1006
1007
        /*  SWITCH TO A DIFFERENT HOMEPAGE VIEW
1008
         the setting homepage_view is adjustable through
1009
         the platform administration section */
1010
        if (!empty($autoLaunchWarning)) {
1011
            $this->addFlash(
1012
                'warning',
1013
                Display::return_message(
1014
                    $autoLaunchWarning,
1015
                    'warning'
1016
                )
1017
            );
1018
        }
1019
    }
1020
1021
    // Implement the real logic to check course enrollment
1022
    private function isUserEnrolledInAnyCourse(User $user, EntityManagerInterface $em): bool
1023
    {
1024
        $enrollmentCount = $em
1025
            ->getRepository(CourseRelUser::class)
1026
            ->count(['user' => $user])
1027
        ;
1028
1029
        return $enrollmentCount > 0;
1030
    }
1031
1032
    // Implement the real logic to check session enrollment
1033
    private function isUserEnrolledInAnySession(User $user, EntityManagerInterface $em): bool
1034
    {
1035
        $enrollmentCount = $em->getRepository(SessionRelUser::class)
1036
            ->count(['user' => $user])
1037
        ;
1038
1039
        return $enrollmentCount > 0;
1040
    }
1041
}
1042