From 628ee05f9806ad71031708270d259f1bdd02cb49 Mon Sep 17 00:00:00 2001 From: Laurent David Date: Wed, 8 Dec 2021 15:26:15 +0100 Subject: [PATCH] MDL-73308 mod_bigbluebuttonn: Breakout room * Fetch recording attached to breakout meetings * Add tests for breakoutrooms --- .../classes/local/proxy/recording_proxy.php | 25 ++--- mod/bigbluebuttonbn/classes/recording.php | 31 +++++- .../classes/test/testcase_helper_trait.php | 9 +- mod/bigbluebuttonbn/tests/generator/lib.php | 21 +++- .../recordings/recording_data_test.php | 11 +-- .../local/proxy/recording_proxy_test.php | 98 +++++++++++++++++++ mod/bigbluebuttonbn/tests/recording_test.php | 48 +++++++++ 7 files changed, 210 insertions(+), 33 deletions(-) create mode 100644 mod/bigbluebuttonbn/tests/local/proxy/recording_proxy_test.php diff --git a/mod/bigbluebuttonbn/classes/local/proxy/recording_proxy.php b/mod/bigbluebuttonbn/classes/local/proxy/recording_proxy.php index a5e3d95f832..154dbae8ac5 100644 --- a/mod/bigbluebuttonbn/classes/local/proxy/recording_proxy.php +++ b/mod/bigbluebuttonbn/classes/local/proxy/recording_proxy.php @@ -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; + } } } } diff --git a/mod/bigbluebuttonbn/classes/recording.php b/mod/bigbluebuttonbn/classes/recording.php index da97da1a7b5..04669ba7411 100644 --- a/mod/bigbluebuttonbn/classes/recording.php +++ b/mod/bigbluebuttonbn/classes/recording.php @@ -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."); diff --git a/mod/bigbluebuttonbn/classes/test/testcase_helper_trait.php b/mod/bigbluebuttonbn/classes/test/testcase_helper_trait.php index 516421ac529..e10cfe69dcd 100644 --- a/mod/bigbluebuttonbn/classes/test/testcase_helper_trait.php +++ b/mod/bigbluebuttonbn/classes/test/testcase_helper_trait.php @@ -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( diff --git a/mod/bigbluebuttonbn/tests/generator/lib.php b/mod/bigbluebuttonbn/tests/generator/lib.php index b11cc56425e..18f64d5d65b 100644 --- a/mod/bigbluebuttonbn/tests/generator/lib.php +++ b/mod/bigbluebuttonbn/tests/generator/lib.php @@ -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); diff --git a/mod/bigbluebuttonbn/tests/local/bigbluebutton/recordings/recording_data_test.php b/mod/bigbluebuttonbn/tests/local/bigbluebutton/recordings/recording_data_test.php index 542cf724401..4622c9d9e83 100644 --- a/mod/bigbluebuttonbn/tests/local/bigbluebutton/recordings/recording_data_test.php +++ b/mod/bigbluebuttonbn/tests/local/bigbluebutton/recordings/recording_data_test.php @@ -14,19 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * 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 diff --git a/mod/bigbluebuttonbn/tests/local/proxy/recording_proxy_test.php b/mod/bigbluebuttonbn/tests/local/proxy/recording_proxy_test.php new file mode 100644 index 00000000000..9807c73fab6 --- /dev/null +++ b/mod/bigbluebuttonbn/tests/local/proxy/recording_proxy_test.php @@ -0,0 +1,98 @@ +. + +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); + } +} diff --git a/mod/bigbluebuttonbn/tests/recording_test.php b/mod/bigbluebuttonbn/tests/recording_test.php index c5722dbb5f0..ebdaf457680 100644 --- a/mod/bigbluebuttonbn/tests/recording_test.php +++ b/mod/bigbluebuttonbn/tests/recording_test.php @@ -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); + } }