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

Bbb   F

Complexity

Total Complexity 273

Size/Duplication

Total Lines 2012
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 974
dl 0
loc 2012
rs 1.6258
c 0
b 0
f 0
wmc 273

54 Methods

Rating   Name   Duplication   Size   Complexity  
A isGlobalConferencePerUserEnabled() 0 3 1
F __construct() 0 102 21
A isGlobalConference() 0 7 2
A getListingUrl() 0 4 1
B showGlobalConferenceLink() 0 26 7
A isGlobalConferenceEnabled() 0 3 1
B getUrlParams() 0 24 8
A setMaxUsersLimit() 0 6 2
C getMaxUsersLimit() 0 54 12
A getAllMeetingsInCourse() 0 36 2
A endMeeting() 0 53 5
A getConferenceUrl() 0 3 1
B isConferenceManager() 0 42 11
A regenerateRecordUrl() 0 16 6
A getMeetingParticipantInfo() 0 33 2
B copyRecordingToLinkTool() 0 41 10
A updateMeetingVideoUrl() 0 6 1
A getActiveSessions() 0 12 1
A getMeetingsLight() 0 42 5
A meetingExists() 0 5 1
A isServerRunning() 0 3 1
A findMeetingByName() 0 14 1
A forceCIdReq() 0 5 1
F joinMeeting() 0 85 15
C getActionLinks() 0 102 13
B getUsersOnlineInCurrentRoom() 0 54 7
A checkDirectMeetingVideoUrl() 0 28 4
A copyToRecordToLinkTool() 0 8 2
A redirectToBBB() 0 10 2
F getMeetings() 0 112 24
D createMeeting() 0 108 14
B regenerateRecording() 0 50 11
A deleteRecordUrl() 0 8 2
A convertMeetingToArray() 0 18 4
C deleteRecording() 0 90 16
A unPublishUrl() 0 8 2
A hasGroupSupport() 0 3 1
A publishUrl() 0 8 2
A publishMeeting() 0 19 3
A endUrl() 0 7 2
A saveParticipant() 0 45 5
A getMeeting() 0 7 1
A getMeetingByRemoteId() 0 7 1
A getUserMeetingPassword() 0 11 4
A regenerateRecordUrlFromMeeting() 0 12 3
A getMeetingInfo() 0 18 5
A unpublishMeeting() 0 19 3
A addToCalendarUrl() 0 6 2
A getCurrentVideoConferenceName() 0 15 4
B getMeetingByName() 0 53 6
A getActiveSessionsCount() 0 13 1
A findConnectedMeetingParticipants() 0 36 4
A isServerConfigured() 0 15 3
A getModMeetingPassword() 0 13 4

How to fix   Complexity   

Complex Class

Complex classes like Bbb often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Bbb, and based on these observations, apply Extract Interface, too.

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

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

880
            error_log('meeting_exists './** @scrutinizer ignore-type */ print_r($meeting ? ['id' => $meeting->getId()] : [], 1));
Loading history...
881
        }
882
883
        if (!$meeting) {
884
            return [];
885
        }
886
887
        return [
888
            'id' => $meeting->getId(),
889
            'c_id' => $meeting->getCourse()?->getId(),
890
            'session_id' => $meeting->getSession()?->getId(),
891
            'meeting_name' => $meeting->getTitle(),
892
            'status' => $meeting->getStatus(),
893
            'access_url' => $meeting->getAccessUrl()?->getId(),
894
            'group_id' => $meeting->getGroup()?->getIid(),
895
            'remote_id' => $meeting->getRemoteId(),
896
            'moderator_pw' => $meeting->getModeratorPw(),
897
            'attendee_pw' => $meeting->getAttendeePw(),
898
            'created_at' => $meeting->getCreatedAt()->format('Y-m-d H:i:s'),
899
            'closed_at' => $meeting->getClosedAt()?->format('Y-m-d H:i:s'),
900
            'visibility' => $meeting->getVisibility(),
901
            'video_url' => $meeting->getVideoUrl(),
902
            'has_video_m4v' => $meeting->isHasVideoM4v(),
903
            'record' => $meeting->isRecord(),
904
            'internal_meeting_id' => $meeting->getInternalMeetingId(),
905
        ];
906
    }
907
908
    /**
909
     * Gets a list from the database of all meetings attached to a course with the given status
910
     * @param int $courseId
911
     * @param int $sessionId
912
     * @param int $status 0 for closed meetings, 1 for open meetings
913
     *
914
     * @return array
915
     */
916
    public function getAllMeetingsInCourse($courseId, $sessionId, $status)
917
    {
918
        $em = Database::getManager();
919
        $courseEntity = api_get_course_entity($courseId);
920
        $sessionEntity = api_get_session_entity($sessionId);
921
922
        $meetings = $em->getRepository(ConferenceMeeting::class)->findBy([
923
            'course' => $courseEntity,
924
            'session' => $sessionEntity,
925
            'status' => $status,
926
        ]);
927
928
        $results = [];
929
        foreach ($meetings as $meeting) {
930
            $results[] = [
931
                'id' => $meeting->getId(),
932
                'c_id' => $meeting->getCourse()?->getId(),
933
                'session_id' => $meeting->getSession()?->getId(),
934
                'meeting_name' => $meeting->getTitle(),
935
                'status' => $meeting->getStatus(),
936
                'access_url' => $meeting->getAccessUrl()?->getId(),
937
                'group_id' => $meeting->getGroup()?->getIid(),
938
                'remote_id' => $meeting->getRemoteId(),
939
                'moderator_pw' => $meeting->getModeratorPw(),
940
                'attendee_pw' => $meeting->getAttendeePw(),
941
                'created_at' => $meeting->getCreatedAt()->format('Y-m-d H:i:s'),
942
                'closed_at' => $meeting->getClosedAt()?->format('Y-m-d H:i:s'),
943
                'visibility' => $meeting->getVisibility(),
944
                'video_url' => $meeting->getVideoUrl(),
945
                'has_video_m4v' => $meeting->isHasVideoM4v(),
946
                'record' => $meeting->isRecord(),
947
                'internal_meeting_id' => $meeting->getInternalMeetingId(),
948
            ];
949
        }
950
951
        return $results;
952
    }
