This commit is contained in:
Andrew Nicols
2022-02-28 14:06:38 +08:00
7 changed files with 250 additions and 64 deletions
@@ -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;
}
}
}
}
+39 -14
View File
@@ -17,7 +17,6 @@
namespace mod_bigbluebuttonbn;
use cache;
use context;
use context_course;
use context_module;
use core\persistent;
@@ -117,8 +116,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();
@@ -170,7 +167,10 @@ class recording extends persistent {
global $DB;
[$selects, $params] = self::get_basic_select_from_parameters(
$includedeleted, $includeimported, $onlyimported, $onlydeleted
$includedeleted,
$includeimported,
$onlyimported,
$onlydeleted
);
if ($courseid) {
$selects[] = "courseid = :courseid";
@@ -230,7 +230,7 @@ class recording extends persistent {
$allowedgroups = groups_get_all_groups($courseid, $USER->id);
}
}
$allowedgroupsid = array_map(function($g) {
$allowedgroupsid = array_map(function ($g) {
return $g->id;
}, $allowedgroups);
if ($groupid || empty($allowedgroups)) {
@@ -553,7 +553,6 @@ class recording extends persistent {
*/
protected function get_description() {
return trim($this->metadata_get('description'));
}
/**
@@ -570,7 +569,7 @@ class recording extends persistent {
*/
protected function get_playbacks() {
if ($playbacks = $this->metadata_get('playbacks')) {
return array_map(function(array $playback): array {
return array_map(function (array $playback): array {
$clone = array_merge([], $playback);
$clone['url'] = new moodle_url('/mod/bigbluebuttonbn/bbb_view.php', [
'action' => 'play',
@@ -695,11 +694,15 @@ class recording extends persistent {
$withindays = time() - (self::RECORDING_TIME_LIMIT_DAYS * DAYSECS);
// Fetch the local data. Arbitrary sort by id, so we get the same result on different db engines.
$recordings = $DB->get_records_select(static::TABLE, implode(" AND ", $selects), $params,
self::DEFAULT_RECORDING_SORT);
$recordings = $DB->get_records_select(
static::TABLE,
implode(" AND ", $selects),
$params,
self::DEFAULT_RECORDING_SORT
);
// Grab the recording IDs.
$recordingids = array_values(array_map(function($recording) {
$recordingids = array_values(array_map(function ($recording) {
return $recording->recordingid;
}, $recordings));
@@ -707,7 +710,7 @@ class recording extends persistent {
$metadatas = recording_proxy::fetch_recordings($recordingids);
// Return the instances.
return array_filter(array_map(function($recording) use ($metadatas, $withindays) {
return array_filter(array_map(function ($recording) use ($metadatas, $withindays) {
// Filter out if no metadata was fetched.
if (!array_key_exists($recording->recordingid, $metadatas)) {
// Mark it as dismissed if it is older than 30 days.
@@ -785,7 +788,7 @@ class recording extends persistent {
mtrace("=> Found {$recordingcount} recordings to query");
// Grab the recording IDs.
$recordingids = array_map(function($recording) {
$recordingids = array_map(function ($recording) {
return $recording->recordingid;
}, $recordings);
@@ -795,12 +798,34 @@ 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.");
@@ -159,17 +159,18 @@ trait testcase_helper_trait {
* @param array $recordingdata array of recording information
* @return array
*/
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(
@@ -265,8 +266,12 @@ trait testcase_helper_trait {
* @param bool $importrecordings
* @return array
*/
protected function create_legacy_log_entries(instance $instance, int $userid, int $count = 30,
$importrecordings = false): array {
protected function create_legacy_log_entries(
instance $instance,
int $userid,
int $count = 30,
$importrecordings = false
): array {
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn');
$plugingenerator->create_meeting([
'instanceid' => $instance->get_instance_id(),
+20 -4
View File
@@ -27,7 +27,6 @@
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\logger;
use mod_bigbluebuttonbn\recording;
use mod_bigbluebuttonbn\testing\generator\mockedserver;
/**
* bigbluebuttonbn module data generator
@@ -233,9 +232,8 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator {
protected function create_mockserver_recording(instance $instance, stdClass $recordingdata, array $data): string {
$now = time();
$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',
@@ -248,6 +246,17 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator {
]);
$mockdata['startTime'] = $data['starttime'] ?? $now;
$mockdata['endTime'] = $data['endtime'] ?? $mockdata['startTime'] + HOURSECS;
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);
return (string) $result->recordID;
@@ -270,7 +279,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(),
@@ -286,6 +294,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);
@@ -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
@@ -0,0 +1,96 @@
<?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);
}
}
+70 -18
View File
@@ -57,8 +57,11 @@ class recording_test extends \advanced_testcase {
*/
public function test_get_allrecordings_status_refresh(int $status) {
$this->resetAfterTest();
['recordings' => $recordings] = $this->create_activity_with_recordings($this->get_course(),
instance::TYPE_ALL, [['status' => $status]]);
['recordings' => $recordings] = $this->create_activity_with_recordings(
$this->get_course(),
instance::TYPE_ALL,
[['status' => $status]]
);
$this->assertEquals($status, (new recording($recordings[0]->id))->get('status'));
}
@@ -70,8 +73,11 @@ class recording_test extends \advanced_testcase {
*/
public function test_get_name(): void {
$this->resetAfterTest();
['recordings' => $recordings] = $this->create_activity_with_recordings($this->get_course(),
instance::TYPE_ALL, [['name' => 'Example name']]);
['recordings' => $recordings] = $this->create_activity_with_recordings(
$this->get_course(),
instance::TYPE_ALL,
[['name' => 'Example name']]
);
$this->assertEquals('Example name', (new recording($recordings[0]->id))->get('name'));
}
@@ -83,10 +89,11 @@ class recording_test extends \advanced_testcase {
*/
public function test_get_description(): void {
$this->resetAfterTest();
['recordings' => $recordings] = $this->create_activity_with_recordings($this->get_course(),
instance::TYPE_ALL, [[
'description' => 'Example description',
]]);
['recordings' => $recordings] = $this->create_activity_with_recordings(
$this->get_course(),
instance::TYPE_ALL,
[['description' => 'Example description']]
);
$this->assertEquals('Example description', (new recording($recordings[0]->id))->get('description'));
}
@@ -113,9 +120,11 @@ class recording_test extends \advanced_testcase {
public function test_get_allrecordings(int $type): void {
$this->resetAfterTest();
$recordingcount = 2; // Two recordings only.
list('activity' => $activity) =
$this->create_activity_with_recordings($this->get_course(),
$type, array_pad([], $recordingcount, []));
['activity' => $activity] = $this->create_activity_with_recordings(
$this->get_course(),
$type,
array_pad([], $recordingcount, [])
);
// Fetch the recordings for the instance.
// The count shoudl match the input count.
@@ -174,11 +183,11 @@ class recording_test extends \advanced_testcase {
]);
$instance = instance::get_from_instanceid($activity->id);
$instance->set_group_id(0);
$this->create_recordings_for_instance($instance, [['name' => "Pre-Recording 1"], ['name' => "Pre-Recording 2"]]);
$this->create_recordings_for_instance($instance, [['name' => "Pre-Recording 1"], ['name' => "Pre-Recording 2"]]);
$instance->set_group_id($group1->id);
$this->create_recordings_for_instance($instance, [['name' => "Group 1 Recording 1"]]);
$this->create_recordings_for_instance($instance, [['name' => "Group 1 Recording 1"]]);
$instance->set_group_id($group2->id);
$this->create_recordings_for_instance($instance, [['name' => "Group 2 Recording 1"]]);
$this->create_recordings_for_instance($instance, [['name' => "Group 2 Recording 1"]]);
$this->setUser($student1);
$instance1 = instance::get_from_instanceid($activity->id);
@@ -201,7 +210,6 @@ class recording_test extends \advanced_testcase {
$this->assertCount(4, $recordings);
$this->assert_has_recording_by_name('Pre-Recording 1', $recordings);
$this->assert_has_recording_by_name('Pre-Recording 2', $recordings);
}
/**
@@ -223,14 +231,14 @@ class recording_test extends \advanced_testcase {
'name' => 'Example'
]);
$instance = instance::get_from_instanceid($activity->id);
$this->create_recordings_for_instance($instance, [['name' => "Deleted Recording 1"]]);
$this->create_recordings_for_instance($instance, [['name' => "Deleted Recording 1"]]);
$activity2 = $plugingenerator->create_instance([
'course' => $testcourse->id,
'type' => $type,
'name' => 'Example'
]);
$instance2 = instance::get_from_instanceid($activity2->id);
$this->create_recordings_for_instance($instance2, [['name' => "Recording 1"]]);
$this->create_recordings_for_instance($instance2, [['name' => "Recording 1"]]);
bigbluebuttonbn_delete_instance($activity->id);
$recordings = recording::get_recordings_for_course($testcourse->id, [], false, false, true);
@@ -248,9 +256,53 @@ class recording_test extends \advanced_testcase {
* @param array $recordings
*/
public function assert_has_recording_by_name($recordingname, $recordings) {
$recordingnames = array_map(function($r) {
$recordingnames = array_map(function ($r) {
return $r->get('name');
}, $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);
}
}