Passed
Push — 1.11.x ( dc537c...f9128c )
by Julito
18:03 queued 08:24
created

bbb   F

Complexity

Total Complexity 258

Size/Duplication

Total Lines 1920
Duplicated Lines 0 %

Importance

Changes 11
Bugs 0 Features 2
Metric Value
eloc 867
c 11
b 0
f 2
dl 0
loc 1920
rs 1.733
wmc 258

52 Methods

Rating   Name   Duplication   Size   Complexity  
F joinMeeting() 0 111 16
B getMaxUsersLimit() 0 41 8
B getUrlParams() 0 19 8
B showGlobalConferenceLink() 0 26 7
A getMeetingInfo() 0 18 5
B checkInterface() 0 29 10
A isGlobalConference() 0 7 2
A getModMeetingPassword() 0 13 4
A getListingUrl() 0 4 1
A isGlobalConferencePerUserEnabled() 0 3 1
A hasGroupSupport() 0 3 1
A getMeetingParticipantInfo() 0 14 2
A getMeetingByName() 0 43 4
A isGlobalConferenceEnabled() 0 3 1
F createMeeting() 0 88 19
A isConferenceManager() 0 22 6
A saveParticipant() 0 16 2
A getAllMeetingsInCourse() 0 16 1
A meetingExists() 0 5 1
A getUserMeetingPassword() 0 12 4
A setMaxUsersLimit() 0 6 2
F __construct() 0 95 20
A getCurrentVideoConferenceName() 0 15 4
B getUsersOnlineInCurrentRoom() 0 69 7
A regenerateRecordUrl() 0 16 6
A findConnectedMeetingParticipants() 0 27 3
A copyToRecordToLinkTool() 0 8 2
A getConferenceUrl() 0 3 1
A publishMeeting() 0 10 2
A addToCalendarUrl() 0 6 2
B copyRecordingToLinkTool() 0 41 8
A unPublishUrl() 0 8 2
A isServerRunning() 0 3 1
A getMeeting() 0 10 1
A getActiveSessionsCount() 0 10 1
B regenerateRecording() 0 59 11
A updateMeetingVideoUrl() 0 6 1
A publishUrl() 0 8 2
C getActionLinks() 0 102 13
C deleteRecording() 0 79 13
A getMeetingByRemoteId() 0 10 1
A unpublishMeeting() 0 10 2
A forceCIdReq() 0 5 1
A regenerateRecordUrlFromMeeting() 0 12 3
A endMeeting() 0 38 3
A endUrl() 0 7 2
F getMeetings() 0 213 30
A redirectToBBB() 0 10 2
A deleteRecordUrl() 0 8 2
A isServerConfigured() 0 15 3
A checkDirectMeetingVideoUrl() 0 26 3
A findMeetingByName() 0 10 1

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
/**
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');
0 ignored issues
show
Bug Best Practice introduced by
The property table does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
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_setting(
89
                                'bbb_enable_conference_in_groups',
90
                                $courseInfo
91
                            ) === '1';
92
                    }
93
                }
94
            }
95
        }
96
        $this->maxUsersLimit = $this->plugin->get('max_users_limit');
97
98
        if ($bbbPluginEnabled === 'true') {
99
            $userInfo = api_get_user_info();
100
            if (empty($userInfo) && !empty($isGlobalPerUser)) {
101
                // If we are following a link to a global "per user" conference
102
                // then generate a random guest name to join the conference
103
                // because there is no part of the process where we give a name
104
                $this->userCompleteName = 'Guest'.rand(1000, 9999);
105
            } else {
106
                $this->userCompleteName = $userInfo['complete_name'];
107
            }
108
109
            $this->salt = $bbb_salt;
110
            if (!empty($bbb_host)) {
111
                if (substr($bbb_host, -1, 1) !== '/') {
112
                    $bbb_host .= '/';
113
                }
114
                if (!preg_match('#/bigbluebutton/$#', $bbb_host)) {
115
                    $bbb_host .= 'bigbluebutton/';
116
                }
117
            }
118
            $info = parse_url($bbb_host);
119
            $this->url = $bbb_host;
120
121
            if (isset($info['scheme'])) {
122
                $this->protocol = $info['scheme'].'://';
123
                $this->url = str_replace($this->protocol, '', $this->url);
124
                $urlWithProtocol = $bbb_host;
125
            } else {
126
                // We assume it's an http, if user wants to use https, the host *must* include the protocol.
127
                $urlWithProtocol = 'http://'.$bbb_host;
128
            }
129
130
            // Setting BBB api
131
            define('CONFIG_SECURITY_SALT', $this->salt);
132
            define('CONFIG_SERVER_URL_WITH_PROTOCOL', $urlWithProtocol);
133
            define('CONFIG_SERVER_BASE_URL', $this->url);
134
135
            $this->api = new BigBlueButtonBN();
136
            $this->pluginEnabled = true;
137
            $this->logoutUrl = $this->getListingUrl();
138
        }
139
    }
140
141
    /**
142
     * @param int $courseId  Optional. Course ID.
143
     * @param int $sessionId Optional. Session ID.
144
     * @param int $groupId   Optional. Group ID.
145
     *
146
     * @return string
147
     */
148
    public function getListingUrl($courseId = 0, $sessionId = 0, $groupId = 0)
149
    {
150
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'
151
            .$this->getUrlParams($courseId, $sessionId, $groupId);
152
    }
153
154
    /**
155
     * @param int $courseId  Optional. Course ID.
156
     * @param int $sessionId Optional. Session ID.
157
     * @param int $groupId   Optional. Group ID.
158
     *
159
     * @return string
160
     */
161
    public function getUrlParams($courseId = 0, $sessionId = 0, $groupId = 0)
162
    {
163
        if (empty($this->courseCode) && !$courseId) {
164
            if ($this->isGlobalConferencePerUserEnabled()) {
165
                return 'global=1&user_id='.$this->userId;
166
            }
167
168
            if ($this->isGlobalConference()) {
169
                return 'global=1';
170
            }
171
172
            return '';
173
        }
174
175
        return http_build_query(
176
            [
177
                'cidReq' => $courseId ? api_get_course_entity($courseId)->getCode() : $this->courseCode,
178
                'id_session' => $sessionId ?: $this->sessionId,
179
                'gidReq' => $groupId ?: $this->groupId,
180
            ]
181
        );
182
    }
183
184
    /**
185
     * @return bool
186
     */
187
    public function isGlobalConferencePerUserEnabled()
188
    {
189
        return $this->enableGlobalConferencePerUser;
190
    }
191
192
    /**
193
     * @return bool
194
     */
195
    public function isGlobalConference()
196
    {
197
        if ($this->isGlobalConferenceEnabled() === false) {
198
            return false;
199
        }
200
201
        return (bool) $this->isGlobalConference;
202
    }
203
204
    /**
205
     * @return bool
206
     */
207
    public function isGlobalConferenceEnabled()
208
    {
209
        return $this->enableGlobalConference;
210
    }
211
212
    /**
213
     * @param array $userInfo
214
     *
215
     * @return bool
216
     */
217
    public static function showGlobalConferenceLink($userInfo)
218
    {
219
        if (empty($userInfo)) {
220
            return false;
221
        }
222
        $setting = api_get_plugin_setting('bbb', 'enable_global_conference');
223
        $settingLink = api_get_plugin_setting('bbb', 'enable_global_conference_link');
224
        if ($setting === 'true' && $settingLink === 'true') {
225
            //$content = Display::url(get_lang('LaunchVideoConferenceRoom'), $url);
226
            $allowedRoles = api_get_plugin_setting(
227
                'bbb',
228
                'global_conference_allow_roles'
229
            );
230
231
            if (api_is_platform_admin()) {
232
                $userInfo['status'] = PLATFORM_ADMIN;
233
            }
234
235
            $showGlobalLink = true;
236
            if (!empty($allowedRoles)) {
237
                if (!in_array($userInfo['status'], $allowedRoles)) {
238
                    $showGlobalLink = false;
239
                }
240
            }
241
242
            return $showGlobalLink;
243
        }
244
    }
