Passed
Pull Request — master (#6553)
by
unknown
08:17
created

Bbb::joinMeeting()   F

Complexity

Conditions 15
Paths 298

Size

Total Lines 85
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 55
nc 298
nop 1
dl 0
loc 85
rs 3.8583
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\ConferenceActivity;
6
use Chamilo\CoreBundle\Entity\ConferenceMeeting;
7
use Chamilo\CoreBundle\Entity\ConferenceRecording;
8
use Chamilo\CoreBundle\Enums\ActionIcon;
9
use Chamilo\CoreBundle\Enums\ObjectIcon;
10
use Chamilo\CoreBundle\Enums\StateIcon;
11
use Chamilo\CoreBundle\Repository\ConferenceActivityRepository;
12
use Chamilo\CoreBundle\Repository\ConferenceMeetingRepository;
13
use Chamilo\CoreBundle\Repository\ConferenceRecordingRepository;
14
use Chamilo\UserBundle\Entity\User;
15
16
/**
17
 * Class Bbb
18
 * This script initiates a video conference session, calling the BigBlueButton
19
 * API BigBlueButton-Chamilo connector class
20
 */
21
class Bbb
22
{
23
    public $url;
24
    public $salt;
25
    public $api;
26
    public $userCompleteName = '';
27
    public $protocol = 'http://';
28
    public $debug = false;
29
    public $logoutUrl = '';
30
    public $pluginEnabled = false;
31
    public $enableGlobalConference = false;
32
    public $enableGlobalConferencePerUser = false;
33
    public $isGlobalConference = false;
34
    public $groupSupport = false;
35
    public $userSupport = false;
36
    public $accessUrl = 1;
37
    public $userId = 0;
38
    public $plugin;
39
    private $courseCode;
40
    private $courseId;
41
    private $sessionId;
42
    private $groupId;
43
    private $maxUsersLimit;
44
45
    /**
46
     * Constructor (generates a connection to the API and the Chamilo settings
47
     * required for the connection to the video conference server)
48
     *
49
     * @param string $host
50
     * @param string $salt
51
     * @param bool   $isGlobalConference
52
     * @param int    $isGlobalPerUser
53
     */
54
    public function __construct(
55
        $host = '',
56
        $salt = '',
57
        $isGlobalConference = false,
58
        $isGlobalPerUser = 0
59
    ) {
60
        $this->courseCode = api_get_course_id();
61
        $this->courseId = api_get_course_int_id();
62
        $this->sessionId = api_get_session_id();
63
        $this->groupId = api_get_group_id();
64
65
        // Initialize video server settings from global settings
66
        $this->plugin = BbbPlugin::create();
67
        $bbbPluginEnabled = $this->plugin->get('tool_enable');
68
69
        $bbb_host = !empty($host) ? $host : $this->plugin->get('host');
70
        $bbb_salt = !empty($salt) ? $salt : $this->plugin->get('salt');
71
72
        $this->table = Database::get_main_table('conference_meeting');
73
        $this->enableGlobalConference = $this->plugin->get('enable_global_conference') === 'true';
74
        $this->isGlobalConference = (bool) $isGlobalConference;
75
76
        $columns = Database::listTableColumns($this->table);
77
        $this->groupSupport = isset($columns['group_id']) ? true : false;
78
        $this->userSupport = isset($columns['user_id']) ? true : false;
79
        $this->accessUrl = api_get_current_access_url_id();
80
81
        $this->enableGlobalConferencePerUser = false;
82
        if ($this->userSupport && !empty($isGlobalPerUser)) {
83
            $this->enableGlobalConferencePerUser = $this->plugin->get('enable_global_conference_per_user') === 'true';
84
            $this->userId = $isGlobalPerUser;
85
        }
86
87
        if ($this->groupSupport) {
88
            // Plugin check
89
            $this->groupSupport = $this->plugin->get('enable_conference_in_course_groups') === 'true' ? true : false;
90
            if ($this->groupSupport) {
91
                // Platform check
92
                $bbbSetting = api_get_plugin_setting('bbb', 'enable_conference_in_course_groups');
93
                $bbbSetting = isset($bbbSetting['bbb']) ? $bbbSetting['bbb'] === 'true' : false;
94
95
                if ($bbbSetting) {
96
                    // Course check
97
                    $courseInfo = api_get_course_info();
98
                    if ($courseInfo) {
99
                        $this->groupSupport = api_get_course_plugin_setting(
100
                                'bbb',
101
                                'bbb_enable_conference_in_groups',
102
                                $courseInfo
103
                            ) === '1';
104
                    }
105
                }
106
            }
107
        }
108
        $this->maxUsersLimit = $this->plugin->get('max_users_limit');
109
110
        if ($bbbPluginEnabled === 'true') {
111
            $userInfo = api_get_user_info();
112
            if (empty($userInfo) && !empty($isGlobalPerUser)) {
113
                // If we are following a link to a global "per user" conference
114
                // then generate a random guest name to join the conference
115
                // because there is no part of the process where we give a name
116
                //$this->userCompleteName = 'Guest'.rand(1000, 9999);
117
            } else {
118
                $this->userCompleteName = $userInfo['complete_name'];
119
            }
120
121
            if (api_is_anonymous()) {
122
                $this->userCompleteName = get_lang('Guest').'_'.rand(1000, 9999);
123
            }
124
125
            $this->salt = $bbb_salt;
126
            if (!empty($bbb_host)) {
127
                if (substr($bbb_host, -1, 1) !== '/') {
128
                    $bbb_host .= '/';
129
                }
130
                $this->url = $bbb_host;
131
                if (!preg_match('#/bigbluebutton/$#', $bbb_host)) {
132
                    $this->url = $bbb_host.'bigbluebutton/';
133
                }
134
            }
135
            $info = parse_url($bbb_host);
136
137
            if (isset($info['scheme'])) {
138
                $this->protocol = $info['scheme'].'://';
139
                $this->url = str_replace($this->protocol, '', $this->url);
140
                $urlWithProtocol = $bbb_host;
141
            } else {
142
                // We assume it's an http, if user wants to use https, the host *must* include the protocol.
143
                $this->protocol = 'http://';
144
                $urlWithProtocol = $this->protocol.$bbb_host;
145
            }
146
147
            // Setting BBB api
148
            define('CONFIG_SECURITY_SALT', $this->salt);
149
            define('CONFIG_SERVER_URL_WITH_PROTOCOL', $urlWithProtocol);
150
            define('CONFIG_SERVER_BASE_URL', $this->url);
151
            define('CONFIG_SERVER_PROTOCOL', $this->protocol);
152
153
            $this->api = new BigBlueButtonBN(CONFIG_SERVER_URL_WITH_PROTOCOL, CONFIG_SECURITY_SALT);
154
            $this->pluginEnabled = true;
155
            $this->logoutUrl = $this->getListingUrl();
156
        }
157
    }
158
159
    /**
160
     * @param int $courseId  Optional. Course ID.
161
     * @param int $sessionId Optional. Session ID.
162
     * @param int $groupId   Optional. Group ID.
163
     *
164
     * @return string
165
     */
166
    public function getListingUrl($courseId = 0, $sessionId = 0, $groupId = 0)
167
    {
168
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'
169
            .$this->getUrlParams($courseId, $sessionId, $groupId);
170
    }
171
172
    /**
173
     * @param int $courseId  Optional. Course ID.
174
     * @param int $sessionId Optional. Session ID.
175
     * @param int $groupId   Optional. Group ID.
176
     *
177
     * @return string
178
     */
179
    public function getUrlParams($courseId = 0, $sessionId = 0, $groupId = 0)
180
    {
181
        if (empty($this->courseId) && !$courseId) {
182
            if ($this->isGlobalConferencePerUserEnabled()) {
183
                return 'global=1&user_id='.$this->userId;
184
            }
185
186
            if ($this->isGlobalConference()) {
187
                return 'global=1';
188
            }
189
190
            return '';
191
        }
192
193
        $defaultCourseId = (int) $this->courseId;
194
        if (!empty($courseId)) {
195
            $defaultCourseId = (int) $courseId;
196
        }
197
198
        return http_build_query(
199
            [
200
                'cid' => $defaultCourseId,
201
                'sid' => (int) $sessionId ?: $this->sessionId,
202
                'gid' => (int) $groupId ?: $this->groupId,
203
            ]
204
        );
205
    }
206
207
    /**
208
     * @return bool
209
     */
210
    public function isGlobalConferencePerUserEnabled()
211
    {
212
        return $this->enableGlobalConferencePerUser;
213
    }
214
215
    /**
216
     * @return bool
217
     */
218
    public function isGlobalConference()
219
    {
220
        if ($this->isGlobalConferenceEnabled() === false) {
221
            return false;
222
        }
223
224
        return (bool) $this->isGlobalConference;
225
    }
226
227
    /**
228
     * @return bool
229
     */
230
    public function isGlobalConferenceEnabled()
231
    {
232
        return $this->enableGlobalConference;
233
    }
234
235
    /**
236
     * @param array $userInfo
237
     *
238
     * @return bool
239
     */
240
    public static function showGlobalConferenceLink($userInfo)
241
    {
242
        if (empty($userInfo)) {
243
            return false;
244
        }
245
        $setting = api_get_plugin_setting('bbb', 'enable_global_conference');
246
        $settingLink = api_get_plugin_setting('bbb', 'enable_global_conference_link');
247
        if ($setting === 'true' && $settingLink === 'true') {
248
            //$content = Display::url(get_lang('LaunchVideoConferenceRoom'), $url);
249
            $allowedRoles = api_get_plugin_setting(
250
                'bbb',
251
                'global_conference_allow_roles'
252
            );
253
254
            if (api_is_platform_admin()) {
255
                $userInfo['status'] = PLATFORM_ADMIN;
256
            }
257
258
            $showGlobalLink = true;
259
            if (!empty($allowedRoles)) {
260
                if (!in_array($userInfo['status'], $allowedRoles)) {
261
                    $showGlobalLink = false;
262
                }
263
            }
264
265
            return $showGlobalLink;
266
        }
267
    }
268
269
    /**
270
     * Gets the global limit of users in a video-conference room.
271
     * This value can be overridden by course-specific values
272
     * @return  int Maximum number of users set globally
273
     */
274
    public function getMaxUsersLimit()
275
    {
276
        $limit = $this->maxUsersLimit;
277
        if ($limit <= 0) {
278
            $limit = 0;
279
        }
280
        $courseLimit = 0;
281
        $sessionLimit = 0;
282
283
        // Check course extra field
284
        if (!empty($this->courseId)) {
285
            $extraField = new ExtraField('course');
286
            $fieldIdList = $extraField->get_all(
287
                array('variable = ?' => 'plugin_bbb_course_users_limit')
288
            );
289
290
            if (!empty($fieldIdList)) {
291
                $fieldId = $fieldIdList[0]['id'] ?? null;
292
                if ($fieldId) {
293
                    $extraValue = new ExtraFieldValue('course');
294
                    $value = $extraValue->get_values_by_handler_and_field_id($this->courseId, $fieldId);
295
                    if (!empty($value['value'])) {
296
                        $courseLimit = (int) $value['value'];
297
                    }
298
                }
299
            }
300
        }
301
302
        // Check session extra field
303
        if (!empty($this->sessionId)) {
304
            $extraField = new ExtraField('session');
305
            $fieldIdList = $extraField->get_all(
306
                array('variable = ?' => 'plugin_bbb_session_users_limit')
307
            );
308
309
            if (!empty($fieldIdList)) {
310
                $fieldId = $fieldIdList[0]['id'] ?? null;
311
                if ($fieldId) {
312
                    $extraValue = new ExtraFieldValue('session');
313
                    $value = $extraValue->get_values_by_handler_and_field_id($this->sessionId, $fieldId);
314
                    if (!empty($value['value'])) {
315
                        $sessionLimit = (int) $value['value'];
316
                    }
317
                }
318
            }
319
        }
320
321
        if (!empty($sessionLimit)) {
322
            return $sessionLimit;
323
        } elseif (!empty($courseLimit)) {
324
            return $courseLimit;
325
        }
326
327
        return (int) $limit;
328
    }
329
330
    /**
331
     * Sets the global limit of users in a video-conference room.
332
     *
333
     * @param int Maximum number of users (globally)
334
     */
335
    public function setMaxUsersLimit($max)
336
    {
337
        if ($max < 0) {
338
            $max = 0;
339
        }
340
        $this->maxUsersLimit = (int) $max;
341
    }
342
343
    /**
344
     * See this file in you BBB to set up default values
345
     *
346
     * @param array $params Array of parameters that will be completed if not containing all expected variables
347
     *
348
     * /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
349
     *
350
     * More record information:
351
     * http://code.google.com/p/bigbluebutton/wiki/RecordPlaybackSpecification
352
     *
353
     * Default maximum number of users a meeting can have.
354
     * Doesn't get enforced yet but is the default value when the create
355
     * API doesn't pass a value.
356
     * defaultMaxUsers=20
357
     *
358
     * Default duration of the meeting in minutes.
359
     * Current default is 0 (meeting doesn't end).
360
     * defaultMeetingDuration=0
361
     *
362
     * Remove the meeting from memory when the end API is called.
363
     * This allows 3rd-party apps to recycle the meeting right-away
364
     * instead of waiting for the meeting to expire (see below).
365
     * removeMeetingWhenEnded=false
366
     *
367
     * The number of minutes before the system removes the meeting from memory.
368
     * defaultMeetingExpireDuration=1
369
     *
370
     * The number of minutes the system waits when a meeting is created and when
371
     * a user joins. If after this period, a user hasn't joined, the meeting is
372
     * removed from memory.
373
     * defaultMeetingCreateJoinDuration=5
374
     *
375
     * @return mixed
376
     */
377
    public function createMeeting($params)
378
    {
379
        $params['c_id'] = api_get_course_int_id();
380
        $params['session_id'] = api_get_session_id();
381
382
        if ($this->hasGroupSupport()) {
383
            $params['group_id'] = api_get_group_id();
384
        }
385
386
        if ($this->isGlobalConferencePerUserEnabled() && !empty($this->userId)) {
387
            $params['user_id'] = (int) $this->userId;
388
        }
389
390
        $params['attendee_pw'] = $params['attendee_pw'] ?? $this->getUserMeetingPassword();
391
        $attendeePassword = $params['attendee_pw'];
392
        $params['moderator_pw'] = $params['moderator_pw'] ?? $this->getModMeetingPassword();
393
        $moderatorPassword = $params['moderator_pw'];
394
395
        $params['record'] = api_get_course_plugin_setting('bbb', 'big_blue_button_record_and_store') == 1 ? 1 : 0;
396
        $max = api_get_course_plugin_setting('bbb', 'big_blue_button_max_students_allowed');
397
        $max = isset($max) ? $max : -1;
398
399
        $params['status'] = 1;
400
        // Generate a pseudo-global-unique-id to avoid clash of conferences on
401
        // the same BBB server with several Chamilo portals
402
        $params['remote_id'] = uniqid(true, true);
403
        // Each simultaneous conference room needs to have a different
404
        // voice_bridge composed of a 5 digits number, so generating a random one
405
        $params['voice_bridge'] = rand(10000, 99999);
406
        $params['created_at'] = api_get_utc_datetime();
407
        $params['access_url'] = $this->accessUrl;
408
409
        $em = Database::getManager();
410
        $meeting = new ConferenceMeeting();
411
412
        $meeting
413
            ->setCourse(api_get_course_entity($params['c_id']))
414
            ->setSession(api_get_session_entity($params['session_id']))
415
            ->setAccessUrl(api_get_url_entity($params['access_url']))
416
            ->setGroup($this->hasGroupSupport() ? api_get_group_entity($params['group_id']) : null)
417
            ->setUser($this->isGlobalConferencePerUserEnabled() ? api_get_user_entity($params['user_id']) : null)
418
            ->setRemoteId($params['remote_id'])
419
            ->setTitle($params['meeting_name'] ?? $this->getCurrentVideoConferenceName())
420
            ->setAttendeePw($attendeePassword)
421
            ->setModeratorPw($moderatorPassword)
422
            ->setRecord((bool) $params['record'])
423
            ->setStatus($params['status'])
424
            ->setVoiceBridge($params['voice_bridge'])
425
            ->setWelcomeMsg($params['welcome_msg'] ?? null)
426
            ->setVisibility(1)
427
            ->setHasVideoM4v(false)
428
            ->setServiceProvider('bbb');
429
430
        $em->persist($meeting);
431
        $em->flush();
432
433
        $id = $meeting->getId();
434
435
        Event::addEvent(
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

435
        Event::/** @scrutinizer ignore-call */ 
436
               addEvent(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
436
            'bbb_create_meeting',
437
            'meeting_id',
438
            $id,
439
            null,
440
            api_get_user_id(),
441
            api_get_course_int_id(),
442
            api_get_session_id()
443
        );
444
445
        $meetingName = $meeting->getTitle();
446
        $record = $meeting->isRecord() ? 'true' : 'false';
447
        $duration = 300;
448
        $meetingDuration = (int) $this->plugin->get('meeting_duration');
449
        if (!empty($meetingDuration)) {
450
            $duration = $meetingDuration;
451
        }
452
453
        $bbbParams = [
454
            'meetingId' => $meeting->getRemoteId(),
455
            'meetingName' => $meetingName,
456
            'attendeePw' => $attendeePassword,
457
            'moderatorPw' => $moderatorPassword,
458
            'welcomeMsg' => $meeting->getWelcomeMsg(),
459
            'dialNumber' => '',
460
            'voiceBridge' => $meeting->getVoiceBridge(),
461
            'webVoice' => '',
462
            'logoutUrl' => $this->logoutUrl . '&action=logout&remote_id=' . $meeting->getRemoteId(),
463
            'maxParticipants' => $max,
464
            'record' => $record,
465
            'duration' => $duration,
466
        ];
467
468
        $status = false;
469
        $finalMeetingUrl = null;
470
471
        while ($status === false) {
472
            $result = $this->api->createMeetingWithXmlResponseArray($bbbParams);
473
            if (isset($result) && strval($result['returncode']) === 'SUCCESS') {
474
                if ($this->plugin->get('allow_regenerate_recording') === 'true') {
475
                    $meeting->setInternalMeetingId($result['internalMeetingID']);
476
                    $em->flush();
477
                }
478
479
                return $this->joinMeeting($meetingName, true);
480
            }
481
        }
482
483
        return false;
484
    }
485
486
    /**
487
     * @return bool
488
     */
489
    public function hasGroupSupport()
490
    {
491
        return $this->groupSupport;
492
    }
493
494
    /**
495
     * Gets the password for a specific meeting for the current user
496
     *
497
     * @param string $courseCode
498
     *
499
     * @return string A moderator password if user is teacher, or the course code otherwise
500
     *
501
     */
502
    public function getUserMeetingPassword($courseCode = null)
503
    {
504
        if ($this->isGlobalConferencePerUserEnabled()) {
505
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
506
        }
507
508
        if ($this->isGlobalConference()) {
509
            return 'url_'.api_get_current_access_url_id();
510
        }
511
512
        return empty($courseCode) ? api_get_course_id() : $courseCode;
513
    }
514
515
    /**
516
     * Generated a moderator password for the meeting.
517
     *
518
     * @param string $courseCode
519
     *
520
     * @return string A password for the moderation of the videoconference
521
     */
522
    public function getModMeetingPassword($courseCode = null)
523
    {
524
        if ($this->isGlobalConferencePerUserEnabled()) {
525
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id().'_mod';
526
        }
527
528
        if ($this->isGlobalConference()) {
529
            return 'url_'.api_get_current_access_url_id().'_mod';
530
        }
531
532
        $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
533
534
        return $courseCode.'mod';
535
    }
536
537
    /**
538
     * @return string
539
     */
540
    public function getCurrentVideoConferenceName()
541
    {
542
        if ($this->isGlobalConferencePerUserEnabled()) {
543
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
544
        }
545
546
        if ($this->isGlobalConference()) {
547
            return 'url_'.api_get_current_access_url_id();
548
        }
549
550
        if ($this->hasGroupSupport()) {
551
            return api_get_course_id().'-'.api_get_session_id().'-'.api_get_group_id();
552
        }
553
554
        return api_get_course_id().'-'.api_get_session_id();
555
    }
556
557
    /**
558
     * Returns a meeting "join" URL
559
     *
560
     * @param string The name of the meeting (usually the course code)
561
     *
562
     * @return mixed The URL to join the meeting, or false on error
563
     * @todo implement moderator pass
564
     * @assert ('') === false
565
     * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false
566
     */
567
    public function joinMeeting($meetingName)
568
    {
569
        if ($this->debug) {
570
            error_log("joinMeeting: $meetingName");
571
        }
572
573
        if (empty($meetingName)) {
574
            return false;
575
        }
576
577
        $manager = $this->isConferenceManager();
578
        $pass = $manager ? $this->getModMeetingPassword() : $this->getUserMeetingPassword();
579
580
        $meetingData = Database::getManager()
581
            ->getRepository(ConferenceMeeting::class)
582
            ->findOneBy([
583
                'title' => $meetingName,
584
                'status' => 1,
585
                'accessUrl' => api_get_url_entity($this->accessUrl),
586
            ]);
587
588
        if (empty($meetingData)) {
589
            if ($this->debug) {
590
                error_log("meeting does not exist: $meetingName");
591
            }
592
593
            return false;
594
        }
595
596
        $params = [
597
            'meetingId' => $meetingData->getRemoteId(),
598
            'password' => $this->getModMeetingPassword(),
599
        ];
600
601
        $meetingInfoExists = false;
602
        $meetingIsRunningInfo = $this->getMeetingInfo($params);
603
        if ($this->debug) {
604
            error_log('Searching meeting with params:');
605
            error_log(print_r($params, 1));
606
            error_log('Result:');
607
            error_log(print_r($meetingIsRunningInfo, 1));
608
        }
609
610
        if ($meetingIsRunningInfo === false) {
611
            $params['meetingId'] = $meetingData->getId();
612
            $meetingIsRunningInfo = $this->getMeetingInfo($params);
613
            if ($this->debug) {
614
                error_log('Searching meetingId with params:');
615
                error_log(print_r($params, 1));
616
                error_log('Result:');
617
                error_log(print_r($meetingIsRunningInfo, 1));
618
            }
619
        }
620
621
        if (
622
            strval($meetingIsRunningInfo['returncode']) === 'SUCCESS' &&
623
            isset($meetingIsRunningInfo['meetingName']) &&
624
            !empty($meetingIsRunningInfo['meetingName'])
625
        ) {
626
            $meetingInfoExists = true;
627
        }
628
629
        if ($this->debug) {
630
            error_log("meeting is running: " . intval($meetingInfoExists));
631
        }
632
633
        if ($meetingInfoExists) {
634
            $joinParams = [
635
                'meetingId' => $meetingData->getRemoteId(),
636
                'username' => $this->userCompleteName,
637
                'password' => $pass,
638
                'userID' => api_get_user_id(),
639
                'moderatorPw' => $this->getModMeetingPassword(),
640
                'userID'      => api_get_user_id(),
641
                'webVoiceConf' => '',
642
            ];
643
            $url = $this->api->getJoinMeetingURL($joinParams);
644
            if (preg_match('#^https?://#i', $url)) {
645
                return $url;
646
            }
647
648
            return $this->protocol . $url;
649
        }
650
651
        return false;
652
    }
653
654
655
    /**
656
     * Checks whether a user is teacher in the current course
657
     * @return bool True if the user can be considered a teacher in this course, false otherwise
658
     */
659
    public function isConferenceManager()
660
    {
661
        if (api_is_coach() || api_is_platform_admin(false, true)) {
662
            return true;
663
        }
664
665
        if ($this->isGlobalConferencePerUserEnabled()) {
666
            $currentUserId = api_get_user_id();
667
            if ($this->userId === $currentUserId) {
668
                return true;
669
            } else {
670
                return false;
671
            }
672
        }
673
674
        $courseInfo = api_get_course_info();
675
        $groupId = api_get_group_id();
676
        if (!empty($groupId) && !empty($courseInfo)) {
677
            $groupEnabled = api_get_course_plugin_setting('bbb', 'bbb_enable_conference_in_groups') === '1';
678
            if ($groupEnabled) {
679
                $studentCanStartConference = api_get_course_plugin_setting(
680
                        'bbb',
681
                        'big_blue_button_students_start_conference_in_groups'
682
                    ) === '1';
683
684
                if ($studentCanStartConference) {
685
                    $isSubscribed = GroupManager::is_user_in_group(
0 ignored issues
show
Bug introduced by
The method is_user_in_group() does not exist on GroupManager. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

685
                    /** @scrutinizer ignore-call */ 
686
                    $isSubscribed = GroupManager::is_user_in_group(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
686
                        api_get_user_id(),
687
                        GroupManager::get_group_properties($groupId)
688
                    );
689
                    if ($isSubscribed) {
690
                        return true;
691
                    }
692
                }
693
            }
694
        }
695
696
        if (!empty($courseInfo)) {
697
            return api_is_course_admin();
698
        }
699
700
        return false;
701
    }
702
703
    /**
704
     * Get information about the given meeting
705
     *
706
     * @param array ...?
707
     *
708
     * @return mixed Array of information on success, false on error
709
     * @assert (array()) === false
710
     */
711
    public function getMeetingInfo($params)
712
    {
713
        try {
714
            $result = $this->api->getMeetingInfoWithXmlResponseArray($params);
715
            if ($result == null) {
716
                if ($this->debug) {
717
                    error_log("Failed to get any response. Maybe we can't contact the BBB server.");
718
                }
719
            }
720
721
            return $result;
722
        } catch (Exception $e) {
723
            if ($this->debug) {
724
                error_log('Caught exception: ', $e->getMessage(), "\n");
725
            }
726
        }
727
728
        return false;
729
    }
730
731
732
    /**
733
     * @param int $meetingId
734
     * @param int $userId
735
     *
736
     * @return array
737
     */
738
    public function getMeetingParticipantInfo($meetingId, $userId): array
739
    {
740
        $em = Database::getManager();
741
        /** @var ConferenceActivityRepository $repo */
742
        $repo = $em->getRepository(ConferenceActivity::class);
743
744
        $activity = $repo->createQueryBuilder('a')
745
            ->join('a.meeting', 'm')
746
            ->join('a.participant', 'u')
747
            ->where('m.id = :meetingId')
748
            ->andWhere('u.id = :userId')
749
            ->setParameter('meetingId', $meetingId)
750
            ->setParameter('userId', $userId)
751
            ->setMaxResults(1)
752
            ->getQuery()
753
            ->getOneOrNullResult();
754
755
        if (!$activity) {
756
            return [];
757
        }
758
759
        return [
760
            'id' => $activity->getId(),
761
            'meeting_id' => $activity->getMeeting()?->getId(),
762
            'participant_id' => $activity->getParticipant()?->getId(),
763
            'in_at' => $activity->getInAt()?->format('Y-m-d H:i:s'),
764
            'out_at' => $activity->getOutAt()?->format('Y-m-d H:i:s'),
765
            'close' => $activity->isClose(),
766
            'type' => $activity->getType(),
767
            'event' => $activity->getEvent(),
768
            'activity_data' => $activity->getActivityData(),
769
            'signature_file' => $activity->getSignatureFile(),
770
            'signed_at' => $activity->getSignedAt()?->format('Y-m-d H:i:s'),
771
        ];
772
    }
773
774
    /**
775
     * Save a participant in a meeting room
776
     *
777
     * @param int $meetingId
778
     * @param int $participantId
779
     *
780
     * @return false|int The last inserted ID. Otherwise return false
781
     */
782
    public function saveParticipant(int $meetingId, int $participantId): false|int
783
    {
784
        $em = Database::getManager();
785
786
        /** @var ConferenceActivityRepository $repo */
787
        $repo = $em->getRepository(ConferenceActivity::class);
788
789
        $meeting = $em->getRepository(ConferenceMeeting::class)->find($meetingId);
790
        $user = api_get_user_entity($participantId);
791
792
        if (!$meeting || !$user) {
793
            return false;
794
        }
795
796
        $existing = $repo->createQueryBuilder('a')
797
            ->where('a.meeting = :meeting')
798
            ->andWhere('a.participant = :participant')
799
            ->andWhere('a.close = :open')
800
            ->setParameter('meeting', $meeting)
801
            ->setParameter('participant', $user)
802
            ->setParameter('open', \BbbPlugin::ROOM_OPEN)
803
            ->getQuery()
804
            ->getResult();
805
806
        foreach ($existing as $activity) {
807
            if ($activity->getInAt() != $activity->getOutAt()) {
808
                $activity->setClose(\BbbPlugin::ROOM_CLOSE);
809
            } else {
810
                $activity->setOutAt(new \DateTime());
811
                $activity->setClose(\BbbPlugin::ROOM_CLOSE);
812
            }
813
            $em->persist($activity);
814
        }
815
816
        $newActivity = new ConferenceActivity();
817
        $newActivity->setMeeting($meeting);
818
        $newActivity->setParticipant($user);
819
        $newActivity->setInAt(new \DateTime());
820
        $newActivity->setOutAt(new \DateTime());
821
        $newActivity->setClose(\BbbPlugin::ROOM_OPEN);
822
823
        $em->persist($newActivity);
824
        $em->flush();
825
826
        return $newActivity->getId();
827
    }
828
829
    /**
830
     * Tells whether the given meeting exists and is running
831
     * (using course code as name)
832
     *
833
     * @param string $meetingName Meeting name (usually the course code)
834
     *
835
     * @return bool True if meeting exists, false otherwise
836
     * @assert ('') === false
837
     * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false
838
     */
839
    public function meetingExists($meetingName)
840
    {
841
        $meetingData = $this->getMeetingByName($meetingName);
842
843
        return !empty($meetingData);
844
    }
845
846
    /**
847
     * @param string $meetingName
848
     *
849
     * @return array
850
     */
851
    public function getMeetingByName($meetingName)
852
    {
853
        if (empty($meetingName)) {
854
            return [];
855
        }
856
857
        $courseEntity = api_get_course_entity();
858
        $sessionEntity = api_get_session_entity();
859
        $accessUrlEntity = api_get_url_entity($this->accessUrl);
860
861
        $criteria = [
862
            'course' => $courseEntity,
863
            'session' => $sessionEntity,
864
            'title' => $meetingName,
865
            'status' => 1,
866
            'accessUrl' => $accessUrlEntity,
867
        ];
868
869
        if ($this->hasGroupSupport()) {
870
            $groupEntity = api_get_group_entity(api_get_group_id());
871
            $criteria['group'] = $groupEntity;
872
        }
873
874
        $meeting = Database::getManager()
875
            ->getRepository(ConferenceMeeting::class)
876
            ->findOneBy($criteria);
877
878
        if ($this->debug) {
879
            error_log('meeting_exists '.print_r($meeting ? ['id' => $meeting->getId()] : [], 1));
0 ignored issues
show
Bug introduced by
Are you sure print_r($meeting ? array...>getId()) : array(), 1) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

879
            error_log('meeting_exists './** @scrutinizer ignore-type */ print_r($meeting ? ['id' => $meeting->getId()] : [], 1));
Loading history...
880
        }
881
882
        if (!$meeting) {
883
            return [];
884
        }
885
886
        return [
887
            'id' => $meeting->getId(),
888
            'c_id' => $meeting->getCourse()?->getId(),
889
            'session_id' => $meeting->getSession()?->getId(),
890
            'meeting_name' => $meeting->getTitle(),
891
            'status' => $meeting->getStatus(),
892
            'access_url' => $meeting->getAccessUrl()?->getId(),
893
            'group_id' => $meeting->getGroup()?->getIid(),
894
            'remote_id' => $meeting->getRemoteId(),
895
            'moderator_pw' => $meeting->getModeratorPw(),
896
            'attendee_pw' => $meeting->getAttendeePw(),
897
            'created_at' => $meeting->getCreatedAt()->format('Y-m-d H:i:s'),
898
            'closed_at' => $meeting->getClosedAt()?->format('Y-m-d H:i:s'),
899
            'visibility' => $meeting->getVisibility(),
900
            'video_url' => $meeting->getVideoUrl(),
901
            'has_video_m4v' => $meeting->isHasVideoM4v(),
902
            'record' => $meeting->isRecord(),
903
            'internal_meeting_id' => $meeting->getInternalMeetingId(),
904
        ];
905
    }
906
907
    /**
908
     * Gets a list from the database of all meetings attached to a course with the given status
909
     * @param int $courseId
910
     * @param int $sessionId
911
     * @param int $status 0 for closed meetings, 1 for open meetings
912
     *
913
     * @return array
914
     */
915
    public function getAllMeetingsInCourse($courseId, $sessionId, $status)
916
    {
917
        $em = Database::getManager();
918
        $courseEntity = api_get_course_entity($courseId);
919
        $sessionEntity = api_get_session_entity($sessionId);
920
921
        $meetings = $em->getRepository(ConferenceMeeting::class)->findBy([
922
            'course' => $courseEntity,
923
            'session' => $sessionEntity,
924
            'status' => $status,
925
        ]);
926
927
        $results = [];
928
        foreach ($meetings as $meeting) {
929
            $results[] = [
930
                'id' => $meeting->getId(),
931
                'c_id' => $meeting->getCourse()?->getId(),
932
                'session_id' => $meeting->getSession()?->getId(),
933
                'meeting_name' => $meeting->getTitle(),
934
                'status' => $meeting->getStatus(),
935
                'access_url' => $meeting->getAccessUrl()?->getId(),
936
                'group_id' => $meeting->getGroup()?->getIid(),
937
                'remote_id' => $meeting->getRemoteId(),
938
                'moderator_pw' => $meeting->getModeratorPw(),
939
                'attendee_pw' => $meeting->getAttendeePw(),
940
                'created_at' => $meeting->getCreatedAt()->format('Y-m-d H:i:s'),
941
                'closed_at' => $meeting->getClosedAt()?->format('Y-m-d H:i:s'),
942
                'visibility' => $meeting->getVisibility(),
943
                'video_url' => $meeting->getVideoUrl(),
944
                'has_video_m4v' => $meeting->isHasVideoM4v(),
945
                'record' => $meeting->isRecord(),
946
                'internal_meeting_id' => $meeting->getInternalMeetingId(),
947
            ];
948
        }
949
950
        return $results;
951
    }
952
953
    /**
954
     * Gets all the course meetings saved in the plugin_bbb_meeting table and
955
     * generate actionable links (join/close/delete/etc)
956
     *
957
     * @param int   $courseId
958
     * @param int   $sessionId
959
     * @param int   $groupId
960
     * @param bool  $isAdminReport Optional. Set to true then the report is for admins
961
     * @param array $dateRange     Optional
962
     *
963
     * @return array Array of current open meeting rooms
964
     * @throws Exception
965
     */
966
    public function getMeetings(
967
        $courseId = 0,
968
        $sessionId = 0,
969
        $groupId = 0,
970
        $isAdminReport = false,
971
        $dateRange = []
972
    ) {
973
        $em = Database::getManager();
974
        $repo = $em->getRepository(ConferenceMeeting::class);
975
        $manager = $this->isConferenceManager();
976
        $isGlobal = $this->isGlobalConference();
977
        $meetings = [];
978
979
        if (!empty($dateRange)) {
980
            $dateStart = (new \DateTime($dateRange['search_meeting_start']))->setTime(0, 0, 0);
981
            $dateEnd = (new \DateTime($dateRange['search_meeting_end']))->setTime(23, 59, 59);
982
            $meetings = $repo->findByDateRange($dateStart, $dateEnd);
983
        } elseif ($this->isGlobalConference()) {
984
            $meetings = $repo->findBy([
985
                'course' => null,
986
                'user' => api_get_user_entity($this->userId),
987
                'accessUrl' => api_get_url_entity($this->accessUrl),
988
            ]);
989
        } elseif ($this->isGlobalConferencePerUserEnabled()) {
990
            $meetings = $repo->findBy([
991
                'course' => api_get_course_entity($courseId),
992
                'session' => api_get_session_entity($sessionId),
993
                'user' => api_get_user_entity($this->userId),
994
                'accessUrl' => api_get_url_entity($this->accessUrl),
995
            ]);
996
        } else {
997
            $criteria = [
998
                'course' => api_get_course_entity($courseId),
999
                'session' => api_get_session_entity($sessionId),
1000
                'accessUrl' => api_get_url_entity($this->accessUrl),
1001
            ];
1002
            if ($this->hasGroupSupport() && $groupId) {
1003
                $criteria['group'] = api_get_group_entity($groupId);
1004
            }
1005
            $meetings = $repo->findBy($criteria, ['createdAt' => 'ASC']);
1006
        }
1007
1008
        $result = [];
1009
        foreach ($meetings as $meeting) {
1010
            $meetingArray = $this->convertMeetingToArray($meeting);
1011
            $recordLink = $this->plugin->get_lang('NoRecording');
1012
            $meetingBBB = $this->getMeetingInfo([
1013
                'meetingId' => $meeting->getRemoteId(),
1014
                'password' => $manager ? $meeting->getModeratorPw() : $meeting->getAttendeePw(),
1015
            ]);
1016
1017
            if (!$meetingBBB && $meeting->getId()) {
1018
                $meetingBBB = $this->getMeetingInfo([
1019
                    'meetingId' => $meeting->getId(),
1020
                    'password' => $manager ? $meeting->getModeratorPw() : $meeting->getAttendeePw(),
1021
                ]);
1022
            }
1023
1024
            if (!$meeting->isVisible() && !$manager) {
1025
                continue;
1026
            }
1027
1028
            $meetingBBB['end_url'] = $this->endUrl(['id' => $meeting->getId()]);
1029
            if (isset($meetingBBB['returncode']) && (string) $meetingBBB['returncode'] === 'FAILED') {
1030
                if ($meeting->getStatus() === 1 && $manager) {
1031
                    $this->endMeeting($meeting->getId(), $meeting->getCourse()?->getCode());
1032
                }
1033
            } else {
1034
                $meetingBBB['add_to_calendar_url'] = $this->addToCalendarUrl($meetingArray);
1035
            }
1036
1037
            if ($meeting->isRecord()) {
1038
                $recordings = $this->api->getRecordingsWithXmlResponseArray(['meetingId' => $meeting->getRemoteId()]);
1039
                if (!empty($recordings) && (!isset($recordings['messageKey']) || $recordings['messageKey'] !== 'noRecordings')) {
1040
                    $record = end($recordings);
1041
                    if (isset($record['playbackFormatUrl'])) {
1042
                        $recordLink = Display::url(
1043
                            $this->plugin->get_lang('ViewRecord'),
1044
                            $record['playbackFormatUrl'],
1045
                            ['target' => '_blank', 'class' => 'btn btn--plain']
1046
                        );
1047
                        $this->updateMeetingVideoUrl($meeting->getId(), $record['playbackFormatUrl']);
1048
                    }
1049
                }
1050
            }
1051
1052
            $actionLinks = $this->getActionLinks($meetingArray, $record ?? [], $isGlobal, $isAdminReport);
1053
1054
            $item = array_merge($meetingArray, [
1055
                'go_url' => '',
1056
                'show_links' => $recordLink,
1057
                'action_links' => implode(PHP_EOL, $actionLinks),
1058
                'publish_url' => $this->publishUrl(['id' => $meeting->getId()]),
1059
                'unpublish_url' => $this->unPublishUrl(['id' => $meeting->getId()]),
1060
            ]);
1061
1062
            if ($meeting->getStatus() === 1) {
1063
                $joinParams = [
1064
                    'meetingId' => $meeting->getRemoteId(),
1065
                    'username' => $this->userCompleteName,
1066
                    'password' => $manager ? $meeting->getModeratorPw() : $meeting->getAttendeePw(),
1067
                    'createTime' => '',
1068
                    'userID' => '',
1069
                    'webVoiceConf' => '',
1070
                ];
1071
                $item['go_url'] = $this->protocol.$this->api->getJoinMeetingURL($joinParams);
1072
            }
1073
1074
            $result[] = array_merge($item, $meetingBBB);
1075
        }
1076
1077
        return $result;
1078
    }
1079
1080
    private function convertMeetingToArray(ConferenceMeeting $meeting): array
1081
    {
1082
        return [
1083
            'id' => $meeting->getId(),
1084
            'remote_id' => $meeting->getRemoteId(),
1085
            'internal_meeting_id' => $meeting->getInternalMeetingId(),
1086
            'meeting_name' => $meeting->getTitle(),
1087
            'status' => $meeting->getStatus(),
1088
            'visibility' => $meeting->getVisibility(),
1089
            'created_at' => $meeting->getCreatedAt() instanceof \DateTime ? $meeting->getCreatedAt()->format('Y-m-d H:i:s') : '',
0 ignored issues
show
introduced by
$meeting->getCreatedAt() is always a sub-type of DateTime.
Loading history...
1090
            'closed_at' => $meeting->getClosedAt() instanceof \DateTime ? $meeting->getClosedAt()->format('Y-m-d H:i:s') : '',
1091
            'record' => $meeting->isRecord() ? 1 : 0,
1092
            'c_id' => $meeting->getCourse()?->getId() ?? 0,
1093
            'session_id' => $meeting->getSession()?->getId() ?? 0,
1094
            'group_id' => $meeting->getGroup()?->getIid() ?? 0,
1095
            'course' => $meeting->getCourse(),
1096
            'session' => $meeting->getSession(),
1097
            'title' => $meeting->getTitle(),
1098
        ];
1099
    }
1100
1101
    public function getMeetingsLight(
1102
        $courseId = 0,
1103
        $sessionId = 0,
1104
        $groupId = 0,
1105
        $dateRange = []
1106
    ): array {
1107
        $em = Database::getManager();
1108
        $repo = $em->getRepository(ConferenceMeeting::class);
1109
        $meetings = [];
1110
1111
        if (!empty($dateRange)) {
1112
            $dateStart = (new \DateTime($dateRange['search_meeting_start']))->setTime(0, 0, 0);
1113
            $dateEnd = (new \DateTime($dateRange['search_meeting_end']))->setTime(23, 59, 59);
1114
            $meetings = $repo->findByDateRange($dateStart, $dateEnd);
1115
        } else {
1116
            $criteria = [
1117
                'course' => api_get_course_entity($courseId),
1118
                'session' => api_get_session_entity($sessionId),
1119
                'accessUrl' => api_get_url_entity($this->accessUrl),
1120
            ];
1121
            if ($this->hasGroupSupport() && $groupId) {
1122
                $criteria['group'] = api_get_group_entity($groupId);
1123
            }
1124
            $meetings = $repo->findBy($criteria, ['createdAt' => 'DESC']);
1125
        }
1126
1127
        $result = [];
1128
        foreach ($meetings as $meeting) {
1129
            $meetingArray = $this->convertMeetingToArray($meeting);
1130
1131
            $item = array_merge($meetingArray, [
1132
                'go_url' => '',
1133
                'show_links' => $this->plugin->get_lang('NoRecording'),
1134
                'action_links' => '',
1135
                'publish_url' => $this->publishUrl(['id' => $meeting->getId()]),
1136
                'unpublish_url' => $this->unPublishUrl(['id' => $meeting->getId()]),
1137
            ]);
1138
1139
            $result[] = $item;
1140
        }
1141
1142
        return $result;
1143
    }
1144
1145
    /**
1146
     * @param array $meeting
1147
     *
1148
     * @return string
1149
     */
1150
    public function endUrl($meeting)
1151
    {
1152
        if (!isset($meeting['id'])) {
1153
            return '';
1154
        }
1155
1156
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams().'&action=end&id='.$meeting['id'];
1157
    }
1158
1159
    /**
1160
     * Closes a meeting (usually when the user click on the close button from
1161
     * the conferences listing.
1162
     *
1163
     * @param string The internal ID of the meeting (id field for this meeting)
1164
     * @param string $courseCode
1165
     *
1166
     * @return void
1167
     * @assert (0) === false
1168
     */
1169
    public function endMeeting($id, $courseCode = null)
1170
    {
1171
        if (empty($id)) {
1172
            return false;
1173
        }
1174
1175
        $em = Database::getManager();
1176
1177
        /** @var ConferenceMeetingRepository $repo */
1178
        $repo = $em->getRepository(ConferenceMeeting::class);
1179
1180
        $meetingData = $repo->findOneAsArrayById((int) $id);
1181
        if (!$meetingData) {
1182
            return false;
1183
        }
1184
1185
        $manager = $this->isConferenceManager();
1186
        $pass = $manager ? $meetingData['moderatorPw'] : $meetingData['attendeePw'];
1187
1188
        Event::addEvent(
1189
            'bbb_end_meeting',
1190
            'meeting_id',
1191
            (int) $id,
1192
            null,
1193
            api_get_user_id(),
1194
            api_get_course_int_id(),
1195
            api_get_session_id()
1196
        );
1197
1198
        $endParams = [
1199
            'meetingId' => $meetingData['remoteId'],
1200
            'password' => $pass,
1201
        ];
1202
        $this->api->endMeetingWithXmlResponseArray($endParams);
1203
1204
        $repo->closeMeeting((int) $id, new \DateTime());
1205
1206
        /** @var ConferenceActivityRepository $activityRepo */
1207
        $activityRepo = $em->getRepository(ConferenceActivity::class);
1208
1209
        $activities = $activityRepo->findOpenWithSameInAndOutTime((int) $id);
1210
1211
        foreach ($activities as $activity) {
1212
            $activity->setOutAt(new \DateTime());
1213
            $activity->setClose(BbbPlugin::ROOM_CLOSE);
1214
            $em->persist($activity);
1215
        }
1216
1217
        $activityRepo->closeAllByMeetingId((int) $id);
1218
1219
        $em->flush();
1220
1221
        return true;
1222
    }
1223
1224
    /**
1225
     * @param array $meeting
1226
     * @param array $record
1227
     *
1228
     * @return string
1229
     */
1230
    public function addToCalendarUrl($meeting, $record = []): string
1231
    {
1232
        $url = isset($record['playbackFormatUrl']) ? $record['playbackFormatUrl'] : '';
1233
1234
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams(
1235
            ).'&action=add_to_calendar&id='.$meeting['id'].'&start='.api_strtotime($meeting['created_at']).'&url='.$url;
1236
    }
1237
1238
    /**
1239
     * @param int    $meetingId
1240
     * @param string $videoUrl
1241
     *
1242
     * @return bool|int
1243
     */
1244
    public function updateMeetingVideoUrl(int $meetingId, string $videoUrl): void
1245
    {
1246
        $em = Database::getManager();
1247
        /** @var ConferenceMeetingRepository $repo */
1248
        $repo = $em->getRepository(ConferenceMeeting::class);
1249
        $repo->updateVideoUrl($meetingId, $videoUrl);
1250
    }
1251
1252
    /**
1253
     * Force the course, session and/or group IDs
1254
     *
1255
     * @param string $courseCode
1256
     * @param int    $sessionId
1257
     * @param int    $groupId
1258
     */
1259
    public function forceCIdReq($courseCode, $sessionId = 0, $groupId = 0)
1260
    {
1261
        $this->courseCode = $courseCode;
1262
        $this->sessionId = (int) $sessionId;
1263
        $this->groupId = (int) $groupId;
1264
    }
1265
1266
    /**
1267
     * @param array $meetingInfo
1268
     * @param array $recordInfo
1269
     * @param bool  $isGlobal
1270
     * @param bool  $isAdminReport
1271
     *
1272
     * @return array
1273
     */
1274
    private function getActionLinks(
1275
        $meetingInfo,
1276
        $recordInfo,
1277
        $isGlobal = false,
1278
        $isAdminReport = false
1279
    ) {
1280
        $isVisible = $meetingInfo['visibility'] != 0;
1281
        $linkVisibility = $isVisible
1282
            ? Display::url(
1283
                Display::getMdiIcon(StateIcon::ACTIVE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('MakeInvisible')),
1284
                $this->unPublishUrl($meetingInfo)
1285
            )
1286
            : Display::url(
1287
                Display::getMdiIcon(StateIcon::INACTIVE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('MakeVisible')),
1288
                $this->publishUrl($meetingInfo)
1289
            );
1290
1291
        $links = [];
1292
        if ($this->plugin->get('allow_regenerate_recording') === 'true' && $meetingInfo['record'] == 1) {
1293
            if (!empty($recordInfo)) {
1294
                $links[] = Display::url(
1295
                    Display::getMdiIcon(ActionIcon::REFRESH, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('RegenerateRecord')),
1296
                    $this->regenerateRecordUrl($meetingInfo, $recordInfo)
1297
                );
1298
            } else {
1299
                $links[] = Display::url(
1300
                    Display::getMdiIcon(ActionIcon::REFRESH, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('RegenerateRecord')),
1301
                    $this->regenerateRecordUrlFromMeeting($meetingInfo)
1302
                );
1303
            }
1304
        }
1305
1306
        if (empty($recordInfo)) {
1307
            if (!$isAdminReport) {
1308
                if ($meetingInfo['status'] == 0) {
1309
                    $links[] = Display::url(
1310
                        Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')),
1311
                        $this->deleteRecordUrl($meetingInfo)
1312
                    );
1313
                    $links[] = $linkVisibility;
1314
                }
1315
1316
                return $links;
1317
            } else {
1318
                $links[] = Display::url(
1319
                    Display::getMdiIcon(ObjectIcon::HOME, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('GoToCourse')),
1320
                    $this->getListingUrl($meetingInfo['c_id'], $meetingInfo['session_id'], $meetingInfo['group_id'])
1321
                );
1322
1323
                return $links;
1324
            }
1325
        }
1326
1327
        if (!$isGlobal) {
1328
            $links[] = Display::url(
1329
                Display::getMdiIcon(ObjectIcon::LINK, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('UrlMeetingToShare')),
1330
                $this->copyToRecordToLinkTool($meetingInfo)
1331
            );
1332
            $links[] = Display::url(
1333
                Display::getMdiIcon(ObjectIcon::AGENDA, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('AddToCalendar')),
1334
                $this->addToCalendarUrl($meetingInfo, $recordInfo)
1335
            );
1336
        }
1337
1338
        $hide = $this->plugin->get('disable_download_conference_link') === 'true' ? true : false;
1339
1340
        if ($hide == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1341
            if ($meetingInfo['has_video_m4v']) {
1342
                $links[] = Display::url(
1343
                    Display::getMdiIcon(ActionIcon::SAVE_FORM, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('DownloadFile')),
1344
                    $recordInfo['playbackFormatUrl'].'/capture.m4v',
1345
                    ['target' => '_blank']
1346
                );
1347
            } else {
1348
                $links[] = Display::url(
1349
                    Display::getMdiIcon(ActionIcon::SAVE_FORM, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('DownloadFile')),
1350
                    '#',
1351
                    [
1352
                        'id' => "btn-check-meeting-video-{$meetingInfo['id']}",
1353
                        'class' => 'check-meeting-video',
1354
                        'data-id' => $meetingInfo['id'],
1355
                    ]
1356
                );
1357
            }
1358
        }
1359
1360
1361
        if (!$isAdminReport) {
1362
            $links[] = Display::url(
1363
                Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')),
1364
                $this->deleteRecordUrl($meetingInfo)
1365
            );
1366
            $links[] = $linkVisibility;
1367
        } else {
1368
            $links[] = Display::url(
1369
                Display::getMdiIcon(ObjectIcon::HOME, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('GoToCourse')),
1370
                $this->getListingUrl($meetingInfo['c_id'], $meetingInfo['session_id'], $meetingInfo['group_id'])
1371
            );
1372
        }
1373
1374
1375
        return $links;
1376
    }
1377
1378
    /**
1379
     * @param array $meeting
1380
     *
1381
     * @return string
1382
     */
1383
    public function unPublishUrl($meeting)
1384
    {
1385
        if (!isset($meeting['id'])) {
1386
            return null;
1387
        }
1388
1389
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams(
1390
            ).'&action=unpublish&id='.$meeting['id'];
1391
    }
1392
1393
    /**
1394
     * @param array $meeting
1395
     *
1396
     * @return string
1397
     */
1398
    public function publishUrl($meeting)
1399
    {
1400
        if (!isset($meeting['id'])) {
1401
            return '';
1402
        }
1403
1404
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams(
1405
            ).'&action=publish&id='.$meeting['id'];
1406
    }
1407
1408
    /**
1409
     * @param array $meeting
1410
     * @param array $recordInfo
1411
     *
1412
     * @return string
1413
     */
1414
    public function regenerateRecordUrl($meeting, $recordInfo)
1415
    {
1416
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1417
            return '';
1418
        }
1419
1420
        if (!isset($meeting['id'])) {
1421
            return '';
1422
        }
1423
1424
        if (empty($recordInfo) || (!empty($recordInfo['recordId']) && !isset($recordInfo['recordId']))) {
1425
            return '';
1426
        }
1427
1428
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams().
1429
            '&action=regenerate_record&id='.$meeting['id'].'&record_id='.$recordInfo['recordId'];
1430
    }
1431
1432
    /**
1433
     * @param array $meeting
1434
     *
1435
     * @return string
1436
     */
1437
    public function regenerateRecordUrlFromMeeting($meeting)
1438
    {
1439
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1440
            return '';
1441
        }
1442
1443
        if (!isset($meeting['id'])) {
1444
            return '';
1445
        }
1446
1447
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams().
1448
            '&action=regenerate_record&id='.$meeting['id'];
1449
    }
1450
1451
    /**
1452
     * @param array $meeting
1453
     *
1454
     * @return string
1455
     */
1456
    public function deleteRecordUrl($meeting)
1457
    {
1458
        if (!isset($meeting['id'])) {
1459
            return '';
1460
        }
1461
1462
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams(
1463
            ).'&action=delete_record&id='.$meeting['id'];
1464
    }
1465
1466
    /**
1467
     * @param array $meeting
1468
     *
1469
     * @return string
1470
     */
1471
    public function copyToRecordToLinkTool($meeting)
1472
    {
1473
        if (!isset($meeting['id'])) {
1474
            return '';
1475
        }
1476
1477
        return api_get_path(WEB_PLUGIN_PATH).
1478
            'Bbb/listing.php?'.$this->getUrlParams().'&action=copy_record_to_link_tool&id='.$meeting['id'];
1479
    }
1480
1481
    /**
1482
     * Function disabled
1483
     */
1484
    public function publishMeeting($id)
1485
    {
1486
        if (empty($id)) {
1487
            return false;
1488
        }
1489
1490
        $em = Database::getManager();
1491
        /** @var ConferenceMeetingRepository $repo */
1492
        $repo = $em->getRepository(ConferenceMeeting::class);
1493
1494
        $meeting = $repo->find($id);
1495
        if (!$meeting) {
1496
            return false;
1497
        }
1498
1499
        $meeting->setVisibility(1);
1500
        $em->flush();
1501
1502
        return true;
1503
    }
1504
1505
    /**
1506
     * Function disabled
1507
     */
1508
    public function unpublishMeeting($id)
1509
    {
1510
        if (empty($id)) {
1511
            return false;
1512
        }
1513
1514
        $em = Database::getManager();
1515
        /** @var ConferenceMeetingRepository $repo */
1516
        $repo = $em->getRepository(ConferenceMeeting::class);
1517
1518
        $meeting = $repo->find($id);
1519
        if (!$meeting) {
1520
            return false;
1521
        }
1522
1523
        $meeting->setVisibility(0);
1524
        $em->flush();
1525
1526
        return true;
1527
    }
1528
1529
    /**
1530
     * Get users online in the current course room.
1531
     *
1532
     * @return int The number of users currently connected to the videoconference
1533
     * @assert () > -1
1534
     */
1535
    public function getUsersOnlineInCurrentRoom()
1536
    {
1537
        $courseId = api_get_course_int_id();
1538
        $sessionId = api_get_session_id();
1539
1540
        $em = Database::getManager();
1541
        $repo = $em->getRepository(ConferenceMeeting::class);
1542
1543
        $qb = $repo->createQueryBuilder('m')
1544
            ->where('m.status = 1')
1545
            ->andWhere('m.accessUrl = :accessUrl')
1546
            ->setParameter('accessUrl', $this->accessUrl)
1547
            ->setMaxResults(1);
1548
1549
        if ($this->hasGroupSupport()) {
1550
            $groupId = api_get_group_id();
1551
            $qb->andWhere('m.course = :courseId')
1552
                ->andWhere('m.session = :sessionId')
1553
                ->andWhere('m.group = :groupId')
1554
                ->setParameter('courseId', $courseId)
1555
                ->setParameter('sessionId', $sessionId)
1556
                ->setParameter('groupId', $groupId);
1557
        } elseif ($this->isGlobalConferencePerUserEnabled()) {
1558
            $qb->andWhere('m.user = :userId')
1559
                ->setParameter('userId', $this->userId);
1560
        } else {
1561
            $qb->andWhere('m.course = :courseId')
1562
                ->andWhere('m.session = :sessionId')
1563
                ->setParameter('courseId', $courseId)
1564
                ->setParameter('sessionId', $sessionId);
1565
        }
1566
1567
        $meetingData = $qb->getQuery()->getOneOrNullResult();
1568
1569
        if (!$meetingData) {
1570
            return 0;
1571
        }
1572
        $pass = $meetingData->getModeratorPw();
1573
        $info = $this->getMeetingInfo([
1574
            'meetingId' => $meetingData->getRemoteId(),
1575
            'password' => $pass,
1576
        ]);
1577
        if ($info === false) {
1578
            $info = $this->getMeetingInfo([
1579
                'meetingId' => $meetingData->getId(),
1580
                'password' => $pass,
1581
            ]);
1582
        }
1583
1584
        if (!empty($info) && isset($info['participantCount'])) {
1585
            return (int) $info['participantCount'];
1586
        }
1587
1588
        return 0;
1589
    }
1590
1591
    /**
1592
     * @param int    $id
1593
     * @param string $recordId
1594
     *
1595
     * @return bool
1596
     */
1597
    public function regenerateRecording($id, $recordId = '')
1598
    {
1599
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1600
            return false;
1601
        }
1602
1603
        if (empty($id)) {
1604
            return false;
1605
        }
1606
1607
        $em = Database::getManager();
1608
        /** @var ConferenceMeetingRepository $repo */
1609
        $repo = $em->getRepository(ConferenceMeeting::class);
1610
1611
        $meetingData = $repo->findOneAsArrayById((int) $id);
1612
        if (!$meetingData) {
1613
            return false;
1614
        }
1615
1616
        Event::addEvent(
1617
            'bbb_regenerate_record',
1618
            'record_id',
1619
            (int) $recordId,
1620
            null,
1621
            api_get_user_id(),
1622
            api_get_course_int_id(),
1623
            api_get_session_id()
1624
        );
1625
1626
        /** @var ConferenceRecordingRepository $recordingRepo */
1627
        $recordingRepo = $em->getRepository(ConferenceRecordingRepository::class);
1628
        $recordings = $recordingRepo->findByMeetingRemoteId($meetingData['remoteId']);
1629
1630
        if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] === 'noRecordings') {
1631
            if (!empty($meetingData['internalMeetingId'])) {
1632
                return $this->api->generateRecording(['recordId' => $meetingData['internalMeetingId']]);
1633
            }
1634
1635
            return false;
1636
        }
