Passed
Push — master ( 2361e4...a90ea1 )
by Julito
09:52
created

bbb::getMeetingByRemoteId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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

596
            error_log("return url :"./** @scrutinizer ignore-type */ $url);
Loading history...
597
        }
598
599
        return $url;
600
    }
601
602
    /**
603
     * Get information about the given meeting
604
     * @param array ...?
605
     * @return mixed Array of information on success, false on error
606
     * @assert (array()) === false
607
     */
608
    public function getMeetingInfo($params)
609
    {
610
        try {
611
            $result = $this->api->getMeetingInfoWithXmlResponseArray($params);
612
            if ($result == null) {
613
                if ($this->debug) {
614
                    error_log("Failed to get any response. Maybe we can't contact the BBB server.");
615
                }
616
            }
617
            return $result;
618
        } catch (Exception $e) {
619
            if ($this->debug) {
620
                error_log('Caught exception: ', $e->getMessage(), "\n");
621
            }
622
        }
623
624
        return false;
625
    }
626
627
    /**
628
     * @param int $courseId
629
     * @param int $sessionId
630
     * @param int $status
631
     *
632
     * @return array
633
     */
634
    public function getAllMeetingsInCourse($courseId, $sessionId, $status)
635
    {
636
        $conditions = array(
637
            'where' => array(
638
                'status = ? AND c_id = ? AND session_id = ? ' => array(
639
                    $status,
640
                    $courseId,
641
                    $sessionId,
642
                ),
643
            ),
644
        );
645
646
        $meetingList = Database::select(
647
            '*',
648
            $this->table,
649
            $conditions
650
        );
651
652
        return $meetingList;
653
    }
654
655
    /**
656
     * Gets all the course meetings saved in the plugin_bbb_meeting table
657
     * @param int $courseId
658
     * @param int $sessionId
659
     * @param int $groupId
660
     * @param bool $isAdminReport Optional. Set to true then the report is for admins
661
     * @param array $dateRange Optional
662
     * @return array Array of current open meeting rooms
663
     */
