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

ZoomPlugin::getEditConferenceForm()   C

Complexity

Conditions 10
Paths 184

Size

Total Lines 78
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 47
c 0
b 0
f 0
dl 0
loc 78
rs 6.7296
cc 10
nc 184
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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