1637
1638
        if (!empty($recordings['records'])) {
1639
            foreach ($recordings['records'] as $record) {
1640
                if ($recordId == $record['recordId']) {
1641
                    return $this->api->generateRecording(['recordId' => $recordId]);
1642
                }
1643
            }
1644
        }
1645
1646
        return false;
1647
    }
1648
1649
    /**
1650
     * Deletes a recording of a meeting
1651
     *
1652
     * @param int $id ID of the recording
1653
     *
1654
     * @return bool
1655
     *
1656
     * @assert () === false
1657
     * @todo Also delete links and agenda items created from this recording
1658
     */
1659
    public function deleteRecording($id)
1660
    {
1661
        if (empty($id)) {
1662
            return false;
1663
        }
1664
1665
        $em = Database::getManager();
1666
1667
        /** @var ConferenceMeetingRepository $meetingRepo */
1668
        $meetingRepo = $em->getRepository(ConferenceMeeting::class);
1669
        $meetingData = $meetingRepo->findOneAsArrayById((int) $id);
1670
        if (!$meetingData) {
1671
            return false;
1672
        }
1673
1674
        Event::addEvent(
1675
            'bbb_delete_record',
1676
            'meeting_id',
1677
            $id,
1678
            null,
1679
            api_get_user_id(),
1680
            api_get_course_int_id(),
1681
            api_get_session_id()
1682
        );
1683
1684
        $delete = false;
1685
        $recordings = [];
1686
1687
        if (!empty($meetingData['remoteId'])) {
1688
            Event::addEvent(
1689
                'bbb_delete_record',
1690
                'remote_id',
1691
                $meetingData['remoteId'],
1692
                null,
1693
                api_get_user_id(),
1694
                api_get_course_int_id(),
1695
                api_get_session_id()
1696
            );
1697
1698
            /** @var ConferenceRecordingRepository $recordingRepo */
1699
            $recordingRepo = $em->getRepository(ConferenceRecording::class);
1700
            $recordings = $recordingRepo->findByMeetingRemoteId($meetingData['remoteId']);
1701
        }
1702
1703
        if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] === 'noRecordings') {
1704
            $delete = true;
1705
        } elseif (!empty($recordings['records'])) {
1706
            $recordsToDelete = [];
1707
            foreach ($recordings['records'] as $record) {
1708
                $recordsToDelete[] = $record['recordId'];
1709
            }
1710
1711
            if (!empty($recordsToDelete)) {
1712
                $recordingParams = ['recordId' => implode(',', $recordsToDelete)];
1713
                Event::addEvent(
1714
                    'bbb_delete_record',
1715
                    'record_id_list',
1716
                    implode(',', $recordsToDelete),
1717
                    null,
1718
                    api_get_user_id(),
1719
                    api_get_course_int_id(),
1720
                    api_get_session_id()
1721
                );
1722
1723
                $result = $this->api->deleteRecordingsWithXmlResponseArray($recordingParams);
1724
1725
                if (!empty($result) && isset($result['deleted']) && $result['deleted'] === 'true') {
1726
                    $delete = true;
1727
                }
1728
            }
1729
        }
