Merge branch 'MDL-83889-main' of https://github.com/laurentdavid/moodle
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
+6
-6
@@ -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,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user