664
    public function getMeetings(
665
        $courseId = 0,
666
        $sessionId = 0,
667
        $groupId = 0,
668
        $isAdminReport = false,
669
        $dateRange = []
670
    ) {
671
        $em = Database::getManager();
672
        $manager = $this->isConferenceManager();
673
674
        $conditions = [];
675
        if ($courseId || $sessionId || $groupId) {
676
            $conditions = array(
677
                'where' => array(
678
                    'c_id = ? AND session_id = ? ' => array($courseId, $sessionId),
679
                ),
680
            );
681
682
            if ($this->hasGroupSupport()) {
683
                $conditions = array(
684
                    'where' => array(
685
                        'c_id = ? AND session_id = ? AND group_id = ? ' => array(
686
                            $courseId,
687
                            $sessionId,
688
                            $groupId
689
                        )
690
                    )
691
                );
692
            }
693
694
            if ($this->isGlobalConferencePerUserEnabled()) {
695
                 $conditions = array(
696
                     'where' => array(
697
                         'c_id = ? AND session_id = ? AND user_id = ?' => array(
698
                             $courseId,
699
                             $sessionId,
700
                             $this->userId
701
                         ),
702
                     ),
703
                 );
704
            }
705
        }
706
707
        if (!empty($dateRange)) {
708
            $dateStart = date_create($dateRange['search_meeting_start']);
709
            $dateStart = date_format($dateStart, 'Y-m-d H:i:s');
710
            $dateEnd = date_create($dateRange['search_meeting_end']);
711
            $dateEnd = $dateEnd->add(new DateInterval('P1D'));
712
            $dateEnd = date_format($dateEnd, 'Y-m-d H:i:s');
713
714
            $conditions = array(
715
                'where' => array(
716
                    'created_at BETWEEN ? AND ? ' => array($dateStart, $dateEnd),
717
                ),
718
            );
719
        }
720
721
        $conditions['order'] = 'created_at ASC';
722
723
        $meetingList = Database::select(
724
            '*',
725
            $this->table,
726
            $conditions
727
        );
728
        $isGlobal = $this->isGlobalConference();
729
        $newMeetingList = array();
730
        foreach ($meetingList as $meetingDB) {
731
            $item = array();
732
            $courseId = $meetingDB['c_id'];
733
            $courseInfo = api_get_course_info_by_id($courseId);
734
            $courseCode = '';
735
            if (!empty($courseInfo)) {
736
                $courseCode = $courseInfo['code'];
737
            }
738
739
            if ($manager) {
740
                $pass = $this->getUserMeetingPassword($courseCode);
741
            } else {
742
                $pass = $this->getModMeetingPassword($courseCode);
743
            }
744
745
            $meetingBBB = $this->getMeetingInfo(
746
                [
747
                    'meetingId' => $meetingDB['remote_id'],
748
                    'password' => $pass
749
                ]
750
            );
751
752
            if ($meetingBBB === false) {
753
                // Checking with the remote_id didn't work, so just in case and
754
                // to provide backwards support, check with the id
755
                $params = array(
756
                    'meetingId' => $meetingDB['id'],
757
                    //  -- REQUIRED - The unique id for the meeting
758
                    'password' => $pass
759
                    //  -- REQUIRED - The moderator password for the meeting
760
                );
761
                $meetingBBB = $this->getMeetingInfo($params);
762
            }
763
764
            if ($meetingDB['visibility'] == 0 && $this->isConferenceManager() === false) {
765
                continue;
766
            }
767
768
            $meetingBBB['end_url'] = $this->endUrl($meetingDB);
769
770
            if (isset($meetingBBB['returncode']) && (string) $meetingBBB['returncode'] === 'FAILED') {
771
                if ($meetingDB['status'] == 1 && $this->isConferenceManager()) {
772
                    $this->endMeeting($meetingDB['id'], $courseCode);
773
                }
774
            } else {
775
                $meetingBBB['add_to_calendar_url'] = $this->addToCalendarUrl($meetingDB);
776
            }
777
778
            if ($meetingDB['record'] == 1) {
779
                // backwards compatibility (when there was no remote ID)
780
                $mId = $meetingDB['remote_id'];
781
                if (empty($mId)) {
782
                    $mId = $meetingDB['id'];
783
                }
784
                if (empty($mId)) {
785
                    // if the id is still empty (should *never* occur as 'id' is
786
                    // the table's primary key), skip this conference
787
                    continue;
788
                }
789
790
                $record = [];
791
                $recordingParams = ['meetingId' => $mId];
792
                $records = $this->api->getRecordingsWithXmlResponseArray($recordingParams);
793
794
                if (!empty($records)) {
795
                    if (!isset($records['messageKey']) || $records['messageKey'] !== 'noRecordings') {
796
                        $record = end($records);
797
                        if (!is_array($record) || !isset($record['recordId'])) {
798
                            continue;
799
                        }
800
801
                        if (!empty($record['playbackFormatUrl'])) {
802
                            $this->updateMeetingVideoUrl($meetingDB['id'], $record['playbackFormatUrl']);
803
                        }
804
805
                        if (!$this->isConferenceManager()) {
806
                            $record = [];
807
                        }
808
                    }
809
                }
810
811
                if (isset($record['playbackFormatUrl']) && !empty($record['playbackFormatUrl'])) {
812
                    $recordLink = Display::url(
813
                        $this->plugin->get_lang('ViewRecord'),
814
                        $record['playbackFormatUrl'],
815
                        ['target' => '_blank', 'class' => 'btn btn-default']
816
                    );
817
                } else {
818
                    $recordLink = $this->plugin->get_lang('NoRecording');
819
                }
820
821
                if ($isAdminReport) {
822
                    $this->forceCIdReq(
823
                        $courseInfo['code'],
824
                        $meetingDB['session_id'],
825
                        $meetingDB['group_id']
826
                    );
827
                }
828
829
                $actionLinks = $this->getActionLinks(
830
                    $meetingDB,
831
                    $record,
832
                    $isGlobal,
833
                    $isAdminReport
834
                );
835
                $item['show_links'] = $recordLink;
836
            } else {
837
                $actionLinks = $this->getActionLinks(
838
                    $meetingDB,
839
                    [],
840
                    $isGlobal,
841
                    $isAdminReport
842
                );
843
844
                $item['show_links'] = $this->plugin->get_lang('NoRecording');
845
            }
846
847
            $item['action_links'] = implode(PHP_EOL, $actionLinks);
848
            $item['created_at'] = api_convert_and_format_date($meetingDB['created_at']);
849
            // created_at
850
            $meetingDB['created_at'] = $item['created_at']; //avoid overwrite in array_merge() below
851
852
            $item['publish_url'] = $this->publishUrl($meetingDB);
853
            $item['unpublish_url'] = $this->unPublishUrl($meetingBBB);
854
855
            if ($meetingDB['status'] == 1) {
856
                $joinParams = [
857
                    'meetingId' => $meetingDB['remote_id'], //-- REQUIRED - A unique id for the meeting
858
                    'username' => $this->userCompleteName, //-- REQUIRED - The name that will display for the user in the meeting
859
                    'password' => $pass, //-- REQUIRED - The attendee or moderator password, depending on what's passed here
860
                    'createTime' => '', //-- OPTIONAL - string. Leave blank ('') unless you set this correctly.
861
                    'userID' => '', //	-- OPTIONAL - string
862
                    'webVoiceConf' => '', //	-- OPTIONAL - string
863
                    'interface' => $this->checkInterface($meetingDB),
864
                ];
865
                $item['go_url'] = $this->protocol.$this->api->getJoinMeetingURL($joinParams);
866
            }
867
            $item = array_merge($item, $meetingDB, $meetingBBB);
868
869
            $item['course'] = $em->find('ChamiloCoreBundle:Course', $item['c_id']);
870
            $item['session'] = $em->find('ChamiloCoreBundle:Session', $item['session_id']);
871
            $newMeetingList[] = $item;
872
        }
873
874
        return $newMeetingList;
875
    }
