This commit is contained in:
ferran
2025-08-21 17:22:31 +02:00
16 changed files with 1219 additions and 109 deletions
@@ -0,0 +1,5 @@
issueNumber: MDL-83889
notes:
mod_bigbluebuttonbn:
- message: Add activity_dates class to BigblueButton module.
type: improved
@@ -92,7 +92,7 @@ final class overviewfactory_test extends \advanced_testcase {
],
'bigbluebuttonbn' => [
'resourcetype' => 'bigbluebuttonbn',
'expected' => resourceoverview::class,
'expected' => \mod_bigbluebuttonbn\courseformat\overview::class,
],
'choice' => [
'resourcetype' => 'choice',
@@ -198,7 +198,7 @@ final class overviewfactory_test extends \advanced_testcase {
public static function activity_has_overview_integration_provider(): array {
return [
'assign' => ['modname' => 'assign', 'hasintegration' => true],
'bigbluebuttonbn' => ['modname' => 'bigbluebuttonbn', 'hasintegration' => false],
'bigbluebuttonbn' => ['modname' => 'bigbluebuttonbn', 'hasintegration' => true],
'book' => ['modname' => 'book', 'hasintegration' => false],
'choice' => ['modname' => 'choice', 'hasintegration' => true],
'data' => ['modname' => 'data', 'hasintegration' => true],
@@ -63,7 +63,7 @@ final class missingoverviewnotice_test extends \advanced_testcase {
public static function overview_integrations_provider(): array {
return [
'assign' => ['modname' => 'assign', 'expectempty' => true],
'bigbluebuttonbn' => ['modname' => 'bigbluebuttonbn', 'expectempty' => false],
'bigbluebuttonbn' => ['modname' => 'bigbluebuttonbn', 'expectempty' => true],
'book' => ['modname' => 'book', 'expectempty' => false],
'choice' => ['modname' => 'choice', 'expectempty' => true],
'data' => ['modname' => 'data', 'expectempty' => true],
@@ -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);
$opendate = $dates->get_open_date();
if (empty($opendate)) {
return new overviewitem(
name: get_string('opens', 'bigbluebuttonbn'),
value: null,
content: '-',
);
}
$content = humandate::create_from_timestamp($opendate);
return new overviewitem(
name: get_string('opens', 'bigbluebuttonbn'),
value: $opendate,
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,116 @@
<?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/>.
declare(strict_types=1);
namespace mod_bigbluebuttonbn;
use cm_info;
use core\activity_dates;
/**
* Class for fetching the important dates in mod_bigbluebuttonbn for a given module instance and a user.
*
* @package mod_bigbluebuttonbn
* @copyright 2025 Laurent David <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dates extends activity_dates {
/**
* Returns the activity due date.
*
* @var int|null $timeclose the activity due date
*/
protected ?int $timeclose = null;
/**
* @var int|null $timeopen the activity open date
*/
protected ?int $timeopen = null;
/**
* @var instance the instance of the activity
*/
protected instance $instance;
/**
* activity_dates constructor.
*
* @param cm_info $cm course module
* @param int $userid user id
*/
public function __construct(cm_info $cm, int $userid) {
parent::__construct($cm, $userid);
$this->instance = instance::get_from_cmid((int) $cm->id);
}
/**
* Returns a list of important dates in mod_choice
*
* @return array
*/
protected function get_dates(): array {
$timeopen = $this->instance->get_instance_var('openingtime');
$timeclose = $this->instance->get_instance_var('closingtime');
$now = time();
$dates = [];
if ($timeopen) {
$openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened';
$dates[] = [
'dataid' => 'timeopen',
'label' => get_string($openlabelid, 'course'),
'timestamp' => (int) $timeopen,
];
$this->timeopen = (int) $timeopen;
}
if ($timeclose) {
$closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed';
$dates[] = [
'dataid' => 'timeclose',
'label' => get_string($closelabelid, 'course'),
'timestamp' => (int) $timeclose,
];
$this->timeclose = (int) $timeclose;
}
return $dates;
}
/**
* Returns the activity due date.
*
* @return int|null
*/
public function get_close_date(): ?int {
if (!isset($this->timeclose)) {
$this->get_dates();
}
return $this->timeclose;
}
/**
* Returns the activity open date.
*
* @return int|null
*/
public function get_open_date(): ?int {
if (!isset($this->timeopen)) {
$this->get_dates();
}
return $this->timeopen;
}
}
@@ -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;
}
}
+3 -31
View File
@@ -24,36 +24,8 @@
* @author Fred Dixon (ffdixon [at] blindsidenetworks [dt] com)
*/
use core\notification;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\output\index;
use mod_bigbluebuttonbn\plugin;
require_once("../../config.php");
require(__DIR__.'/../../config.php');
global $PAGE, $OUTPUT;
$id = required_param('id', PARAM_INT);
$course = get_course($id);
require_login($course, true);
$courseid = required_param('id', PARAM_INT);
$PAGE->set_url('/mod/bigbluebuttonbn/index.php', ['id' => $id]);
$PAGE->set_title(get_string('modulename', plugin::COMPONENT));
$PAGE->set_heading($course->fullname);
$PAGE->set_cacheable(false);
$PAGE->set_pagelayout('incourse');
$PAGE->navbar->add($PAGE->title, $PAGE->url);
$instances = instance::get_all_instances_in_course($course->id);
if (empty($instances)) {
notification::add(
get_string('index_error_noinstances', plugin::COMPONENT),
notification::ERROR
);
redirect(new moodle_url('/course/view.php', ['id' => $course->id]));
}
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('index_heading', plugin::COMPONENT));
$renderer = $PAGE->get_renderer(plugin::COMPONENT);
echo $renderer->render(new index($course, $instances));
echo $OUTPUT->footer();
\core_courseformat\activityoverviewbase::redirect_to_overview_page($courseid, 'bigbluebuttonbn');
@@ -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';
@@ -40,25 +40,6 @@
<span>{{#userdate}} {{startedat}}, {{#str}} strftimetime, core_langconfig{{/str}} {{/userdate}}.</span>
<span class="status-message">{{statusmessage}}</span>
</div>
{{/statusrunning}}
{{^statusrunning}}
<div class="status-message">{{statusmessage}}</div>
{{/statusrunning}}
<div class="conf-opening-time">
{{#openingtime}}
<span class="conf-opening">
<span class="fw-bold">{{#str}}mod_form_field_openingtime, bigbluebuttonbn{{/str}}:</span>
{{#userdate}} {{.}}, {{#str}} strftimedaydatetime, langconfig {{/str}} {{/userdate}}
</span>
{{/openingtime}}
{{#closingtime}}
<div class="conf-closing">
<span class="fw-bold">{{#str}}mod_form_field_closingtime, bigbluebuttonbn{{/str}}:</span>
{{#userdate}} {{.}}, {{#str}} strftimedaydatetime, langconfig {{/str}} {{/userdate}}
</div>
{{/closingtime}}
</div>
{{#statusrunning}}
<div>
<span class="fw-bold">
{{#moderatorplural}}{{#str}}view_message_moderators, mod_bigbluebuttonbn{{/str}}{{/moderatorplural}}
@@ -74,6 +55,9 @@
<span>{{participantcount}}</span>
</div>
{{/statusrunning}}
{{^statusrunning}}
<div class="status-message">{{statusmessage}}</div>
{{/statusrunning}}
</div>
<div id="bigbluebuttonbn-room-view-control-panel" data-bbb-id="{{bigbluebuttonbnid}}" class="mt-2">
@@ -28,9 +28,9 @@ Feature: Manage BigBlueButton session timings
| C1 | bigbluebuttonbn | BBB 1 | <openingtime> | <closingtime> |
When I am on the "BBB 1" "bigbluebuttonbn activity" page logged in as student1
And "Join session" "link" <buttonvisibility> exist
And I should see "Open:"
And I should see "<opens>:"
And I should see "<openingtime>%A, %d %B %Y##"
And I should see "Close:"
And I should see "<closes>:"
And I should see "<closingtime>%A, %d %B %Y##"
And I am viewing calendar in "month" view
And I <calendarvisibility> see "BBB 1"
@@ -39,7 +39,7 @@ Feature: Manage BigBlueButton session timings
And I <upcomingeventvisibility> see "BBB 1" in the "Upcoming events" "block"
Examples:
| openingtime | closingtime | calendarvisibility | buttonvisibility | upcomingeventvisibility |
| ##now +1 minute## | ##now +5 minutes## | should | should not | should |
| ##1 hour ago## | ##+2 hours## | should | should | should not |
| ##yesterday## | ##yesterday +3 hours## | should not | should not | should not |
| opens | closes | openingtime | closingtime | calendarvisibility | buttonvisibility | upcomingeventvisibility |
| Opens | Closes | ##now +1 minute## | ##now +5 minutes## | should | should not | should |
| Opened | Closes | ##1 hour ago## | ##+2 hours## | should | should | should not |
| Opened | Closed | ##yesterday## | ##yesterday +3 hours## | should not | should not | should not |
@@ -54,50 +54,3 @@ Feature: Test the ability to end a meeting
| activity | Room recordings |
When I am on the "Room recordings" Activity page logged in as admin
Then "End session" "link" should exist
Scenario Outline: Only a BigBlueButton moderator can end a session from the index page
Given the following course exists:
| name | Test course |
| shortname | C1 |
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 | C1 | <role> |
And the following "activity" exists:
| course | C1 |
| activity | bigbluebuttonbn |
| name | Room recordings |
| idnumber | Room recordings |
| moderators | <moderators> |
And the following "mod_bigbluebuttonbn > meeting" exists:
| activity | Room recordings |
When I am on the C1 "mod_bigbluebuttonbn > Index" page logged in as traverst
Then "End session" "link" <existence> exist
Examples:
# Note: If the teacher is not listed as a moderator in the activity roles, then will not have permission to end the
# session.
| moderators | role | existence |
| | editingteacher | should not |
| | teacher | should not |
| | student | should not |
| role:teacher | student | should not |
| role:teacher | teacher | should |
| role:student | student | should |
| user:traverst | student | should |
Scenario: An administrator can always end a meeting on the index page
Given the following course exists:
| name | Test course |
| shortname | C1 |
And the following "activity" exists:
| course | C1 |
| activity | bigbluebuttonbn |
| name | Room recordings |
| idnumber | Room recordings |
And the following "mod_bigbluebuttonbn > meeting" exists:
| activity | Room recordings |
When I am on the C1 "mod_bigbluebuttonbn > Index" page logged in as admin
Then "End session" "link" should exist
@@ -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,434 @@
<?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.
*/
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.
*/
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
* @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
* @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
*
* @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
* @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,
];
}
}
@@ -0,0 +1,217 @@
<?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/>.
declare(strict_types=1);
namespace mod_bigbluebuttonbn;
use advanced_testcase;
use core\activity_dates;
/**
* Class for unit testing mod_bigbluebutton\dates.
*
* @package mod_bigbluebuttonbn
* @copyright 2025 Laurent David <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \mod_bigbluebuttonbn\dates
*/
final class dates_test extends advanced_testcase {
use \mod_bigbluebuttonbn\test\testcase_helper_trait;
/**
* Data provider for get_dates_for_module().
*
* @return array[]
*/
public static function get_dates_for_module_provider(): array {
$clock = \core\di::get(\core\clock::class);
$now = $clock->time();
$open = $now - DAYSECS;
$close = $now + DAYSECS;
return [
'Without any dates' => [
null, null, [],
],
'Only with opening time' => [
$open,
null,
[
[
'label' => get_string('activitydate:opened', 'course'),
'timestamp' => $open,
'dataid' => 'timeopen',
],
],
],
'Only with closing time' => [
null,
$close,
[
[
'label' => get_string('activitydate:closes', 'course'),
'timestamp' => $close,
'dataid' => 'timeclose',
],
],
],
'With both times' => [
$open,
$close,
[
[
'label' => get_string('activitydate:opened', 'course'),
'timestamp' => $open,
'dataid' => 'timeopen',
],
[
'label' => get_string('activitydate:closes', 'course'),
'timestamp' => $close,
'dataid' => 'timeclose',
],
],
],
'With both times in the future' => [
$now + DAYSECS,
$now + (2 * DAYSECS),
[
[
'label' => get_string('activitydate:opens', 'course'),
'timestamp' => $now + DAYSECS,
'dataid' => 'timeopen',
],
[
'label' => get_string('activitydate:closes', 'course'),
'timestamp' => $now + (2 * DAYSECS),
'dataid' => 'timeclose',
],
],
],
'With both times in the past' => [
$now - (2 * DAYSECS),
$now - DAYSECS,
[
[
'label' => get_string('activitydate:opened', 'course'),
'timestamp' => $now - (2 * DAYSECS),
'dataid' => 'timeopen',
],
[
'label' => get_string('activitydate:closed', 'course'),
'timestamp' => $now - DAYSECS,
'dataid' => 'timeclose',
],
],
],
];
}
/**
* Test for get_dates_for_module().
*
* @param int|null $open Opening time in the BigBlueButton.
* @param int|null $close Closing time in the BigBlueButton.
* @param array $expected The expected value of calling get_dates_for_module()
* @covers ::get_dates_for_module
* @dataProvider get_dates_for_module_provider
*/
public function test_get_dates_for_module(
?int $open,
?int $close,
array $expected
): void {
$this->resetAfterTest();
['user' => $user, 'cm' => $cm] = $this->setup_instance($open, $close);
$this->setUser($user);
$dates = activity_dates::get_dates_for_module($cm, (int) $user->id);
$this->assertEquals($expected, $dates);
}
/**
* Test for get_open_date().
*
* @param int|null $open Opening time in the BigBlueButton.
* @param int|null $close Closing time in the BigBlueButton.
* @covers ::get_open_date
* @dataProvider get_dates_for_module_provider
*/
public function test_get_open_date(
?int $open,
?int $close,
): void {
$this->resetAfterTest();
['user' => $user, 'cm' => $cm] = $this->setup_instance($open, $close);
$this->setUser($user);
$dates = new \mod_bigbluebuttonbn\dates($cm, (int) $user->id);
$this->assertEquals($open, $dates->get_open_date());
}
/**
* Test for get_close_date().
*
* @param int|null $open Opening time in the BigBlueButton.
* @param int|null $close Closing time in the BigBlueButton.
* @covers ::get_close_date
* @dataProvider get_dates_for_module_provider
*/
public function test_get_close_date(
?int $open,
?int $close,
): void {
$this->resetAfterTest();
['user' => $user, 'cm' => $cm] = $this->setup_instance($open, $close);
$dates = new \mod_bigbluebuttonbn\dates($cm, (int) $user->id);
$this->assertEquals($close, $dates->get_close_date());
}
/**
* Setup a BigBlueButton activity instance.
*
* @param int|null $open Opening time in the BigBlueButton.
* @param int|null $close Closing time in the BigBlueButton.
* @return array with keys 'user' and 'cm'.
*/
private function setup_instance(
?int $open,
?int $close,
): array {
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$user = $generator->create_user();
$generator->enrol_user($user->id, $course->id);
$data = [];
if ($open !== null) {
$data['openingtime'] = $open;
}
if ($close !== null) {
$data['closingtime'] = $close;
}
$this->setAdminUser();
[$bbactivitycontext, $bbactivitycm, $bbactivity] = $this->create_instance(
$course,
$data
);
return ['user' => $user, 'cm' => $bbactivitycm];
}
}
@@ -69,11 +69,27 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator {
"recordings_preview" => 0,
"grade" => 0,
];
$record = (array) $record;
$record['participants'] = json_encode($this->get_participants_from_record($record));
if (!empty($record['openingtime'])) {
if (is_numeric($record['openingtime'])) {
$record['openingtime'] = intval($record['openingtime']);
} else {
// If it is a string, we assume it is a timestamp.
$record['openingtime'] = strtotime($record['openingtime']);
}
}
if (!empty($record['closingtime'])) {
if (is_numeric($record['closingtime'])) {
$record['closingtime'] = intval($record['closingtime']);
} else {
// If it is a string, we assume it is a timestamp.
$record['closingtime'] = strtotime($record['closingtime']);
}
}
foreach ($defaults as $key => $value) {
if (!isset($record[$key])) {
$record[$key] = $value;
@@ -0,0 +1,105 @@
<?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;
/**
* Genarator tests class for mod_bigbluebuttonbn.
*
* @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 generator_test extends \advanced_testcase {
/**
* Test the creation of a bigbluebuttonbn instance.
* @covers \mod_bigbluebuttonbn_generator::create_instance
*/
public function test_create_instance(): void {
$db = \core\di::get(\moodle_database::class);
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$bigbluebuttonbn = $this->getDataGenerator()->create_module('bigbluebuttonbn', ['course' => $course]);
$records = $db->get_records('bigbluebuttonbn', ['course' => $course->id], 'id');
$this->assertEquals(1, count($records));
$this->assertTrue(array_key_exists($bigbluebuttonbn->id, $records));
$params = ['course' => $course->id, 'name' => 'Another bigbluebuttonbn'];
$bigbluebuttonbn = $this->getDataGenerator()->create_module('bigbluebuttonbn', $params);
$records = $db->get_records('bigbluebuttonbn', ['course' => $course->id], 'id');
$this->assertEquals(2, count($records));
$this->assertEquals('Another bigbluebuttonbn', $records[$bigbluebuttonbn->id]->name);
}
/**
* Test the creation of a bigbluebuttonbn instance with a custom name.
*
* @param string|int $opening The opening time as a timestamp or human-readable date.
* @param string|int $closing The closing time as a timestamp or human-readable date
* @param int $expectedopening The expected opening time as a timestamp.
* @param int $expectedclosing The expected closing time as a timestamp.
* @covers \mod_bigbluebuttonbn_generator::create_instance
* @dataProvider provider_create_instance_with_name
*/
public function test_create_instance_with_dates(
string|int $opening,
string|int $closing,
int $expectedopening,
int $expectedclosing
): void {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$params = ['course' => $course->id, 'openingtime' => $opening, 'closingtime' => $closing];
$bigbluebuttonbn = $this->getDataGenerator()->create_module('bigbluebuttonbn', $params);
$instance = \mod_bigbluebuttonbn\instance::get_from_instanceid($bigbluebuttonbn->id);
$this->assertEquals($expectedopening, $instance->get_instance_var('openingtime'));
$this->assertEquals($expectedclosing, $instance->get_instance_var('closingtime'));
}
/**
* Data provider for test_create_instance_with_dates.
*
* @return array[]
*/
public static function provider_create_instance_with_name(): array {
global $CFG;
require_once($CFG->libdir . '/testing/classes/frozen_clock.php');
$clock = new \frozen_clock();
\core\di::set(\core\clock::class, $clock);
$opening = $clock->time();
$closing = $opening + DAYSECS;
return [
'Timestamp' => [
$opening,
$closing,
$opening,
$closing,
],
'Human date' => [
userdate($opening, get_string('strftimedatetimeaccurate', 'langconfig')),
userdate($closing, get_string('strftimedatetimeaccurate', 'langconfig')),
$opening,
$closing,
],
];
}
}