245
246
    /**
247
     * Gets the global limit of users in a video-conference room.
248
     * This value can be overridden by course-specific values
249
     * @return  int Maximum number of users set globally
250
     */
251
    public function getMaxUsersLimit()
252
    {
253
        $limit = $this->maxUsersLimit;
254
        if ($limit <= 0) {
255
            $limit = 0;
256
        }
257
        $courseLimit = 0;
258
        $sessionLimit = 0;
259
        // Check the extra fields for this course and session
260
        // Session limit takes priority over course limit
261
        // Course limit takes priority over global limit
262
        if (!empty($this->courseId)) {
263
            $extraField = new ExtraField('course');
264
            $fieldId = $extraField->get_all(
265
                array('variable = ?' => 'plugin_bbb_course_users_limit')
266
            );
267
            $extraValue = new ExtraFieldValue('course');
268
            $value = $extraValue->get_values_by_handler_and_field_id($this->courseId, $fieldId[0]['id']);
269
            if (!empty($value['value'])) {
270
                $courseLimit = (int) $value['value'];
271
            }
272
        }
273
        if (!empty($this->sessionId)) {
274
            $extraField = new ExtraField('session');
275
            $fieldId = $extraField->get_all(
276
                array('variable = ?' => 'plugin_bbb_session_users_limit')
277
            );
278
            $extraValue = new ExtraFieldValue('session');
279
            $value = $extraValue->get_values_by_handler_and_field_id($this->sessionId, $fieldId[0]['id']);
280
            if (!empty($value['value'])) {
281
                $sessionLimit = (int) $value['value'];
282
            }
283
        }
284
285
        if (!empty($sessionLimit)) {
286
            return $sessionLimit;
287
        } elseif (!empty($courseLimit)) {
288
            return $courseLimit;
289
        }
290
291
        return (int) $limit;
292
    }
293
294
    /**
295
     * Sets the global limit of users in a video-conference room.
296
     *
297
     * @param int Maximum number of users (globally)
298
     */
299
    public function setMaxUsersLimit($max)
300
    {
301
        if ($max < 0) {
302
            $max = 0;
303
        }
304
        $this->maxUsersLimit = (int) $max;
305
    }
306
307
    /**
308
     * See this file in you BBB to set up default values
309
     *
310
     * @param array $params Array of parameters that will be completed if not containing all expected variables
311
     *
312
     * /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
313
     *
314
     * More record information:
315
     * http://code.google.com/p/bigbluebutton/wiki/RecordPlaybackSpecification
316
     *
317
     * Default maximum number of users a meeting can have.
318
     * Doesn't get enforced yet but is the default value when the create
319
     * API doesn't pass a value.
320
     * defaultMaxUsers=20
321
     *
322
     * Default duration of the meeting in minutes.
323
     * Current default is 0 (meeting doesn't end).
324
     * defaultMeetingDuration=0
325
     *
326
     * Remove the meeting from memory when the end API is called.
327
     * This allows 3rd-party apps to recycle the meeting right-away
328
     * instead of waiting for the meeting to expire (see below).
329
     * removeMeetingWhenEnded=false
330
     *
331
     * The number of minutes before the system removes the meeting from memory.
332
     * defaultMeetingExpireDuration=1
333
     *
334
     * The number of minutes the system waits when a meeting is created and when
335
     * a user joins. If after this period, a user hasn't joined, the meeting is
336
     * removed from memory.
337
     * defaultMeetingCreateJoinDuration=5
338
     *
339
     * @return mixed
340
     */
341
    public function createMeeting($params)
342
    {
343
        $params['c_id'] = api_get_course_int_id();
344
        $params['session_id'] = api_get_session_id();
345
346
        if ($this->hasGroupSupport()) {
347
            $params['group_id'] = api_get_group_id();
348
        }
349
350
        if ($this->isGlobalConferencePerUserEnabled() && !empty($this->userId)) {
351
            $params['user_id'] = (int) $this->userId;
352
        }
353
354
        $params['attendee_pw'] = isset($params['attendee_pw']) ? $params['attendee_pw'] : $this->getUserMeetingPassword(
355
        );
356
        $attendeePassword = $params['attendee_pw'];
357
        $params['moderator_pw'] = isset($params['moderator_pw']) ? $params['moderator_pw'] : $this->getModMeetingPassword(
358
        );
359
        $moderatorPassword = $params['moderator_pw'];
360
361
        $params['record'] = api_get_course_setting('big_blue_button_record_and_store') == 1 ? true : false;
362
        $max = api_get_course_setting('big_blue_button_max_students_allowed');
363
        $max = isset($max) ? $max : -1;
364
365
        $params['status'] = 1;
366
        // Generate a pseudo-global-unique-id to avoid clash of conferences on
367
        // the same BBB server with several Chamilo portals
368
        $params['remote_id'] = uniqid(true, true);
369
        // Each simultaneous conference room needs to have a different
370
        // voice_bridge composed of a 5 digits number, so generating a random one
371
        $params['voice_bridge'] = rand(10000, 99999);
372
        $params['created_at'] = api_get_utc_datetime();
373
        $params['access_url'] = $this->accessUrl;
374
375
        // Check interface feature is installed
376
        $interfaceFeature = $this->plugin->get('interface');
377
        if ($interfaceFeature === false) {
378
            if (isset($params['interface'])) {
379
                unset($params['interface']);
380
            }
381
        }
382
383
        $id = Database::insert($this->table, $params);
384
385
        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...
386
            $meetingName = isset($params['meeting_name']) ? $params['meeting_name'] : $this->getCurrentVideoConferenceName(
387
            );
388
            $welcomeMessage = isset($params['welcome_msg']) ? $params['welcome_msg'] : null;
389
            $record = isset($params['record']) && $params['record'] ? 'true' : 'false';
390
            //$duration = isset($params['duration']) ? intval($params['duration']) : 0;
391
            // This setting currently limits the maximum conference duration,
392
            // to avoid lingering sessions on the video-conference server #6261
393
            $duration = 300;
394
            $bbbParams = array(
395
                'meetingId' => $params['remote_id'], // REQUIRED
396
                'meetingName' => $meetingName, // REQUIRED
397
                'attendeePw' => $attendeePassword, // Match this value in getJoinMeetingURL() to join as attendee.
398
                'moderatorPw' => $moderatorPassword, // Match this value in getJoinMeetingURL() to join as moderator.
399
                'welcomeMsg' => $welcomeMessage, // ''= use default. Change to customize.
400
                'dialNumber' => '', // The main number to call into. Optional.
401
                'voiceBridge' => $params['voice_bridge'], // PIN to join voice. Required.
402
                'webVoice' => '', // Alphanumeric to join voice. Optional.
403
                'logoutUrl' => $this->logoutUrl.'&action=logout&remote_id='.$params['remote_id'],
404
                'maxParticipants' => $max, // Optional. -1 = unlimitted. Not supported in BBB. [number]
405
                'record' => $record, // New. 'true' will tell BBB to record the meeting.
406
                'duration' => $duration, // Default = 0 which means no set duration in minutes. [number]
407
                //'meta_category' => '',  // Use to pass additional info to BBB server. See API docs.
408
            );
409
410
            $status = false;
411
            $meeting = null;
412
            while ($status === false) {
413
                $result = $this->api->createMeetingWithXmlResponseArray($bbbParams);
414
                if (isset($result) && strval($result['returncode']) == 'SUCCESS') {
415
                    if ($this->plugin->get('allow_regenerate_recording') === 'true') {
416
                        $internalId = Database::escape_string($result['internalMeetingID']);
417
                        $sql = "UPDATE $this->table SET internal_meeting_id = '".$internalId."'
418
                                WHERE id = $id";
419
                        Database::query($sql);
420
                    }
421
                    $meeting = $this->joinMeeting($meetingName, true);
422
423
                    return $meeting;
424
                }
425
            }
426
        }
427
428
        return false;
429
    }
430
431
    /**
432
     * @return bool
433
     */
434
    public function hasGroupSupport()