953
954
    /**
955
     * Gets all the course meetings saved in the plugin_bbb_meeting table and
956
     * generate actionable links (join/close/delete/etc)
957
     *
958
     * @param int   $courseId
959
     * @param int   $sessionId
960
     * @param int   $groupId
961
     * @param bool  $isAdminReport Optional. Set to true then the report is for admins
962
     * @param array $dateRange     Optional
963
     *
964
     * @return array Array of current open meeting rooms
965
     * @throws Exception
966
     */
967
    public function getMeetings(
968
        $courseId = 0,
969
        $sessionId = 0,
970
        $groupId = 0,
971
        $isAdminReport = false,
972
        $dateRange = []
973
    ) {
974
        $em = Database::getManager();
975
        $repo = $em->getRepository(ConferenceMeeting::class);
976
        $manager = $this->isConferenceManager();
977
        $isGlobal = $this->isGlobalConference();
978
        $meetings = [];
979
980
        if (!empty($dateRange)) {
981
            $dateStart = (new \DateTime($dateRange['search_meeting_start']))->setTime(0, 0, 0);
982
            $dateEnd = (new \DateTime($dateRange['search_meeting_end']))->setTime(23, 59, 59);
983
            $meetings = $repo->findByDateRange($dateStart, $dateEnd);
984
        } elseif ($this->isGlobalConference()) {
985
            $meetings = $repo->findBy([
986
                'course' => null,
987
                'user' => api_get_user_entity($this->userId),
988
                'accessUrl' => api_get_url_entity($this->accessUrl),
989
            ]);
990
        } elseif ($this->isGlobalConferencePerUserEnabled()) {
991
            $meetings = $repo->findBy([
992
                'course' => api_get_course_entity($courseId),
993
                'session' => api_get_session_entity($sessionId),
994
                'user' => api_get_user_entity($this->userId),
995
                'accessUrl' => api_get_url_entity($this->accessUrl),
996
            ]);
997
        } else {
998
            $criteria = [
999
                'course' => api_get_course_entity($courseId),
1000
                'session' => api_get_session_entity($sessionId),
1001
                'accessUrl' => api_get_url_entity($this->accessUrl),
1002
            ];
1003
            if ($this->hasGroupSupport() && $groupId) {
1004
                $criteria['group'] = api_get_group_entity($groupId);
1005
            }
1006
            $meetings = $repo->findBy($criteria, ['createdAt' => 'ASC']);
1007
        }
1008
1009
        $result = [];
1010
        foreach ($meetings as $meeting) {
1011
            $meetingArray = $this->convertMeetingToArray($meeting);
1012
            $recordLink = $this->plugin->get_lang('NoRecording');
1013
            $meetingBBB = $this->getMeetingInfo([
1014
                'meetingId' => $meeting->getRemoteId(),
1015
                'password' => $manager ? $meeting->getModeratorPw() : $meeting->getAttendeePw(),
1016
            ]);
1017
1018
            if (!$meetingBBB && $meeting->getId()) {
1019
                $meetingBBB = $this->getMeetingInfo([
1020
                    'meetingId' => $meeting->getId(),
1021
                    'password' => $manager ? $meeting->getModeratorPw() : $meeting->getAttendeePw(),
1022
                ]);
1023
            }
1024
1025
            if (!$meeting->isVisible() && !$manager) {
1026
                continue;
1027
            }
1028
1029
            $meetingBBB['end_url'] = $this->endUrl(['id' => $meeting->getId()]);
1030
            if (isset($meetingBBB['returncode']) && (string) $meetingBBB['returncode'] === 'FAILED') {
1031
                if ($meeting->getStatus() === 1 && $manager) {
1032
                    $this->endMeeting($meeting->getId(), $meeting->getCourse()?->getCode());
1033
                }
1034
            } else {
1035
                $meetingBBB['add_to_calendar_url'] = $this->addToCalendarUrl($meetingArray);
1036
            }
1037
1038
            if ($meeting->isRecord()) {
1039
                $recordings = $this->api->getRecordingsWithXmlResponseArray(['meetingId' => $meeting->getRemoteId()]);
1040
                if (!empty($recordings) && (!isset($recordings['messageKey']) || $recordings['messageKey'] !== 'noRecordings')) {
1041
                    $record = end($recordings);
1042
                    if (isset($record['playbackFormatUrl'])) {
1043
                        $recordLink = Display::url(
1044
                            $this->plugin->get_lang('ViewRecord'),
1045
                            $record['playbackFormatUrl'],
1046
                            ['target' => '_blank', 'class' => 'btn btn--plain']
1047
                        );
1048
                        $this->updateMeetingVideoUrl($meeting->getId(), $record['playbackFormatUrl']);
1049
                    }
1050
                }
1051
            }
1052
1053
            $actionLinks = $this->getActionLinks($meetingArray, $record ?? [], $isGlobal, $isAdminReport);
1054
1055
            $item = array_merge($meetingArray, [
1056
                'go_url' => '',
1057
                'show_links' => $recordLink,
1058
                'action_links' => implode(PHP_EOL, $actionLinks),
1059
                'publish_url' => $this->publishUrl(['id' => $meeting->getId()]),
1060
                'unpublish_url' => $this->unPublishUrl(['id' => $meeting->getId()]),
1061
            ]);
1062
1063
            if ($meeting->getStatus() === 1) {
1064
                $joinParams = [
1065
                    'meetingId' => $meeting->getRemoteId(),
1066
                    'username' => $this->userCompleteName,
1067
                    'password' => $manager ? $meeting->getModeratorPw() : $meeting->getAttendeePw(),
1068
                    'createTime' => '',
1069
                    'userID' => '',
1070
                    'webVoiceConf' => '',
1071
                ];
1072
                $item['go_url'] = $this->protocol.$this->api->getJoinMeetingURL($joinParams);
1073
            }
1074
1075
            $result[] = array_merge($item, $meetingBBB);
1076
        }
1077
1078
        return $result;
1079
    }