876
877
    /**
878
     * @param $meetingInfo
879
     *
880
     * @return int
881
     */
882
    public function checkInterface($meetingInfo)
883
    {
884
        $interface = BBBPlugin::LAUNCH_TYPE_DEFAULT;
885
886
        $type = $this->plugin->get('launch_type');
887
        switch ($type) {
888
            case BBBPlugin::LAUNCH_TYPE_DEFAULT:
889
                $interface = $this->plugin->get('interface');
890
                break;
891
            case BBBPlugin::LAUNCH_TYPE_SET_BY_TEACHER:
892
                if (isset($meetingInfo['interface'])) {
893
                    $interface = $meetingInfo['interface'];
894
                }
895
                break;
896
            case BBBPlugin::LAUNCH_TYPE_SET_BY_STUDENT:
897
                if (isset($meetingInfo['id'])) {
898
                    $roomInfo = $this->getMeetingParticipantInfo($meetingInfo['id'], api_get_user_id());
899
                    if (!empty($roomInfo) && isset($roomInfo['interface'])) {
900
                        $interface = $roomInfo['interface'];
901
                    } else {
902
                        if (isset($_REQUEST['interface'])) {
903
                            $interface = isset($_REQUEST['interface']) ? (int) $_REQUEST['interface'] : 0;
904
                        }
905
                    }
906
                }
907
                break;
908
        }
909
910
        return $interface;
911
    }
912
913
    /**
914
     * Function disabled
915
     */
916
    public function publishMeeting($id)
917
    {
918
        //return BigBlueButtonBN::setPublishRecordings($id, 'true', $this->url, $this->salt);
919
        if (empty($id)) {
920
            return false;
921
        }
922
        $id = intval($id);
923
        Database::update($this->table, array('visibility' => 1), array('id = ? ' => $id));
924
925
        return true;
926
    }
927
928
    /**
929
     * Function disabled
930
     */
931
    public function unpublishMeeting($id)
932
    {
933
        //return BigBlueButtonBN::setPublishRecordings($id, 'false', $this->url, $this->salt);
934
        if (empty($id)) {
935
            return false;
936
        }
937
        $id = intval($id);
938
        Database::update($this->table, array('visibility' => 0), array('id = ?' => $id));
939
940
        return true;
941
    }
942
943
    /**
944
     * Closes a meeting (usually when the user click on the close button from
945
     * the conferences listing.
946
     * @param string The internal ID of the meeting (id field for this meeting)
947
     * @param string $courseCode
948
     *
949
     * @return void
950
     * @assert (0) === false
951
     */
952
    public function endMeeting($id, $courseCode = null)
953
    {
954
        if (empty($id)) {
955
            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...
956
        }
957
958
        $meetingData = Database::select(
959
            '*',
960
            $this->table,
961
            array('where' => array('id = ?' => array($id))),
962
            'first'
963
        );
964
        $manager = $this->isConferenceManager();
965
        if ($manager) {
966
            $pass = $this->getUserMeetingPassword($courseCode);
967
        } else {
968
            $pass = $this->getModMeetingPassword($courseCode);
969
        }
970
971
        $endParams = array(
972
            'meetingId' => $meetingData['remote_id'], // REQUIRED - We have to know which meeting to end.
973
            'password' => $pass, // REQUIRED - Must match moderator pass for meeting.
974
        );
975
        $this->api->endMeetingWithXmlResponseArray($endParams);
976
        Database::update(
977
            $this->table,
978
            array('status' => 0, 'closed_at' => api_get_utc_datetime()),
979
            array('id = ? ' => $id)
980
        );
981
    }
982
983
    /**
984
     * Gets the password for a specific meeting for the current user
985
     * @param string $courseCode
986
     * @return string A moderator password if user is teacher, or the course code otherwise
987
     *
988
     */
989
    public function getUserMeetingPassword($courseCode = null)
990
    {
991
        if ($this->isGlobalConferencePerUserEnabled()) {
992
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
993
        }
994
995
        if ($this->isGlobalConference()) {
996
            return 'url_'.api_get_current_access_url_id();
997
        }
998
        $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
999
1000
        return $courseCode;
1001
    }
1002
1003
    /**
1004
     * Generated a moderator password for the meeting.
1005
     *
1006
     * @param string $courseCode
1007
     *
1008
     * @return string A password for the moderation of the videoconference
1009
     */
1010
    public function getModMeetingPassword($courseCode = null)
1011
    {
1012
        if ($this->isGlobalConferencePerUserEnabled()) {
1013
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id().'_mod';
1014
        }
1015
1016
        if ($this->isGlobalConference()) {
1017
            return 'url_'.api_get_current_access_url_id().'_mod';
1018
        }
1019
1020
        $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
1021
1022
        return $courseCode.'mod';
1023
    }