435
    {
436
        return $this->groupSupport;
437
    }
438
439
    /**
440
     * Gets the password for a specific meeting for the current user
441
     *
442
     * @param string $courseCode
443
     *
444
     * @return string A moderator password if user is teacher, or the course code otherwise
445
     *
446
     */
447
    public function getUserMeetingPassword($courseCode = null)
448
    {
449
        if ($this->isGlobalConferencePerUserEnabled()) {
450
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
451
        }
452
453
        if ($this->isGlobalConference()) {
454
            return 'url_'.api_get_current_access_url_id();
455
        }
456
        $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
457
458
        return $courseCode;
459
    }
460
461
    /**
462
     * Generated a moderator password for the meeting.
463
     *
464
     * @param string $courseCode
465
     *
466
     * @return string A password for the moderation of the videoconference
467
     */
468
    public function getModMeetingPassword($courseCode = null)
469
    {
470
        if ($this->isGlobalConferencePerUserEnabled()) {
471
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id().'_mod';
472
        }
473
474
        if ($this->isGlobalConference()) {
475
            return 'url_'.api_get_current_access_url_id().'_mod';
476
        }
477
478
        $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
479
480
        return $courseCode.'mod';
481
    }
482
483
    /**
484
     * @return string
485
     */
486
    public function getCurrentVideoConferenceName()
487
    {
488
        if ($this->isGlobalConferencePerUserEnabled()) {
489
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
490
        }
491
492
        if ($this->isGlobalConference()) {
493
            return 'url_'.api_get_current_access_url_id();
494
        }
495
496
        if ($this->hasGroupSupport()) {
497
            return api_get_course_id().'-'.api_get_session_id().'-'.api_get_group_id();
498
        }
499
500
        return api_get_course_id().'-'.api_get_session_id();
501
    }
502
503
    /**
504
     * Returns a meeting "join" URL
505
     *
506
     * @param string The name of the meeting (usually the course code)
507
     *
508
     * @return mixed The URL to join the meeting, or false on error
509
     * @todo implement moderator pass
510
     * @assert ('') === false
511
     * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false
512
     */
513
    public function joinMeeting($meetingName)
514
    {
515
        if ($this->debug) {
516
            error_log("joinMeeting: $meetingName");
517
        }
518
519
        if (empty($meetingName)) {
520
            return false;
521
        }
522
523
        $manager = $this->isConferenceManager();
524
        if ($manager) {
525
            $pass = $this->getModMeetingPassword();
526
        } else {
527
            $pass = $this->getUserMeetingPassword();
528
        }
529
530
        $meetingData = Database::select(
531
            '*',
532
            $this->table,
533
            array(
534
                'where' => array(
535
                    'meeting_name = ? AND status = 1 AND access_url = ?' => array(
536
                        $meetingName,
537
                        $this->accessUrl,
538
                    ),
539
                ),
540
            ),
541
            'first'
542
        );
543
544
        if (empty($meetingData) || !is_array($meetingData)) {
545
            if ($this->debug) {
546
                error_log("meeting does not exist: $meetingName");
547
            }
548
549
            return false;
550
        }
551
552
        $params = array(
553
            'meetingId' => $meetingData['remote_id'],
554
            //  -- REQUIRED - The unique id for the meeting
555
            'password' => $this->getModMeetingPassword()
556
            //  -- REQUIRED - The moderator password for the meeting
557
        );
558
559
        $meetingInfoExists = false;
560
        $meetingIsRunningInfo = $this->getMeetingInfo($params);
561
        if ($this->debug) {
562
            error_log('Searching meeting with params:');
563
            error_log(print_r($params, 1));
564
            error_log('Result:');
565
            error_log(print_r($meetingIsRunningInfo, 1));
566
        }
567
568
        if ($meetingIsRunningInfo === false) {
569
            // checking with the remote_id didn't work, so just in case and
570
            // to provide backwards support, check with the id
571
            $params = array(
572
                'meetingId' => $meetingData['id'],
573
                //  -- REQUIRED - The unique id for the meeting
574
                'password' => $this->getModMeetingPassword()
575
                //  -- REQUIRED - The moderator password for the meeting
576
            );
577
            $meetingIsRunningInfo = $this->getMeetingInfo($params);
578
            if ($this->debug) {
579
                error_log('Searching meetingId with params:');
580
                error_log(print_r($params, 1));
581
                error_log('Result:');
582
                error_log(print_r($meetingIsRunningInfo, 1));
583
            }
584
        }
585
586
        if (strval($meetingIsRunningInfo['returncode']) === 'SUCCESS' &&
587
            isset($meetingIsRunningInfo['meetingName']) &&
588
            !empty($meetingIsRunningInfo['meetingName'])
589
        ) {
590
            $meetingInfoExists = true;
591
        }
592
593
        if ($this->debug) {
594
            error_log(
595
                "meeting is running: ".intval($meetingInfoExists)
596
            );
597
        }
598
599
        $url = false;
600
        if ($meetingInfoExists) {
601
            $joinParams = [
602
                'meetingId' => $meetingData['remote_id'],
603
                //	-- REQUIRED - A unique id for the meeting
604
                'username' => $this->userCompleteName,
605
                //-- REQUIRED - The name that will display for the user in the meeting
606
                'password' => $pass,
607
                //-- REQUIRED - The attendee or moderator password, depending on what's passed here
608
                //'createTime' => api_get_utc_datetime(),			//-- OPTIONAL - string. Leave blank ('') unless you set this correctly.
609
                'userID' => api_get_user_id(),
610
                //-- OPTIONAL - string
611
                'webVoiceConf' => '',
612
                //	-- OPTIONAL - string
613
                'interface' => $this->checkInterface($meetingData),
614
            ];
615
            $url = $this->api->getJoinMeetingURL($joinParams);
616
            $url = $this->protocol.$url;
617
        }
618
619
        if ($this->debug) {
620
            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

620
            error_log("return url :"./** @scrutinizer ignore-type */ $url);
Loading history...
621
        }
622
623
        return $url;
624
    }
625
626
    /**
627
     * Checks whether a user is teacher in the current course
628
     * @return bool True if the user can be considered a teacher in this course, false otherwise
629
     */
630
    public function isConferenceManager()
631
    {
632
        if (api_is_coach() || api_is_platform_admin()) {
633
            return true;
634
        }
635
636
        if ($this->isGlobalConferencePerUserEnabled()) {
637
            $currentUserId = api_get_user_id();
638
            if ($this->userId === $currentUserId) {
639
                return true;
640
            } else {
641
                return false;
642
            }
643
        }
644
645
        $courseInfo = api_get_course_info();
646
647
        if (!empty($courseInfo)) {
648
            return api_is_course_admin();
649
        }
650
651
        return false;
652
    }
653
654
    /**
655
     * Get information about the given meeting
656
     *
657
     * @param array ...?
658
     *
659
     * @return mixed Array of information on success, false on error
660
     * @assert (array()) === false
661
     */
662
    public function getMeetingInfo($params)
663
    {
664
        try {
665
            $result = $this->api->getMeetingInfoWithXmlResponseArray($params);
666
            if ($result == null) {
667
                if ($this->debug) {
668
                    error_log("Failed to get any response. Maybe we can't contact the BBB server.");
669
                }
670
            }
671
672
            return $result;
673
        } catch (Exception $e) {
674
            if ($this->debug) {
675
                error_log('Caught exception: ', $e->getMessage(), "\n");
676
            }
677
        }
678
679
        return false;
680
    }
681
682
    /**
683
     * @param $meetingInfo
684
     *
685
     * @return int
686
     */
687
    public function checkInterface($meetingInfo)
