Passed
Push — master ( 9d97a9...43700c )
by Yannick
10:26 queued 01:53
created

CourseController::getNextCourse()   B

Complexity

Conditions 9
Paths 15

Size

Total Lines 56
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 34
nc 15
nop 6
dl 0
loc 56
rs 8.0555
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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