Passed
Push — 1.11.x ( 3e8c74...20d861 )
by Julito
13:55 queued 10s
created

bbb::showGlobalConferenceLink()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 15
nc 8
nop 1
dl 0
loc 26
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
/**
6
 * Class bbb
7
 * This script initiates a video conference session, calling the BigBlueButton
8
 * API BigBlueButton-Chamilo connector class
9
 */
10
class bbb
11
{
12
    public $url;
13
    public $salt;
14
    public $api;
15
    public $userCompleteName = '';
16
    public $protocol = 'http://';
17
    public $debug = false;
18
    public $logoutUrl = '';
19
    public $pluginEnabled = false;
20
    public $enableGlobalConference = false;
21
    public $enableGlobalConferencePerUser = false;
22
    public $isGlobalConference = false;
23
    public $groupSupport = false;
24
    public $userSupport = false;
25
    public $accessUrl = 1;
26
    public $userId = 0;
27
    public $plugin;
28
    private $courseCode;
29
    private $courseId;
30
    private $sessionId;
31
    private $groupId;
32
    private $maxUsersLimit;
33
34
    /**
35
     * Constructor (generates a connection to the API and the Chamilo settings
36
     * required for the connection to the video conference server)
37
     *
38
     * @param string $host
39
     * @param string $salt
40
     * @param bool   $isGlobalConference
41
     * @param int    $isGlobalPerUser
42
     */
43
    public function __construct(
44
        $host = '',
45
        $salt = '',
46
        $isGlobalConference = false,
47
        $isGlobalPerUser = 0
48
    ) {
49
        $this->courseCode = api_get_course_id();
50
        $this->courseId = api_get_course_int_id();
51
        $this->sessionId = api_get_session_id();
52
        $this->groupId = api_get_group_id();
53
54
        // Initialize video server settings from global settings
55
        $this->plugin = BBBPlugin::create();
56
        $bbbPluginEnabled = $this->plugin->get('tool_enable');
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

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

56
        /** @scrutinizer ignore-call */ 
57
        $bbbPluginEnabled = $this->plugin->get('tool_enable');

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...
57
58
        $bbb_host = !empty($host) ? $host : $this->plugin->get('host');
59
        $bbb_salt = !empty($salt) ? $salt : $this->plugin->get('salt');
60
61
        $this->table = Database::get_main_table('plugin_bbb_meeting');
62
        $this->enableGlobalConference = $this->plugin->get('enable_global_conference') === 'true';
63
        $this->isGlobalConference = (bool) $isGlobalConference;
64
65
        $columns = Database::listTableColumns($this->table);
66
        $this->groupSupport = isset($columns['group_id']) ? true : false;
67
        $this->userSupport = isset($columns['user_id']) ? true : false;
68
        $this->accessUrl = api_get_current_access_url_id();
69
70
        $this->enableGlobalConferencePerUser = false;
71
        if ($this->userSupport && !empty($isGlobalPerUser)) {
72
            $this->enableGlobalConferencePerUser = $this->plugin->get('enable_global_conference_per_user') === 'true';
73
            $this->userId = $isGlobalPerUser;
74
        }
75
76
        if ($this->groupSupport) {
77
            // Plugin check
78
            $this->groupSupport = $this->plugin->get('enable_conference_in_course_groups') === 'true' ? true : false;
79
            if ($this->groupSupport) {
80
                // Platform check
81
                $bbbSetting = api_get_setting('bbb_enable_conference_in_course_groups');
82
                $bbbSetting = isset($bbbSetting['bbb']) ? $bbbSetting['bbb'] === 'true' : false;
83
84
                if ($bbbSetting) {
85
                    // Course check
86
                    $courseInfo = api_get_course_info();
87
                    if ($courseInfo) {
88
                        $this->groupSupport = api_get_course_plugin_setting(
89
                                'bbb',
90
                                'bbb_enable_conference_in_groups',
91
                                $courseInfo
92
                            ) === '1';
93
                    }
94
                }
95
            }
96
        }
97
        $this->maxUsersLimit = $this->plugin->get('max_users_limit');
98
99
        if ($bbbPluginEnabled === 'true') {
100
            $userInfo = api_get_user_info();
101
            if (empty($userInfo) && !empty($isGlobalPerUser)) {
102
                // If we are following a link to a global "per user" conference
103
                // then generate a random guest name to join the conference
104
                // because there is no part of the process where we give a name
105
                //$this->userCompleteName = 'Guest'.rand(1000, 9999);
106
            } else {
107
                $this->userCompleteName = $userInfo['complete_name'];
108
            }
109
110
            if (api_is_anonymous()) {
111
                $this->userCompleteName = get_lang('Guest').'_'.rand(1000, 9999);
112
            }
113
114
            $this->salt = $bbb_salt;
115
            if (!empty($bbb_host)) {
116
                if (substr($bbb_host, -1, 1) !== '/') {
117
                    $bbb_host .= '/';
118
                }
119
                $this->url = $bbb_host;
120
                if (!preg_match('#/bigbluebutton/$#', $bbb_host)) {
121
                    $this->url = $bbb_host.'bigbluebutton/';
122
                }
123
            }
124
            $info = parse_url($bbb_host);
125
126
            if (isset($info['scheme'])) {
127
                $this->protocol = $info['scheme'].'://';
128
                $this->url = str_replace($this->protocol, '', $this->url);
129
                $urlWithProtocol = $bbb_host;
130
            } else {
131
                // We assume it's an http, if user wants to use https, the host *must* include the protocol.
132
                $this->protocol = 'http://';
133
                $urlWithProtocol = $this->protocol.$bbb_host;
134
            }
135
136
            // Setting BBB api
137
            define('CONFIG_SECURITY_SALT', $this->salt);
138
            define('CONFIG_SERVER_URL_WITH_PROTOCOL', $urlWithProtocol);
139
            define('CONFIG_SERVER_BASE_URL', $this->url);
140
            define('CONFIG_SERVER_PROTOCOL', $this->protocol);
141
142
            $this->api = new BigBlueButtonBN();
143
            $this->pluginEnabled = true;
144
            $this->logoutUrl = $this->getListingUrl();
145
        }
146
    }
147
148
    /**
149
     * @param int $courseId  Optional. Course ID.
150
     * @param int $sessionId Optional. Session ID.
151
     * @param int $groupId   Optional. Group ID.
152
     *
153
     * @return string
154
     */
155
    public function getListingUrl($courseId = 0, $sessionId = 0, $groupId = 0)
156
    {
157
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'
158
            .$this->getUrlParams($courseId, $sessionId, $groupId);
159
    }
160
161
    /**
162
     * @param int $courseId  Optional. Course ID.
163
     * @param int $sessionId Optional. Session ID.
164
     * @param int $groupId   Optional. Group ID.
165
     *
166
     * @return string
167
     */
168
    public function getUrlParams($courseId = 0, $sessionId = 0, $groupId = 0)
169
    {
170
        if (empty($this->courseCode) && !$courseId) {
171
            if ($this->isGlobalConferencePerUserEnabled()) {
172
                return 'global=1&user_id='.$this->userId;
173
            }
174
175
            if ($this->isGlobalConference()) {
176
                return 'global=1';
177
            }
178
179
            return '';
180
        }
181
182
        $courseCode = $this->courseCode;
183
        if (!empty($courseId)) {
184
            $course = api_get_course_info_by_id($courseId);
185
            if ($course) {
186
                $courseCode = $course['code'];
187
            }
188
        }
189
190
        return http_build_query(
191
            [
192
                'cidReq' => $courseCode,
193
                'id_session' => $sessionId ?: $this->sessionId,
194
                'gidReq' => $groupId ?: $this->groupId,
195
            ]
196
        );
197
    }
198
199
    /**
200
     * @return bool
201
     */
202
    public function isGlobalConferencePerUserEnabled()
203
    {
204
        return $this->enableGlobalConferencePerUser;
205
    }
206
207
    /**
208
     * @return bool
209
     */
210
    public function isGlobalConference()
211
    {
212
        if ($this->isGlobalConferenceEnabled() === false) {
213
            return false;
214
        }
215
216
        return (bool) $this->isGlobalConference;
217
    }
218
219
    /**
220
     * @return bool
221
     */
222
    public function isGlobalConferenceEnabled()
223
    {
224
        return $this->enableGlobalConference;
225
    }
226
227
    /**
228
     * @param array $userInfo
229
     *
230
     * @return bool
231
     */
232
    public static function showGlobalConferenceLink($userInfo)
233
    {
234
        if (empty($userInfo)) {
235
            return false;
236
        }
237
        $setting = api_get_plugin_setting('bbb', 'enable_global_conference');
238
        $settingLink = api_get_plugin_setting('bbb', 'enable_global_conference_link');
239
        if ($setting === 'true' && $settingLink === 'true') {
240
            //$content = Display::url(get_lang('LaunchVideoConferenceRoom'), $url);
241
            $allowedRoles = api_get_plugin_setting(
242
                'bbb',
243
                'global_conference_allow_roles'
244
            );
245
246
            if (api_is_platform_admin()) {
247
                $userInfo['status'] = PLATFORM_ADMIN;
248
            }
249
250
            $showGlobalLink = true;
251
            if (!empty($allowedRoles)) {
252
                if (!in_array($userInfo['status'], $allowedRoles)) {
253
                    $showGlobalLink = false;
254
                }
255
            }
256
257
            return $showGlobalLink;
258
        }
259
    }
260
261
    /**
262
     * Gets the global limit of users in a video-conference room.
263
     * This value can be overridden by course-specific values
264
     * @return  int Maximum number of users set globally
265
     */
266
    public function getMaxUsersLimit()
267
    {
268
        $limit = $this->maxUsersLimit;
269
        if ($limit <= 0) {
270
            $limit = 0;
271
        }
272
        $courseLimit = 0;
273
        $sessionLimit = 0;
274
        // Check the extra fields for this course and session
275
        // Session limit takes priority over course limit
276
        // Course limit takes priority over global limit
277
        if (!empty($this->courseId)) {
278
            $extraField = new ExtraField('course');
279
            $fieldId = $extraField->get_all(
280
                array('variable = ?' => 'plugin_bbb_course_users_limit')
281
            );
282
            $extraValue = new ExtraFieldValue('course');
283
            $value = $extraValue->get_values_by_handler_and_field_id($this->courseId, $fieldId[0]['id']);
284
            if (!empty($value['value'])) {
285
                $courseLimit = (int) $value['value'];
286
            }
287
        }
288
        if (!empty($this->sessionId)) {
289
            $extraField = new ExtraField('session');
290
            $fieldId = $extraField->get_all(
291
                array('variable = ?' => 'plugin_bbb_session_users_limit')
292
            );
293
            $extraValue = new ExtraFieldValue('session');
294
            $value = $extraValue->get_values_by_handler_and_field_id($this->sessionId, $fieldId[0]['id']);
295
            if (!empty($value['value'])) {
296
                $sessionLimit = (int) $value['value'];
297
            }
298
        }
299
300
        if (!empty($sessionLimit)) {
301
            return $sessionLimit;
302
        } elseif (!empty($courseLimit)) {
303
            return $courseLimit;
304
        }
305
306
        return (int) $limit;
307
    }
308
309
    /**
310
     * Sets the global limit of users in a video-conference room.
311
     *
312
     * @param int Maximum number of users (globally)
313
     */
314
    public function setMaxUsersLimit($max)
315
    {
316
        if ($max < 0) {
317
            $max = 0;
318
        }
319
        $this->maxUsersLimit = (int) $max;
320
    }
321
322
    /**
323
     * See this file in you BBB to set up default values
324
     *
325
     * @param array $params Array of parameters that will be completed if not containing all expected variables
326
     *
327
     * /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
328
     *
329
     * More record information:
330
     * http://code.google.com/p/bigbluebutton/wiki/RecordPlaybackSpecification
331
     *
332
     * Default maximum number of users a meeting can have.
333
     * Doesn't get enforced yet but is the default value when the create
334
     * API doesn't pass a value.
335
     * defaultMaxUsers=20
336
     *
337
     * Default duration of the meeting in minutes.
338
     * Current default is 0 (meeting doesn't end).
339
     * defaultMeetingDuration=0
340
     *
341
     * Remove the meeting from memory when the end API is called.
342
     * This allows 3rd-party apps to recycle the meeting right-away
343
     * instead of waiting for the meeting to expire (see below).
344
     * removeMeetingWhenEnded=false
345
     *
346
     * The number of minutes before the system removes the meeting from memory.
347
     * defaultMeetingExpireDuration=1
348
     *
349
     * The number of minutes the system waits when a meeting is created and when
350
     * a user joins. If after this period, a user hasn't joined, the meeting is
351
     * removed from memory.
352
     * defaultMeetingCreateJoinDuration=5
353
     *
354
     * @return mixed
355
     */
356
    public function createMeeting($params)
357
    {
358
        $params['c_id'] = api_get_course_int_id();
359
        $params['session_id'] = api_get_session_id();
360
361
        if ($this->hasGroupSupport()) {
362
            $params['group_id'] = api_get_group_id();
363
        }
364
365
        if ($this->isGlobalConferencePerUserEnabled() && !empty($this->userId)) {
366
            $params['user_id'] = (int) $this->userId;
367
        }
368
369
        $params['attendee_pw'] = isset($params['attendee_pw']) ? $params['attendee_pw'] : $this->getUserMeetingPassword();
370
        $attendeePassword = $params['attendee_pw'];
371
        $params['moderator_pw'] = isset($params['moderator_pw']) ? $params['moderator_pw'] : $this->getModMeetingPassword();
372
        $moderatorPassword = $params['moderator_pw'];
373
374
        $params['record'] = api_get_course_plugin_setting('bbb', 'big_blue_button_record_and_store') == 1;
375
        $max = api_get_course_plugin_setting('bbb', 'big_blue_button_max_students_allowed');
376
        $max = isset($max) ? $max : -1;
377
378
        $params['status'] = 1;
379
        // Generate a pseudo-global-unique-id to avoid clash of conferences on
380
        // the same BBB server with several Chamilo portals
381
        $params['remote_id'] = uniqid(true, true);
382
        // Each simultaneous conference room needs to have a different
383
        // voice_bridge composed of a 5 digits number, so generating a random one
384
        $params['voice_bridge'] = rand(10000, 99999);
385
        $params['created_at'] = api_get_utc_datetime();
386
        $params['access_url'] = $this->accessUrl;
387
388
        // Check interface feature is installed
389
        $interfaceFeature = $this->plugin->get('interface');
390
        if ($interfaceFeature === false) {
391
            if (isset($params['interface'])) {
392
                unset($params['interface']);
393
            }
394
        }
395
396
        $id = Database::insert($this->table, $params);
397
398
        if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
399
            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

399
            Event::/** @scrutinizer ignore-call */ 
400
                   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...
400
                'bbb_create_meeting',
401
                'meeting_id',
402
                (int) $id,
403
                null,
404
                api_get_user_id(),
405
                api_get_course_int_id(),
406
                api_get_session_id()
407
            );
408
409
            $meetingName = isset($params['meeting_name']) ? $params['meeting_name'] : $this->getCurrentVideoConferenceName();
410
            $welcomeMessage = isset($params['welcome_msg']) ? $params['welcome_msg'] : null;
411
            $record = isset($params['record']) && $params['record'] ? 'true' : 'false';
412
            //$duration = isset($params['duration']) ? intval($params['duration']) : 0;
413
            // This setting currently limits the maximum conference duration,
414
            // to avoid lingering sessions on the video-conference server #6261
415
            $duration = 300;
416
            $meetingDuration = (int) $this->plugin->get('meeting_duration');
417
            if (!empty($meetingDuration)) {
418
                $duration = $meetingDuration;
419
            }
420
            $bbbParams = array(
421
                'meetingId' => $params['remote_id'], // REQUIRED
422
                'meetingName' => $meetingName, // REQUIRED
423
                'attendeePw' => $attendeePassword, // Match this value in getJoinMeetingURL() to join as attendee.
424
                'moderatorPw' => $moderatorPassword, // Match this value in getJoinMeetingURL() to join as moderator.
425
                'welcomeMsg' => $welcomeMessage, // ''= use default. Change to customize.
426
                'dialNumber' => '', // The main number to call into. Optional.
427
                'voiceBridge' => $params['voice_bridge'], // PIN to join voice. Required.
428
                'webVoice' => '', // Alphanumeric to join voice. Optional.
429
                'logoutUrl' => $this->logoutUrl.'&action=logout&remote_id='.$params['remote_id'],
430
                'maxParticipants' => $max, // Optional. -1 = unlimitted. Not supported in BBB. [number]
431
                'record' => $record, // New. 'true' will tell BBB to record the meeting.
432
                'duration' => $duration, // Default = 0 which means no set duration in minutes. [number]
433
                //'meta_category' => '',  // Use to pass additional info to BBB server. See API docs.
434
            );
435
436
            $status = false;
437
            $meeting = null;
438
            while ($status === false) {
439
                $result = $this->api->createMeetingWithXmlResponseArray($bbbParams);
440
                if (isset($result) && strval($result['returncode']) == 'SUCCESS') {
441
                    if ($this->plugin->get('allow_regenerate_recording') === 'true') {
442
                        $internalId = Database::escape_string($result['internalMeetingID']);
443
                        $sql = "UPDATE $this->table SET internal_meeting_id = '".$internalId."'
444
                                WHERE id = $id";
445
                        Database::query($sql);
446
                    }
447
                    $meeting = $this->joinMeeting($meetingName, true);
448
449
                    return $meeting;
450
                }
451
            }
452
        }
