Merge branch 'MDL-77660-master' of https://github.com/call-learning/moodle

This commit is contained in:
Jun Pataleta 2023-11-13 11:45:45 +08:00
commit 46d3660891
No known key found for this signature in database
GPG Key ID: F83510526D99E2C7
14 changed files with 410 additions and 27 deletions

View File

@ -16,7 +16,9 @@
namespace mod_bigbluebuttonbn\completion;
use cm_info;
use core_completion\activity_custom_completion;
use mod_bigbluebuttonbn\extension;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\logger;
use moodle_exception;
@ -44,6 +46,26 @@ class custom_completion extends activity_custom_completion {
'completionengagementemojis' => [logger::EVENT_SUMMARY],
];
/**
* @var array $completionaddons array of extension class for the completion
*/
private $completionaddons;
/**
* activity_custom_completion constructor.
*
* @param cm_info $cm
* @param int $userid
* @param array|null $completionstate The current state of the core completion criteria
*/
public function __construct(cm_info $cm, int $userid, ?array $completionstate = null) {
parent::__construct($cm, $userid, $completionstate);
$completionaddonsclasses = extension::custom_completion_addons_instances($cm, $userid, $completionstate);
$this->completionaddons = array_map(function($targetclassname) use ($cm, $userid, $completionstate) {
return new $targetclassname($cm, $userid, $completionstate);
}, $completionaddonsclasses);
}
/**
* Get current state
*
@ -79,6 +101,12 @@ class custom_completion extends activity_custom_completion {
}
}
}
// Check for any completion for this rule in addons / extensions.
foreach ($this->completionaddons as $customcompletion) {
if (in_array($rule, $customcompletion->get_defined_custom_rules())) {
$returnedvalue = $returnedvalue || $customcompletion->get_state($rule);
}
}
return $returnedvalue;
}
@ -109,7 +137,7 @@ class custom_completion extends activity_custom_completion {
* @return array
*/
public static function get_defined_custom_rules(): array {
return [
$rules = [
'completionattendance',
'completionengagementchats',
'completionengagementtalks',
@ -117,6 +145,11 @@ class custom_completion extends activity_custom_completion {
'completionengagementpollvotes',
'completionengagementemojis',
];
$completionaddonsclasses = extension::custom_completion_addons_classes();
foreach ($completionaddonsclasses as $customcompletion) {
$rules = array_merge($rules, $customcompletion::get_defined_custom_rules());
}
return $rules;
}
/**
@ -131,7 +164,7 @@ class custom_completion extends activity_custom_completion {
$completionengagementpollvotes = $this->cm->customdata['customcompletionrules']['completionengagementpollvotes'] ?? 1;
$completionengagementemojis = $this->cm->customdata['customcompletionrules']['completionengagementemojis'] ?? 1;
$completionattendance = $this->cm->customdata['customcompletionrules']['completionattendance'] ?? 1;
return [
$descriptions = [
'completionengagementchats' => get_string('completionengagementchats_desc', 'mod_bigbluebuttonbn',
$completionengagementchats),
'completionengagementtalks' => get_string('completionengagementtalks_desc', 'mod_bigbluebuttonbn',
@ -145,6 +178,11 @@ class custom_completion extends activity_custom_completion {
'completionattendance' => get_string('completionattendance_desc', 'mod_bigbluebuttonbn',
$completionattendance),
];
// Check for any completion for this rule in addons / extensions.
foreach ($this->completionaddons as $customcompletion) {
$descriptions = array_merge($descriptions, $customcompletion->get_custom_rule_descriptions());
}
return $descriptions;
}
/**
@ -153,15 +191,9 @@ class custom_completion extends activity_custom_completion {
* @return array
*/
public function get_sort_order(): array {
return [
'completionview',
'completionengagementchats',
'completionengagementtalks',
'completionengagementraisehand',
'completionengagementpollvotes',
'completionengagementemojis',
'completionattendance',
];
$rules = self::get_defined_custom_rules();
array_unshift($rules, 'completionview');
return $rules;
}
/**

View File

@ -16,7 +16,9 @@
namespace mod_bigbluebuttonbn;
use cache;
use cm_info;
use mod_bigbluebuttonbn\local\extension\action_url_addons;
use mod_bigbluebuttonbn\local\extension\custom_completion_addons;
use mod_bigbluebuttonbn\local\extension\mod_form_addons;
use mod_bigbluebuttonbn\local\extension\mod_instance_helper;
use stdClass;
@ -114,6 +116,27 @@ class extension {
return $extensionclasses;
}
/**
* Get all custom_completion addons classes.
*
* @return array of custom completion addon classes.
*/
public static function custom_completion_addons_classes(): array {
return self::get_classes_implementing(custom_completion_addons::class);
}
/**
* Get all custom_completion addons classes instances.
*
* @param cm_info $cm
* @param int $userid
* @param array|null $completionstate
* @return array of custom completion addon instances.
*/
public static function custom_completion_addons_instances(cm_info $cm, int $userid, ?array $completionstate = null): array {
return self::get_instances_implementing(custom_completion_addons::class, [$cm, $userid, $completionstate]);
}
/**
* Get all mod_form addons classes instances
*

View File

@ -0,0 +1,79 @@
<?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\extension;
use cm_info;
/**
* A class to deal with completion rules addons in a subplugin
*
* @package mod_bigbluebuttonbn
* @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)
*/
abstract class custom_completion_addons {
/** @var cm_info The course module information object. */
protected $cm;
/** @var int The user's ID. */
protected $userid;
/** @var array The current state of core completion */
protected $completionstate;
/**
* activity_custom_completion constructor.
*
* @param cm_info $cm
* @param int $userid
* @param array|null $completionstate The current state of the core completion criteria
*/
public function __construct(cm_info $cm, int $userid, ?array $completionstate = null) {
$this->cm = $cm;
$this->userid = $userid;
$this->completionstate = $completionstate;
}
/**
* Fetches the completion state for a given completion rule.
*
* @param string $rule The completion rule.
* @return int The completion state.
*/
abstract public function get_state(string $rule): int;
/**
* Fetch the list of custom completion rules that this module defines.
*
* @return array
*/
abstract public static function get_defined_custom_rules(): array;
/**
* Returns an associative array of the descriptions of custom completion rules.
*
* @return array
*/
abstract public function get_custom_rule_descriptions(): array;
/**
* Returns an array of all completion rules, in the order they should be displayed to users.
*
* @return array
*/
abstract public function get_sort_order(): array;
}

View File

@ -251,6 +251,8 @@ class admin_plugin_manager {
$class = \core_plugin_manager::resolve_plugininfo_class(extension::BBB_EXTENSION_PLUGIN_NAME);
$class::enable_plugin($plugin, false);
cache_helper::purge_by_event('mod_bigbluebuttonbn/pluginenabledisabled');
// Also clear the cache for all BigBlueButtonModules.
rebuild_course_cache(0, true);
return 'view';
}

View File

@ -49,7 +49,7 @@ class recording_proxy extends proxy_base {
* @param string $recordid a recording id
* @return bool
*/
public static function delete_recording(string $recordid, ?int $instanceid = null): bool {
public static function delete_recording(string $recordid): bool {
$result = self::fetch_endpoint_xml('deleteRecordings', ['recordID' => $recordid]);
if (!$result || $result->returncode != 'SUCCESS') {
return false;

View File

@ -37,9 +37,9 @@ Feature: I can edit a bigbluebutton instance
| bigbluebuttonbn_meetingevents_enabled | 1 |
And the following "users" exist:
| username | firstname | lastname |
| student1 | Student1 | 1 |
| student1 | Student1 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| user | course | role |
| student1 | Test course | student |
And I am on the "RoomRecordings" "bigbluebuttonbn activity editing" page logged in as "admin"
And I expand all fieldsets

View File

@ -3,14 +3,15 @@ Feature: BigBlueButtonBN Subplugins test
As a BigBlueButtonBN user
I can list the subplugins the admin settings pages
I can see the additional settings coming from the subplugins in the edit form
Background: Make sure that the BigBlueButtonBN plugin is enabled
Given I accept dpa and enable bigbluebuttonbn plugin
And the following "courses" exist:
| fullname | shortname | category |
| Test course | Test course | 0 |
| fullname | shortname | category | enablecompletion |
| Test course | Test course | 0 | 1 |
And the following "activities" exist:
| activity | course | name | type |
| bigbluebuttonbn | Test course | BBB Instance name | 0 |
| activity | course | name | type |
| bigbluebuttonbn | Test course | BBB Instance name | 0 |
Scenario: Add a subplugin and check that the settings are available
Given I log in as "admin"
@ -43,3 +44,44 @@ 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 completion with subplugin works
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 I am on the "BBB Instance name" "bigbluebuttonbn activity editing" page logged in as "admin"
And I expand all fieldsets
And I set the following fields to these values:
| Add requirements | 1 |
| Raise hand twice | 1 |
And I set the field "New field" to "50"
And I press "Save and display"
# We start the meeting here so to make sure that meta_analytics-callback-url is set.
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 | raisehand | 1 |
| BBB Instance name | raisehand | 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 run all adhoc tasks
When I reload the page
Then I should see "Done: Raise hand twice in a meeting."

View File

@ -19,7 +19,6 @@ namespace mod_bigbluebuttonbn\completion;
use completion_info;
use context_module;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\local\config;
use mod_bigbluebuttonbn\logger;
use mod_bigbluebuttonbn\meeting;
use mod_bigbluebuttonbn\test\testcase_helper_trait;

View File

@ -0,0 +1,82 @@
<?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 bbbext_simple\bigbluebuttonbn;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\logger;
/**
* A class to deal with completion rules addons in a subplugin
*
* @package mod_bigbluebuttonbn
* @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 custom_completion_addons extends \mod_bigbluebuttonbn\local\extension\custom_completion_addons {
/**
* Get defined custom rule
*
* @return string[]
*/
public static function get_defined_custom_rules(): array {
return ['completionextraisehandtwice'];
}
/**
* Get state
*
* @param string $rule
* @return int
*/
public function get_state(string $rule): int {
$instance = instance::get_from_cmid($this->cm->id);
$logs = logger::get_user_completion_logs($instance, $this->userid, [logger::EVENT_SUMMARY]);
$raisehandcount = 0;
foreach ($logs as $log) {
$summary = json_decode($log->meta);
$raisehandcount += intval($summary->data->engagement->raisehand ?? 0);
}
if ($raisehandcount >= 2) {
return COMPLETION_COMPLETE;
}
return COMPLETION_INCOMPLETE;
}
/**
* Get rule description
*
* @return array
* @throws \coding_exception
*/
public function get_custom_rule_descriptions(): array {
return [
'completionextraisehandtwice' => get_string('completionextraisehandtwice_desc', 'bbbext_simple'),
];
}
/**
* Get sort order
*
* @return string[]
*/
public function get_sort_order(): array {
return [
'completionattendance',
];
}
}

View File

@ -69,7 +69,14 @@ class mod_form_addons extends \mod_bigbluebuttonbn\local\extension\mod_form_addo
* @return array Array of string IDs of added items, empty array if none
*/
public function add_completion_rules(): array {
return [];
$this->mform->addElement('advcheckbox', 'completionextraisehandtwice',
get_string('completionextraisehandtwice', 'bbbext_simple'),
get_string('completionextraisehandtwice_desc', 'bbbext_simple'));
$this->mform->addHelpButton('completionextraisehandtwice', 'completionextraisehandtwice',
'bbbext_simple');
$this->mform->disabledIf('completionextraisehandtwice', 'completion', 'neq', COMPLETION_AGGREGATION_ANY);
return ['completionextraisehandtwice' . $this->suffix];
}
/**
@ -81,7 +88,7 @@ class mod_form_addons extends \mod_bigbluebuttonbn\local\extension\mod_form_addo
* default returns false
*/
public function completion_rule_enabled(array $data): bool {
return false;
return !empty($data['completionextraisehandtwice' . $this->suffix]);
}
/**
@ -111,7 +118,7 @@ class mod_form_addons extends \mod_bigbluebuttonbn\local\extension\mod_form_addo
*/
public function validation(array $data, array $files): array {
$errors = [];
if (empty($data['newfield'])) {
if (empty($data['newfield' . $this->suffix])) {
$errors['newfield'] = get_string('newfielderror', 'bbbext_simple');
}
return $errors;

View File

@ -35,7 +35,8 @@ class mod_instance_helper extends \mod_bigbluebuttonbn\local\extension\mod_insta
global $DB;
$DB->insert_record('bbbext_simple', (object) [
'bigbluebuttonbnid' => $bigbluebuttonbn->id,
'newfield' => $bigbluebuttonbn->newfield ?? ''
'newfield' => $bigbluebuttonbn->newfield ?? '',
'completionextraisehandtwice' => $bigbluebuttonbn->completionextraisehandtwice ?? '',
]);
}
@ -54,9 +55,11 @@ class mod_instance_helper extends \mod_bigbluebuttonbn\local\extension\mod_insta
$record = new stdClass();
$record->bigbluebuttonbnid = $bigbluebuttonbn->id;
$record->newfield = $bigbluebuttonbn->newfield ?? '';
$record->completionextraisehandtwice = $bigbluebuttonbn->completionextraisehandtwice ?? 0;
$DB->insert_record('bbbext_simple', $record);
} else {
$record->newfield = $bigbluebuttonbn->newfield ?? '';
$record->completionextraisehandtwice = $bigbluebuttonbn->completionextraisehandtwice ?? 0;
$DB->update_record('bbbext_simple', $record);
}
}

View File

@ -9,6 +9,7 @@
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="bigbluebuttonbnid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="newfield" TYPE="int" LENGTH="2" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="completionextraisehandtwice" TYPE="int" LENGTH="9" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>

View File

@ -27,3 +27,6 @@ $string['config_extension'] = 'Sample config extension setting';
$string['newfield'] = 'New field';
$string['newfielderror'] = 'New field cannot be empty';
$string['pluginname'] = 'Simple BigBlueButtonPlugin';
$string['completionextraisehandtwice'] = 'Raise hand twice';
$string['completionextraisehandtwice_desc'] = 'Raise hand twice in a meeting.';
$string['completionextraisehandtwice_help'] = 'Raise hand twice in a meeting.';

View File

@ -17,6 +17,7 @@ namespace mod_bigbluebuttonbn\local;
use backup;
use backup_controller;
use mod_bigbluebuttonbn\completion\custom_completion;
use mod_bigbluebuttonbn\extension;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\local\extension\mod_instance_helper;
@ -258,6 +259,115 @@ class extension_test extends \advanced_testcase {
$this->assertEquals(5, $oldfieldrecord->newfield);
}
/**
* Test completion with and without addons.
*
* @param array $customcompletionrules
* @param array $events
* @param int $expectedstate
* @return void
* @dataProvider custom_completion_data_provider
* @covers \mod_bigbluebuttonbn\local\extension\custom_completion_addons
*/
public function test_additional_completion(array $customcompletionrules, array $events, int $expectedstate): void {
// Enable plugin.
$this->enable_plugins(true);
$this->initialise_mock_server();
list($bbactivitycontext, $bbactivitycm, $bbactivity) = $this->create_instance(
$this->get_course(),
array_merge([
'completion' => '2',
], $customcompletionrules)
);
$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]),
]);
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_decode(json_encode($result->data));
meeting::meeting_events($instance, $data);
$completion = new custom_completion($bbactivitycm, $user->id);
$result = $completion->get_overall_completion_state();
$this->assertEquals($expectedstate, $result);
}
/**
* Data generator
*
* @return array[]
*/
public static function custom_completion_data_provider(): array {
global $CFG;
require_once($CFG->libdir . '/completionlib.php');
return [
'simple' => [
'customcompletionrules' => [
'completionengagementtalks' => 1,
'completionextraisehandtwice' => 1,
],
'events' => [
(object) ['name' => 'talks'],
(object) ['name' => 'raisehand'],
(object) ['name' => 'raisehand'],
],
'expectedstate' => COMPLETION_COMPLETE,
],
'not right events' => [
'customcompletionrules' => [
'completionextraisehandtwice' => 1,
],
'events' => [
(object) ['name' => 'chats'],
],
'expectedstate' => COMPLETION_INCOMPLETE,
],
'not enough events' => [
'customcompletionrules' => [
'completionextraisehandtwice' => 1,
],
'events' => [
(object) ['name' => 'raisehand'],
],
'expectedstate' => COMPLETION_INCOMPLETE,
],
'more events' => [
'customcompletionrules' => [
'completionengagementtalks' => 1,
],
'events' => [
(object) ['name' => 'talks'],
(object) ['name' => 'talks'],
(object) ['name' => 'talks'],
],
'expectedstate' => COMPLETION_COMPLETE,
],
'basics are still working' => [
'customcompletionrules' => [
'completionengagementtalks' => 1,
],
'events' => [
(object) ['name' => 'talks'],
],
'expectedstate' => COMPLETION_COMPLETE,
],
];
}
/**
* Data provider for testing get_class_implementing
*
@ -268,15 +378,15 @@ class extension_test extends \advanced_testcase {
'mod_instance_helper with plugin disabled' => [
'bbbenabled' => false,
'apiclass' => mod_instance_helper::class,
'result' => []
'result' => [],
],
'mod_instance_helper with plugin enabled' => [
'bbbenabled' => true,
'apiclass' => mod_instance_helper::class,
'result' => [
'bbbext_simple\\bigbluebuttonbn\\mod_instance_helper'
]
]
'bbbext_simple\\bigbluebuttonbn\\mod_instance_helper',
],
],
];
}