688
    {
689
        $interface = BBBPlugin::LAUNCH_TYPE_DEFAULT;
690
691
        $type = $this->plugin->get('launch_type');
692
        switch ($type) {
693
            case BBBPlugin::LAUNCH_TYPE_DEFAULT:
694
                $interface = $this->plugin->get('interface');
695
                break;
696
            case BBBPlugin::LAUNCH_TYPE_SET_BY_TEACHER:
697
                if (isset($meetingInfo['interface'])) {
698
                    $interface = $meetingInfo['interface'];
699
                }
700
                break;
701
            case BBBPlugin::LAUNCH_TYPE_SET_BY_STUDENT:
702
                if (isset($meetingInfo['id'])) {
703
                    $roomInfo = $this->getMeetingParticipantInfo($meetingInfo['id'], api_get_user_id());
704
                    if (!empty($roomInfo) && isset($roomInfo['interface'])) {
705
                        $interface = $roomInfo['interface'];
706
                    } else {
707
                        if (isset($_REQUEST['interface'])) {
708
                            $interface = isset($_REQUEST['interface']) ? (int) $_REQUEST['interface'] : 0;
709
                        }
710
                    }
711
                }
712
                break;
713
        }
714
715
        return $interface;
716
    }
717
718
    /**
719
     * @param int $meetingId
720
     * @param int $userId
721
     *
722
     * @return array
723
     */
724
    public function getMeetingParticipantInfo($meetingId, $userId)
725
    {
726
        $meetingData = Database::select(
727
            '*',
728
            'plugin_bbb_room',
729
            array('where' => array('meeting_id = ? AND participant_id = ?' => [$meetingId, $userId])),
730
            'first'
731
        );
732
733
        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...
734
            return $meetingData;
735
        }
736
737
        return [];
738
    }
739
740
    /**
741
     * Save a participant in a meeting room
742
     *
743
     * @param int $meetingId
744
     * @param int $participantId
745
     * @param int $interface
746
     *
747
     * @return false|int The last inserted ID. Otherwise return false
748
     */
749
    public function saveParticipant($meetingId, $participantId, $interface = 0)
750
    {
751
        $params = [
752
            'meeting_id' => $meetingId,
753
            'participant_id' => $participantId,
754
            'in_at' => api_get_utc_datetime(),
755
            'out_at' => api_get_utc_datetime(),
756
        ];
757
758
        if ($this->plugin->get('interface') !== false) {
759
            $params['interface'] = $interface;
760
        }
761
762
        return Database::insert(
763
            'plugin_bbb_room',
764
            $params
765
        );
766
    }
767
768
    /**
769
     * Tells whether the given meeting exists and is running
770
     * (using course code as name)
771
     *
772
     * @param string $meetingName Meeting name (usually the course code)
773
     *
774
     * @return bool True if meeting exists, false otherwise
775
     * @assert ('') === false
776
     * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false
777
     */
778
    public function meetingExists($meetingName)
779
    {
780
        $meetingData = $this->getMeetingByName($meetingName);
781
782
        return !empty($meetingData);
783
    }
784
785
    /**
786
     * @param string $meetingName
787
     *
788
     * @return array
789
     */
790
    public function getMeetingByName($meetingName)
791
    {
792
        if (empty($meetingName)) {
793
            return [];
794
        }
795
796
        $courseId = api_get_course_int_id();
797
        $sessionId = api_get_session_id();
798
        $conditions = array(
799
            'where' => array(
800
                'c_id = ? AND session_id = ? AND meeting_name = ? AND status = 1 AND access_url = ?' =>
801
                    array($courseId, $sessionId, $meetingName, $this->accessUrl),
802
            ),
803
        );
804
805
        if ($this->hasGroupSupport()) {
806
            $groupId = api_get_group_id();
807
            $conditions = array(
808
                'where' => array(
809
                    'c_id = ? AND session_id = ? AND meeting_name = ? AND group_id = ? AND status = 1 AND access_url = ?' =>
810
                        array(
811
                            $courseId,
812
                            $sessionId,
813
                            $meetingName,
814
                            $groupId,
815
                            $this->accessUrl,
816
                        ),
817
                ),
818
            );
819
        }
820
821
        $meetingData = Database::select(
822
            '*',
823
            $this->table,
824
            $conditions,
825
            'first'
826
        );
827
828
        if ($this->debug) {
829
            error_log('meeting_exists '.print_r($meetingData, 1));
830
        }
831
832
        return $meetingData;
833
    }
834
835
    /**
836
     * Gets a list from the database of all meetings attached to a course with the given status
837
     * @param int $courseId
838
     * @param int $sessionId
839
     * @param int $status 0 for closed meetings, 1 for open meetings
840
     *
841
     * @return array
842
     */
843
    public function getAllMeetingsInCourse($courseId, $sessionId, $status)
844
    {
845
        $conditions = array(
846
            'where' => array(
847
                'status = ? AND c_id = ? AND session_id = ? ' => array(
848
                    $status,
849
                    $courseId,
850
                    $sessionId,
851
                ),
852
            ),
853
        );
854
855
        return Database::select(
856
            '*',
857
            $this->table,
858
            $conditions
859
        );
860
    }
861
862
    /**
863
     * Gets all the course meetings saved in the plugin_bbb_meeting table and
864
     * generate actionable links (join/close/delete/etc)
865
     *
866
     * @param int   $courseId
867
     * @param int   $sessionId
868
     * @param int   $groupId
869
     * @param bool  $isAdminReport Optional. Set to true then the report is for admins
870
     * @param array $dateRange     Optional
871
     *
872
     * @return array Array of current open meeting rooms
873
     * @throws Exception
874
     */
