Passed
Push — preprodparkur ( 6ca08d...92eeef )
by Julito
21:33
created

bbb::isGlobalConferenceEnabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
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
                $urlWithProtocol = 'http://'.$bbb_host;
133
            }
134
135
            // Setting BBB api
136
            define('CONFIG_SECURITY_SALT', $this->salt);
137
            define('CONFIG_SERVER_URL_WITH_PROTOCOL', $urlWithProtocol);
138
            define('CONFIG_SERVER_BASE_URL', $this->url);
139
140
            $this->api = new BigBlueButtonBN();
141
            $this->pluginEnabled = true;
142
            $this->logoutUrl = $this->getListingUrl();
143
        }
144
    }
145
146
    /**
147
     * @param int $courseId  Optional. Course ID.
148
     * @param int $sessionId Optional. Session ID.
149
     * @param int $groupId   Optional. Group ID.
150
     *
151
     * @return string
152
     */
153
    public function getListingUrl($courseId = 0, $sessionId = 0, $groupId = 0)
154
    {
155
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'
156
            .$this->getUrlParams($courseId, $sessionId, $groupId);
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 getUrlParams($courseId = 0, $sessionId = 0, $groupId = 0)
167
    {
168
        if (empty($this->courseCode) && !$courseId) {
169
            if ($this->isGlobalConferencePerUserEnabled()) {
170
                return 'global=1&user_id='.$this->userId;
171
            }
172
173
            if ($this->isGlobalConference()) {
174
                return 'global=1';
175
            }
176
177
            return '';
178
        }
179
180
        $courseCode = $this->courseCode;
181
        if (!empty($courseId)) {
182
            $course = api_get_course_info_by_id($courseId);
183
            if ($course) {
184
                $courseCode = $course['code'];
185
            }
186
        }
187
188
        return http_build_query(
189
            [
190
                'cidReq' => $courseCode,
191
                'id_session' => $sessionId ?: $this->sessionId,
192
                'gidReq' => $groupId ?: $this->groupId,
193
            ]
194
        );
195
    }
196
197
    /**
198
     * @return bool
199
     */
200
    public function isGlobalConferencePerUserEnabled()
201
    {
202
        return $this->enableGlobalConferencePerUser;
203
    }
204
205
    /**
206
     * @return bool
207
     */
208
    public function isGlobalConference()
209
    {
210
        if ($this->isGlobalConferenceEnabled() === false) {
211
            return false;
212
        }
213
214
        return (bool) $this->isGlobalConference;
215
    }
216
217
    /**
218
     * @return bool
219
     */
220
    public function isGlobalConferenceEnabled()
221
    {
222
        return $this->enableGlobalConference;
223
    }
224
225
    /**
226
     * @param array $userInfo
227
     *
228
     * @return bool
229
     */
230
    public static function showGlobalConferenceLink($userInfo)
231
    {
232
        if (empty($userInfo)) {
233
            return false;
234
        }
235
        $setting = api_get_plugin_setting('bbb', 'enable_global_conference');
236
        $settingLink = api_get_plugin_setting('bbb', 'enable_global_conference_link');
237
        if ($setting === 'true' && $settingLink === 'true') {
238
            //$content = Display::url(get_lang('LaunchVideoConferenceRoom'), $url);
239
            $allowedRoles = api_get_plugin_setting(
240
                'bbb',
241
                'global_conference_allow_roles'
242
            );
243
244
            if (api_is_platform_admin()) {
245
                $userInfo['status'] = PLATFORM_ADMIN;
246
            }
247
248
            $showGlobalLink = true;
249
            if (!empty($allowedRoles)) {
250
                if (!in_array($userInfo['status'], $allowedRoles)) {
251
                    $showGlobalLink = false;
252
                }
253
            }
254
255
            return $showGlobalLink;
256
        }
257
    }
258
259
    /**
260
     * Gets the global limit of users in a video-conference room.
261
     * This value can be overridden by course-specific values
262
     * @return  int Maximum number of users set globally
263
     */
264
    public function getMaxUsersLimit()
265
    {
266
        $limit = $this->maxUsersLimit;
267
        if ($limit <= 0) {
268
            $limit = 0;
269
        }
270
        $courseLimit = 0;
271
        $sessionLimit = 0;
272
        // Check the extra fields for this course and session
273
        // Session limit takes priority over course limit
274
        // Course limit takes priority over global limit
275
        if (!empty($this->courseId)) {
276
            $extraField = new ExtraField('course');
277
            $fieldId = $extraField->get_all(
278
                array('variable = ?' => 'plugin_bbb_course_users_limit')
279
            );
280
            $extraValue = new ExtraFieldValue('course');
281
            $value = $extraValue->get_values_by_handler_and_field_id($this->courseId, $fieldId[0]['id']);
282
            if (!empty($value['value'])) {
283
                $courseLimit = (int) $value['value'];
284
            }
285
        }
286
        if (!empty($this->sessionId)) {
287
            $extraField = new ExtraField('session');
288
            $fieldId = $extraField->get_all(
289
                array('variable = ?' => 'plugin_bbb_session_users_limit')
290
            );
291
            $extraValue = new ExtraFieldValue('session');
292
            $value = $extraValue->get_values_by_handler_and_field_id($this->sessionId, $fieldId[0]['id']);
293
            if (!empty($value['value'])) {
294
                $sessionLimit = (int) $value['value'];
295
            }
296
        }
297
298
        if (!empty($sessionLimit)) {
299
            return $sessionLimit;
300
        } elseif (!empty($courseLimit)) {
301
            return $courseLimit;
302
        }
303
304
        return (int) $limit;
305
    }
306
307
    /**
308
     * Sets the global limit of users in a video-conference room.
309
     *
310
     * @param int Maximum number of users (globally)
311
     */
312
    public function setMaxUsersLimit($max)
313
    {
314
        if ($max < 0) {
315
            $max = 0;
316
        }
317
        $this->maxUsersLimit = (int) $max;
318
    }
319
320
    /**
321
     * See this file in you BBB to set up default values
322
     *
323
     * @param array $params Array of parameters that will be completed if not containing all expected variables
324
     *
325
     * /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
326
     *
327
     * More record information:
328
     * http://code.google.com/p/bigbluebutton/wiki/RecordPlaybackSpecification
329
     *
330
     * Default maximum number of users a meeting can have.
331
     * Doesn't get enforced yet but is the default value when the create
332
     * API doesn't pass a value.
333
     * defaultMaxUsers=20
334
     *
335
     * Default duration of the meeting in minutes.
336
     * Current default is 0 (meeting doesn't end).
337
     * defaultMeetingDuration=0
338
     *
339
     * Remove the meeting from memory when the end API is called.
340
     * This allows 3rd-party apps to recycle the meeting right-away
341
     * instead of waiting for the meeting to expire (see below).
342
     * removeMeetingWhenEnded=false
343
     *
344
     * The number of minutes before the system removes the meeting from memory.
345
     * defaultMeetingExpireDuration=1
346
     *
347
     * The number of minutes the system waits when a meeting is created and when
348
     * a user joins. If after this period, a user hasn't joined, the meeting is
349
     * removed from memory.
350
     * defaultMeetingCreateJoinDuration=5
351
     *
352
     * @return mixed
353
     */
354
    public function createMeeting($params)
355
    {
356
        $params['c_id'] = api_get_course_int_id();
357
        $params['session_id'] = api_get_session_id();
358
359
        if ($this->hasGroupSupport()) {
360
            $params['group_id'] = api_get_group_id();
361
        }
362
363
        if ($this->isGlobalConferencePerUserEnabled() && !empty($this->userId)) {
364
            $params['user_id'] = (int) $this->userId;
365
        }
366
367
        $params['attendee_pw'] = isset($params['attendee_pw']) ? $params['attendee_pw'] : $this->getUserMeetingPassword();
368
        $attendeePassword = $params['attendee_pw'];
369
        $params['moderator_pw'] = isset($params['moderator_pw']) ? $params['moderator_pw'] : $this->getModMeetingPassword();
370
        $moderatorPassword = $params['moderator_pw'];
371
372
        $params['record'] = api_get_course_plugin_setting('bbb', 'big_blue_button_record_and_store') == 1 ? true : false;
373
        $max = api_get_course_plugin_setting('bbb', 'big_blue_button_max_students_allowed');
374
        $max = isset($max) ? $max : -1;
375
376
        $params['status'] = 1;
377
        // Generate a pseudo-global-unique-id to avoid clash of conferences on
378
        // the same BBB server with several Chamilo portals
379
        $params['remote_id'] = uniqid(true, true);
380
        // Each simultaneous conference room needs to have a different
381
        // voice_bridge composed of a 5 digits number, so generating a random one
382
        $params['voice_bridge'] = rand(10000, 99999);
383
        $params['created_at'] = api_get_utc_datetime();
384
        $params['access_url'] = $this->accessUrl;
385
386
        // Check interface feature is installed
387
        $interfaceFeature = $this->plugin->get('interface');
388
        if ($interfaceFeature === false) {
389
            if (isset($params['interface'])) {
390
                unset($params['interface']);
391
            }
392
        }
393
394
        $id = Database::insert($this->table, $params);
395
396
        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...
397
            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

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

640
            error_log("return url :"./** @scrutinizer ignore-type */ $url);
Loading history...
641
        }
642
643
        return $url;
644
    }
