From 9fea7c067fe7b9cf4f908f3807b4eaab380ab93f Mon Sep 17 00:00:00 2001 From: "Shamiso.Jaravaza" <33659194+ssj365@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:02:05 -0700 Subject: [PATCH] MDL-82872 mod_bigbluebuttonbn: Extend meeting_events with subplugins * Backport of broker events addons --- mod/bigbluebuttonbn/bbb_broker.php | 3 +- mod/bigbluebuttonbn/classes/broker.php | 52 ++++++++++++------- mod/bigbluebuttonbn/classes/event/base.php | 4 +- mod/bigbluebuttonbn/classes/extension.php | 12 +++++ .../broker_meeting_events_addons.php | 51 ++++++++++++++++++ .../tests/behat/subplugins.feature | 34 ++++++++++++ .../backup_bbbext_simple_subplugin.class.php | 2 +- .../broker_meeting_events_addons.php | 46 ++++++++++++++++ .../bigbluebuttonbn/mod_instance_helper.php | 3 ++ .../fixtures/extension/simple/db/install.xml | 1 + .../fixtures/show_simpleplugin_values.php | 44 ++++++++++++++++ .../tests/local/extension_test.php | 46 ++++++++++++++++ 12 files changed, 275 insertions(+), 23 deletions(-) create mode 100644 mod/bigbluebuttonbn/classes/local/extension/broker_meeting_events_addons.php create mode 100644 mod/bigbluebuttonbn/tests/fixtures/extension/simple/classes/bigbluebuttonbn/broker_meeting_events_addons.php create mode 100644 mod/bigbluebuttonbn/tests/fixtures/show_simpleplugin_values.php diff --git a/mod/bigbluebuttonbn/bbb_broker.php b/mod/bigbluebuttonbn/bbb_broker.php index 6d6cab0824226..19794f72a6501 100644 --- a/mod/bigbluebuttonbn/bbb_broker.php +++ b/mod/bigbluebuttonbn/bbb_broker.php @@ -38,8 +38,7 @@ $params = $_REQUEST; -$broker = new broker(); -$error = $broker->validate_parameters($params); +$error = broker::validate_parameters($params); if (!empty($error)) { header('HTTP/1.0 400 Bad Request. ' . $error); return; diff --git a/mod/bigbluebuttonbn/classes/broker.php b/mod/bigbluebuttonbn/classes/broker.php index 3c48634df31c9..161c75e06f9e0 100644 --- a/mod/bigbluebuttonbn/classes/broker.php +++ b/mod/bigbluebuttonbn/classes/broker.php @@ -30,34 +30,31 @@ * @author Jesus Federico (jesus [at] blindsidenetworks [dt] com) */ class broker { - - /** @var array List of required params */ - protected $requiredparams = [ - 'recording_ready' => [ - 'bigbluebuttonbn' => 'The BigBlueButtonBN instance ID must be specified.', - 'signed_parameters' => 'A JWT encoded string must be included as [signed_parameters].' - ], - 'meeting_events' => [ - 'bigbluebuttonbn' => 'The BigBlueButtonBN instance ID must be specified.' - ], - ]; - /** * Validate the supplied list of parameters, providing feedback about any missing or incorrect values. * * @param array $params * @return null|string */ - public function validate_parameters(array $params): ?string { - if (!isset($params['action']) || empty($params['action']) ) { - return 'Parameter ['.$params['action'].'] was not included'; + public static function validate_parameters(array $params): ?string { + $requiredparams = [ + 'recording_ready' => [ + 'bigbluebuttonbn' => 'The BigBlueButtonBN instance ID must be specified.', + 'signed_parameters' => 'A JWT encoded string must be included as [signed_parameters].', + ], + 'meeting_events' => [ + 'bigbluebuttonbn' => 'The BigBlueButtonBN instance ID must be specified.', + ], + ]; + if (!isset($params['action']) || empty($params['action'])) { + return 'Parameter [' . $params['action'] . '] was not included'; } $action = strtolower($params['action']); - if (!array_key_exists($action, $this->requiredparams)) { + if (!array_key_exists($action, $requiredparams)) { return "Action {$params['action']} can not be performed."; } - return $this->validate_parameters_message($params, $this->requiredparams[$action]); + return self::validate_parameters_message($params, $requiredparams[$action]); } /** @@ -157,13 +154,30 @@ public static function process_meeting_events(instance $instance) { // Convert JSON string to a JSON object. $jsonobj = json_decode($jsonstr); $headermsg = meeting::meeting_events($instance, $jsonobj); - header($headermsg); + self::process_extension_actions($instance, $jsonstr); } catch (Exception $e) { $msg = 'Caught exception: ' . $e->getMessage(); - header('HTTP/1.0 400 Bad Request. ' . $msg); + debugging($msg, DEBUG_DEVELOPER); + $headermsg = 'HTTP/1.0 400 Bad Request. ' . $msg; } + + header($headermsg); } + /** + * Process meeting events extension actions. + * + * @param instance $instance + * @param string $jsonstr + * @return void + */ + protected static function process_extension_actions(instance $instance, string $jsonstr) { + // Hooks for extensions. + $extensions = extension::broker_meeting_events_addons_instances($instance, $jsonstr); + foreach ($extensions as $extension) { + $extension->process_action(); + } + } /** * Get authorisation token diff --git a/mod/bigbluebuttonbn/classes/event/base.php b/mod/bigbluebuttonbn/classes/event/base.php index 629b943778d6f..c4723c219d8fb 100644 --- a/mod/bigbluebuttonbn/classes/event/base.php +++ b/mod/bigbluebuttonbn/classes/event/base.php @@ -67,7 +67,9 @@ public function get_description() { ]; $string = $this->description; foreach ($vars as $key => $value) { - $string = str_replace("##" . $key, $value, $string); + if ($value !== null) { + $string = str_replace("##" . $key, $value, $string); + } } return $string; } diff --git a/mod/bigbluebuttonbn/classes/extension.php b/mod/bigbluebuttonbn/classes/extension.php index 804ea07c583af..be47284017fa5 100644 --- a/mod/bigbluebuttonbn/classes/extension.php +++ b/mod/bigbluebuttonbn/classes/extension.php @@ -19,6 +19,7 @@ use mod_bigbluebuttonbn\local\extension\action_url_addons; use mod_bigbluebuttonbn\local\extension\mod_form_addons; use mod_bigbluebuttonbn\local\extension\mod_instance_helper; +use mod_bigbluebuttonbn\local\extension\broker_meeting_events_addons; use stdClass; use core_plugin_manager; @@ -192,4 +193,15 @@ public static function delete_instance(int $id): void { $fmclass->delete_instance($id); } } + + /** + * Get all broker_meeting_events addons classes instances + * + * @param instance|null $instance + * @param string|null $data + * @return array of custom completion addon classes instances + */ + public static function broker_meeting_events_addons_instances(instance $instance, string $data): array { + return self::get_instances_implementing(broker_meeting_events_addons::class, [$instance, $data]); + } } diff --git a/mod/bigbluebuttonbn/classes/local/extension/broker_meeting_events_addons.php b/mod/bigbluebuttonbn/classes/local/extension/broker_meeting_events_addons.php new file mode 100644 index 0000000000000..c30d09f3ebd33 --- /dev/null +++ b/mod/bigbluebuttonbn/classes/local/extension/broker_meeting_events_addons.php @@ -0,0 +1,51 @@ +. + +namespace mod_bigbluebuttonbn\local\extension; +use mod_bigbluebuttonbn\instance; + +/** + * A class to deal with broker addons in a subplugin + * + * @package mod_bigbluebuttonbn + * @copyright 2024 onwards, Blindside Networks Inc + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @author Jesus Federico (jesus [at] blindsidenetworks [dt] com) + */ +abstract class broker_meeting_events_addons { + /** + * @var instance $instance BigBlueButton instance if any + */ + protected $instance; + /** + * @var string $data data to be processed + */ + protected $data; + /** + * Constructor + * + * @param instance $instance BigBlueButton instance + * @param string $data data to be processed + */ + public function __construct(instance $instance, string $data) { + $this->instance = $instance; + $this->data = $data; + } + /** + * Data processing action + */ + abstract public function process_action(); +} diff --git a/mod/bigbluebuttonbn/tests/behat/subplugins.feature b/mod/bigbluebuttonbn/tests/behat/subplugins.feature index 9a0dab1197362..60593fd6dc94e 100644 --- a/mod/bigbluebuttonbn/tests/behat/subplugins.feature +++ b/mod/bigbluebuttonbn/tests/behat/subplugins.feature @@ -43,3 +43,37 @@ Feature: BigBlueButtonBN Subplugins test And I am on the "BBB Instance name" "bigbluebuttonbn activity editing" page When I expand all fieldsets Then I should not see "New field" + + @javascript + Scenario: I check that custom events are triggered and sent to subplugin when enabled + Given a BigBlueButton mock server is configured + And the following config values are set as admin: + | bigbluebuttonbn_meetingevents_enabled | 1 | + And the following "users" exist: + | username | firstname | lastname | email | + | traverst | Terry | Travers | t.travers@example.com | + And the following "course enrolments" exist: + | user | course | role | + | traverst | Test course | student | + And the following "mod_bigbluebuttonbn > meeting" exists: + | activity | BBB Instance name | + And I log out + And I am on the "BBB Instance name" "bigbluebuttonbn activity" page logged in as "traverst" + And I click on "Join session" "link" + And I switch to "bigbluebutton_conference" window + And I wait until the page is ready + And I follow "End Meeting" + And the BigBlueButtonBN server has received the following events from user "traverst": + | instancename | eventtype | eventdata | + | BBB Instance name | chats | 1 | + # Selenium driver does not like the click action to be done before we + # automatically close the window so we need to make sure that the window + # is closed before. + And I close all opened windows + And I switch to the main window + And the BigBlueButtonBN activity "BBB Instance name" has sent recording all its events + And I log out + And I log in as "admin" + And I run all adhoc tasks + When I am on fixture page "/mod/bigbluebuttonbn/tests/behat/fixtures/show_simpleplugin_values.php" + Then I should see "(BBB Instance name): meetingevents: 1" diff --git a/mod/bigbluebuttonbn/tests/fixtures/extension/simple/backup/moodle2/backup_bbbext_simple_subplugin.class.php b/mod/bigbluebuttonbn/tests/fixtures/extension/simple/backup/moodle2/backup_bbbext_simple_subplugin.class.php index e609840abe991..a724dbf01d367 100644 --- a/mod/bigbluebuttonbn/tests/fixtures/extension/simple/backup/moodle2/backup_bbbext_simple_subplugin.class.php +++ b/mod/bigbluebuttonbn/tests/fixtures/extension/simple/backup/moodle2/backup_bbbext_simple_subplugin.class.php @@ -37,7 +37,7 @@ protected function define_bigbluebuttonbn_subplugin_structure() { $subpluginelement = new backup_nested_element( 'bbbext_simple', null, - ['newfield', 'completionextraisehandtwice'] + ['newfield', 'completionextraisehandtwice', 'meetingevents'] ); // Connect XML elements into the tree. diff --git a/mod/bigbluebuttonbn/tests/fixtures/extension/simple/classes/bigbluebuttonbn/broker_meeting_events_addons.php b/mod/bigbluebuttonbn/tests/fixtures/extension/simple/classes/bigbluebuttonbn/broker_meeting_events_addons.php new file mode 100644 index 0000000000000..1852b55ed8c66 --- /dev/null +++ b/mod/bigbluebuttonbn/tests/fixtures/extension/simple/classes/bigbluebuttonbn/broker_meeting_events_addons.php @@ -0,0 +1,46 @@ +. + +namespace bbbext_simple\bigbluebuttonbn; + +/** + * When meeting_events callback is implemented by BigBlueButton, Moodle receives a POST request + * which is processed in the function using super globals. + * + * @package mod_bigbluebuttonbn + * @copyright 2024 onwards, Blindside Networks Inc + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @author Jesus Federico (jesus [at] blindsidenetworks [dt] com) + */ +class broker_meeting_events_addons extends \mod_bigbluebuttonbn\local\extension\broker_meeting_events_addons { + /** + * Data processing action + */ + public function process_action() { + global $DB; + if ($this->instance) { + $bigbluebuttonbnid = $this->instance->get_instance_id(); + $record = $DB->get_record('bbbext_simple', [ + 'bigbluebuttonbnid' => $bigbluebuttonbnid, + ]); + if ($record) { + $record->meetingevents = $this->data; + return $DB->update_record('bbbext_simple', $record); + } + } + return false; + } +} \ No newline at end of file diff --git a/mod/bigbluebuttonbn/tests/fixtures/extension/simple/classes/bigbluebuttonbn/mod_instance_helper.php b/mod/bigbluebuttonbn/tests/fixtures/extension/simple/classes/bigbluebuttonbn/mod_instance_helper.php index a5379b6e7a31a..abfcec1036b4d 100644 --- a/mod/bigbluebuttonbn/tests/fixtures/extension/simple/classes/bigbluebuttonbn/mod_instance_helper.php +++ b/mod/bigbluebuttonbn/tests/fixtures/extension/simple/classes/bigbluebuttonbn/mod_instance_helper.php @@ -36,6 +36,7 @@ public function add_instance(stdClass $bigbluebuttonbn) { $DB->insert_record('bbbext_simple', (object) [ 'bigbluebuttonbnid' => $bigbluebuttonbn->id, 'newfield' => $bigbluebuttonbn->newfield ?? '', + 'meetingevents' => $bigbluebuttonbn->meetingevents ?? '', ]); } @@ -54,9 +55,11 @@ public function update_instance(stdClass $bigbluebuttonbn): void { $record = new stdClass(); $record->bigbluebuttonbnid = $bigbluebuttonbn->id; $record->newfield = $bigbluebuttonbn->newfield ?? ''; + $record->meetingevents = $bigbluebuttonbn->meetingevents ?? ''; $DB->insert_record('bbbext_simple', $record); } else { $record->newfield = $bigbluebuttonbn->newfield ?? ''; + $record->meetingevents = $bigbluebuttonbn->meetingevents ?? ''; $DB->update_record('bbbext_simple', $record); } } diff --git a/mod/bigbluebuttonbn/tests/fixtures/extension/simple/db/install.xml b/mod/bigbluebuttonbn/tests/fixtures/extension/simple/db/install.xml index 27be9232c3021..4d57080633d19 100644 --- a/mod/bigbluebuttonbn/tests/fixtures/extension/simple/db/install.xml +++ b/mod/bigbluebuttonbn/tests/fixtures/extension/simple/db/install.xml @@ -9,6 +9,7 @@ + diff --git a/mod/bigbluebuttonbn/tests/fixtures/show_simpleplugin_values.php b/mod/bigbluebuttonbn/tests/fixtures/show_simpleplugin_values.php new file mode 100644 index 0000000000000..973dd38740771 --- /dev/null +++ b/mod/bigbluebuttonbn/tests/fixtures/show_simpleplugin_values.php @@ -0,0 +1,44 @@ +. + +/** + * Test page for simple subplugins. + * + * @package mod_bigbluebuttonbn + * @copyright 2024 onwards, Blindside Networks Inc + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @author Shamiso Jaravaza (shamiso [dt] jaravaza [at] blindsidenetworks [dt] com) + */ + +require_once(__DIR__ . '/../../../../../config.php'); + +defined('BEHAT_SITE_RUNNING') || die(); +global $PAGE, $OUTPUT; +require_login(); +$PAGE->set_context(context_system::instance()); +$PAGE->set_url('/mod/bigbluebuttonbn/tests/behat/fixtures/show_simpleplugin_values.php'); + +echo $OUTPUT->header(); + +$bnid = $DB->get_field('bigbluebuttonbn', 'id', ['name' => 'BBB Instance name']); +// Check that the subplugin has the correct meeting events data. +$meetingevent = $DB->get_field('bbbext_simple', 'meetingevents', ['bigbluebuttonbnid' => $bnid]); +$meetingevent = json_decode($meetingevent, true); +$chats = $meetingevent['data']['attendees'][0]['engagement']['chats']; + +echo "

(BBB Instance name): meetingevents: {$chats}

"; + +echo $OUTPUT->footer(); diff --git a/mod/bigbluebuttonbn/tests/local/extension_test.php b/mod/bigbluebuttonbn/tests/local/extension_test.php index 3b9b2c03dca3f..4ea3caa8c262a 100644 --- a/mod/bigbluebuttonbn/tests/local/extension_test.php +++ b/mod/bigbluebuttonbn/tests/local/extension_test.php @@ -17,6 +17,7 @@ use backup; use backup_controller; +use mod_bigbluebuttonbn\broker; use mod_bigbluebuttonbn\extension; use mod_bigbluebuttonbn\instance; use mod_bigbluebuttonbn\local\extension\mod_instance_helper; @@ -258,6 +259,51 @@ public function test_backup_restore(): void { $this->assertEquals(5, $oldfieldrecord->newfield); } + /** + * Test broker meeting_events with and without addons. + * @return void + * @covers \mod_bigbluebuttonbn\local\extension\broker_meeting_events_addons + */ + public function test_broker_meeting_events_addons(): void { + $this->resetAfterTest(); + global $DB; + // Enable plugin. + $this->enable_plugins(true); + $this->initialise_mock_server(); + [$bbactivitycontext, $bbactivitycm, $bbactivity] = $this->create_instance( + $this->get_course()); + $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + // Now create a couple of events. + $instance = instance::get_from_instanceid($bbactivity->id); + set_config('bigbluebuttonbn_meetingevents_enabled', true); + $meeting = $plugingenerator->create_meeting([ + 'instanceid' => $instance->get_instance_id(), + 'groupid' => $instance->get_group_id(), + 'participants' => json_encode([$user->id]), + ]); + $events = [ + (object) ['name' => 'talks'], + (object) ['name' => 'raisehand'], + (object) ['name' => 'raisehand'], + ]; + foreach ($events as $edesc) { + $plugingenerator->add_meeting_event($user, $instance, $edesc->name, $edesc->data ?? ''); + } + $result = $plugingenerator->send_all_events($instance); + $this->assertNotEmpty($result->data); + $data = json_encode($result->data); + $reflection = new \ReflectionClass(broker::class); + $method = $reflection->getMethod('process_extension_actions'); + $method->setAccessible(true); + $method->invokeArgs(null, [$instance, $data]); + $addondata = $DB->get_field('bbbext_simple', 'meetingevents', ['bigbluebuttonbnid' => $bbactivity->id]); + $addondata = json_decode($addondata); + // Check that the data is received. + $this->assertEquals(json_encode($addondata), $data); + } + /** * Data provider for testing get_class_implementing *