875
    public function getMeetings(
876
        $courseId = 0,
877
        $sessionId = 0,
878
        $groupId = 0,
879
        $isAdminReport = false,
880
        $dateRange = []
881
    ) {
882
        $em = Database::getManager();
883
        $manager = $this->isConferenceManager();
884
885
        $conditions = [];
886
        if ($courseId || $sessionId || $groupId) {
887
            $conditions = array(
888
                'where' => array(
889
                    'c_id = ? AND session_id = ? ' => array($courseId, $sessionId),
890
                ),
891
            );
892
893
            if ($this->hasGroupSupport()) {
894
                $conditions = array(
895
                    'where' => array(
896
                        'c_id = ? AND session_id = ? AND group_id = ? ' => array(
897
                            $courseId,
898
                            $sessionId,
899
                            $groupId,
900
                        ),
901
                    ),
902
                );
903
            }
904
905
            if ($this->isGlobalConferencePerUserEnabled()) {
906
                $conditions = array(
907
                    'where' => array(
908
                        'c_id = ? AND session_id = ? AND user_id = ?' => array(
909
                            $courseId,
910
                            $sessionId,
911
                            $this->userId,
912
                        ),
913
                    ),
914
                );
915
            }
916
        }
917
918
        if (!empty($dateRange)) {
919
            $dateStart = date_create($dateRange['search_meeting_start']);
920
            $dateStart = date_format($dateStart, 'Y-m-d H:i:s');
921
            $dateEnd = date_create($dateRange['search_meeting_end']);
922
            $dateEnd = $dateEnd->add(new DateInterval('P1D'));
923
            $dateEnd = date_format($dateEnd, 'Y-m-d H:i:s');
924
925
            $conditions = array(
926
                'where' => array(
927
                    'created_at BETWEEN ? AND ? ' => array($dateStart, $dateEnd),
928
                ),
929
            );
930
        }
931
932
        $conditions['order'] = 'created_at ASC';
933
934
        $meetingList = Database::select(
935
            '*',
936
            $this->table,
937
            $conditions
938
        );
939
        $isGlobal = $this->isGlobalConference();
940
        $newMeetingList = array();
941
        foreach ($meetingList as $meetingDB) {
942
            $item = array();
943
            $courseId = $meetingDB['c_id'];
944
            $courseInfo = api_get_course_info_by_id($courseId);
945
            $courseCode = '';
946
            if (!empty($courseInfo)) {
947
                $courseCode = $courseInfo['code'];
948
            }
949
950
            if ($manager) {
951
                $pass = $meetingDB['moderator_pw'];
952
            } else {
953
                $pass = $meetingDB['attendee_pw'];
954
            }
955
956
            $meetingBBB = $this->getMeetingInfo(
957
                [
958
                    'meetingId' => $meetingDB['remote_id'],
959
                    'password' => $pass,
960
                ]
961
            );
962
963
            if ($meetingBBB === false) {
964
                // Checking with the remote_id didn't work, so just in case and
965
                // to provide backwards support, check with the id
966
                $params = array(
967
                    'meetingId' => $meetingDB['id'],
968
                    //  -- REQUIRED - The unique id for the meeting
969
                    'password' => $pass
970
                    //  -- REQUIRED - The moderator password for the meeting
971
                );
972
                $meetingBBB = $this->getMeetingInfo($params);
973
            }
974
975
            if ($meetingDB['visibility'] == 0 && $this->isConferenceManager() === false) {
976
                continue;
977
            }
978
979
            $meetingBBB['end_url'] = $this->endUrl($meetingDB);
980
981
            if (isset($meetingBBB['returncode']) && (string) $meetingBBB['returncode'] === 'FAILED') {
982
                if ($meetingDB['status'] == 1 && $this->isConferenceManager()) {
983
                    $this->endMeeting($meetingDB['id'], $courseCode);
984
                }
985
            } else {
986
                $meetingBBB['add_to_calendar_url'] = $this->addToCalendarUrl($meetingDB);
987
            }
988
989
            if ($meetingDB['record'] == 1) {
990
                // backwards compatibility (when there was no remote ID)
991
                $mId = $meetingDB['remote_id'];
992
                if (empty($mId)) {
993
                    $mId = $meetingDB['id'];
994
                }
995
                if (empty($mId)) {
996
                    // if the id is still empty (should *never* occur as 'id' is
997
                    // the table's primary key), skip this conference
998
                    continue;
999
                }
1000
1001
                $record = [];
1002
                $recordingParams = ['meetingId' => $mId];
1003
                $records = $this->api->getRecordingsWithXmlResponseArray($recordingParams);
1004
1005
                if (!empty($records)) {
1006
                    if (!isset($records['messageKey']) || $records['messageKey'] !== 'noRecordings') {
1007
                        $record = end($records);
1008
                        if (!is_array($record) || !isset($record['recordId'])) {
1009
                            continue;
1010
                        }
1011
1012
                        if (!empty($record['playbackFormatUrl'])) {
1013
                            $this->updateMeetingVideoUrl($meetingDB['id'], $record['playbackFormatUrl']);
1014
                        }
1015
                    }
1016
                }
1017
1018
                if (isset($record['playbackFormatUrl']) && !empty($record['playbackFormatUrl'])) {
1019
                    $recordLink = Display::url(
1020
                        $this->plugin->get_lang('ViewRecord'),
1021
                        $record['playbackFormatUrl'],
1022
                        ['target' => '_blank', 'class' => 'btn btn-default']
1023
                    );
1024
                } else {
1025
                    $recordLink = $this->plugin->get_lang('NoRecording');
1026
                }
1027
1028
                if ($isAdminReport) {
1029
                    $this->forceCIdReq(
1030
                        $courseInfo['code'],
1031
                        $meetingDB['session_id'],
1032
                        $meetingDB['group_id']
1033
                    );
1034
                }
1035
1036
                $actionLinks = $this->getActionLinks(
1037
                    $meetingDB,
1038
                    $record,
1039
                    $isGlobal,
1040
                    $isAdminReport
1041
                );
1042
                $item['show_links'] = $recordLink;
1043
            } else {
1044
                $actionLinks = $this->getActionLinks(
1045
                    $meetingDB,
1046
                    [],
1047
                    $isGlobal,
1048
                    $isAdminReport
1049
                );
1050
1051
                $item['show_links'] = $this->plugin->get_lang('NoRecording');
1052
            }
1053
1054
            $item['action_links'] = implode(PHP_EOL, $actionLinks);
1055
            $item['created_at'] = api_convert_and_format_date($meetingDB['created_at']);
1056
            // created_at
1057
            $meetingDB['created_at'] = $item['created_at']; //avoid overwrite in array_merge() below
1058
1059
            $item['publish_url'] = $this->publishUrl($meetingDB);
1060
            $item['unpublish_url'] = $this->unPublishUrl($meetingBBB);
1061
1062
            if ($meetingDB['status'] == 1) {
1063
                $joinParams = [
1064
                    'meetingId' => $meetingDB['remote_id'],
1065
                    //-- REQUIRED - A unique id for the meeting
1066
                    'username' => $this->userCompleteName,
1067
                    //-- REQUIRED - The name that will display for the user in the meeting
1068
                    'password' => $pass,
1069
                    //-- REQUIRED - The attendee or moderator password, depending on what's passed here
1070
                    'createTime' => '',
1071
                    //-- OPTIONAL - string. Leave blank ('') unless you set this correctly.
1072
                    'userID' => '',
1073
                    //	-- OPTIONAL - string
1074
                    'webVoiceConf' => '',
1075
                    //	-- OPTIONAL - string
1076
                    'interface' => $this->checkInterface($meetingDB),
1077
                ];
1078
                $item['go_url'] = $this->protocol.$this->api->getJoinMeetingURL($joinParams);
1079
            }
1080
            $item = array_merge($item, $meetingDB, $meetingBBB);
1081
1082
            $item['course'] = $em->find('ChamiloCoreBundle:Course', $item['c_id']);
1083
            $item['session'] = $em->find('ChamiloCoreBundle:Session', $item['session_id']);
1084
            $newMeetingList[] = $item;
1085
        }
1086
1087
        return $newMeetingList;
1088
    }
1089
1090
    /**
1091
     * @param array $meeting
1092
     *
1093
     * @return string
1094
     */
1095
    public function endUrl($meeting)
1096
    {
1097
        if (!isset($meeting['id'])) {
1098
            return '';
1099
        }
1100
1101
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=end&id='.$meeting['id'];
1102
    }
1103
1104
    /**
1105
     * Closes a meeting (usually when the user click on the close button from
1106
     * the conferences listing.
1107
     *
1108
     * @param string The internal ID of the meeting (id field for this meeting)
1109
     * @param string $courseCode
1110
     *
1111
     * @return void
1112
     * @assert (0) === false
1113
     */
1114
    public function endMeeting($id, $courseCode = null)
0 ignored issues
show
Unused Code introduced by
The parameter $courseCode is not used and could be removed. ( Ignorable by Annotation )

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

1114
    public function endMeeting($id, /** @scrutinizer ignore-unused */ $courseCode = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1115
    {
1116
        if (empty($id)) {
1117
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
1118
        }
1119
1120
        $meetingData = Database::select(
1121
            '*',
1122
            $this->table,
1123
            array('where' => array('id = ?' => array($id))),
1124
            'first'
1125
        );
1126
        $manager = $this->isConferenceManager();
1127
        if ($manager) {
1128
            $pass = $meetingData['moderator_pw'];
1129
        } else {
1130
            $pass = $meetingData['attendee_pw'];
1131
        }
1132
1133
        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

1133
        Event::/** @scrutinizer ignore-call */ 
1134
               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...
1134
            'bbb_end_meeting',
1135
            'meeting_id',
1136
            (int) $id,
1137
            null,
1138
            api_get_user_id(),
1139
            api_get_course_int_id(),
1140
            api_get_session_id()
1141
        );
1142
1143
        $endParams = array(
1144
            'meetingId' => $meetingData['remote_id'], // REQUIRED - We have to know which meeting to end.
1145
            'password' => $pass, // REQUIRED - Must match moderator pass for meeting.
1146
        );
1147
        $this->api->endMeetingWithXmlResponseArray($endParams);
1148
        Database::update(
1149
            $this->table,
1150
            array('status' => 0, 'closed_at' => api_get_utc_datetime()),
1151
            array('id = ? ' => $id)
1152
        );
1153
    }
1154
1155
    /**
1156
     * @param array $meeting
1157
     * @param array $record
1158
     *
1159
     * @return string
1160
     */
1161
    public function addToCalendarUrl($meeting, $record = [])
1162
    {
1163
        $url = isset($record['playbackFormatUrl']) ? $record['playbackFormatUrl'] : '';
1164
1165
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams(
1166
            ).'&action=add_to_calendar&id='.$meeting['id'].'&start='.api_strtotime($meeting['created_at']).'&url='.$url;
1167
    }