1080
1081
    private function convertMeetingToArray(ConferenceMeeting $meeting): array
1082
    {
1083
        return [
1084
            'id' => $meeting->getId(),
1085
            'remote_id' => $meeting->getRemoteId(),
1086
            'internal_meeting_id' => $meeting->getInternalMeetingId(),
1087
            'meeting_name' => $meeting->getTitle(),
1088
            'status' => $meeting->getStatus(),
1089
            'visibility' => $meeting->getVisibility(),
1090
            '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...
1091
            'closed_at' => $meeting->getClosedAt() instanceof \DateTime ? $meeting->getClosedAt()->format('Y-m-d H:i:s') : '',
1092
            'record' => $meeting->isRecord() ? 1 : 0,
1093
            'c_id' => $meeting->getCourse()?->getId() ?? 0,
1094
            'session_id' => $meeting->getSession()?->getId() ?? 0,
1095
            'group_id' => $meeting->getGroup()?->getIid() ?? 0,
1096
            'course' => $meeting->getCourse(),
1097
            'session' => $meeting->getSession(),
1098
            'title' => $meeting->getTitle(),
1099
        ];
1100
    }
1101
1102
    public function getMeetingsLight(
1103
        $courseId = 0,
1104
        $sessionId = 0,
1105
        $groupId = 0,
1106
        $dateRange = []
1107
    ): array {
1108
        $em = Database::getManager();
1109
        $repo = $em->getRepository(ConferenceMeeting::class);
1110
        $meetings = [];
1111
1112
        if (!empty($dateRange)) {
1113
            $dateStart = (new \DateTime($dateRange['search_meeting_start']))->setTime(0, 0, 0);
1114
            $dateEnd = (new \DateTime($dateRange['search_meeting_end']))->setTime(23, 59, 59);
1115
            $meetings = $repo->findByDateRange($dateStart, $dateEnd);
1116
        } else {
1117
            $criteria = [
1118
                'course' => api_get_course_entity($courseId),
1119
                'session' => api_get_session_entity($sessionId),
1120
                'accessUrl' => api_get_url_entity($this->accessUrl),
1121
            ];
1122
            if ($this->hasGroupSupport() && $groupId) {
1123
                $criteria['group'] = api_get_group_entity($groupId);
1124
            }
1125
            $meetings = $repo->findBy($criteria, ['createdAt' => 'DESC']);
1126
        }
1127
1128
        $result = [];
1129
        foreach ($meetings as $meeting) {
1130
            $meetingArray = $this->convertMeetingToArray($meeting);
1131
1132
            $item = array_merge($meetingArray, [
1133
                'go_url' => '',
1134
                'show_links' => $this->plugin->get_lang('NoRecording'),
1135
                'action_links' => '',
1136
                'publish_url' => $this->publishUrl(['id' => $meeting->getId()]),
1137
                'unpublish_url' => $this->unPublishUrl(['id' => $meeting->getId()]),
1138
            ]);
1139
1140
            $result[] = $item;
1141
        }
1142
1143
        return $result;
1144
    }
1145
1146
    /**
1147
     * @param array $meeting
1148
     *
1149
     * @return string
1150
     */
1151
    public function endUrl($meeting)
1152
    {
1153
        if (!isset($meeting['id'])) {
1154
            return '';
1155
        }
1156
1157
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams().'&action=end&id='.$meeting['id'];
1158
    }
1159
1160
    /**
1161
     * Closes a meeting (usually when the user click on the close button from
1162
     * the conferences listing.
1163
     *
1164
     * @param string The internal ID of the meeting (id field for this meeting)
1165
     * @param string $courseCode
1166
     *
1167
     * @return void
1168
     * @assert (0) === false
1169
     */
1170
    public function endMeeting($id, $courseCode = null)