453
454
        return false;
455
    }
456
457
    /**
458
     * @return bool
459
     */
460
    public function hasGroupSupport()
461
    {
462
        return $this->groupSupport;
463
    }
464
465
    /**
466
     * Gets the password for a specific meeting for the current user
467
     *
468
     * @param string $courseCode
469
     *
470
     * @return string A moderator password if user is teacher, or the course code otherwise
471
     *
472
     */
473
    public function getUserMeetingPassword($courseCode = null)
474
    {
475
        if ($this->isGlobalConferencePerUserEnabled()) {
476
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
477
        }
478
479
        if ($this->isGlobalConference()) {
480
            return 'url_'.api_get_current_access_url_id();
481
        }
482
        $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
483
484
        return $courseCode;
485
    }
486
487
    /**
488
     * Generated a moderator password for the meeting.
489
     *
490
     * @param string $courseCode
491
     *
492
     * @return string A password for the moderation of the videoconference
493
     */
494
    public function getModMeetingPassword($courseCode = null)
495
    {
496
        if ($this->isGlobalConferencePerUserEnabled()) {
497
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id().'_mod';
498
        }
499
500
        if ($this->isGlobalConference()) {
501
            return 'url_'.api_get_current_access_url_id().'_mod';
502
        }
503
504
        $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
505
506
        return $courseCode.'mod';
507
    }