1730
1731
        if (!$delete) {
1732
            $delete = true;
1733
        }
1734
1735
        if ($delete) {
1736
            /** @var ConferenceActivityRepository $activityRepo */
1737
            $activityRepo = $em->getRepository(ConferenceActivity::class);
1738
            $activityRepo->closeAllByMeetingId((int) $id);
1739
1740
            $meeting = $meetingRepo->find((int) $id);
1741
            if ($meeting) {
1742
                $em->remove($meeting);
1743
            }
1744
1745
            $em->flush();
1746
        }
1747
1748
        return $delete;
1749
    }
1750
1751
    /**
1752
     * Creates a link in the links tool from the given videoconference recording
1753
     *
1754
     * @param int $id ID of the item in the plugin_bbb_meeting table
1755
     * @param string Hash identifying the recording, as provided by the API
1756
     *
1757
     * @return mixed ID of the newly created link, or false on error
1758
     * @assert (null, null) === false
1759
     * @assert (1, null) === false
1760
     * @assert (null, 'abcdefabcdefabcdefabcdef') === false
1761
     */
1762
    public function copyRecordingToLinkTool($id)
1763
    {
1764
        if (empty($id)) {
1765
            return false;
1766
        }
1767
1768
        $em = Database::getManager();
1769
        /** @var ConferenceMeetingRepository $repo */
1770
        $repo = $em->getRepository(ConferenceMeeting::class);
1771
1772
        $meetingData = $repo->findOneAsArrayById((int) $id);
1773
        if (!$meetingData || empty($meetingData['remoteId'])) {
1774
            return false;
1775
        }
1776
1777
        $records = $this->api->getRecordingsWithXmlResponseArray([
1778
            'meetingId' => $meetingData['remoteId']
1779
        ]);
1780
1781
        if (!empty($records)) {
1782
            if (isset($records['message']) && !empty($records['message'])) {
1783
                if ($records['messageKey'] == 'noRecordings') {
1784
                    return false;
1785
                }
1786
            } else {
1787
                $record = $records[0];
1788
                if (is_array($record) && isset($record['recordId'])) {
1789
                    $url = $record['playbackFormatUrl'];
1790
                    $link = new \Link();
1791
                    $params = [
1792
                        'url' => $url,
1793
                        'title' => $meetingData['title'],
1794
                    ];
1795
                    $id = $link->save($params);
1796
1797
                    return $id;
1798
                }
1799
            }
1800
        }
1801
1802
        return false;
1803
    }
