Passed
Push — 1.11.x ( 5dd04e...e08fc6 )
by Julito
21:38
created

ZoomPlugin::scheduleMeeting()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 2
eloc 15
c 3
b 0
f 0
nc 2
nop 9
dl 0
loc 28
rs 9.7666

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

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