1171
    {
1172
        if (empty($id)) {
1173
            return false;
1174
        }
1175
1176
        $em = Database::getManager();
1177
1178
        /** @var ConferenceMeetingRepository $repo */
1179
        $repo = $em->getRepository(ConferenceMeeting::class);
1180
1181
        $meetingData = $repo->findOneAsArrayById((int) $id);
1182
        if (!$meetingData) {
1183
            return false;
1184
        }
1185
1186
        $manager = $this->isConferenceManager();
1187
        $pass = $manager ? $meetingData['moderatorPw'] : $meetingData['attendeePw'];
1188
1189
        Event::addEvent(
1190
            'bbb_end_meeting',
1191
            'meeting_id',
1192
            (int) $id,
1193
            null,
1194
            api_get_user_id(),
1195
            api_get_course_int_id(),
1196
            api_get_session_id()
1197
        );
1198
1199
        $endParams = [
1200
            'meetingId' => $meetingData['remoteId'],
1201
            'password' => $pass,
1202
        ];
1203
        $this->api->endMeetingWithXmlResponseArray($endParams);
1204
1205
        $repo->closeMeeting((int) $id, new \DateTime());
1206
1207
        /** @var ConferenceActivityRepository $activityRepo */
1208
        $activityRepo = $em->getRepository(ConferenceActivity::class);
1209
1210
        $activities = $activityRepo->findOpenWithSameInAndOutTime((int) $id);
1211
1212
        foreach ($activities as $activity) {
1213
            $activity->setOutAt(new \DateTime());
1214
            $activity->setClose(BbbPlugin::ROOM_CLOSE);
1215
            $em->persist($activity);
1216
        }
1217
1218
        $activityRepo->closeAllByMeetingId((int) $id);
1219
1220
        $em->flush();
1221
1222
        return true;
1223
    }
1224
1225
    /**
1226
     * @param array $meeting
1227
     * @param array $record
1228
     *
1229
     * @return string
1230
     */
1231
    public function addToCalendarUrl($meeting, $record = []): string
1232
    {
1233
        $url = isset($record['playbackFormatUrl']) ? $record['playbackFormatUrl'] : '';
1234
1235
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams(
1236
            ).'&action=add_to_calendar&id='.$meeting['id'].'&start='.api_strtotime($meeting['created_at']).'&url='.$url;
1237
    }
1238
1239
    /**
1240
     * @param int    $meetingId
1241
     * @param string $videoUrl
1242
     *
1243
     * @return bool|int
1244
     */
1245
    public function updateMeetingVideoUrl(int $meetingId, string $videoUrl): void
1246
    {
1247
        $em = Database::getManager();
1248
        /** @var ConferenceMeetingRepository $repo */
1249
        $repo = $em->getRepository(ConferenceMeeting::class);
1250
        $repo->updateVideoUrl($meetingId, $videoUrl);
1251
    }
1252
1253
    /**
1254
     * Force the course, session and/or group IDs
1255
     *
1256
     * @param string $courseCode
1257
     * @param int    $sessionId
1258
     * @param int    $groupId
1259
     */
1260
    public function forceCIdReq($courseCode, $sessionId = 0, $groupId = 0)
1261
    {
1262
        $this->courseCode = $courseCode;
1263
        $this->sessionId = (int) $sessionId;
1264
        $this->groupId = (int) $groupId;
1265
    }
1266
1267
    /**
1268
     * @param array $meetingInfo
1269
     * @param array $recordInfo
1270
     * @param bool  $isGlobal
1271
     * @param bool  $isAdminReport
1272
     *
1273
     * @return array
1274
     */