1804
1805
    /**
1806
     * Checks if the video conference server is running.
1807
     * Function currently disabled (always returns 1)
1808
     * @return bool True if server is running, false otherwise
1809
     * @assert () === false
1810
     */
1811
    public function isServerRunning()
1812
    {
1813
        return true;
1814
        //return BigBlueButtonBN::isServerRunning($this->protocol.$this->url);
1815
    }
1816
1817
    /**
1818
     * Checks if the video conference plugin is properly configured
1819
     * @return bool True if plugin has a host and a salt, false otherwise
1820
     * @assert () === false
1821
     */
1822
    public function isServerConfigured()
1823
    {
1824
        $host = $this->plugin->get('host');
1825
1826
        if (empty($host)) {
1827
            return false;
1828
        }
1829
1830
        $salt = $this->plugin->get('salt');
1831
1832
        if (empty($salt)) {
1833
            return false;
1834
        }
1835
1836
        return true;
1837
        //return BigBlueButtonBN::isServerRunning($this->protocol.$this->url);
1838
    }
1839
1840
    /**
1841
     * Get active session in the all platform
1842
     */
1843
    public function getActiveSessionsCount(): int
1844
    {
1845
        $em = Database::getManager();
1846
        $qb = $em->createQueryBuilder();
1847
1848
        $qb->select('COUNT(m.id)')
1849
            ->from(ConferenceMeeting::class, 'm')
1850
            ->where('m.status = :status')
1851
            ->andWhere('m.accessUrl = :accessUrl')
1852
            ->setParameter('status', 1)
1853
            ->setParameter('accessUrl', $this->accessUrl);
1854
1855
        return (int) $qb->getQuery()->getSingleScalarResult();
1856
    }
