Rev 1 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?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 core_course;use core_course_category;/*** Tests for class core_course_category** @package core_course* @category test* @copyright 2013 Marina Glancy* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class category_test extends \advanced_testcase {protected $roles;protected function setUp(): void {parent::setUp();$this->resetAfterTest();$user = $this->getDataGenerator()->create_user();$this->setUser($user);}protected function get_roleid($context = null) {global $USER;if ($context === null) {$context = \context_system::instance();}if (is_object($context)) {$context = $context->id;}if (empty($this->roles)) {$this->roles = array();}if (empty($this->roles[$USER->id])) {$this->roles[$USER->id] = array();}if (empty($this->roles[$USER->id][$context])) {$this->roles[$USER->id][$context] = create_role('Role for '.$USER->id.' in '.$context, 'role'.$USER->id.'-'.$context, '-');role_assign($this->roles[$USER->id][$context], $USER->id, $context);}return $this->roles[$USER->id][$context];}protected function assign_capability($capability, $permission = CAP_ALLOW, $contextid = null) {if ($contextid === null) {$contextid = \context_system::instance();}if (is_object($contextid)) {$contextid = $contextid->id;}assign_capability($capability, $permission, $this->get_roleid($contextid), $contextid, true);accesslib_clear_all_caches_for_unit_testing();}public function test_create_coursecat(): void {// Create the category.$data = new \stdClass();$data->name = 'aaa';$data->description = 'aaa';$data->idnumber = '';$category1 = core_course_category::create($data);// Initially confirm that base data was inserted correctly.$this->assertSame($data->name, $category1->name);$this->assertSame($data->description, $category1->description);$this->assertSame($data->idnumber, $category1->idnumber);$this->assertGreaterThanOrEqual(1, $category1->sortorder);// Create two more categories and test the sortorder worked correctly.$data->name = 'ccc';$category2 = core_course_category::create($data);$data->name = 'bbb';$category3 = core_course_category::create($data);$this->assertGreaterThan($category1->sortorder, $category2->sortorder);$this->assertGreaterThan($category2->sortorder, $category3->sortorder);}public function test_name_idnumber_exceptions(): void {try {core_course_category::create(array('name' => ''));$this->fail('Missing category name exception expected in core_course_category::create');} catch (\moodle_exception $e) {$this->assertInstanceOf('moodle_exception', $e);}$cat1 = core_course_category::create(array('name' => 'Cat1', 'idnumber' => '1'));try {$cat1->update(array('name' => ''));$this->fail('Missing category name exception expected in core_course_category::update');} catch (\moodle_exception $e) {$this->assertInstanceOf('moodle_exception', $e);}try {core_course_category::create(array('name' => 'Cat2', 'idnumber' => '1'));$this->fail('Duplicate idnumber exception expected in core_course_category::create');} catch (\moodle_exception $e) {$this->assertInstanceOf('moodle_exception', $e);}$cat2 = core_course_category::create(array('name' => 'Cat2', 'idnumber' => '2'));try {$cat2->update(array('idnumber' => '1'));$this->fail('Duplicate idnumber exception expected in core_course_category::update');} catch (\moodle_exception $e) {$this->assertInstanceOf('moodle_exception', $e);}// Test that duplicates with an idnumber of 0 cannot be created.core_course_category::create(array('name' => 'Cat3', 'idnumber' => '0'));try {core_course_category::create(array('name' => 'Cat4', 'idnumber' => '0'));$this->fail('Duplicate idnumber "0" exception expected in core_course_category::create');} catch (\moodle_exception $e) {$this->assertInstanceOf('moodle_exception', $e);}// Test an update cannot make a duplicate idnumber of 0.try {$cat2->update(array('idnumber' => '0'));$this->fail('Duplicate idnumber "0" exception expected in core_course_category::update');} catch (\Exception $e) {$this->assertInstanceOf('moodle_exception', $e);}}public function test_visibility(): void {$this->assign_capability('moodle/category:viewhiddencategories');$this->assign_capability('moodle/category:manage');// Create category 1 initially hidden.$category1 = core_course_category::create(array('name' => 'Cat1', 'visible' => 0));$this->assertEquals(0, $category1->visible);$this->assertEquals(0, $category1->visibleold);// Create category 2 initially hidden as a child of hidden category 1.$category2 = core_course_category::create(array('name' => 'Cat2', 'visible' => 0, 'parent' => $category1->id));$this->assertEquals(0, $category2->visible);$this->assertEquals(0, $category2->visibleold);// Create category 3 initially visible as a child of hidden category 1.$category3 = core_course_category::create(array('name' => 'Cat3', 'visible' => 1, 'parent' => $category1->id));$this->assertEquals(0, $category3->visible);$this->assertEquals(1, $category3->visibleold);// Show category 1 and make sure that category 2 is hidden and category 3 is visible.$category1->show();$this->assertEquals(1, core_course_category::get($category1->id)->visible);$this->assertEquals(0, core_course_category::get($category2->id)->visible);$this->assertEquals(1, core_course_category::get($category3->id)->visible);// Create visible category 4.$category4 = core_course_category::create(array('name' => 'Cat4'));$this->assertEquals(1, $category4->visible);$this->assertEquals(1, $category4->visibleold);// Create visible category 5 as a child of visible category 4.$category5 = core_course_category::create(array('name' => 'Cat5', 'parent' => $category4->id));$this->assertEquals(1, $category5->visible);$this->assertEquals(1, $category5->visibleold);// Hide category 4 and make sure category 5 is hidden too.$category4->hide();$this->assertEquals(0, $category4->visible);$this->assertEquals(0, $category4->visibleold);$category5 = core_course_category::get($category5->id); // We have to re-read from DB.$this->assertEquals(0, $category5->visible);$this->assertEquals(1, $category5->visibleold);// Show category 4 and make sure category 5 is visible too.$category4->show();$this->assertEquals(1, $category4->visible);$this->assertEquals(1, $category4->visibleold);$category5 = core_course_category::get($category5->id); // We have to re-read from DB.$this->assertEquals(1, $category5->visible);$this->assertEquals(1, $category5->visibleold);// Move category 5 under hidden category 2 and make sure it became hidden.$category5->change_parent($category2->id);$this->assertEquals(0, $category5->visible);$this->assertEquals(1, $category5->visibleold);// Re-read object for category 5 from DB and check again.$category5 = core_course_category::get($category5->id);$this->assertEquals(0, $category5->visible);$this->assertEquals(1, $category5->visibleold);// Rricky one! Move hidden category 5 under visible category ("Top") and make sure it is still hidden-// WHY? Well, different people may expect different behaviour here. So better keep it hidden.$category5->change_parent(0);$this->assertEquals(0, $category5->visible);$this->assertEquals(1, $category5->visibleold);}public function test_hierarchy(): void {$this->assign_capability('moodle/category:viewhiddencategories');$this->assign_capability('moodle/category:manage');$category1 = core_course_category::create(array('name' => 'Cat1'));$category2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id));$category3 = core_course_category::create(array('name' => 'Cat3', 'parent' => $category1->id));$category4 = core_course_category::create(array('name' => 'Cat4', 'parent' => $category2->id));// Check function get_children().$this->assertEquals(array($category2->id, $category3->id), array_keys($category1->get_children()));// Check function get_parents().$this->assertEquals(array($category1->id, $category2->id), $category4->get_parents());// Can not move category to itself or to it's children.$this->assertFalse($category1->can_change_parent($category2->id));$this->assertFalse($category2->can_change_parent($category2->id));// Can move category to grandparent.$this->assertTrue($category4->can_change_parent($category1->id));try {$category2->change_parent($category4->id);$this->fail('Exception expected - can not move category');} catch (\moodle_exception $e) {$this->assertInstanceOf('moodle_exception', $e);}$category4->change_parent(0);$this->assertEquals(array(), $category4->get_parents());$this->assertEquals(array($category2->id, $category3->id), array_keys($category1->get_children()));$this->assertEquals(array(), array_keys($category2->get_children()));}public function test_update(): void {$category1 = core_course_category::create(array('name' => 'Cat1'));$timecreated = $category1->timemodified;$this->assertSame('Cat1', $category1->name);$this->assertTrue(empty($category1->description));$this->waitForSecond();$testdescription = 'This is cat 1 а также русский текст';$category1->update(array('description' => $testdescription));$this->assertSame($testdescription, $category1->description);$category1 = core_course_category::get($category1->id);$this->assertSame($testdescription, $category1->description);\cache_helper::purge_by_event('changesincoursecat');$category1 = core_course_category::get($category1->id);$this->assertSame($testdescription, $category1->description);$this->assertGreaterThan($timecreated, $category1->timemodified);}public function test_delete(): void {global $DB;$this->assign_capability('moodle/category:manage');$this->assign_capability('moodle/course:create');$initialcatid = $DB->get_field_sql('SELECT max(id) from {course_categories}');$category1 = core_course_category::create(array('name' => 'Cat1'));$category2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id));$category3 = core_course_category::create(array('name' => 'Cat3'));$category4 = core_course_category::create(array('name' => 'Cat4', 'parent' => $category2->id));$course1 = $this->getDataGenerator()->create_course(array('category' => $category2->id));$course2 = $this->getDataGenerator()->create_course(array('category' => $category4->id));$course3 = $this->getDataGenerator()->create_course(array('category' => $category4->id));$course4 = $this->getDataGenerator()->create_course(array('category' => $category1->id));// Now we have// $category1// $category2// $category4// $course2// $course3// $course1// $course4// $category3// structure.// Login as another user to test course:delete capability (user who created course can delete it within 24h even without cap).$this->setUser($this->getDataGenerator()->create_user());// Delete category 2 and move content to category 3.$this->assertFalse($category2->can_move_content_to($category3->id)); // No luck!// Add necessary capabilities.$this->assign_capability('moodle/course:create', CAP_ALLOW, \context_coursecat::instance($category3->id));$this->assign_capability('moodle/category:manage');$this->assertTrue($category2->can_move_content_to($category3->id)); // Hurray!$category2->delete_move($category3->id);// Make sure we have:// $category1// $course4// $category3// $category4// $course2// $course3// $course1// structure.$this->assertNull(core_course_category::get($category2->id, IGNORE_MISSING, true));$this->assertEquals(array(), $category1->get_children());$this->assertEquals(array($category4->id), array_keys($category3->get_children()));$this->assertEquals($category4->id, $DB->get_field('course', 'category', array('id' => $course2->id)));$this->assertEquals($category4->id, $DB->get_field('course', 'category', array('id' => $course3->id)));$this->assertEquals($category3->id, $DB->get_field('course', 'category', array('id' => $course1->id)));// Delete category 3 completely.$this->assertFalse($category3->can_delete_full()); // No luck!// Add necessary capabilities.$this->assign_capability('moodle/course:delete', CAP_ALLOW, \context_coursecat::instance($category3->id));$this->assertTrue($category3->can_delete_full()); // Hurray!$category3->delete_full();// Make sure we have:// $category1// $course4// structure.// Note that we also have default course category and default 'site' course.$this->assertEquals(1, $DB->get_field_sql('SELECT count(*) FROM {course_categories} WHERE id > ?', array($initialcatid)));$this->assertEquals($category1->id, $DB->get_field_sql('SELECT max(id) FROM {course_categories}'));$this->assertEquals(1, $DB->get_field_sql('SELECT count(*) FROM {course} WHERE id <> ?', array(SITEID)));$this->assertEquals(array('id' => $course4->id, 'category' => $category1->id),(array)$DB->get_record_sql('SELECT id, category from {course} where id <> ?', array(SITEID)));}public function test_get_children(): void {$category1 = core_course_category::create(array('name' => 'Cat1'));$category2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id));$category3 = core_course_category::create(array('name' => 'Cat3', 'parent' => $category1->id, 'visible' => 0));$category4 = core_course_category::create(array('name' => 'Cat4', 'idnumber' => '12', 'parent' => $category1->id));$category5 = core_course_category::create(array('name' => 'Cat5', 'idnumber' => '11','parent' => $category1->id, 'visible' => 0));$category6 = core_course_category::create(array('name' => 'Cat6', 'idnumber' => '10', 'parent' => $category1->id));$category7 = core_course_category::create(array('name' => 'Cat0', 'parent' => $category1->id));$children = $category1->get_children();// User does not have the capability to view hidden categories, so the list should be// 2, 4, 6, 7.$this->assertEquals(array($category2->id, $category4->id, $category6->id, $category7->id), array_keys($children));$this->assertEquals(4, $category1->get_children_count());$children = $category1->get_children(array('offset' => 2));$this->assertEquals(array($category6->id, $category7->id), array_keys($children));$this->assertEquals(4, $category1->get_children_count());$children = $category1->get_children(array('limit' => 2));$this->assertEquals(array($category2->id, $category4->id), array_keys($children));$children = $category1->get_children(array('offset' => 1, 'limit' => 2));$this->assertEquals(array($category4->id, $category6->id), array_keys($children));$children = $category1->get_children(array('sort' => array('name' => 1)));// Must be 7, 2, 4, 6.$this->assertEquals(array($category7->id, $category2->id, $category4->id, $category6->id), array_keys($children));$children = $category1->get_children(array('sort' => array('idnumber' => 1, 'name' => -1)));// Must be 2, 7, 6, 4.$this->assertEquals(array($category2->id, $category7->id, $category6->id, $category4->id), array_keys($children));// Check that everything is all right after purging the caches.\cache_helper::purge_by_event('changesincoursecat');$children = $category1->get_children();$this->assertEquals(array($category2->id, $category4->id, $category6->id, $category7->id), array_keys($children));$this->assertEquals(4, $category1->get_children_count());}/*** Test the get_all_children_ids function.*/public function test_get_all_children_ids(): void {$category1 = core_course_category::create(array('name' => 'Cat1'));$category2 = core_course_category::create(array('name' => 'Cat2'));$category11 = core_course_category::create(array('name' => 'Cat11', 'parent' => $category1->id));$category12 = core_course_category::create(array('name' => 'Cat12', 'parent' => $category1->id));$category13 = core_course_category::create(array('name' => 'Cat13', 'parent' => $category1->id));$category111 = core_course_category::create(array('name' => 'Cat111', 'parent' => $category11->id));$category112 = core_course_category::create(array('name' => 'Cat112', 'parent' => $category11->id));$category1121 = core_course_category::create(array('name' => 'Cat1121', 'parent' => $category112->id));$this->assertCount(0, $category2->get_all_children_ids());$this->assertCount(6, $category1->get_all_children_ids());$cmpchildrencat1 = array($category11->id, $category12->id, $category13->id, $category111->id, $category112->id,$category1121->id);$childrencat1 = $category1->get_all_children_ids();// Order of values does not matter. Compare sorted arrays.sort($cmpchildrencat1);sort($childrencat1);$this->assertEquals($cmpchildrencat1, $childrencat1);$this->assertCount(3, $category11->get_all_children_ids());$this->assertCount(0, $category111->get_all_children_ids());$this->assertCount(1, $category112->get_all_children_ids());$this->assertEquals(array($category1121->id), $category112->get_all_children_ids());}/*** Test the countall function*/public function test_count_all(): void {global $DB;// Dont assume there is just one. An add-on might create a category as part of the install.$numcategories = $DB->count_records('course_categories');$this->assertEquals($numcategories, core_course_category::count_all());$this->assertDebuggingCalled('Method core_course_category::count_all() is deprecated. Please use ' .'core_course_category::is_simple_site()', DEBUG_DEVELOPER);$category1 = core_course_category::create(array('name' => 'Cat1'));$category2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $category1->id));$category3 = core_course_category::create(array('name' => 'Cat3', 'parent' => $category2->id, 'visible' => 0));// Now we've got three more.$this->assertEquals($numcategories + 3, core_course_category::count_all());$this->assertDebuggingCalled('Method core_course_category::count_all() is deprecated. Please use ' .'core_course_category::is_simple_site()', DEBUG_DEVELOPER);\cache_helper::purge_by_event('changesincoursecat');// We should still have 4.$this->assertEquals($numcategories + 3, core_course_category::count_all());$this->assertDebuggingCalled('Method core_course_category::count_all() is deprecated. Please use ' .'core_course_category::is_simple_site()', DEBUG_DEVELOPER);}/*** Test the is_simple_site function*/public function test_is_simple_site(): void {// By default site has one category and is considered simple.$this->assertEquals(true, core_course_category::is_simple_site());$default = core_course_category::get_default();// When there is only one category but it is hidden, it is not a simple site.$default->update(['visible' => 0]);$this->assertEquals(false, core_course_category::is_simple_site());$default->update(['visible' => 1]);$this->assertEquals(true, core_course_category::is_simple_site());// As soon as there is more than one category, site is not simple any more.core_course_category::create(array('name' => 'Cat1'));$this->assertEquals(false, core_course_category::is_simple_site());}/*** Test a categories ability to resort courses.*/public function test_resort_courses(): void {$this->resetAfterTest(true);$generator = $this->getDataGenerator();$category = $generator->create_category();$course1 = $generator->create_course(array('category' => $category->id,'idnumber' => '006-01','shortname' => 'Biome Study','fullname' => '<span lang="ar" class="multilang">'.'دراسة منطقة إحيائية'.'</span><span lang="en" class="multilang">Biome Study</span>','timecreated' => '1000000001'));$course2 = $generator->create_course(array('category' => $category->id,'idnumber' => '007-02','shortname' => 'Chemistry Revision','fullname' => 'Chemistry Revision','timecreated' => '1000000002'));$course3 = $generator->create_course(array('category' => $category->id,'idnumber' => '007-03','shortname' => 'Swiss Rolls and Sunflowers','fullname' => 'Aarkvarks guide to Swiss Rolls and Sunflowers','timecreated' => '1000000003'));$course4 = $generator->create_course(array('category' => $category->id,'idnumber' => '006-04','shortname' => 'Scratch','fullname' => '<a href="test.php">Basic Scratch</a>','timecreated' => '1000000004'));$c1 = (int)$course1->id;$c2 = (int)$course2->id;$c3 = (int)$course3->id;$c4 = (int)$course4->id;$coursecat = core_course_category::get($category->id);$this->assertTrue($coursecat->resort_courses('idnumber'));$this->assertSame(array($c1, $c4, $c2, $c3), array_keys($coursecat->get_courses()));$this->assertTrue($coursecat->resort_courses('shortname'));$this->assertSame(array($c1, $c2, $c4, $c3), array_keys($coursecat->get_courses()));$this->assertTrue($coursecat->resort_courses('timecreated'));$this->assertSame(array($c1, $c2, $c3, $c4), array_keys($coursecat->get_courses()));try {// Enable the multilang filter and set it to apply to headings and content.\filter_manager::reset_caches();filter_set_global_state('multilang', TEXTFILTER_ON);filter_set_applies_to_strings('multilang', true);$expected = array($c3, $c4, $c1, $c2);} catch (\coding_exception $ex) {$expected = array($c3, $c4, $c2, $c1);}$this->assertTrue($coursecat->resort_courses('fullname'));$this->assertSame($expected, array_keys($coursecat->get_courses()));}public function test_get_search_courses(): void {global $DB;$cat1 = core_course_category::create(array('name' => 'Cat1'));$cat2 = core_course_category::create(array('name' => 'Cat2', 'parent' => $cat1->id));$c1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 3', 'summary' => ' ', 'idnumber' => 'ID3'));$c2 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 1', 'summary' => ' ', 'visible' => 0));$c3 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Математика', 'summary' => ' Test '));$c4 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 4', 'summary' => ' ', 'idnumber' => 'ID4'));$c5 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Test 5', 'summary' => ' '));$c6 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Дискретная Математика', 'summary' => ' '));$c7 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Test 7', 'summary' => ' ', 'visible' => 0));$c8 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Test 8', 'summary' => ' '));// Get courses in category 1 (returned visible only because user is not enrolled).$res = $cat1->get_courses(array('sortorder' => 1));$this->assertEquals(array($c4->id, $c3->id, $c1->id), array_keys($res)); // Courses are added in reverse order.$this->assertEquals(3, $cat1->get_courses_count());// Get courses in category 1 recursively (returned visible only because user is not enrolled).$res = $cat1->get_courses(array('recursive' => 1));$this->assertEquals(array($c4->id, $c3->id, $c1->id, $c8->id, $c6->id, $c5->id), array_keys($res));$this->assertEquals(6, $cat1->get_courses_count(array('recursive' => 1)));// Get courses sorted by fullname.$res = $cat1->get_courses(array('sort' => array('fullname' => 1)));$this->assertEquals(array($c1->id, $c4->id, $c3->id), array_keys($res));$this->assertEquals(3, $cat1->get_courses_count(array('sort' => array('fullname' => 1))));// Get courses sorted by fullname recursively.$res = $cat1->get_courses(array('recursive' => 1, 'sort' => array('fullname' => 1)));$this->assertEquals(array($c1->id, $c4->id, $c5->id, $c8->id, $c6->id, $c3->id), array_keys($res));$this->assertEquals(6, $cat1->get_courses_count(array('recursive' => 1, 'sort' => array('fullname' => 1))));// Get courses sorted by fullname recursively, use offset and limit.$res = $cat1->get_courses(array('recursive' => 1, 'offset' => 1, 'limit' => 2, 'sort' => array('fullname' => -1)));$this->assertEquals(array($c6->id, $c8->id), array_keys($res));// Offset and limit do not affect get_courses_count().$this->assertEquals(6, $cat1->get_courses_count(array('recursive' => 1, 'offset' => 1, 'limit' => 2, 'sort' => array('fullname' => 1))));// Calling get_courses_count without prior call to get_courses().$this->assertEquals(3, $cat2->get_courses_count(array('recursive' => 1, 'sort' => array('idnumber' => 1))));// Search courses.// Search by text.$res = core_course_category::search_courses(array('search' => 'Test'));$this->assertEquals(array($c4->id, $c3->id, $c1->id, $c8->id, $c5->id), array_keys($res));$this->assertEquals(5, core_course_category::search_courses_count(array('search' => 'Test')));// Search by text with specified offset and limit.$options = array('sort' => array('fullname' => 1), 'offset' => 1, 'limit' => 2);$res = core_course_category::search_courses(array('search' => 'Test'), $options);$this->assertEquals(array($c4->id, $c5->id), array_keys($res));$this->assertEquals(5, core_course_category::search_courses_count(array('search' => 'Test'), $options));// IMPORTANT: the tests below may fail on some databases// case-insensitive search.$res = core_course_category::search_courses(array('search' => 'test'));$this->assertEquals(array($c4->id, $c3->id, $c1->id, $c8->id, $c5->id), array_keys($res));$this->assertEquals(5, core_course_category::search_courses_count(array('search' => 'test')));// Non-latin language search.$res = core_course_category::search_courses(array('search' => 'Математика'));$this->assertEquals(array($c3->id, $c6->id), array_keys($res));$this->assertEquals(2, core_course_category::search_courses_count(array('search' => 'Математика'), array()));$user = $this->getDataGenerator()->create_user();$this->setUser($user);// Add necessary capabilities.$this->assign_capability('moodle/course:create', CAP_ALLOW, \context_coursecat::instance($cat2->id));// Do another search with restricted capabilities.$reqcaps = array('moodle/course:create');$res = core_course_category::search_courses(array('search' => 'test'), array(), $reqcaps);$this->assertEquals(array($c8->id, $c5->id), array_keys($res));$this->assertEquals(2, core_course_category::search_courses_count(array('search' => 'test'), array(), $reqcaps));// We should get no courses here as user is not enrolled to any courses.$res = core_course_category::search_courses(['search' => '','limittoenrolled' => 1,]);$this->assertEquals([], $res);$this->assertEquals(0, core_course_category::search_courses_count(['search' => '','limittoenrolled' => 1,]));$manual = enrol_get_plugin('manual');$teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']);$enrol = $DB->get_record('enrol', ['courseid' => $c5->id, 'enrol' => 'manual'], '*', MUST_EXIST);$manual->enrol_user($enrol, $user->id, $teacherrole->id);// Avoid using the cached values from previous method call.\cache::make('core', 'coursecat')->purge();// As the user is now enrolled, we should get this one course.$res = core_course_category::search_courses(['search' => '','limittoenrolled' => 1,]);$this->assertEquals([$c5->id], array_keys($res));$this->assertEquals(1, core_course_category::search_courses_count(['search' => '','limittoenrolled' => 1,]));}public function test_course_contacts(): void {global $DB, $CFG;set_config('coursecontactduplicates', false);$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'));$managerrole = $DB->get_record('role', array('shortname'=>'manager'));$studentrole = $DB->get_record('role', array('shortname'=>'student'));$oldcoursecontact = $CFG->coursecontact;$CFG->coursecontact = $managerrole->id. ','. $teacherrole->id;/** User is listed in course contacts for the course if he has one of the* "course contact" roles ($CFG->coursecontact) AND is enrolled in the course.* If the user has several roles only the highest is displayed.*/// Test case://// == Cat1 (user2 has teacher role)// == Cat2// -- course21 (user2 is enrolled as manager) | [Expected] Manager: F2 L2// -- course22 (user2 is enrolled as student) | [Expected] Teacher: F2 L2// == Cat4 (user2 has manager role)// -- course41 (user4 is enrolled as teacher, user5 is enrolled as manager) | [Expected] Manager: F5 L5, Teacher: F4 L4// -- course42 (user2 is enrolled as teacher) | [Expected] Manager: F2 L2// == Cat3 (user3 has manager role)// -- course31 (user3 is enrolled as student) | [Expected] Manager: F3 L3// -- course32 | [Expected]// -- course11 (user1 is enrolled as teacher) | [Expected] Teacher: F1 L1// -- course12 (user1 has teacher role) | [Expected]// also user4 is enrolled as teacher but enrolment is not active$category = $course = $enrol = $user = array();$category[1] = core_course_category::create(array('name' => 'Cat1'))->id;$category[2] = core_course_category::create(array('name' => 'Cat2', 'parent' => $category[1]))->id;$category[3] = core_course_category::create(array('name' => 'Cat3', 'parent' => $category[1]))->id;$category[4] = core_course_category::create(array('name' => 'Cat4', 'parent' => $category[2]))->id;foreach (array(1, 2, 3, 4) as $catid) {foreach (array(1, 2) as $courseid) {$course[$catid][$courseid] = $this->getDataGenerator()->create_course(array('idnumber' => 'id'.$catid.$courseid,'category' => $category[$catid]))->id;$enrol[$catid][$courseid] = $DB->get_record('enrol', array('courseid'=>$course[$catid][$courseid], 'enrol'=>'manual'), '*', MUST_EXIST);}}foreach (array(1, 2, 3, 4, 5) as $userid) {$user[$userid] = $this->getDataGenerator()->create_user(array('firstname' => 'F'.$userid, 'lastname' => 'L'.$userid))->id;}$manual = enrol_get_plugin('manual');// Nobody is enrolled now and course contacts are empty.$allcourses = core_course_category::get(0)->get_courses(array('recursive' => true, 'coursecontacts' => true, 'sort' => array('idnumber' => 1)));foreach ($allcourses as $onecourse) {$this->assertEmpty($onecourse->get_course_contacts());}// Cat1 (user2 has teacher role)role_assign($teacherrole->id, $user[2], \context_coursecat::instance($category[1]));// course21 (user2 is enrolled as manager)$manual->enrol_user($enrol[2][1], $user[2], $managerrole->id);// course22 (user2 is enrolled as student)$manual->enrol_user($enrol[2][2], $user[2], $studentrole->id);// Cat4 (user2 has manager role)role_assign($managerrole->id, $user[2], \context_coursecat::instance($category[4]));// course41 (user4 is enrolled as teacher, user5 is enrolled as manager)$manual->enrol_user($enrol[4][1], $user[4], $teacherrole->id);$manual->enrol_user($enrol[4][1], $user[5], $managerrole->id);// course42 (user2 is enrolled as teacher)$manual->enrol_user($enrol[4][2], $user[2], $teacherrole->id);// Cat3 (user3 has manager role)role_assign($managerrole->id, $user[3], \context_coursecat::instance($category[3]));// course31 (user3 is enrolled as student)$manual->enrol_user($enrol[3][1], $user[3], $studentrole->id);// course11 (user1 is enrolled as teacher)$manual->enrol_user($enrol[1][1], $user[1], $teacherrole->id);// -- course12 (user1 has teacher role)// also user4 is enrolled as teacher but enrolment is not activerole_assign($teacherrole->id, $user[1], \context_course::instance($course[1][2]));$manual->enrol_user($enrol[1][2], $user[4], $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED);$allcourses = core_course_category::get(0)->get_courses(array('recursive' => true, 'coursecontacts' => true, 'sort' => array('idnumber' => 1)));// Simplify the list of contacts for each course (similar as renderer would do).$contacts = array();foreach (array(1, 2, 3, 4) as $catid) {foreach (array(1, 2) as $courseid) {$tmp = array();foreach ($allcourses[$course[$catid][$courseid]]->get_course_contacts() as $contact) {$tmp[] = $contact['rolename']. ': '. $contact['username'];}$contacts[$catid][$courseid] = join(', ', $tmp);}}// Assert:// -- course21 (user2 is enrolled as manager) | Manager: F2 L2$this->assertSame('Manager: F2 L2', $contacts[2][1]);// -- course22 (user2 is enrolled as student) | Teacher: F2 L2$this->assertSame('Teacher: F2 L2', $contacts[2][2]);// -- course41 (user4 is enrolled as teacher, user5 is enrolled as manager) | Manager: F5 L5, Teacher: F4 L4$this->assertSame('Manager: F5 L5, Teacher: F4 L4', $contacts[4][1]);// -- course42 (user2 is enrolled as teacher) | [Expected] Manager: F2 L2$this->assertSame('Manager: F2 L2', $contacts[4][2]);// -- course31 (user3 is enrolled as student) | Manager: F3 L3$this->assertSame('Manager: F3 L3', $contacts[3][1]);// -- course32 |$this->assertSame('', $contacts[3][2]);// -- course11 (user1 is enrolled as teacher) | Teacher: F1 L1$this->assertSame('Teacher: F1 L1', $contacts[1][1]);// -- course12 (user1 has teacher role) |$this->assertSame('', $contacts[1][2]);// Suspend user 4 and make sure he is no longer in contacts of course 1 in category 4.$manual->enrol_user($enrol[4][1], $user[4], $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED);$allcourses = core_course_category::get(0)->get_courses(array('recursive' => true,'coursecontacts' => true,'sort' => array('idnumber' => 1)));$contacts = $allcourses[$course[4][1]]->get_course_contacts();$this->assertCount(1, $contacts);$contact = reset($contacts);$this->assertEquals('F5 L5', $contact['username']);$CFG->coursecontact = $oldcoursecontact;}public function test_course_contacts_with_duplicates(): void {global $DB, $CFG;set_config('coursecontactduplicates', true);$displayall = get_config('core', 'coursecontactduplicates');$this->assertEquals(true, $displayall);$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));$managerrole = $DB->get_record('role', array('shortname' => 'manager'));$studentrole = $DB->get_record('role', array('shortname' => 'student'));$oldcoursecontact = $CFG->coursecontact;$CFG->coursecontact = $managerrole->id. ','. $teacherrole->id;/** User is listed in course contacts for the course if he has one of the* "course contact" roles ($CFG->coursecontact) AND is enrolled in the course.* If the user has several roles all roles are displayed, but each role only once per user.*//** Test case:** == Cat1 (user2 has teacher role)* == Cat2* -- course21 (user2 is enrolled as manager) | [Expected] Manager: F2 L2* -- course22 (user2 is enrolled as student) | [Expected] Teacher: F2 L2* == Cat4 (user2 has manager role)* -- course41 (user4 is enrolled as teacher, user5 is enrolled as manager)* | [Expected] Manager: F5 L5, Teacher: F4 L4* -- course42 (user2 is enrolled as teacher) | [Expected] Manager: F2 L2* == Cat3 (user3 has manager role)* -- course31 (user3 is enrolled as student) | [Expected] Manager: F3 L3* -- course32 | [Expected]* -- course11 (user1 is enrolled as teacher) | [Expected] Teacher: F1 L1* -- course12 (user1 has teacher role) | [Expected]* also user4 is enrolled as teacher but enrolment is not active*/$category = $course = $enrol = $user = array();$category[1] = core_course_category::create(array('name' => 'Cat1'))->id;$category[2] = core_course_category::create(array('name' => 'Cat2', 'parent' => $category[1]))->id;$category[3] = core_course_category::create(array('name' => 'Cat3', 'parent' => $category[1]))->id;$category[4] = core_course_category::create(array('name' => 'Cat4', 'parent' => $category[2]))->id;foreach (array(1, 2, 3, 4) as $catid) {foreach (array(1, 2) as $courseid) {$course[$catid][$courseid] = $this->getDataGenerator()->create_course(array('idnumber' => 'id'.$catid.$courseid,'category' => $category[$catid]))->id;$enrol[$catid][$courseid] = $DB->get_record('enrol',array('courseid' => $course[$catid][$courseid], 'enrol' => 'manual'),'*',MUST_EXIST);}}foreach (array(1, 2, 3, 4, 5) as $userid) {$user[$userid] = $this->getDataGenerator()->create_user(array('firstname' => 'F'.$userid,'lastname' => 'L'.$userid))->id;}$manual = enrol_get_plugin('manual');// Nobody is enrolled now and course contacts are empty.$allcourses = core_course_category::get(0)->get_courses(array('recursive' => true,'coursecontacts' => true,'sort' => array('idnumber' => 1)));foreach ($allcourses as $onecourse) {$this->assertEmpty($onecourse->get_course_contacts());}// Cat1: user2 has teacher role.role_assign($teacherrole->id, $user[2], \context_coursecat::instance($category[1]));// Course21: user2 is enrolled as manager.$manual->enrol_user($enrol[2][1], $user[2], $managerrole->id);// Course22: user2 is enrolled as student.$manual->enrol_user($enrol[2][2], $user[2], $studentrole->id);// Cat4: user2 has manager role.role_assign($managerrole->id, $user[2], \context_coursecat::instance($category[4]));// Course41: user4 is enrolled as teacher, user5 is enrolled as manager.$manual->enrol_user($enrol[4][1], $user[4], $teacherrole->id);$manual->enrol_user($enrol[4][1], $user[5], $managerrole->id);// Course42: user2 is enrolled as teacher.$manual->enrol_user($enrol[4][2], $user[2], $teacherrole->id);// Cat3: user3 has manager role.role_assign($managerrole->id, $user[3], \context_coursecat::instance($category[3]));// Course31: user3 is enrolled as student.$manual->enrol_user($enrol[3][1], $user[3], $studentrole->id);// Course11: user1 is enrolled as teacher and user4 is enrolled as teacher and has manager role.$manual->enrol_user($enrol[1][1], $user[1], $teacherrole->id);$manual->enrol_user($enrol[1][1], $user[4], $teacherrole->id);role_assign($managerrole->id, $user[4], \context_course::instance($course[1][1]));// Course12: user1 has teacher role, but is not enrolled, as well as user4 is enrolled as teacher, but user4's enrolment is// not active.role_assign($teacherrole->id, $user[1], \context_course::instance($course[1][2]));$manual->enrol_user($enrol[1][2], $user[4], $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED);$allcourses = core_course_category::get(0)->get_courses(array('recursive' => true, 'coursecontacts' => true, 'sort' => array('idnumber' => 1)));// Simplify the list of contacts for each course (similar as renderer would do).$contacts = array();foreach (array(1, 2, 3, 4) as $catid) {foreach (array(1, 2) as $courseid) {$tmp = array();foreach ($allcourses[$course[$catid][$courseid]]->get_course_contacts() as $contact) {$rolenames = array_map(function ($role) {return $role->displayname;}, $contact['roles']);$tmp[] = implode(", ", $rolenames). ': '.$contact['username'];}$contacts[$catid][$courseid] = join(', ', $tmp);}}// Assert:// Course21: user2 is enrolled as manager. [Expected] Manager: F2 L2, Teacher: F2 L2.$this->assertSame('Manager, Teacher: F2 L2', $contacts[2][1]);// Course22: user2 is enrolled as student. [Expected] Teacher: F2 L2.$this->assertSame('Teacher: F2 L2', $contacts[2][2]);// Course41: user4 is enrolled as teacher, user5 is enrolled as manager. [Expected] Manager: F5 L5, Teacher: F4 L4.$this->assertSame('Manager: F5 L5, Teacher: F4 L4', $contacts[4][1]);// Course42: user2 is enrolled as teacher. [Expected] Manager: F2 L2, Teacher: F2 L2.$this->assertSame('Manager, Teacher: F2 L2', $contacts[4][2]);// Course31: user3 is enrolled as student. [Expected] Manager: F3 L3.$this->assertSame('Manager: F3 L3', $contacts[3][1]);// Course32: nobody is enrolled. [Expected] (nothing).$this->assertSame('', $contacts[3][2]);// Course11: user1 is enrolled as teacher and user4 is enrolled as teacher and has manager role. [Expected] Manager: F4 L4,// Teacher: F1 L1, Teacher: F4 L4.$this->assertSame('Manager, Teacher: F4 L4, Teacher: F1 L1', $contacts[1][1]);// Course12: user1 has teacher role, but is not enrolled, as well as user4 is enrolled as teacher, but user4's enrolment is// not active. [Expected] (nothing).$this->assertSame('', $contacts[1][2]);// Suspend user 4 and make sure he is no longer in contacts of course 1 in category 4.$manual->enrol_user($enrol[4][1], $user[4], $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED);$allcourses = core_course_category::get(0)->get_courses(array('recursive' => true,'coursecontacts' => true,'sort' => array('idnumber' => 1)));$contacts = $allcourses[$course[4][1]]->get_course_contacts();$this->assertCount(1, $contacts);$contact = reset($contacts);$this->assertEquals('F5 L5', $contact['username']);$CFG->coursecontact = $oldcoursecontact;}public function test_overview_files(): void {global $CFG;$this->setAdminUser();$cat1 = core_course_category::create(array('name' => 'Cat1'));// Create course c1 with one image file.$dratid1 = $this->fill_draft_area(array('filename.jpg' => 'Test file contents1'));$c1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id,'fullname' => 'Test 1', 'overviewfiles_filemanager' => $dratid1));// Create course c2 with two image files (only one file will be added because of settings).$dratid2 = $this->fill_draft_area(array('filename21.jpg' => 'Test file contents21', 'filename22.jpg' => 'Test file contents22'));$c2 = $this->getDataGenerator()->create_course(array('category' => $cat1->id,'fullname' => 'Test 2', 'overviewfiles_filemanager' => $dratid2));// Create course c3 without files.$c3 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 3'));// Change the settings to allow multiple files of any types.$CFG->courseoverviewfileslimit = 3;$CFG->courseoverviewfilesext = '*';// Create course c5 with two image files.$dratid4 = $this->fill_draft_area(array('filename41.jpg' => 'Test file contents41', 'filename42.jpg' => 'Test file contents42'));$c4 = $this->getDataGenerator()->create_course(array('category' => $cat1->id,'fullname' => 'Test 4', 'overviewfiles_filemanager' => $dratid4));// Create course c6 with non-image file.$dratid5 = $this->fill_draft_area(array('filename51.zip' => 'Test file contents51'));$c5 = $this->getDataGenerator()->create_course(array('category' => $cat1->id,'fullname' => 'Test 5', 'overviewfiles_filemanager' => $dratid5));// Reset default settings.$CFG->courseoverviewfileslimit = 1;$CFG->courseoverviewfilesext = 'web_image';$courses = $cat1->get_courses();$this->assertTrue($courses[$c1->id]->has_course_overviewfiles());$this->assertTrue($courses[$c2->id]->has_course_overviewfiles());$this->assertFalse($courses[$c3->id]->has_course_overviewfiles());$this->assertTrue($courses[$c4->id]->has_course_overviewfiles());$this->assertTrue($courses[$c5->id]->has_course_overviewfiles()); // Does not validate the filetypes.$this->assertEquals(1, count($courses[$c1->id]->get_course_overviewfiles()));$this->assertEquals(1, count($courses[$c2->id]->get_course_overviewfiles()));$this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles()));$this->assertEquals(1, count($courses[$c4->id]->get_course_overviewfiles()));$this->assertEquals(0, count($courses[$c5->id]->get_course_overviewfiles())); // Validate the filetypes.// Overview files are not allowed, all functions return empty values.$CFG->courseoverviewfileslimit = 0;$this->assertFalse($courses[$c1->id]->has_course_overviewfiles());$this->assertFalse($courses[$c2->id]->has_course_overviewfiles());$this->assertFalse($courses[$c3->id]->has_course_overviewfiles());$this->assertFalse($courses[$c4->id]->has_course_overviewfiles());$this->assertFalse($courses[$c5->id]->has_course_overviewfiles());$this->assertEquals(0, count($courses[$c1->id]->get_course_overviewfiles()));$this->assertEquals(0, count($courses[$c2->id]->get_course_overviewfiles()));$this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles()));$this->assertEquals(0, count($courses[$c4->id]->get_course_overviewfiles()));$this->assertEquals(0, count($courses[$c5->id]->get_course_overviewfiles()));// Multiple overview files are allowed but still limited to images.$CFG->courseoverviewfileslimit = 3;$this->assertTrue($courses[$c1->id]->has_course_overviewfiles());$this->assertTrue($courses[$c2->id]->has_course_overviewfiles());$this->assertFalse($courses[$c3->id]->has_course_overviewfiles());$this->assertTrue($courses[$c4->id]->has_course_overviewfiles());$this->assertTrue($courses[$c5->id]->has_course_overviewfiles()); // Still does not validate the filetypes.$this->assertEquals(1, count($courses[$c1->id]->get_course_overviewfiles()));$this->assertEquals(1, count($courses[$c2->id]->get_course_overviewfiles())); // Only 1 file was actually added.$this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles()));$this->assertEquals(2, count($courses[$c4->id]->get_course_overviewfiles()));$this->assertEquals(0, count($courses[$c5->id]->get_course_overviewfiles()));// Multiple overview files of any type are allowed.$CFG->courseoverviewfilesext = '*';$this->assertTrue($courses[$c1->id]->has_course_overviewfiles());$this->assertTrue($courses[$c2->id]->has_course_overviewfiles());$this->assertFalse($courses[$c3->id]->has_course_overviewfiles());$this->assertTrue($courses[$c4->id]->has_course_overviewfiles());$this->assertTrue($courses[$c5->id]->has_course_overviewfiles());$this->assertEquals(1, count($courses[$c1->id]->get_course_overviewfiles()));$this->assertEquals(1, count($courses[$c2->id]->get_course_overviewfiles()));$this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles()));$this->assertEquals(2, count($courses[$c4->id]->get_course_overviewfiles()));$this->assertEquals(1, count($courses[$c5->id]->get_course_overviewfiles()));}public function test_get_nested_name(): void {$cat1name = 'Cat1';$cat2name = 'Cat2';$cat3name = 'Cat3';$cat4name = 'Cat4';$category1 = core_course_category::create(array('name' => $cat1name));$category2 = core_course_category::create(array('name' => $cat2name, 'parent' => $category1->id));$category3 = core_course_category::create(array('name' => $cat3name, 'parent' => $category2->id));$category4 = core_course_category::create(array('name' => $cat4name, 'parent' => $category2->id));$this->assertEquals($cat1name, $category1->get_nested_name(false));$this->assertEquals("{$cat1name} / {$cat2name}", $category2->get_nested_name(false));$this->assertEquals("{$cat1name} / {$cat2name} / {$cat3name}", $category3->get_nested_name(false));$this->assertEquals("{$cat1name} / {$cat2name} / {$cat4name}", $category4->get_nested_name(false));}public function test_coursecat_is_uservisible(): void {global $USER;// Create category 1 as visible.$category1 = core_course_category::create(array('name' => 'Cat1', 'visible' => 1));// Create category 2 as hidden.$category2 = core_course_category::create(array('name' => 'Cat2', 'visible' => 0));$this->assertTrue($category1->is_uservisible());$this->assertFalse($category2->is_uservisible());$this->assign_capability('moodle/category:viewhiddencategories');$this->assertTrue($category1->is_uservisible());$this->assertTrue($category2->is_uservisible());// First, store current user's id, then login as another user.$userid = $USER->id;$this->setUser($this->getDataGenerator()->create_user());// User $user should still have the moodle/category:viewhiddencategories capability.$this->assertTrue($category1->is_uservisible($userid));$this->assertTrue($category2->is_uservisible($userid));$this->assign_capability('moodle/category:viewhiddencategories', CAP_INHERIT);$this->assertTrue($category1->is_uservisible());$this->assertFalse($category2->is_uservisible());}public function test_current_user_coursecat_get(): void {$this->assign_capability('moodle/category:viewhiddencategories');// Create category 1 as visible.$category1 = core_course_category::create(array('name' => 'Cat1', 'visible' => 1));// Create category 2 as hidden.$category2 = core_course_category::create(array('name' => 'Cat2', 'visible' => 0));$this->assertEquals($category1->id, core_course_category::get($category1->id)->id);$this->assertEquals($category2->id, core_course_category::get($category2->id)->id);// Login as another user to test core_course_category::get.$this->setUser($this->getDataGenerator()->create_user());$this->assertEquals($category1->id, core_course_category::get($category1->id)->id);// Expecting to get an exception as this new user does not have the moodle/category:viewhiddencategories capability.$this->expectException('moodle_exception');$this->expectExceptionMessage(get_string('cannotviewcategory', 'error'));core_course_category::get($category2->id);}public function test_another_user_coursecat_get(): void {global $USER;$this->assign_capability('moodle/category:viewhiddencategories');// Create category 1 as visible.$category1 = core_course_category::create(array('name' => 'Cat1', 'visible' => 1));// Create category 2 as hidden.$category2 = core_course_category::create(array('name' => 'Cat2', 'visible' => 0));// First, store current user's object, then login as another user.$user1 = $USER;$user2 = $this->getDataGenerator()->create_user();$this->setUser($user2);$this->assertEquals($category1->id, core_course_category::get($category1->id, MUST_EXIST, false, $user1)->id);$this->assertEquals($category2->id, core_course_category::get($category2->id, MUST_EXIST, false, $user1)->id);$this->setUser($user1);$this->assertEquals($category1->id, core_course_category::get($category1->id, MUST_EXIST, false, $user2)->id);$this->expectException('moodle_exception');$this->expectExceptionMessage(get_string('cannotviewcategory', 'error'));core_course_category::get($category2->id, MUST_EXIST, false, $user2);}/*** Creates a draft area for current user and fills it with fake files** @param array $files array of files that need to be added to filearea, filename => filecontents* @return int draftid for the filearea*/protected function fill_draft_area(array $files) {global $USER;$usercontext = \context_user::instance($USER->id);$draftid = file_get_unused_draft_itemid();foreach ($files as $filename => $filecontents) {// Add actual file there.$filerecord = array('component' => 'user', 'filearea' => 'draft','contextid' => $usercontext->id, 'itemid' => $draftid,'filename' => $filename, 'filepath' => '/');$fs = get_file_storage();$fs->create_file_from_string($filerecord, $filecontents);}return $draftid;}/*** This test ensures that is the list of courses in a category can be retrieved while a course is being deleted.*/public function test_get_courses_during_delete(): void {global $DB;$category = self::getDataGenerator()->create_category();$course = self::getDataGenerator()->create_course(['category' => $category->id]);$othercourse = self::getDataGenerator()->create_course(['category' => $category->id]);$coursecategory = core_course_category::get($category->id);// Get a list of courses before deletion to populate the cache.$originalcourses = $coursecategory->get_courses();$this->assertCount(2, $originalcourses);$this->assertArrayHasKey($course->id, $originalcourses);$this->assertArrayHasKey($othercourse->id, $originalcourses);// Simulate the course deletion process being part way though.$DB->delete_records('course', ['id' => $course->id]);// Get the list of courses while a deletion is in progress.$courses = $coursecategory->get_courses();$this->assertCount(1, $courses);$this->assertArrayHasKey($othercourse->id, $courses);}/*** Test get_nearest_editable_subcategory() method.** @covers \core_course_category::get_nearest_editable_subcategory*/public function test_get_nearest_editable_subcategory(): void {global $DB;$coursecreatorrole = $DB->get_record('role', ['shortname' => 'coursecreator']);$managerrole = $DB->get_record('role', ['shortname' => 'manager']);// Create categories.$category1 = core_course_category::create(['name' => 'Cat1']);$category2 = core_course_category::create(['name' => 'Cat2']);$category3 = core_course_category::create(['name' => 'Cat3']);// Get the category contexts.$category1context = $category1->get_context();$category2context = $category2->get_context();$category3context = $category3->get_context();// Create user.$user1 = $this->getDataGenerator()->create_user();$user2 = $this->getDataGenerator()->create_user();$user3 = $this->getDataGenerator()->create_user();// Assign the user1 to 'Course creator' role for Cat1.role_assign($coursecreatorrole->id, $user1->id, $category1context->id);// Assign the user2 to 'Manager' role for Cat3.role_assign($managerrole->id, $user2->id, $category3context->id);// Start scenario 1.// user3 has no permission to create course or manage category.$this->setUser($user3);$coursecat = core_course_category::user_top();$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create']));$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create']));$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['manage']));$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/category:manage']));$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create', 'manage']));// End scenario 1.// Start scenario 2.// user1 has permission to create course but has no permission to manage category.$this->setUser($user1);$coursecat = core_course_category::user_top();$this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create']));$this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create']));$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['manage']));$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/category:manage']));$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create', 'manage']));// The get_nearest_editable_subcategory should return Cat1.$this->assertEquals($category1->id, core_course_category::get_nearest_editable_subcategory($coursecat, ['create'])->id);$this->assertEquals($category1->id,core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create'])->id);// Assign the user1 to 'Course creator' role for Cat2.role_assign($coursecreatorrole->id, $user1->id, $category2context->id);// The get_nearest_editable_subcategory should still return Cat1 (First creatable subcategory) for create course capability.$this->assertEquals($category1->id, core_course_category::get_nearest_editable_subcategory($coursecat, ['create'])->id);$this->assertEquals($category1->id,core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create'])->id);// End scenario 2.// Start scenario 3.// user2 has no permission to create course but has permission to manage category.$this->setUser($user2);// Remove the moodle/course:create capability for the manager role.unassign_capability('moodle/course:create', $managerrole->id);$coursecat = core_course_category::user_top();$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create']));$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create']));$this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['manage']));$this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/category:manage']));$this->assertEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create', 'manage']));// The get_nearest_editable_subcategory should return Cat3.$this->assertEquals($category3->id, core_course_category::get_nearest_editable_subcategory($coursecat, ['manage'])->id);$this->assertEquals($category3->id,core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/category:manage'])->id);// End scenario 3.// Start scenario 4.// user2 has both permission to create course and manage category.// Add the moodle/course:create capability back again for the manager role.assign_capability('moodle/course:create', CAP_ALLOW, $managerrole->id, $category3context->id);$this->setUser($user2);$coursecat = core_course_category::user_top();$this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create']));$this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:create']));$this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['manage']));$this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/category:manage']));$this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, ['create', 'manage']));// The get_nearest_editable_subcategory should return Cat3.$this->assertEquals($category3->id,core_course_category::get_nearest_editable_subcategory($coursecat, ['create', 'manage'])->id);$this->assertEquals($category3->id, core_course_category::get_nearest_editable_subcategory($coursecat,['moodle/course:create', 'moodle/category:manage'])->id);// End scenario 4.// Start scenario 5.// Exception will be thrown if $permissionstocheck is empty.$this->setUser($user1);$coursecat = core_course_category::user_top();$this->expectException('coding_exception');$this->expectExceptionMessage('Invalid permissionstocheck parameter');$this->assertNotEmpty(core_course_category::get_nearest_editable_subcategory($coursecat, []));// End scenario 5.}/*** Test get_nearest_editable_subcategory() method with hidden categories.** @param int $visible Whether the category is visible or not.* @param bool $child Whether the category is child of main category or not.* @param string $role The role the user must have.* @param array $permissions An array of permissions we must check.* @param bool $result Whether the result should be the category or null.** @dataProvider get_nearest_editable_subcategory_provider* @covers \core_course_category::get_nearest_editable_subcategory*/public function test_get_nearest_editable_subcategory_with_hidden_categories(int $visible = 0,bool $child = false,string $role = 'manager',array $permissions = [],bool $result = false): void {global $DB;$userrole = $DB->get_record('role', ['shortname' => $role]);$maincat = core_course_category::create(['name' => 'Main cat']);$catparams = new \stdClass();$catparams->name = 'Test category';$catparams->visible = $visible;if ($child) {$catparams->parent = $maincat->id;}$category = core_course_category::create($catparams);$catcontext = $category->get_context();$user = $this->getDataGenerator()->create_user();role_assign($userrole->id, $user->id, $catcontext->id);$this->setUser($user);$nearestcat = core_course_category::get_nearest_editable_subcategory(core_course_category::user_top(), $permissions);if ($result) {$this->assertEquals($category->id, $nearestcat->id);} else {$this->assertEmpty($nearestcat);}}/*** Data provider for test_get_nearest_editable_subcategory_with_hidden_categories().** @return array*/public function get_nearest_editable_subcategory_provider(): array {return ['Hidden main category for manager. Checking create and manage' => [0,false,'manager',['create', 'manage'],true,],'Hidden main category for course creator. Checking create and manage' => [0,false,'coursecreator',['create', 'manage'],false,],'Hidden main category for student. Checking create and manage' => [0,false,'student',['create', 'manage'],false,],'Hidden main category for manager. Checking create' => [0,false,'manager',['create'],true,],'Hidden main category for course creator. Checking create' => [0,false,'coursecreator',['create'],true,],'Hidden main category for student. Checking create' => [0,false,'student',['create'],false,],'Hidden subcategory for manager. Checking create and manage' => [0,true,'manager',['create', 'manage'],true,],'Hidden subcategory for course creator. Checking create and manage' => [0,true,'coursecreator',['create', 'manage'],false,],'Hidden subcategory for student. Checking create and manage' => [0,true,'student',['create', 'manage'],false,],'Hidden subcategory for manager. Checking create' => [0,true,'manager',['create'],true,],'Hidden subcategory for course creator. Checking create' => [0,true,'coursecreator',['create'],true,],'Hidden subcategory for student. Checking create' => [0,true,'student',['create'],false,],];}/*** This test ensures that the filter context list is populated by the correct filter contexts from make_category_list.** @coversNothing*/public function test_make_category_list_context(): void {global $DB;// Ensure that the category list is empty.$DB->delete_records('course_categories');set_config('perfdebug', 15);// Create a few categories to populate the context cache.$this->getDataGenerator()->create_category(['name' => 'cat1']);$this->getDataGenerator()->create_category(['name' => 'cat2']);$this->getDataGenerator()->create_category(['name' => 'cat3']);$filtermanager = \filter_manager::instance();// Configure a filter to apply to all content and headings.filter_set_global_state('multilang', TEXTFILTER_ON);filter_set_applies_to_strings('multilang', true);$perf = $filtermanager->get_performance_summary();$this->assertEquals(0, $perf[0]['contextswithfilters']);// Now fill the cache with the category strings.\core_course_category::make_categories_list();// 3 Categories + system context.$perf = $filtermanager->get_performance_summary();$this->assertEquals(3, $perf[0]['contextswithfilters']);$filtermanager->reset_caches();// We need to refresh the instance, resetting caches unloads the singleton.$filtermanager = \filter_manager::instance();\cache_helper::purge_by_definition('core', 'coursecat');// Now flip the bit on the filter context.set_config('filternavigationwithsystemcontext', 1);// Repeat the check. Only context should be system context.\core_course_category::make_categories_list();$perf = $filtermanager->get_performance_summary();$this->assertEquals(1, $perf[0]['contextswithfilters']);}}