Passed
Push — master ( c22b6a...5f79f5 )
by Julito
11:21
created

ZoomPlugin::handleException()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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