645
646
    /**
647
     * Checks whether a user is teacher in the current course
648
     * @return bool True if the user can be considered a teacher in this course, false otherwise
649
     */
650
    public function isConferenceManager()
651
    {
652
        if (api_is_coach() || api_is_platform_admin()) {
653
            return true;
654
        }
655
656
        if ($this->isGlobalConferencePerUserEnabled()) {
657
            $currentUserId = api_get_user_id();
658
            if ($this->userId === $currentUserId) {
659
                return true;
660
            } else {
661
                return false;
662
            }
663
        }
664
665
        $courseInfo = api_get_course_info();
666
        $groupId = api_get_group_id();
667
668
        if (!empty($groupId) && !empty($courseInfo)) {
669
            $groupEnabled = api_get_course_plugin_setting('bbb', 'bbb_enable_conference_in_groups') === '1';
670
            if ($groupEnabled) {
671
                $studentCanStartConference = api_get_course_plugin_setting(
672
                        'bbb',
673
                        'big_blue_button_students_start_conference_in_groups'
674
                    ) === '1';
675
676
                if ($studentCanStartConference) {
677
                    $isSubscribed = GroupManager::is_user_in_group(
678
                        api_get_user_id(),
679
                        GroupManager::get_group_properties($groupId)
680
                    );
681
                    if ($isSubscribed) {
682
                        return true;
683
                    }
684
                }
685
            }
686
        }
687
688
        if (!empty($courseInfo)) {
689
            return api_is_course_admin();
690
        }
691
692
        return false;
693
    }