1168
1169
    /**
1170
     * @param int    $meetingId
1171
     * @param string $videoUrl
1172
     *
1173
     * @return bool|int
1174
     */
1175
    public function updateMeetingVideoUrl($meetingId, $videoUrl)
1176
    {
1177
        return Database::update(
1178
            'plugin_bbb_meeting',
1179
            ['video_url' => $videoUrl],
1180
            ['id = ?' => intval($meetingId)]
1181
        );
1182
    }
1183
1184
    /**
1185
     * Force the course, session and/or group IDs
1186
     *
1187
     * @param string $courseCode
1188
     * @param int    $sessionId
1189
     * @param int    $groupId
1190
     */
1191
    public function forceCIdReq($courseCode, $sessionId = 0, $groupId = 0)
1192
    {
1193
        $this->courseCode = $courseCode;
1194
        $this->sessionId = (int) $sessionId;
1195
        $this->groupId = (int) $groupId;
1196
    }
1197
1198
    /**
1199
     * @param array $meetingInfo
1200
     * @param array $recordInfo
1201
     * @param bool  $isGlobal
1202
     * @param bool  $isAdminReport
1203
     *
1204
     * @return array
1205
     */
1206
    private function getActionLinks(
1207
        $meetingInfo,
1208
        $recordInfo,
1209
        $isGlobal = false,
1210
        $isAdminReport = false
1211
    ) {
1212
        $isVisible = $meetingInfo['visibility'] != 0;
1213
        $linkVisibility = $isVisible
1214
            ? Display::url(
1215
                Display::return_icon('visible.png', get_lang('MakeInvisible')),
1216
                $this->unPublishUrl($meetingInfo)
1217
            )
1218
            : Display::url(
1219
                Display::return_icon('invisible.png', get_lang('MakeVisible')),
1220
                $this->publishUrl($meetingInfo)
1221
            );
1222
1223
        $links = [];
1224
        if ($this->plugin->get('allow_regenerate_recording') === 'true' && $meetingInfo['record'] == 1) {
1225
            if (!empty($recordInfo)) {
1226
                $links[] = Display::url(
1227
                    Display::return_icon('reload.png', get_lang('RegenerateRecord')),
1228
                    $this->regenerateRecordUrl($meetingInfo, $recordInfo)
1229
                );
1230
            } else {
1231
                $links[] = Display::url(
1232
                    Display::return_icon('reload.png', get_lang('RegenerateRecord')),
1233
                    $this->regenerateRecordUrlFromMeeting($meetingInfo)
1234
                );
1235
            }
1236
        }
1237
1238
        if (empty($recordInfo)) {
1239
            if (!$isAdminReport) {
1240
                if ($meetingInfo['status'] == 0) {
1241
                    $links[] = Display::url(
1242
                        Display::return_icon('delete.png', get_lang('Delete')),
1243
                        $this->deleteRecordUrl($meetingInfo)
1244
                    );
1245
                    $links[] = $linkVisibility;
1246
                }
1247
1248
                return $links;
1249
            } else {
1250
                $links[] = Display::url(
1251
                    Display::return_icon('course_home.png', get_lang('GoToCourse')),
1252
                    $this->getListingUrl($meetingInfo['c_id'], $meetingInfo['session_id'], $meetingInfo['group_id'])
1253
                );
1254
1255
                return $links;
1256
            }
1257
        }
1258
1259
        if (!$isGlobal) {
1260
            $links[] = Display::url(
1261
                Display::return_icon('link.gif', get_lang('UrlMeetingToShare')),
1262
                $this->copyToRecordToLinkTool($meetingInfo)
1263
            );
1264
            $links[] = Display::url(
1265
                Display::return_icon('agenda.png', get_lang('AddToCalendar')),
1266
                $this->addToCalendarUrl($meetingInfo, $recordInfo)
1267
            );
1268
        }
1269
1270
        $hide = $this->plugin->get('disable_download_conference_link') === 'true' ? true : false;
1271
1272
        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...
1273
            if ($meetingInfo['has_video_m4v']) {
1274
                $links[] = Display::url(
1275
                    Display::return_icon('save.png', get_lang('DownloadFile')),
1276
                    $recordInfo['playbackFormatUrl'].'/capture.m4v',
1277
                    ['target' => '_blank']
1278
                );
1279
            } else {
1280
                $links[] = Display::url(
1281
                    Display::return_icon('save.png', get_lang('DownloadFile')),
1282
                    '#',
1283
                    [
1284
                        'id' => "btn-check-meeting-video-{$meetingInfo['id']}",
1285
                        'class' => 'check-meeting-video',
1286
                        'data-id' => $meetingInfo['id'],
1287
                    ]
1288
                );
1289
            }
1290
        }
1291
1292
1293
        if (!$isAdminReport) {
1294
            $links[] = Display::url(
1295
                Display::return_icon('delete.png', get_lang('Delete')),
1296
                $this->deleteRecordUrl($meetingInfo)
1297
            );
1298
            $links[] = $linkVisibility;
1299
        } else {
1300
            $links[] = Display::url(
1301
                Display::return_icon('course_home.png', get_lang('GoToCourse')),
1302
                $this->getListingUrl($meetingInfo['c_id'], $meetingInfo['session_id'], $meetingInfo['group_id'])
1303
            );
1304
        }
1305
1306
1307
        return $links;
1308
    }
1309
1310
    /**
1311
     * @param array $meeting
1312
     *
1313
     * @return string
1314
     */
1315
    public function unPublishUrl($meeting)
1316
    {
1317
        if (!isset($meeting['id'])) {
1318
            return null;
1319
        }
1320
1321
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams(
1322
            ).'&action=unpublish&id='.$meeting['id'];
1323
    }
1324
1325
    /**
1326
     * @param array $meeting
1327
     *
1328
     * @return string
1329
     */
1330
    public function publishUrl($meeting)
1331
    {
1332
        if (!isset($meeting['id'])) {
1333
            return '';
1334
        }
1335
1336
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams(
1337
            ).'&action=publish&id='.$meeting['id'];
1338
    }
1339
1340
    /**
1341
     * @param array $meeting
1342
     * @param array $recordInfo
1343
     *
1344
     * @return string
1345
     */
1346
    public function regenerateRecordUrl($meeting, $recordInfo)
1347
    {
1348
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1349
            return '';
1350
        }
1351
1352
        if (!isset($meeting['id'])) {
1353
            return '';
1354
        }
1355
1356
        if (empty($recordInfo) || (!empty($recordInfo['recordId']) && !isset($recordInfo['recordId']))) {
1357
            return '';
1358
        }
1359
1360
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().
1361
            '&action=regenerate_record&id='.$meeting['id'].'&record_id='.$recordInfo['recordId'];
1362
    }
1363
1364
    /**
1365
     * @param array $meeting
1366
     *
1367
     * @return string
1368
     */
1369
    public function regenerateRecordUrlFromMeeting($meeting)
1370
    {
1371
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1372
            return '';
1373
        }
1374
1375
        if (!isset($meeting['id'])) {
1376
            return '';
1377
        }
1378
1379
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().
1380
            '&action=regenerate_record&id='.$meeting['id'];
1381
    }
1382
1383
    /**
1384
     * @param array $meeting
1385
     *
1386
     * @return string
1387
     */
1388
    public function deleteRecordUrl($meeting)
1389
    {
1390
        if (!isset($meeting['id'])) {
1391
            return '';
1392
        }
1393
1394
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams(
1395
            ).'&action=delete_record&id='.$meeting['id'];
1396
    }
1397
1398
    /**
1399
     * @param array $meeting
1400
     *
1401
     * @return string
1402
     */
1403
    public function copyToRecordToLinkTool($meeting)
1404
    {
1405
        if (!isset($meeting['id'])) {
1406
            return '';
1407
        }
1408
1409
        return api_get_path(WEB_PLUGIN_PATH).
1410
            'bbb/listing.php?'.$this->getUrlParams().'&action=copy_record_to_link_tool&id='.$meeting['id'];
1411
    }
