Passed
Push — 1.11.x ( 8c4a90...9feff2 )
by Julito
13:24
created

ZoomPlugin::scheduleMeeting()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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