1024
1025
    /**
1026
     * Get users online in the current course room.
1027
     *
1028
     * @return int The number of users currently connected to the videoconference
1029
     * @assert () > -1
1030
     */
1031
    public function getUsersOnlineInCurrentRoom()
1032
    {
1033
        $courseId = api_get_course_int_id();
1034
        $sessionId = api_get_session_id();
1035
1036
        $conditions = array(
1037
            'where' => array(
1038
                'c_id = ? AND session_id = ? AND status = 1 AND access_url = ?' => array(
1039
                    $courseId,
1040
                    $sessionId,
1041
                    $this->accessUrl
1042
                ),
1043
            ),
1044
        );
1045
1046
        if ($this->hasGroupSupport()) {
1047
            $groupId = api_get_group_id();
1048
            $conditions = array(
1049
                'where' => array(
1050
                    'c_id = ? AND session_id = ? AND group_id = ? AND status = 1 AND access_url = ?' => array(
1051
                        $courseId,
1052
                        $sessionId,
1053
                        $groupId,
1054
                        $this->accessUrl
1055
                    ),
1056
                ),
1057
            );
1058
        }
1059
1060
        if ($this->isGlobalConferencePerUserEnabled()) {
1061
            $conditions = array(
1062
                'where' => array(
1063
                    'user_id = ? AND status = 1 AND access_url = ?' => array(
1064
                        $this->userId,
1065
                        $this->accessUrl
1066
                    ),
1067
                ),
1068
            );
1069
        }
1070
1071
        $meetingData = Database::select(
1072
            '*',
1073
            $this->table,
1074
            $conditions,
1075
            'first'
1076
        );
1077
1078
        if (empty($meetingData)) {
1079
            return 0;
1080
        }
1081
        $pass = $this->getModMeetingPassword();
1082
        $info = $this->getMeetingInfo(array('meetingId' => $meetingData['remote_id'], 'password' => $pass));
1083
        if ($info === false) {
1084
            //checking with the remote_id didn't work, so just in case and
1085
            // to provide backwards support, check with the id
1086
            $params = array(
1087
                'meetingId' => $meetingData['id'],
1088
                //  -- REQUIRED - The unique id for the meeting
1089
                'password' => $pass
1090
                //  -- REQUIRED - The moderator password for the meeting
1091
            );
1092
            $info = $this->getMeetingInfo($params);
1093
        }
1094
1095
        if (!empty($info) && isset($info['participantCount'])) {
1096
            return $info['participantCount'];
1097
        }
1098
1099
        return 0;
1100
    }
1101
1102
    /**
1103
     * @param int    $id
1104
     * @param string $recordId
1105
     *
1106
     * @return bool
1107
     */
1108
    public function regenerateRecording($id, $recordId = '')
1109
    {
1110
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1111
            return false;
1112
        }
1113
1114
        if (empty($id)) {
1115
            return false;
1116
        }
1117
1118
        $meetingData = Database::select(
1119
            '*',
1120
            $this->table,
1121
            array('where' => array('id = ?' => array($id))),
1122
            'first'
1123
        );
1124
1125
        // Check if there are recordings for this meeting
1126
        $recordings = $this->api->getRecordings(['meetingId' => $meetingData['remote_id']]);
1127
        if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] === 'noRecordings') {
1128
            // Regenerate the meeting id
1129
            if (!empty($meetingData['internal_meeting_id'])) {
1130
                return $this->api->generateRecording(['recordId' => $meetingData['internal_meeting_id']]);
1131
            }
1132
            /*$pass = $this->getModMeetingPassword();
1133
            $info = $this->getMeetingInfo(['meetingId' => $meetingData['remote_id'], 'password' => $pass]);
1134
            if (!empty($info) && isset($info['internalMeetingID'])) {
1135
                return $this->api->generateRecording(['recordId' => $meetingData['internal_meeting_id']]);
1136
            }*/
1137
            return false;
1138
        } else {
1139
            if (!empty($recordings['records'])) {
1140
                $recordExists = false;
1141
                foreach ($recordings['records'] as $record) {
1142
                    if ($recordId == $record['recordId']) {
1143
                        $recordExists = true;
1144
                        break;
1145
                    }
1146
                }
1147
1148
                if ($recordExists) {
1149
                    return $this->api->generateRecording(['recordId' => $recordId]);
1150
                }
1151
            }
1152
        }
1153
1154
        return false;
1155
    }
1156
1157
    /**
1158
     * Deletes a recording of a meeting
1159
     *
1160
     * @param int $id ID of the recording
1161
     *
1162
     * @return bool
1163
     *
1164
     * @assert () === false
1165
     * @todo Also delete links and agenda items created from this recording
1166
     */
1167
    public function deleteRecording($id)
