MDL-73308 mod_bigbluebuttonn: Breakout room

* Fetch recording attached to breakout meetings
* Add tests for breakoutrooms
This commit is contained in:
Laurent David 2021-12-08 15:26:15 +01:00
parent d24a4ab56f
commit 628ee05f98
7 changed files with 210 additions and 33 deletions

View file

@ -223,23 +223,24 @@ class recording_proxy extends proxy_base {
}
$recordings = [];
// If there were meetings already created.
// If there were recordings already created.
foreach ($xml->recordings->recording as $recordingxml) {
$recording = self::parse_recording($recordingxml);
$recordings[$recording['recordID']] = $recording;
// Check if there is childs.
// Check if there are any child.
if (isset($recordingxml->breakoutRooms->breakoutRoom)) {
$breakoutrooms = [];
foreach ($recordingxml->breakoutRooms->breakoutRoom as $breakoutroom) {
$xml = self::fetch_endpoint_xml('getRecordings', ['recordID' => implode(',', (array) $breakoutroom)]);
if (!$xml || $xml->returncode != 'SUCCESS' || empty($xml->recordings)) {
continue;
}
// If there were meetings already created.
foreach ($xml->recordings->recording as $subrecordingxml) {
$recording = self::parse_recording($subrecordingxml);
$recordings[$recording['recordID']] = $recording;
$breakoutrooms[] = trim((string) $breakoutroom);
}
if ($breakoutrooms) {
$xml = self::fetch_endpoint_xml('getRecordings', ['recordID' => implode(',', $breakoutrooms)]);
if ($xml && $xml->returncode == 'SUCCESS' && isset($xml->recordings)) {
// If there were already created meetings.
foreach ($xml->recordings->recording as $subrecordingxml) {
$recording = self::parse_recording($subrecordingxml);
$recordings[$recording['recordID']] = $recording;
}
}
}
}

View file

@ -117,8 +117,6 @@ class recording extends persistent {
bool $includeimported = false,
bool $onlyimported = false
): array {
global $DB;
[$selects, $params] = self::get_basic_select_from_parameters(false, $includeimported, $onlyimported);
$selects[] = "bigbluebuttonbnid = :bbbid";
$params['bbbid'] = $instance->get_instance_id();
@ -802,12 +800,37 @@ class recording extends persistent {
$foundcount = 0;
foreach ($metadatas as $recordingid => $metadata) {
mtrace("==> Found updated metadata for {$recordingid}. Updating local cache.");
mtrace("==> Found metadata for {$recordingid}.");
$id = array_search($recordingid, $recordingids);
if (!$id) {
// Recording was not found, skip.
mtrace("===> Skip as fetched recording was not found.");
continue;
}
// Recording was found, update status.
mtrace("===> Update local cache as fetched recording was found.");
$recording = new self(0, $recordings[$id], $metadata);
$recording->set_status(self::RECORDING_STATUS_PROCESSED);
$foundcount++;
// Iterate breakout recordings (if any) and update status.
foreach ($metadata['breakouts'] as $breakoutrecordingid => $breakoutmetadata) {
$breakoutrecording = self::get_record(['recordingid' => $breakoutrecordingid]);
if (!$breakoutrecording) {
$breakoutrecording = new recording(0,
(object) [
'courseid' => $recording->get('courseid'),
'bigbluebuttonbnid' => $recording->get('bigbluebuttonbnid'),
'groupid' => $recording->get('groupid'),
'recordingid' => $breakoutrecordingid
],
$breakoutmetadata
);
$breakoutrecording->create();
}
$breakoutrecording->set_status(self::RECORDING_STATUS_PROCESSED);
$foundcount++;
}
}
mtrace("=> Finished processing recordings. Updated status for {$foundcount} / {$recordingcount} recordings.");

View file

@ -160,17 +160,18 @@ trait testcase_helper_trait {
* @return array
* @throws \coding_exception
*/
protected function create_recordings_for_instance(instance $instance, array $recordingdata = []): array {
protected function create_recordings_for_instance(instance $instance, array $recordingdata = [],
$additionalmeetingdata = []): array {
$recordings = [];
$bbbgenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn');
// Create the meetings on the mock server, so like this we can find the recordings.
$meeting = new meeting($instance);
if (!$meeting->is_running()) {
$bbbgenerator->create_meeting([
$additionalmeetingdata = array_merge([
'instanceid' => $instance->get_instance_id(),
'groupid' => $instance->get_group_id()
]);
], $additionalmeetingdata);
$bbbgenerator->create_meeting($additionalmeetingdata);
}
foreach ($recordingdata as $rindex => $data) {
$recordings[] = $bbbgenerator->create_recording(

View file

@ -233,9 +233,8 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator {
*/
protected function create_mockserver_recording(instance $instance, stdClass $recordingdata, array $data): string {
$mockdata = array_merge((array) $recordingdata, [
'meetingID' => $instance->get_meeting_id(),
'sequence' => 1,
'meta' => [
'isBreakout' => 'false',
'bn-presenter-name' => $data['presentername'] ?? 'Fake presenter',
'bn-recording-ready-url' => new moodle_url('/mod/bigbluebuttonbn/bbb_broker.php', [
'action' => 'recording_ready',
@ -246,6 +245,15 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator {
'bbb-recording-tags' => $data['tags'] ?? '',
],
]);
if (!empty($data['isBreakout'])) {
// If it is a breakout meeting, we do not have any way to know the real Id of the meeting
// unless we query the list of submeetings.
// For now we will just send the parent ID and let the mock server deal with the sequence + parentID
// to find the meetingID.
$mockdata['parentMeetingID'] = $instance->get_meeting_id();
} else {
$mockdata['meetingID'] = $instance->get_meeting_id();
}
$result = $this->send_mock_request('backoffice/createRecording', [], $mockdata);
@ -269,7 +277,6 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator {
// Default room configuration.
$roomconfig = array_merge($data, [
'meetingID' => $meetingid,
'meetingName' => $instance->get_meeting_name(),
'attendeePW' => $instance->get_viewer_password(),
'moderatorPW' => $instance->get_moderator_password(),
@ -285,6 +292,14 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator {
'bbb-recording-name' => $instance->get_meeting_name(),
],
]);
if (!empty($roomconfig['isBreakout'])) {
// If it is a breakout meeting, we do not have any way to know the real Id of the meeting
// For now we will just send the parent ID and let the mock server deal with the sequence + parentID
// to find the meetingID.
$roomconfig['parentMeetingID'] = $instance->get_meeting_id();
} else {
$roomconfig['meetingID'] = $meetingid;
}
$this->send_mock_request('backoffice/createMeeting', [], $roomconfig);

View file

@ -14,19 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy provider tests.
*
* @package mod_bigbluebuttonbn
* @copyright 2018 - present, Blindside Networks Inc
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
*/
namespace mod_bigbluebuttonbn\local\bigbluebutton\recordings;
/**
* Privacy provider tests class.
* Recording data tests.
*
* @package mod_bigbluebuttonbn
* @copyright 2018 - present, Blindside Networks Inc

View file

@ -0,0 +1,98 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace mod_bigbluebuttonbn\local\proxy;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\test\testcase_helper_trait;
/**
* Recording proxy tests class.
*
* @package mod_bigbluebuttonbn
* @copyright 2018 - present, Blindside Networks Inc
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Jesus Federico (jesus [at] blindsidenetworks [dt] com)
* @covers \mod_bigbluebuttonbn\local\proxy\recording_proxy
* @coversDefaultClass \mod_bigbluebuttonbn\local\proxy\recording_proxy
*/
class recording_proxy_test extends \advanced_testcase {
use testcase_helper_trait;
/**
* Simple recording fetcher test
*
* @return void
*/
public function test_fetch_recordings() {
$this->resetAfterTest();
$this->initialise_mock_server();
[$context, $cm, $bbbactivity] = $this->create_instance();
$instance = instance::get_from_instanceid($bbbactivity->id);
$recordings = $this->create_recordings_for_instance($instance, [['name' => 'Recording 1'], ['name' => 'Recording 2']]);
$recordingsid = array_map(function($r) {
return $r->recordingid;
}, $recordings);
$recordings = recording_proxy::fetch_recordings($recordingsid);
$this->assertCount(2, $recordings);
}
/**
* Simple recording with breakoutroom fetcher test
*
* @return void
*/
public function test_fetch_recordings_breakoutroom() {
$this->resetAfterTest();
$this->initialise_mock_server();
[$context, $cm, $bbbactivity] = $this->create_instance();
$instance = instance::get_from_instanceid($bbbactivity->id);
$bbbgenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn');
$mainmeeting = $bbbgenerator->create_meeting([
'instanceid' => $instance->get_instance_id(),
'groupid' => $instance->get_group_id(),
]);
// TODO: here we artificially create breakout meetings but the current implementations does not handle
// breakout rooms for one BBB instance. At this point we just have the ability to retrieve subrecordings
// from breakout rooms and manage them as if they belong to the parent recording.
// The meetingId is not sent to the server but autogenerated by the mock server and
// parentID is the meetingID from the current instance.
$submeeting1 = $bbbgenerator->create_meeting([
'instanceid' => $instance->get_instance_id(),
'groupid' => $instance->get_group_id(),
'isBreakout' => true,
'sequence' => 1
]);
$submeeting2 = $bbbgenerator->create_meeting([
'instanceid' => $instance->get_instance_id(),
'groupid' => $instance->get_group_id(),
'isBreakout' => true,
'sequence' => 2
]);
$recordings = $this->create_recordings_for_instance($instance,
[
['name' => 'Recording 1'],
['name' => 'Recording 2', 'isBreakout' => true, 'sequence' => 1],
['name' => 'Recording 3', 'isBreakout' => true, 'sequence' => 2]
]
);
$recordingsid = array_map(function($r) {
return $r->recordingid;
}, $recordings);
$recordings = recording_proxy::fetch_recordings([$recordingsid[0]]);
$this->assertCount(3, $recordings);
}
}

View file

@ -253,4 +253,52 @@ class recording_test extends \advanced_testcase {
}, $recordings);
$this->assertContains($recordingname, $recordingnames);
}
/**
* Simple recording with breakoutroom fetcher test
*
* @return void
*/
public function test_recordings_breakoutroom() {
$this->resetAfterTest();
$this->initialise_mock_server();
[$context, $cm, $bbbactivity] = $this->create_instance();
$instance = instance::get_from_instanceid($bbbactivity->id);
$bbbgenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn');
$mainmeeting = $bbbgenerator->create_meeting([
'instanceid' => $instance->get_instance_id(),
'groupid' => $instance->get_group_id(),
]);
// This creates a meeting to receive the recordings (specific to the mock server implementation). See recording_proxy_test.
$bbbgenerator->create_meeting([
'instanceid' => $instance->get_instance_id(),
'groupid' => $instance->get_group_id(),
'isBreakout' => true,
'sequence' => 1
]);
$bbbgenerator->create_meeting([
'instanceid' => $instance->get_instance_id(),
'groupid' => $instance->get_group_id(),
'isBreakout' => true,
'sequence' => 2
]);
// For now only recording from the main room have been created.
$this->create_recordings_for_instance($instance,
[
['name' => 'Recording 1'],
]
);
$recordings = recording::get_recordings_for_instance($instance);
$this->assertCount(1, $recordings);
// Now the breakoutroom recordings appears.
$this->create_recordings_for_instance($instance,
[
['name' => 'Recording 2', 'isBreakout' => true, 'sequence' => 1],
['name' => 'Recording 3', 'isBreakout' => true, 'sequence' => 2]
]
);
$recordings = recording::get_recordings_for_instance($instance);
$this->assertCount(3, $recordings);
}
}