See Release Notes
Long Term Support Release
Differences Between: [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace mod_bigbluebuttonbn\external; 18 19 use external_api; 20 use mod_bigbluebuttonbn\instance; 21 use mod_bigbluebuttonbn\test\testcase_helper_trait; 22 use require_login_exception; 23 24 defined('MOODLE_INTERNAL') || die(); 25 26 global $CFG; 27 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 28 29 /** 30 * Tests for the update_course class. 31 * 32 * @package mod_bigbluebuttonbn 33 * @category test 34 * @copyright 2021 - present, Blindside Networks Inc 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 * @author Laurent David (laurent@call-learning.fr) 37 * @covers \mod_bigbluebuttonbn\external\get_recordings 38 */ 39 class get_recordings_test extends \externallib_advanced_testcase { 40 use testcase_helper_trait; 41 42 /** 43 * Setup for test 44 */ 45 public function setUp(): void { 46 parent::setUp(); 47 $this->initialise_mock_server(); 48 } 49 50 /** 51 * Helper 52 * 53 * @param mixed ...$params 54 * @return array|bool|mixed 55 */ 56 protected function get_recordings(...$params) { 57 $recordings = get_recordings::execute(...$params); 58 59 return external_api::clean_returnvalue(get_recordings::execute_returns(), $recordings); 60 } 61 62 /** 63 * Test execute API CALL with no instance 64 */ 65 public function test_execute_wrong_instance() { 66 $getrecordings = $this->get_recordings(1234); 67 68 $this->assertIsArray($getrecordings); 69 $this->assertArrayHasKey('status', $getrecordings); 70 $this->assertEquals(false, $getrecordings['status']); 71 $this->assertStringContainsString('nosuchinstance', $getrecordings['warnings'][0]['warningcode']); 72 } 73 74 /** 75 * Test execute API CALL without login 76 */ 77 public function test_execute_without_login() { 78 $this->resetAfterTest(); 79 80 $course = $this->getDataGenerator()->create_course(); 81 $record = $this->getDataGenerator()->create_module('bigbluebuttonbn', ['course' => $course->id]); 82 $instance = instance::get_from_instanceid($record->id); 83 84 $this->expectException(require_login_exception::class); 85 $this->get_recordings($instance->get_instance_id()); 86 } 87 88 /** 89 * Test execute API CALL with invalid login 90 */ 91 public function test_execute_with_invalid_login() { 92 $this->resetAfterTest(); 93 94 $generator = $this->getDataGenerator(); 95 $course = $generator->create_course(); 96 $record = $generator->create_module('bigbluebuttonbn', ['course' => $course->id]); 97 $instance = instance::get_from_instanceid($record->id); 98 99 $user = $generator->create_user(); 100 $this->setUser($user); 101 102 $this->expectException(require_login_exception::class); 103 $this->get_recordings($instance->get_instance_id()); 104 } 105 106 /** 107 * When login as a student 108 */ 109 public function test_execute_with_valid_login() { 110 $this->resetAfterTest(); 111 112 $generator = $this->getDataGenerator(); 113 $course = $generator->create_course(); 114 $record = $generator->create_module('bigbluebuttonbn', ['course' => $course->id]); 115 $instance = instance::get_from_instanceid($record->id); 116 117 $user = $generator->create_and_enrol($course, 'student'); 118 $this->setUser($user); 119 120 $getrecordings = $this->get_recordings($instance->get_instance_id()); 121 122 $this->assertIsArray($getrecordings); 123 $this->assertArrayHasKey('status', $getrecordings); 124 $this->assertEquals(true, $getrecordings['status']); 125 $this->assertNotEmpty($getrecordings['tabledata']); 126 $this->assertEquals('[]', $getrecordings['tabledata']['data']); 127 } 128 129 /** 130 * Check if tools are present for teacher/moderator 131 */ 132 public function test_get_recordings_tools() { 133 $this->resetAfterTest(); 134 $dataset = [ 135 'type' => instance::TYPE_ALL, 136 'groups' => null, 137 'users' => [['username' => 't1', 'role' => 'editingteacher'], ['username' => 's1', 'role' => 'student']], 138 'recordingsdata' => [ 139 [['name' => 'Recording1']], 140 [['name' => 'Recording2']] 141 ], 142 ]; 143 $activityid = $this->create_from_dataset($dataset); 144 $instance = instance::get_from_instanceid($activityid); 145 146 $context = \context_course::instance($instance->get_course_id()); 147 foreach ($dataset['users'] as $userdef) { 148 $user = \core_user::get_user_by_username($userdef['username']); 149 $this->setUser($user); 150 $getrecordings = $this->get_recordings($instance->get_instance_id()); 151 // Check users see or do not see recording dependings on their groups. 152 foreach ($dataset['recordingsdata'] as $recordingdata) { 153 foreach ($recordingdata as $recording) { 154 if (has_capability('moodle/course:update', $context)) { 155 $this->assertStringContainsString('data-action=\"delete\"', $getrecordings['tabledata']['data'], 156 "User $user->username, should be able to delete the recording {$recording['name']}"); 157 $this->assertStringContainsString('data-action=\"publish\"', $getrecordings['tabledata']['data'], 158 "User $user->username, should be able to publish the recording {$recording['name']}"); 159 } else { 160 $this->assertStringNotContainsString('data-action=\"delete\"', $getrecordings['tabledata']['data'], 161 "User $user->username, should not be able to delete the recording {$recording['name']}"); 162 } 163 } 164 } 165 } 166 // Now without delete. 167 foreach ($dataset['users'] as $userdef) { 168 $user = \core_user::get_user_by_username($userdef['username']); 169 $this->setUser($user); 170 $getrecordings = $this->get_recordings($instance->get_instance_id(), 'protect'); 171 // Check users see or do not see recording dependings on their groups. 172 foreach ($dataset['recordingsdata'] as $recordingdata) { 173 foreach ($recordingdata as $recording) { 174 $this->assertStringNotContainsString('data-action=\"delete\"', $getrecordings['tabledata']['data'], 175 "User $user->username, should not be able to delete the recording {$recording['name']}"); 176 } 177 } 178 } 179 } 180 181 /** 182 * Check preview is present and displayed 183 */ 184 public function test_get_recordings_preview() { 185 $this->resetAfterTest(); 186 $dataset = [ 187 'type' => instance::TYPE_ALL, 188 'additionalsettings' => [ 189 'recordings_preview' => 1 190 ], 191 'groups' => null, 192 'users' => [['username' => 't1', 'role' => 'editingteacher'], ['username' => 's1', 'role' => 'student']], 193 'recordingsdata' => [ 194 [['name' => 'Recording1']], 195 [['name' => 'Recording2']] 196 ], 197 ]; 198 $activityid = $this->create_from_dataset($dataset); 199 $instance = instance::get_from_instanceid($activityid); 200 201 $context = \context_course::instance($instance->get_course_id()); 202 foreach ($dataset['users'] as $userdef) { 203 $user = \core_user::get_user_by_username($userdef['username']); 204 $this->setUser($user); 205 $getrecordings = $this->get_recordings($instance->get_instance_id()); 206 $this->assertNotEmpty($getrecordings['tabledata']['columns']['3']); 207 $this->assertEquals('preview', $getrecordings['tabledata']['columns']['3']['key']); 208 } 209 } 210 211 /** 212 * Check we can see all recording from a course in a room only instance 213 * @covers \mod_bigbluebuttonbn\external\get_recordings::execute 214 */ 215 public function test_get_recordings_room_only() { 216 $this->resetAfterTest(); 217 set_config('bigbluebuttonbn_importrecordings_enabled', 1); 218 $dataset = [ 219 'type' => instance::TYPE_ALL, 220 'groups' => null, 221 'users' => [['username' => 't1', 'role' => 'editingteacher'], ['username' => 's1', 'role' => 'student']], 222 'recordingsdata' => [ 223 [['name' => 'Recording1']], 224 [['name' => 'Recording2']] 225 ], 226 ]; 227 $activityid = $this->create_from_dataset($dataset); 228 $instance = instance::get_from_instanceid($activityid); 229 230 // Now create a recording only activity. 231 $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); 232 // Now create a new activity and import the first record. 233 $newactivity = $plugingenerator->create_instance([ 234 'course' => $instance->get_course_id(), 235 'type' => instance::TYPE_RECORDING_ONLY, 236 'name' => 'Example 2' 237 ]); 238 $plugingenerator->create_meeting([ 239 'instanceid' => $newactivity->id, 240 ]); // We need to have a meeting created in order to import recordings. 241 $newinstance = instance::get_from_instanceid($newactivity->id); 242 $this->create_recordings_for_instance($newinstance, [['name' => 'Recording3']]); 243 244 foreach ($dataset['users'] as $userdef) { 245 $user = \core_user::get_user_by_username($userdef['username']); 246 $this->setUser($user); 247 $getrecordings = $this->get_recordings($newinstance->get_instance_id()); 248 // Check users see or do not see recording dependings on their groups. 249 $data = json_decode($getrecordings['tabledata']['data']); 250 $this->assertCount(3, $data); 251 } 252 } 253 254 /** 255 * Check if we can see the imported recording in a new instance 256 * @covers \mod_bigbluebuttonbn\external\get_recordings::execute 257 */ 258 public function test_get_recordings_imported() { 259 $this->resetAfterTest(); 260 set_config('bigbluebuttonbn_importrecordings_enabled', 1); 261 $dataset = [ 262 'type' => instance::TYPE_ALL, 263 'groups' => null, 264 'users' => [['username' => 't1', 'role' => 'editingteacher'], ['username' => 's1', 'role' => 'student']], 265 'recordingsdata' => [ 266 [['name' => 'Recording1']], 267 [['name' => 'Recording2']] 268 ], 269 ]; 270 271 $activityid = $this->create_from_dataset($dataset); 272 $instance = instance::get_from_instanceid($activityid); 273 274 $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); 275 // Now create a new activity and import the first record. 276 $newactivity = $plugingenerator->create_instance([ 277 'course' => $instance->get_course_id(), 278 'type' => instance::TYPE_ALL, 279 'name' => 'Example 2' 280 ]); 281 $plugingenerator->create_meeting([ 282 'instanceid' => $newactivity->id, 283 ]); // We need to have a meeting created in order to import recordings. 284 $newinstance = instance::get_from_instanceid($newactivity->id); 285 $recordings = $instance->get_recordings(); 286 foreach ($recordings as $recording) { 287 if ($recording->get('name') == 'Recording1') { 288 $recording->create_imported_recording($newinstance); 289 } 290 } 291 292 foreach ($dataset['users'] as $userdef) { 293 $user = \core_user::get_user_by_username($userdef['username']); 294 $this->setUser($user); 295 $getrecordings = $this->get_recordings($newinstance->get_instance_id()); 296 // Check users see or do not see recording dependings on their groups. 297 foreach ($dataset['recordingsdata'] as $index => $recordingdata) { 298 foreach ($recordingdata as $recording) { 299 if ($instance->can_manage_recordings()) { 300 $this->assertStringContainsString('data-action=\"delete\"', $getrecordings['tabledata']['data'], 301 "User $user->username, should be able to delete the recording {$recording['name']}"); 302 } else { 303 $this->assertStringNotContainsString('data-action=\"delete\"', $getrecordings['tabledata']['data'], 304 "User $user->username, should not be able to delete the recording {$recording['name']}"); 305 } 306 if ($index === 0) { 307 $this->assertStringContainsString($recording['name'], $getrecordings['tabledata']['data']); 308 } else { 309 $this->assertStringNotContainsString($recording['name'], $getrecordings['tabledata']['data']); 310 } 311 } 312 } 313 314 } 315 } 316 317 /** 318 * Check we can see only imported recordings in a recordings only instance when "Show only imported links" enabled. 319 * @covers \mod_bigbluebuttonbn\external\get_recordings::execute 320 */ 321 public function test_get_imported_recordings_only() { 322 $this->resetAfterTest(); 323 set_config('bigbluebuttonbn_importrecordings_enabled', 1); 324 $dataset = [ 325 'type' => instance::TYPE_ALL, 326 'groups' => null, 327 'users' => [['username' => 's1', 'role' => 'student']], 328 'recordingsdata' => [ 329 [['name' => 'Recording1']], 330 [['name' => 'Recording2']] 331 ], 332 ]; 333 $activityid = $this->create_from_dataset($dataset); 334 $instance = instance::get_from_instanceid($activityid); 335 336 // Now create a recording only activity. 337 $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn'); 338 // Now create a new activity and import the first record. 339 $newactivity = $plugingenerator->create_instance([ 340 'course' => $instance->get_course_id(), 341 'type' => instance::TYPE_RECORDING_ONLY, 342 'name' => 'Example 2' 343 ]); 344 $plugingenerator->create_meeting([ 345 'instanceid' => $newactivity->id, 346 ]); // We need to have a meeting created in order to import recordings. 347 $newinstance = instance::get_from_instanceid($newactivity->id); 348 $recordings = $instance->get_recordings(); 349 foreach ($recordings as $recording) { 350 if ($recording->get('name') == 'Recording1') { 351 $recording->create_imported_recording($newinstance); 352 } 353 } 354 $user = \core_user::get_user_by_username('s1'); 355 $this->setUser($user); 356 $getrecordings = $this->get_recordings($newinstance->get_instance_id()); 357 $data = json_decode($getrecordings['tabledata']['data']); 358 // Check that all recordings including the imported recording appear. 359 $this->assertCount(3, $data); 360 // Set the flags to enable "Show only imported links". 361 set_config('bigbluebuttonbn_recordings_imported_default', 1); 362 set_config('bigbluebuttonbn_recordings_imported_editable', 0); 363 $getrecordings = $this->get_recordings($newinstance->get_instance_id()); 364 $data = json_decode($getrecordings['tabledata']['data']); 365 $this->assertCount(1, $data); 366 } 367 368 /** 369 * Check if recording are visible/invisible depending on the group. 370 * 371 * @param string $type 372 * @param array $groups 373 * @param array $users 374 * @param array $recordingsdata 375 * @param array $test 376 * @param int $coursemode 377 * 378 * @covers \mod_bigbluebuttonbn\external\get_recordings::execute 379 * @dataProvider recording_group_test_data 380 */ 381 public function test_get_recordings_groups($type, $groups, $users, $recordingsdata, $test, $coursemode) { 382 $this->resetAfterTest(); 383 $dataset = compact('type', 'groups', 'users', 'recordingsdata', 'test', 'coursemode'); 384 $activityid = $this->create_from_dataset($dataset); 385 $instance = instance::get_from_instanceid($activityid); 386 387 foreach ($dataset['users'] as $userdef) { 388 $user = \core_user::get_user_by_username($userdef['username']); 389 $this->setUser($user); 390 $groups = array_values(groups_get_my_groups()); 391 $mygroup = !empty($groups) ? end($groups) : null; 392 393 $getrecordings = $this->get_recordings( 394 $instance->get_instance_id(), null, !empty($mygroup) ? $mygroup->id : null); 395 $allrecordingsnames = []; 396 foreach ($recordingsdata as $groups => $rsinfo) { 397 $rnames = array_map(function($rdata) { 398 return $rdata['name']; 399 }, $rsinfo); 400 $allrecordingsnames = array_merge($allrecordingsnames, $rnames); 401 } 402 // Check users see or do not see recording dependings on their groups. 403 foreach ($dataset['test'][$user->username] as $viewablerecordings) { 404 $viewablerecordings = $dataset['test'][$user->username]; 405 $invisiblerecordings = array_diff($allrecordingsnames, $viewablerecordings); 406 foreach ($viewablerecordings as $viewablerecordingname) { 407 $this->assertStringContainsString($viewablerecordingname, $getrecordings['tabledata']['data'], 408 "User $user->username, should see recording {$viewablerecordingname}"); 409 } 410 foreach ($invisiblerecordings as $invisiblerecordingname) { 411 $this->assertStringNotContainsString($invisiblerecordingname, $getrecordings['tabledata']['data'], 412 "User $user->username, should not see recording {$viewablerecordingname}"); 413 } 414 } 415 } 416 } 417 418 /** 419 * Recording group test 420 * 421 * @return array[] 422 */ 423 public function recording_group_test_data() { 424 return [ 425 'visiblegroups' => [ 426 'type' => instance::TYPE_ALL, 427 'groups' => ['G1' => ['s1'], 'G2' => ['s2']], 428 'users' => [ 429 ['username' => 't1', 'role' => 'editingteacher'], 430 ['username' => 's1', 'role' => 'student'], 431 ['username' => 's2', 'role' => 'student'], 432 ['username' => 's3', 'role' => 'student'] 433 ], 434 'recordingsdata' => [ 435 'G1' => [['name' => 'Recording1']], 436 'G2' => [['name' => 'Recording2']], 437 '' => [['name' => 'Recording3']] 438 ], 439 'test' => [ 440 't1' => ['Recording1', 'Recording2', 'Recording3'], // A moderator should see all recordings. 441 's1' => ['Recording1'], // S1 can only see the recordings from his group. 442 's2' => ['Recording2'], // S2 can only see the recordings from his group. 443 's3' => ['Recording3', 'Recording2', 'Recording1'] 444 // S3 should see recordings which have no groups and his groups's recording. 445 ], 446 'coursemode' => VISIBLEGROUPS 447 ], 448 'separategroups' => [ 449 'type' => instance::TYPE_ALL, 450 'groups' => ['G1' => ['s1'], 'G2' => ['s2']], 451 'users' => [ 452 ['username' => 't1', 'role' => 'editingteacher'], 453 ['username' => 's1', 'role' => 'student'], 454 ['username' => 's2', 'role' => 'student'] 455 ], 456 'recordingsdata' => [ 457 'G1' => [['name' => 'Recording1']], 458 'G2' => [['name' => 'Recording2']], 459 '' => [['name' => 'Recording3']] 460 ], 461 'test' => [ 462 't1' => ['Recording1', 'Recording2', 'Recording3'], // A moderator should see all recordings. 463 's1' => ['Recording1'], // S1 can only see the recordings from his group. 464 's2' => ['Recording2'], // S2 can only see the recordings from his group. 465 's3' => ['Recording3'] // S3 should see recordings which have no groups. 466 ], 467 'coursemode' => SEPARATEGROUPS 468 ] 469 ]; 470 } 471 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body