1857
1858
    /**
1859
     * Get active session in the all platform
1860
     */
1861
    public function getActiveSessions(): array
1862
    {
1863
        $em = Database::getManager();
1864
        $repo = $em->getRepository(ConferenceMeeting::class);
1865
1866
        $qb = $repo->createQueryBuilder('m')
1867
            ->where('m.status = :status')
1868
            ->andWhere('m.accessUrl = :accessUrl')
1869
            ->setParameter('status', 1)
1870
            ->setParameter('accessUrl', $this->accessUrl);
1871
1872
        return $qb->getQuery()->getArrayResult();
1873
    }
1874
1875
    /**
1876
     * @param string $url
1877
     */
1878
    public function redirectToBBB($url)
1879
    {
1880
        if (file_exists(__DIR__.'/../config.vm.php')) {
1881
            // Using VM
1882
            echo Display::url($this->plugin->get_lang('ClickToContinue'), $url);
1883
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1884
        } else {
1885
            // Classic
1886
            header("Location: $url");
1887
            exit;
1888
        }
1889
    }
1890
1891
    /**
1892
     * @return string
1893
     */
1894
    public function getConferenceUrl()
1895
    {
1896
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/start.php?launch=1&'.$this->getUrlParams();
1897
    }
1898
1899
    /**
1900
     * Get the meeting info from DB by its name
1901
     *
1902
     * @param string $name
1903
     *
1904
     * @return array
1905
     */