1275
    private function getActionLinks(
1276
        $meetingInfo,
1277
        $recordInfo,
1278
        $isGlobal = false,
1279
        $isAdminReport = false
1280
    ) {
1281
        $isVisible = $meetingInfo['visibility'] != 0;
1282
        $linkVisibility = $isVisible
1283
            ? Display::url(
1284
                Display::getMdiIcon(StateIcon::ACTIVE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Make invisible')),
1285
                $this->unPublishUrl($meetingInfo)
1286
            )
1287
            : Display::url(
1288
                Display::getMdiIcon(StateIcon::INACTIVE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Make visible')),
1289
                $this->publishUrl($meetingInfo)
1290
            );
1291
1292
        $links = [];
1293
        if ($this->plugin->get('allow_regenerate_recording') === 'true' && $meetingInfo['record'] == 1) {
1294
            if (!empty($recordInfo)) {
1295
                $links[] = Display::url(
1296
                    Display::getMdiIcon(ActionIcon::REFRESH, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('RegenerateRecord')),
1297
                    $this->regenerateRecordUrl($meetingInfo, $recordInfo)
1298
                );
1299
            } else {
1300
                $links[] = Display::url(
1301
                    Display::getMdiIcon(ActionIcon::REFRESH, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('RegenerateRecord')),
1302
                    $this->regenerateRecordUrlFromMeeting($meetingInfo)
1303
                );
1304
            }
1305
        }
1306
1307
        if (empty($recordInfo)) {
1308
            if (!$isAdminReport) {
1309
                if ($meetingInfo['status'] == 0) {
1310
                    $links[] = Display::url(
1311
                        Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')),
1312
                        $this->deleteRecordUrl($meetingInfo)
1313
                    );
1314
                    $links[] = $linkVisibility;
1315
                }
1316
1317
                return $links;
1318
            } else {
1319
                $links[] = Display::url(
1320
                    Display::getMdiIcon(ObjectIcon::HOME, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Go to the course')),
1321
                    $this->getListingUrl($meetingInfo['c_id'], $meetingInfo['session_id'], $meetingInfo['group_id'])
1322
                );
1323
1324
                return $links;
1325
            }
1326
        }
1327
1328
        if (!$isGlobal) {
1329
            $links[] = Display::url(
1330
                Display::getMdiIcon(ObjectIcon::LINK, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('UrlMeetingToShare')),
1331
                $this->copyToRecordToLinkTool($meetingInfo)
1332
            );
1333
            $links[] = Display::url(
1334
                Display::getMdiIcon(ObjectIcon::AGENDA, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Add to calendar')),
1335
                $this->addToCalendarUrl($meetingInfo, $recordInfo)
1336
            );
1337
        }
1338
1339
        $hide = $this->plugin->get('disable_download_conference_link') === 'true' ? true : false;
1340
1341
        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...
1342
            if ($meetingInfo['has_video_m4v']) {
1343
                $links[] = Display::url(
1344
                    Display::getMdiIcon(ActionIcon::SAVE_FORM, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Download file')),
1345
                    $recordInfo['playbackFormatUrl'].'/capture.m4v',
1346
                    ['target' => '_blank']
1347
                );
1348
            } else {
1349
                $links[] = Display::url(
1350
                    Display::getMdiIcon(ActionIcon::SAVE_FORM, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Download file')),
1351
                    '#',
1352
                    [
1353
                        'id' => "btn-check-meeting-video-{$meetingInfo['id']}",
1354
                        'class' => 'check-meeting-video',
1355
                        'data-id' => $meetingInfo['id'],
1356
                    ]
1357
                );
1358
            }
1359
        }
1360
1361
1362
        if (!$isAdminReport) {
1363
            $links[] = Display::url(
1364
                Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')),
1365
                $this->deleteRecordUrl($meetingInfo)
1366
            );
1367
            $links[] = $linkVisibility;
1368
        } else {
1369
            $links[] = Display::url(
1370
                Display::getMdiIcon(ObjectIcon::HOME, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Go to the course')),
1371
                $this->getListingUrl($meetingInfo['c_id'], $meetingInfo['session_id'], $meetingInfo['group_id'])
1372
            );
1373
        }
1374
1375
1376
        return $links;
1377
    }
1378
1379
    /**
1380
     * @param array $meeting
1381
     *
1382
     * @return string
1383
     */
1384
    public function unPublishUrl($meeting)
1385
    {
1386
        if (!isset($meeting['id'])) {
1387
            return null;
1388
        }
1389
1390
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams(
1391
            ).'&action=unpublish&id='.$meeting['id'];
1392
    }
1393
1394
    /**
1395
     * @param array $meeting
1396
     *
1397
     * @return string
1398
     */
1399
    public function publishUrl($meeting)
1400
    {
1401
        if (!isset($meeting['id'])) {
1402
            return '';
1403
        }
1404
1405
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams(
1406
            ).'&action=publish&id='.$meeting['id'];
1407
    }
1408
1409
    /**
1410
     * @param array $meeting
1411
     * @param array $recordInfo
1412
     *
1413
     * @return string
1414
     */
1415
    public function regenerateRecordUrl($meeting, $recordInfo)
1416
    {
1417
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1418
            return '';
1419
        }
1420
1421
        if (!isset($meeting['id'])) {
1422
            return '';
1423
        }
1424
1425
        if (empty($recordInfo) || (!empty($recordInfo['recordId']) && !isset($recordInfo['recordId']))) {
1426
            return '';
1427
        }
1428
1429
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams().
1430
            '&action=regenerate_record&id='.$meeting['id'].'&record_id='.$recordInfo['recordId'];
1431
    }
1432
1433
    /**
1434
     * @param array $meeting
1435
     *
1436
     * @return string
1437
     */
1438
    public function regenerateRecordUrlFromMeeting($meeting)
1439
    {
1440
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1441
            return '';
1442
        }
1443
1444
        if (!isset($meeting['id'])) {
1445
            return '';
1446
        }
1447
1448
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams().
1449
            '&action=regenerate_record&id='.$meeting['id'];
1450
    }
1451
1452
    /**
1453
     * @param array $meeting
1454
     *
1455
     * @return string
1456
     */
1457
    public function deleteRecordUrl($meeting)
1458
    {
1459
        if (!isset($meeting['id'])) {
1460
            return '';
1461
        }
1462
1463
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/listing.php?'.$this->getUrlParams(
1464
            ).'&action=delete_record&id='.$meeting['id'];
1465
    }
1466
1467
    /**
1468
     * @param array $meeting
1469
     *
1470
     * @return string
1471
     */
1472
    public function copyToRecordToLinkTool($meeting)
1473
    {
1474
        if (!isset($meeting['id'])) {
1475
            return '';
1476
        }
1477
1478
        return api_get_path(WEB_PLUGIN_PATH).
1479
            'Bbb/listing.php?'.$this->getUrlParams().'&action=copy_record_to_link_tool&id='.$meeting['id'];
1480
    }
1481
1482
    /**
1483
     * Function disabled
1484
     */
1485
    public function publishMeeting($id)
1486
    {
1487
        if (empty($id)) {
1488
            return false;
1489
        }
1490
1491
        $em = Database::getManager();
1492
        /** @var ConferenceMeetingRepository $repo */
1493
        $repo = $em->getRepository(ConferenceMeeting::class);
1494
1495
        $meeting = $repo->find($id);
1496
        if (!$meeting) {
1497
            return false;
1498
        }
1499
1500
        $meeting->setVisibility(1);
1501
        $em->flush();
1502
1503
        return true;
1504
    }
1505
1506
    /**
1507
     * Function disabled
1508
     */
1509
    public function unpublishMeeting($id)
1510
    {
1511
        if (empty($id)) {
1512
            return false;
1513
        }
1514
1515
        $em = Database::getManager();
1516
        /** @var ConferenceMeetingRepository $repo */
1517
        $repo = $em->getRepository(ConferenceMeeting::class);
1518
1519
        $meeting = $repo->find($id);
1520
        if (!$meeting) {
1521
            return false;
1522
        }
1523
1524
        $meeting->setVisibility(0);
1525
        $em->flush();
1526
1527
        return true;
1528
    }
1529
1530
    /**
1531
     * Get users online in the current course room.
1532
     *
1533
     * @return int The number of users currently connected to the videoconference
1534
     * @assert () > -1
1535
     */
1536
    public function getUsersOnlineInCurrentRoom()
1537
    {
1538
        $courseId = api_get_course_int_id();
1539
        $sessionId = api_get_session_id();
1540
1541
        $em = Database::getManager();
1542
        $repo = $em->getRepository(ConferenceMeeting::class);
1543
1544
        $qb = $repo->createQueryBuilder('m')
1545
            ->where('m.status = 1')
1546
            ->andWhere('m.accessUrl = :accessUrl')
1547
            ->setParameter('accessUrl', $this->accessUrl)
1548
            ->setMaxResults(1);
1549
1550
        if ($this->hasGroupSupport()) {
1551
            $groupId = api_get_group_id();
1552
            $qb->andWhere('m.course = :courseId')
1553
                ->andWhere('m.session = :sessionId')
1554
                ->andWhere('m.group = :groupId')
1555
                ->setParameter('courseId', $courseId)
1556
                ->setParameter('sessionId', $sessionId)
1557
                ->setParameter('groupId', $groupId);
1558
        } elseif ($this->isGlobalConferencePerUserEnabled()) {
1559
            $qb->andWhere('m.user = :userId')
1560
                ->setParameter('userId', $this->userId);
1561
        } else {
1562
            $qb->andWhere('m.course = :courseId')
1563
                ->andWhere('m.session = :sessionId')
1564
                ->setParameter('courseId', $courseId)
1565
                ->setParameter('sessionId', $sessionId);
1566
        }
1567
1568
        $meetingData = $qb->getQuery()->getOneOrNullResult();
1569
1570
        if (!$meetingData) {
1571
            return 0;
1572
        }
1573
        $pass = $meetingData->getModeratorPw();
1574
        $info = $this->getMeetingInfo([
1575
            'meetingId' => $meetingData->getRemoteId(),
1576
            'password' => $pass,
1577
        ]);
1578
        if ($info === false) {
1579
            $info = $this->getMeetingInfo([
1580
                'meetingId' => $meetingData->getId(),
1581
                'password' => $pass,
1582
            ]);
1583
        }
1584
1585
        if (!empty($info) && isset($info['participantCount'])) {
1586
            return (int) $info['participantCount'];
1587
        }
1588
1589
        return 0;
1590
    }
1591
1592
    /**
1593
     * @param int    $id
1594
     * @param string $recordId
1595
     *
1596
     * @return bool
1597
     */
1598
    public function regenerateRecording($id, $recordId = '')
1599
    {
1600
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1601
            return false;
1602
        }
1603
1604
        if (empty($id)) {
1605
            return false;
1606
        }
1607
1608
        $em = Database::getManager();
1609
        /** @var ConferenceMeetingRepository $repo */
1610
        $repo = $em->getRepository(ConferenceMeeting::class);
1611
1612
        $meetingData = $repo->findOneAsArrayById((int) $id);
1613
        if (!$meetingData) {
1614
            return false;
1615
        }
1616
1617
        Event::addEvent(
1618
            'bbb_regenerate_record',
1619
            'record_id',
1620
            (int) $recordId,
1621
            null,
1622
            api_get_user_id(),
1623
            api_get_course_int_id(),
1624
            api_get_session_id()
1625
        );
1626
1627
        /** @var ConferenceRecordingRepository $recordingRepo */
1628
        $recordingRepo = $em->getRepository(ConferenceRecordingRepository::class);
1629
        $recordings = $recordingRepo->findByMeetingRemoteId($meetingData['remoteId']);
1630
1631
        if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] === 'noRecordings') {
1632
            if (!empty($meetingData['internalMeetingId'])) {
1633
                return $this->api->generateRecording(['recordId' => $meetingData['internalMeetingId']]);
1634
            }
1635
1636
            return false;
1637
        }
1638
1639
        if (!empty($recordings['records'])) {
1640
            foreach ($recordings['records'] as $record) {
1641
                if ($recordId == $record['recordId']) {
1642
                    return $this->api->generateRecording(['recordId' => $recordId]);
1643
                }
1644
            }
1645
        }
1646
1647
        return false;
1648
    }