1412
1413
    /**
1414
     * Function disabled
1415
     */
1416
    public function publishMeeting($id)
1417
    {
1418
        //return BigBlueButtonBN::setPublishRecordings($id, 'true', $this->url, $this->salt);
1419
        if (empty($id)) {
1420
            return false;
1421
        }
1422
        $id = intval($id);
1423
        Database::update($this->table, array('visibility' => 1), array('id = ? ' => $id));
1424
1425
        return true;
1426
    }
1427
1428
    /**
1429
     * Function disabled
1430
     */
1431
    public function unpublishMeeting($id)
1432
    {
1433
        //return BigBlueButtonBN::setPublishRecordings($id, 'false', $this->url, $this->salt);
1434
        if (empty($id)) {
1435
            return false;
1436
        }
1437
        $id = intval($id);
1438
        Database::update($this->table, array('visibility' => 0), array('id = ?' => $id));
1439
1440
        return true;
1441
    }
1442
1443
    /**
1444
     * Get users online in the current course room.
1445
     *
1446
     * @return int The number of users currently connected to the videoconference
1447
     * @assert () > -1
1448
     */
1449
    public function getUsersOnlineInCurrentRoom()
1450
    {
1451
        $courseId = api_get_course_int_id();
1452
        $sessionId = api_get_session_id();
1453
1454
        $conditions = array(
1455
            'where' => array(
1456
                'c_id = ? AND session_id = ? AND status = 1 AND access_url = ?' => array(
1457
                    $courseId,
1458
                    $sessionId,
1459
                    $this->accessUrl,
1460
                ),
1461
            ),
1462
        );
1463
1464
        if ($this->hasGroupSupport()) {
1465
            $groupId = api_get_group_id();
1466
            $conditions = array(
1467
                'where' => array(
1468
                    'c_id = ? AND session_id = ? AND group_id = ? AND status = 1 AND access_url = ?' => array(
1469
                        $courseId,
1470
                        $sessionId,
1471
                        $groupId,
1472
                        $this->accessUrl,
1473
                    ),
1474
                ),
1475
            );
1476
        }
1477
1478
        if ($this->isGlobalConferencePerUserEnabled()) {
1479
            $conditions = array(
1480
                'where' => array(
1481
                    'user_id = ? AND status = 1 AND access_url = ?' => array(
1482
                        $this->userId,
1483
                        $this->accessUrl,
1484
                    ),
1485
                ),
1486
            );
1487
        }
1488
1489
        $meetingData = Database::select(
1490
            '*',
1491
            $this->table,
1492
            $conditions,
1493
            'first'
1494
        );
1495
1496
        if (empty($meetingData)) {
1497
            return 0;
1498
        }
1499
        $pass = $meetingData['moderator_pw'];
1500
        $info = $this->getMeetingInfo(array('meetingId' => $meetingData['remote_id'], 'password' => $pass));
1501
        if ($info === false) {
1502
            //checking with the remote_id didn't work, so just in case and
1503
            // to provide backwards support, check with the id
1504
            $params = array(
1505
                'meetingId' => $meetingData['id'],
1506
                //  -- REQUIRED - The unique id for the meeting
1507
                'password' => $pass
1508
                //  -- REQUIRED - The moderator password for the meeting
1509
            );
1510
            $info = $this->getMeetingInfo($params);
1511
        }
1512
1513
        if (!empty($info) && isset($info['participantCount'])) {
1514
            return $info['participantCount'];
1515
        }
1516
1517
        return 0;
1518
    }
1519
1520
    /**
1521
     * @param int    $id
1522
     * @param string $recordId
1523
     *
1524
     * @return bool
1525
     */
1526
    public function regenerateRecording($id, $recordId = '')
1527
    {
1528
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1529
            return false;
1530
        }
1531
1532
        if (empty($id)) {
1533
            return false;
1534
        }
1535
1536
        $meetingData = Database::select(
1537
            '*',
1538
            $this->table,
1539
            array('where' => array('id = ?' => array($id))),
1540
            'first'
1541
        );
1542
1543
        Event::addEvent(
1544
            'bbb_regenerate_record',
1545
            'record_id',
1546
            (int) $recordId,
1547
            null,
1548
            api_get_user_id(),
1549
            api_get_course_int_id(),
1550
            api_get_session_id()
1551
        );
1552
1553
        // Check if there are recordings for this meeting
1554
        $recordings = $this->api->getRecordings(['meetingId' => $meetingData['remote_id']]);
1555
        if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] === 'noRecordings') {
1556
            // Regenerate the meeting id
1557
            if (!empty($meetingData['internal_meeting_id'])) {
1558
                return $this->api->generateRecording(['recordId' => $meetingData['internal_meeting_id']]);
1559
            }
1560
1561
            /*$pass = $this->getModMeetingPassword();
1562
            $info = $this->getMeetingInfo(['meetingId' => $meetingData['remote_id'], 'password' => $pass]);
1563
            if (!empty($info) && isset($info['internalMeetingID'])) {
1564
                return $this->api->generateRecording(['recordId' => $meetingData['internal_meeting_id']]);
1565
            }*/
1566
1567
            return false;
1568
        } else {
1569
            if (!empty($recordings['records'])) {
1570
                $recordExists = false;
1571
                foreach ($recordings['records'] as $record) {
1572
                    if ($recordId == $record['recordId']) {
1573
                        $recordExists = true;
1574
                        break;
1575
                    }
1576
                }
1577
1578
                if ($recordExists) {
1579
                    return $this->api->generateRecording(['recordId' => $recordId]);
1580
                }
1581
            }
1582
        }
1583
1584
        return false;
1585
    }
1586
1587
    /**
1588
     * Deletes a recording of a meeting
1589
     *
1590
     * @param int $id ID of the recording
1591
     *
1592
     * @return bool
1593
     *
1594
     * @assert () === false
1595
     * @todo Also delete links and agenda items created from this recording
1596
     */
1597
    public function deleteRecording($id)
1598
    {
1599
        if (empty($id)) {
1600
            return false;
1601
        }
1602
1603
        $meetingData = Database::select(
1604
            '*',
1605
            $this->table,
1606
            array('where' => array('id = ?' => array($id))),
1607
            'first'
1608
        );
1609
1610
        Event::addEvent(
1611
            'bbb_delete_record',
1612
            'meeting_id',
1613
            $id,
1614
            null,
1615
            api_get_user_id(),
1616
            api_get_course_int_id(),
1617
            api_get_session_id()
1618
        );
1619
1620
        $delete = false;
1621
        $recordings = [];
1622
        // Check if there are recordings for this meeting
1623
        if (!empty($meetingData['remote_id'])) {
1624
            Event::addEvent(
1625
                'bbb_delete_record',
1626
                'remote_id',
1627
                $meetingData['remote_id'],
1628
                null,
1629
                api_get_user_id(),
1630
                api_get_course_int_id(),
1631
                api_get_session_id()
1632
            );
1633
            $recordings = $this->api->getRecordings(['meetingId' => $meetingData['remote_id']]);
1634
        }
1635
        if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] == 'noRecordings') {
1636
            $delete = true;
1637
        } else {
1638
            if (!empty($recordings['records'])) {
1639
                $recordsToDelete = [];
1640
                foreach ($recordings['records'] as $record) {
1641
                    $recordsToDelete[] = $record['recordId'];
1642
                }
1643
                $delete = true;
1644
                if (!empty($recordsToDelete)) {
1645
                    $recordingParams = ['recordId' => implode(',', $recordsToDelete)];
1646
                    Event::addEvent(
1647
                        'bbb_delete_record',
1648
                        'record_id_list',
1649
                        implode(',', $recordsToDelete),
1650
                        null,
1651
                        api_get_user_id(),
1652
                        api_get_course_int_id(),
1653
                        api_get_session_id()
1654
                    );
1655
                    $result = $this->api->deleteRecordingsWithXmlResponseArray($recordingParams);
1656
                    if (!empty($result) && isset($result['deleted']) && $result['deleted'] === 'true') {
1657
                        $delete = true;
1658
                    }
1659
                }
1660
            }
1661
        }