508
509
    /**
510
     * @return string
511
     */
512
    public function getCurrentVideoConferenceName()
513
    {
514
        if ($this->isGlobalConferencePerUserEnabled()) {
515
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
516
        }
517
518
        if ($this->isGlobalConference()) {
519
            return 'url_'.api_get_current_access_url_id();
520
        }
521
522
        if ($this->hasGroupSupport()) {
523
            return api_get_course_id().'-'.api_get_session_id().'-'.api_get_group_id();
524
        }
525
526
        return api_get_course_id().'-'.api_get_session_id();
527
    }
528
529
    /**
530
     * Returns a meeting "join" URL
531
     *
532
     * @param string The name of the meeting (usually the course code)
533
     *
534
     * @return mixed The URL to join the meeting, or false on error
535
     * @todo implement moderator pass
536
     * @assert ('') === false
537
     * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false
538
     */
539
    public function joinMeeting($meetingName)
540
    {
541
        if ($this->debug) {
542
            error_log("joinMeeting: $meetingName");
543
        }
544
545
        if (empty($meetingName)) {
546
            return false;
547
        }
548
549
        $manager = $this->isConferenceManager();
550
        if ($manager) {
551
            $pass = $this->getModMeetingPassword();
552
        } else {
553
            $pass = $this->getUserMeetingPassword();
554
        }
555
556
        $meetingData = Database::select(
557
            '*',
558
            $this->table,
559
            array(
560
                'where' => array(
561
                    'meeting_name = ? AND status = 1 AND access_url = ?' => array(
562
                        $meetingName,
563
                        $this->accessUrl,
564
                    ),
565
                ),
566
            ),
567
            'first'
568
        );
569
570
        if (empty($meetingData) || !is_array($meetingData)) {
571
            if ($this->debug) {
572
                error_log("meeting does not exist: $meetingName");
573
            }
574
575
            return false;
576
        }
577
578
        $params = array(
579
            'meetingId' => $meetingData['remote_id'],
580
            //  -- REQUIRED - The unique id for the meeting
581
            'password' => $this->getModMeetingPassword()
582
            //  -- REQUIRED - The moderator password for the meeting
583
        );
584
585
        $meetingInfoExists = false;
586
        $meetingIsRunningInfo = $this->getMeetingInfo($params);
587
        if ($this->debug) {
588
            error_log('Searching meeting with params:');
589
            error_log(print_r($params, 1));
590
            error_log('Result:');
591
            error_log(print_r($meetingIsRunningInfo, 1));
592
        }
593
594
        if ($meetingIsRunningInfo === false) {
595
            // checking with the remote_id didn't work, so just in case and
596
            // to provide backwards support, check with the id
597
            $params = array(
598
                'meetingId' => $meetingData['id'],
599
                //  -- REQUIRED - The unique id for the meeting
600
                'password' => $this->getModMeetingPassword()
601
                //  -- REQUIRED - The moderator password for the meeting
602
            );
603
            $meetingIsRunningInfo = $this->getMeetingInfo($params);
604
            if ($this->debug) {
605
                error_log('Searching meetingId with params:');
606
                error_log(print_r($params, 1));
607
                error_log('Result:');
608
                error_log(print_r($meetingIsRunningInfo, 1));
609
            }
610
        }
611
612
        if (strval($meetingIsRunningInfo['returncode']) === 'SUCCESS' &&
613
            isset($meetingIsRunningInfo['meetingName']) &&
614
            !empty($meetingIsRunningInfo['meetingName'])
615
        ) {
616
            $meetingInfoExists = true;
617
        }
618
619
        if ($this->debug) {
620
            error_log(
621
                "meeting is running: ".intval($meetingInfoExists)
622
            );
623
        }
624
625
        $url = false;
626
        if ($meetingInfoExists) {
627
            $joinParams = [
628
                'meetingId' => $meetingData['remote_id'],
629
                //	-- REQUIRED - A unique id for the meeting
630
                'username' => $this->userCompleteName,
631
                //-- REQUIRED - The name that will display for the user in the meeting
632
                'password' => $pass,
633
                //-- REQUIRED - The attendee or moderator password, depending on what's passed here
634
                //'createTime' => api_get_utc_datetime(),			//-- OPTIONAL - string. Leave blank ('') unless you set this correctly.
635
                'userID' => api_get_user_id(),
636
                //-- OPTIONAL - string
637
                'webVoiceConf' => '',
638
                //	-- OPTIONAL - string
639
                'interface' => $this->checkInterface($meetingData),
640
            ];
641
            $url = $this->api->getJoinMeetingURL($joinParams);
642
            $url = $this->protocol.$url;
643
        }
644
645
        if ($this->debug) {
646
            error_log("return url :".$url);
0 ignored issues
show
Bug introduced by
Are you sure $url of type false|string 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

646
            error_log("return url :"./** @scrutinizer ignore-type */ $url);
Loading history...
647
        }
648
649
        return $url;
650
    }