1649
1650
    /**
1651
     * Deletes a recording of a meeting
1652
     *
1653
     * @param int $id ID of the recording
1654
     *
1655
     * @return bool
1656
     *
1657
     * @assert () === false
1658
     * @todo Also delete links and agenda items created from this recording
1659
     */
1660
    public function deleteRecording($id)
1661
    {
1662
        if (empty($id)) {
1663
            return false;
1664
        }
1665
1666
        $em = Database::getManager();
1667
1668
        /** @var ConferenceMeetingRepository $meetingRepo */
1669
        $meetingRepo = $em->getRepository(ConferenceMeeting::class);
1670
        $meetingData = $meetingRepo->findOneAsArrayById((int) $id);
1671
        if (!$meetingData) {
1672
            return false;
1673
        }
1674
1675
        Event::addEvent(
1676
            'bbb_delete_record',
1677
            'meeting_id',
1678
            $id,
1679
            null,
1680
            api_get_user_id(),
1681
            api_get_course_int_id(),
1682
            api_get_session_id()
1683
        );
1684
1685
        $delete = false;
1686
        $recordings = [];
1687
1688
        if (!empty($meetingData['remoteId'])) {
1689
            Event::addEvent(
1690
                'bbb_delete_record',
1691
                'remote_id',
1692
                $meetingData['remoteId'],
1693
                null,
1694
                api_get_user_id(),
1695
                api_get_course_int_id(),
1696
                api_get_session_id()
1697
            );
1698
1699
            /** @var ConferenceRecordingRepository $recordingRepo */
1700
            $recordingRepo = $em->getRepository(ConferenceRecording::class);
1701
            $recordings = $recordingRepo->findByMeetingRemoteId($meetingData['remoteId']);
1702
        }
1703
1704
        if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] === 'noRecordings') {
1705
            $delete = true;
1706
        } elseif (!empty($recordings['records'])) {
1707
            $recordsToDelete = [];
1708
            foreach ($recordings['records'] as $record) {
1709
                $recordsToDelete[] = $record['recordId'];
1710
            }
1711
1712
            if (!empty($recordsToDelete)) {
1713
                $recordingParams = ['recordId' => implode(',', $recordsToDelete)];
1714
                Event::addEvent(
1715
                    'bbb_delete_record',
1716
                    'record_id_list',
1717
                    implode(',', $recordsToDelete),
1718
                    null,
1719
                    api_get_user_id(),
1720
                    api_get_course_int_id(),
1721
                    api_get_session_id()
1722
                );
1723
1724
                $result = $this->api->deleteRecordingsWithXmlResponseArray($recordingParams);
1725
1726
                if (!empty($result) && isset($result['deleted']) && $result['deleted'] === 'true') {
1727
                    $delete = true;
1728
                }
1729
            }
1730
        }
