MDL-83889 mod_bigbluebuttonbn: Add overview page

This commit is contained in:
Laurent David
2025-08-18 08:05:16 +02:00
parent f6da0c1ff0
commit bf2ccb6592
5 changed files with 754 additions and 2 deletions
@@ -0,0 +1,183 @@
<?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\courseformat;
use core_calendar\output\humandate;
use cm_info;
use core_courseformat\local\overview\overviewitem;
use core\output\action_link;
use core\output\local\properties\text_align;
use core\output\local\properties\button;
use core\url;
use mod_bigbluebuttonbn\dates;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
use mod_bigbluebuttonbn\recording;
/**
* bigbluebuttonbn overview integration.
*
* @package mod_bigbluebuttonbn
* @copyright 2025 Laurent David <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class overview extends \core_courseformat\activityoverviewbase {
/** @var instance $bigbluebuttonbn the bigbluebuttonbn instance. */
private instance $bigbluebuttonbn;
/**
* Constructor.
*
* @param cm_info $cm the course module instance.
*/
public function __construct(
cm_info $cm,
) {
parent::__construct($cm);
$this->bigbluebuttonbn = instance::get_from_cmid($cm->id);
}
#[\Override]
public function get_actions_overview(): ?overviewitem {
if (!$this->bigbluebuttonbn->is_moderator()) {
return null;
}
$content = new action_link(
url: new url('/mod/bigbluebuttonbn/view.php', ['id' => $this->cm->id]),
text: get_string('view'),
attributes: ['class' => button::BODY_OUTLINE->classes()],
);
return new overviewitem(
name: get_string('actions'),
value: get_string('view'),
content: $content,
textalign: text_align::CENTER,
);
}
#[\Override]
public function get_extra_overview_items(): array {
return [
'opens' => $this->get_extra_date_open(),
'closes' => $this->get_extra_date_close(),
'roomtype' => $this->get_extra_room_type_overview(),
'recordings' => $this->get_extra_recordings_overview(),
];
}
/**
* Retrieves the open date overview item.
*
* @return overviewitem|null An overview item with the open date, or null if the user lacks the required capability.
*/
public function get_extra_date_open(): ?overviewitem {
global $USER;
$dates = new dates($this->cm, $USER->id);
$closedate = $dates->get_open_date();
if (empty($closedate)) {
return new overviewitem(
name: get_string('opens', 'bigbluebuttonbn'),
value: null,
content: '-',
);
}
$content = humandate::create_from_timestamp($closedate);
return new overviewitem(
name: get_string('opens', 'bigbluebuttonbn'),
value: $closedate,
content: $content,
);
}
/**
* Retrieves the close date overview item.
*
* @return overviewitem|null An overview item with the open date, or null if the user lacks the required capability.
*/
public function get_extra_date_close(): ?overviewitem {
global $USER;
$dates = new dates($this->cm, $USER->id);
$closedate = $dates->get_close_date();
if (empty($closedate)) {
return new overviewitem(
name: get_string('closes', 'bigbluebuttonbn'),
value: null,
content: '-',
);
}
$content = humandate::create_from_timestamp($closedate);
return new overviewitem(
name: get_string('closes', 'bigbluebuttonbn'),
value: $closedate,
content: $content,
);
}
/**
* Retrieves the recording count overview item.
*
* @return overviewitem|null An overview item c, or null if the user lacks the required capability.
*/
private function get_extra_room_type_overview(): ?overviewitem {
if (!$this->bigbluebuttonbn->is_moderator()) {
return null;
}
$typeprofiles = bigbluebutton_proxy::get_instance_type_profiles();
$profilename = $typeprofiles[$this->bigbluebuttonbn->get_type()]['name'];
return new overviewitem(
name: get_string('mod_form_field_instanceprofiles', 'mod_bigbluebuttonbn'),
value: $profilename,
content: $profilename,
textalign: text_align::START,
);
}
/**
* Retrieves the recording count overview item.
*
* @return overviewitem|null An overview item c, or null if the user lacks the required capability.
*/
private function get_extra_recordings_overview(): ?overviewitem {
if (!$this->bigbluebuttonbn->is_moderator()) {
return null;
}
$content = '-';
$recordingcount = 0;
if ($this->bigbluebuttonbn->get_type() !== strval(instance::TYPE_ROOM_ONLY)) {
$recordings = recording::get_recordings_for_instance(
$this->bigbluebuttonbn,
includeimported: true,
);
$recordingcount = count($recordings);
$content = strval($recordingcount);
}
return new overviewitem(
name: get_string('recordings', 'mod_bigbluebuttonbn'),
value: $recordingcount,
content: $content,
textalign: text_align::END,
);
}
}
@@ -0,0 +1,40 @@
<?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\event;
/**
* The mod_bigbluebuttonbn instance list viewed event class.
*
* @package mod_bigbluebuttonbn
* @copyright 2025 Laurent David <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {
/**
* Create the event from course record.
*
* @param \stdClass $course
* @return course_module_instance_list_viewed
*/
public static function create_from_course(\stdClass $course) {
$params = [
'context' => \context_course::instance($course->id),
];
$event = self::create($params);
$event->add_record_snapshot('course', $course);
return $event;
}
}
@@ -299,6 +299,7 @@ $string['config_hideuserlist_editable'] = 'Hide user list can be edited';
$string['config_hideuserlist_editable_description'] = 'Hide user list by default can be edited when the instance is added or updated.';
$string['config_experimental_features'] = 'Experimental features';
$string['closes'] = 'Closes';
$string['config_experimental_features_description'] = 'Configuration for experimental features.';
$string['general_error_unable_connect'] = 'Unable to connect. Please check the url of the BigBlueButton server AND check to see if the BigBlueButton server is running.
@@ -420,6 +421,7 @@ $string['mod_form_field_disablepublicchat'] = 'Disable public chat';
$string['mod_form_field_disablenote'] = 'Disable shared notes';
$string['mod_form_field_hideuserlist'] = 'Hide user list';
$string['mod_form_locksettings'] = 'Lock settings';
$string['opens'] = 'Opens';
$string['report_join_info'] = '{$a} meeting(s)';
$string['report_play_recording_info'] = '{$a} recording(s) played';
$string['report_room_view'] = 'viewed';
@@ -645,10 +647,9 @@ $string['taskname:check_pending_recordings'] = 'Fetch pending recordings';
$string['taskname:check_dismissed_recordings'] = 'Check for recordings that haven\'t been found yet';
$string['userlimitreached'] = 'The number of users allowed in a session has been reached.';
$string['waitformoderator'] = 'Waiting for a moderator to join.';
$string['recordingnotfound'] = 'The recording was not found.';
$string['recordings'] = 'Recordings';
$string['recordingurlnotfound'] = 'The recording URL is invalid.';
$string['subplugintype_bbbext'] = 'BigBlueButton activity extension';
$string['subplugintype_bbbext_plural'] = 'BigBlueButton activity extensions';
@@ -0,0 +1,84 @@
@mod @mod_bigbluebuttonbn @javascript
Feature: Testing overview integration in mod_bigbluebuttonbn
In order to summarize the bigbluebuttonbn activities
As a user
I need to be able to see the bigbluebuttonbn overview
Background:
Given a BigBlueButton mock server is configured
And I enable "bigbluebuttonbn" "mod" plugin
And the following "users" exist:
| username | firstname | lastname |
| student1 | Username | 1 |
| student2 | Username | 2 |
| student3 | Username | 3 |
| student4 | Username | 4 |
| student5 | Username | 5 |
| student6 | Username | 6 |
| student7 | Username | 7 |
| student8 | Username | 8 |
| teacher1 | Teacher | T |
| editingteacher1 | EditingTeacher | T |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role | firstname | lastname |
| student1 | C1 | student | Username | 1 |
| student2 | C1 | student | Username | 2 |
| student3 | C1 | student | Username | 3 |
| teacher1 | C1 | teacher | Username | T |
| editingteacher1 | C1 | editingteacher | Username | ET |
And the following "activities" exist:
| activity | name | intro | course | idnumber | type | recordings_imported | openingtime | closingtime | grade | moderators |
| bigbluebuttonbn | RoomRecordings | Test Room Recording description | C1 | bigbluebuttonbn1 | 0 | 0 | 1 January 2024 | | 100 | role:editingteacher |
| bigbluebuttonbn | RoomOnly | Test Room Recording with visible groups | C1 | bigbluebuttonbn2 | 1 | 0 | | 1 January 2040 | 100 | role:editingteacher |
| bigbluebuttonbn | RecordingOnly | Test Room Recording with visible groups | C1 | bigbluebuttonbn3 | 2 | 0 | | | 0 | role:editingteacher |
| bigbluebuttonbn | RoomRecordingsNoUser | Test Room Recording with visible groups | C1 | bigbluebuttonbn4 | 0 | 0 | 1 January 2024 | 1 January 2040 | 0 | role:editingteacher |
| bigbluebuttonbn | RoomRecordingsNoModerator | Test Room Recording with visible groups | C1 | bigbluebuttonbn5 | 0 | 0 | 1 January 2024 | 1 January 2040 | 0 | |
And the following "mod_bigbluebuttonbn > meeting" exists:
| activity | RoomRecordings |
And the following "mod_bigbluebuttonbn > recordings" exist:
| bigbluebuttonbn | name | description | status |
| RoomRecordings | Recording 1 | Description 1 | 2 |
| RoomRecordings | Recording 2 | Description 2 | 2 |
| RoomRecordings | Recording 3 | Description 3 | 2 |
| RoomRecordings | Recording 4 | Description 4 | 0 |
And I am on the "Course 1" "grades > Grader report > View" page logged in as "editingteacher1"
And I turn editing mode on
And I give the grade "90.00" to the user "Username 1" for the grade item "RoomRecordings"
And I give the grade "100.00" to the user "Username 2" for the grade item "RoomOnly"
And I click on "Save changes" "button"
And I log out
Scenario: The bigbluebuttonbn overview report should generate log events
Given I am on the "Course 1" "course > activities > bigbluebuttonbn" page logged in as "teacher1"
When I am on the "Course 1" "course" page logged in as "teacher1"
And I navigate to "Reports" in current page administration
And I click on "Logs" "link"
And I click on "Get these logs" "button"
Then I should see "Course activities overview page viewed"
And I should see "viewed the instance list for the module 'bigbluebuttonbn'"
Scenario: Teachers can see relevant columns in the bigbluebuttonbn overview
Given I am on the "Course 1" "course > activities > bigbluebuttonbn" page logged in as "editingteacher1"
When I should not see "Grade" in the "bigbluebuttonbn_overview_collapsible" "region"
Then the following should exist in the "Table listing all BigBlueButton activities" table:
| Name | Opens | Closes | Instance type | Recordings | Actions |
| RoomRecordings | Monday, 1 January 2024, 12:00 AM | - | Room with recordings | 3 | View |
| RoomOnly | - | Sunday, 1 January 2040, 12:00 AM | Room only | - | View |
| RecordingOnly | - | - | Recordings only | 0 | View |
| RoomRecordingsNoUser | Monday, 1 January 2024, 12:00 AM | Sunday, 1 January 2040, 12:00 AM | Room with recordings | 0 | View |
| RoomRecordingsNoModerator | Monday, 1 January 2024, 12:00 AM | Sunday, 1 January 2040, 12:00 AM | | | |
Scenario: Students can see relevant columns in the bigbluebuttonbn overview
Given I am on the "Course 1" "course > activities > bigbluebuttonbn" page logged in as "student1"
Then the following should exist in the "Table listing all BigBlueButton activities" table:
| Name | Opens | Closes | Grade |
| RoomRecordings | Monday, 1 January 2024, 12:00 AM | - | 90.00 |
| RoomOnly | - | Sunday, 1 January 2040, 12:00 AM | - |
| RecordingOnly | - | - | |
| RoomRecordingsNoUser | Monday, 1 January 2024, 12:00 AM | Sunday, 1 January 2040, 12:00 AM | |
| RoomRecordingsNoModerator | Monday, 1 January 2024, 12:00 AM | Sunday, 1 January 2040, 12:00 AM | |
And I should not see "Instance type" in the "bigbluebuttonbn_overview_collapsible" "region"
And I should not see "Actions" in the "bigbluebuttonbn_overview_collapsible" "region"
@@ -0,0 +1,444 @@
<?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\courseformat;
use core_courseformat\local\overview\overviewfactory;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\meeting;
use mod_bigbluebuttonbn\recording;
use mod_bigbluebuttonbn\test\testcase_helper_trait;
/**
* Tests for bigbluebuttonbn activity overview
*
* @covers \mod_bigbluebuttonbn\courseformat\overview
* @package mod_bigbluebuttonbn
* @category test
* @copyright 2025 Laurent David <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class overview_test extends \advanced_testcase {
use testcase_helper_trait;
/**
* Test get_actions_overview.
*
* @covers ::get_actions_overview
*/
public function test_get_actions_overview(): void {
$this->resetAfterTest();
['users' => $users, 'course' => $course, 'instances' => $instances] =
$this->setup_users_and_activity(createrecordings: false);
['withoutrecordings' => $instancewa] = $instances;
$cm = get_fast_modinfo($course)->get_cm($instancewa->cmid);
// Students or non moderators have no action column.
foreach (['s1', 's2', 't1', 't2'] as $username) {
$this->setUser($users[$username]);
$this->assertNull(overviewfactory::create($cm)->get_actions_overview());
}
// T3 is a moderator and should have an action column..
$this->setUser($users['t3']);
$items = overviewfactory::create($cm)->get_actions_overview();
$this->assertNotNull($items);
$this->assertEquals(get_string('actions'), $items->get_name());
// Admin should have an action column too.
$this->setAdminUser();
$this->assertNotNull(overviewfactory::create($cm)->get_actions_overview());
$this->assertNotNull($items);
$this->assertEquals(get_string('actions'), $items->get_name());
}
/**
* Test get_due_date_overview method.
*
* Note here: we do not use the due date overview for bigbluebuttonbn activities as we have a opening date
* and a closing date instead. If we were to use the due date overview as a closing date, this column
* would not be displayed in the right order - next to closing date column (probably separated by the completion status column).
* So we decided to not use the due date overview at all and instead add two extra date columns:
* - opens: the opening date of the activity.
* - closes: the closing date of the activity.
* This test just checks that the due date overview is not used in the bigbluebuttonbn activity overview.
*
* @covers ::get_due_date_overview
*/
public function test_get_due_date_overview(): void {
$this->resetAfterTest();
$this->setAdminUser();
$bigbluebuttonbntemplate = [];
$clock = $this->mock_clock_with_frozen();
$timeincrement = DAYSECS;
$bigbluebuttonbntemplate['timeclose'] = $clock->time() + $timeincrement;
['users' => $users, 'course' => $course, 'instances' => $instances] =
$this->setup_users_and_activity(instancedata: $bigbluebuttonbntemplate, createrecordings: false);
['withoutrecordings' => $instancewa] = $instances;
$cm = get_fast_modinfo($course)->get_cm($instancewa->cmid);
$this->setUser($users['s1']);
$overview = overviewfactory::create($cm);
$this->assertNull($overview->get_due_date_overview());
}
/**
* Test get_extra_date_open method.
*
* @param int|null $timeincrement
*
* @covers ::get_extra_date_open
* @dataProvider get_extra_date_data
*/
public function test_get_extra_date_open(?int $timeincrement): void {
$this->resetAfterTest();
$this->setAdminUser();
$bigbluebuttonbntemplate = [];
$clock = $this->mock_clock_with_frozen();
if (!is_null($timeincrement)) {
$bigbluebuttonbntemplate['openingtime'] = $clock->time() + $timeincrement;
}
['users' => $users, 'course' => $course, 'instances' => $instances] =
$this->setup_users_and_activity(instancedata: $bigbluebuttonbntemplate, createrecordings: false);
['withoutrecordings' => $instancewa] = $instances;
$cm = get_fast_modinfo($course)->get_cm($instancewa->cmid);
$this->setUser($users['s1']);
$overview = overviewfactory::create($cm);
$this->assertEquals(
is_null($timeincrement) ? null : $clock->time() + $timeincrement,
$overview->get_extra_date_open()->get_value(),
);
}
/**
* Test get_extra_date_close method.
*
* @param int|null $timeincrement
*
* @covers ::get_extra_date_close
* @dataProvider get_extra_date_data
*/
public function test_get_extra_date_close(?int $timeincrement): void {
$this->resetAfterTest();
$this->setAdminUser();
$bigbluebuttonbntemplate = [];
$clock = $this->mock_clock_with_frozen();
if (!is_null($timeincrement)) {
$bigbluebuttonbntemplate['closingtime'] = $clock->time() + $timeincrement;
}
['users' => $users, 'course' => $course, 'instances' => $instances] =
$this->setup_users_and_activity(instancedata: $bigbluebuttonbntemplate, createrecordings: false);
['withoutrecordings' => $instancewa] = $instances;
$cm = get_fast_modinfo($course)->get_cm($instancewa->cmid);
$this->setUser($users['s1']);
$overview = overviewfactory::create($cm);
$this->assertEquals(
is_null($timeincrement) ? null : $clock->time() + $timeincrement,
$overview->get_extra_date_close()->get_value(),
);
}
/**
* Data provider for test_get_due_date_overview.
*
* @return array
*/
public static function get_extra_date_data(): array {
return [
'tomorrow' => [
'timeincrement' => DAYSECS,
],
'yesterday' => [
'timeincrement' => -1 * DAYSECS,
],
'today' => [
'timeincrement' => 0,
],
'No date' => [
'timeincrement' => null,
],
];
}
/**
* Test get_extra_date_close method.
*
* @param int $roomtype
* @param string $expectedtype
*
* @covers ::get_extra_room_type_overview
* @dataProvider get_extra_room_type_overview_data
*/
public function test_get_extra_room_type_overview(int $roomtype, string $expectedtype): void {
$this->resetAfterTest();
$this->setAdminUser();
$bigbluebuttonbntemplate = [];
$bigbluebuttonbntemplate['type'] = $roomtype;
['users' => $users, 'course' => $course, 'instances' => $instances] =
$this->setup_users_and_activity(instancedata: $bigbluebuttonbntemplate, createrecordings: false);
['withoutrecordings' => $instancewa] = $instances;
$cm = get_fast_modinfo($course)->get_cm($instancewa->cmid);
$this->setUser($users['t3']);
$overview = overviewfactory::create($cm);
$overviewitem = $overview->get_extra_overview_items();
$this->assertArrayHasKey('roomtype', $overviewitem);
$this->assertEquals(
$expectedtype,
$overviewitem['roomtype']->get_value(),
);
}
/**
* Data provider for test_get_due_date_overview.
*
* @return array
*/
public static function get_extra_room_type_overview_data(): array {
return [
'All' => [
'roomtype' => instance::TYPE_ALL,
'expectedtype' => get_string('instance_type_default', 'bigbluebuttonbn'),
],
'Room Only' => [
'roomtype' => instance::TYPE_ROOM_ONLY,
'expectedtype' => get_string('instance_type_room_only', 'bigbluebuttonbn'),
],
'Recording Only' => [
'roomtype' => instance::TYPE_RECORDING_ONLY,
'expectedtype' => get_string('instance_type_recording_only', 'bigbluebuttonbn'),
],
];
}
/**
* Test test_get_extra_recordings_overview.
*
* @param string $activityname
* @param int $recordingcount
* @throws \coding_exception
* @throws \moodle_exception
* @covers ::get_extra_recordings_overview
* @dataProvider get_extra_recordings_overview_data
*/
public function test_get_extra_recordings_overview(string $activityname, int $recordingcount): void {
$this->resetAfterTest();
$this->setAdminUser();
$bigbluebuttonbntemplate = [];
['users' => $users, 'course' => $course, 'instances' => $instances] =
$this->setup_users_and_activity(instancedata: $bigbluebuttonbntemplate);
[$activityname => $instance] = $instances;
$cm = get_fast_modinfo($course)->get_cm($instance->cmid);
$this->setUser($users['t3']);
$overview = overviewfactory::create($cm);
$overviewitem = $overview->get_extra_overview_items();
$this->assertArrayHasKey('recordings', $overviewitem);
$this->assertEquals(
$recordingcount,
$overviewitem['recordings']->get_value(),
);
}
/**
* Data provider for test_get_extra_studentsattempted_overview and test_get_extra_totalattempts_overview
*
* @return array
*/
public static function get_extra_recordings_overview_data(): array {
return [
'with recordings' => [
'activityname' => 'withrecordings',
'recordingcount' => 2,
],
'without recordings' => [
'activityname' => 'withoutrecordings',
'recordingcount' => 0,
],
];
}
/**
* Test get_extra_overview_items when there are no users in the course
*
* @return void
*/
public function test_get_extra_overview_no_users(): void {
$this->resetAfterTest();
['course' => $course, 'instances' => $instances] =
$this->setup_users_and_activity(createusers: false, createrecordings: false);
['withoutrecordings' => $instancewa] = $instances; // This has no user so no attempt too.
$cm = get_fast_modinfo($course)->get_cm($instancewa->cmid);
$this->setAdminUser();
$items = overviewfactory::create($cm)->get_extra_overview_items();
$this->assertArrayHasKey('recordings', $items);
$this->assertEquals(0, $items['recordings']->get_value());
$this->assertArrayHasKey('roomtype', $items);
}
/**
* Test check columns and content depeding on role (moderator, admin, other (viewer)).
*
* @return void
*/
public function test_all_get_extra_overview_items(): void {
$this->resetAfterTest();
['course' => $course, 'instances' => $instances, 'users' => $users] =
$this->setup_users_and_activity(createusers: true);
['withoutrecordings' => $instancewa] = $instances; // This has no user so no attempt too.
$cm = get_fast_modinfo($course)->get_cm($instancewa->cmid);
$this->setAdminUser();
$items = overviewfactory::create($cm)->get_extra_overview_items();
$this->assertNotEmpty($items['opens']);
$this->assertNotEmpty($items['closes']);
$this->assertNotEmpty($items['roomtype']);
$this->assertNotEmpty($items['recordings']);
// Normal viewers.
$viewers = ['s1', 's2', 't1', 't2'];
foreach ($viewers as $username) {
$this->setUser($users[$username]);
$items = overviewfactory::create($cm)->get_extra_overview_items();
$this->assertNotEmpty($items['opens']);
$this->assertNotEmpty($items['closes']);
$this->assertEmpty($items['roomtype']);
$this->assertEmpty($items['recordings']);
}
$moderators = ['t3']; // T3 is a moderator in the room.
foreach ($moderators as $username) {
$this->setUser($users[$username]);
$items = overviewfactory::create($cm)->get_extra_overview_items();
$this->assertNotEmpty($items['opens']);
$this->assertNotEmpty($items['closes']);
$this->assertNotEmpty($items['roomtype']);
$this->assertNotEmpty($items['recordings']);
}
}
/**
* Setup users and activity for testing answers retrieval.
*
* @param int $groupmode the group mode to use for the course.
* @param bool $createrecordings whether to create an attempt for the student.
* @param array|null $instancedata additional data for the instance.
* @param array|null $grades the grade to set for the student.
* @param bool $createusers whether to enrol users in the course.
* @return array indexed array with 'users', 'course' and 'instance'.
*/
private function setup_users_and_activity(
int $groupmode = NOGROUPS,
bool $createrecordings = true,
?array $instancedata = null,
?array $grades = null,
bool $createusers = true,
): array {
$users = [];
$generator = $this->getDataGenerator();
$courseparams = [];
if ($groupmode !== NOGROUPS) {
// Set the group mode for the course.
$courseparams['groupmode'] = $groupmode;
$courseparams['groupmodeforce'] = 1; // Force the group mode.
}
$course = $generator->create_course($courseparams);
$groups = [];
if ($createusers) {
$data = [
's1' => ['role' => 'student', 'groups' => ['g1']],
's2' => ['role' => 'student', 'groups' => ['g2']],
't1' => ['role' => 'editingteacher', 'groups' => ['g1']],
't2' => ['role' => 'editingteacher', 'groups' => []],
't3' => ['role' => 'teacher', 'groups' => ['g1']], // T3 will be a moderator in the room.
];
// Enrol users in the course.
foreach ($data as $username => $userinfo) {
['role' => $role, 'groups' => $groups] = $userinfo;
$users[$username] = $generator->create_and_enrol($course, $role, ['username' => $username]);
foreach ($groups as $group) {
if (!isset($groups[$group])) {
// Create the group if it does not exist.
$groups[$group] = $generator->create_group(['courseid' => $course->id, 'name' => $group]);
}
// Add the user to the group.
groups_add_member($groups[$group], $users[$username]->id);
}
}
}
$this->setAdminUser();
$instancedata = $instancedata ?? [];
$instancedata = array_merge($instancedata, [
'course' => $course->id,
'gradetype' => GRADE_TYPE_VALUE, // Use highest grade for grading.
]);
if ($createusers) {
// Add the users to the instance data.
$instancedata['moderators'] = 'user:t3';// Set T3 as moderator in the room.
}
$instances = [];
$instances['withoutrecordings'] =
$generator->create_module('bigbluebuttonbn', $instancedata); // Create a second instance with no recordings.
$bbbgenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn');
$recordings = [];
if ($createrecordings) {
// We do that only when we want to create recordings.
$this->initialise_mock_server();
$instances['withrecordings'] = $generator->create_module('bigbluebuttonbn', $instancedata);
$instance = instance::get_from_instanceid($instances['withrecordings']->id);
// We need to create a meeting for the instance in order to create recordings.
$bbbgenerator->create_meeting([
'instanceid' => $instance->get_instance_id(),
'groupid' => $instance->get_group_id(),
]);
$now = time();
$recordingstatus = [
recording::RECORDING_STATUS_AWAITING,
recording::RECORDING_STATUS_PROCESSED,
recording::RECORDING_STATUS_PROCESSED,
recording::RECORDING_STATUS_DISMISSED,
];
foreach ($recordingstatus as $status) {
$recordings[] = $bbbgenerator->create_recording(
array_merge([
'bigbluebuttonbnid' => $instance->get_instance_id(),
'groupid' => $instance->get_group_id(),
'starttime' => $now,
'endtime' => $now + HOURSECS,
'status' => $status,
])
);
}
}
if ($grades) {
if ($grades) {
foreach ($instances as $instance) {
foreach ($grades as $grade) {
$instancedata = (object) [
'iteminstance' => $instance->id,
'itemmodule' => 'bigbluebuttonbn',
'itemtype' => 'mod',
'courseid' => $course->id,
];
$instancedata->rawgrade = $grade;
bigbluebuttonbn_grade_item_update($instancedata);
}
}
}
}
return [
'users' => $users,
'course' => $course,
'instances' => $instances,
'recordings' => $recordings,
];
}
}