1168
    {
1169
        if (empty($id)) {
1170
            return false;
1171
        }
1172
1173
        $meetingData = Database::select(
1174
            '*',
1175
            $this->table,
1176
            array('where' => array('id = ?' => array($id))),
1177
            'first'
1178
        );
1179
1180
        $delete = false;
1181
        // Check if there are recordings for this meeting
1182
        $recordings = $this->api->getRecordings(['meetingId' => $meetingData['remote_id']]);
1183
        if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] == 'noRecordings') {
1184
            $delete = true;
1185
        } else {
1186
            $recordsToDelete = [];
1187
            if (!empty($recordings['records'])) {
1188
                foreach ($recordings['records'] as $record) {
1189
                    $recordsToDelete[] = $record['recordId'];
1190
                }
1191
                $recordingParams = ['recordId' => implode(',', $recordsToDelete)];
1192
                $result = $this->api->deleteRecordingsWithXmlResponseArray($recordingParams);
1193
                if (!empty($result) && isset($result['deleted']) && $result['deleted'] === 'true') {
1194
                    $delete = true;
1195
                }
1196
            }
1197
        }
1198
1199
        if ($delete) {
1200
            Database::delete(
1201
                'plugin_bbb_room',
1202
                array('meeting_id = ?' => array($id))
1203
            );
1204
1205
            Database::delete(
1206
                $this->table,
1207
                array('id = ?' => array($id))
1208
            );
1209
        }
1210
1211
        return $delete;
1212
    }
1213
1214
    /**
1215
     * Creates a link in the links tool from the given videoconference recording
1216
     * @param int $id ID of the item in the plugin_bbb_meeting table
1217
     * @param string Hash identifying the recording, as provided by the API
1218
     * @return mixed ID of the newly created link, or false on error
1219
     * @assert (null, null) === false
1220
     * @assert (1, null) === false
1221
     * @assert (null, 'abcdefabcdefabcdefabcdef') === false
1222
     */
1223
    public function copyRecordingToLinkTool($id)
1224
    {
1225
        if (empty($id)) {
1226
            return false;
1227
        }
1228
        //$records =  BigBlueButtonBN::getRecordingsUrl($id);
1229
        $meetingData = Database::select(
1230
            '*',
1231
            $this->table,
1232
            array('where' => array('id = ?' => array($id))),
1233
            'first'
1234
        );
1235
1236
        $records = $this->api->getRecordingsWithXmlResponseArray(
1237
            array('meetingId' => $meetingData['remote_id'])
1238
        );
1239
1240
        if (!empty($records)) {
1241
            if (isset($records['message']) && !empty($records['message'])) {
1242
                if ($records['messageKey'] == 'noRecordings') {
1243
                    $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...
1244
                } else {
1245
                    //$recordArray[] = $records['message'];
1246
                }
1247
                return false;
1248
            } else {
1249
                $record = $records[0];
1250
                if (is_array($record) && isset($record['recordId'])) {
1251
                    $url = $record['playbackFormatUrl'];
1252
                    $link = new Link();
1253
                    $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...
1254
                    $params['title'] = $meetingData['meeting_name'];
1255
                    $id = $link->save($params);
1256
1257
                    return $id;
1258
                }
1259
            }
1260
        }
1261
1262
        return false;
1263
    }
1264
1265
    /**
1266
     * Checks if the video conference server is running.
1267
     * Function currently disabled (always returns 1)
1268
     * @return bool True if server is running, false otherwise
1269
     * @assert () === false
1270
     */
1271
    public function isServerRunning()
1272
    {
1273
        return true;
1274
        //return BigBlueButtonBN::isServerRunning($this->protocol.$this->url);
1275
    }
1276
1277
    /**
1278
     * Get active session in the all platform
1279
     */
1280
    public function getActiveSessionsCount()
1281
    {
1282
        $meetingList = Database::select(
1283
            'count(id) as count',
1284
            $this->table,
1285
            array('where' => array('status = ? AND access_url = ?' => array(1, $this->accessUrl))),
1286
            'first'
1287
        );
1288
1289
        return $meetingList['count'];
1290
    }
1291
1292
    /**
1293
     * @param string $url
1294
     */
1295
    public function redirectToBBB($url)
1296
    {
1297
        if (file_exists(__DIR__.'/../config.vm.php')) {
1298
            // Using VM
1299
            echo Display::url($this->plugin->get_lang('ClickToContinue'), $url);
1300
            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...
1301
        } else {
1302
            // Classic
1303
            header("Location: $url");
1304
            exit;
1305
        }
1306
    }
1307
1308
    /**
1309
     * @return string
1310
     */
1311
    public function getUrlParams()
1312
    {
1313
        if (empty($this->courseCode)) {
1314
            if ($this->isGlobalConferencePerUserEnabled()) {
1315
                return 'global=1&user_id='.$this->userId;
1316
            }
1317
1318
            if ($this->isGlobalConference()) {
1319
                return 'global=1';
1320
            }
1321
1322
            return '';
1323
        }
1324
1325
        return http_build_query([
1326
            'cidReq' => $this->courseCode,
1327
            'id_session' => $this->sessionId,
1328
            'gidReq' => $this->groupId
1329
        ]);
1330
    }