651
652
    /**
653
     * Checks whether a user is teacher in the current course
654
     * @return bool True if the user can be considered a teacher in this course, false otherwise
655
     */
656
    public function isConferenceManager()
657
    {
658
        if (api_is_coach() || api_is_platform_admin(false, true)) {
659
            return true;
660
        }
661
662
        if ($this->isGlobalConferencePerUserEnabled()) {
663
            $currentUserId = api_get_user_id();
664
            if ($this->userId === $currentUserId) {
665
                return true;
666
            } else {
667
                return false;
668
            }
669
        }
670
671
        $courseInfo = api_get_course_info();
672
        $groupId = api_get_group_id();
673
        if (!empty($groupId) && !empty($courseInfo)) {
674
            $groupEnabled = api_get_course_plugin_setting('bbb', 'bbb_enable_conference_in_groups') === '1';
675
            if ($groupEnabled) {
676
                $studentCanStartConference = api_get_course_plugin_setting(
677
                        'bbb',
678
                        'big_blue_button_students_start_conference_in_groups'
679
                    ) === '1';
680
681
                if ($studentCanStartConference) {
682
                    $isSubscribed = GroupManager::is_user_in_group(
683
                        api_get_user_id(),
684
                        GroupManager::get_group_properties($groupId)
685
                    );
686
                    if ($isSubscribed) {
687
                        return true;
688
                    }
689
                }
690
            }
691
        }
692
693
694
        if (!empty($courseInfo)) {
695
            return api_is_course_admin();
696
        }
697
698
        return false;
699
    }