1906
    public function findMeetingByName(string $name): ?array
1907
    {
1908
        $em = Database::getManager();
1909
        /** @var ConferenceMeetingRepository $repo */
1910
        $repo = $em->getRepository(ConferenceMeeting::class);
1911
1912
        $qb = $repo->createQueryBuilder('m')
1913
            ->where('m.title = :name')
1914
            ->setParameter('name', $name)
1915
            ->setMaxResults(1);
1916
1917
        $result = $qb->getQuery()->getArrayResult();
1918
1919
        return $result[0] ?? null;
1920
    }
1921
1922
    /**
1923
     * Get the meeting info from DB by its name
1924
     *
1925
     * @param int $id
1926
     *
1927
     * @return array
1928
     */
1929
    public function getMeeting(int $id): ?array
1930
    {
1931
        $em = Database::getManager();
1932
        /** @var ConferenceMeetingRepository $repo */
1933
        $repo = $em->getRepository(ConferenceMeeting::class);
1934
1935
        return $repo->findOneAsArrayById($id);
1936
    }
1937
1938
    /**
1939
     * Get the meeting info.
1940
     *
1941
     * @param int $id
1942
     *
1943
     * @return array
1944
     */
1945
    public function getMeetingByRemoteId(string $id): ?array
1946
    {
1947
        $em = Database::getManager();
1948
        /** @var ConferenceMeetingRepository $repo */
1949
        $repo = $em->getRepository(ConferenceMeeting::class);
1950
1951
        return $repo->findOneByRemoteIdAndAccessUrl($id, $this->accessUrl);
1952
    }