1331
1332
    /**
1333
     * @return string
1334
     */
1335
    public function getCurrentVideoConferenceName()
1336
    {
1337
        if ($this->isGlobalConferencePerUserEnabled()) {
1338
            return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
1339
        }
1340
1341
        if ($this->isGlobalConference()) {
1342
            return 'url_'.api_get_current_access_url_id();
1343
        }
1344
1345
        if ($this->hasGroupSupport()) {
1346
            return api_get_course_id().'-'.api_get_session_id().'-'.api_get_group_id();
1347
        }
1348
1349
        return api_get_course_id().'-'.api_get_session_id();
1350
    }
1351
1352
    /**
1353
     * @return string
1354
     */
1355
    public function getConferenceUrl()
1356
    {
1357
        return api_get_path(WEB_PLUGIN_PATH).'bbb/start.php?launch=1&'.$this->getUrlParams();
1358
    }
1359
1360
    /**
1361
     * @return string
1362
     */
1363
    public function getListingUrl()
1364
    {
1365
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams();
1366
    }
1367
1368
    /**
1369
     * @param array $meeting
1370
     * @return string
1371
     */
1372
    public function endUrl($meeting)
1373
    {
1374
        if (!isset($meeting['id'])) {
1375
            return '';
1376
        }
1377
1378
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=end&id='.$meeting['id'];
1379
    }
1380
1381
    /**
1382
     * @param array $meeting
1383
     * @param array $record
1384
     * @return string
1385
     */
1386
    public function addToCalendarUrl($meeting, $record = [])
1387
    {
1388
        $url = isset($record['playbackFormatUrl']) ? $record['playbackFormatUrl'] : '';
1389
1390
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=add_to_calendar&id='.$meeting['id'].'&start='.api_strtotime($meeting['created_at']).'&url='.$url;
1391
    }
1392
1393
    /**
1394
     * @param array $meeting
1395
     * @return string
1396
     */
1397
    public function publishUrl($meeting)
1398
    {
1399
        if (!isset($meeting['id'])) {
1400
            return '';
1401
        }
1402
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=publish&id='.$meeting['id'];
1403
    }
1404
1405
    /**
1406
     * @param array $meeting
1407
     * @return string
1408
     */
1409
    public function unPublishUrl($meeting)
1410
    {
1411
        if (!isset($meeting['id'])) {
1412
            return null;
1413
        }
1414
1415
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=unpublish&id='.$meeting['id'];
1416
    }
1417
1418
    /**
1419
     * @param array $meeting
1420
     * @return string
1421
     */
1422
    public function deleteRecordUrl($meeting)
1423
    {
1424
        if (!isset($meeting['id'])) {
1425
            return '';
1426
        }
1427
1428
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=delete_record&id='.$meeting['id'];
1429
    }
1430
1431
    /**
1432
     * @param array $meeting
1433
     * @param array $recordInfo
1434
     *
1435
     * @return string
1436
     */
1437
    public function regenerateRecordUrl($meeting, $recordInfo)
1438
    {
1439
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1440
            return '';
1441
        }
1442
1443
        if (!isset($meeting['id'])) {
1444
            return '';
1445
        }
1446
1447
        if (empty($recordInfo) || (!empty($recordInfo['recordId']) && !isset($recordInfo['recordId']))) {
1448
            return '';
1449
        }
1450
1451
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().
1452
            '&action=regenerate_record&id='.$meeting['id'].'&record_id='.$recordInfo['recordId'];
1453
    }
1454
1455
    /**
1456
     * @param array $meeting
1457
     *
1458
     * @return string
1459
     */
1460
    public function regenerateRecordUrlFromMeeting($meeting)
1461
    {
1462
        if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
1463
            return '';
1464
        }
1465
1466
        if (!isset($meeting['id'])) {
1467
            return '';
1468
        }
1469
1470
        return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().
1471
            '&action=regenerate_record&id='.$meeting['id'];
1472
    }
1473
1474
    /**
1475
     * @param array $meeting
1476
     * @return string
1477
     */
1478
    public function copyToRecordToLinkTool($meeting)
1479
    {
1480
        if (!isset($meeting['id'])) {
1481
            return '';
1482
        }
1483
1484
        return api_get_path(WEB_PLUGIN_PATH).
1485
            'bbb/listing.php?'.$this->getUrlParams().'&action=copy_record_to_link_tool&id='.$meeting['id'];
1486
    }
1487
1488
    /**
1489
     * Get the meeting info from DB by its name
1490
     * @param string $name
1491
     *
1492
     * @return array
1493
     */
1494
    public function findMeetingByName($name)