1731
1732
        if (!$delete) {
1733
            $delete = true;
1734
        }
1735
1736
        if ($delete) {
1737
            /** @var ConferenceActivityRepository $activityRepo */
1738
            $activityRepo = $em->getRepository(ConferenceActivity::class);
1739
            $activityRepo->closeAllByMeetingId((int) $id);
1740
1741
            $meeting = $meetingRepo->find((int) $id);
1742
            if ($meeting) {
1743
                $em->remove($meeting);
1744
            }
1745
1746
            $em->flush();
1747
        }
1748
1749
        return $delete;
1750
    }
1751
1752
    /**
1753
     * Creates a link in the links tool from the given videoconference recording
1754
     *
1755
     * @param int $id ID of the item in the plugin_bbb_meeting table
1756
     * @param string Hash identifying the recording, as provided by the API
1757
     *
1758
     * @return mixed ID of the newly created link, or false on error
1759
     * @assert (null, null) === false
1760
     * @assert (1, null) === false
1761
     * @assert (null, 'abcdefabcdefabcdefabcdef') === false
1762
     */
1763
    public function copyRecordingToLinkTool($id)
1764
    {
1765
        if (empty($id)) {
1766
            return false;
1767
        }
1768
1769
        $em = Database::getManager();
1770
        /** @var ConferenceMeetingRepository $repo */
1771
        $repo = $em->getRepository(ConferenceMeeting::class);
1772
1773
        $meetingData = $repo->findOneAsArrayById((int) $id);
1774
        if (!$meetingData || empty($meetingData['remoteId'])) {
1775
            return false;
1776
        }
1777
1778
        $records = $this->api->getRecordingsWithXmlResponseArray([
1779
            'meetingId' => $meetingData['remoteId']
1780
        ]);
1781
1782
        if (!empty($records)) {
1783
            if (isset($records['message']) && !empty($records['message'])) {
1784
                if ($records['messageKey'] == 'noRecordings') {
1785
                    return false;
1786
                }
1787
            } else {
1788
                $record = $records[0];
1789
                if (is_array($record) && isset($record['recordId'])) {
1790
                    $url = $record['playbackFormatUrl'];
1791
                    $link = new \Link();
1792
                    $params = [
1793
                        'url' => $url,
1794
                        'title' => $meetingData['title'],
1795
                    ];
1796
                    $id = $link->save($params);
1797
1798
                    return $id;
1799
                }
1800
            }
1801
        }
1802
1803
        return false;
1804
    }
1805
1806
    /**
1807
     * Checks if the video conference server is running.
1808
     * Function currently disabled (always returns 1)
1809
     * @return bool True if server is running, false otherwise
1810
     * @assert () === false
1811
     */
1812
    public function isServerRunning()
1813
    {
1814
        return true;
1815
        //return BigBlueButtonBN::isServerRunning($this->protocol.$this->url);
1816
    }
1817
1818
    /**
1819
     * Checks if the video conference plugin is properly configured
1820
     * @return bool True if plugin has a host and a salt, false otherwise
1821
     * @assert () === false
1822
     */
1823
    public function isServerConfigured()
1824
    {
1825
        $host = $this->plugin->get('host');
1826
1827
        if (empty($host)) {
1828
            return false;
1829
        }
1830
1831
        $salt = $this->plugin->get('salt');
1832
1833
        if (empty($salt)) {
1834
            return false;
1835
        }
1836
1837
        return true;
1838
        //return BigBlueButtonBN::isServerRunning($this->protocol.$this->url);
1839
    }
1840
1841
    /**
1842
     * Get active session in the all platform
1843
     */
1844
    public function getActiveSessionsCount(): int
1845
    {
1846
        $em = Database::getManager();
1847
        $qb = $em->createQueryBuilder();
1848
1849
        $qb->select('COUNT(m.id)')
1850
            ->from(ConferenceMeeting::class, 'm')
1851
            ->where('m.status = :status')
1852
            ->andWhere('m.accessUrl = :accessUrl')
1853
            ->setParameter('status', 1)
1854
            ->setParameter('accessUrl', $this->accessUrl);
1855
1856
        return (int) $qb->getQuery()->getSingleScalarResult();
1857
    }
1858
1859
    /**
1860
     * Get active session in the all platform
1861
     */
1862
    public function getActiveSessions(): array
