Passed
Push — master ( 0085e5...a02707 )
by Julito
10:26
created

bbb::endMeeting()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 65
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 45
nc 7
nop 2
dl 0
loc 65
rs 8.8888
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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

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

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
399
            Event::addEvent(
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

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

399
            Event::/** @scrutinizer ignore-call */ 
400
                   addEvent(

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

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

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

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

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