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

CourseService::useTemplateAsBasisIfRequired()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 17
c 0
b 0
f 0
nc 12
nop 2
dl 0
loc 23
rs 8.8333
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