Passed
Pull Request — master (#6330)
by
unknown
11:18
created

CourseController::getNextCourse()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 47
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

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