1495
    {
1496
        $meetingData = Database::select(
1497
            '*',
1498
            'plugin_bbb_meeting',
1499
            array('where' => array('meeting_name = ? AND status = 1 ' => $name)),
1500
            'first'
1501
        );
1502
1503
        return $meetingData;
1504
    }
1505
1506
    /**
1507
     * Get the meeting info from DB by its name
1508
     * @param int $id
1509
     *
1510
     * @return array
1511
     */
1512
    public function getMeeting($id)
1513
    {
1514
        $meetingData = Database::select(
1515
            '*',
1516
            'plugin_bbb_meeting',
1517
            array('where' => array('id = ?' => $id)),
1518
            'first'
1519
        );
1520
1521
        return $meetingData;
1522
    }
1523
1524
    /**
1525
     * Get the meeting info.
1526
     *
1527
     * @param int $id
1528
     *
1529
     * @return array
1530
     */
1531
    public function getMeetingByRemoteId($id)
1532
    {
1533
        $meetingData = Database::select(
1534
            '*',
1535
            'plugin_bbb_meeting',
1536
            array('where' => array('remote_id = ?' => $id)),
1537
            'first'
1538
        );
1539
1540
        return $meetingData;
1541
    }
1542
1543
    /**
1544
     * @param int $meetingId
1545
     * @return array
1546
     */
1547
    public function findConnectedMeetingParticipants($meetingId)
1548
    {
1549
        $meetingData = Database::select(
1550
            '*',
1551
            'plugin_bbb_room',
1552
            array('where' => array('meeting_id = ? AND in_at IS NOT NULL' => $meetingId))
1553
        );
1554
        $participantIds = [];
1555
        $return = [];
1556
1557
        foreach ($meetingData as $participantInfo) {
1558
            if (in_array($participantInfo['participant_id'], $participantIds)) {
1559
                continue;
1560
            }
1561
1562
            $participantIds[] = $participantInfo['participant_id'];
1563
1564
            $return[] = [
1565
                'id' => $participantInfo['id'],
1566
                'meeting_id' => $participantInfo['meeting_id'],
1567
                'participant' => api_get_user_entity($participantInfo['participant_id']),
1568
                'in_at' => $participantInfo['in_at'],
1569
                'out_at' => $participantInfo['out_at']
1570
            ];
1571
        }
1572
1573
        return $return;
1574
    }
1575
1576
    /**
1577
     * @param int $meetingId
1578
     * @param int $userId
1579
     *
1580
     * @return array
1581
     */
1582
    public function getMeetingParticipantInfo($meetingId, $userId)
1583
    {
1584
        $meetingData = Database::select(
1585
            '*',
1586
            'plugin_bbb_room',
1587
            array('where' => array('meeting_id = ? AND participant_id = ?' => [$meetingId, $userId])),
1588
            'first'
1589
        );
1590
1591
        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...
1592
            return $meetingData;
1593
        }
1594
1595
        return [];
1596
    }
1597
1598
    /**
1599
     * @param array $meetingInfo
1600
     * @param array $recordInfo
1601
     * @param bool  $isGlobal
1602
     * @param bool  $isAdminReport
1603
     *
1604
     * @return array
1605
     */
1606
    private function getActionLinks(
1607
        $meetingInfo,
1608
        $recordInfo,
1609
        $isGlobal = false,
1610
        $isAdminReport = false
1611
    ) {
1612
        $isVisible = $meetingInfo['visibility'] != 0;
1613
        $linkVisibility = $isVisible
1614
            ? Display::url(
1615
                Display::return_icon('visible.png', get_lang('MakeInvisible')),
1616
                $this->unPublishUrl($meetingInfo)
1617
            )
1618
            : Display::url(
1619
                Display::return_icon('invisible.png', get_lang('MakeVisible')),
1620
                $this->publishUrl($meetingInfo)
1621
            );
1622
1623
        $links = [];
1624
        if ($this->plugin->get('allow_regenerate_recording') === 'true' && $meetingInfo['record'] == 1) {
1625
            if (!empty($recordInfo)) {
1626
                $links[] = Display::url(
1627
                    Display::return_icon('reload.png', get_lang('RegenerateRecord')),
1628
                    $this->regenerateRecordUrl($meetingInfo, $recordInfo)
1629
                );
1630
            } else {
1631
                $links[] = Display::url(
1632
                    Display::return_icon('reload.png', get_lang('RegenerateRecord')),
1633
                    $this->regenerateRecordUrlFromMeeting($meetingInfo)
1634
                );
1635
            }
1636
        }
1637
1638
        if (empty($recordInfo)) {
1639
            if (!$isAdminReport) {
1640
                if ($meetingInfo['status'] == 0) {
1641
                    $links[] = Display::url(
1642
                        Display::return_icon('delete.png', get_lang('Delete')),
1643
                        $this->deleteRecordUrl($meetingInfo)
1644
                    );
1645
                    $links[] = $linkVisibility;
1646
                }
1647
1648
                return $links;
1649
            } else {
1650
                $links[] = Display::url(
1651
                    Display::return_icon('course_home.png', get_lang('GoToCourse')),
1652
                    $this->getListingUrl()
1653
                );
1654
1655
                return $links;
1656
            }
1657
        }
1658
1659
        if (!$isGlobal) {
1660
            $links[] = Display::url(
1661
                Display::return_icon('link.gif', get_lang('UrlMeetingToShare')),
1662
                $this->copyToRecordToLinkTool($meetingInfo)
1663
            );
1664
            $links[] = Display::url(
1665
                Display::return_icon('agenda.png', get_lang('AddToCalendar')),
1666
                $this->addToCalendarUrl($meetingInfo, $recordInfo)
1667
            );
1668
        }
1669
1670
        $hide = $this->plugin->get('disable_download_conference_link') === 'true' ? true : false;
1671
1672
        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...
1673
            if ($meetingInfo['has_video_m4v']) {
1674
                $links[] = Display::url(
1675
                    Display::return_icon('save.png', get_lang('DownloadFile')),
1676
                    $recordInfo['playbackFormatUrl'].'/capture.m4v',
1677
                    ['target' => '_blank']
1678
                );
1679
            } else {
1680
                $links[] = Display::url(
1681
                    Display::return_icon('save.png', get_lang('DownloadFile')),
1682
                    '#',
1683
                    [
1684
                        'id' => "btn-check-meeting-video-{$meetingInfo['id']}",
1685
                        'class' => 'check-meeting-video',
1686
                        'data-id' => $meetingInfo['id']
1687
                    ]
1688
                );
1689
            }
1690
        }