1953
1954
    /**
1955
     * @param int $meetingId
1956
     *
1957
     * @return array
1958
     */
1959
    public function findConnectedMeetingParticipants(int $meetingId): array
1960
    {
1961
        $em = Database::getManager();
1962
        /** @var ConferenceActivityRepository $repo */
1963
        $repo = $em->getRepository(ConferenceActivity::class);
1964
1965
        $activities = $repo->createQueryBuilder('a')
1966
            ->where('a.meeting = :meetingId')
1967
            ->andWhere('a.inAt IS NOT NULL')
1968
            ->setParameter('meetingId', $meetingId)
1969
            ->getQuery()
1970
            ->getResult();
1971
1972
        $participantIds = [];
1973
        $return = [];
1974
1975
        foreach ($activities as $activity) {
1976
            $participant = $activity->getParticipant();
1977
            $participantId = $participant?->getId();
1978
1979
            if (!$participantId || in_array($participantId, $participantIds)) {
1980
                continue;
1981
            }
1982
1983
            $participantIds[] = $participantId;
1984
1985
            $return[] = [
1986
                'id' => $activity->getId(),
1987
                'meeting_id' => $meetingId,
1988
                'participant' => api_get_user_entity($participantId),
1989
                'in_at' => $activity->getInAt()?->format('Y-m-d H:i:s'),
1990
                'out_at' => $activity->getOutAt()?->format('Y-m-d H:i:s'),
1991
            ];
1992
        }
1993
1994
        return $return;
1995
    }