694
695
    /**
696
     * Get information about the given meeting
697
     *
698
     * @param array ...?
699
     *
700
     * @return mixed Array of information on success, false on error
701
     * @assert (array()) === false
702
     */
703
    public function getMeetingInfo($params)
704
    {
705
        try {
706
            $result = $this->api->getMeetingInfoWithXmlResponseArray($params);
707
            if ($result == null) {
708
                if ($this->debug) {
709
                    error_log("Failed to get any response. Maybe we can't contact the BBB server.");
710
                }
711
            }
712
713
            return $result;
714
        } catch (Exception $e) {
715
            if ($this->debug) {
716
                error_log('Caught exception: ', $e->getMessage(), "\n");
717
            }
718
        }
719
720
        return false;
721
    }
722
723
    /**
724
     * @param $meetingInfo
725
     *
726
     * @return int
727
     */
728
    public function checkInterface($meetingInfo)
729
    {
730
        $interface = BBBPlugin::LAUNCH_TYPE_DEFAULT;
731
732
        $type = $this->plugin->get('launch_type');
733
        switch ($type) {
734
            case BBBPlugin::LAUNCH_TYPE_DEFAULT:
735
                $interface = $this->plugin->get('interface');
736
                break;
737
            case BBBPlugin::LAUNCH_TYPE_SET_BY_TEACHER:
738
                if (isset($meetingInfo['interface'])) {
739
                    $interface = $meetingInfo['interface'];
740
                }
741
                break;
742
            case BBBPlugin::LAUNCH_TYPE_SET_BY_STUDENT:
743
                if (isset($meetingInfo['id'])) {
744
                    $roomInfo = $this->getMeetingParticipantInfo($meetingInfo['id'], api_get_user_id());
745
                    if (!empty($roomInfo) && isset($roomInfo['interface'])) {
746
                        $interface = $roomInfo['interface'];
747
                    } else {
748
                        if (isset($_REQUEST['interface'])) {
749
                            $interface = isset($_REQUEST['interface']) ? (int) $_REQUEST['interface'] : 0;
750
                        }
751
                    }
752
                }
753
                break;
754
        }
755
756
        return $interface;
757
    }