1691
1692
1693
        if (!$isAdminReport) {
1694
            $links[] = Display::url(
1695
                Display::return_icon('delete.png', get_lang('Delete')),
1696
                $this->deleteRecordUrl($meetingInfo)
1697
            );
1698
            $links[] = $linkVisibility;
1699
        } else {
1700
            $links[] = Display::url(
1701
                Display::return_icon('course_home.png', get_lang('GoToCourse')),
1702
                $this->getListingUrl()
1703
            );
1704
        }
1705
1706
1707
        return $links;
1708
    }
1709
1710
    /**
1711
     * @param int $meetingId
1712
     * @param string $videoUrl
1713
     * @return bool|int
1714
     */
1715
    public function updateMeetingVideoUrl($meetingId, $videoUrl)
1716
    {
1717
        return Database::update(
1718
            'plugin_bbb_meeting',
1719
            ['video_url' => $videoUrl],
1720
            ['id = ?' => intval($meetingId)]
1721
        );
1722
    }
1723
1724
    /**
1725
     * Check if the meeting has a capture.m4v video file. If exists then the has_video_m4v field is updated
1726
     * @param int $meetingId
1727
     * @return bool
1728
     */
1729
    public function checkDirectMeetingVideoUrl($meetingId)
1730
    {
1731
        $meetingInfo = Database::select(
1732
            '*',
1733
            'plugin_bbb_meeting',
1734
            [
1735
                'where' => ['id = ?' => intval($meetingId)]
1736
            ],
1737
            'first'
1738
        );
1739
1740
        if (!isset($meetingInfo['video_url'])) {
1741
            return false;
1742
        }
1743
1744
        $hasCapture = SocialManager::verifyUrl($meetingInfo['video_url'].'/capture.m4v');
1745
1746
        if ($hasCapture) {
1747
            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...
1748
                'plugin_bbb_meeting',
1749
                ['has_video_m4v' => true],
1750
                ['id = ?' => intval($meetingId)]
1751
            );
1752
        }
1753
1754
        return $hasCapture;
1755
    }
1756
1757
    /**
1758
     * @param array $userInfo
1759
     * @return bool
1760
     */
1761
    public static function showGlobalConferenceLink($userInfo)
1762
    {
1763
        if (empty($userInfo)) {
1764
            return false;
1765
        }
1766
        $setting = api_get_plugin_setting('bbb', 'enable_global_conference');
1767
        $settingLink = api_get_plugin_setting('bbb', 'enable_global_conference_link');
1768
        if ($setting === 'true' && $settingLink === 'true') {
1769
            //$content = Display::url(get_lang('LaunchVideoConferenceRoom'), $url);
1770
            $allowedRoles = api_get_plugin_setting(
1771
                'bbb',
1772
                'global_conference_allow_roles'
1773
            );
1774
1775
            if (api_is_platform_admin()) {
1776
                $userInfo['status'] = PLATFORM_ADMIN;
1777
            }
1778
1779
            $showGlobalLink = true;
1780
            if (!empty($allowedRoles)) {
1781
                if (!in_array($userInfo['status'], $allowedRoles)) {
1782
                    $showGlobalLink = false;
1783
                }
1784
            }
1785
1786
            return $showGlobalLink;
1787
        }
1788
    }
1789
}
1790