1863
    {
1864
        $em = Database::getManager();
1865
        $repo = $em->getRepository(ConferenceMeeting::class);
1866
1867
        $qb = $repo->createQueryBuilder('m')
1868
            ->where('m.status = :status')
1869
            ->andWhere('m.accessUrl = :accessUrl')
1870
            ->setParameter('status', 1)
1871
            ->setParameter('accessUrl', $this->accessUrl);
1872
1873
        return $qb->getQuery()->getArrayResult();
1874
    }
1875
1876
    /**
1877
     * @param string $url
1878
     */
1879
    public function redirectToBBB($url)
1880
    {
1881
        if (file_exists(__DIR__.'/../config.vm.php')) {
1882
            // Using VM
1883
            echo Display::url($this->plugin->get_lang('ClickToContinue'), $url);
1884
            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...
1885
        } else {
1886
            // Classic
1887
            header("Location: $url");
1888
            exit;
1889
        }
1890
    }
1891
1892
    /**
1893
     * @return string
1894
     */
1895
    public function getConferenceUrl()
1896
    {
1897
        return api_get_path(WEB_PLUGIN_PATH).'Bbb/start.php?launch=1&'.$this->getUrlParams();
1898
    }
1899
1900
    /**
1901
     * Get the meeting info from DB by its name
1902
     *
1903
     * @param string $name
1904
     *
1905
     * @return array
1906
     */
1907
    public function findMeetingByName(string $name): ?array
1908
    {
1909
        $em = Database::getManager();
1910
        /** @var ConferenceMeetingRepository $repo */
1911
        $repo = $em->getRepository(ConferenceMeeting::class);
1912
1913
        $qb = $repo->createQueryBuilder('m')
1914
            ->where('m.title = :name')
1915
            ->setParameter('name', $name)
1916
            ->setMaxResults(1);
1917
1918
        $result = $qb->getQuery()->getArrayResult();
1919
1920
        return $result[0] ?? null;
1921
    }
1922
1923
    /**
1924
     * Get the meeting info from DB by its name
1925
     *
1926
     * @param int $id
1927
     *
1928
     * @return array
1929
     */
1930
    public function getMeeting(int $id): ?array
1931
    {
1932
        $em = Database::getManager();
1933
        /** @var ConferenceMeetingRepository $repo */
1934
        $repo = $em->getRepository(ConferenceMeeting::class);
1935
1936
        return $repo->findOneAsArrayById($id);
1937
    }
1938
1939
    /**
1940
     * Get the meeting info.
1941
     *
1942
     * @param int $id
1943
     *
1944
     * @return array
1945
     */
1946
    public function getMeetingByRemoteId(string $id): ?array
1947
    {
1948
        $em = Database::getManager();
1949
        /** @var ConferenceMeetingRepository $repo */
1950
        $repo = $em->getRepository(ConferenceMeeting::class);
1951
1952
        return $repo->findOneByRemoteIdAndAccessUrl($id, $this->accessUrl);
1953
    }
1954
1955
    /**
1956
     * @param int $meetingId
1957
     *
1958
     * @return array
1959
     */
1960
    public function findConnectedMeetingParticipants(int $meetingId): array
1961
    {
1962
        $em = Database::getManager();
1963
        /** @var ConferenceActivityRepository $repo */
1964
        $repo = $em->getRepository(ConferenceActivity::class);
1965
1966
        $activities = $repo->createQueryBuilder('a')
1967
            ->where('a.meeting = :meetingId')
1968
            ->andWhere('a.inAt IS NOT NULL')
1969
            ->setParameter('meetingId', $meetingId)
1970
            ->getQuery()
1971
            ->getResult();
1972
1973
        $participantIds = [];
1974
        $return = [];
1975
1976
        foreach ($activities as $activity) {
1977
            $participant = $activity->getParticipant();
1978
            $participantId = $participant?->getId();
1979
1980
            if (!$participantId || in_array($participantId, $participantIds)) {
1981
                continue;
1982
            }
1983
1984
            $participantIds[] = $participantId;
1985
1986
            $return[] = [
1987
                'id' => $activity->getId(),
1988
                'meeting_id' => $meetingId,
1989
                'participant' => api_get_user_entity($participantId),
1990
                'in_at' => $activity->getInAt()?->format('Y-m-d H:i:s'),
1991
                'out_at' => $activity->getOutAt()?->format('Y-m-d H:i:s'),
1992
            ];
1993
        }
1994
1995
        return $return;
1996
    }
1997
1998
    /**
1999
     * Check if the meeting has a capture.m4v video file. If exists then the has_video_m4v field is updated
2000
     *
2001
     * @param int $meetingId
2002
     *
2003
     * @return bool
2004
     */
2005
    public function checkDirectMeetingVideoUrl(int $meetingId): bool
2006
    {
2007
        $em = Database::getManager();
2008
        /** @var ConferenceMeetingRepository $repo */
2009
        $repo = $em->getRepository(ConferenceMeeting::class);
2010
2011
        $meetingInfo = $repo->findOneAsArrayById($meetingId);
2012
2013
        if (empty($meetingInfo) || !isset($meetingInfo['videoUrl'])) {
2014
            return false;
2015
        }
2016
2017
        $hasCapture = SocialManager::verifyUrl($meetingInfo['videoUrl'].'/capture.m4v');
2018
2019
        if ($hasCapture) {
2020
            $qb = $em->createQueryBuilder();
2021
            $qb->update(ConferenceMeeting::class, 'm')
2022
                ->set('m.hasVideoM4v', ':value')
2023
                ->where('m.id = :id')
2024
                ->setParameter('value', true)
2025
                ->setParameter('id', $meetingId)
2026
                ->getQuery()
2027
                ->execute();
2028
2029
            return true;
2030
        }
2031
2032
        return false;
2033
    }
2034
}
2035