700
701
    /**
702
     * Get information about the given meeting
703
     *
704
     * @param array ...?
705
     *
706
     * @return mixed Array of information on success, false on error
707
     * @assert (array()) === false
708
     */
709
    public function getMeetingInfo($params)
710
    {
711
        try {
712
            $result = $this->api->getMeetingInfoWithXmlResponseArray($params);
713
            if ($result == null) {
714
                if ($this->debug) {
715
                    error_log("Failed to get any response. Maybe we can't contact the BBB server.");
716
                }
717
            }
718
719
            return $result;
720
        } catch (Exception $e) {
721
            if ($this->debug) {
722
                error_log('Caught exception: ', $e->getMessage(), "\n");
723
            }
724
        }
725
726
        return false;
727
    }
728
729
    /**
730
     * @param $meetingInfo
731
     *
732
     * @return int
733
     */
734
    public function checkInterface($meetingInfo)
735
    {
736
        $interface = BBBPlugin::LAUNCH_TYPE_DEFAULT;
737
738
        $type = $this->plugin->get('launch_type');
739
        switch ($type) {
740
            case BBBPlugin::LAUNCH_TYPE_DEFAULT:
741
                $interface = $this->plugin->get('interface');
742
                break;
743
            case BBBPlugin::LAUNCH_TYPE_SET_BY_TEACHER:
744
                if (isset($meetingInfo['interface'])) {
745
                    $interface = $meetingInfo['interface'];
746
                }
747
                break;
748
            case BBBPlugin::LAUNCH_TYPE_SET_BY_STUDENT:
749
                if (isset($meetingInfo['id'])) {
750
                    $roomInfo = $this->getMeetingParticipantInfo($meetingInfo['id'], api_get_user_id());
751
                    if (!empty($roomInfo) && isset($roomInfo['interface'])) {
752
                        $interface = $roomInfo['interface'];
753
                    } else {
754
                        if (isset($_REQUEST['interface'])) {
755
                            $interface = isset($_REQUEST['interface']) ? (int) $_REQUEST['interface'] : 0;
756
                        }
757
                    }
758
                }
759
                break;
760
        }
761
762
        return $interface;
763
    }