1996
1997
    /**
1998
     * Check if the meeting has a capture.m4v video file. If exists then the has_video_m4v field is updated
1999
     *
2000
     * @param int $meetingId
2001
     *
2002
     * @return bool
2003
     */
2004
    public function checkDirectMeetingVideoUrl(int $meetingId): bool
2005
    {
2006
        $em = Database::getManager();
2007
        /** @var ConferenceMeetingRepository $repo */
2008
        $repo = $em->getRepository(ConferenceMeeting::class);
2009
2010
        $meetingInfo = $repo->findOneAsArrayById($meetingId);
2011
2012
        if (empty($meetingInfo) || !isset($meetingInfo['videoUrl'])) {
2013
            return false;
2014
        }
2015
2016
        $hasCapture = SocialManager::verifyUrl($meetingInfo['videoUrl'].'/capture.m4v');
2017
2018
        if ($hasCapture) {
2019
            $qb = $em->createQueryBuilder();
2020
            $qb->update(ConferenceMeeting::class, 'm')
2021
                ->set('m.hasVideoM4v', ':value')
2022
                ->where('m.id = :id')
2023
                ->setParameter('value', true)
2024
                ->setParameter('id', $meetingId)
2025
                ->getQuery()
2026
                ->execute();
2027
2028
            return true;
2029
        }
2030
2031
        return false;
2032
    }
2033
}
2034