758
759
    /**
760
     * @param int $meetingId
761
     * @param int $userId
762
     *
763
     * @return array
764
     */
765
    public function getMeetingParticipantInfo($meetingId, $userId)
766
    {
767
        $meetingData = Database::select(
768
            '*',
769
            'plugin_bbb_room',
770
            array('where' => array('meeting_id = ? AND participant_id = ?' => [$meetingId, $userId])),
771
            'first'
772
        );
773
774
        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...
775
            return $meetingData;
776
        }
777
778
        return [];
779
    }
780
781
    /**
782
     * Save a participant in a meeting room
783
     *
784
     * @param int $meetingId
785
     * @param int $participantId
786
     * @param int $interface
787
     *
788
     * @return false|int The last inserted ID. Otherwise return false
789
     */
790
    public function saveParticipant($meetingId, $participantId, $interface = 0)
791
    {
792
        $params = [
793
            'meeting_id' => $meetingId,
794
            'participant_id' => $participantId,
795
            'in_at' => api_get_utc_datetime(),
796
            'out_at' => api_get_utc_datetime(),
797
        ];
798
799
        if ($this->plugin->get('interface') !== false) {
800
            $params['interface'] = $interface;
801
        }
802
803
        return Database::insert(
804
            'plugin_bbb_room',
805
            $params
806
        );
807
    }
808
809
    /**
810
     * Tells whether the given meeting exists and is running
811
     * (using course code as name)
812
     *
813
     * @param string $meetingName Meeting name (usually the course code)
814
     *
815
     * @return bool True if meeting exists, false otherwise
816
     * @assert ('') === false
817
     * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false
818
     */
819
    public function meetingExists($meetingName)
820
    {
821
        $meetingData = $this->getMeetingByName($meetingName);
822
823
        return !empty($meetingData);
824
    }
825
826
    /**
827
     * @param string $meetingName
828
     *
829
     * @return array
830
     */
831
    public function getMeetingByName($meetingName)
832
    {
833
        if (empty($meetingName)) {
834
            return [];
835
        }
836
837
        $courseId = api_get_course_int_id();
838
        $sessionId = api_get_session_id();
839
        $conditions = array(
840
            'where' => array(
841
                'c_id = ? AND session_id = ? AND meeting_name = ? AND status = 1 AND access_url = ?' =>
842
                    array($courseId, $sessionId, $meetingName, $this->accessUrl),
843
            ),
844
        );
845
846
        if ($this->hasGroupSupport()) {
847
            $groupId = api_get_group_id();
848
            $conditions = array(
849
                'where' => array(
850
                    'c_id = ? AND session_id = ? AND meeting_name = ? AND group_id = ? AND status = 1 AND access_url = ?' =>
851
                        array(
852
                            $courseId,
853
                            $sessionId,
854
                            $meetingName,
855
                            $groupId,
856
                            $this->accessUrl,
857
                        ),
858
                ),
859
            );
860
        }
861
862
        $meetingData = Database::select(
863
            '*',
864
            $this->table,
865
            $conditions,
866
            'first'
867
        );
868
869
        if ($this->debug) {
870
            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

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