MDL-59890 coursecat: Add helper to fetch nested view of coursecat
[moodle.git] / lib / tests / coursecatlib_test.php
CommitLineData
b28bb7e8
MG
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/**
18 * Tests for class coursecat from lib/coursecatlib.php
19 *
20 * @package core
21 * @category phpunit
22 * @copyright 2013 Marina Glancy
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28global $CFG;
29require_once($CFG->libdir . '/coursecatlib.php');
30
31/**
7db309f3 32 * Functional test for coursecatlib.php
b28bb7e8 33 */
7db309f3 34class core_coursecatlib_testcase extends advanced_testcase {
b28bb7e8 35
7db309f3 36 protected $roles;
b28bb7e8 37
7db309f3 38 protected function setUp() {
b28bb7e8 39 parent::setUp();
7db309f3 40 $this->resetAfterTest();
b28bb7e8
MG
41 $user = $this->getDataGenerator()->create_user();
42 $this->setUser($user);
43 }
44
45 protected function get_roleid($context = null) {
46 global $USER;
47 if ($context === null) {
48 $context = context_system::instance();
49 }
50 if (is_object($context)) {
51 $context = $context->id;
52 }
53 if (empty($this->roles)) {
54 $this->roles = array();
55 }
56 if (empty($this->roles[$USER->id])) {
57 $this->roles[$USER->id] = array();
58 }
59 if (empty($this->roles[$USER->id][$context])) {
60 $this->roles[$USER->id][$context] = create_role('Role for '.$USER->id.' in '.$context, 'role'.$USER->id.'-'.$context, '-');
61 role_assign($this->roles[$USER->id][$context], $USER->id, $context);
62 }
63 return $this->roles[$USER->id][$context];
64 }
65
66 protected function assign_capability($capability, $permission = CAP_ALLOW, $contextid = null) {
67 if ($contextid === null) {
68 $contextid = context_system::instance();
69 }
70 if (is_object($contextid)) {
71 $contextid = $contextid->id;
72 }
73 assign_capability($capability, $permission, $this->get_roleid($contextid), $contextid, true);
74 accesslib_clear_all_caches_for_unit_testing();
75 }
76
77 public function test_create_coursecat() {
7db309f3 78 // Create the category.
b28bb7e8
MG
79 $data = new stdClass();
80 $data->name = 'aaa';
81 $data->description = 'aaa';
82 $data->idnumber = '';
83
84 $category1 = coursecat::create($data);
85
7db309f3
PS
86 // Initially confirm that base data was inserted correctly.
87 $this->assertSame($data->name, $category1->name);
88 $this->assertSame($data->description, $category1->description);
89 $this->assertSame($data->idnumber, $category1->idnumber);
b28bb7e8
MG
90
91 $this->assertGreaterThanOrEqual(1, $category1->sortorder);
92
7db309f3 93 // Create two more categories and test the sortorder worked correctly.
b28bb7e8
MG
94 $data->name = 'ccc';
95 $category2 = coursecat::create($data);
96
97 $data->name = 'bbb';
98 $category3 = coursecat::create($data);
99
100 $this->assertGreaterThan($category1->sortorder, $category2->sortorder);
101 $this->assertGreaterThan($category2->sortorder, $category3->sortorder);
102 }
103
104 public function test_name_idnumber_exceptions() {
105 try {
106 coursecat::create(array('name' => ''));
107 $this->fail('Missing category name exception expected in coursecat::create');
108 } catch (moodle_exception $e) {
7db309f3 109 $this->assertInstanceOf('moodle_exception', $e);
b28bb7e8
MG
110 }
111 $cat1 = coursecat::create(array('name' => 'Cat1', 'idnumber' => '1'));
112 try {
113 $cat1->update(array('name' => ''));
114 $this->fail('Missing category name exception expected in coursecat::update');
115 } catch (moodle_exception $e) {
7db309f3 116 $this->assertInstanceOf('moodle_exception', $e);
b28bb7e8
MG
117 }
118 try {
119 coursecat::create(array('name' => 'Cat2', 'idnumber' => '1'));
120 $this->fail('Duplicate idnumber exception expected in coursecat::create');
121 } catch (moodle_exception $e) {
7db309f3 122 $this->assertInstanceOf('moodle_exception', $e);
b28bb7e8
MG
123 }
124 $cat2 = coursecat::create(array('name' => 'Cat2', 'idnumber' => '2'));
125 try {
126 $cat2->update(array('idnumber' => '1'));
127 $this->fail('Duplicate idnumber exception expected in coursecat::update');
128 } catch (moodle_exception $e) {
7db309f3 129 $this->assertInstanceOf('moodle_exception', $e);
b28bb7e8 130 }
c10bfee4
NM
131 // Test that duplicates with an idnumber of 0 cannot be created.
132 coursecat::create(array('name' => 'Cat3', 'idnumber' => '0'));
133 try {
134 coursecat::create(array('name' => 'Cat4', 'idnumber' => '0'));
135 $this->fail('Duplicate idnumber "0" exception expected in coursecat::create');
136 } catch (moodle_exception $e) {
137 $this->assertInstanceOf('moodle_exception', $e);
138 }
139 // Test an update cannot make a duplicate idnumber of 0.
140 try {
141 $cat2->update(array('idnumber' => '0'));
142 $this->fail('Duplicate idnumber "0" exception expected in coursecat::update');
143 } catch (Exception $e) {
144 $this->assertInstanceOf('moodle_exception', $e);
145 }
b28bb7e8
MG
146 }
147
148 public function test_visibility() {
149 $this->assign_capability('moodle/category:viewhiddencategories');
150 $this->assign_capability('moodle/category:manage');
151
7db309f3 152 // Create category 1 initially hidden.
b28bb7e8
MG
153 $category1 = coursecat::create(array('name' => 'Cat1', 'visible' => 0));
154 $this->assertEquals(0, $category1->visible);
155 $this->assertEquals(0, $category1->visibleold);
156
7db309f3 157 // Create category 2 initially hidden as a child of hidden category 1.
b28bb7e8
MG
158 $category2 = coursecat::create(array('name' => 'Cat2', 'visible' => 0, 'parent' => $category1->id));
159 $this->assertEquals(0, $category2->visible);
160 $this->assertEquals(0, $category2->visibleold);
161
7db309f3 162 // Create category 3 initially visible as a child of hidden category 1.
b28bb7e8
MG
163 $category3 = coursecat::create(array('name' => 'Cat3', 'visible' => 1, 'parent' => $category1->id));
164 $this->assertEquals(0, $category3->visible);
165 $this->assertEquals(1, $category3->visibleold);
166
7db309f3 167 // Show category 1 and make sure that category 2 is hidden and category 3 is visible.
b28bb7e8
MG
168 $category1->show();
169 $this->assertEquals(1, coursecat::get($category1->id)->visible);
170 $this->assertEquals(0, coursecat::get($category2->id)->visible);
171 $this->assertEquals(1, coursecat::get($category3->id)->visible);
172
7db309f3 173 // Create visible category 4.
b28bb7e8
MG
174 $category4 = coursecat::create(array('name' => 'Cat4'));
175 $this->assertEquals(1, $category4->visible);
176 $this->assertEquals(1, $category4->visibleold);
177
7db309f3 178 // Create visible category 5 as a child of visible category 4.
b28bb7e8
MG
179 $category5 = coursecat::create(array('name' => 'Cat5', 'parent' => $category4->id));
180 $this->assertEquals(1, $category5->visible);
181 $this->assertEquals(1, $category5->visibleold);
182
7db309f3 183 // Hide category 4 and make sure category 5 is hidden too.
b28bb7e8
MG
184 $category4->hide();
185 $this->assertEquals(0, $category4->visible);
186 $this->assertEquals(0, $category4->visibleold);
7db309f3 187 $category5 = coursecat::get($category5->id); // We have to re-read from DB.
b28bb7e8
MG
188 $this->assertEquals(0, $category5->visible);
189 $this->assertEquals(1, $category5->visibleold);
190
7db309f3 191 // Show category 4 and make sure category 5 is visible too.
b28bb7e8
MG
192 $category4->show();
193 $this->assertEquals(1, $category4->visible);
194 $this->assertEquals(1, $category4->visibleold);
7db309f3 195 $category5 = coursecat::get($category5->id); // We have to re-read from DB.
b28bb7e8
MG
196 $this->assertEquals(1, $category5->visible);
197 $this->assertEquals(1, $category5->visibleold);
198
7db309f3 199 // Move category 5 under hidden category 2 and make sure it became hidden.
b28bb7e8
MG
200 $category5->change_parent($category2->id);
201 $this->assertEquals(0, $category5->visible);
202 $this->assertEquals(1, $category5->visibleold);
203
7db309f3 204 // Re-read object for category 5 from DB and check again.
b28bb7e8
MG
205 $category5 = coursecat::get($category5->id);
206 $this->assertEquals(0, $category5->visible);
207 $this->assertEquals(1, $category5->visibleold);
208
7db309f3
PS
209 // Rricky one! Move hidden category 5 under visible category ("Top") and make sure it is still hidden-
210 // WHY? Well, different people may expect different behaviour here. So better keep it hidden.
b28bb7e8
MG
211 $category5->change_parent(0);
212 $this->assertEquals(0, $category5->visible);
213 $this->assertEquals(1, $category5->visibleold);
214 }
215
216 public function test_hierarchy() {
217 $this->assign_capability('moodle/category:viewhiddencategories');
218 $this->assign_capability('moodle/category:manage');
219
220 $category1 = coursecat::create(array('name' => 'Cat1'));
221 $category2 = coursecat::create(array('name' => 'Cat2', 'parent' => $category1->id));
222 $category3 = coursecat::create(array('name' => 'Cat3', 'parent' => $category1->id));
223 $category4 = coursecat::create(array('name' => 'Cat4', 'parent' => $category2->id));
224
7db309f3 225 // Check function get_children().
b28bb7e8 226 $this->assertEquals(array($category2->id, $category3->id), array_keys($category1->get_children()));
7db309f3 227 // Check function get_parents().
15d50fff 228 $this->assertEquals(array($category1->id, $category2->id), $category4->get_parents());
b28bb7e8 229
7db309f3 230 // Can not move category to itself or to it's children.
b28bb7e8
MG
231 $this->assertFalse($category1->can_change_parent($category2->id));
232 $this->assertFalse($category2->can_change_parent($category2->id));
7db309f3 233 // Can move category to grandparent.
b28bb7e8
MG
234 $this->assertTrue($category4->can_change_parent($category1->id));
235
236 try {
237 $category2->change_parent($category4->id);
238 $this->fail('Exception expected - can not move category');
239 } catch (moodle_exception $e) {
7db309f3 240 $this->assertInstanceOf('moodle_exception', $e);
b28bb7e8
MG
241 }
242
243 $category4->change_parent(0);
15d50fff 244 $this->assertEquals(array(), $category4->get_parents());
b28bb7e8
MG
245 $this->assertEquals(array($category2->id, $category3->id), array_keys($category1->get_children()));
246 $this->assertEquals(array(), array_keys($category2->get_children()));
247 }
248
249 public function test_update() {
250 $category1 = coursecat::create(array('name' => 'Cat1'));
251 $timecreated = $category1->timemodified;
7db309f3 252 $this->assertSame('Cat1', $category1->name);
b28bb7e8 253 $this->assertTrue(empty($category1->description));
74ee9d29 254 $this->waitForSecond();
b28bb7e8
MG
255 $testdescription = 'This is cat 1 а также русский текст';
256 $category1->update(array('description' => $testdescription));
7db309f3 257 $this->assertSame($testdescription, $category1->description);
b28bb7e8 258 $category1 = coursecat::get($category1->id);
7db309f3 259 $this->assertSame($testdescription, $category1->description);
eabbfa82 260 cache_helper::purge_by_event('changesincoursecat');
b28bb7e8 261 $category1 = coursecat::get($category1->id);
7db309f3 262 $this->assertSame($testdescription, $category1->description);
b28bb7e8
MG
263
264 $this->assertGreaterThan($timecreated, $category1->timemodified);
265 }
266
267 public function test_delete() {
268 global $DB;
269
270 $this->assign_capability('moodle/category:manage');
271 $this->assign_capability('moodle/course:create');
272
273 $initialcatid = $DB->get_field_sql('SELECT max(id) from {course_categories}');
274
275 $category1 = coursecat::create(array('name' => 'Cat1'));
276 $category2 = coursecat::create(array('name' => 'Cat2', 'parent' => $category1->id));
277 $category3 = coursecat::create(array('name' => 'Cat3'));
278 $category4 = coursecat::create(array('name' => 'Cat4', 'parent' => $category2->id));
279
280 $course1 = $this->getDataGenerator()->create_course(array('category' => $category2->id));
281 $course2 = $this->getDataGenerator()->create_course(array('category' => $category4->id));
282 $course3 = $this->getDataGenerator()->create_course(array('category' => $category4->id));
283 $course4 = $this->getDataGenerator()->create_course(array('category' => $category1->id));
284
285 // Now we have
286 // $category1
287 // $category2
288 // $category4
289 // $course2
290 // $course3
291 // $course1
292 // $course4
293 // $category3
7db309f3 294 // structure.
b28bb7e8 295
7db309f3 296 // Login as another user to test course:delete capability (user who created course can delete it within 24h even without cap).
b28bb7e8
MG
297 $this->setUser($this->getDataGenerator()->create_user());
298
7db309f3
PS
299 // Delete category 2 and move content to category 3.
300 $this->assertFalse($category2->can_move_content_to($category3->id)); // No luck!
301 // Add necessary capabilities.
b28bb7e8
MG
302 $this->assign_capability('moodle/course:create', CAP_ALLOW, context_coursecat::instance($category3->id));
303 $this->assign_capability('moodle/category:manage');
7db309f3 304 $this->assertTrue($category2->can_move_content_to($category3->id)); // Hurray!
b28bb7e8
MG
305 $category2->delete_move($category3->id);
306
307 // Make sure we have:
308 // $category1
309 // $course4
310 // $category3
311 // $category4
312 // $course2
313 // $course3
314 // $course1
7db309f3 315 // structure.
b28bb7e8
MG
316
317 $this->assertNull(coursecat::get($category2->id, IGNORE_MISSING, true));
318 $this->assertEquals(array(), $category1->get_children());
319 $this->assertEquals(array($category4->id), array_keys($category3->get_children()));
320 $this->assertEquals($category4->id, $DB->get_field('course', 'category', array('id' => $course2->id)));
321 $this->assertEquals($category4->id, $DB->get_field('course', 'category', array('id' => $course3->id)));
322 $this->assertEquals($category3->id, $DB->get_field('course', 'category', array('id' => $course1->id)));
323
7db309f3
PS
324 // Delete category 3 completely.
325 $this->assertFalse($category3->can_delete_full()); // No luck!
326 // Add necessary capabilities.
b28bb7e8 327 $this->assign_capability('moodle/course:delete', CAP_ALLOW, context_coursecat::instance($category3->id));
7db309f3 328 $this->assertTrue($category3->can_delete_full()); // Hurray!
b28bb7e8
MG
329 $category3->delete_full();
330
331 // Make sure we have:
332 // $category1
333 // $course4
7db309f3 334 // structure.
b28bb7e8 335
7db309f3 336 // Note that we also have default 'Miscellaneous' category and default 'site' course.
b28bb7e8
MG
337 $this->assertEquals(1, $DB->get_field_sql('SELECT count(*) FROM {course_categories} WHERE id > ?', array($initialcatid)));
338 $this->assertEquals($category1->id, $DB->get_field_sql('SELECT max(id) FROM {course_categories}'));
339 $this->assertEquals(1, $DB->get_field_sql('SELECT count(*) FROM {course} WHERE id <> ?', array(SITEID)));
340 $this->assertEquals(array('id' => $course4->id, 'category' => $category1->id),
341 (array)$DB->get_record_sql('SELECT id, category from {course} where id <> ?', array(SITEID)));
342 }
eabbfa82
MG
343
344 public function test_get_children() {
345 $category1 = coursecat::create(array('name' => 'Cat1'));
346 $category2 = coursecat::create(array('name' => 'Cat2', 'parent' => $category1->id));
347 $category3 = coursecat::create(array('name' => 'Cat3', 'parent' => $category1->id, 'visible' => 0));
348 $category4 = coursecat::create(array('name' => 'Cat4', 'idnumber' => '12', 'parent' => $category1->id));
349 $category5 = coursecat::create(array('name' => 'Cat5', 'idnumber' => '11', 'parent' => $category1->id, 'visible' => 0));
350 $category6 = coursecat::create(array('name' => 'Cat6', 'idnumber' => '10', 'parent' => $category1->id));
351 $category7 = coursecat::create(array('name' => 'Cat0', 'parent' => $category1->id));
352
353 $children = $category1->get_children();
7db309f3
PS
354 // User does not have the capability to view hidden categories, so the list should be
355 // 2, 4, 6, 7.
eabbfa82
MG
356 $this->assertEquals(array($category2->id, $category4->id, $category6->id, $category7->id), array_keys($children));
357 $this->assertEquals(4, $category1->get_children_count());
358
359 $children = $category1->get_children(array('offset' => 2));
360 $this->assertEquals(array($category6->id, $category7->id), array_keys($children));
361 $this->assertEquals(4, $category1->get_children_count());
362
363 $children = $category1->get_children(array('limit' => 2));
364 $this->assertEquals(array($category2->id, $category4->id), array_keys($children));
365
366 $children = $category1->get_children(array('offset' => 1, 'limit' => 2));
367 $this->assertEquals(array($category4->id, $category6->id), array_keys($children));
368
369 $children = $category1->get_children(array('sort' => array('name' => 1)));
7db309f3 370 // Must be 7, 2, 4, 6.
eabbfa82
MG
371 $this->assertEquals(array($category7->id, $category2->id, $category4->id, $category6->id), array_keys($children));
372
373 $children = $category1->get_children(array('sort' => array('idnumber' => 1, 'name' => -1)));
7db309f3 374 // Must be 2, 7, 6, 4.
eabbfa82
MG
375 $this->assertEquals(array($category2->id, $category7->id, $category6->id, $category4->id), array_keys($children));
376
7db309f3 377 // Check that everything is all right after purging the caches.
eabbfa82
MG
378 cache_helper::purge_by_event('changesincoursecat');
379 $children = $category1->get_children();
380 $this->assertEquals(array($category2->id, $category4->id, $category6->id, $category7->id), array_keys($children));
381 $this->assertEquals(4, $category1->get_children_count());
382 }
93c544bd 383
5dc361e1
SH
384 /**
385 * Test the countall function
386 */
387 public function test_count_all() {
e06bb5f8
TH
388 global $DB;
389 // Dont assume there is just one. An add-on might create a category as part of the install.
390 $numcategories = $DB->count_records('course_categories');
391 $this->assertEquals($numcategories, coursecat::count_all());
5dc361e1
SH
392 $category1 = coursecat::create(array('name' => 'Cat1'));
393 $category2 = coursecat::create(array('name' => 'Cat2', 'parent' => $category1->id));
394 $category3 = coursecat::create(array('name' => 'Cat3', 'parent' => $category2->id, 'visible' => 0));
e06bb5f8
TH
395 // Now we've got three more.
396 $this->assertEquals($numcategories + 3, coursecat::count_all());
5dc361e1
SH
397 cache_helper::purge_by_event('changesincoursecat');
398 // We should still have 4.
e06bb5f8 399 $this->assertEquals($numcategories + 3, coursecat::count_all());
5dc361e1
SH
400 }
401
402 /**
403 * Test a categories ability to resort courses.
404 */
405 public function test_resort_courses() {
406 $this->resetAfterTest(true);
407 $generator = $this->getDataGenerator();
408 $category = $generator->create_category();
409 $course1 = $generator->create_course(array(
410 'category' => $category->id,
411 'idnumber' => '006-01',
412 'shortname' => 'Biome Study',
9a4231e9 413 'fullname' => '<span lang="ar" class="multilang">'.'دراسة منطقة إحيائية'.'</span><span lang="en" class="multilang">Biome Study</span>',
fc6a0fe2 414 'timecreated' => '1000000001'
5dc361e1
SH
415 ));
416 $course2 = $generator->create_course(array(
417 'category' => $category->id,
418 'idnumber' => '007-02',
419 'shortname' => 'Chemistry Revision',
9a4231e9 420 'fullname' => 'Chemistry Revision',
fc6a0fe2 421 'timecreated' => '1000000002'
5dc361e1
SH
422 ));
423 $course3 = $generator->create_course(array(
424 'category' => $category->id,
425 'idnumber' => '007-03',
426 'shortname' => 'Swiss Rolls and Sunflowers',
9a4231e9 427 'fullname' => 'Aarkvarks guide to Swiss Rolls and Sunflowers',
fc6a0fe2 428 'timecreated' => '1000000003'
5dc361e1
SH
429 ));
430 $course4 = $generator->create_course(array(
431 'category' => $category->id,
432 'idnumber' => '006-04',
433 'shortname' => 'Scratch',
9a4231e9 434 'fullname' => '<a href="test.php">Basic Scratch</a>',
fc6a0fe2 435 'timecreated' => '1000000004'
5dc361e1
SH
436 ));
437 $c1 = (int)$course1->id;
438 $c2 = (int)$course2->id;
439 $c3 = (int)$course3->id;
440 $c4 = (int)$course4->id;
441
442 $coursecat = coursecat::get($category->id);
443 $this->assertTrue($coursecat->resort_courses('idnumber'));
444 $this->assertSame(array($c1, $c4, $c2, $c3), array_keys($coursecat->get_courses()));
445
446 $this->assertTrue($coursecat->resort_courses('shortname'));
447 $this->assertSame(array($c1, $c2, $c4, $c3), array_keys($coursecat->get_courses()));
448
9a4231e9
S
449 $this->assertTrue($coursecat->resort_courses('timecreated'));
450 $this->assertSame(array($c1, $c2, $c3, $c4), array_keys($coursecat->get_courses()));
5dc361e1
SH
451
452 try {
453 // Enable the multilang filter and set it to apply to headings and content.
e37c1286 454 filter_manager::reset_caches();
5dc361e1
SH
455 filter_set_global_state('multilang', TEXTFILTER_ON);
456 filter_set_applies_to_strings('multilang', true);
457 $expected = array($c3, $c4, $c1, $c2);
458 } catch (coding_exception $ex) {
459 $expected = array($c3, $c4, $c2, $c1);
460 }
461 $this->assertTrue($coursecat->resort_courses('fullname'));
462 $this->assertSame($expected, array_keys($coursecat->get_courses()));
463 }
464
93c544bd
MG
465 public function test_get_search_courses() {
466 $cat1 = coursecat::create(array('name' => 'Cat1'));
467 $cat2 = coursecat::create(array('name' => 'Cat2', 'parent' => $cat1->id));
468 $c1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 3', 'summary' => ' ', 'idnumber' => 'ID3'));
469 $c2 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 1', 'summary' => ' ', 'visible' => 0));
470 $c3 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Математика', 'summary' => ' Test '));
471 $c4 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 4', 'summary' => ' ', 'idnumber' => 'ID4'));
472
473 $c5 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Test 5', 'summary' => ' '));
474 $c6 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Дискретная Математика', 'summary' => ' '));
475 $c7 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Test 7', 'summary' => ' ', 'visible' => 0));
476 $c8 = $this->getDataGenerator()->create_course(array('category' => $cat2->id, 'fullname' => 'Test 8', 'summary' => ' '));
477
7db309f3 478 // Get courses in category 1 (returned visible only because user is not enrolled).
93c544bd 479 $res = $cat1->get_courses(array('sortorder' => 1));
7db309f3 480 $this->assertEquals(array($c4->id, $c3->id, $c1->id), array_keys($res)); // Courses are added in reverse order.
93c544bd
MG
481 $this->assertEquals(3, $cat1->get_courses_count());
482
7db309f3 483 // Get courses in category 1 recursively (returned visible only because user is not enrolled).
93c544bd
MG
484 $res = $cat1->get_courses(array('recursive' => 1));
485 $this->assertEquals(array($c4->id, $c3->id, $c1->id, $c8->id, $c6->id, $c5->id), array_keys($res));
486 $this->assertEquals(6, $cat1->get_courses_count(array('recursive' => 1)));
487
7db309f3 488 // Get courses sorted by fullname.
93c544bd
MG
489 $res = $cat1->get_courses(array('sort' => array('fullname' => 1)));
490 $this->assertEquals(array($c1->id, $c4->id, $c3->id), array_keys($res));
491 $this->assertEquals(3, $cat1->get_courses_count(array('sort' => array('fullname' => 1))));
492
7db309f3 493 // Get courses sorted by fullname recursively.
93c544bd
MG
494 $res = $cat1->get_courses(array('recursive' => 1, 'sort' => array('fullname' => 1)));
495 $this->assertEquals(array($c1->id, $c4->id, $c5->id, $c8->id, $c6->id, $c3->id), array_keys($res));
496 $this->assertEquals(6, $cat1->get_courses_count(array('recursive' => 1, 'sort' => array('fullname' => 1))));
497
7db309f3 498 // Get courses sorted by fullname recursively, use offset and limit.
93c544bd
MG
499 $res = $cat1->get_courses(array('recursive' => 1, 'offset' => 1, 'limit' => 2, 'sort' => array('fullname' => -1)));
500 $this->assertEquals(array($c6->id, $c8->id), array_keys($res));
7db309f3 501 // Offset and limit do not affect get_courses_count().
93c544bd
MG
502 $this->assertEquals(6, $cat1->get_courses_count(array('recursive' => 1, 'offset' => 1, 'limit' => 2, 'sort' => array('fullname' => 1))));
503
7db309f3 504 // Calling get_courses_count without prior call to get_courses().
93c544bd
MG
505 $this->assertEquals(3, $cat2->get_courses_count(array('recursive' => 1, 'sort' => array('idnumber' => 1))));
506
7db309f3 507 // Search courses.
93c544bd 508
7db309f3 509 // Search by text.
5e71c37e
MG
510 $res = coursecat::search_courses(array('search' => 'Test'));
511 $this->assertEquals(array($c4->id, $c3->id, $c1->id, $c8->id, $c5->id), array_keys($res));
512 $this->assertEquals(5, coursecat::search_courses_count(array('search' => 'Test')));
513
7db309f3 514 // Search by text with specified offset and limit.
5e71c37e
MG
515 $options = array('sort' => array('fullname' => 1), 'offset' => 1, 'limit' => 2);
516 $res = coursecat::search_courses(array('search' => 'Test'), $options);
517 $this->assertEquals(array($c4->id, $c5->id), array_keys($res));
518 $this->assertEquals(5, coursecat::search_courses_count(array('search' => 'Test'), $options));
519
520 // IMPORTANT: the tests below may fail on some databases
7db309f3 521 // case-insensitive search.
93c544bd
MG
522 $res = coursecat::search_courses(array('search' => 'test'));
523 $this->assertEquals(array($c4->id, $c3->id, $c1->id, $c8->id, $c5->id), array_keys($res));
524 $this->assertEquals(5, coursecat::search_courses_count(array('search' => 'test')));
525
7db309f3 526 // Non-latin language search.
93c544bd
MG
527 $res = coursecat::search_courses(array('search' => 'Математика'));
528 $this->assertEquals(array($c3->id, $c6->id), array_keys($res));
529 $this->assertEquals(2, coursecat::search_courses_count(array('search' => 'Математика'), array()));
3e66b0fc
DW
530
531 $this->setUser($this->getDataGenerator()->create_user());
532
533 // Add necessary capabilities.
534 $this->assign_capability('moodle/course:create', CAP_ALLOW, context_coursecat::instance($cat2->id));
535 // Do another search with restricted capabilities.
536 $reqcaps = array('moodle/course:create');
537 $res = coursecat::search_courses(array('search' => 'test'), array(), $reqcaps);
538 $this->assertEquals(array($c8->id, $c5->id), array_keys($res));
539 $this->assertEquals(2, coursecat::search_courses_count(array('search' => 'test'), array(), $reqcaps));
93c544bd 540 }
31b0530a
MG
541
542 public function test_course_contacts() {
543 global $DB, $CFG;
544 $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'));
545 $managerrole = $DB->get_record('role', array('shortname'=>'manager'));
546 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
547 $oldcoursecontact = $CFG->coursecontact;
548
549 $CFG->coursecontact = $managerrole->id. ','. $teacherrole->id;
550
7db309f3 551 /*
31b0530a
MG
552 * User is listed in course contacts for the course if he has one of the
553 * "course contact" roles ($CFG->coursecontact) AND is enrolled in the course.
554 * If the user has several roles only the highest is displayed.
555 */
556
557 // Test case:
558 //
559 // == Cat1 (user2 has teacher role)
560 // == Cat2
561 // -- course21 (user2 is enrolled as manager) | [Expected] Manager: F2 L2
562 // -- course22 (user2 is enrolled as student) | [Expected] Teacher: F2 L2
563 // == Cat4 (user2 has manager role)
564 // -- course41 (user4 is enrolled as teacher, user5 is enrolled as manager) | [Expected] Manager: F5 L5, Teacher: F4 L4
565 // -- course42 (user2 is enrolled as teacher) | [Expected] Manager: F2 L2
566 // == Cat3 (user3 has manager role)
567 // -- course31 (user3 is enrolled as student) | [Expected] Manager: F3 L3
568 // -- course32 | [Expected]
569 // -- course11 (user1 is enrolled as teacher) | [Expected] Teacher: F1 L1
570 // -- course12 (user1 has teacher role) | [Expected]
571 // also user4 is enrolled as teacher but enrolment is not active
572 $category = $course = $enrol = $user = array();
573 $category[1] = coursecat::create(array('name' => 'Cat1'))->id;
574 $category[2] = coursecat::create(array('name' => 'Cat2', 'parent' => $category[1]))->id;
575 $category[3] = coursecat::create(array('name' => 'Cat3', 'parent' => $category[1]))->id;
576 $category[4] = coursecat::create(array('name' => 'Cat4', 'parent' => $category[2]))->id;
7db309f3
PS
577 foreach (array(1, 2, 3, 4) as $catid) {
578 foreach (array(1, 2) as $courseid) {
31b0530a
MG
579 $course[$catid][$courseid] = $this->getDataGenerator()->create_course(array('idnumber' => 'id'.$catid.$courseid,
580 'category' => $category[$catid]))->id;
581 $enrol[$catid][$courseid] = $DB->get_record('enrol', array('courseid'=>$course[$catid][$courseid], 'enrol'=>'manual'), '*', MUST_EXIST);
582 }
583 }
7db309f3 584 foreach (array(1, 2, 3, 4, 5) as $userid) {
31b0530a
MG
585 $user[$userid] = $this->getDataGenerator()->create_user(array('firstname' => 'F'.$userid, 'lastname' => 'L'.$userid))->id;
586 }
587
588 $manual = enrol_get_plugin('manual');
589
5667e602
MG
590 // Nobody is enrolled now and course contacts are empty.
591 $allcourses = coursecat::get(0)->get_courses(array('recursive' => true, 'coursecontacts' => true, 'sort' => array('idnumber' => 1)));
592 foreach ($allcourses as $onecourse) {
593 $this->assertEmpty($onecourse->get_course_contacts());
594 }
595
31b0530a
MG
596 // Cat1 (user2 has teacher role)
597 role_assign($teacherrole->id, $user[2], context_coursecat::instance($category[1]));
598 // course21 (user2 is enrolled as manager)
599 $manual->enrol_user($enrol[2][1], $user[2], $managerrole->id);
600 // course22 (user2 is enrolled as student)
601 $manual->enrol_user($enrol[2][2], $user[2], $studentrole->id);
602 // Cat4 (user2 has manager role)
603 role_assign($managerrole->id, $user[2], context_coursecat::instance($category[4]));
604 // course41 (user4 is enrolled as teacher, user5 is enrolled as manager)
605 $manual->enrol_user($enrol[4][1], $user[4], $teacherrole->id);
606 $manual->enrol_user($enrol[4][1], $user[5], $managerrole->id);
607 // course42 (user2 is enrolled as teacher)
608 $manual->enrol_user($enrol[4][2], $user[2], $teacherrole->id);
609 // Cat3 (user3 has manager role)
610 role_assign($managerrole->id, $user[3], context_coursecat::instance($category[3]));
611 // course31 (user3 is enrolled as student)
612 $manual->enrol_user($enrol[3][1], $user[3], $studentrole->id);
613 // course11 (user1 is enrolled as teacher)
614 $manual->enrol_user($enrol[1][1], $user[1], $teacherrole->id);
615 // -- course12 (user1 has teacher role)
616 // also user4 is enrolled as teacher but enrolment is not active
617 role_assign($teacherrole->id, $user[1], context_course::instance($course[1][2]));
618 $manual->enrol_user($enrol[1][2], $user[4], $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED);
619
620 $allcourses = coursecat::get(0)->get_courses(array('recursive' => true, 'coursecontacts' => true, 'sort' => array('idnumber' => 1)));
7db309f3 621 // Simplify the list of contacts for each course (similar as renderer would do).
31b0530a 622 $contacts = array();
7db309f3
PS
623 foreach (array(1, 2, 3, 4) as $catid) {
624 foreach (array(1, 2) as $courseid) {
31b0530a
MG
625 $tmp = array();
626 foreach ($allcourses[$course[$catid][$courseid]]->get_course_contacts() as $contact) {
627 $tmp[] = $contact['rolename']. ': '. $contact['username'];
628 }
629 $contacts[$catid][$courseid] = join(', ', $tmp);
630 }
631 }
632
633 // Assert:
634 // -- course21 (user2 is enrolled as manager) | Manager: F2 L2
7db309f3 635 $this->assertSame('Manager: F2 L2', $contacts[2][1]);
31b0530a 636 // -- course22 (user2 is enrolled as student) | Teacher: F2 L2
7db309f3 637 $this->assertSame('Teacher: F2 L2', $contacts[2][2]);
31b0530a 638 // -- course41 (user4 is enrolled as teacher, user5 is enrolled as manager) | Manager: F5 L5, Teacher: F4 L4
7db309f3 639 $this->assertSame('Manager: F5 L5, Teacher: F4 L4', $contacts[4][1]);
31b0530a 640 // -- course42 (user2 is enrolled as teacher) | [Expected] Manager: F2 L2
7db309f3 641 $this->assertSame('Manager: F2 L2', $contacts[4][2]);
31b0530a 642 // -- course31 (user3 is enrolled as student) | Manager: F3 L3
7db309f3 643 $this->assertSame('Manager: F3 L3', $contacts[3][1]);
31b0530a 644 // -- course32 |
7db309f3 645 $this->assertSame('', $contacts[3][2]);
31b0530a 646 // -- course11 (user1 is enrolled as teacher) | Teacher: F1 L1
7db309f3 647 $this->assertSame('Teacher: F1 L1', $contacts[1][1]);
31b0530a 648 // -- course12 (user1 has teacher role) |
7db309f3 649 $this->assertSame('', $contacts[1][2]);
31b0530a 650
5667e602
MG
651 // Suspend user 4 and make sure he is no longer in contacts of course 1 in category 4.
652 $manual->enrol_user($enrol[4][1], $user[4], $teacherrole->id, 0, 0, ENROL_USER_SUSPENDED);
653 $allcourses = coursecat::get(0)->get_courses(array('recursive' => true, 'coursecontacts' => true, 'sort' => array('idnumber' => 1)));
654 $contacts = $allcourses[$course[4][1]]->get_course_contacts();
655 $this->assertCount(1, $contacts);
656 $contact = reset($contacts);
657 $this->assertEquals('F5 L5', $contact['username']);
658
31b0530a
MG
659 $CFG->coursecontact = $oldcoursecontact;
660 }
961403c4
MG
661
662 public function test_overview_files() {
663 global $CFG;
664 $this->setAdminUser();
665 $cat1 = coursecat::create(array('name' => 'Cat1'));
666
667 // Create course c1 with one image file.
668 $dratid1 = $this->fill_draft_area(array('filename.jpg' => 'Test file contents1'));
669 $c1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id,
670 'fullname' => 'Test 1', 'overviewfiles_filemanager' => $dratid1));
671 // Create course c2 with two image files (only one file will be added because of settings).
672 $dratid2 = $this->fill_draft_area(array('filename21.jpg' => 'Test file contents21', 'filename22.jpg' => 'Test file contents22'));
673 $c2 = $this->getDataGenerator()->create_course(array('category' => $cat1->id,
674 'fullname' => 'Test 2', 'overviewfiles_filemanager' => $dratid2));
675 // Create course c3 without files.
676 $c3 = $this->getDataGenerator()->create_course(array('category' => $cat1->id, 'fullname' => 'Test 3'));
677
678 // Change the settings to allow multiple files of any types.
679 $CFG->courseoverviewfileslimit = 3;
680 $CFG->courseoverviewfilesext = '*';
681 // Create course c5 with two image files.
682 $dratid4 = $this->fill_draft_area(array('filename41.jpg' => 'Test file contents41', 'filename42.jpg' => 'Test file contents42'));
683 $c4 = $this->getDataGenerator()->create_course(array('category' => $cat1->id,
684 'fullname' => 'Test 4', 'overviewfiles_filemanager' => $dratid4));
685 // Create course c6 with non-image file.
686 $dratid5 = $this->fill_draft_area(array('filename51.zip' => 'Test file contents51'));
687 $c5 = $this->getDataGenerator()->create_course(array('category' => $cat1->id,
688 'fullname' => 'Test 5', 'overviewfiles_filemanager' => $dratid5));
689
690 // Reset default settings.
691 $CFG->courseoverviewfileslimit = 1;
692 $CFG->courseoverviewfilesext = '.jpg,.gif,.png';
693
694 $courses = $cat1->get_courses();
695 $this->assertTrue($courses[$c1->id]->has_course_overviewfiles());
696 $this->assertTrue($courses[$c2->id]->has_course_overviewfiles());
697 $this->assertFalse($courses[$c3->id]->has_course_overviewfiles());
698 $this->assertTrue($courses[$c4->id]->has_course_overviewfiles());
699 $this->assertTrue($courses[$c5->id]->has_course_overviewfiles()); // Does not validate the filetypes.
700
701 $this->assertEquals(1, count($courses[$c1->id]->get_course_overviewfiles()));
702 $this->assertEquals(1, count($courses[$c2->id]->get_course_overviewfiles()));
703 $this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles()));
704 $this->assertEquals(1, count($courses[$c4->id]->get_course_overviewfiles()));
705 $this->assertEquals(0, count($courses[$c5->id]->get_course_overviewfiles())); // Validate the filetypes.
706
707 // Overview files are not allowed, all functions return empty values.
708 $CFG->courseoverviewfileslimit = 0;
709
710 $this->assertFalse($courses[$c1->id]->has_course_overviewfiles());
711 $this->assertFalse($courses[$c2->id]->has_course_overviewfiles());
712 $this->assertFalse($courses[$c3->id]->has_course_overviewfiles());
713 $this->assertFalse($courses[$c4->id]->has_course_overviewfiles());
714 $this->assertFalse($courses[$c5->id]->has_course_overviewfiles());
715
716 $this->assertEquals(0, count($courses[$c1->id]->get_course_overviewfiles()));
717 $this->assertEquals(0, count($courses[$c2->id]->get_course_overviewfiles()));
718 $this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles()));
719 $this->assertEquals(0, count($courses[$c4->id]->get_course_overviewfiles()));
720 $this->assertEquals(0, count($courses[$c5->id]->get_course_overviewfiles()));
721
722 // Multiple overview files are allowed but still limited to images.
723 $CFG->courseoverviewfileslimit = 3;
724
725 $this->assertTrue($courses[$c1->id]->has_course_overviewfiles());
726 $this->assertTrue($courses[$c2->id]->has_course_overviewfiles());
727 $this->assertFalse($courses[$c3->id]->has_course_overviewfiles());
728 $this->assertTrue($courses[$c4->id]->has_course_overviewfiles());
729 $this->assertTrue($courses[$c5->id]->has_course_overviewfiles()); // Still does not validate the filetypes.
730
731 $this->assertEquals(1, count($courses[$c1->id]->get_course_overviewfiles()));
732 $this->assertEquals(1, count($courses[$c2->id]->get_course_overviewfiles())); // Only 1 file was actually added.
733 $this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles()));
734 $this->assertEquals(2, count($courses[$c4->id]->get_course_overviewfiles()));
735 $this->assertEquals(0, count($courses[$c5->id]->get_course_overviewfiles()));
736
737 // Multiple overview files of any type are allowed.
738 $CFG->courseoverviewfilesext = '*';
739
740 $this->assertTrue($courses[$c1->id]->has_course_overviewfiles());
741 $this->assertTrue($courses[$c2->id]->has_course_overviewfiles());
742 $this->assertFalse($courses[$c3->id]->has_course_overviewfiles());
743 $this->assertTrue($courses[$c4->id]->has_course_overviewfiles());
744 $this->assertTrue($courses[$c5->id]->has_course_overviewfiles());
745
746 $this->assertEquals(1, count($courses[$c1->id]->get_course_overviewfiles()));
747 $this->assertEquals(1, count($courses[$c2->id]->get_course_overviewfiles()));
748 $this->assertEquals(0, count($courses[$c3->id]->get_course_overviewfiles()));
749 $this->assertEquals(2, count($courses[$c4->id]->get_course_overviewfiles()));
750 $this->assertEquals(1, count($courses[$c5->id]->get_course_overviewfiles()));
751 }
752
e9321ad0
AN
753 public function test_get_nested_name() {
754 $cat1name = 'Cat1';
755 $cat2name = 'Cat2';
756 $cat3name = 'Cat3';
757 $cat4name = 'Cat4';
758 $category1 = coursecat::create(array('name' => $cat1name));
759 $category2 = coursecat::create(array('name' => $cat2name, 'parent' => $category1->id));
760 $category3 = coursecat::create(array('name' => $cat3name, 'parent' => $category2->id));
761 $category4 = coursecat::create(array('name' => $cat4name, 'parent' => $category2->id));
762
763 $this->assertEquals($cat1name, $category1->get_nested_name(false));
764 $this->assertEquals("{$cat1name} / {$cat2name}", $category2->get_nested_name(false));
765 $this->assertEquals("{$cat1name} / {$cat2name} / {$cat3name}", $category3->get_nested_name(false));
766 $this->assertEquals("{$cat1name} / {$cat2name} / {$cat4name}", $category4->get_nested_name(false));
767 }
768
961403c4
MG
769 /**
770 * Creates a draft area for current user and fills it with fake files
771 *
772 * @param array $files array of files that need to be added to filearea, filename => filecontents
773 * @return int draftid for the filearea
774 */
775 protected function fill_draft_area(array $files) {
776 global $USER;
777 $usercontext = context_user::instance($USER->id);
778 $draftid = file_get_unused_draft_itemid();
779 foreach ($files as $filename => $filecontents) {
780 // Add actual file there.
781 $filerecord = array('component' => 'user', 'filearea' => 'draft',
782 'contextid' => $usercontext->id, 'itemid' => $draftid,
783 'filename' => $filename, 'filepath' => '/');
784 $fs = get_file_storage();
785 $fs->create_file_from_string($filerecord, $filecontents);
786 }
787 return $draftid;
788 }
7db309f3 789}