From 8897c98a6882c8cc9d1863a5a3871e53be1fef41 Mon Sep 17 00:00:00 2001 From: Laurent David Date: Thu, 23 Nov 2023 22:32:04 +0100 Subject: [PATCH] Finish the first implementation of the plugin * Unit test * Form and basic fields --- .github/workflows/ci.yml | 116 +++++++++++ README.md | 7 +- .../backup_bbbext_flexurl_subplugin.class.php | 12 +- ...restore_bbbext_flexurl_subplugin.class.php | 2 - classes/bigbluebuttonbn/action_url_addons.php | 40 ++-- classes/bigbluebuttonbn/mod_form_addons.php | 99 ++++++--- .../bigbluebuttonbn/mod_instance_helper.php | 76 ++++--- classes/utils.php | 108 +++++++++- db/install.xml | 2 +- lang/en/bbbext_flexurl.php | 1 + .../bigbluebutton/action_url_addons_tests.php | 97 +++++++++ tests/utils_test.php | 193 ++++++++++++++++++ 12 files changed, 647 insertions(+), 106 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 tests/bigbluebutton/action_url_addons_tests.php create mode 100644 tests/utils_test.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..50e1da5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,116 @@ +name: Moodle Plugin CI + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-22.04 + + services: + postgres: + image: postgres:13 + env: + POSTGRES_USER: 'postgres' + POSTGRES_HOST_AUTH_METHOD: 'trust' + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3 + + mariadb: + image: mariadb:10 + env: + MYSQL_USER: 'root' + MYSQL_ALLOW_EMPTY_PASSWORD: "true" + MYSQL_CHARACTER_SET_SERVER: "utf8mb4" + MYSQL_COLLATION_SERVER: "utf8mb4_unicode_ci" + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3 + + strategy: + fail-fast: false + matrix: + php: ['8.1'] + moodle-branch: ['MOODLE_403_STABLE'] + database: [pgsql, mariadb] + + steps: + - name: Check out repository code + uses: actions/checkout@v3 + with: + path: plugin + + - name: Setup PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: ${{ matrix.extensions }} + ini-values: max_input_vars=5000 + # If you are not using code coverage, keep "none". Otherwise, use "pcov" (Moodle 3.10 and up) or "xdebug". + # If you try to use code coverage with "none", it will fallback to phpdbg (which has known problems). + coverage: none + + - name: Initialise moodle-plugin-ci + run: | + composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^4 + echo $(cd ci/bin; pwd) >> $GITHUB_PATH + echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH + sudo locale-gen en_AU.UTF-8 + echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV + + - name: Install moodle-plugin-ci + run: | + moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 + env: + DB: ${{ matrix.database }} + MOODLE_BRANCH: ${{ matrix.moodle-branch }} + + - name: PHP Lint + if: ${{ !cancelled() }} + run: moodle-plugin-ci phplint + + - name: PHP Copy/Paste Detector + continue-on-error: true # This step will show errors but will not fail + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpcpd + + - name: PHP Mess Detector + continue-on-error: true # This step will show errors but will not fail + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpmd + + - name: Moodle Code Checker + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpcs --max-warnings 0 + + - name: Moodle PHPDoc Checker + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpdoc --max-warnings 0 + + - name: Validating + if: ${{ !cancelled() }} + run: moodle-plugin-ci validate + + - name: Check upgrade savepoints + if: ${{ !cancelled() }} + run: moodle-plugin-ci savepoints + + - name: Mustache Lint + if: ${{ !cancelled() }} + run: moodle-plugin-ci mustache + + - name: Grunt + if: ${{ !cancelled() }} + run: moodle-plugin-ci grunt --max-lint-warnings 0 + + - name: PHPUnit tests + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpunit --fail-on-warning + + - name: Behat features + if: ${{ !cancelled() }} + run: moodle-plugin-ci behat --profile chrome + + - name: Mark cancelled jobs as failed. + if: ${{ cancelled() }} + run: exit 1 \ No newline at end of file diff --git a/README.md b/README.md index 86b2d43..5008c26 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,16 @@ BigblueButton Extension - FlexURL * Copyright: Blindside Networks Inc * License: GNU GENERAL PUBLIC LICENSE Version 3 -This is an extension plugin for BigBluebButtonBN module that will allow you to some parameters dynamically -to the create and join URL. This is sometimes needed for specific BigBlueButton integrations. +This is an extension plugin for the BigBlueButtonBN module. This will allow user to add parameters to a create and join action url, so to +be able to address a variety of use cases. +For now the parameters are not "free forms" but fixed on entities like user, course and module so to be able to address the most common use cases while +keeping some control on the parameters that are passed to the BigBlueButton server. Description =========== This plugin shows how to extend BigBluebButtonBN module to: * Add a new parameter to an Action URL (create and join) -* Setup a new parameter in the BigBlueButtonBN module settings (and edit form) Installation diff --git a/backup/moodle2/backup_bbbext_flexurl_subplugin.class.php b/backup/moodle2/backup_bbbext_flexurl_subplugin.class.php index c729e2b..64a5404 100644 --- a/backup/moodle2/backup_bbbext_flexurl_subplugin.class.php +++ b/backup/moodle2/backup_bbbext_flexurl_subplugin.class.php @@ -33,17 +33,21 @@ protected function define_bigbluebuttonbn_subplugin_structure() { // Create XML elements. $subplugin = $this->get_subplugin_element(); $subpluginwrapper = new backup_nested_element($this->get_recommended_name()); - $subpluginelement = new backup_nested_element('bbbext_flexurl', + $subpluginelement = new backup_nested_element( + 'bbbext_flexurl', null, - ['additionalparams']); + ['eventtype', 'paramname', 'paramvalue'] + ); // Connect XML elements into the tree. $subplugin->add_child($subpluginwrapper); $subpluginwrapper->add_child($subpluginelement); // Set source to populate the data. - $subpluginelement->set_source_table('bbbext_flexurl', - ['bigbluebuttonbnid' => backup::VAR_PARENTID]); + $subpluginelement->set_source_table( + 'bbbext_flexurl', + ['bigbluebuttonbnid' => backup::VAR_PARENTID] + ); return $subplugin; } diff --git a/backup/moodle2/restore_bbbext_flexurl_subplugin.class.php b/backup/moodle2/restore_bbbext_flexurl_subplugin.class.php index 98d9b88..7155444 100644 --- a/backup/moodle2/restore_bbbext_flexurl_subplugin.class.php +++ b/backup/moodle2/restore_bbbext_flexurl_subplugin.class.php @@ -30,7 +30,6 @@ class restore_bbbext_flexurl_subplugin extends restore_subplugin { * @return array */ protected function define_bigbluebuttonbn_subplugin_structure() { - $paths = []; $elename = $this->get_namefor('bigbluebuttonbn'); @@ -38,7 +37,6 @@ protected function define_bigbluebuttonbn_subplugin_structure() { $elepath = $this->get_pathfor('/bbbext_flexurl'); $paths[] = new restore_path_element($elename, $elepath); - return $paths; } diff --git a/classes/bigbluebuttonbn/action_url_addons.php b/classes/bigbluebuttonbn/action_url_addons.php index 60f1b00..7851dc7 100644 --- a/classes/bigbluebuttonbn/action_url_addons.php +++ b/classes/bigbluebuttonbn/action_url_addons.php @@ -15,6 +15,10 @@ // along with Moodle. If not, see . namespace bbbext_flexurl\bigbluebuttonbn; +use bbbext_flexurl\utils; +use core_form\util; +use mod_bigbluebuttonbn\instance; + /** * A single action class to mutate the action URL. * @@ -36,31 +40,23 @@ class action_url_addons extends \mod_bigbluebuttonbn\local\extension\action_url_ * 'metadata' keys) */ public function execute(string $action = '', array $data = [], array $metadata = [], ?int $instanceid = null): array { - if ($action == 'create') { - $analyticcburl = get_config('bbbext_flexurl', 'analytics_callback_url'); - if ($analyticcburl) { - $metadata['analytics-callback-url'] = $analyticcburl; - } - } - if ($action == 'create' || $action == 'join') { - if (empty($instanceid)) { - if (!(defined('PHPUNIT_TEST') && PHPUNIT_TEST) && !defined('BEHAT_SITE_RUNNING')) { - // Debugging messages will fail mod_bigbluebuttonbn behat or phpunit tests as soon as the plugin is installed. - // Which is not what we want here. - debugging('No instanceid provided to action_url_addons, this mean we will not be able to retrieve any' . - 'instance specific data in the subplugins.'); - } - } else { - global $DB; - $record = $DB->get_record(mod_instance_helper::SUBPLUGIN_TABLE, [ - 'bigbluebuttonbnid' => $instanceid, - ]); - if ($record) { - $metadata['additionalparams'] = $record->additionalparams ?? ''; + global $DB; + if ($instanceid) { + $instance = instance::get_from_instanceid($instanceid); + $flexurlrecords = $DB->get_records(mod_instance_helper::SUBPLUGIN_TABLE, [ + 'bigbluebuttonbnid' => $instanceid, + ]); + $eventtypes = array_flip(utils::ACTION_CODES); + foreach ($flexurlrecords as $flexurlrecord) { + if ($flexurlrecord->eventtype != utils::ACTION_CODES['all'] && + $eventtypes[$flexurlrecord->eventtype] != $action) { + continue; } + + $metadata[$flexurlrecord->paramname] = utils::get_value_for_field($flexurlrecord->paramvalue, $instance); + } } - return ['data' => $data, 'metadata' => $metadata]; } } diff --git a/classes/bigbluebuttonbn/mod_form_addons.php b/classes/bigbluebuttonbn/mod_form_addons.php index 7e259bd..ec40d02 100644 --- a/classes/bigbluebuttonbn/mod_form_addons.php +++ b/classes/bigbluebuttonbn/mod_form_addons.php @@ -27,6 +27,50 @@ * @author Laurent David (laurent@call-learning.fr) */ class mod_form_addons extends \mod_bigbluebuttonbn\local\extension\mod_form_addons { + + /** + * Constructor + * + * @param \MoodleQuickForm $mform + * @param stdClass|null $bigbluebuttonbndata + * @param string|null $suffix + */ + public function __construct(\MoodleQuickForm &$mform, ?stdClass $bigbluebuttonbndata = null, string $suffix = null) { + parent::__construct($mform, $bigbluebuttonbndata, $suffix); + // Supplement BBB data with additional information. + if (!empty($bigbluebuttonbndata->id)) { + $data = $this->retrieve_additional_data($bigbluebuttonbndata->id); + $this->bigbluebuttonbndata = (object) array_merge((array) $this->bigbluebuttonbndata, $data); + $this->bigbluebuttonbndata->flexurl_paramcount = count($data); + } + } + + /** + * Retrieve data from the database if any. + * + * @param int $id + * @return array + */ + private function retrieve_additional_data(int $id): array { + global $DB; + $data = []; + $flexurlrecords = $DB->get_records(mod_instance_helper::SUBPLUGIN_TABLE, [ + 'bigbluebuttonbnid' => $id, + ]); + if ($flexurlrecords) { + $flexurlrecords = array_values($flexurlrecords); + foreach ($flexurlrecords as $flexurlrecord) { + foreach (utils::PARAM_TYPES as $paramtype => $paramtypevalue) { + if (!isset($data["flexurl_{$paramtype}"])) { + $data["flexurl_{$paramtype}"] = []; + } + $data["flexurl_{$paramtype}"][] = $flexurlrecord->{$paramtype} ?? ''; + } + } + } + return $data; + } + /** * Allows modules to modify the data returned by form get_data(). * This method is also called in the bulk activity completion form. @@ -49,21 +93,8 @@ public function data_postprocessing(\stdClass &$data): void { public function data_preprocessing(?array &$defaultvalues): void { // This is where we can add the data from the flexurl table to the data provided. if (!empty($defaultvalues['id'])) { - global $DB; - $flexurlrecords = $DB->get_records(mod_instance_helper::SUBPLUGIN_TABLE, [ - 'bigbluebuttonbnid' => $defaultvalues['id'], - ]); - if ($flexurlrecords) { - $flexurlrecords = array_values($flexurlrecords); - foreach($flexurlrecords as $flexurlrecord) { - foreach (utils::PARAM_TYPES as $paramtype => $paramtypevalue) { - if (!isset($defaultvalues["flexurl_{$paramtype}"])) { - $defaultvalues["flexurl_{$paramtype}"] = []; - } - $defaultvalues["flexurl_{$paramtype}"][] = $flexurlrecord->{$paramtype} ?? ''; - } - } - } + $data = $this->retrieve_additional_data(intval($defaultvalues['id'])); + $defaultvalues = (object) array_merge($defaultvalues, $data); } } @@ -101,7 +132,7 @@ public function completion_rule_enabled(array $data): bool { public function definition_after_data() { // After data. $isdeleting = optional_param_array('flexurl_paramdelete', [], PARAM_RAW); - //// Get the index of the delete button that was pressed. + // Get the index of the delete button that was pressed. if (!empty($isdeleting)) { $firstindex = array_key_first($isdeleting); // Then reassign values from the deleted group to the previous group. @@ -133,7 +164,7 @@ public function definition_after_data() { public function add_fields(): void { $this->mform->addElement('header', 'flexurl', get_string('pluginname', 'bbbext_flexurl')); - $paramcount = optional_param('flexurl_paramcount', $this->bigbluebuttonbndata->paramcount ?? 0, PARAM_RAW); + $paramcount = optional_param('flexurl_paramcount', $this->bigbluebuttonbndata->flexurl_paramcount ?? 0, PARAM_RAW); $paramcount += optional_param('flexurl_addparamgroup', 0, PARAM_RAW) ? 1 : 0; $isdeleting = optional_param_array('flexurl_paramdelete', [], PARAM_RAW); foreach ($isdeleting as $index => $value) { @@ -141,45 +172,45 @@ public function add_fields(): void { $this->mform->registerNoSubmitButton("flexurl_paramdelete[$index]"); } for ($index = 0; $index < $paramcount; $index++) { - $paramtype = $this->mform->createElement( - 'select', - "flexurl_eventtype[$index]", - get_string('param_eventtype', 'bbbext_flexurl'), - utils::get_option_for_eventtype(), - ['multiple' => true, 'size' => '2'] - ); $paramname = $this->mform->createElement( 'text', "flexurl_paramname[$index]", get_string('param_name', 'bbbext_flexurl'), - ['size' => '8'] + ['size' => '6'] ); $paramvalue = $this->mform->createElement( 'selectgroups', "flexurl_paramvalue[$index]", get_string('param_value', 'bbbext_flexurl'), utils::get_options_for_parameters(), - ['size' => '1'] ); - + $paramtype = $this->mform->createElement( + 'select', + "flexurl_eventtype[$index]", + get_string('param_eventtype', 'bbbext_flexurl'), + utils::get_option_for_eventtype(), + ); $paramdelete = $this->mform->createElement( 'submit', "flexurl_paramdelete[$index]", get_string('delete'), + [], + false, + ['customclassoverride' => 'btn-sm btn-secondary float-left'] ); $this->mform->addGroup( [ - $paramtype, $paramname, $paramvalue, $paramdelete, + $paramname, $paramvalue, $paramtype, $paramdelete, ], "flexurl_paramgroup[$index]", get_string('paramgroup', 'bbbext_flexurl'), [' '], false ); - $this->mform->setType("flexurl_paramname[$index]", PARAM_TEXT); - $this->mform->setType("flexurl_paramvalue[$index]", PARAM_RAW); - $this->mform->setType("flexurl_eventtype[$index]", PARAM_RAW); + $this->mform->setType("flexurl_paramname[$index]", utils::PARAM_TYPES['paramname']); + $this->mform->setType("flexurl_paramvalue[$index]", utils::PARAM_TYPES['paramvalue']); + $this->mform->setType("flexurl_eventtype[$index]", utils::PARAM_TYPES['eventtype']); $this->mform->setType("flexurl_paramdelete[$index]", PARAM_RAW); $this->mform->registerNoSubmitButton("flexurl_paramdelete[$index]"); } @@ -201,8 +232,10 @@ public function add_fields(): void { */ public function validation(array $data, array $files): array { $errors = []; - if (strip_tags($data['additionalparams']) != $data['additionalparams']) { - $errors['additionalparams'] = get_string('additionalparams:error', 'bbbext_flexurl'); + foreach (utils::PARAM_TYPES as $paramtype => $paramtypevalue) { + if (clean_param_array($data['flexurl_' . $paramtype], $paramtypevalue, true) === false) { + $errors["flexurl_{$paramtype}"] = get_string('invalidvalue', 'bbbext_flexurl'); + } } return $errors; } diff --git a/classes/bigbluebuttonbn/mod_instance_helper.php b/classes/bigbluebuttonbn/mod_instance_helper.php index d8cbf33..986f032 100644 --- a/classes/bigbluebuttonbn/mod_instance_helper.php +++ b/classes/bigbluebuttonbn/mod_instance_helper.php @@ -27,7 +27,9 @@ * @author Laurent David (laurent@call-learning.fr) */ class mod_instance_helper extends \mod_bigbluebuttonbn\local\extension\mod_instance_helper { - // This is the name of the table that will be used to store additional data for the instance. + /** + * This is the name of the table that will be used to store additional data for the instance. + */ const SUBPLUGIN_TABLE = 'bbbext_flexurl'; /** @@ -39,37 +41,9 @@ public function add_instance(stdClass $bigbluebuttonbn) { $this->sync_additional_params($bigbluebuttonbn); } - /** - * Runs any processes that must be run after a bigbluebuttonbn insert/update. - * - * @param stdClass $bigbluebuttonbn BigBlueButtonBN form data - **/ - public function update_instance(stdClass $bigbluebuttonbn): void { - $this->sync_additional_params($bigbluebuttonbn); - } - - /** - * Runs any processes that must be run after a bigbluebuttonbn delete. - * - * @param int $id - */ - public function delete_instance(int $id): void { - global $DB; - $DB->delete_records(self::SUBPLUGIN_TABLE, [ - 'bigbluebuttonbnid' => $id, - ]); - } - - /** - * Get any join table name that is used to store additional data for the instance. - * @return array - */ - public function get_join_tables(): array { - return [self::SUBPLUGIN_TABLE]; - } - /** * Make sure that the bbbext_flexurl has the right parameters (and not more) + * * @param stdClass $bigbluebuttonbn * @return void */ @@ -77,12 +51,16 @@ private function sync_additional_params(stdClass $bigbluebuttonbn): void { global $DB; // Checks first. $count = $bigbluebuttonbn->flexurl_paramcount ?? 0; - foreach(utils::PARAM_TYPES as $type =>$paramtype) { + foreach (utils::PARAM_TYPES as $type => $paramtype) { + if (!isset($bigbluebuttonbn->{'flexurl_' . $type})) { + return; + } if ($count != count($bigbluebuttonbn->{'flexurl_' . $type})) { debugging('FlexURL : The number of ' . $type . ' does not match the number of parameters.'); return; } - if (clean_param_array($bigbluebuttonbn->{'flexurl_' . $type}, $paramtype, true) != $bigbluebuttonbn->{'flexurl_' . $type}) { + if (clean_param_array($bigbluebuttonbn->{'flexurl_' . $type}, $paramtype, true) != + $bigbluebuttonbn->{'flexurl_' . $type}) { debugging('FlexURL : The ' . $type . ' contains invalid value.'); return; } @@ -91,13 +69,43 @@ private function sync_additional_params(stdClass $bigbluebuttonbn): void { // First delete everything related to this module. $DB->delete_records(self::SUBPLUGIN_TABLE, ['bigbluebuttonbnid' => $bigbluebuttonbn->id]); - for($index = 0; $index < $count; $index++) { + for ($index = 0; $index < $count; $index++) { $queryfields = []; - foreach(array_keys(utils::PARAM_TYPES) as $type) { + foreach (array_keys(utils::PARAM_TYPES) as $type) { $queryfields[$type] = $bigbluebuttonbn->{'flexurl_' . $type}[$index]; } $queryfields['bigbluebuttonbnid'] = $bigbluebuttonbn->id; $DB->insert_record(self::SUBPLUGIN_TABLE, (object) $queryfields); } } + + /** + * Runs any processes that must be run after a bigbluebuttonbn insert/update. + * + * @param stdClass $bigbluebuttonbn BigBlueButtonBN form data + **/ + public function update_instance(stdClass $bigbluebuttonbn): void { + $this->sync_additional_params($bigbluebuttonbn); + } + + /** + * Runs any processes that must be run after a bigbluebuttonbn delete. + * + * @param int $id + */ + public function delete_instance(int $id): void { + global $DB; + $DB->delete_records(self::SUBPLUGIN_TABLE, [ + 'bigbluebuttonbnid' => $id, + ]); + } + + /** + * Get any join table name that is used to store additional data for the instance. + * + * @return array + */ + public function get_join_tables(): array { + return []; // We don't make the join here as we have several values per instance. + } } diff --git a/classes/utils.php b/classes/utils.php index fbb32b0..fca7686 100644 --- a/classes/utils.php +++ b/classes/utils.php @@ -17,6 +17,7 @@ use core_course\external\course_module_summary_exporter; use core_course\external\course_summary_exporter; +use mod_bigbluebuttonbn\instance; /** * Utility class @@ -27,12 +28,20 @@ * @author Laurent David (laurent@call-learning.fr) */ class utils { + /** + * Types of actions + */ + const ACTION_CODES = [ + 'create' => 2, + 'join' => 1, + 'all' => 8, + ]; /** * Types of additional parameters */ public const PARAM_TYPES = [ - 'eventtype' => PARAM_ALPHA, - 'paramname' => PARAM_ALPHA, + 'eventtype' => PARAM_INT, + 'paramname' => PARAM_ALPHANUMEXT, 'paramvalue' => PARAM_RAW, ]; @@ -45,11 +54,13 @@ public static function get_options_for_parameters(): array { $parametertypes = self::get_parameter_types(); $options = []; $selectedptypes = explode(',', get_config('bbbext_flexurl', 'available_info')); + $selectedptypes = array_map('trim', $selectedptypes); foreach ($parametertypes as $key => $value) { if (in_array($key, $selectedptypes)) { $options[$value] = self::get_fields_for_parameter($key); } } + ksort($options); return $options; } @@ -63,23 +74,68 @@ public static function get_parameter_types(): array { return [ 'activityinfo' => get_string('activity_info', 'bbbext_flexurl'), 'courseinfo' => get_string('course_info', 'bbbext_flexurl'), - 'userinfo' => get_string('user_info', 'bbbext_flexurl'), + 'user' => get_string('user_info', 'bbbext_flexurl'), ]; } + /** + * Get fields for parameter + * + * @param string $key + * @return array + */ public static function get_fields_for_parameter(string $key): array { if (method_exists(self::class, 'get_' . $key . '_fields')) { - return call_user_func(self::class . '::get_' . $key . '_fields'); + $fields = call_user_func(self::class . '::get_' . $key . '_fields'); + ksort($fields); + return array_combine($fields, $fields); } else { return []; } } + /** + * Get value for field + * + * @param string $field + * @param instance $instance + * @return string + */ + public static function get_value_for_field(string $field, instance $instance): string { + // Split the string before the first dot (this will be the type) and the other will be the name of the field. + [$fieldtype, $fieldname] = explode('.', $field); + if (method_exists(self::class, 'get_' . $fieldtype . '_value')) { + return call_user_func(self::class . '::get_' . $fieldtype . '_value', $fieldname, $instance); + } else { + return ''; + } + } + /** * Get user fields prefixed by user. + * + * @param string $fieldname + * @param instance $instance + * @return string + */ + public static function get_user_value(string $fieldname, instance $instance): string { + global $USER; + $fields = self::get_user_fields(); + if (!in_array('user.' . $fieldname, $fields)) { + return ''; + } + $userwithfields = \core_user::get_user($USER->id); + return $userwithfields->$fieldname ?? ''; + } + + /** + * Get user fields prefixed by user. + * + * For now we only return simple fields located in core_user definition. + * * @return string[] */ - public static function get_userinfo_fields() { + public static function get_user_fields() { $userfields = \core_user\fields::get_identity_fields(\context_system::instance()); $userfields = array_merge($userfields, \core_user\fields::get_name_fields()); sort($userfields); @@ -91,6 +147,25 @@ function($field) { ); } + /** + * Course information + * + * @param string $fieldname + * @param instance $instance + * @return string + */ + public static function get_courseinfo_value(string $fieldname, instance $instance) { + global $PAGE; + $fields = self::get_courseinfo_fields(); + if (!in_array('courseinfo.' . $fieldname, $fields)) { + return ''; + } + $exporter = + new course_summary_exporter($instance->get_course(), ['context' => $instance->get_context()->get_course_context()]); + $coursedata = $exporter->export($PAGE->get_renderer('core')); + return $coursedata->$fieldname ?? ''; + } + /** * Course information * @@ -105,6 +180,24 @@ function($field) { ); } + /** + * Activity information + * + * @param string $fieldname + * @param instance $instance + * @return string + */ + public static function get_activityinfo_value(string $fieldname, instance $instance) { + global $PAGE; + $fields = self::get_activityinfo_fields(); + if (!in_array('activityinfo.' . $fieldname, $fields)) { + return ''; + } + $exporter = new course_module_summary_exporter(null, ['cm' => $instance->get_cm()]); + $moduledata = $exporter->export($PAGE->get_renderer('core')); + return $moduledata->$fieldname ?? ''; + } + /** * Activity information * @@ -126,8 +219,9 @@ function($field) { */ public static function get_option_for_eventtype() { return [ - 1 => get_string('event_join', 'bbbext_flexurl'), - 2 => get_string('event_create', 'bbbext_flexurl'), + self::ACTION_CODES['create'] => get_string('event_join', 'bbbext_flexurl'), + self::ACTION_CODES['join'] => get_string('event_create', 'bbbext_flexurl'), + self::ACTION_CODES['all'] => get_string('event_all', 'bbbext_flexurl'), ]; } } diff --git a/db/install.xml b/db/install.xml index e4f6c58..9b37145 100644 --- a/db/install.xml +++ b/db/install.xml @@ -8,7 +8,7 @@ - + diff --git a/lang/en/bbbext_flexurl.php b/lang/en/bbbext_flexurl.php index ff93c84..c776a9a 100644 --- a/lang/en/bbbext_flexurl.php +++ b/lang/en/bbbext_flexurl.php @@ -30,6 +30,7 @@ $string['course_info'] = 'Course info (COURSE)'; $string['event_create'] = 'Create'; $string['event_join'] = 'Join'; +$string['event_all'] = 'All'; $string['user_info'] = 'Basic user info (USER)'; $string['pluginname'] = 'BigBlueButton FlexURL'; $string['paramgroup'] = 'Parameter'; diff --git a/tests/bigbluebutton/action_url_addons_tests.php b/tests/bigbluebutton/action_url_addons_tests.php new file mode 100644 index 0000000..43f1898 --- /dev/null +++ b/tests/bigbluebutton/action_url_addons_tests.php @@ -0,0 +1,97 @@ +. +namespace bbbext_flexurl\bigbluebuttonbn; + +use bbbext_flexurl\utils; +use mod_bigbluebuttonbn\extension; +use mod_bigbluebuttonbn\external\get_join_url; +use mod_bigbluebuttonbn\instance; + +/** + * Action URL addons tests + * + * @package bbbext_flexurl + * @copyright 2023 onwards, Blindside Networks Inc + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @author Laurent David (laurent@call-learning.fr) + */ +class action_url_addons_tests extends \advanced_testcase { + /** + * @var \stdClass $bbb + */ + protected $bbb; + /** + * @var \stdClass $course + */ + protected $course; + /** + * @var \stdClass $user + */ + protected $user; + + /** + * Setup + */ + public function setUp(): void { + global $DB; + $this->resetAfterTest(); + $datagenerator = $this->getDataGenerator(); + $this->course = $datagenerator->create_course(['fullname' => 'BBBCourse FULL', 'shortname' => 'BBBCourse']); + $this->user = $datagenerator->create_user(['firstname' => 'BBB User FN', 'lastname' => 'BBB LN', + 'email' => 'bbb@blindsidenetworks.com', ]); + $bbbgenerator = $datagenerator->get_plugin_generator('mod_bigbluebuttonbn'); + $this->bbb = $bbbgenerator->create_instance(['name' => 'BBB Activity', 'course' => $this->course->id]); + set_config('available_info', 'user, courseinfo, activityinfo', 'bbbext_flexurl'); + $datagenerator->enrol_user($this->user->id, $this->course->id); + $DB->insert_record('bbbext_flexurl', + ['bigbluebuttonbnid' => $this->bbb->id, 'eventtype' => utils::ACTION_CODES['create'], 'paramname' => 'firstname', + 'paramvalue' => 'user.firstname', ]); + $DB->insert_record('bbbext_flexurl', + ['bigbluebuttonbnid' => $this->bbb->id, 'eventtype' => utils::ACTION_CODES['join'], 'paramname' => 'lastname', + 'paramvalue' => 'user.lastname', ]); + $DB->insert_record('bbbext_flexurl', + ['bigbluebuttonbnid' => $this->bbb->id, 'eventtype' => utils::ACTION_CODES['all'], 'paramname' => 'coursename', + 'paramvalue' => 'courseinfo.fullname', ]); + } + + /** + * Test join URL + * + * @return void + */ + public function test_join_url_has_options() { + $this->setUser($this->user); + $instance = instance::get_from_instanceid($this->bbb->id); + $joinurl = get_join_url::execute($instance->get_cm_id()); + $this->assertStringContainsString('lastname', $joinurl['join_url']); + $this->assertStringContainsString('coursename', $joinurl['join_url']); + $this->assertStringContainsString('BBB+LN', $joinurl['join_url']); + } + + /** + * Test create URL + * + * @return void + */ + public function test_create_url_has_options() { + $this->setUser($this->user); + instance::get_from_instanceid($this->bbb->id); + $addons = extension::action_url_addons('create', [], ['bbb-meta' => 'Test'], $this->bbb->id); + $this->assertContains('firstname', array_keys($addons['metadata'])); + $this->assertContains('coursename', array_keys($addons['metadata'])); + $this->assertNotContains('lastname', array_keys($addons['metadata'])); + } +} diff --git a/tests/utils_test.php b/tests/utils_test.php new file mode 100644 index 0000000..27d7ac1 --- /dev/null +++ b/tests/utils_test.php @@ -0,0 +1,193 @@ +. +namespace bbbext_flexurl; + +use mod_bigbluebuttonbn\instance; + +/** + * BBB Utils tests class. + * + * @package bbbext_flexurl + * @copyright 2023 onwards, Blindside Networks Inc + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @author Laurent David (laurent@call-learning.fr) + * @coversDefaultClass \bbbext_flexurl\utils + */ +class utils_test extends \advanced_testcase { + /** + * @var \stdClass $bbb + */ + protected $bbb; + /** + * @var \stdClass $course + */ + protected $course; + /** + * @var \stdClass $user + */ + protected $user; + + /** + * Provider for get_fields_for_parameter + * + * @return array[] + */ + public static function provider_get_fields_for_parameter(): array { + return [ + 'actitivityinfo' => [ + 'activityinfo', + ["activityinfo.id", "activityinfo.name", "activityinfo.url", "activityinfo.iconurl"], ], + 'courseinfo' => ['courseinfo', + ["courseinfo.id", "courseinfo.fullname", "courseinfo.shortname", "courseinfo.idnumber", "courseinfo.summary", + "courseinfo.summaryformat", "courseinfo.startdate", "courseinfo.enddate", "courseinfo.visible", + "courseinfo.showactivitydates", "courseinfo.showcompletionconditions", "courseinfo.pdfexportfont", + "courseinfo.fullnamedisplay", "courseinfo.viewurl", "courseinfo.courseimage", "courseinfo.progress", + "courseinfo.hasprogress", "courseinfo.isfavourite", "courseinfo.hidden", "courseinfo.timeaccess", + "courseinfo.showshortname", "courseinfo.coursecategory", ], ], + 'user' => ['user', ['user.alternatename', 'user.email', 'user.firstname', 'user.firstnamephonetic', 'user.lastname', + 'user.lastnamephonetic', 'user.middlename', ], ], + ]; + } + + /** + * Provider for get_fields_for_parameter + * + * @return array[] + */ + public static function provider_get_value_for_fields(): array { + return [ + 'actitivityinfo' => [ + ["activityinfo.name" => "BBB Activity", + "activityinfo.url" => "https://www.example.com/moodle/mod/bigbluebuttonbn/view.php", + "activityinfo.iconurl" => "https://www.example.com/moodle/theme/image.php/boost/bigbluebuttonbn", + ], ], + 'courseinfo' => [ + [ + "courseinfo.fullname" => "BBBCourse FULL", + "courseinfo.shortname" => "BBBCourse", + "courseinfo.idnumber" => "", + "courseinfo.summary" => "Test course 1", + ], ], + 'user' => [[ + 'user.email' => "", + 'user.firstname' => "BBB User FN", + 'user.lastname' => "BBB LN", + ], ], + ]; + } + + /** + * Setup + */ + public function setUp(): void { + $this->resetAfterTest(); + $datagenerator = $this->getDataGenerator(); + $this->course = $datagenerator->create_course(['fullname' => 'BBBCourse FULL', 'shortname' => 'BBBCourse']); + $this->user = $datagenerator->create_user(['firstname' => 'BBB User FN', 'lastname' => 'BBB LN', + 'email' => 'bbb@blindsidenetworks.com', ]); + $bbbgenerator = $datagenerator->get_plugin_generator('mod_bigbluebuttonbn'); + $this->bbb = $bbbgenerator->create_instance(['name' => 'BBB Activity', 'course' => $this->course->id]); + set_config('available_info', 'user, courseinfo, activityinfo', 'bbbext_flexurl'); + $datagenerator->enrol_user($this->user->id, $this->course->id); + } + + /** + * Test get_options_for_parameters + * + * @covers \bbbext_flexurl\utils::get_options_for_parameters + */ + public function test_get_options_for_parameters() { + $this->setAdminUser(); // To get the email. + $options = utils::get_options_for_parameters(); + $this->assertNotEmpty($options); + $this->assertEquals( + json_decode('{ +"Activity information (ACTIVITY)": { + "activityinfo.id": "activityinfo.id", + "activityinfo.name": "activityinfo.name", + "activityinfo.url": "activityinfo.url", + "activityinfo.iconurl": "activityinfo.iconurl" +}, +"Course info (COURSE)": { + "courseinfo.id": "courseinfo.id", + "courseinfo.fullname": "courseinfo.fullname", + "courseinfo.shortname": "courseinfo.shortname", + "courseinfo.idnumber": "courseinfo.idnumber", + "courseinfo.summary": "courseinfo.summary", + "courseinfo.summaryformat": "courseinfo.summaryformat", + "courseinfo.startdate": "courseinfo.startdate", + "courseinfo.enddate": "courseinfo.enddate", + "courseinfo.visible": "courseinfo.visible", + "courseinfo.showactivitydates": "courseinfo.showactivitydates", + "courseinfo.showcompletionconditions": "courseinfo.showcompletionconditions", + "courseinfo.pdfexportfont": "courseinfo.pdfexportfont", + "courseinfo.fullnamedisplay": "courseinfo.fullnamedisplay", + "courseinfo.viewurl": "courseinfo.viewurl", + "courseinfo.courseimage": "courseinfo.courseimage", + "courseinfo.progress": "courseinfo.progress", + "courseinfo.hasprogress": "courseinfo.hasprogress", + "courseinfo.isfavourite": "courseinfo.isfavourite", + "courseinfo.hidden": "courseinfo.hidden", + "courseinfo.timeaccess": "courseinfo.timeaccess", + "courseinfo.showshortname": "courseinfo.showshortname", + "courseinfo.coursecategory": "courseinfo.coursecategory" +}, +"Basic user info (USER)": { + "user.alternatename": "user.alternatename", + "user.email": "user.email", + "user.firstname": "user.firstname", + "user.firstnamephonetic": "user.firstnamephonetic", + "user.lastname": "user.lastname", + "user.lastnamephonetic": "user.lastnamephonetic", + "user.middlename": "user.middlename" +} +}', true) + , $options); + } + + /** + * Test get_fields_for_parameter + * + * @param string $paramtype + * @param array $expected + * + * @covers \bbbext_flexurl\utils::get_fields_for_parameter + * @dataProvider provider_get_fields_for_parameter + */ + public function test_get_fields_for_parameter(string $paramtype, array $expected) { + $this->setAdminUser(); // To get the email. + $fields = utils::get_fields_for_parameter($paramtype); + $this->assertNotEmpty($fields); + $this->assertEquals($expected, array_keys($fields)); + } + + /** + * Test get_value_for_field + * + * @param array $expected + * @covers \bbbext_flexurl\utils::get_value_for_field + * @dataProvider provider_get_value_for_fields + */ + public function test_get_value_for_field(array $expected) { + $instance = instance::get_from_instanceid($this->bbb->id); + + $this->setUser($this->user); + foreach ($expected as $key => $expectedvalue) { + $fieldvalue = utils::get_value_for_field($key, $instance); + $this->assertStringContainsString($expectedvalue, $fieldvalue, "Field $key does not contain $expectedvalue"); + } + } +}