Passed
Push — master ( 1c618d...c1c6b0 )
by Yannick
10:36 queued 02:52
created

CourseService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
c 0
b 0
f 0
nc 1
nop 11
dl 0
loc 13
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CoreBundle\Service;
8
9
use Agenda;
10
use AnnouncementManager;
11
use Answer;
12
use AppPlugin;
13
use Chamilo\CoreBundle\Entity\Course;
14
use Chamilo\CoreBundle\Entity\CourseRelUser;
15
use Chamilo\CoreBundle\Entity\GradebookCategory;
16
use Chamilo\CoreBundle\Entity\GradebookLink;
17
use Chamilo\CoreBundle\Entity\User;
18
use Chamilo\CoreBundle\Repository\CourseCategoryRepository;
19
use Chamilo\CoreBundle\Repository\Node\CourseRepository;
20
use Chamilo\CoreBundle\Repository\Node\UserRepository;
21
use Chamilo\CoreBundle\Settings\SettingsManager;
22
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
23
use Chamilo\CourseBundle\Component\CourseCopy\CourseRestorer;
24
use Chamilo\CourseBundle\Entity\CCourseSetting;
25
use Chamilo\CourseBundle\Entity\CForum;
26
use Chamilo\CourseBundle\Entity\CGroupCategory;
27
use DateTime;
28
use DateTimeZone;
29
use Doctrine\ORM\EntityManager;
30
use DocumentManager;
31
use Exception;
32
use Exercise;
33
use InvalidArgumentException;
34
use Link;
35
use LogicException;
36
use MultipleAnswer;
37
use Symfony\Bundle\SecurityBundle\Security;
38
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
39
use Symfony\Component\Finder\Finder;
40
use Symfony\Component\Finder\SplFileInfo;
41
use Symfony\Component\HttpFoundation\RequestStack;
42
use Symfony\Component\Mailer\MailerInterface;
43
use Symfony\Component\Mime\Email;
44
use Symfony\Contracts\Translation\TranslatorInterface;
45
46
class CourseService
47
{
48
    public const MAX_COURSE_LENGTH_CODE = 40;
49
50
    public function __construct(
51
        private readonly EntityManager $entityManager,
52
        private readonly CourseRepository $courseRepository,
53
        private readonly Security $security,
54
        private readonly CourseCategoryRepository $courseCategoryRepository,
55
        private readonly UserRepository $userRepository,
56
        private readonly SettingsManager $settingsManager,
57
        private readonly TranslatorInterface $translator,
58
        private readonly MailerInterface $mailer,
59
        private readonly EventLoggerService $eventLoggerService,
60
        private readonly ParameterBagInterface $parameterBag,
61
        private readonly RequestStack $requestStack
62
    ) {}
63
64
    public function createCourse(array $params): ?Course
65
    {
66
        if (empty($params['title'])) {
67
            throw new InvalidArgumentException('The course title cannot be empty.');
68
        }
69
70
        if (empty($params['wanted_code'])) {
71
            $params['wanted_code'] = $this->generateCourseCode($params['title']);
72
        }
73
74
        if ($this->courseRepository->courseCodeExists($params['wanted_code'])) {
75
            throw new Exception('The course code already exists: '.$params['wanted_code']);
76
        }
77
78
        $keys = $this->defineCourseKeys($params['wanted_code']);
79
80
        $params = array_merge($params, $keys);
81
        $course = $this->registerCourse($params);
82
        if ($course) {
83
            $this->handlePostCourseCreation($course, $params);
84
        }
85
86
        return $course;
87
    }
88
89
    public function registerCourse(array $rawParams): ?Course
90
    {
91
        try {
92
            /** @var User $currentUser */
93
            $currentUser = $this->security->getUser();
94
95
            $params = $this->prepareAndValidateCourseData($rawParams);
96
97
            $course = new Course();
98
            $course
99
                ->setTitle($params['title'])
100
                ->setCode($params['code'])
101
                ->setVisualCode($params['visualCode'])
102
                ->setCourseLanguage($params['courseLanguage'])
103
                ->setDescription($this->translator->trans('Course Description'))
104
                ->setVisibility((int) $params['visibility'])
105
                ->setShowScore(1)
106
                ->setDiskQuota((int) $params['diskQuota'])
107
                ->setExpirationDate($params['expirationDate'])
108
                ->setDepartmentName($params['departmentName'] ?? '')
109
                ->setDepartmentUrl($params['departmentUrl'])
110
                ->setSubscribe($params['subscribe'])
111
                ->setSticky($params['sticky'] ?? false)
112
                ->setVideoUrl($params['videoUrl'] ?? '')
113
                ->setUnsubscribe($params['unsubscribe'])
114
                ->setCreator($currentUser)
115
            ;
116
117
            if (!empty($params['categories'])) {
118
                foreach ($params['categories'] as $categoryId) {
119
                    $category = $this->courseCategoryRepository->find($categoryId);
120
                    if ($category) {
121
                        $course->addCategory($category);
122
                    }
123
                }
124
            }
125
126
            $addTeacher = $params['add_user_as_teacher'] ?? true;
127
            $user = $currentUser;
128
            if (!empty($params['user_id'])) {
129
                $user = $this->userRepository->find((int) $params['user_id']);
130
            }
131
            if ($addTeacher) {
132
                $courseRelTutor = (new CourseRelUser())
133
                    ->setCourse($course)
134
                    ->setUser($user)
135
                    ->setStatus(1)
136
                    ->setTutor(true)
137
                    ->setRelationType(0)
138
                    ->setUserCourseCat(0)
139
                ;
140
                $course->addSubscription($courseRelTutor);
141
            }
142
143
            if (!empty($params['teachers'])) {
144
                foreach ($params['teachers'] as $teacherId) {
145
                    $teacher = $this->userRepository->find($teacherId);
146
                    if ($teacher) {
147
                        $courseRelTeacher = (new CourseRelUser())
148
                            ->setCourse($course)
149
                            ->setUser($teacher)
150
                            ->setStatus(1)
151
                            ->setTutor(false)
152
                            ->setRelationType(0)
153
                            ->setUserCourseCat(0)
154
                        ;
155
                        $course->addSubscription($courseRelTeacher);
156
                    }
157
                }
158
            }
159
160
            $this->courseRepository->create($course);
161
162
            if ($rawParams['exemplary_content']) {
163
                $this->fillCourse($course, $params);
164
            }
165
166
            if (isset($rawParams['course_template'])) {
167
                $this->useTemplateAsBasisIfRequired(
168
                    $course->getCode(),
169
                    $rawParams['course_template']
170
                );
171
            }
172
173
            return $course;
174
        } catch (Exception $e) {
175
            return null;
176
        }
177
    }
178
179
    public function sendEmailToAdmin(Course $course): void
180
    {
181
        $siteName = $this->getDefaultSetting('platform.site_name');
182
        $recipientEmail = $this->getDefaultSetting('admin.administrator_email');
183
        $recipientName = $this->getDefaultSetting('admin.administrator_name').' '.$this->getDefaultSetting('admin.administrator_surname');
184
        $institutionName = $this->getDefaultSetting('platform.institution');
185
        $courseName = $course->getTitle();
186
187
        $subject = $this->translator->trans('New Course Created in')."$siteName - $institutionName";
188
189
        $greeting = $this->translator->trans('email.greeting');
190
        $intro = $this->translator->trans('email.course_created_intro');
191
        $courseNameLabel = $this->translator->trans('email.course_name');
192
193
        $message = "$greeting $recipientName,\n\n";
194
        $message .= "$intro $siteName - $institutionName.\n";
195
        $message .= "$courseNameLabel $courseName\n";
196
        $message .= $this->translator->trans('Course name: ').$course->getTitle()."\n";
197
198
        foreach ($course->getCategories() as $category) {
199
            $message .= $this->translator->trans('Category: ').$category->getCode()."\n";
200
        }
201
202
        $message .= $this->translator->trans('Coach: ').$course->getTutorName()."\n";
203
        $message .= $this->translator->trans('Language: ').$course->getCourseLanguage();
204
205
        $email = (new Email())
206
            ->from($recipientEmail)
207
            ->to($recipientEmail)
208
            ->subject($subject)
209
            ->text($message)
210
        ;
211
212
        $this->mailer->send($email);
213
    }
214
215
    public function defineCourseKeys(
216
        string $wantedCode,
217
        string $prefixForAll = '',
218
        string $prefixForPath = '',
219
        bool $addUniquePrefix = false,
220
        bool $useCodeIndependentKeys = true
221
    ): array {
222
        $wantedCode = $this->generateCourseCode($wantedCode);
223
        $keysCourseCode = $useCodeIndependentKeys ? $wantedCode : '';
224
225
        $uniquePrefix = $addUniquePrefix ? substr(md5(uniqid((string) rand(), true)), 0, 10) : '';
226
227
        $keys = [];
228
        $finalSuffix = ['CourseId' => '', 'CourseDir' => ''];
229
        $limitNumTry = 100;
230
        $tryCount = 0;
231
232
        $keysAreUnique = false;
233
234
        while (!$keysAreUnique && $tryCount < $limitNumTry) {
235
            $keysCourseId = $prefixForAll.$uniquePrefix.$keysCourseCode.$finalSuffix['CourseId'];
236
            $keysCourseRepository = $prefixForPath.$uniquePrefix.$wantedCode.$finalSuffix['CourseDir'];
237
238
            if ($this->courseRepository->courseCodeExists($keysCourseId)) {
239
                $finalSuffix['CourseId'] = substr(md5(uniqid((string) rand(), true)), 0, 4);
240
                $tryCount++;
241
            } else {
242
                $keysAreUnique = true;
243
            }
244
        }
245
246
        if ($keysAreUnique) {
247
            $keys = [
248
                'code' => $keysCourseCode,
249
                'visual_code' => $keysCourseId,
250
                'directory' => $keysCourseRepository,
251
            ];
252
        }
253
254
        return $keys;
255
    }
256
257
    public function fillCourse(Course $course, array $params): void
258
    {
259
        $entityManager = $this->entityManager;
260
261
        $this->insertCourseSettings($course);
262
263
        $this->createGroupCategory($course);
264
265
        $gradebook = $this->createRootGradebook($course);
266
267
        if ('true' === $this->settingsManager->getSetting('course.example_material_course_creation')) {
268
            $this->insertExampleContent($course, $gradebook);
269
        }
270
271
        $this->installCoursePlugins($course->getId());
272
273
        $entityManager->flush();
274
    }
275
276
    private function insertCourseSettings(Course $course): void
277
    {
278
        $defaultEmailExerciseAlert = 0;
279
        if ('true' === $this->settingsManager->getSetting('exercise.email_alert_manager_on_new_quiz')) {
280
            $defaultEmailExerciseAlert = 1;
281
        }
282
283
        $settings = [
284
            'email_alert_manager_on_new_doc' => ['title' => '', 'default' => 0, 'category' => 'work'],
285
            'email_alert_on_new_doc_dropbox' => ['default' => 0, 'category' => 'dropbox'],
286
            'allow_user_edit_agenda' => ['default' => 0, 'category' => 'agenda'],
287
            'allow_user_edit_announcement' => ['default' => 0, 'category' => 'announcement'],
288
            'email_alert_manager_on_new_quiz' => ['default' => $defaultEmailExerciseAlert, 'category' => 'quiz'],
289
            'allow_user_image_forum' => ['default' => 1, 'category' => 'forum'],
290
            'course_theme' => ['default' => '', 'category' => 'theme'],
291
            'allow_learning_path_theme' => ['default' => 1, 'category' => 'theme'],
292
            'allow_open_chat_window' => ['default' => 1, 'category' => 'chat'],
293
            'email_alert_to_teacher_on_new_user_in_course' => ['default' => 0, 'category' => 'registration'],
294
            'allow_user_view_user_list' => ['default' => 1, 'category' => 'user'],
295
            'display_info_advance_inside_homecourse' => ['default' => 1, 'category' => 'thematic_advance'],
296
            'email_alert_students_on_new_homework' => ['default' => 0, 'category' => 'work'],
297
            'enable_lp_auto_launch' => ['default' => 0, 'category' => 'learning_path'],
298
            'enable_exercise_auto_launch' => ['default' => 0, 'category' => 'exercise'],
299
            'enable_document_auto_launch' => ['default' => 0, 'category' => 'document'],
300
            'pdf_export_watermark_text' => ['default' => '', 'category' => 'learning_path'],
301
            'allow_public_certificates' => [
302
                'default' => 'true' === $this->settingsManager->getSetting('course.allow_public_certificates') ? 1 : '',
303
                'category' => 'certificates',
304
            ],
305
            'documents_default_visibility' => ['default' => 'visible', 'category' => 'document'],
306
            'show_course_in_user_language' => ['default' => 2, 'category' => null],
307
            'email_to_teachers_on_new_work_feedback' => ['default' => 1, 'category' => null],
308
        ];
309
310
        foreach ($settings as $variable => $setting) {
311
            $courseSetting = new CCourseSetting();
312
            $courseSetting->setCId($course->getId());
313
            $courseSetting->setVariable($variable);
314
            $courseSetting->setTitle($setting['title'] ?? '');
315
            $courseSetting->setValue((string) $setting['default']);
316
            $courseSetting->setCategory($setting['category'] ?? '');
317
318
            $this->entityManager->persist($courseSetting);
319
        }
320
321
        $this->entityManager->flush();
322
    }
323
324
    private function createGroupCategory(Course $course): void
325
    {
326
        $groupCategory = new CGroupCategory();
327
        $groupCategory
328
            ->setTitle($this->translator->trans('Default groups'))
329
            ->setParent($course)
330
            ->addCourseLink($course)
331
        ;
332
333
        $this->entityManager->persist($groupCategory);
334
        $this->entityManager->flush();
335
    }
336
337
    private function createRootGradebook(Course $course): GradebookCategory
338
    {
339
        /** @var User $currentUser */
340
        $currentUser = $this->security->getUser();
341
        if (!$currentUser) {
342
            throw new LogicException('There is no user currently authenticated..');
343
        }
344
345
        $gradebookCategory = new GradebookCategory();
346
        $gradebookCategory
347
            ->setTitle($course->getCode())
348
            ->setLocked(0)
349
            ->setGenerateCertificates(false)
350
            ->setDescription('')
351
            ->setCourse($course)
352
            ->setWeight(100)
353
            ->setVisible(false)
354
            ->setCertifMinScore(75)
355
            ->setUser($currentUser)
356
        ;
357
358
        $this->entityManager->persist($gradebookCategory);
359
        $this->entityManager->flush();
360
361
        return $gradebookCategory;
362
    }
363
364
    private function insertExampleContent(Course $course, GradebookCategory $gradebook): void
365
    {
366
        /** @var User $currentUser */
367
        $currentUser = $this->security->getUser();
368
369
        $files = [
370
            ['path' => '/audio', 'title' => $this->translator->trans('Audio'), 'filetype' => 'folder', 'size' => 0],
371
            ['path' => '/images', 'title' => $this->translator->trans('Images'), 'filetype' => 'folder', 'size' => 0],
372
            ['path' => '/images/gallery', 'title' => $this->translator->trans('Gallery'), 'filetype' => 'folder', 'size' => 0],
373
            ['path' => '/video', 'title' => $this->translator->trans('Video'), 'filetype' => 'folder', 'size' => 0],
374
        ];
375
        $paths = [];
376
        $courseInfo = ['real_id' => $course->getId(), 'code' => $course->getCode()];
377
        foreach ($files as $file) {
378
            $doc = DocumentManager::addDocument(
379
                $courseInfo,
380
                $file['path'],
381
                $file['filetype'],
382
                $file['size'],
383
                $file['title'],
384
                null,
385
                0,
386
                null,
387
                0,
388
                0,
389
                0,
390
                false
391
            );
392
            $paths[$file['path']] = $doc->getIid();
393
        }
394
395
        $projectDir = $this->parameterBag->get('kernel.project_dir');
396
        $defaultPath = $projectDir.'/public/img/document';
397
398
        $request = $this->requestStack->getCurrentRequest();
399
        $baseUrl = $request->getSchemeAndHttpHost().$request->getBasePath();
400
401
        $finder = new Finder();
402
        $finder->in($defaultPath);
403
404
        /** @var SplFileInfo $file */
405
        foreach ($finder as $file) {
406
            $parentName = \dirname(str_replace($defaultPath, '', $file->getRealPath()));
407
            if ('/' === $parentName || '/certificates' === $parentName) {
408
                continue;
409
            }
410
411
            $title = $file->getFilename();
412
            $parentId = $paths[$parentName];
413
414
            if ($file->isDir()) {
415
                $realPath = str_replace($defaultPath, '', $file->getRealPath());
416
                $document = DocumentManager::addDocument(
417
                    $courseInfo,
418
                    $realPath,
419
                    'folder',
420
                    null,
421
                    $title,
422
                    '',
423
                    null,
424
                    null,
425
                    null,
426
                    null,
427
                    null,
428
                    false,
429
                    null,
430
                    $parentId,
431
                    $file->getRealPath()
432
                );
433
                $paths[$realPath] = $document->getIid();
434
            } else {
435
                $realPath = str_replace($defaultPath, '', $file->getRealPath());
436
                $document = DocumentManager::addDocument(
437
                    $courseInfo,
438
                    $realPath,
439
                    'file',
440
                    $file->getSize(),
441
                    $title,
442
                    '',
443
                    null,
444
                    null,
445
                    null,
446
                    null,
447
                    null,
448
                    false,
449
                    null,
450
                    $parentId,
451
                    $file->getRealPath()
452
                );
453
            }
454
        }
455
456
        $now = new DateTime('now', new DateTimeZone('UTC'));
457
        $formattedNow = $now->format('Y-m-d H:i:s');
458
459
        $agenda = new Agenda('course');
460
        $agenda->set_course($courseInfo);
461
        $agenda->addEvent(
462
            $formattedNow,
463
            $formattedNow,
464
            0,
465
            $this->translator->trans('Course creation'),
466
            $this->translator->trans('This course was created at this time'),
467
            ['everyone' => 'everyone']
468
        );
469
470
        /*  Links tool */
471
        $link = new Link();
472
        $link->setCourse($courseInfo);
473
        $links = [
474
            [
475
                'c_id' => $course->getId(),
476
                'url' => 'http://www.google.com',
477
                'title' => 'Quick and powerful search engine',
478
                'description' => $this->translator->trans('Quick and powerful search engine'),
479
                'category_id' => 0,
480
                'on_homepage' => 0,
481
                'target' => '_self',
482
                'session_id' => 0,
483
            ],
484
            [
485
                'c_id' => $course->getId(),
486
                'url' => 'http://www.wikipedia.org',
487
                'title' => 'Free online encyclopedia',
488
                'description' => $this->translator->trans('Free online encyclopedia'),
489
                'category_id' => 0,
490
                'on_homepage' => 0,
491
                'target' => '_self',
492
                'session_id' => 0,
493
            ],
494
        ];
495
496
        foreach ($links as $params) {
497
            $link->save($params, false, false);
498
        }
499
500
        /* Announcement tool */
501
        AnnouncementManager::add_announcement(
502
            $courseInfo,
503
            0,
504
            $this->translator->trans('This is an announcement example'),
505
            $this->translator->trans('This is an announcement example. Only trainers are allowed to publish announcements.'),
506
            ['everyone' => 'everyone'],
507
            null,
508
            null,
509
            $formattedNow
510
        );
511
512
        /*  Exercise tool */
513
        $exercise = new Exercise($course->getId());
514
        $exercise->exercise = $this->translator->trans('Sample test');
515
        $html = '<table width="100%" border="0" cellpadding="0" cellspacing="0">
516
                        <tr>
517
                        <td width="220" valign="top" align="left">
518
                            <img src="'.$baseUrl.'/public/img/document/images/mr_chamilo/doubts.png">
519
                        </td>
520
                        <td valign="top" align="left">'.$this->translator->trans('Irony').'</td></tr>
521
                    </table>';
522
        $exercise->type = 1;
523
        $exercise->setRandom(0);
524
        $exercise->active = 1;
525
        $exercise->results_disabled = 0;
526
        $exercise->description = $html;
527
        $exercise->save();
528
529
        $question = new MultipleAnswer();
530
        $question->course = $courseInfo;
531
        $question->question = $this->translator->trans('Socratic irony is...');
532
        $question->description = $this->translator->trans('(more than one answer can be true)');
533
        $question->weighting = 10;
534
        $question->position = 1;
535
        $question->course = $courseInfo;
536
        $question->save($exercise);
537
        $questionId = $question->id;
538
539
        $answer = new Answer($questionId, $courseInfo['real_id']);
540
        $answer->createAnswer($this->translator->trans('Ridiculise one\'s interlocutor in order to have him concede he is wrong.'), 0, $this->translator->trans('No. Socratic irony is not a matter of psychology, it concerns argumentation.'), -5, 1);
541
        $answer->createAnswer($this->translator->trans('Admit one\'s own errors to invite one\'s interlocutor to do the same.'), 0, $this->translator->trans('No. Socratic irony is not a seduction strategy or a method based on the example.'), -5, 2);
542
        $answer->createAnswer($this->translator->trans('Compell one\'s interlocutor, by a series of questions and sub-questions, to admit he doesn\'t know what he claims to know.'), 1, $this->translator->trans('Indeed'), 5, 3);
543
        $answer->createAnswer($this->translator->trans('Use the Principle of Non Contradiction to force one\'s interlocutor into a dead end.'), 1, $this->translator->trans('This answer is not false. It is true that the revelation of the interlocutor\'s ignorance means showing the contradictory conclusions where lead his premisses.'), 5, 4);
544
        $answer->save();
545
546
        // Forums.
547
        $params = [
548
            'forum_category_title' => $this->translator->trans('Example Forum Category'),
549
            'forum_category_comment' => '',
550
        ];
551
552
        $forumCategoryId = saveForumCategory($params, $courseInfo, false);
553
554
        $params = [
555
            'forum_category' => $forumCategoryId,
556
            'forum_title' => $this->translator->trans('Example Forum'),
557
            'forum_comment' => '',
558
            'default_view_type_group' => ['default_view_type' => 'flat'],
559
        ];
560
561
        $forumId = store_forum($params, $courseInfo, true);
562
        $repo = $this->entityManager->getRepository(CForum::class);
563
        $forumEntity = $repo->find($forumId);
564
565
        $params = [
566
            'post_title' => $this->translator->trans('Example Thread'),
567
            'forum_id' => $forumId,
568
            'post_text' => $this->translator->trans('Example ThreadContent'),
569
            'calification_notebook_title' => '',
570
            'numeric_calification' => '',
571
            'weight_calification' => '',
572
            'forum_category' => $forumCategoryId,
573
            'thread_peer_qualify' => 0,
574
        ];
575
576
        saveThread($forumEntity, $params, $courseInfo, false);
577
578
        $this->createExampleGradebookContent($course, $gradebook, $exercise->id);
579
    }
580
581
    private function createExampleGradebookContent(Course $course, GradebookCategory $parentCategory, int $refId): void
582
    {
583
        $manager = $this->entityManager;
584
585
        /* Gradebook tool */
586
        $courseCode = $course->getCode();
587
588
        $childGradebookCategory = new GradebookCategory();
589
        $childGradebookCategory->setTitle($courseCode);
590
        $childGradebookCategory->setLocked(0);
591
        $childGradebookCategory->setGenerateCertificates(false);
592
        $childGradebookCategory->setDescription('');
593
        $childGradebookCategory->setCourse($course);
594
        $childGradebookCategory->setWeight(100);
595
        $childGradebookCategory->setVisible(true);
596
        $childGradebookCategory->setCertifMinScore(75);
597
        $childGradebookCategory->setParent($parentCategory);
598
        $childGradebookCategory->setUser(api_get_user_entity());
599
600
        $manager->persist($childGradebookCategory);
601
        $manager->flush();
602
603
        $gradebookLink = new GradebookLink();
604
605
        $gradebookLink->setType(1);
606
        $gradebookLink->setRefId($refId);
607
        $gradebookLink->setUser(api_get_user_entity());
608
        $gradebookLink->setCourse($course);
609
        $gradebookLink->setCategory($childGradebookCategory);
610
        $gradebookLink->setCreatedAt(new DateTime());
611
        $gradebookLink->setWeight(100);
612
        $gradebookLink->setVisible(1);
613
        $gradebookLink->setLocked(0);
614
615
        $manager->persist($gradebookLink);
616
        $manager->flush();
617
    }
618
619
    private function installCoursePlugins(int $courseId): void
620
    {
621
        $app_plugin = new AppPlugin();
622
        $app_plugin->install_course_plugins($courseId);
623
    }
624
625
    public function useTemplateAsBasisIfRequired($courseCode, $courseTemplate): void
626
    {
627
        $templateSetting = $this->settingsManager->getSetting('course.course_creation_use_template');
628
        $teacherCanSelectCourseTemplate = 'true' === $this->settingsManager->getSetting('course.teacher_can_select_course_template');
629
        $courseTemplate = isset($courseTemplate) ? (int) $courseTemplate : 0;
630
631
        $useTemplate = false;
632
633
        if ($teacherCanSelectCourseTemplate && $courseTemplate) {
634
            $useTemplate = true;
635
            $originCourse = $this->courseRepository->findCourseAsArray($courseTemplate);
636
        } elseif (!empty($templateSetting)) {
637
            $useTemplate = true;
638
            $originCourse = $this->courseRepository->findCourseAsArray($templateSetting);
639
        }
640
641
        if ($useTemplate && $originCourse) {
642
            $originCourse['official_code'] = $originCourse['code'];
643
            $cb = new CourseBuilder(null, $originCourse);
644
            $course = $cb->build(null, $originCourse['code']);
645
            $cr = new CourseRestorer($course);
646
            $cr->set_file_option();
647
            $cr->restore($courseCode);
648
        }
649
    }
650
651
    private function prepareAndValidateCourseData(array $params): array
652
    {
653
        $title = str_replace('&amp;', '&', $params['title'] ?? '');
654
        $code = $params['code'] ?? '';
655
        $visualCode = $params['visual_code'] ?? '';
656
        $directory = $params['directory'] ?? '';
657
        $tutorName = $params['tutor_name'] ?? null;
658
        $courseLanguage = !empty($params['course_language']) ? $params['course_language'] : $this->getDefaultSetting('language.platform_language');
659
        $departmentName = $params['department_name'] ?? null;
660
        $departmentUrl = $this->fixDepartmentUrl($params['department_url'] ?? '');
661
        $diskQuota = $params['disk_quota'] ?? $this->getDefaultSetting('document.default_document_quotum');
662
        $visibility = $params['visibility'] ?? $this->getDefaultSetting('course.courses_default_creation_visibility', Course::OPEN_PLATFORM);
663
        $subscribe = $params['subscribe'] ?? (Course::OPEN_PLATFORM == $visibility);
664
        $unsubscribe = $params['unsubscribe'] ?? false;
665
        $expirationDate = $params['expiration_date'] ?? $this->getFutureExpirationDate();
666
        $teachers = $params['teachers'] ?? [];
667
        $categories = $params['course_categories'] ?? [];
668
        $notifyAdmins = $this->getDefaultSetting('course.send_email_to_admin_when_create_course');
669
670
        $errors = [];
671
        if (empty($code)) {
672
            $errors[] = 'courseSysCode is missing';
673
        }
674
        if (empty($visualCode)) {
675
            $errors[] = 'courseScreenCode is missing';
676
        }
677
        if (empty($directory)) {
678
            $errors[] = 'courseRepository is missing';
679
        }
680
        if (empty($title)) {
681
            $errors[] = 'title is missing';
682
        }
683
        if ($visibility < 0 || $visibility > 4) {
684
            $errors[] = 'visibility is invalid';
685
        }
686
687
        if (!empty($errors)) {
688
            throw new Exception(implode(', ', $errors));
689
        }
690
691
        return [
692
            'title' => $title,
693
            'code' => $code,
694
            'visualCode' => $visualCode,
695
            'directory' => $directory,
696
            'tutorName' => $tutorName,
697
            'courseLanguage' => $courseLanguage,
698
            'departmentName' => $departmentName,
699
            'departmentUrl' => $departmentUrl,
700
            'diskQuota' => $diskQuota,
701
            'visibility' => $visibility,
702
            'subscribe' => $subscribe,
703
            'unsubscribe' => $unsubscribe,
704
            'expirationDate' => new DateTime($expirationDate),
705
            'teachers' => $teachers,
706
            'categories' => $categories,
707
            'notifyAdmins' => $notifyAdmins,
708
        ];
709
    }
710
711
    private function getDefaultSetting(string $name, $default = null)
712
    {
713
        $settingValue = $this->settingsManager->getSetting($name);
714
715
        return null !== $settingValue ? $settingValue : $default;
716
    }
717
718
    private function fixDepartmentUrl(string $url): string
719
    {
720
        if (!empty($url) && !str_starts_with($url, 'http://') && !str_starts_with($url, 'https://')) {
721
            return 'http://'.$url;
722
        }
723
724
        return 'http://' === $url ? '' : $url;
725
    }
726
727
    private function getFutureExpirationDate(): string
728
    {
729
        return (new DateTime())->modify('+1 year')->format('Y-m-d H:i:s');
730
    }
731
732
    private function generateCourseCode(string $title): string
733
    {
734
        $cleanTitle = preg_replace('/[^A-Z0-9]/', '', strtoupper($this->replaceDangerousChar($title)));
735
736
        return substr($cleanTitle, 0, self::MAX_COURSE_LENGTH_CODE);
737
    }
738
739
    private function replaceDangerousChar(string $text): string
740
    {
741
        $encoding = mb_detect_encoding($text, mb_detect_order(), true);
742
        if ('UTF-8' !== $encoding) {
743
            $text = mb_convert_encoding($text, 'UTF-8', $encoding);
744
        }
745
746
        $text = str_replace(' ', '-', $text);
747
        $text = preg_replace('/[^-\w]+/', '', $text);
748
749
        return preg_replace('/\.+$/', '', $text);
750
    }
751
752
    private function handlePostCourseCreation(Course $course, array $params): void
753
    {
754
        if ($params['notifyAdmins'] ?? false) {
755
            $this->sendEmailToAdmin($course);
756
        }
757
758
        $this->eventLoggerService->addEvent(
759
            'course_created',
760
            'course_id',
761
            $course->getId(),
762
            null,
763
            $this->security->getUser()->getId(),
764
            $course->getId()
765
        );
766
    }
767
}
768