Passed
Push — master ( e9e739...1e7f19 )
by Yannick
09:01
created

CourseController::checkTermsAndConditionJson()   B

Complexity

Conditions 10
Paths 11

Size

Total Lines 62
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 36
nc 11
nop 5
dl 0
loc 62
rs 7.6666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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