1662
1663
        if ($delete) {
1664
            Database::delete(
1665
                'plugin_bbb_room',
1666
                array('meeting_id = ?' => array($id))
1667
            );
1668
1669
            Database::delete(
1670
                $this->table,
1671
                array('id = ?' => array($id))
1672
            );
1673
        }
1674
1675
        return $delete;
1676
    }
1677
1678
    /**
1679
     * Creates a link in the links tool from the given videoconference recording
1680
     *
1681
     * @param int $id ID of the item in the plugin_bbb_meeting table
1682
     * @param string Hash identifying the recording, as provided by the API
1683
     *
1684
     * @return mixed ID of the newly created link, or false on error
1685
     * @assert (null, null) === false
1686
     * @assert (1, null) === false
1687
     * @assert (null, 'abcdefabcdefabcdefabcdef') === false
1688
     */
1689
    public function copyRecordingToLinkTool($id)
1690
    {
1691
        if (empty($id)) {
1692
            return false;
1693
        }
1694
        //$records =  BigBlueButtonBN::getRecordingsUrl($id);
1695
        $meetingData = Database::select(
1696
            '*',
1697
            $this->table,
1698
            array('where' => array('id = ?' => array($id))),
1699
            'first'
1700
        );
1701
1702
        $records = $this->api->getRecordingsWithXmlResponseArray(
1703
            array('meetingId' => $meetingData['remote_id'])
1704
        );
1705
1706
        if (!empty($records)) {
1707
            if (isset($records['message']) && !empty($records['message'])) {
1708
                if ($records['messageKey'] == 'noRecordings') {
1709
                    $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...
1710
                } else {
1711
                    //$recordArray[] = $records['message'];
1712
                }
1713
1714
                return false;
1715
            } else {
1716
                $record = $records[0];
1717
                if (is_array($record) && isset($record['recordId'])) {
1718
                    $url = $record['playbackFormatUrl'];
1719
                    $link = new Link();
1720
                    $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...
1721
                    $params['title'] = $meetingData['meeting_name'];
1722
                    $id = $link->save($params);
1723
1724
                    return $id;
1725
                }
1726
            }
1727
        }
1728
1729
        return false;
1730
    }
1731
1732
    /**
1733
     * Checks if the video conference server is running.
1734
     * Function currently disabled (always returns 1)
1735
     * @return bool True if server is running, false otherwise
1736
     * @assert () === false
1737
     */
1738
    public function isServerRunning()
1739
    {
1740
        return true;
1741
        //return BigBlueButtonBN::isServerRunning($this->protocol.$this->url);
1742
    }
1743
1744
    /**
1745
     * Checks if the video conference plugin is properly configured
1746
     * @return bool True if plugin has a host and a salt, false otherwise
1747
     * @assert () === false
1748
     */
1749
    public function isServerConfigured()
1750
    {
1751
        $host = $this->plugin->get('host');
1752
1753
        if (empty($host)) {
1754
            return false;
1755
        }
1756
1757
        $salt = $this->plugin->get('salt');
1758
1759
        if (empty($salt)) {
1760
            return false;
1761
        }
1762
1763
        return true;
1764
        //return BigBlueButtonBN::isServerRunning($this->protocol.$this->url);
1765
    }
1766
1767
    /**
1768
     * Get active session in the all platform
1769
     */
1770
    public function getActiveSessionsCount()
1771
    {
1772
        $meetingList = Database::select(
1773
            'count(id) as count',
1774
            $this->table,
1775
            array('where' => array('status = ? AND access_url = ?' => array(1, $this->accessUrl))),
1776
            'first'
1777
        );
1778
1779
        return $meetingList['count'];
1780
    }
1781
1782
    /**
1783
     * @param string $url
1784
     */
1785
    public function redirectToBBB($url)
1786
    {
1787
        if (file_exists(__DIR__.'/../config.vm.php')) {
1788
            // Using VM
1789
            echo Display::url($this->plugin->get_lang('ClickToContinue'), $url);
1790
            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...
1791
        } else {
1792
            // Classic
1793
            header("Location: $url");
1794
            exit;
1795
        }
1796
    }
1797
1798
    /**
1799
     * @return string
1800
     */
1801
    public function getConferenceUrl()
1802
    {
1803
        return api_get_path(WEB_PLUGIN_PATH).'bbb/start.php?launch=1&'.$this->getUrlParams();
1804
    }
1805
1806
    /**
1807
     * Get the meeting info from DB by its name
1808
     *
1809
     * @param string $name
1810
     *
1811
     * @return array
1812
     */
1813
    public function findMeetingByName($name)
1814
    {
1815
        $meetingData = Database::select(
1816
            '*',
1817
            'plugin_bbb_meeting',
1818
            array('where' => array('meeting_name = ? AND status = 1 ' => $name)),
1819
            'first'
1820
        );
1821
1822
        return $meetingData;
1823
    }
1824
1825
    /**
1826
     * Get the meeting info from DB by its name
1827
     *
1828
     * @param int $id
1829
     *
1830
     * @return array
1831
     */
1832
    public function getMeeting($id)
1833
    {
1834
        $meetingData = Database::select(
1835
            '*',
1836
            'plugin_bbb_meeting',
1837
            array('where' => array('id = ?' => $id)),
1838
            'first'
1839
        );
1840
1841
        return $meetingData;
1842
    }
1843
1844
    /**
1845
     * Get the meeting info.
1846
     *
1847
     * @param int $id
1848
     *
1849
     * @return array
1850
     */
1851
    public function getMeetingByRemoteId($id)
1852
    {
1853
        $meetingData = Database::select(
1854
            '*',
1855
            'plugin_bbb_meeting',
1856
            array('where' => array('remote_id = ?' => $id)),
1857
            'first'
1858
        );
1859
1860
        return $meetingData;
1861
    }
1862
1863
    /**
1864
     * @param int $meetingId
1865
     *
1866
     * @return array
1867
     */
1868
    public function findConnectedMeetingParticipants($meetingId)
1869
    {
1870
        $meetingData = Database::select(
1871
            '*',
1872
            'plugin_bbb_room',
1873
            array('where' => array('meeting_id = ? AND in_at IS NOT NULL' => $meetingId))
1874
        );
1875
        $participantIds = [];
1876
        $return = [];
1877
1878
        foreach ($meetingData as $participantInfo) {
1879
            if (in_array($participantInfo['participant_id'], $participantIds)) {
1880
                continue;
1881
            }
1882
1883
            $participantIds[] = $participantInfo['participant_id'];
1884
1885
            $return[] = [
1886
                'id' => $participantInfo['id'],
1887
                'meeting_id' => $participantInfo['meeting_id'],
1888
                'participant' => api_get_user_entity($participantInfo['participant_id']),
1889
                'in_at' => $participantInfo['in_at'],
1890
                'out_at' => $participantInfo['out_at'],
1891
            ];
1892
        }
1893
1894
        return $return;
1895
    }
1896
1897
    /**
1898
     * Check if the meeting has a capture.m4v video file. If exists then the has_video_m4v field is updated
1899
     *
1900
     * @param int $meetingId
1901
     *
1902
     * @return bool
1903
     */
1904
    public function checkDirectMeetingVideoUrl($meetingId)
1905
    {
1906
        $meetingInfo = Database::select(
1907
            '*',
1908
            'plugin_bbb_meeting',
1909
            [
1910
                'where' => ['id = ?' => intval($meetingId)],
1911
            ],
1912
            'first'
1913
        );
1914
1915
        if (!isset($meetingInfo['video_url'])) {
1916
            return false;
1917
        }
1918
1919
        $hasCapture = SocialManager::verifyUrl($meetingInfo['video_url'].'/capture.m4v');
1920
1921
        if ($hasCapture) {
1922
            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...
1923
                'plugin_bbb_meeting',
1924
                ['has_video_m4v' => true],
1925
                ['id = ?' => intval($meetingId)]
1926
            );
1927
        }
1928
1929
        return $hasCapture;
1930
    }
1931
}
1932