Passed
Pull Request — 1.11.x (#4160)
by Angel Fernando Quiroz
10:13
created

ZoomPlugin::createScheduleWebinar()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 31
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 14
dl 0
loc 31
rs 9.7998
c 1
b 0
f 0
cc 2
nc 2
nop 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
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\Session;
7
use Chamilo\CourseBundle\Entity\CGroupInfo;
8
use Chamilo\PluginBundle\Zoom\API\JWTClient;
9
use Chamilo\PluginBundle\Zoom\API\MeetingInfoGet;
10
use Chamilo\PluginBundle\Zoom\API\MeetingRegistrant;
11
use Chamilo\PluginBundle\Zoom\API\MeetingSettings;
12
use Chamilo\PluginBundle\Zoom\API\RecordingFile;
13
use Chamilo\PluginBundle\Zoom\API\RecordingList;
14
use Chamilo\PluginBundle\Zoom\API\WebinarSchema;
15
use Chamilo\PluginBundle\Zoom\API\WebinarSettings;
16
use Chamilo\PluginBundle\Zoom\Meeting;
17
use Chamilo\PluginBundle\Zoom\MeetingActivity;
18
use Chamilo\PluginBundle\Zoom\MeetingRepository;
19
use Chamilo\PluginBundle\Zoom\Recording;
20
use Chamilo\PluginBundle\Zoom\RecordingRepository;
21
use Chamilo\PluginBundle\Zoom\Registrant;
22
use Chamilo\PluginBundle\Zoom\RegistrantRepository;
23
use Chamilo\PluginBundle\Zoom\Webinar;
24
use Chamilo\UserBundle\Entity\User;
25
use Doctrine\ORM\EntityRepository;
26
use Doctrine\ORM\OptimisticLockException;
27
use Doctrine\ORM\Tools\SchemaTool;
28
use Doctrine\ORM\Tools\ToolsException;
29
30
/**
31
 * Class ZoomPlugin. Integrates Zoom meetings in courses.
32
 */
33
class ZoomPlugin extends Plugin
34
{
35
    const RECORDING_TYPE_CLOUD = 'cloud';
36
    const RECORDING_TYPE_LOCAL = 'local';
37
    const RECORDING_TYPE_NONE = 'none';
38
    public $isCoursePlugin = true;
39
40
    /**
41
     * @var JWTClient
42
     */
43
    private $jwtClient;
44
45
    /**
46
     * ZoomPlugin constructor.
47
     * {@inheritdoc}
48
     * Initializes the API JWT client and the entity repositories.
49
     */
50
    public function __construct()
51
    {
52
        parent::__construct(
53
            '0.4',
54
            'Sébastien Ducoulombier, Julio Montoya',
55
            [
56
                'tool_enable' => 'boolean',
57
                'apiKey' => 'text',
58
                'apiSecret' => 'text',
59
                'verificationToken' => 'text',
60
                'enableParticipantRegistration' => 'boolean',
61
                'enableCloudRecording' => [
62
                    'type' => 'select',
63
                    'options' => [
64
                        self::RECORDING_TYPE_CLOUD => 'Cloud',
65
                        self::RECORDING_TYPE_LOCAL => 'Local',
66
                        self::RECORDING_TYPE_NONE => get_lang('None'),
67
                    ],
68
                ],
69
                'enableGlobalConference' => 'boolean',
70
                'globalConferenceAllowRoles' => [
71
                    'type' => 'select',
72
                    'options' => [
73
                        PLATFORM_ADMIN => get_lang('Administrator'),
74
                        COURSEMANAGER => get_lang('Teacher'),
75
                        STUDENT => get_lang('Student'),
76
                        STUDENT_BOSS => get_lang('StudentBoss'),
77
                    ],
78
                    'attributes' => ['multiple' => 'multiple'],
79
                ],
80
                'accountSelector' => 'text',
81
            ]
82
        );
83
84
        $this->isAdminPlugin = true;
85
        $this->jwtClient = new JWTClient($this->get('apiKey'), $this->get('apiSecret'));
86
    }
87
88
    /**
89
     * Caches and returns an instance of this class.
90
     *
91
     * @return ZoomPlugin the instance to use
92
     */
93
    public static function create()
94
    {
95
        static $instance = null;
96
97
        return $instance ? $instance : $instance = new self();
98
    }
99
100
    /**
101
     * @return bool
102
     */
103
    public static function currentUserCanJoinGlobalMeeting()
104
    {
105
        $user = api_get_user_entity(api_get_user_id());
106
107
        if (null === $user) {
108
            return false;
109
        }
110
111
        //return 'true' === api_get_plugin_setting('zoom', 'enableGlobalConference') && api_user_is_login();
112
        return
113
            'true' === api_get_plugin_setting('zoom', 'enableGlobalConference')
114
            && in_array(
115
                (api_is_platform_admin() ? PLATFORM_ADMIN : $user->getStatus()),
116
                (array) api_get_plugin_setting('zoom', 'globalConferenceAllowRoles')
117
            );
118
    }
119
120
    /**
121
     * @return array
122
     */
123
    public function getProfileBlockItems()
124
    {
125
        $elements = $this->meetingsToWhichCurrentUserIsRegisteredComingSoon();
126
        $addMeetingLink = false;
127
        if (self::currentUserCanJoinGlobalMeeting()) {
128
            $addMeetingLink = true;
129
        }
130
131
        if ($addMeetingLink) {
132
            $elements[$this->get_lang('Meetings')] = api_get_path(WEB_PLUGIN_PATH).'zoom/meetings.php';
133
        }
134
135
        $items = [];
136
        foreach ($elements as $title => $link) {
137
            $items[] = [
138
                'class' => 'video-conference',
139
                'icon' => Display::return_icon(
140
                    'bbb.png',
141
                    get_lang('VideoConference')
142
                ),
143
                'link' => $link,
144
                'title' => $title,
145
            ];
146
        }
147
148
        return $items;
149
    }
150
151
    /**
152
     * @return array [ $title => $link ]
153
     */
154
    public function meetingsToWhichCurrentUserIsRegisteredComingSoon()
155
    {
156
        $linkTemplate = api_get_path(WEB_PLUGIN_PATH).'zoom/join_meeting.php?meetingId=%s';
157
        $user = api_get_user_entity(api_get_user_id());
158
        $meetings = self::getRegistrantRepository()->meetingsComingSoonRegistrationsForUser($user);
159
        $items = [];
160
        foreach ($meetings as $registrant) {
161
            $meeting = $registrant->getMeeting();
162
            $items[sprintf(
163
                $this->get_lang('DateMeetingTitle'),
164
                $meeting->formattedStartTime,
165
                $meeting->getMeetingInfoGet()->topic
166
            )] = sprintf($linkTemplate, $meeting->getId());
167
        }
168
169
        return $items;
170
    }
171
172
    /**
173
     * @return RegistrantRepository|EntityRepository
174
     */
175
    public static function getRegistrantRepository()
176
    {
177
        return Database::getManager()->getRepository(Registrant::class);
178
    }
179
180
    /**
181
     * Creates this plugin's related tables in the internal database.
182
     * Installs course fields in all courses.
183
     *
184
     * @throws ToolsException
185
     */
186
    public function install()
187
    {
188
        $schemaManager = Database::getManager()->getConnection()->getSchemaManager();
189
190
        $tablesExists = $schemaManager->tablesExist(
191
            [
192
                'plugin_zoom_meeting',
193
                'plugin_zoom_meeting_activity',
194
                'plugin_zoom_recording',
195
                'plugin_zoom_registrant',
196
            ]
197
        );
198
199
        if ($tablesExists) {
200
            return;
201
        }
202
203
        (new SchemaTool(Database::getManager()))->createSchema(
204
            [
205
                Database::getManager()->getClassMetadata(Meeting::class),
206
                Database::getManager()->getClassMetadata(Webinar::class),
207
                Database::getManager()->getClassMetadata(MeetingActivity::class),
208
                Database::getManager()->getClassMetadata(Recording::class),
209
                Database::getManager()->getClassMetadata(Registrant::class),
210
            ]
211
        );
212
213
        // Copy icons into the main/img/icons folder
214
        $iconName = 'zoom_meet';
215
        $iconsList = [
216
            '64/'.$iconName.'.png',
217
            '64/'.$iconName.'_na.png',
218
            '32/'.$iconName.'.png',
219
            '32/'.$iconName.'_na.png',
220
            '22/'.$iconName.'.png',
221
            '22/'.$iconName.'_na.png',
222
        ];
223
        $sourceDir = api_get_path(SYS_PLUGIN_PATH).'zoom/resources/img/';
224
        $destinationDir = api_get_path(SYS_CODE_PATH).'img/icons/';
225
        foreach ($iconsList as $icon) {
226
            $src = $sourceDir.$icon;
227
            $dest = $destinationDir.$icon;
228
            copy($src, $dest);
229
        }
230
231
        $this->install_course_fields_in_all_courses(true, 'zoom_meet.png');
232
    }
233
234
    /**
235
     * Drops this plugins' related tables from the internal database.
236
     * Uninstalls course fields in all courses().
237
     */
238
    public function uninstall()
239
    {
240
        (new SchemaTool(Database::getManager()))->dropSchema(
241
            [
242
                Database::getManager()->getClassMetadata(Meeting::class),
243
                Database::getManager()->getClassMetadata(Webinar::class),
244
                Database::getManager()->getClassMetadata(MeetingActivity::class),
245
                Database::getManager()->getClassMetadata(Recording::class),
246
                Database::getManager()->getClassMetadata(Registrant::class),
247
            ]
248
        );
249
        $this->uninstall_course_fields_in_all_courses();
250
251
        // Remove icons from the main/img/icons folder
252
        $iconName = 'zoom_meet';
253
        $iconsList = [
254
            '64/'.$iconName.'.png',
255
            '64/'.$iconName.'_na.png',
256
            '32/'.$iconName.'.png',
257
            '32/'.$iconName.'_na.png',
258
            '22/'.$iconName.'.png',
259
            '22/'.$iconName.'_na.png',
260
        ];
261
        $destinationDir = api_get_path(SYS_CODE_PATH).'img/icons/';
262
        foreach ($iconsList as $icon) {
263
            $dest = $destinationDir.$icon;
264
            if (is_file($dest)) {
265
                @unlink($dest);
266
            }
267
        }
268
    }
269
270
    /**
271
     * Generates the search form to include in the meeting list administration page.
272
     * The form has DatePickers 'start' and 'end' and Checkbox 'reloadRecordingLists'.
273
     *
274
     * @return FormValidator the form
275
     */
276
    public function getAdminSearchForm()
277
    {
278
        $form = new FormValidator('search');
279
        $form->addHeader($this->get_lang('SearchMeeting'));
280
        $form->addDatePicker('start', get_lang('StartDate'));
281
        $form->addDatePicker('end', get_lang('EndDate'));
282
        $form->addButtonSearch(get_lang('Search'));
283
        $oneMonth = new DateInterval('P1M');
284
        if ($form->validate()) {
285
            try {
286
                $start = new DateTime($form->getSubmitValue('start'));
287
            } catch (Exception $exception) {
288
                $start = new DateTime();
289
                $start->sub($oneMonth);
290
            }
291
            try {
292
                $end = new DateTime($form->getSubmitValue('end'));
293
            } catch (Exception $exception) {
294
                $end = new DateTime();
295
                $end->add($oneMonth);
296
            }
297
        } else {
298
            $start = new DateTime();
299
            $start->sub($oneMonth);
300
            $end = new DateTime();
301
            $end->add($oneMonth);
302
        }
303
        try {
304
            $form->setDefaults(
305
                [
306
                    'start' => $start->format('Y-m-d'),
307
                    'end' => $end->format('Y-m-d'),
308
                ]
309
            );
310
        } catch (Exception $exception) {
311
            error_log(join(':', [__FILE__, __LINE__, $exception]));
312
        }
313
314
        return $form;
315
    }
316
317
    /**
318
     * Generates a meeting edit form and updates the meeting on validation.
319
     *
320
     * @param Meeting $meeting the meeting
321
     *
322
     * @throws Exception
323
     *
324
     * @return FormValidator
325
     */
326
    public function getEditMeetingForm($meeting)
327
    {
328
        $meetingInfoGet = $meeting->getMeetingInfoGet();
329
        $form = new FormValidator('edit', 'post', $_SERVER['REQUEST_URI']);
330
        $form->addHeader($this->get_lang('UpdateMeeting'));
331
        $form->addText('topic', $this->get_lang('Topic'));
332
        if ($meeting->requiresDateAndDuration()) {
333
            $startTimeDatePicker = $form->addDateTimePicker('startTime', get_lang('StartTime'));
334
            $form->setRequired($startTimeDatePicker);
335
            $durationNumeric = $form->addNumeric('duration', $this->get_lang('DurationInMinutes'));
336
            $form->setRequired($durationNumeric);
337
        }
338
        $form->addTextarea('agenda', get_lang('Agenda'), ['maxlength' => 2000]);
339
        //$form->addLabel(get_lang('Password'), $meeting->getMeetingInfoGet()->password);
340
        // $form->addText('password', get_lang('Password'), false, ['maxlength' => '10']);
341
        $form->addButtonUpdate(get_lang('Update'));
342
        if ($form->validate()) {
343
            if ($meeting->requiresDateAndDuration()) {
344
                $meetingInfoGet->start_time = (new DateTime($form->getSubmitValue('startTime')))->format(
345
                    DATE_ATOM
346
                );
347
                $meetingInfoGet->timezone = date_default_timezone_get();
348
                $meetingInfoGet->duration = (int) $form->getSubmitValue('duration');
349
            }
350
            $meetingInfoGet->topic = $form->getSubmitValue('topic');
351
            $meetingInfoGet->agenda = $form->getSubmitValue('agenda');
352
            try {
353
                $meetingInfoGet->update();
354
                $meeting->setMeetingInfoGet($meetingInfoGet);
355
                Database::getManager()->persist($meeting);
356
                Database::getManager()->flush();
357
                Display::addFlash(
358
                    Display::return_message($this->get_lang('MeetingUpdated'), 'confirm')
359
                );
360
            } catch (Exception $exception) {
361
                Display::addFlash(
362
                    Display::return_message($exception->getMessage(), 'error')
363
                );
364
            }
365
        }
366
        $defaults = [
367
            'topic' => $meetingInfoGet->topic,
368
            'agenda' => $meetingInfoGet->agenda,
369
        ];
370
        if ($meeting->requiresDateAndDuration()) {
371
            $defaults['startTime'] = $meeting->startDateTime->format('Y-m-d H:i');
372
            $defaults['duration'] = $meetingInfoGet->duration;
373
        }
374
        $form->setDefaults($defaults);
375
376
        return $form;
377
    }
378
379
    /**
380
     * @throws Exception
381
     */
382
    public function getEditWebinarForm(Webinar $webinar): FormValidator
383
    {
384
        $schema = $webinar->getWebinarSchema();
385
        $requiresDateAndDuration = $schema->requiresDateAndDuration();
386
387
        $form = new FormValidator('edit', 'post', $_SERVER['REQUEST_URI']);
388
        $form->addHeader($this->get_lang('UpdateWebinar'));
389
        $form->addText('topic', $this->get_lang('Topic'));
390
391
        if ($requiresDateAndDuration) {
392
            $startTimeDatePicker = $form->addDateTimePicker('startTime', get_lang('StartTime'));
393
            $durationNumeric = $form->addNumeric('duration', $this->get_lang('DurationInMinutes'));
394
395
            $form->setRequired($startTimeDatePicker);
396
            $form->setRequired($durationNumeric);
397
        }
398
399
        $form->addTextarea('agenda', get_lang('Agenda'), ['maxlength' => 2000]);
400
        $form->addButtonUpdate(get_lang('Update'));
401
402
        if ($form->validate()) {
403
            $formValues = $form->exportValues();
404
405
            if ($requiresDateAndDuration) {
406
                $schema->start_time = (new DateTime($formValues['startTime']))
407
                    ->format(DATE_ATOM);
408
                $schema->timezone = date_default_timezone_get();
409
                $schema->duration = (int) $formValues['duration'];
410
            }
411
412
            $schema->topic = $formValues['topic'];
413
            $schema->agenda = $formValues['agenda'];
414
415
            try {
416
                $schema->update();
417
                $webinar->setWebinarSchema($schema);
418
419
                $em = Database::getManager();
420
                $em->persist($webinar);
421
                $em->flush();
422
423
                Display::addFlash(
424
                    Display::return_message($this->get_lang('WebinarUpdated'), 'success')
425
                );
426
            } catch (Exception $exception) {
427
                Display::addFlash(
428
                    Display::return_message($exception->getMessage(), 'error')
429
                );
430
            }
431
        }
432
433
        $defaults = [
434
            'topic' => $schema->topic,
435
            'agenda' => $schema->agenda,
436
        ];
437
438
        if ($requiresDateAndDuration) {
439
            $defaults['startTime'] = $webinar->startDateTime->format('Y-m-d H:i');
440
            $defaults['duration'] = $schema->duration;
441
        }
442
443
        $form->setDefaults($defaults);
444
445
        return $form;
446
    }
447
448
    /**
449
     * Generates a meeting delete form and deletes the meeting on validation.
450
     *
451
     * @param Meeting $meeting
452
     * @param string  $returnURL where to redirect to on successful deletion
453
     *
454
     * @throws Exception
455
     *
456
     * @return FormValidator
457
     */
458
    public function getDeleteMeetingForm($meeting, $returnURL)
459
    {
460
        $id = $meeting->getMeetingId();
461
        $form = new FormValidator('delete', 'post', api_get_self().'?meetingId='.$id);
462
        $form->addButtonDelete($this->get_lang('DeleteMeeting'));
463
        if ($form->validate()) {
464
            $this->deleteMeeting($meeting, $returnURL);
465
        }
466
467
        return $form;
468
    }
469
470
    public function getDeleteWebinarForm(Webinar $webinar, string $returnURL): FormValidator
471
    {
472
        $id = $webinar->getId();
473
        $form = new FormValidator('delete', 'post', api_get_self()."?meetingId=$id");
474
        $form->addButtonDelete($this->get_lang('DeleteWebinar'));
475
476
        if ($form->validate()) {
477
            $this->deleteWebinar($webinar, $returnURL);
478
        }
479
480
        return $form;
481
    }
482
483
    /**
484
     * @param Meeting $meeting
485
     * @param string  $returnURL
486
     *
487
     * @return false
488
     */
489
    public function deleteMeeting($meeting, $returnURL)
490
    {
491
        if (null === $meeting) {
492
            return false;
493
        }
494
495
        $em = Database::getManager();
496
        try {
497
            // No need to delete a instant meeting.
498
            if (\Chamilo\PluginBundle\Zoom\API\Meeting::TYPE_INSTANT != $meeting->getMeetingInfoGet()->type) {
499
                $meeting->getMeetingInfoGet()->delete();
500
            }
501
502
            $em->remove($meeting);
503
            $em->flush();
504
505
            Display::addFlash(
506
                Display::return_message($this->get_lang('MeetingDeleted'), 'confirm')
507
            );
508
            api_location($returnURL);
509
        } catch (Exception $exception) {
510
            $this->handleException($exception);
511
        }
512
    }
513
514
    public function deleteWebinar(Webinar $webinar, string $returnURL)
515
    {
516
        $em = Database::getManager();
517
518
        try {
519
            $webinar->getWebinarSchema()->delete();
520
521
            $em->remove($webinar);
522
            $em->flush();
523
524
            Display::addFlash(
525
                Display::return_message($this->get_lang('WebinarDeleted'), 'success')
526
            );
527
528
            api_location($returnURL);
529
        } catch (Exception $exception) {
530
            $this->handleException($exception);
531
        }
532
    }
533
534
    /**
535
     * @param Exception $exception
536
     */
537
    public function handleException($exception)
538
    {
539
        if ($exception instanceof Exception) {
0 ignored issues
show
introduced by
$exception is always a sub-type of Exception.
Loading history...
540
            $error = json_decode($exception->getMessage());
541
            $message = $exception->getMessage();
542
            if ($error->message) {
543
                $message = $error->message;
544
            }
545
            Display::addFlash(
546
                Display::return_message($message, 'error')
547
            );
548
        }
549
    }
550
551
    /**
552
     * Generates a registrant list update form listing course and session users.
553
     * Updates the list on validation.
554
     *
555
     * @param Meeting $meeting
556
     *
557
     * @throws Exception
558
     *
559
     * @return FormValidator
560
     */
561
    public function getRegisterParticipantForm($meeting)
562
    {
563
        $form = new FormValidator('register', 'post', $_SERVER['REQUEST_URI']);
564
        $userIdSelect = $form->addSelect('userIds', $this->get_lang('RegisteredUsers'));
565
        $userIdSelect->setMultiple(true);
566
        $form->addButtonSend($this->get_lang('UpdateRegisteredUserList'));
567
568
        $users = $meeting->getRegistrableUsers();
569
        foreach ($users as $user) {
570
            $userIdSelect->addOption(
571
                api_get_person_name($user->getFirstname(), $user->getLastname()),
572
                $user->getId()
573
            );
574
        }
575
576
        if ($form->validate()) {
577
            $selectedUserIds = $form->getSubmitValue('userIds');
578
            $selectedUsers = [];
579
            if (!empty($selectedUserIds)) {
580
                foreach ($users as $user) {
581
                    if (in_array($user->getId(), $selectedUserIds)) {
582
                        $selectedUsers[] = $user;
583
                    }
584
                }
585
            }
586
587
            try {
588
                $this->updateRegistrantList($meeting, $selectedUsers);
589
                Display::addFlash(
590
                    Display::return_message($this->get_lang('RegisteredUserListWasUpdated'), 'confirm')
591
                );
592
            } catch (Exception $exception) {
593
                Display::addFlash(
594
                    Display::return_message($exception->getMessage(), 'error')
595
                );
596
            }
597
        }
598
        $registeredUserIds = [];
599
        foreach ($meeting->getRegistrants() as $registrant) {
600
            $registeredUserIds[] = $registrant->getUser()->getId();
601
        }
602
        $userIdSelect->setSelected($registeredUserIds);
603
604
        return $form;
605
    }
606
607
    /**
608
     * Generates a meeting recording files management form.
609
     * Takes action on validation.
610
     *
611
     * @param Meeting $meeting
612
     *
613
     * @throws Exception
614
     *
615
     * @return FormValidator
616
     */
617
    public function getFileForm($meeting, $returnURL)
618
    {
619
        $form = new FormValidator('fileForm', 'post', $_SERVER['REQUEST_URI']);
620
        if (!$meeting->getRecordings()->isEmpty()) {
621
            $fileIdSelect = $form->addSelect('fileIds', get_lang('Files'));
622
            $fileIdSelect->setMultiple(true);
623
            $recordingList = $meeting->getRecordings();
624
            foreach ($recordingList as &$recording) {
625
                // $recording->instanceDetails = $plugin->getPastMeetingInstanceDetails($instance->uuid);
626
                $options = [];
627
                $recordings = $recording->getRecordingMeeting()->recording_files;
628
                foreach ($recordings as $file) {
629
                    $options[] = [
630
                        'text' => sprintf(
631
                            '%s.%s (%s)',
632
                            $file->recording_type,
633
                            $file->file_type,
634
                            $file->file_size
635
                        ),
636
                        'value' => $file->id,
637
                    ];
638
                }
639
                $fileIdSelect->addOptGroup(
640
                    $options,
641
                    sprintf("%s (%s)", $recording->formattedStartTime, $recording->formattedDuration)
642
                );
643
            }
644
            $actions = [];
645
            if ($meeting->isCourseMeeting()) {
646
                $actions['CreateLinkInCourse'] = $this->get_lang('CreateLinkInCourse');
647
                $actions['CopyToCourse'] = $this->get_lang('CopyToCourse');
648
            }
649
            $actions['DeleteFile'] = $this->get_lang('DeleteFile');
650
            $form->addRadio(
651
                'action',
652
                get_lang('Action'),
653
                $actions
654
            );
655
            $form->addButtonUpdate($this->get_lang('DoIt'));
656
            if ($form->validate()) {
657
                $action = $form->getSubmitValue('action');
658
                $idList = $form->getSubmitValue('fileIds');
659
660
                foreach ($recordingList as $recording) {
661
                    $recordings = $recording->getRecordingMeeting()->recording_files;
662
663
                    foreach ($recordings as $file) {
664
                        if (in_array($file->id, $idList)) {
665
                            $name = sprintf(
666
                                $this->get_lang('XRecordingOfMeetingXFromXDurationXDotX'),
667
                                $file->recording_type,
668
                                $meeting->getId(),
669
                                $recording->formattedStartTime,
670
                                $recording->formattedDuration,
671
                                $file->file_type
672
                            );
673
                            if ('CreateLinkInCourse' === $action && $meeting->isCourseMeeting()) {
674
                                try {
675
                                    $this->createLinkToFileInCourse($meeting, $file, $name);
676
                                    Display::addFlash(
677
                                        Display::return_message(
678
                                            $this->get_lang('LinkToFileWasCreatedInCourse'),
679
                                            'success'
680
                                        )
681
                                    );
682
                                } catch (Exception $exception) {
683
                                    Display::addFlash(
684
                                        Display::return_message($exception->getMessage(), 'error')
685
                                    );
686
                                }
687
                            } elseif ('CopyToCourse' === $action && $meeting->isCourseMeeting()) {
688
                                try {
689
                                    $this->copyFileToCourse($meeting, $file, $name);
690
                                    Display::addFlash(
691
                                        Display::return_message($this->get_lang('FileWasCopiedToCourse'), 'confirm')
692
                                    );
693
                                } catch (Exception $exception) {
694
                                    Display::addFlash(
695
                                        Display::return_message($exception->getMessage(), 'error')
696
                                    );
697
                                }
698
                            } elseif ('DeleteFile' === $action) {
699
                                try {
700
                                    $name = $file->recording_type;
701
                                    $file->delete();
702
                                    Display::addFlash(
703
                                        Display::return_message($this->get_lang('FileWasDeleted').': '.$name, 'confirm')
704
                                    );
705
                                } catch (Exception $exception) {
706
                                    Display::addFlash(
707
                                        Display::return_message($exception->getMessage(), 'error')
708
                                    );
709
                                }
710
                            }
711
                        }
712
                    }
713
                }
714
                api_location($returnURL);
715
            }
716
        }
717
718
        return $form;
719
    }
720
721
    /**
722
     * Adds to the meeting course documents a link to a meeting instance recording file.
723
     *
724
     * @param Meeting       $meeting
725
     * @param RecordingFile $file
726
     * @param string        $name
727
     *
728
     * @throws Exception
729
     */
730
    public function createLinkToFileInCourse($meeting, $file, $name)
731
    {
732
        $course = $meeting->getCourse();
733
        if (null === $course) {
734
            throw new Exception('This meeting is not linked to a course');
735
        }
736
        $courseInfo = api_get_course_info_by_id($course->getId());
737
        if (empty($courseInfo)) {
738
            throw new Exception('This meeting is not linked to a valid course');
739
        }
740
        $path = '/zoom_meeting_recording_file_'.$file->id.'.'.$file->file_type;
741
        $docId = DocumentManager::addCloudLink($courseInfo, $path, $file->play_url, $name);
742
        if (!$docId) {
743
            throw new Exception(get_lang(DocumentManager::cloudLinkExists($courseInfo, $path, $file->play_url) ? 'UrlAlreadyExists' : 'ErrorAddCloudLink'));
744
        }
745
    }
746
747
    /**
748
     * Copies a recording file to a meeting's course.
749
     *
750
     * @param Meeting       $meeting
751
     * @param RecordingFile $file
752
     * @param string        $name
753
     *
754
     * @throws Exception
755
     */
756
    public function copyFileToCourse($meeting, $file, $name)
757
    {
758
        $course = $meeting->getCourse();
759
        if (null === $course) {
760
            throw new Exception('This meeting is not linked to a course');
761
        }
762
        $courseInfo = api_get_course_info_by_id($course->getId());
763
        if (empty($courseInfo)) {
764
            throw new Exception('This meeting is not linked to a valid course');
765
        }
766
        $tmpFile = tmpfile();
767
        if (false === $tmpFile) {
768
            throw new Exception('tmpfile() returned false');
769
        }
770
        $curl = curl_init($file->getFullDownloadURL($this->jwtClient->token));
771
        if (false === $curl) {
772
            throw new Exception('Could not init curl: '.curl_error($curl));
773
        }
774
        if (!curl_setopt_array(
775
            $curl,
776
            [
777
                CURLOPT_FILE => $tmpFile,
778
                CURLOPT_FOLLOWLOCATION => true,
779
                CURLOPT_MAXREDIRS => 10,
780
                CURLOPT_TIMEOUT => 120,
781
            ]
782
        )) {
783
            throw new Exception("Could not set curl options: ".curl_error($curl));
784
        }
785
        if (false === curl_exec($curl)) {
786
            throw new Exception("curl_exec failed: ".curl_error($curl));
787
        }
788
789
        $sessionId = 0;
790
        $session = $meeting->getSession();
791
        if (null !== $session) {
792
            $sessionId = $session->getId();
793
        }
794
795
        $groupId = 0;
796
        $group = $meeting->getGroup();
797
        if (null !== $group) {
798
            $groupId = $group->getIid();
799
        }
800
801
        $newPath = handle_uploaded_document(
802
            $courseInfo,
803
            [
804
                'name' => $name,
805
                'tmp_name' => stream_get_meta_data($tmpFile)['uri'],
806
                'size' => filesize(stream_get_meta_data($tmpFile)['uri']),
807
                'from_file' => true,
808
                'move_file' => true,
809
                'type' => $file->file_type,
810
            ],
811
            api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document',
812
            '/',
813
            api_get_user_id(),
814
            $groupId,
815
            null,
816
            0,
817
            'overwrite',
818
            true,
819
            false,
820
            null,
821
            $sessionId,
822
            true
823
        );
824
825
        fclose($tmpFile);
826
        if (false === $newPath) {
827
            throw new Exception('Could not handle uploaded document');
828
        }
829
    }
830
831
    /**
832
     * Generates a form to fast and easily create and start an instant meeting.
833
     * On validation, create it then redirect to it and exit.
834
     *
835
     * @return FormValidator
836
     */
837
    public function getCreateInstantMeetingForm(
838
        User $user,
839
        Course $course,
840
        CGroupInfo $group = null,
841
        Session $session = null
842
    ) {
843
        $extraUrl = '';
844
        if (!empty($course)) {
845
            $extraUrl = api_get_cidreq();
846
        }
847
        $form = new FormValidator('createInstantMeetingForm', 'post', api_get_self().'?'.$extraUrl, '_blank');
848
        $form->addButton('startButton', $this->get_lang('StartInstantMeeting'), 'video-camera', 'primary');
849
        if ($form->validate()) {
850
            try {
851
                $this->startInstantMeeting($this->get_lang('InstantMeeting'), $user, $course, $group, $session);
852
            } catch (Exception $exception) {
853
                Display::addFlash(
854
                    Display::return_message($exception->getMessage(), 'error')
855
                );
856
            }
857
        }
858
859
        return $form;
860
    }
861
862
    /**
863
     * Generates a form to schedule a meeting.
864
     * On validation, creates it and redirects to its page.
865
     *
866
     * @throws Exception
867
     *
868
     * @return FormValidator
869
     */
870
    public function getScheduleMeetingForm(User $user, Course $course = null, CGroupInfo $group = null, Session $session = null)
871
    {
872
        $extraUrl = '';
873
        if (!empty($course)) {
874
            $extraUrl = api_get_cidreq();
875
        }
876
        $form = new FormValidator('scheduleMeetingForm', 'post', api_get_self().'?'.$extraUrl);
877
        $form->addHeader($this->get_lang('ScheduleAMeeting'));
878
879
        $form->addSelect(
880
            'conference_type',
881
            $this->get_lang('ConferenceType'),
882
            [
883
                'meeting' => $this->get_lang('Meeting'),
884
                'webinar' => $this->get_lang('Webinar'),
885
            ]
886
        );
887
        $form->addRule('conference_type', get_lang('ThisFieldIsRequired'), 'required');
888
889
        $startTimeDatePicker = $form->addDateTimePicker('startTime', get_lang('StartTime'));
890
        $form->setRequired($startTimeDatePicker);
891
892
        $form->addText('topic', $this->get_lang('Topic'), true);
893
        $form->addTextarea('agenda', get_lang('Agenda'), ['maxlength' => 2000]);
894
895
        $durationNumeric = $form->addNumeric('duration', $this->get_lang('DurationInMinutes'));
896
        $form->setRequired($durationNumeric);
897
898
        if (null === $course && 'true' === $this->get('enableGlobalConference')) {
899
            $options = [];
900
            $options['everyone'] = $this->get_lang('ForEveryone');
901
            $options['registered_users'] = $this->get_lang('SomeUsers');
902
            if (!empty($options)) {
903
                if (1 === count($options)) {
904
                    $form->addHidden('type', key($options));
905
                } else {
906
                    $form->addSelect('type', $this->get_lang('AudienceType'), $options);
907
                }
908
            }
909
        } else {
910
            // To course
911
            $form->addHidden('type', 'course');
912
        }
913
914
        /*
915
       // $passwordText = $form->addText('password', get_lang('Password'), false, ['maxlength' => '10']);
916
       if (null !== $course) {
917
           $registrationOptions = [
918
               'RegisterAllCourseUsers' => $this->get_lang('RegisterAllCourseUsers'),
919
           ];
920
           $groups = GroupManager::get_groups();
921
           if (!empty($groups)) {
922
               $registrationOptions['RegisterTheseGroupMembers'] = get_lang('RegisterTheseGroupMembers');
923
           }
924
           $registrationOptions['RegisterNoUser'] = $this->get_lang('RegisterNoUser');
925
           $userRegistrationRadio = $form->addRadio(
926
               'userRegistration',
927
               $this->get_lang('UserRegistration'),
928
               $registrationOptions
929
           );
930
           $groupOptions = [];
931
           foreach ($groups as $group) {
932
               $groupOptions[$group['id']] = $group['name'];
933
           }
934
           $groupIdsSelect = $form->addSelect(
935
               'groupIds',
936
               $this->get_lang('RegisterTheseGroupMembers'),
937
               $groupOptions
938
           );
939
           $groupIdsSelect->setMultiple(true);
940
           if (!empty($groups)) {
941
               $jsCode = sprintf(
942
                   "getElementById('%s').parentNode.parentNode.parentNode.style.display = getElementById('%s').checked ? 'block' : 'none'",
943
                   $groupIdsSelect->getAttribute('id'),
944
                   $userRegistrationRadio->getelements()[1]->getAttribute('id')
945
               );
946
947
               $form->setAttribute('onchange', $jsCode);
948
           }
949
       }*/
950
951
        $accountEmails = $this->getAccountEmails();
952
953
        if (!empty($accountEmails)) {
954
            $form->addSelect('account_email', $this->get_lang('AccountEmail'), $accountEmails);
955
        }
956
957
        $form->addButtonCreate(get_lang('Save'));
958
959
        if ($form->validate()) {
960
            $formValues = $form->exportValues();
961
            $conferenceType = $formValues['conference_type'];
962
            $password = substr(uniqid('z', true), 0, 10);
963
964
            switch ($formValues['type']) {
965
                case 'everyone':
966
                    $user = null;
967
                    $group = null;
968
                    $course = null;
969
                    $session = null;
970
971
                    break;
972
                case 'registered_users':
973
                    //$user = null;
974
                    $course = null;
975
                    $session = null;
976
977
                    break;
978
                case 'course':
979
                    $user = null;
980
                    //$course = null;
981
                    //$session = null;
982
983
                    break;
984
            }
985
986
            $accountEmail = $formValues['account_email'] ?? null;
987
            $accountEmail = $accountEmail && in_array($accountEmail, $accountEmails) ? $accountEmail : null;
988
989
            try {
990
                $startTime = new DateTime($formValues['startTime']);
991
992
                if ('meeting' === $conferenceType) {
993
                    $newMeeting = $this->createScheduleMeeting(
994
                        $user,
995
                        $course,
996
                        $group,
997
                        $session,
998
                        $startTime,
999
                        $formValues['duration'],
1000
                        $formValues['topic'],
1001
                        $formValues['agenda'],
1002
                        $password,
1003
                        $accountEmail
1004
                    );
1005
                } elseif ('webinar' === $conferenceType) {
1006
                    $newMeeting = $this->createScheduleWebinar(
1007
                        $user,
1008
                        $course,
1009
                        $group,
1010
                        $session,
1011
                        $startTime,
1012
                        $formValues['duration'],
1013
                        $formValues['topic'],
1014
                        $formValues['agenda'],
1015
                        $password,
1016
                        $accountEmail
1017
                    );
1018
                } else {
1019
                    throw new Exception('Invalid conference type');
1020
                }
1021
1022
                Display::addFlash(
1023
                    Display::return_message($this->get_lang('NewMeetingCreated'))
1024
                );
1025
1026
                if ($newMeeting->isCourseMeeting()) {
1027
                    if ('RegisterAllCourseUsers' === $form->getSubmitValue('userRegistration')) {
1028
                        $this->registerAllCourseUsers($newMeeting);
1029
                        Display::addFlash(
1030
                            Display::return_message($this->get_lang('AllCourseUsersWereRegistered'))
1031
                        );
1032
                    } elseif ('RegisterTheseGroupMembers' === $form->getSubmitValue('userRegistration')) {
1033
                        $userIds = [];
1034
                        foreach ($form->getSubmitValue('groupIds') as $groupId) {
1035
                            $userIds = array_unique(array_merge($userIds, GroupManager::get_users($groupId)));
1036
                        }
1037
                        $users = Database::getManager()->getRepository('ChamiloUserBundle:User')->findBy(
1038
                            ['id' => $userIds]
1039
                        );
1040
                        $this->registerUsers($newMeeting, $users);
1041
                        Display::addFlash(
1042
                            Display::return_message($this->get_lang('GroupUsersWereRegistered'))
1043
                        );
1044
                    }
1045
                }
1046
                api_location('meeting.php?meetingId='.$newMeeting->getMeetingId().'&'.$extraUrl);
1047
            } catch (Exception $exception) {
1048
                Display::addFlash(
1049
                    Display::return_message($exception->getMessage(), 'error')
1050
                );
1051
            }
1052
        } else {
1053
            $form->setDefaults(
1054
                [
1055
                    'duration' => 60,
1056
                    'userRegistration' => 'RegisterAllCourseUsers',
1057
                ]
1058
            );
1059
        }
1060
1061
        return $form;
1062
    }
1063
1064
    /**
1065
     * Return the current global meeting (create it if needed).
1066
     *
1067
     * @throws Exception
1068
     *
1069
     * @return string
1070
     */
1071
    public function getGlobalMeeting()
1072
    {
1073
        foreach ($this->getMeetingRepository()->unfinishedGlobalMeetings() as $meeting) {
1074
            return $meeting;
1075
        }
1076
1077
        return $this->createGlobalMeeting();
1078
    }
1079
1080
    /**
1081
     * @return MeetingRepository|EntityRepository
1082
     */
1083
    public static function getMeetingRepository()
1084
    {
1085
        return Database::getManager()->getRepository(Meeting::class);
1086
    }
1087
1088
    /**
1089
     * Returns the URL to enter (start or join) a meeting or null if not possible to enter the meeting,
1090
     * The returned URL depends on the meeting current status (waiting, started or finished) and the current user.
1091
     *
1092
     * @param Meeting $meeting
1093
     *
1094
     * @throws OptimisticLockException
1095
     * @throws Exception
1096
     *
1097
     * @return string|null
1098
     */
1099
    public function getStartOrJoinMeetingURL($meeting)
1100
    {
1101
        $status = $meeting->getMeetingInfoGet()->status;
1102
        $userId = api_get_user_id();
1103
        $currentUser = api_get_user_entity($userId);
1104
        $isGlobal = 'true' === $this->get('enableGlobalConference') && $meeting->isGlobalMeeting();
1105
1106
        switch ($status) {
1107
            case 'ended':
1108
                if ($this->userIsConferenceManager($meeting)) {
1109
                    return $meeting->getMeetingInfoGet()->start_url;
1110
                }
1111
                break;
1112
            case 'waiting':
1113
                // Zoom does not allow for a new meeting to be started on first participant join.
1114
                // It requires the host to start the meeting first.
1115
                // Therefore for global meetings we must make the first participant the host
1116
                // that is use start_url rather than join_url.
1117
                // the participant will not be registered and will appear as the Zoom user account owner.
1118
                // For course and user meetings, only the host can start the meeting.
1119
                if ($this->userIsConferenceManager($meeting)) {
1120
                    return $meeting->getMeetingInfoGet()->start_url;
1121
                }
1122
1123
                break;
1124
            case 'started':
1125
                // User per conference.
1126
                if ($currentUser === $meeting->getUser()) {
1127
                    return $meeting->getMeetingInfoGet()->join_url;
1128
                }
1129
1130
                // The participant is not registered, he can join only the global meeting (automatic registration).
1131
                if ($isGlobal) {
1132
                    return $this->registerUser($meeting, $currentUser)->getCreatedRegistration()->join_url;
1133
                }
1134
1135
                if ($meeting->isCourseMeeting()) {
1136
                    if ($this->userIsCourseConferenceManager()) {
1137
                        return $meeting->getMeetingInfoGet()->start_url;
1138
                    }
1139
1140
                    $sessionId = api_get_session_id();
1141
                    $courseCode = api_get_course_id();
1142
1143
                    if (empty($sessionId)) {
1144
                        $isSubscribed = CourseManager::is_user_subscribed_in_course(
1145
                            $userId,
1146
                            $courseCode,
1147
                            false
1148
                        );
1149
                    } else {
1150
                        $isSubscribed = CourseManager::is_user_subscribed_in_course(
1151
                            $userId,
1152
                            $courseCode,
1153
                            true,
1154
                            $sessionId
1155
                        );
1156
                    }
1157
1158
                    if ($isSubscribed) {
1159
                        if ($meeting->isCourseGroupMeeting()) {
1160
                            $groupInfo = GroupManager::get_group_properties($meeting->getGroup()->getIid(), true);
1161
                            $isInGroup = GroupManager::is_user_in_group($userId, $groupInfo);
1162
                            if (false === $isInGroup) {
1163
                                throw new Exception($this->get_lang('YouAreNotRegisteredToThisMeeting'));
1164
                            }
1165
                        }
1166
1167
                        if (\Chamilo\PluginBundle\Zoom\API\Meeting::TYPE_INSTANT == $meeting->getMeetingInfoGet()->type) {
1168
                            return $meeting->getMeetingInfoGet()->join_url;
1169
                        }
1170
1171
                        return $this->registerUser($meeting, $currentUser)->getCreatedRegistration()->join_url;
1172
                    }
1173
1174
                    throw new Exception($this->get_lang('YouAreNotRegisteredToThisMeeting'));
1175
                }
1176
1177
                //if ('true' === $this->get('enableParticipantRegistration')) {
1178
                    //if ('true' === $this->get('enableParticipantRegistration') && $meeting->requiresRegistration()) {
1179
                    // the participant must be registered
1180
                    $registrant = $meeting->getRegistrant($currentUser);
1181
                    if (null == $registrant) {
1182
                        throw new Exception($this->get_lang('YouAreNotRegisteredToThisMeeting'));
1183
                    }
1184
1185
                    // the participant is registered
1186
                    return $registrant->getCreatedRegistration()->join_url;
1187
                //}
1188
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1189
        }
1190
1191
        return null;
1192
    }
1193
1194
    /**
1195
     * @param Meeting $meeting
1196
     *
1197
     * @return bool whether the logged-in user can manage conferences in this context, that is either
1198
     *              the current course or session coach, the platform admin or the current course admin
1199
     */
1200
    public function userIsConferenceManager($meeting)
1201
    {
1202
        if (null === $meeting) {
1203
            return false;
1204
        }
1205
1206
        if (api_is_coach() || api_is_platform_admin()) {
1207
            return true;
1208
        }
1209
1210
        if ($meeting->isCourseMeeting() && api_get_course_id() && api_is_course_admin()) {
1211
            return true;
1212
        }
1213
1214
        return $meeting->isUserMeeting() && $meeting->getUser()->getId() == api_get_user_id();
1215
    }
1216
1217
    /**
1218
     * @return bool whether the logged-in user can manage conferences in this context, that is either
1219
     *              the current course or session coach, the platform admin or the current course admin
1220
     */
1221
    public function userIsCourseConferenceManager()
1222
    {
1223
        if (api_is_coach() || api_is_platform_admin()) {
1224
            return true;
1225
        }
1226
1227
        if (api_get_course_id() && api_is_course_admin()) {
1228
            return true;
1229
        }
1230
1231
        return false;
1232
    }
1233
1234
    /**
1235
     * Update local recording list from remote Zoom server's version.
1236
     * Kept to implement a future administration button ("import existing data from zoom server").
1237
     *
1238
     * @param DateTime $startDate
1239
     * @param DateTime $endDate
1240
     *
1241
     * @throws OptimisticLockException
1242
     * @throws Exception
1243
     */
1244
    public function reloadPeriodRecordings($startDate, $endDate)
1245
    {
1246
        $em = Database::getManager();
1247
        $recordingRepo = $this->getRecordingRepository();
1248
        $meetingRepo = $this->getMeetingRepository();
1249
        $recordings = RecordingList::loadPeriodRecordings($startDate, $endDate);
1250
1251
        foreach ($recordings as $recordingMeeting) {
1252
            $recordingEntity = $recordingRepo->findOneBy(['uuid' => $recordingMeeting->uuid]);
1253
            if (null === $recordingEntity) {
1254
                $recordingEntity = new Recording();
1255
                $meeting = $meetingRepo->findOneBy(['meetingId' => $recordingMeeting->id]);
1256
                if (null === $meeting) {
1257
                    try {
1258
                        $meetingInfoGet = MeetingInfoGet::fromId($recordingMeeting->id);
1259
                    } catch (Exception $exception) {
1260
                        $meetingInfoGet = null; // deleted meeting with recordings
1261
                    }
1262
                    if (null !== $meetingInfoGet) {
1263
                        $meeting = $this->createMeetingFromMeeting(
1264
                            (new Meeting())->setMeetingInfoGet($meetingInfoGet)
1265
                        );
1266
                        $em->persist($meeting);
1267
                    }
1268
                }
1269
                if (null !== $meeting) {
1270
                    $recordingEntity->setMeeting($meeting);
1271
                }
1272
            }
1273
            $recordingEntity->setRecordingMeeting($recordingMeeting);
1274
            $em->persist($recordingEntity);
1275
        }
1276
        $em->flush();
1277
    }
1278
1279
    /**
1280
     * @return RecordingRepository|EntityRepository
1281
     */
1282
    public static function getRecordingRepository()
1283
    {
1284
        return Database::getManager()->getRepository(Recording::class);
1285
    }
1286
1287
    public function getToolbar($returnUrl = '')
1288
    {
1289
        if (!api_is_platform_admin()) {
1290
            return '';
1291
        }
1292
1293
        $actionsLeft = '';
1294
        $back = '';
1295
        $courseId = api_get_course_id();
1296
        if (empty($courseId)) {
1297
            $actionsLeft .=
1298
                Display::url(
1299
                    Display::return_icon('bbb.png', $this->get_lang('Meetings'), null, ICON_SIZE_MEDIUM),
1300
                    api_get_path(WEB_PLUGIN_PATH).'zoom/meetings.php'
1301
                );
1302
        } else {
1303
            $actionsLeft .=
1304
                Display::url(
1305
                    Display::return_icon('bbb.png', $this->get_lang('Meetings'), null, ICON_SIZE_MEDIUM),
1306
                    api_get_path(WEB_PLUGIN_PATH).'zoom/start.php?'.api_get_cidreq()
1307
                );
1308
        }
1309
1310
        if (!empty($returnUrl)) {
1311
            $back = Display::url(
1312
                Display::return_icon('back.png', get_lang('Back'), null, ICON_SIZE_MEDIUM),
1313
                $returnUrl
1314
            );
1315
        }
1316
1317
        if (api_is_platform_admin()) {
1318
            $actionsLeft .= Display::url(
1319
                Display::return_icon('agenda.png', get_lang('Calendar'), [], ICON_SIZE_MEDIUM),
1320
                'calendar.php'
1321
            );
1322
            $actionsLeft .=
1323
                Display::url(
1324
                    Display::return_icon('settings.png', get_lang('Settings'), null, ICON_SIZE_MEDIUM),
1325
                    api_get_path(WEB_CODE_PATH).'admin/configure_plugin.php?name=zoom'
1326
                ).$back;
1327
        }
1328
1329
        return Display::toolbarAction('toolbar', [$actionsLeft]);
1330
    }
1331
1332
    public function getRecordingSetting()
1333
    {
1334
        $recording = (string) $this->get('enableCloudRecording');
1335
1336
        if (in_array($recording, [self::RECORDING_TYPE_LOCAL, self::RECORDING_TYPE_CLOUD], true)) {
1337
            return $recording;
1338
        }
1339
1340
        return self::RECORDING_TYPE_NONE;
1341
    }
1342
1343
    public function hasRecordingAvailable()
1344
    {
1345
        $recording = $this->getRecordingSetting();
1346
1347
        return self::RECORDING_TYPE_NONE !== $recording;
1348
    }
1349
1350
    /**
1351
     * @throws Exception
1352
     */
1353
    public function createWebinarFromSchema(Webinar $webinar, WebinarSchema $schema): Webinar
1354
    {
1355
        $currentUser = api_get_user_entity(api_get_user_id());
1356
1357
        $schema->settings->contact_email = $currentUser->getEmail();
1358
        $schema->settings->contact_name = $currentUser->getFullname();
1359
        $schema->settings->auto_recording = $this->getRecordingSetting();
1360
        $schema->settings->registrants_email_notification = false;
1361
        $schema->settings->attendees_and_panelists_reminder_email_notification->enable = false;
1362
        $schema->settings->follow_up_attendees_email_notification->enable = false;
1363
        $schema->settings->follow_up_absentees_email_notification->enable = false;
1364
1365
        $schema = $schema->create($webinar->getAccountEmail());
1366
1367
        $webinar->setWebinarSchema($schema);
1368
1369
        $em = Database::getManager();
1370
        $em->persist($webinar);
1371
        $em->flush();
1372
1373
        return $webinar;
1374
    }
1375
1376
    public function getAccountEmails(): array
1377
    {
1378
        $currentValue = $this->get('accountSelector');
1379
1380
        if (empty($currentValue)) {
1381
            return [];
1382
        }
1383
1384
        $emails = explode(';', $currentValue);
1385
        $trimmed = array_map('trim', $emails);
1386
        $filtered = array_filter($trimmed);
1387
1388
        return array_combine($filtered, $filtered);
1389
    }
1390
1391
    /**
1392
     * Updates meeting registrants list. Adds the missing registrants and removes the extra.
1393
     *
1394
     * @param Meeting $meeting
1395
     * @param User[]  $users   list of users to be registered
1396
     *
1397
     * @throws Exception
1398
     */
1399
    private function updateRegistrantList($meeting, $users)
1400
    {
1401
        $usersToAdd = [];
1402
        foreach ($users as $user) {
1403
            $found = false;
1404
            foreach ($meeting->getRegistrants() as $registrant) {
1405
                if ($registrant->getUser() === $user) {
1406
                    $found = true;
1407
                    break;
1408
                }
1409
            }
1410
            if (!$found) {
1411
                $usersToAdd[] = $user;
1412
            }
1413
        }
1414
        $registrantsToRemove = [];
1415
        foreach ($meeting->getRegistrants() as $registrant) {
1416
            $found = false;
1417
            foreach ($users as $user) {
1418
                if ($registrant->getUser() === $user) {
1419
                    $found = true;
1420
                    break;
1421
                }
1422
            }
1423
            if (!$found) {
1424
                $registrantsToRemove[] = $registrant;
1425
            }
1426
        }
1427
        $this->registerUsers($meeting, $usersToAdd);
1428
        $this->unregister($meeting, $registrantsToRemove);
1429
    }
1430
1431
    /**
1432
     * Register users to a meeting.
1433
     *
1434
     * @param Meeting $meeting
1435
     * @param User[]  $users
1436
     *
1437
     * @throws OptimisticLockException
1438
     *
1439
     * @return User[] failed registrations [ user id => errorMessage ]
1440
     */
1441
    private function registerUsers($meeting, $users)
1442
    {
1443
        $failedUsers = [];
1444
        foreach ($users as $user) {
1445
            try {
1446
                $this->registerUser($meeting, $user, false);
1447
            } catch (Exception $exception) {
1448
                $failedUsers[$user->getId()] = $exception->getMessage();
1449
            }
1450
        }
1451
        Database::getManager()->flush();
1452
1453
        return $failedUsers;
1454
    }
1455
1456
    /**
1457
     * @throws Exception
1458
     * @throws OptimisticLockException
1459
     *
1460
     * @return Registrant
1461
     */
1462
    private function registerUser(Meeting $meeting, User $user, $andFlush = true)
1463
    {
1464
        if (empty($user->getEmail())) {
1465
            throw new Exception($this->get_lang('CannotRegisterWithoutEmailAddress'));
1466
        }
1467
1468
        $meetingRegistrant = MeetingRegistrant::fromEmailAndFirstName(
1469
            $user->getEmail(),
1470
            $user->getFirstname(),
1471
            $user->getLastname()
1472
        );
1473
1474
        $registrantEntity = (new Registrant())
1475
            ->setMeeting($meeting)
1476
            ->setUser($user)
1477
            ->setMeetingRegistrant($meetingRegistrant)
1478
            ->setCreatedRegistration($meeting->getMeetingInfoGet()->addRegistrant($meetingRegistrant));
1479
        Database::getManager()->persist($registrantEntity);
1480
1481
        if ($andFlush) {
1482
            Database::getManager()->flush($registrantEntity);
1483
        }
1484
1485
        return $registrantEntity;
1486
    }
1487
1488
    /**
1489
     * Removes registrants from a meeting.
1490
     *
1491
     * @param Meeting      $meeting
1492
     * @param Registrant[] $registrants
1493
     *
1494
     * @throws Exception
1495
     */
1496
    private function unregister($meeting, $registrants)
1497
    {
1498
        $meetingRegistrants = [];
1499
        foreach ($registrants as $registrant) {
1500
            $meetingRegistrants[] = $registrant->getMeetingRegistrant();
1501
        }
1502
        $meeting->getMeetingInfoGet()->removeRegistrants($meetingRegistrants);
1503
        $em = Database::getManager();
1504
        foreach ($registrants as $registrant) {
1505
            $em->remove($registrant);
1506
        }
1507
        $em->flush();
1508
    }
1509
1510
    /**
1511
     * Starts a new instant meeting and redirects to its start url.
1512
     *
1513
     * @param string          $topic
1514
     * @param User|null       $user
1515
     * @param Course|null     $course
1516
     * @param CGroupInfo|null $group
1517
     * @param Session|null    $session
1518
     *
1519
     * @throws Exception
1520
     */
1521
    private function startInstantMeeting($topic, $user = null, $course = null, $group = null, $session = null)
1522
    {
1523
        $meetingInfoGet = MeetingInfoGet::fromTopicAndType($topic, MeetingInfoGet::TYPE_INSTANT);
1524
        //$meetingInfoGet->settings->approval_type = MeetingSettings::APPROVAL_TYPE_AUTOMATICALLY_APPROVE;
1525
        $meeting = $this->createMeetingFromMeeting(
1526
            (new Meeting())
1527
                ->setMeetingInfoGet($meetingInfoGet)
1528
                ->setUser($user)
1529
                ->setGroup($group)
1530
                ->setCourse($course)
1531
                ->setSession($session)
1532
        );
1533
        api_location($meeting->getMeetingInfoGet()->start_url);
1534
    }
1535
1536
    /**
1537
     * Creates a meeting on Zoom servers and stores it in the local database.
1538
     *
1539
     * @param Meeting $meeting a new, unsaved meeting with at least a type and a topic
1540
     *
1541
     * @throws Exception
1542
     *
1543
     * @return Meeting
1544
     */
1545
    private function createMeetingFromMeeting($meeting)
1546
    {
1547
        $currentUser = api_get_user_entity(api_get_user_id());
1548
1549
        $meeting->getMeetingInfoGet()->settings->contact_email = $currentUser->getEmail();
1550
        $meeting->getMeetingInfoGet()->settings->contact_name = $currentUser->getFullname();
1551
        $meeting->getMeetingInfoGet()->settings->auto_recording = $this->getRecordingSetting();
1552
        $meeting->getMeetingInfoGet()->settings->registrants_email_notification = false;
1553
1554
        //$meeting->getMeetingInfoGet()->host_email = $currentUser->getEmail();
1555
        //$meeting->getMeetingInfoGet()->settings->alternative_hosts = $currentUser->getEmail();
1556
1557
        // Send create to Zoom.
1558
        $meeting->setMeetingInfoGet(
1559
            $meeting->getMeetingInfoGet()->create(
1560
                $meeting->getAccountEmail()
1561
            )
1562
        );
1563
1564
        Database::getManager()->persist($meeting);
1565
        Database::getManager()->flush();
1566
1567
        return $meeting;
1568
    }
1569
1570
    /**
1571
     * @throws Exception
1572
     *
1573
     * @return Meeting
1574
     */
1575
    private function createGlobalMeeting()
1576
    {
1577
        $meetingInfoGet = MeetingInfoGet::fromTopicAndType(
1578
            $this->get_lang('GlobalMeeting'),
1579
            MeetingInfoGet::TYPE_SCHEDULED
1580
        );
1581
        $meetingInfoGet->start_time = (new DateTime())->format(DATE_ATOM);
1582
        $meetingInfoGet->duration = 60;
1583
        $meetingInfoGet->settings->approval_type =
1584
            ('true' === $this->get('enableParticipantRegistration'))
1585
                ? MeetingSettings::APPROVAL_TYPE_AUTOMATICALLY_APPROVE
1586
                : MeetingSettings::APPROVAL_TYPE_NO_REGISTRATION_REQUIRED;
1587
        // $meetingInfoGet->settings->host_video = true;
1588
        $meetingInfoGet->settings->participant_video = true;
1589
        $meetingInfoGet->settings->join_before_host = true;
1590
        $meetingInfoGet->settings->registrants_email_notification = false;
1591
1592
        return $this->createMeetingFromMeeting((new Meeting())->setMeetingInfoGet($meetingInfoGet));
1593
    }
1594
1595
    /**
1596
     * Schedules a meeting and returns it.
1597
     * set $course, $session and $user to null in order to create a global meeting.
1598
     *
1599
     * @param DateTime $startTime meeting local start date-time (configure local timezone on your Zoom account)
1600
     * @param int      $duration  in minutes
1601
     * @param string   $topic     short title of the meeting, required
1602
     * @param string   $agenda    ordre du jour
1603
     * @param string   $password  meeting password
1604
     *
1605
     * @throws Exception
1606
     *
1607
     * @return Meeting meeting
1608
     */
1609
    private function createScheduleMeeting(
1610
        User $user = null,
1611
        Course $course = null,
1612
        CGroupInfo $group = null,
1613
        Session $session = null,
1614
        $startTime,
1615
        $duration,
1616
        $topic,
1617
        $agenda,
1618
        $password,
1619
        string $accountEmail = null
1620
    ) {
1621
        $meetingInfoGet = MeetingInfoGet::fromTopicAndType($topic, MeetingInfoGet::TYPE_SCHEDULED);
1622
        $meetingInfoGet->duration = $duration;
1623
        $meetingInfoGet->start_time = $startTime->format(DATE_ATOM);
1624
        $meetingInfoGet->agenda = $agenda;
1625
        $meetingInfoGet->password = $password;
1626
        $meetingInfoGet->settings->approval_type = MeetingSettings::APPROVAL_TYPE_NO_REGISTRATION_REQUIRED;
1627
        if ('true' === $this->get('enableParticipantRegistration')) {
1628
            $meetingInfoGet->settings->approval_type = MeetingSettings::APPROVAL_TYPE_AUTOMATICALLY_APPROVE;
1629
        }
1630
1631
        return $this->createMeetingFromMeeting(
1632
            (new Meeting())
1633
                ->setMeetingInfoGet($meetingInfoGet)
1634
                ->setUser($user)
1635
                ->setCourse($course)
1636
                ->setGroup($group)
1637
                ->setSession($session)
1638
                ->setAccountEmail($accountEmail)
1639
        );
1640
    }
1641
1642
    /**
1643
     * @throws Exception
1644
     */
1645
    private function createScheduleWebinar(
1646
        ?User $user,
1647
        ?Course $course,
1648
        ?CGroupInfo $group,
1649
        ?Session $session,
1650
        DateTime $startTime,
1651
        $duration,
1652
        $topic,
1653
        $agenda,
1654
        $password,
1655
        string $accountEmail = null
1656
    ): Webinar {
1657
        $webinarSchema = WebinarSchema::fromTopicAndType($topic);
1658
        $webinarSchema->duration = $duration;
1659
        $webinarSchema->start_time = $startTime->format(DATE_ATOM);
1660
        $webinarSchema->agenda = $agenda;
1661
        $webinarSchema->password = $password;
1662
1663
        if ('true' === $this->get('enableParticipantRegistration')) {
1664
            $webinarSchema->settings->approval_type = WebinarSettings::APPROVAL_TYPE_AUTOMATICALLY_APPROVE;
1665
        }
1666
1667
        $webinar = (new Webinar())
1668
            ->setUser($user)
1669
            ->setCourse($course)
1670
            ->setGroup($group)
1671
            ->setSession($session)
1672
            ->setAccountEmail($accountEmail)
1673
        ;
1674
1675
        return $this->createWebinarFromSchema($webinar, $webinarSchema);
1676
    }
1677
1678
    /**
1679
     * Registers all the course users to a course meeting.
1680
     *
1681
     * @param Meeting $meeting
1682
     *
1683
     * @throws OptimisticLockException
1684
     */
1685
    private function registerAllCourseUsers($meeting)
1686
    {
1687
        $this->registerUsers($meeting, $meeting->getRegistrableUsers());
1688
    }
1689
}
1690