From bf2ccb659236e6840155d859f53595b5a031dc8e Mon Sep 17 00:00:00 2001 From: Laurent David Date: Fri, 1 Aug 2025 10:41:49 +0200 Subject: [PATCH] MDL-83889 mod_bigbluebuttonbn: Add overview page --- .../classes/courseformat/overview.php | 183 ++++++++ .../course_module_instance_list_viewed.php | 40 ++ .../lang/en/bigbluebuttonbn.php | 5 +- .../tests/behat/overview_report.feature | 84 ++++ .../tests/courseformat/overview_test.php | 444 ++++++++++++++++++ 5 files changed, 754 insertions(+), 2 deletions(-) create mode 100644 public/mod/bigbluebuttonbn/classes/courseformat/overview.php create mode 100644 public/mod/bigbluebuttonbn/classes/event/course_module_instance_list_viewed.php create mode 100644 public/mod/bigbluebuttonbn/tests/behat/overview_report.feature create mode 100644 public/mod/bigbluebuttonbn/tests/courseformat/overview_test.php diff --git a/public/mod/bigbluebuttonbn/classes/courseformat/overview.php b/public/mod/bigbluebuttonbn/classes/courseformat/overview.php new file mode 100644 index 00000000000..abcc42ec62c --- /dev/null +++ b/public/mod/bigbluebuttonbn/classes/courseformat/overview.php @@ -0,0 +1,183 @@ +. + +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 + * @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, + ); + } +} diff --git a/public/mod/bigbluebuttonbn/classes/event/course_module_instance_list_viewed.php b/public/mod/bigbluebuttonbn/classes/event/course_module_instance_list_viewed.php new file mode 100644 index 00000000000..d1ca1a9e39c --- /dev/null +++ b/public/mod/bigbluebuttonbn/classes/event/course_module_instance_list_viewed.php @@ -0,0 +1,40 @@ +. + +namespace mod_bigbluebuttonbn\event; +/** + * The mod_bigbluebuttonbn instance list viewed event class. + * + * @package mod_bigbluebuttonbn + * @copyright 2025 Laurent David + * @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; + } +} diff --git a/public/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php b/public/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php index 9628ee25d93..0a246f88c0c 100644 --- a/public/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php +++ b/public/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php @@ -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'; diff --git a/public/mod/bigbluebuttonbn/tests/behat/overview_report.feature b/public/mod/bigbluebuttonbn/tests/behat/overview_report.feature new file mode 100644 index 00000000000..b13c583663a --- /dev/null +++ b/public/mod/bigbluebuttonbn/tests/behat/overview_report.feature @@ -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" diff --git a/public/mod/bigbluebuttonbn/tests/courseformat/overview_test.php b/public/mod/bigbluebuttonbn/tests/courseformat/overview_test.php new file mode 100644 index 00000000000..0297005c5a5 --- /dev/null +++ b/public/mod/bigbluebuttonbn/tests/courseformat/overview_test.php @@ -0,0 +1,444 @@ +. + +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 + * @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, + ]; + } +}