Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402]
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 core_course; 18 19 use core_course_category; 20 21 /** 22 * Tests for class core_course_category 23 * 24 * @package core_course 25 * @category test 26 * @copyright 2013 Marina Glancy 27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 */ 29 class category_test extends \advanced_testcase { 30 31 protected $roles; 32 33 protected function setUp(): void { 34 parent::setUp(); 35 $this->resetAfterTest(); 36 $user = $this->getDataGenerator()->create_user(); 37 $this->setUser($user); 38 } 39 40 protected function get_roleid($context = null) { 41 global $USER; 42 if ($context === null) { 43 $context = \context_system::instance(); 44 } 45 if (is_object($context)) { 46 $context = $context->id; 47 } 48 if (empty($this->roles)) { 49 $this->roles = array(); 50 } 51 if (empty($this->roles[$USER->id])) { 52 $this->roles[$USER->id] = array(); 53 } 54 if (empty($this->roles[$USER->id][$context])) { 55 $this->roles[$USER->id][$context] = create_role('Role for '.$USER->id.' in '.$context, 'role'.$USER->id.'-'.$context, '-'); 56 role_assign($this->roles[$USER->id][$context], $USER->id, $context); 57 } 58 return $this->roles[$USER->id][$context]; 59 } 60 61 protected function assign_capability($capability, $permission = CAP_ALLOW, $contextid = null) { 62 if ($contextid === null) { 63 $contextid = \context_system::instance(); 64 } 65 if (is_object($contextid)) { 66 $contextid = $contextid->id; 67 } 68 assign_capability($capability, $permission, $this->get_roleid($contextid), $contextid, true); 69 accesslib_clear_all_caches_for_unit_testing(); 70 } 71 72 public function test_create_coursecat() { 73 // Create the category. 74 $data = new \stdClass(); 75 $data->name = 'aaa'; 76 $data->description = 'aaa'; 77 $data->idnumber = ''; 78 79 $category1 = core_course_category::create($data); 80 81 // Initially confirm that base data was inserted correctly. 82 $this->assertSame($data->name, $category1->name); 83 $this->assertSame($data->description, $category1->description); 84 $this->assertSame($data->idnumber, $category1->idnumber); 85 86 $this->assertGreaterThanOrEqual(1, $category1->sortorder); 87 88 // Create two more categories and test the sortorder worked correctly. 89 $data->name = 'ccc'; 90 $category2 = core_course_category::create($data); 91 92 $data->name = 'bbb'; 93 $category3 = core_course_category::create($data); 94 95 $this->assertGreaterThan($category1->sortorder, $category2->sortorder); 96 $this->assertGreaterThan($category2->sortorder, $category3->sortorder); 97 } 98 99 public function test_name_idnumber_exceptions() { 100 try { 101 core_course_category::create(array('name' => '')); 102 $this->fail('Missing category name exception expected in core_course_category::create'); 103 } catch (\moodle_exception $e) { 104 $this->assertInstanceOf('moodle_exception', $e); 105 } 106 $cat1 = core_course_category::create(array('name' => 'Cat1', 'idnumber' => '1')); 107 try { 108 $cat1->update(array('name' => '')); 109 $this->fail('Missing category name exception expected in core_course_category::update'); 110 } catch (\moodle_exception $e) { 111 $this->assertInstanceOf('moodle_exception', $e); 112 } 113 try { 114 core_course_category::create(array('name' => 'Cat2', 'idnumber' => '1')); 115 $this->fail('Duplicate idnumber exception expected in core_course_category::create'); 116 } catch (\moodle_exception $e) { 117 $this->assertInstanceOf('moodle_exception', $e); 118 } 119 $cat2 = core_course_category::create(array('name' => 'Cat2', 'idnumber' => '2')); 120 try { 121 $cat2->update(array('idnumber' => '1')); 122 $this->fail('Duplicate idnumber exception expected in core_course_category::update'); 123 } catch (\moodle_exception $e) { 124 $this->assertInstanceOf('moodle_exception', $e); 125 } 126 // Test that duplicates with an idnumber of 0 cannot be created. 127 core_course_category::create(array('name' => 'Cat3', 'idnumber' => '0')); 128 try { 129 core_course_category::create(array('name' => 'Cat4', 'idnumber' => '0')); 130 $this->fail('Duplicate idnumber "0" exception expected in core_course_category::create'); 131 } catch (\moodle_exception $e) { 132 $this->assertInstanceOf('moodle_exception', $e); 133 } 134 // Test an update cannot make a duplicate idnumber of 0. 135 try { 136 $cat2->update(array('idnumber' => '0')); 137 $this->fail('Duplicate idnumber "0" exception expected in core_course_category::update'); 138 } catch (\Exception $e) { 139 $this->assertInstanceOf('moodle_exception', $e); 140 } 141 } 142 143 public function test_visibility() { 144 $this->assign_capability('moodle/category:viewhiddencategories'); 145 $this->assign_capability('moodle/category:manage'); 146 147 // Create category 1 initially hidden. 148 $category1 = core_course_category::create(array('name' => 'Cat1', 'visible' => 0)); 149 $this->assertEquals(0, $category1->visible); 150 $this->assertEquals(0, $category1->visibleold); 151 152 // Create category 2 initially hidden as a child of hidden category 1. 153 $category2 = core_course_category::create(array('name' => 'Cat2', 'visible' => 0, 'parent' => $category1->id)); 154 $this->assertEquals(0, $category2->visible); 155 $this->assertEquals(0, $category2->visibleold); 156 157 // Create category 3 initially visible as a child of hidden category 1. 158 $category3 = core_course_category::create(array('name' => 'Cat3', 'visible' => 1, 'parent' => $category1->id)); 159 $this->assertEquals(0, $category3->visible); 160 $this->assertEquals(1, $category3->visibleold); 161 162 // Show category 1 and make sure that category 2 is hidden and category 3 is visible. 163 $category1->show(); 164 $this->assertEquals(1, core_course_category::get($category1->id)->visible); 165 $this->assertEquals(0, core_course_category::get($category2->id)->visible); 166 $this->assertEquals(1, core_course_category::get($category3->id)->visible); 167 168 // Create visible category 4. 169 $category4 = core_course_category::create(array('name' => 'Cat4')); 170 $this->assertEquals(1, $category4->visible); 171 $this->assertEquals(1, $category4->visibleold); 172 173 // Create visible category 5 as a child of visible category 4. 174 $category5 = core_course_category::create(array('name' => 'Cat5', 'parent' => $category4->id)); 175 $this->assertEquals(1, $category5->visible); 176 $this->assertEquals(1, $category5->visibleold); 177 178 // Hide category 4 and make sure category 5 is hidden too. 179 $category4->hide(); 180 $this->assertEquals(0, $category4->visible); 181 $this->assertEquals(0, $category4->visibleold); 182 $category5 = core_course_category::get($category5->id); // We have to re-read from DB. 183 $this->assertEquals(0, $category5->visible); 184 $this->assertEquals(1, $category5->visibleold); 185 186 // Show category 4 and make sure category 5 is visible too. 187 $category4->show(); 188 $this->assertEquals(1, $category4->visible); 189 $this->assertEquals(1, $category4->visibleold); 190 $category5 = core_course_category::get($category5->id); // We have to re-read from DB. 191 $this->assertEquals(1, $category5->visible); 192 $this->assertEquals(1, $category5->visibleold); 193 194 // Move category 5 under hidden category 2 and make sure it became hidden. 195 $category5->change_parent($category2->id); 196 $this->assertEquals(0, $category5->visible); 197 $this->assertEquals(1, $category5->visibleold); 198 199 // Re-read object for category 5 from DB and check again. 200 $category5 = core_course_category::get($category5->id); 201 $this->assertEquals(0, $category5->visible); 202 $this->assertEquals(1, $category5->visibleold); 203 204 // Rricky one! Move hidden category 5 under visible category ("Top") and make sure it is still hidden- 205 // WHY? Well, different people may expect different behaviour here. So better keep it hidden. 206 $category5->change_parent(0); 207 $this->assertEquals(0, $category5->visible); 208 $this->assertEquals(1, $category5->visibleold); 209 } 210 211 public function test_hierarchy() { 212 $this->assign_capability('moodle/category:viewhiddencategories'); 213 $this->assign_capability('moodle/category:manage'); 214 215 $category1 = core_course_category::create(array('name' => 'Cat1')); 216 $category2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id)); 217 $category3 = core_course_category::create(array('name' => 'Cat3', 'parent' => $category1->id)); 218 $category4 = core_course_category::create(array('name' => 'Cat4', 'parent' => $category2->id)); 219 220 // Check function get_children(). 221 $this->assertEquals(array($category2->id, $category3->id), array_keys($category1->get_children())); 222 // Check function get_parents(). 223 $this->assertEquals(array($category1->id, $category2->id), $category4->get_parents()); 224 225 // Can not move category to itself or to it's children. 226 $this->assertFalse($category1->can_change_parent($category2->id)); 227 $this->assertFalse($category2->can_change_parent($category2->id)); 228 // Can move category to grandparent. 229 $this->assertTrue($category4->can_change_parent($category1->id)); 230 231 try { 232 $category2->change_parent($category4->id); 233 $this->fail('Exception expected - can not move category'); 234 } catch (\moodle_exception $e) { 235 $this->assertInstanceOf('moodle_exception', $e); 236 } 237 238 $category4->change_parent(0); 239 $this->assertEquals(array(), $category4->get_parents()); 240 $this->assertEquals(array($category2->id, $category3->id), array_keys($category1->get_children())); 241 $this->assertEquals(array(), array_keys($category2->get_children())); 242 } 243 244 public function test_update() { 245 $category1 = core_course_category::create(array('name' => 'Cat1')); 246 $timecreated = $category1->timemodified; 247 $this->assertSame('Cat1', $category1->name); 248 $this->assertTrue(empty($category1->description)); 249 $this->waitForSecond(); 250 $testdescription = 'This is cat 1 а также русский текст'; 251 $category1->update(array('description' => $testdescription)); 252 $this->assertSame($testdescription, $category1->description); 253 $category1 = core_course_category::get($category1->id); 254 $this->assertSame($testdescription, $category1->description); 255 \cache_helper::purge_by_event('changesincoursecat'); 256 $category1 = core_course_category::get($category1->id); 257 $this->assertSame($testdescription, $category1->description); 258 259 $this->assertGreaterThan($timecreated, $category1->timemodified); 260 } 261 262 public function test_delete() { 263 global $DB; 264 265 $this->assign_capability('moodle/category:manage'); 266 $this->assign_capability('moodle/course:create'); 267 268 $initialcatid = $DB->get_field_sql('SELECT max(id) from {course_categories}'); 269 270 $category1 = core_course_category::create(array('name' => 'Cat1')); 271 $category2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id)); 272 $category3 = core_course_category::create(array('name' => 'Cat3')); 273 $category4 = core_course_category::create(array('name' => 'Cat4', 'parent' => $category2->id)); 274 275 $course1 = $this->getDataGenerator()->create_course(array('category' => $category2->id)); 276 $course2 = $this->getDataGenerator()->create_course(array('category' => $category4->id)); 277 $course3 = $this->getDataGenerator()->create_course(array('category' => $category4->id)); 278 $course4 = $this->getDataGenerator()->create_course(array('category' => $category1->id)); 279 280 // Now we have 281 // $category1 282 // $category2 283 // $category4 284 // $course2 285 // $course3 286 // $course1 287 // $course4 288 // $category3 289 // structure. 290 291 // Login as another user to test course:delete capability (user who created course can delete it within 24h even without cap). 292 $this->setUser($this->getDataGenerator()->create_user()); 293 294 // Delete category 2 and move content to category 3. 295 $this->assertFalse($category2->can_move_content_to($category3->id)); // No luck! 296 // Add necessary capabilities. 297 $this->assign_capability('moodle/course:create', CAP_ALLOW, \context_coursecat::instance($category3->id)); 298 $this->assign_capability('moodle/category:manage'); 299 $this->assertTrue($category2->can_move_content_to($category3->id)); // Hurray! 300 $category2->delete_move($category3->id); 301 302 // Make sure we have: 303 // $category1 304 // $course4 305 // $category3 306 // $category4 307 // $course2 308 // $course3 309 // $course1 310 // structure. 311 312 $this->assertNull(core_course_category::get($category2->id, IGNORE_MISSING, true)); 313 $this->assertEquals(array(), $category1->get_children()); 314 $this->assertEquals(array($category4->id), array_keys($category3->get_children())); 315 $this->assertEquals($category4->id, $DB->get_field('course', 'category', array('id' => $course2->id))); 316 $this->assertEquals($category4->id, $DB->get_field('course', 'category', array('id' => $course3->id))); 317 $this->assertEquals($category3->id, $DB->get_field('course', 'category', array('id' => $course1->id))); 318 319 // Delete category 3 completely. 320 $this->assertFalse($category3->can_delete_full()); // No luck! 321 // Add necessary capabilities. 322 $this->assign_capability('moodle/course:delete', CAP_ALLOW, \context_coursecat::instance($category3->id)); 323 $this->assertTrue($category3->can_delete_full()); // Hurray! 324 $category3->delete_full(); 325 326 // Make sure we have: 327 // $category1 328 // $course4 329 // structure. 330 331 // Note that we also have default course category and default 'site' course. 332 $this->assertEquals(1, $DB->get_field_sql('SELECT count(*) FROM {course_categories} WHERE id > ?', array($initialcatid))); 333 $this->assertEquals($category1->id, $DB->get_field_sql('SELECT max(id) FROM {course_categories}')); 334 $this->assertEquals(1, $DB->get_field_sql('SELECT count(*) FROM {course} WHERE id <> ?', array(SITEID))); 335 $this->assertEquals(array('id' => $course4->id, 'category' => $category1->id), 336 (array)$DB->get_record_sql('SELECT id, category from {course} where id <> ?', array(SITEID))); 337 } 338 339 public function test_get_children() { 340 $category1 = core_course_category::create(array('name' => 'Cat1')); 341 $category2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id)); 342 $category3 = core_course_category::create(array('name' => 'Cat3', 'parent' => $category1->id, 'visible' => 0)); 343 $category4 = core_course_category::create(array('name' => 'Cat4', 'idnumber' => '12', 'parent' => $category1->id)); 344 $category5 = core_course_category::create(array('name' => 'Cat5', 'idnumber' => '11', 345 'parent' => $category1->id, 'visible' => 0)); 346 $category6 = core_course_category::create(array('name' => 'Cat6', 'idnumber' => '10', 'parent' => $category1->id)); 347 $category7 = core_course_category::create(array('name' => 'Cat0', 'parent' => $category1->id)); 348 349 $children = $category1->get_children(); 350 // User does not have the capability to view hidden categories, so the list should be 351 // 2, 4, 6, 7. 352 $this->assertEquals(array($category2->id, $category4->id, $category6->id, $category7->id), array_keys($children)); 353 $this->assertEquals(4, $category1->get_children_count()); 354 355 $children = $category1->get_children(array('offset' => 2)); 356 $this->assertEquals(array($category6->id, $category7->id), array_keys($children)); 357 $this->assertEquals(4, $category1->get_children_count()); 358 359 $children = $category1->get_children(array('limit' => 2)); 360 $this->assertEquals(array($category2->id, $category4->id), array_keys($children)); 361 362 $children = $category1->get_children(array('offset' => 1, 'limit' => 2)); 363 $this->assertEquals(array($category4->id, $category6->id), array_keys($children)); 364 365 $children = $category1->get_children(array('sort' => array('name' => 1))); 366 // Must be 7, 2, 4, 6. 367 $this->assertEquals(array($category7->id, $category2->id, $category4->id, $category6->id), array_keys($children)); 368 369 $children = $category1->get_children(array('sort' => array('idnumber' => 1, 'name' => -1))); 370 // Must be 2, 7, 6, 4. 371 $this->assertEquals(array($category2->id, $category7->id, $category6->id, $category4->id), array_keys($children)); 372 373 // Check that everything is all right after purging the caches. 374 \cache_helper::purge_by_event('changesincoursecat'); 375 $children = $category1->get_children(); 376 $this->assertEquals(array($category2->id, $category4->id, $category6->id, $category7->id), array_keys($children)); 377 $this->assertEquals(4, $category1->get_children_count()); 378 } 379 380 /** 381 * Test the get_all_children_ids function. 382 */ 383 public function test_get_all_children_ids() { 384 $category1 = core_course_category::create(array('name' => 'Cat1')); 385 $category2 = core_course_category::create(array('name' => 'Cat2')); 386 $category11 = core_course_category::create(array('name' => 'Cat11', 'parent' => $category1->id)); 387 $category12 = core_course_category::create(array('name' => 'Cat12', 'parent' => $category1->id)); 388 $category13 = core_course_category::create(array('name' => 'Cat13', 'parent' => $category1->id)); 389 $category111 = core_course_category::create(array('name' => 'Cat111', 'parent' => $category11->id)); 390 $category112 = core_course_category::create(array('name' => 'Cat112', 'parent' => $category11->id)); 391 $category1121 = core_course_category::create(array('name' => 'Cat1121', 'parent' => $category112->id)); 392 393 $this->assertCount(0, $category2->get_all_children_ids()); 394 $this->assertCount(6, $category1->get_all_children_ids()); 395 396 $cmpchildrencat1 = array($category11->id, $category12->id, $category13->id, $category111->id, $category112->id, 397 $category1121->id); 398 $childrencat1 = $category1->get_all_children_ids(); 399 // Order of values does not matter. Compare sorted arrays. 400 sort($cmpchildrencat1); 401 sort($childrencat1); 402 $this->assertEquals($cmpchildrencat1, $childrencat1); 403 404 $this->assertCount(3, $category11->get_all_children_ids()); 405 $this->assertCount(0, $category111->get_all_children_ids()); 406 $this->assertCount(1, $category112->get_all_children_ids()); 407 408 $this->assertEquals(array($category1121->id), $category112->get_all_children_ids()); 409 } 410 411 /** 412 * Test the countall function 413 */ 414 public function test_count_all() { 415 global $DB; 416 // Dont assume there is just one. An add-on might create a category as part of the install. 417 $numcategories = $DB->count_records('course_categories'); 418 $this->assertEquals($numcategories, core_course_category::count_all()); 419 $this->assertDebuggingCalled('Method core_course_category::count_all() is deprecated. Please use ' . 420 'core_course_category::is_simple_site()', DEBUG_DEVELOPER); 421 $category1 = core_course_category::create(array('name' => 'Cat1')); 422 $category2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id)); 423 $category3 = core_course_category::create(array('name' => 'Cat3', 'parent' => $category2->id, 'visible' => 0)); 424 // Now we've got three more. 425 $this->assertEquals($numcategories + 3, core_course_category::count_all()); 426 $this->assertDebuggingCalled('Method core_course_category::count_all() is deprecated. Please use ' . 427 'core_course_category::is_simple_site()', DEBUG_DEVELOPER); 428 \cache_helper::purge_by_event('changesincoursecat'); 429 // We should still have 4. 430 $this->assertEquals($numcategories + 3, core_course_category::count_all()); 431 $this->assertDebuggingCalled('Method core_course_category::count_all() is deprecated. Please use ' . 432 'core_course_category::is_simple_site()', DEBUG_DEVELOPER); 433 } 434 435 /** 436 * Test the is_simple_site function 437 */ 438 public function test_is_simple_site() { 439 // By default site has one category and is considered simple. 440 $this->assertEquals(true, core_course_category::is_simple_site()); 441 $default = core_course_category::get_default(); 442 // When there is only one category but it is hidden, it is not a simple site. 443 $default->update(['visible' => 0]); 444 $this->assertEquals(false, core_course_category::is_simple_site()); 445 $default->update(['visible' => 1]); 446 $this->assertEquals(true, core_course_category::is_simple_site()); 447 // As soon as there is more than one category, site is not simple any more. 448 core_course_category::create(array('name' => 'Cat1')); 449 $this->assertEquals(false, core_course_category::is_simple_site()); 450 } 451 452 /** 453 * Test a categories ability to resort courses. 454 */ 455 public function test_resort_courses() { 456 $this->resetAfterTest(true); 457 $generator = $this->getDataGenerator(); 458 $category = $generator->create_category(); 459 $course1 = $generator->create_course(array( 460 'category' => $category->id, 461 'idnumber' => '006-01', 462 'shortname' => 'Biome Study', 463 'fullname' => '<span lang="ar" class="multilang">'.'دراسة منطقة إحيائية'.'</span><span lang="en" class="multilang">Biome Study</span>', 464 'timecreated' => '1000000001' 465 )); 466 $course2 = $generator->create_course(array( 467 'category' => $category->id, 468 'idnumber' => '007-02', 469 'shortname' => 'Chemistry Revision', 470 'fullname' => 'Chemistry Revision', 471 'timecreated' => '1000000002' 472 )); 473 $course3 = $generator->create_course(array( 474 'category' => $category->id, 475 'idnumber' => '007-03', 476 'shortname' => 'Swiss Rolls and Sunflowers', 477 'fullname' => 'Aarkvarks guide to Swiss Rolls and Sunflowers', 478 'timecreated' => '1000000003' 479 )); 480 $course4 = $generator->create_course(array( 481 'category' => $category->id, 482 'idnumber' => '006-04', 483 'shortname' => 'Scratch', 484 'fullname' => '<a href="test.php">Basic Scratch</a>', 485 'timecreated' => '1000000004' 486 )); 487 $c1 = (int)$course1->id; 488 $c2 = (int)$course2->id; 489 $c3 = (int)$course3->id; 490 $c4 = (int)$course4->id; 491 492 $coursecat = core_course_category::get($category->id); 493 $this->assertTrue($coursecat->resort_courses('idnumber')); 494 $this->assertSame(array($c1, $c4, $c2, $c3), array_keys($coursecat->get_courses())); 495 496 $this->assertTrue($coursecat->resort_courses('shortname')); 497 $this->assertSame(array($c1, $c2, $c4, $c3), array_keys($coursecat->get_courses())); 498 499 $this->assertTrue($coursecat->resort_courses('timecreated')); 500 $this->assertSame(array($c1, $c2, $c3, $c4), array_keys($coursecat->get_courses())); 501 502 try { 503 // Enable the multilang filter and set it to apply to headings and content. 504 \filter_manager::reset_caches(); 505 filter_set_global_state('multilang', TEXTFILTER_ON); 506 filter_set_applies_to_strings('multilang', true); 507 $expected = array($c3, $c4, $c1, $c2); 508 } catch (\coding_exception $ex) { 509 $expected = array($c3, $c4, $c2, $c1); 510 } 511 $this->assertTrue($coursecat->resort_courses('fullname')); 512 $this->assertSame($expected, array_keys($coursecat->get_courses())); 513 } 514 515 public function test_get_search_courses() { 516 $cat1 = core_course_category::create(array('name' => 'Cat1')); 517 $cat2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $cat1->id)); 518 $c1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 3', 'summary' => ' ', 'idnumber' => 'ID3')); 519 $c2 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 1', 'summary' => ' ', 'visible' => 0)); 520 $c3 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Математика', 'summary' => ' Test ')); 521 $c4 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 4', 'summary' => ' ', 'idnumber' => 'ID4')); 522 523 $c5 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Test 5', 'summary' => ' ')); 524 $c6 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Дискретная Математика', 'summary' => ' ')); 525 $c7 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Test 7', 'summary' => ' ', 'visible' => 0)); 526 $c8 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Test 8', 'summary' => ' ')); 527 528 // Get courses in category 1 (returned visible only because user is not enrolled). 529 $res = $cat1->get_courses(array('sortorder' => 1)); 530 $this->assertEquals(array($c4->id, $c3->id, $c1->id), array_keys($res)); // Courses are added in reverse order. 531 $this->assertEquals(3, $cat1->get_courses_count()); 532 533 // Get courses in category 1 recursively (returned visible only because user is not enrolled). 534 $res = $cat1->get_courses(array('recursive' => 1)); 535 $this->assertEquals(array($c4->id, $c3->id, $c1->id, $c8->id, $c6->id, $c5->id), array_keys($res)); 536 $this->assertEquals(6, $cat1->get_courses_count(array('recursive' => 1))); 537 538 // Get courses sorted by fullname. 539 $res = $cat1->get_courses(array('sort' => array('fullname' => 1))); 540 $this->assertEquals(array($c1->id, $c4->id, $c3->id), array_keys($res)); 541 $this->assertEquals(3, $cat1->get_courses_count(array('sort' => array('fullname' => 1)))); 542 543 // Get courses sorted by fullname recursively. 544 $res = $cat1->get_courses(array('recursive' => 1, 'sort' => array('fullname' => 1))); 545 $this->assertEquals(array($c1->id, $c4->id, $c5->id, $c8->id, $c6->id, $c3->id), array_keys($res)); 546 $this->assertEquals(6, $cat1->get_courses_count(array('recursive' => 1, 'sort' => array('fullname' => 1)))); 547 548 // Get courses sorted by fullname recursively, use offset and limit. 549 $res = $cat1->get_courses(array('recursive' => 1, 'offset' => 1, 'limit' => 2, 'sort' => array('fullname' => -1))); 550 $this->assertEquals(array($c6->id, $c8->id), array_keys($res)); 551 // Offset and limit do not affect get_courses_count(). 552 $this->assertEquals(6, $cat1->get_courses_count(array('recursive' => 1, 'offset' => 1, 'limit' => 2, 'sort' => array('fullname' => 1)))); 553 554 // Calling get_courses_count without prior call to get_courses(). 555 $this->assertEquals(3, $cat2->get_courses_count(array('recursive' => 1, 'sort' => array('idnumber' => 1)))); 556 557 // Search courses. 558 559 // Search by text. 560 $res = core_course_category::search_courses(array('search' => 'Test')); 561 $this->assertEquals(array($c4->id, $c3->id, $c1->id, $c8->id, $c5->id), array_keys($res)); 562 $this->assertEquals(5, core_course_category::search_courses_count(array('search' => 'Test'))); 563 564 // Search by text with specified offset and limit. 565 $options = array('sort' => array('fullname' => 1), 'offset' => 1, 'limit' => 2); 566 $res = core_course_category::search_courses(array('search' => 'Test'), $options); 567 $this->assertEquals(array($c4->id, $c5->id), array_keys($res)); 568 $this->assertEquals(5, core_course_category::search_courses_count(array('search' => 'Test'), $options)); 569 570 // IMPORTANT: the tests below may fail on some databases 571 // case-insensitive search. 572 $res = core_course_category::search_courses(array('search' => 'test')); 573 $this->assertEquals(array($c4->id, $c3->id, $c1->id, $c8->id, $c5->id), array_keys($res)); 574 $this->assertEquals(5, core_course_category::search_courses_count(array('search' => 'test'))); 575 576 // Non-latin language search. 577 $res = core_course_category::search_courses(array('search' => 'Математика')); 578 $this->assertEquals(array($c3->id, $c6->id), array_keys($res)); 579 $this->assertEquals(2, core_course_category::search_courses_count(array('search' => 'Математика'), array())); 580 581 $this->setUser($this->getDataGenerator()->create_user()); 582 583 // Add necessary capabilities. 584 $this->assign_capability('moodle/course:create', CAP_ALLOW, \context_coursecat::instance($cat2->id)); 585 // Do another search with restricted capabilities. 586 $reqcaps = array('moodle/course:create'); 587 $res = core_course_category::search_courses(array('search' => 'test'), array(), $reqcaps); 588 $this->assertEquals(array($c8->id, $c5->id), array_keys($res)); 589 $this->assertEquals(2, core_course_category::search_courses_count(array('search' => 'test'), array(), $reqcaps)); 590 } 591 592 public function test_course_contacts() { 593 global $DB, $CFG; 594 595 set_config('coursecontactduplicates', false); 596 597 $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher')); 598 $managerrole = $DB->get_record('role', array('shortname'=>'manager')); 599 $studentrole = $DB->get_record('role', array('shortname'=>'student')); 600 $oldcoursecontact = $CFG->coursecontact; 601 602 $CFG->coursecontact = $managerrole->id. ','. $teacherrole->id; 603 604 /* 605 * User is listed in course contacts for the course if he has one of the 606 * "course contact" roles ($CFG->coursecontact) AND is enrolled in the course. 607 * If the user has several roles only the highest is displayed. 608 */ 609 610 // Test case: 611 // 612 // == Cat1 (user2 has teacher role) 613 // == Cat2 614 // -- course21 (user2 is enrolled as manager) | [Expected] Manager: F2 L2 615 // -- course22 (user2 is enrolled as student) | [Expected] Teacher: F2 L2 616 // == Cat4 (user2 has manager role) 617 // -- course41 (user4 is enrolled as teacher, user5 is enrolled as manager) | [Expected] Manager: F5 L5, Teacher: F4 L4 618 // -- course42 (user2 is enrolled as teacher) | [Expected] Manager: F2 L2 619 // == Cat3 (user3 has manager role) 620 // -- course31 (user3 is enrolled as student) | [Expected] Manager: F3 L3 621 // -- course32 | [Expected] 622 // -- course11 (user1 is enrolled as teacher) | [Expected] Teacher: F1 L1 623 // -- course12 (user1 has teacher role) | [Expected] 624 // also user4 is enrolled as teacher but enrolment is not active 625 $category = $course = $enrol = $user = array(); 626 $category[1] = core_course_category::create(array('name' => 'Cat1'))->id; 627 $category[2] = core_course_category::create(array('name' => 'Cat2', 'parent' => $category[1]))->id; 628 $category[3] = core_course_category::create(array('name' => 'Cat3', 'parent' => $category[1]))->id; 629 $category[4] = core_course_category::create(array('name' => 'Cat4', 'parent' => $category[2]))->id; 630 foreach (array(1, 2, 3, 4) as $catid) { 631 foreach (array(1, 2) as $courseid) { 632 $course[$catid][$courseid] = $this->getDataGenerator()->create_course(array('idnumber' => 'id'.$catid.$courseid, 633 'category' => $category[$catid]))->id; 634 $enrol[$catid][$courseid] = $DB->get_record('enrol', array('courseid'=>$course[$catid][$courseid], 'enrol'=>'manual'), '*', MUST_EXIST); 635 } 636 } 637 foreach (array(1, 2, 3, 4, 5) as $userid) { 638 $user[$userid] = $this->getDataGenerator()->create_user(array('firstname' => 'F'.$userid, 'lastname' => 'L'.$userid))->id; 639 } 640 641 $manual = enrol_get_plugin('manual'); 642 643 // Nobody is enrolled now and course contacts are empty. 644 $allcourses = core_course_category::get(0)->get_courses( 645 array('recursive' => true, 'coursecontacts' => true, 'sort' => array('idnumber' => 1))); 646 foreach ($allcourses as $onecourse) { 647 $this->assertEmpty($onecourse->get_course_contacts()); 648 } 649 650 // Cat1 (user2 has teacher role) 651 role_assign($teacherrole->id, $user[2], \context_coursecat::instance($category[1])); 652 // course21 (user2 is enrolled as manager) 653 $manual->enrol_user($enrol[2][1], $user[2], $managerrole->id); 654 // course22 (user2 is enrolled as student) 655 $manual->enrol_user($enrol[2][2], $user[2], $studentrole->id); 656 // Cat4 (user2 has manager role) 657 role_assign($managerrole->id, $user[2], \context_coursecat::instance($category[4])); 658 // course41 (user4 is enrolled as teacher, user5 is enrolled as manager) 659 $manual->enrol_user($enrol[4][1], $user[4], $teacherrole->id); 660 $manual->enrol_user($enrol[4][1], $user[5], $managerrole->id); 661 // course42 (user2 is enrolled as teacher) 662 $manual->enrol_user($enrol[4][2], $user[2], $teacherrole->id); 663 // Cat3 (user3 has manager role) 664 role_assign($managerrole->id, $user[3], \context_coursecat::instance($category[3])); 665 // course31 (user3 is enrolled as student) 666 $manual->enrol_user($enrol[3][1], $user[3], $studentrole->id); 667 // course11 (user1 is enrolled as teacher) 668 $manual->enrol_user($enrol[1][1], $user[1], $teacherrole->id); 669 // -- course12 (user1 has teacher role) 670 // also user4 is enrolled as teacher but enrolment is not active 671 role_assign($teacherrole->id, $user[1], \context_course::instance($course[1][2])); 672 $manual->enrol_user($enrol[1][2], $user[4], $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED); 673 674 $allcourses = core_course_category::get(0)->get_courses( 675 array('recursive' => true, 'coursecontacts' => true, 'sort' => array('idnumber' => 1))); 676 // Simplify the list of contacts for each course (similar as renderer would do). 677 $contacts = array(); 678 foreach (array(1, 2, 3, 4) as $catid) { 679 foreach (array(1, 2) as $courseid) { 680 $tmp = array(); 681 foreach ($allcourses[$course[$catid][$courseid]]->get_course_contacts() as $contact) { 682 $tmp[] = $contact['rolename']. ': '. $contact['username']; 683 } 684 $contacts[$catid][$courseid] = join(', ', $tmp); 685 } 686 } 687 688 // Assert: 689 // -- course21 (user2 is enrolled as manager) | Manager: F2 L2 690 $this->assertSame('Manager: F2 L2', $contacts[2][1]); 691 // -- course22 (user2 is enrolled as student) | Teacher: F2 L2 692 $this->assertSame('Teacher: F2 L2', $contacts[2][2]); 693 // -- course41 (user4 is enrolled as teacher, user5 is enrolled as manager) | Manager: F5 L5, Teacher: F4 L4 694 $this->assertSame('Manager: F5 L5, Teacher: F4 L4', $contacts[4][1]); 695 // -- course42 (user2 is enrolled as teacher) | [Expected] Manager: F2 L2 696 $this->assertSame('Manager: F2 L2', $contacts[4][2]); 697 // -- course31 (user3 is enrolled as student) | Manager: F3 L3 698 $this->assertSame('Manager: F3 L3', $contacts[3][1]); 699 // -- course32 | 700 $this->assertSame('', $contacts[3][2]); 701 // -- course11 (user1 is enrolled as teacher) | Teacher: F1 L1 702 $this->assertSame('Teacher: F1 L1', $contacts[1][1]); 703 // -- course12 (user1 has teacher role) | 704 $this->assertSame('', $contacts[1][2]); 705 706 // Suspend user 4 and make sure he is no longer in contacts of course 1 in category 4. 707 $manual->enrol_user($enrol[4][1], $user[4], $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED); 708 $allcourses = core_course_category::get(0)->get_courses(array( 709 'recursive' => true, 710 'coursecontacts' => true, 711 'sort' => array('idnumber' => 1)) 712 ); 713 $contacts = $allcourses[$course[4][1]]->get_course_contacts(); 714 $this->assertCount(1, $contacts); 715 $contact = reset($contacts); 716 $this->assertEquals('F5 L5', $contact['username']); 717 718 $CFG->coursecontact = $oldcoursecontact; 719 } 720 721 public function test_course_contacts_with_duplicates() { 722 global $DB, $CFG; 723 724 set_config('coursecontactduplicates', true); 725 726 $displayall = get_config('core', 'coursecontactduplicates'); 727 $this->assertEquals(true, $displayall); 728 729 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); 730 $managerrole = $DB->get_record('role', array('shortname' => 'manager')); 731 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 732 $oldcoursecontact = $CFG->coursecontact; 733 734 $CFG->coursecontact = $managerrole->id. ','. $teacherrole->id; 735 736 /* 737 * User is listed in course contacts for the course if he has one of the 738 * "course contact" roles ($CFG->coursecontact) AND is enrolled in the course. 739 * If the user has several roles all roles are displayed, but each role only once per user. 740 */ 741 742 /* 743 * Test case: 744 * 745 * == Cat1 (user2 has teacher role) 746 * == Cat2 747 * -- course21 (user2 is enrolled as manager) | [Expected] Manager: F2 L2 748 * -- course22 (user2 is enrolled as student) | [Expected] Teacher: F2 L2 749 * == Cat4 (user2 has manager role) 750 * -- course41 (user4 is enrolled as teacher, user5 is enrolled as manager) 751 * | [Expected] Manager: F5 L5, Teacher: F4 L4 752 * -- course42 (user2 is enrolled as teacher) | [Expected] Manager: F2 L2 753 * == Cat3 (user3 has manager role) 754 * -- course31 (user3 is enrolled as student) | [Expected] Manager: F3 L3 755 * -- course32 | [Expected] 756 * -- course11 (user1 is enrolled as teacher) | [Expected] Teacher: F1 L1 757 * -- course12 (user1 has teacher role) | [Expected] 758 * also user4 is enrolled as teacher but enrolment is not active 759 */ 760 $category = $course = $enrol = $user = array(); 761 $category[1] = core_course_category::create(array('name' => 'Cat1'))->id; 762 $category[2] = core_course_category::create(array('name' => 'Cat2', 'parent' => $category[1]))->id; 763 $category[3] = core_course_category::create(array('name' => 'Cat3', 'parent' => $category[1]))->id; 764 $category[4] = core_course_category::create(array('name' => 'Cat4', 'parent' => $category[2]))->id; 765 foreach (array(1, 2, 3, 4) as $catid) { 766 foreach (array(1, 2) as $courseid) { 767 $course[$catid][$courseid] = $this->getDataGenerator()->create_course(array( 768 'idnumber' => 'id'.$catid.$courseid, 769 'category' => $category[$catid]) 770 )->id; 771 $enrol[$catid][$courseid] = $DB->get_record( 772 'enrol', 773 array('courseid' => $course[$catid][$courseid], 'enrol' => 'manual'), 774 '*', 775 MUST_EXIST 776 ); 777 } 778 } 779 foreach (array(1, 2, 3, 4, 5) as $userid) { 780 $user[$userid] = $this->getDataGenerator()->create_user(array( 781 'firstname' => 'F'.$userid, 782 'lastname' => 'L'.$userid) 783 )->id; 784 } 785 786 $manual = enrol_get_plugin('manual'); 787 788 // Nobody is enrolled now and course contacts are empty. 789 $allcourses = core_course_category::get(0)->get_courses(array( 790 'recursive' => true, 791 'coursecontacts' => true, 792 'sort' => array('idnumber' => 1)) 793 ); 794 foreach ($allcourses as $onecourse) { 795 $this->assertEmpty($onecourse->get_course_contacts()); 796 } 797 798 // Cat1: user2 has teacher role. 799 role_assign($teacherrole->id, $user[2], \context_coursecat::instance($category[1])); 800 // Course21: user2 is enrolled as manager. 801 $manual->enrol_user($enrol[2][1], $user[2], $managerrole->id); 802 // Course22: user2 is enrolled as student. 803 $manual->enrol_user($enrol[2][2], $user[2], $studentrole->id); 804 // Cat4: user2 has manager role. 805 role_assign($managerrole->id, $user[2], \context_coursecat::instance($category[4])); 806 // Course41: user4 is enrolled as teacher, user5 is enrolled as manager. 807 $manual->enrol_user($enrol[4][1], $user[4], $teacherrole->id); 808 $manual->enrol_user($enrol[4][1], $user[5], $managerrole->id); 809 // Course42: user2 is enrolled as teacher. 810 $manual->enrol_user($enrol[4][2], $user[2], $teacherrole->id); 811 // Cat3: user3 has manager role. 812 role_assign($managerrole->id, $user[3], \context_coursecat::instance($category[3])); 813 // Course31: user3 is enrolled as student. 814 $manual->enrol_user($enrol[3][1], $user[3], $studentrole->id); 815 // Course11: user1 is enrolled as teacher and user4 is enrolled as teacher and has manager role. 816 $manual->enrol_user($enrol[1][1], $user[1], $teacherrole->id); 817 $manual->enrol_user($enrol[1][1], $user[4], $teacherrole->id); 818 role_assign($managerrole->id, $user[4], \context_course::instance($course[1][1])); 819 // Course12: user1 has teacher role, but is not enrolled, as well as user4 is enrolled as teacher, but user4's enrolment is 820 // not active. 821 role_assign($teacherrole->id, $user[1], \context_course::instance($course[1][2])); 822 $manual->enrol_user($enrol[1][2], $user[4], $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED); 823 824 $allcourses = core_course_category::get(0)->get_courses( 825 array('recursive' => true, 'coursecontacts' => true, 'sort' => array('idnumber' => 1))); 826 // Simplify the list of contacts for each course (similar as renderer would do). 827 $contacts = array(); 828 foreach (array(1, 2, 3, 4) as $catid) { 829 foreach (array(1, 2) as $courseid) { 830 $tmp = array(); 831 foreach ($allcourses[$course[$catid][$courseid]]->get_course_contacts() as $contact) { 832 $rolenames = array_map(function ($role) { 833 return $role->displayname; 834 }, $contact['roles']); 835 $tmp[] = implode(", ", $rolenames). ': '. 836 $contact['username']; 837 } 838 $contacts[$catid][$courseid] = join(', ', $tmp); 839 } 840 } 841 842 // Assert: 843 // Course21: user2 is enrolled as manager. [Expected] Manager: F2 L2, Teacher: F2 L2. 844 $this->assertSame('Manager, Teacher: F2 L2', $contacts[2][1]); 845 // Course22: user2 is enrolled as student. [Expected] Teacher: F2 L2. 846 $this->assertSame('Teacher: F2 L2', $contacts[2][2]); 847 // Course41: user4 is enrolled as teacher, user5 is enrolled as manager. [Expected] Manager: F5 L5, Teacher: F4 L4. 848 $this->assertSame('Manager: F5 L5, Teacher: F4 L4', $contacts[4][1]); 849 // Course42: user2 is enrolled as teacher. [Expected] Manager: F2 L2, Teacher: F2 L2. 850 $this->assertSame('Manager, Teacher: F2 L2', $contacts[4][2]); 851 // Course31: user3 is enrolled as student. [Expected] Manager: F3 L3. 852 $this->assertSame('Manager: F3 L3', $contacts[3][1]); 853 // Course32: nobody is enrolled. [Expected] (nothing). 854 $this->assertSame('', $contacts[3][2]); 855 // Course11: user1 is enrolled as teacher and user4 is enrolled as teacher and has manager role. [Expected] Manager: F4 L4, 856 // Teacher: F1 L1, Teacher: F4 L4. 857 $this->assertSame('Manager, Teacher: F4 L4, Teacher: F1 L1', $contacts[1][1]); 858 // Course12: user1 has teacher role, but is not enrolled, as well as user4 is enrolled as teacher, but user4's enrolment is 859 // not active. [Expected] (nothing). 860 $this->assertSame('', $contacts[1][2]); 861 862 // Suspend user 4 and make sure he is no longer in contacts of course 1 in category 4. 863 $manual->enrol_user($enrol[4][1], $user[4], $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED); 864 $allcourses = core_course_category::get(0)->get_courses(array( 865 'recursive' => true, 866 'coursecontacts' => true, 867 'sort' => array('idnumber' => 1) 868 )); 869 $contacts = $allcourses[$course[4][1]]->get_course_contacts(); 870 $this->assertCount(1, $contacts); 871 $contact = reset($contacts); 872 $this->assertEquals('F5 L5', $contact['username']); 873 874 $CFG->coursecontact = $oldcoursecontact; 875 } 876 877 public function test_overview_files() { 878 global $CFG; 879 $this->setAdminUser(); 880 $cat1 = core_course_category::create(array('name' => 'Cat1')); 881 882 // Create course c1 with one image file. 883 $dratid1 = $this->fill_draft_area(array('filename.jpg' => 'Test file contents1')); 884 $c1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 885 'fullname' => 'Test 1', 'overviewfiles_filemanager' => $dratid1)); 886 // Create course c2 with two image files (only one file will be added because of settings). 887 $dratid2 = $this->fill_draft_area(array('filename21.jpg' => 'Test file contents21', 'filename22.jpg' => 'Test file contents22')); 888 $c2 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 889 'fullname' => 'Test 2', 'overviewfiles_filemanager' => $dratid2)); 890 // Create course c3 without files. 891 $c3 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 3')); 892 893 // Change the settings to allow multiple files of any types. 894 $CFG->courseoverviewfileslimit = 3; 895 $CFG->courseoverviewfilesext = '*'; 896 // Create course c5 with two image files. 897 $dratid4 = $this->fill_draft_area(array('filename41.jpg' => 'Test file contents41', 'filename42.jpg' => 'Test file contents42')); 898 $c4 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 899 'fullname' => 'Test 4', 'overviewfiles_filemanager' => $dratid4)); 900 // Create course c6 with non-image file. 901 $dratid5 = $this->fill_draft_area(array('filename51.zip' => 'Test file contents51')); 902 $c5 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 903 'fullname' => 'Test 5', 'overviewfiles_filemanager' => $dratid5)); 904 905 // Reset default settings. 906 $CFG->courseoverviewfileslimit = 1; 907 $CFG->courseoverviewfilesext = 'web_image'; 908 909 $courses = $cat1->get_courses(); 910 $this->assertTrue($courses[$c1->id]->has_course_overviewfiles()); 911 $this->assertTrue($courses[$c2->id]->has_course_overviewfiles()); 912 $this->assertFalse($courses[$c3->id]->has_course_overviewfiles()); 913 $this->assertTrue($courses[$c4->id]->has_course_overviewfiles()); 914 $this->assertTrue($courses[$c5->id]->has_course_overviewfiles()); // Does not validate the filetypes. 915 916 $this->assertEquals(1, count($courses[$c1->id]->get_course_overviewfiles())); 917 $this->assertEquals(1, count($courses[$c2->id]->get_course_overviewfiles())); 918 $this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles())); 919 $this->assertEquals(1, count($courses[$c4->id]->get_course_overviewfiles())); 920 $this->assertEquals(0, count($courses[$c5->id]->get_course_overviewfiles())); // Validate the filetypes. 921 922 // Overview files are not allowed, all functions return empty values. 923 $CFG->courseoverviewfileslimit = 0; 924 925 $this->assertFalse($courses[$c1->id]->has_course_overviewfiles()); 926 $this->assertFalse($courses[$c2->id]->has_course_overviewfiles()); 927 $this->assertFalse($courses[$c3->id]->has_course_overviewfiles()); 928 $this->assertFalse($courses[$c4->id]->has_course_overviewfiles()); 929 $this->assertFalse($courses[$c5->id]->has_course_overviewfiles()); 930 931 $this->assertEquals(0, count($courses[$c1->id]->get_course_overviewfiles())); 932 $this->assertEquals(0, count($courses[$c2->id]->get_course_overviewfiles())); 933 $this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles())); 934 $this->assertEquals(0, count($courses[$c4->id]->get_course_overviewfiles())); 935 $this->assertEquals(0, count($courses[$c5->id]->get_course_overviewfiles())); 936 937 // Multiple overview files are allowed but still limited to images. 938 $CFG->courseoverviewfileslimit = 3; 939 940 $this->assertTrue($courses[$c1->id]->has_course_overviewfiles()); 941 $this->assertTrue($courses[$c2->id]->has_course_overviewfiles()); 942 $this->assertFalse($courses[$c3->id]->has_course_overviewfiles()); 943 $this->assertTrue($courses[$c4->id]->has_course_overviewfiles()); 944 $this->assertTrue($courses[$c5->id]->has_course_overviewfiles()); // Still does not validate the filetypes. 945 946 $this->assertEquals(1, count($courses[$c1->id]->get_course_overviewfiles())); 947 $this->assertEquals(1, count($courses[$c2->id]->get_course_overviewfiles())); // Only 1 file was actually added. 948 $this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles())); 949 $this->assertEquals(2, count($courses[$c4->id]->get_course_overviewfiles())); 950 $this->assertEquals(0, count($courses[$c5->id]->get_course_overviewfiles())); 951 952 // Multiple overview files of any type are allowed. 953 $CFG->courseoverviewfilesext = '*'; 954 955 $this->assertTrue($courses[$c1->id]->has_course_overviewfiles()); 956 $this->assertTrue($courses[$c2->id]->has_course_overviewfiles()); 957 $this->assertFalse($courses[$c3->id]->has_course_overviewfiles()); 958 $this->assertTrue($courses[$c4->id]->has_course_overviewfiles()); 959 $this->assertTrue($courses[$c5->id]->has_course_overviewfiles()); 960 961 $this->assertEquals(1, count($courses[$c1->id]->get_course_overviewfiles())); 962 $this->assertEquals(1, count($courses[$c2->id]->get_course_overviewfiles())); 963 $this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles())); 964 $this->assertEquals(2, count($courses[$c4->id]->get_course_overviewfiles())); 965 $this->assertEquals(1, count($courses[$c5->id]->get_course_overviewfiles())); 966 } 967 968 public function test_get_nested_name() { 969 $cat1name = 'Cat1'; 970 $cat2name = 'Cat2'; 971 $cat3name = 'Cat3'; 972 $cat4name = 'Cat4'; 973 $category1 = core_course_category::create(array('name' => $cat1name)); 974 $category2 = core_course_category::create(array('name' => $cat2name, 'parent' => $category1->id)); 975 $category3 = core_course_category::create(array('name' => $cat3name, 'parent' => $category2->id)); 976 $category4 = core_course_category::create(array('name' => $cat4name, 'parent' => $category2->id)); 977 978 $this->assertEquals($cat1name, $category1->get_nested_name(false)); 979 $this->assertEquals("{$cat1name} / {$cat2name}", $category2->get_nested_name(false)); 980 $this->assertEquals("{$cat1name} / {$cat2name} / {$cat3name}", $category3->get_nested_name(false)); 981 $this->assertEquals("{$cat1name} / {$cat2name} / {$cat4name}", $category4->get_nested_name(false)); 982 } 983 984 public function test_coursecat_is_uservisible() { 985 global $USER; 986 987 // Create category 1 as visible. 988 $category1 = core_course_category::create(array('name' => 'Cat1', 'visible' => 1)); 989 // Create category 2 as hidden. 990 $category2 = core_course_category::create(array('name' => 'Cat2', 'visible' => 0)); 991 992 $this->assertTrue($category1->is_uservisible()); 993 $this->assertFalse($category2->is_uservisible()); 994 995 $this->assign_capability('moodle/category:viewhiddencategories'); 996 997 $this->assertTrue($category1->is_uservisible()); 998 $this->assertTrue($category2->is_uservisible()); 999 1000 // First, store current user's id, then login as another user. 1001 $userid = $USER->id; 1002 $this->setUser($this->getDataGenerator()->create_user()); 1003 1004 // User $user should still have the moodle/category:viewhiddencategories capability. 1005 $this->assertTrue($category1->is_uservisible($userid)); 1006 $this->assertTrue($category2->is_uservisible($userid)); 1007 1008 $this->assign_capability('moodle/category:viewhiddencategories', CAP_INHERIT); 1009 1010 $this->assertTrue($category1->is_uservisible()); 1011 $this->assertFalse($category2->is_uservisible()); 1012 } 1013 1014 public function test_current_user_coursecat_get() { 1015 $this->assign_capability('moodle/category:viewhiddencategories'); 1016 1017 // Create category 1 as visible. 1018 $category1 = core_course_category::create(array('name' => 'Cat1', 'visible' => 1)); 1019 // Create category 2 as hidden. 1020 $category2 = core_course_category::create(array('name' => 'Cat2', 'visible' => 0)); 1021 1022 $this->assertEquals($category1->id, core_course_category::get($category1->id)->id); 1023 $this->assertEquals($category2->id, core_course_category::get($category2->id)->id); 1024 1025 // Login as another user to test core_course_category::get. 1026 $this->setUser($this->getDataGenerator()->create_user()); 1027 $this->assertEquals($category1->id, core_course_category::get($category1->id)->id); 1028 1029 // Expecting to get an exception as this new user does not have the moodle/category:viewhiddencategories capability. 1030 $this->expectException('moodle_exception'); 1031 $this->expectExceptionMessage(get_string('cannotviewcategory', 'error')); 1032 core_course_category::get($category2->id); 1033 } 1034 1035 public function test_another_user_coursecat_get() { 1036 global $USER; 1037 1038 $this->assign_capability('moodle/category:viewhiddencategories'); 1039 1040 // Create category 1 as visible. 1041 $category1 = core_course_category::create(array('name' => 'Cat1', 'visible' => 1)); 1042 // Create category 2 as hidden. 1043 $category2 = core_course_category::create(array('name' => 'Cat2', 'visible' => 0)); 1044 1045 // First, store current user's object, then login as another user. 1046 $user1 = $USER; 1047 $user2 = $this->getDataGenerator()->create_user(); 1048 $this->setUser($user2); 1049 1050 $this->assertEquals($category1->id, core_course_category::get($category1->id, MUST_EXIST, false, $user1)->id); 1051 $this->assertEquals($category2->id, core_course_category::get($category2->id, MUST_EXIST, false, $user1)->id); 1052 1053 $this->setUser($user1); 1054 1055 $this->assertEquals($category1->id, core_course_category::get($category1->id, MUST_EXIST, false, $user2)->id); 1056 $this->expectException('moodle_exception'); 1057 $this->expectExceptionMessage(get_string('cannotviewcategory', 'error')); 1058 core_course_category::get($category2->id, MUST_EXIST, false, $user2); 1059 } 1060 1061 /** 1062 * Creates a draft area for current user and fills it with fake files 1063 * 1064 * @param array $files array of files that need to be added to filearea, filename => filecontents 1065 * @return int draftid for the filearea 1066 */ 1067 protected function fill_draft_area(array $files) { 1068 global $USER; 1069 $usercontext = \context_user::instance($USER->id); 1070 $draftid = file_get_unused_draft_itemid(); 1071 foreach ($files as $filename => $filecontents) { 1072 // Add actual file there. 1073 $filerecord = array('component' => 'user', 'filearea' => 'draft', 1074 'contextid' => $usercontext->id, 'itemid' => $draftid, 1075 'filename' => $filename, 'filepath' => '/'); 1076 $fs = get_file_storage(); 1077 $fs->create_file_from_string($filerecord, $filecontents); 1078 } 1079 return $draftid; 1080 } 1081 1082 /** 1083 * This test ensures that is the list of courses in a category can be retrieved while a course is being deleted. 1084 */ 1085 public function test_get_courses_during_delete() { 1086 global $DB; 1087 $category = self::getDataGenerator()->create_category(); 1088 $course = self::getDataGenerator()->create_course(['category' => $category->id]); 1089 $othercourse = self::getDataGenerator()->create_course(['category' => $category->id]); 1090 $coursecategory = core_course_category::get($category->id); 1091 // Get a list of courses before deletion to populate the cache. 1092 $originalcourses = $coursecategory->get_courses(); 1093 $this->assertCount(2, $originalcourses); 1094 $this->assertArrayHasKey($course->id, $originalcourses); 1095 $this->assertArrayHasKey($othercourse->id, $originalcourses); 1096 // Simulate the course deletion process being part way though. 1097 $DB->delete_records('course', ['id' => $course->id]); 1098 // Get the list of courses while a deletion is in progress. 1099 $courses = $coursecategory->get_courses(); 1100 $this->assertCount(1, $courses); 1101 $this->assertArrayHasKey($othercourse->id, $courses); 1102 } 1103 1104 /** 1105 * Test get_nearest_editable_subcategory() method. 1106 * 1107 * @covers \core_course_category::get_nearest_editable_subcategory 1108 */ 1109 public function test_get_nearest_editable_subcategory(): void { 1110 global $DB; 1111 1112 $coursecreatorrole = $DB->get_record('role', ['shortname' => 'coursecreator']); 1113 $managerrole = $DB->get_record('role', ['shortname' => 'manager']); 1114 1115 // Create categories. 1116 $category1 = core_course_category::create(['name' => 'Cat1']); 1117 $category2 = core_course_category::create(['name' => 'Cat2']); 1118 $category3 = core_course_category::create(['name' => 'Cat3']); 1119 // Get the category contexts. 1120 $category1context = $category1->get_context(); 1121 $category2context = $category2->get_context(); 1122 $category3context = $category3->get_context(); 1123 // Create user. 1124 $user1 = $this->getDataGenerator()->create_user(); 1125 $user2 = $this->getDataGenerator()->create_user(); 1126 $user3 = $this->getDataGenerator()->create_user(); 1127 // Assign the user1 to 'Course creator' role for Cat1. 1128 role_assign($coursecreatorrole->id, $user1->id, $category1context->id); 1129 // Assign the user2 to 'Manager' role for Cat3. 1130 role_assign($managerrole->id, $user2->id, $category3context->id); 1131 1132 // Start scenario 1. 1133 // user3 has no permission to create course or manage category. 1134 $this->setUser($user3); 1135 $coursecat = core_course_category::user_top(); 1136 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create'])); 1137 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create'])); 1138 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['manage'])); 1139 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/category:manage'])); 1140 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create', 'manage'])); 1141 // End scenario 1. 1142 1143 // Start scenario 2. 1144 // user1 has permission to create course but has no permission to manage category. 1145 $this->setUser($user1); 1146 $coursecat = core_course_category::user_top(); 1147 $this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create'])); 1148 $this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create'])); 1149 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['manage'])); 1150 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/category:manage'])); 1151 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create', 'manage'])); 1152 // The get_nearest_editable_subcategory should return Cat1. 1153 $this->assertEquals($category1->id, core_course_category::get_nearest_editable_subcategory($coursecat, ['create'])->id); 1154 $this->assertEquals($category1->id, 1155 core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create'])->id); 1156 // Assign the user1 to 'Course creator' role for Cat2. 1157 role_assign($coursecreatorrole->id, $user1->id, $category2context->id); 1158 // The get_nearest_editable_subcategory should still return Cat1 (First creatable subcategory) for create course capability. 1159 $this->assertEquals($category1->id, core_course_category::get_nearest_editable_subcategory($coursecat, ['create'])->id); 1160 $this->assertEquals($category1->id, 1161 core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create'])->id); 1162 // End scenario 2. 1163 1164 // Start scenario 3. 1165 // user2 has no permission to create course but has permission to manage category. 1166 $this->setUser($user2); 1167 // Remove the moodle/course:create capability for the manager role. 1168 unassign_capability('moodle/course:create', $managerrole->id); 1169 $coursecat = core_course_category::user_top(); 1170 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create'])); 1171 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create'])); 1172 $this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['manage'])); 1173 $this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/category:manage'])); 1174 $this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create', 'manage'])); 1175 // The get_nearest_editable_subcategory should return Cat3. 1176 $this->assertEquals($category3->id, core_course_category::get_nearest_editable_subcategory($coursecat, ['manage'])->id); 1177 $this->assertEquals($category3->id, 1178 core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/category:manage'])->id); 1179 // End scenario 3. 1180 1181 // Start scenario 4. 1182 // user2 has both permission to create course and manage category. 1183 // Add the moodle/course:create capability back again for the manager role. 1184 assign_capability('moodle/course:create', CAP_ALLOW, $managerrole->id, $category3context->id); 1185 $this->setUser($user2); 1186 $coursecat = core_course_category::user_top(); 1187 $this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create'])); 1188 $this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create'])); 1189 $this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['manage'])); 1190 $this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/category:manage'])); 1191 $this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create', 'manage'])); 1192 // The get_nearest_editable_subcategory should return Cat3. 1193 $this->assertEquals($category3->id, 1194 core_course_category::get_nearest_editable_subcategory($coursecat, ['create', 'manage'])->id); 1195 $this->assertEquals($category3->id, core_course_category::get_nearest_editable_subcategory($coursecat, 1196 ['moodle/course:create', 'moodle/category:manage'])->id); 1197 // End scenario 4. 1198 1199 // Start scenario 5. 1200 // Exception will be thrown if $permissionstocheck is empty. 1201 $this->setUser($user1); 1202 $coursecat = core_course_category::user_top(); 1203 $this->expectException('coding_exception'); 1204 $this->expectExceptionMessage('Invalid permissionstocheck parameter'); 1205 $this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, [])); 1206 // End scenario 5. 1207 } 1208 1209 /** 1210 * Test get_nearest_editable_subcategory() method with hidden categories. 1211 * 1212 * @param int $visible Whether the category is visible or not. 1213 * @param bool $child Whether the category is child of main category or not. 1214 * @param string $role The role the user must have. 1215 * @param array $permissions An array of permissions we must check. 1216 * @param bool $result Whether the result should be the category or null. 1217 * 1218 * @dataProvider get_nearest_editable_subcategory_provider 1219 * @covers \core_course_category::get_nearest_editable_subcategory 1220 */ 1221 public function test_get_nearest_editable_subcategory_with_hidden_categories( 1222 int $visible = 0, 1223 bool $child = false, 1224 string $role = 'manager', 1225 array $permissions = [], 1226 bool $result = false 1227 ): void { 1228 global $DB; 1229 1230 $userrole = $DB->get_record('role', ['shortname' => $role]); 1231 $maincat = core_course_category::create(['name' => 'Main cat']); 1232 1233 $catparams = new \stdClass(); 1234 $catparams->name = 'Test category'; 1235 $catparams->visible = $visible; 1236 if ($child) { 1237 $catparams->parent = $maincat->id; 1238 } 1239 $category = core_course_category::create($catparams); 1240 $catcontext = $category->get_context(); 1241 $user = $this->getDataGenerator()->create_user(); 1242 role_assign($userrole->id, $user->id, $catcontext->id); 1243 $this->setUser($user); 1244 1245 $nearestcat = core_course_category::get_nearest_editable_subcategory(core_course_category::user_top(), $permissions); 1246 1247 if ($result) { 1248 $this->assertEquals($category->id, $nearestcat->id); 1249 } else { 1250 $this->assertEmpty($nearestcat); 1251 } 1252 } 1253 1254 /** 1255 * Data provider for test_get_nearest_editable_subcategory_with_hidden_categories(). 1256 * 1257 * @return array 1258 */ 1259 public function get_nearest_editable_subcategory_provider(): array { 1260 return [ 1261 'Hidden main category for manager. Checking create and manage' => [ 1262 0, 1263 false, 1264 'manager', 1265 ['create', 'manage'], 1266 true, 1267 ], 1268 'Hidden main category for course creator. Checking create and manage' => [ 1269 0, 1270 false, 1271 'coursecreator', 1272 ['create', 'manage'], 1273 false, 1274 ], 1275 'Hidden main category for student. Checking create and manage' => [ 1276 0, 1277 false, 1278 'student', 1279 ['create', 'manage'], 1280 false, 1281 ], 1282 'Hidden main category for manager. Checking create' => [ 1283 0, 1284 false, 1285 'manager', 1286 ['create'], 1287 true, 1288 ], 1289 'Hidden main category for course creator. Checking create' => [ 1290 0, 1291 false, 1292 'coursecreator', 1293 ['create'], 1294 true, 1295 ], 1296 'Hidden main category for student. Checking create' => [ 1297 0, 1298 false, 1299 'student', 1300 ['create'], 1301 false, 1302 ], 1303 'Hidden subcategory for manager. Checking create and manage' => [ 1304 0, 1305 true, 1306 'manager', 1307 ['create', 'manage'], 1308 true, 1309 ], 1310 'Hidden subcategory for course creator. Checking create and manage' => [ 1311 0, 1312 true, 1313 'coursecreator', 1314 ['create', 'manage'], 1315 false, 1316 ], 1317 'Hidden subcategory for student. Checking create and manage' => [ 1318 0, 1319 true, 1320 'student', 1321 ['create', 'manage'], 1322 false, 1323 ], 1324 'Hidden subcategory for manager. Checking create' => [ 1325 0, 1326 true, 1327 'manager', 1328 ['create'], 1329 true, 1330 ], 1331 'Hidden subcategory for course creator. Checking create' => [ 1332 0, 1333 true, 1334 'coursecreator', 1335 ['create'], 1336 true, 1337 ], 1338 'Hidden subcategory for student. Checking create' => [ 1339 0, 1340 true, 1341 'student', 1342 ['create'], 1343 false, 1344 ], 1345 ]; 1346 } 1347 1348 /** 1349 * This test ensures that the filter context list is populated by the correct filter contexts from make_category_list. 1350 * 1351 * @coversNothing 1352 */ 1353 public function test_make_category_list_context() { 1354 global $DB; 1355 // Ensure that the category list is empty. 1356 $DB->delete_records('course_categories'); 1357 set_config('perfdebug', 15); 1358 1359 // Create a few categories to populate the context cache. 1360 $this->getDataGenerator()->create_category(['name' => 'cat1']); 1361 $this->getDataGenerator()->create_category(['name' => 'cat2']); 1362 $this->getDataGenerator()->create_category(['name' => 'cat3']); 1363 $filtermanager = \filter_manager::instance(); 1364 1365 // Configure a filter to apply to all content and headings. 1366 filter_set_global_state('multilang', TEXTFILTER_ON); 1367 filter_set_applies_to_strings('multilang', true); 1368 1369 $perf = $filtermanager->get_performance_summary(); 1370 $this->assertEquals(0, $perf[0]['contextswithfilters']); 1371 1372 // Now fill the cache with the category strings. 1373 \core_course_category::make_categories_list(); 1374 // 3 Categories + system context. 1375 $perf = $filtermanager->get_performance_summary(); 1376 $this->assertEquals(3, $perf[0]['contextswithfilters']); 1377 $filtermanager->reset_caches(); 1378 // We need to refresh the instance, resetting caches unloads the singleton. 1379 $filtermanager = \filter_manager::instance(); 1380 \cache_helper::purge_by_definition('core', 'coursecat'); 1381 1382 // Now flip the bit on the filter context. 1383 set_config('filternavigationwithsystemcontext', 1); 1384 1385 // Repeat the check. Only context should be system context. 1386 \core_course_category::make_categories_list(); 1387 $perf = $filtermanager->get_performance_summary(); 1388 $this->assertEquals(1, $perf[0]['contextswithfilters']); 1389 } 1390 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body