764
765
    /**
766
     * @param int $meetingId
767
     * @param int $userId
768
     *
769
     * @return array
770
     */
771
    public function getMeetingParticipantInfo($meetingId, $userId)
772
    {
773
        $meetingData = Database::select(
774
            '*',
775
            'plugin_bbb_room',
776
            array('where' => array('meeting_id = ? AND participant_id = ?' => [$meetingId, $userId])),
777
            'first'
778
        );
779
780
        if ($meetingData) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $meetingData of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
781
            return $meetingData;
782
        }
783
784
        return [];
785
    }
786
787
    /**
788
     * Save a participant in a meeting room
789
     *
790
     * @param int $meetingId
791
     * @param int $participantId
792
     * @param int $interface
793
     *
794
     * @return false|int The last inserted ID. Otherwise return false
795
     */
796
    public function saveParticipant($meetingId, $participantId, $interface = 0)
797
    {
798
        $meetingData = Database::select(
799
            '*',
800
            'plugin_bbb_room',
801
            [
802
                'where' => [
803
                    'meeting_id = ? AND participant_id = ? AND close = ?' => [
804
                        $meetingId,
805
                        $participantId,
806
                        BBBPlugin::ROOM_OPEN,
807
                    ],
808
                ],
809
            ]
810
        );
811
812
        foreach ($meetingData as $roomItem) {
813
            $inAt = $roomItem['in_at'];
814
            $outAt = $roomItem['out_at'];
815
            $roomId = $roomItem['id'];
816
            if (!empty($roomId)) {
817
                if ($inAt != $outAt) {
818
                    Database::update(
819
                        'plugin_bbb_room',
820
                        ['close' => BBBPlugin::ROOM_CLOSE],
821
                        ['id = ? ' => $roomId]
822
                    );
823
                } else {
824
                    Database::update(
825
                        'plugin_bbb_room',
826
                        ['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_CLOSE],
827
                        ['id = ? ' => $roomId]
828
                    );
829
                }
830
            }
831
        }
832
833
        $params = [
834
            'meeting_id' => $meetingId,
835
            'participant_id' => $participantId,
836
            'in_at' => api_get_utc_datetime(),
837
            'out_at' => api_get_utc_datetime(),
838
            'close' => BBBPlugin::ROOM_OPEN,
839
        ];
840
841
        if ($this->plugin->get('interface') !== false) {
842
            $params['interface'] = $interface;
843
        }
844
845
        return Database::insert(
846
            'plugin_bbb_room',
847
            $params
848
        );
849
    }
850
851
    /**
852
     * Tells whether the given meeting exists and is running
853
     * (using course code as name)
854
     *
855
     * @param string $meetingName Meeting name (usually the course code)
856
     *
857
     * @return bool True if meeting exists, false otherwise
858
     * @assert ('') === false
859
     * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false
860
     */
861
    public function meetingExists($meetingName)
862
    {
863
        $meetingData = $this->getMeetingByName($meetingName);
864
865
        return !empty($meetingData);
866
    }
867
868
    /**
869
     * @param string $meetingName
870
     *
871
     * @return array
872
     */
873
    public function getMeetingByName($meetingName)
874
    {
875
        if (empty($meetingName)) {
876
            return [];
877
        }
878
879
        $courseId = api_get_course_int_id();
880
        $sessionId = api_get_session_id();
881
        $conditions = array(
882
            'where' => array(
883
                'c_id = ? AND session_id = ? AND meeting_name = ? AND status = 1 AND access_url = ?' =>
884
                    array($courseId, $sessionId, $meetingName, $this->accessUrl),
885
            ),
886
        );
887
888
        if ($this->hasGroupSupport()) {
889
            $groupId = api_get_group_id();
890
            $conditions = array(
891
                'where' => array(
892
                    'c_id = ? AND session_id = ? AND meeting_name = ? AND group_id = ? AND status = 1 AND access_url = ?' =>
893
                        array(
894
                            $courseId,
895
                            $sessionId,
896
                            $meetingName,
897
                            $groupId,
898
                            $this->accessUrl,
899
                        ),
900
                ),
901
            );
902
        }
903
904
        $meetingData = Database::select(
905
            '*',
906
            $this->table,
907
            $conditions,
908
            'first'
909
        );
910
911
        if ($this->debug) {
912
            error_log('meeting_exists '.print_r($meetingData, 1));
0 ignored issues
show
Bug introduced by
Are you sure print_r($meetingData, 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

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